TkAstral3D/packages/sdk/lib/workers/bvh/GenerateColliderEnvironmentWorker.js
2025-10-04 23:36:07 +08:00

137 lines
4.8 KiB
JavaScript

import {Group, BufferGeometryLoader, Mesh} from "three";
import {Viewport} from "@/core/preview/Viewport.ts";
import GenerateColliderEnvironmentWorkerUrl from './generateColliderEnvironment.worker.js?worker&url';
export class GenerateColliderEnvironmentWorker{
constructor() {
this.worker = new Worker(GenerateColliderEnvironmentWorkerUrl, { type: 'module' });
this.worker.onerror = e => {
if ( e.message ) {
throw new Error( `GenerateColliderEnvironmentWorker: Could not create Web Worker with error "${ e.message }"` );
} else {
throw new Error( `GenerateColliderEnvironmentWorker: Could not create Web Worker.`);
}
};
}
parseAttr(attr,gArray){
// 遍历attr的key,还原
for (const key in attr) {
const item = attr[key];
if(item.array){
item.array = gArray[key].array;
item.count = gArray[key].count;
item.itemSize = gArray[key].itemSize;
}
}
}
generate(scene){
if (this.worker === null) {
throw new Error( 'GenerateColliderEnvironmentWorker: Worker has been disposed.' );
}
const { worker } = this;
const dbTable = "environment";
return new Promise(async (resolve,reject)=>{
worker.onerror = e => {
reject(new Error(`GenerateColliderEnvironmentWorker: ${ e.message }`));
};
// 按照材质分组mesh
let toMerge = {},keys = [];
const loader = new BufferGeometryLoader();
const db = window.VIEWPORT.modules["db"];
const has = await db.hasStore(dbTable);
if(has){
// TODO 此处应该直接读取返回
//db.clear(dbTable);
}else{
await db.addStore(dbTable);
}
let environment = new Group();
environment.name = "ROAMING-ColliderEnvironment";
worker.onmessage = ({data}) => {
switch (data.type) {
case "env":
const geoJson = data.geometryJson;
const geometry = loader.parse(geoJson);
const newMesh = new Mesh(geometry, window.BOM3D.materials[data.materialUuid]);
newMesh.castShadow = true;
newMesh.receiveShadow = true;
newMesh.material.shadowSide = 2;
environment.add(newMesh);
break;
case "collider":
const mergedGeometryJson = data.mergedGeometryJson;
const mergedGeometry = loader.parse(mergedGeometryJson);
this.parseAttr(mergedGeometry.attributes,data.gArray);
mergedGeometry.index.array = data.gArray.index.array;
mergedGeometry.index.itemSize = data.gArray.index.itemSize;
resolve({mergedGeometry, environment});
worker.onmessage = null;
break;
}
}
scene.traverse(c => {
// @ts-ignore 只合并网格
if (c.isMesh) {
// @ts-ignore
let uuid;
// @ts-ignore
if (c.material.uuid === Viewport.RegularMat.uuid) {
uuid = c.userData.old.materialUuid;
} else {
// @ts-ignore
uuid = c.material.uuid;
}
toMerge[uuid] = toMerge[uuid] || [];
let mesh = c.clone();
mesh.material = undefined;
// if (mesh.material?.map === null || !mesh.material?.map?.isTexture) {
// // 材质不存在贴图则删除uv属性
// mesh.geometry.deleteAttribute("uv");
// }
const json = mesh.toJSON();
json.matrixWorld = c.matrixWorld.toArray();
toMerge[uuid].push(json);
}
});
keys = Object.keys(toMerge);
const num = new Proxy({value:keys.length},{
set(target, p, newValue, receiver) {
target[p] = newValue;
if(newValue === 0){
toMerge = undefined;
worker.postMessage({indexedDBName:"BOM",table:dbTable,uuids:[...keys]})
}
return true;
}
})
keys.forEach((uuid)=>{
db.setItem(uuid,toMerge[uuid],dbTable).then(()=>{
num.value--;
});
})
})
}
}