feat / AEB-XX Реализован базовый 3D просмотрщик моделей с поддержкой GLTF
This commit is contained in:
168
frontend/utils/meshCache.ts
Normal file
168
frontend/utils/meshCache.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import { AbstractMesh } from '@babylonjs/core'
|
||||
|
||||
export interface ParsedMeshData {
|
||||
meshes: unknown[]
|
||||
boundingBox: {
|
||||
min: { x: number; y: number; z: number }
|
||||
max: { x: number; y: number; z: number }
|
||||
}
|
||||
metadata: {
|
||||
modelPath: string
|
||||
parsedAt: string
|
||||
totalVertices: number
|
||||
totalIndices: number
|
||||
}
|
||||
}
|
||||
|
||||
export const getCacheFileName = (modelPath: string) => {
|
||||
const fileName = modelPath.split('/').pop()?.replace('.gltf', '') || 'model'
|
||||
return `${fileName}_parsed.json`
|
||||
}
|
||||
|
||||
export const loadCachedData = async (cacheFileName: string): Promise<ParsedMeshData | null> => {
|
||||
try {
|
||||
const response = await fetch(`/data/${cacheFileName}`)
|
||||
if (response.ok) {
|
||||
const cachedData = await response.json()
|
||||
console.log('📦 Loaded cached mesh data:', cachedData.metadata)
|
||||
return cachedData
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('❌ No cached data found, will parse scene')
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const extractAllMeshData = (mesh: AbstractMesh) => {
|
||||
const meshData: Record<string, unknown> = {}
|
||||
|
||||
const safeStringify = (obj: unknown): unknown => {
|
||||
try {
|
||||
JSON.stringify(obj)
|
||||
return obj
|
||||
} catch {
|
||||
return '[Circular Reference]'
|
||||
}
|
||||
}
|
||||
|
||||
const extractValue = (value: unknown): unknown => {
|
||||
if (value === null || value === undefined) {
|
||||
return value
|
||||
}
|
||||
|
||||
if (typeof value === 'function') {
|
||||
return '[Function]'
|
||||
}
|
||||
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
const obj = value as Record<string, unknown>
|
||||
|
||||
if (obj.constructor.name === 'Vector3') {
|
||||
return { x: obj.x, y: obj.y, z: obj.z }
|
||||
} else if (obj.constructor.name === 'Quaternion') {
|
||||
return { x: obj.x, y: obj.y, z: obj.z, w: obj.w }
|
||||
} else if (obj.constructor.name === 'Color3') {
|
||||
return { r: obj.r, g: obj.g, b: obj.b }
|
||||
} else if (obj.constructor.name === 'Color4') {
|
||||
return { r: obj.r, g: obj.g, b: obj.b, a: obj.a }
|
||||
} else if (Array.isArray(value)) {
|
||||
return value.map(item => extractValue(item))
|
||||
} else {
|
||||
return safeStringify(value)
|
||||
}
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
for (const key in mesh) {
|
||||
try {
|
||||
const value = (mesh as unknown as Record<string, unknown>)[key]
|
||||
meshData[key] = extractValue(value)
|
||||
} catch (error) {
|
||||
meshData[key] = '[Error accessing property]'
|
||||
}
|
||||
}
|
||||
|
||||
meshData.geometry = {
|
||||
vertices: mesh.getVerticesData('position') ? Array.from(mesh.getVerticesData('position')!) : [],
|
||||
indices: mesh.getIndices() ? Array.from(mesh.getIndices()!) : [],
|
||||
normals: mesh.getVerticesData('normal') ? Array.from(mesh.getVerticesData('normal')!) : [],
|
||||
uvs: mesh.getVerticesData('uv') ? Array.from(mesh.getVerticesData('uv')!) : [],
|
||||
colors: mesh.getVerticesData('color') ? Array.from(mesh.getVerticesData('color')!) : [],
|
||||
tangents: mesh.getVerticesData('tangent') ? Array.from(mesh.getVerticesData('tangent')!) : [],
|
||||
matricesWeights: mesh.getVerticesData('matricesWeights') ? Array.from(mesh.getVerticesData('matricesWeights')!) : [],
|
||||
matricesIndices: mesh.getVerticesData('matricesIndices') ? Array.from(mesh.getVerticesData('matricesIndices')!) : [],
|
||||
totalVertices: mesh.getTotalVertices(),
|
||||
totalIndices: mesh.getIndices() ? mesh.getIndices()!.length : 0
|
||||
}
|
||||
|
||||
return meshData
|
||||
}
|
||||
|
||||
export const parseAndCacheScene = async (meshes: AbstractMesh[], cacheFileName: string, modelPath: string): Promise<ParsedMeshData> => {
|
||||
const parsedMeshes = meshes.map(mesh => extractAllMeshData(mesh))
|
||||
|
||||
const boundingBox = meshes[0].getHierarchyBoundingVectors()
|
||||
const totalVertices = parsedMeshes.reduce((sum, mesh) => {
|
||||
const meshData = mesh as Record<string, unknown>
|
||||
const geometry = meshData.geometry as Record<string, unknown>
|
||||
const vertices = geometry.vertices as number[]
|
||||
return sum + vertices.length / 3
|
||||
}, 0)
|
||||
const totalIndices = parsedMeshes.reduce((sum, mesh) => {
|
||||
const meshData = mesh as Record<string, unknown>
|
||||
const geometry = meshData.geometry as Record<string, unknown>
|
||||
const indices = geometry.indices as number[]
|
||||
return sum + indices.length
|
||||
}, 0)
|
||||
|
||||
const parsedData: ParsedMeshData = {
|
||||
meshes: parsedMeshes,
|
||||
boundingBox: {
|
||||
min: boundingBox.min,
|
||||
max: boundingBox.max
|
||||
},
|
||||
metadata: {
|
||||
modelPath,
|
||||
parsedAt: new Date().toISOString(),
|
||||
totalVertices: Math.floor(totalVertices),
|
||||
totalIndices
|
||||
}
|
||||
}
|
||||
|
||||
console.log('💾 Caching parsed mesh data:', parsedData.metadata)
|
||||
|
||||
try {
|
||||
console.log('💾 Attempting to cache mesh data...')
|
||||
console.log('💾 Cache file name:', cacheFileName)
|
||||
console.log('💾 Data size:', JSON.stringify(parsedData).length, 'bytes')
|
||||
|
||||
const response = await fetch('/api/cache-mesh-data', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
fileName: cacheFileName,
|
||||
data: parsedData
|
||||
})
|
||||
})
|
||||
|
||||
console.log('💾 Response status:', response.status)
|
||||
console.log('💾 Response ok:', response.ok)
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json()
|
||||
console.log('✅ Mesh data cached successfully:', result)
|
||||
} else {
|
||||
const errorText = await response.text()
|
||||
console.warn('⚠️ Failed to cache mesh data:', response.status, errorText)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Could not cache mesh data:', error)
|
||||
console.error('💾 Full error details:', error)
|
||||
}
|
||||
|
||||
return parsedData
|
||||
}
|
||||
Reference in New Issue
Block a user