OpenGL 与 3D第 1 / 4 章
Worker 端:几何合并与序列化
按材质合并几何体,并整理可转移的 Buffer 与图片资源。
worker
重写了加载过程
import CustGLTFLoader from '@/workers/gltf-loader.js'
import { LoadingManager, FrontSide, DoubleSide, Mesh } from 'three'
import { mergeGeometries } from './BufferGeometryUtils.js'
self.onmessage = async function (e) {
const { isMainModel, files, model_id } = e.data
postMessage({ type: 1 })
const modelGroup = await loadAndMergeGLTF(model_id, files, isMainModel)
if (modelGroup) {
genMainModelMsg(modelGroup)
}
}
async function genMainModelMsg(modelGroup) {
const materialMaps = {}
modelGroup.updateMatrixWorld()
modelGroup.traverse(mesh => {
if (mesh instanceof Mesh) {
if (mesh.material.uuid in materialMaps) {
materialMaps[mesh.material.uuid].meshes.push(mesh)
} else {
materialMaps[mesh.material.uuid] = {
material: mesh.material,
meshes: [mesh],
}
}
}
})
const geometryMaps = {}
for (const key in materialMaps) {
const { material, meshes } = materialMaps[key]
const geometryList = meshes.map((mesh, index) => {
const geometry = mesh.geometry.clone() //没必要克隆
geometry.applyMatrix4(mesh.matrixWorld)
mesh.geometry.dispose()
return geometry
})
const mergedGeometry = mergeGeometries(geometryList, true)
if(mergedGeometry){
mergedGeometry.computeVertexNormals()
} else {
// 合并失败,则不处理
continue
}
geometryMaps[key] = {
material,
geometry: mergedGeometry,
groups: mergedGeometry.groups,
originalMeshes: meshes.map(mesh => {
const originalGeometry = mesh.geometry
return {
boundingBox: originalGeometry.boundingBox.clone().applyMatrix4(mesh.matrixWorld),
boundingSphere: originalGeometry.boundingSphere.clone().applyMatrix4(mesh.matrixWorld),
assimpMeshName: mesh.userData.assimpMeshName,
}
}),
}
}
const transferableObjects= []
const geometryData = {}
for (const key in geometryMaps) {
const { material, geometry, originalMeshes } = geometryMaps[key]
const buffers = {}
for (const attributeName in geometry.attributes) {
const attribute = geometry.attributes[attributeName]
buffers[attributeName] = attribute.array.buffer
transferableObjects.push(attribute.array.buffer)
}
if (geometry.index) {
buffers['index'] = geometry.index.array.buffer
transferableObjects.push(geometry.index.array.buffer)
}
const jsons = material.toJSON()
//删除无用的属性
for (const key of ['images', 'metadata', 'textures', 'envMapRotation']) {
if (Reflect.has(jsons, key)) delete jsons[key]
}
geometryData[key] = {
attributes: Object.keys(geometry.attributes).reduce((acc, name) => {
const attr = geometry.attributes[name]
acc[name] = {
itemSize: attr.itemSize,
normalized: attr.normalized,
array: attr.array,
}
return acc
}, {}),
index: geometry.index
? {
array: geometry.index.array,
}
: null,
materialProps: {
jsons,
type: material.type,
map: material.map?.source?.data instanceof ImageBitmap ? material.map.source.data : null,
metalnessMap: material.metalnessMap?.source?.data instanceof ImageBitmap ? material.metalnessMap.source.data : null,
roughnessMap: material.roughnessMap?.source?.data instanceof ImageBitmap ? material.roughnessMap.source.data : null,
},
originalMeshes: originalMeshes.map(mesh => ({
boundingBox: {
min: { x: mesh.boundingBox.min.x, y: mesh.boundingBox.min.y, z: mesh.boundingBox.min.z },
max: { x: mesh.boundingBox.max.x, y: mesh.boundingBox.max.y, z: mesh.boundingBox.max.z },
},
boundingSphere: {
center: { x: mesh.boundingSphere.center.x, y: mesh.boundingSphere.center.y, z: mesh.boundingSphere.center.z },
radius: mesh.boundingSphere.radius,
},
assimpMeshName: mesh.assimpMeshName,
})),
groups: geometry.groups,
}
if (geometryData[key].materialProps.map) {
transferableObjects.push(geometryData[key].materialProps.map)
}
material.dispose()
}
self.postMessage(
{
type: 4,
geometryData,
},
transferableObjects
)
}