feat(All):Initial

This commit is contained in:
2025-10-04 23:36:07 +08:00
commit 2b4e5d2668
1321 changed files with 415958 additions and 0 deletions
@@ -0,0 +1,137 @@
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--;
});
})
})
}
}
@@ -0,0 +1,104 @@
import { Box3, BufferAttribute } from 'three';
import { MeshBVH } from 'three-mesh-bvh';
import GenerateAsyncWorkerUrl from './generateAsync.worker.js?worker&url';
export class GenerateMeshBVHWorker {
constructor() {
this.running = false;
this.worker = new Worker(GenerateAsyncWorkerUrl, { type: 'module' });
this.worker.onerror = e => {
if ( e.message ) {
throw new Error( `GenerateMeshBVHWorker: Could not create Web Worker with error "${ e.message }"` );
} else {
throw new Error( 'GenerateMeshBVHWorker: Could not create Web Worker.' );
}
};
}
generate( geometry, options = {} ) {
if (this.running) {
throw new Error( 'GenerateMeshBVHWorker: Already running job.' );
}
if (this.worker === null) {
throw new Error( 'GenerateMeshBVHWorker: Worker has been disposed.' );
}
const { worker } = this;
this.running = true;
return new Promise((resolve, reject) => {
worker.onerror = e => {
reject( new Error( `GenerateMeshBVHWorker: ${ e.message }` ) );
this.running = false;
};
worker.onmessage = e => {
this.running = false;
const { data } = e;
if (data.error) {
reject( new Error( data.error ) );
worker.onmessage = null;
} else if ( data.serialized ) {
const { serialized, position } = data;
const bvh = MeshBVH.deserialize( serialized, geometry, { setIndex: false } );
const boundsOptions = Object.assign( {
setBoundingBox: true,
}, options );
// we need to replace the arrays because they're neutered entirely by the
// webworker transfer.
geometry.attributes.position.array = position;
if ( geometry.index ) {
geometry.index.array = serialized.index;
} else {
const newIndex = new BufferAttribute( serialized.index, 1, false );
geometry.setIndex( newIndex );
}
if ( boundsOptions.setBoundingBox ) {
geometry.boundingBox = bvh.getBoundingBox( new Box3() );
}
resolve( bvh );
worker.onmessage = null;
} else if ( options.onProgress ) {
options.onProgress( data.progress );
}
};
const index = geometry.index ? geometry.index.array : null;
const position = geometry.attributes.position.array;
if (position.isInterleavedBufferAttribute || index && index.isInterleavedBufferAttribute) {
throw new Error( 'GenerateMeshBVHWorker: InterleavedBufferAttribute are not supported for the geometry attributes.' );
}
const transferable = [ position ];
if (index) {
transferable.push( index );
}
worker.postMessage({
index,
position,
options: {
...options,
onProgress: null,
includedProgressCallback: Boolean( options.onProgress ),
groups: [ ... geometry.groups ],
},
}, transferable.map(arr => arr.buffer));
});
}
dispose() {
this.worker.terminate();
this.worker = null;
}
terminate() {
console.warn( 'GenerateMeshBVHWorker: "terminate" is deprecated. Use "dispose" instead.' );
this.dispose();
}
}
@@ -0,0 +1,60 @@
import {
BufferGeometry,
BufferAttribute,
} from 'three';
import { MeshBVH } from 'three-mesh-bvh';
onmessage = function ( { data } ) {
let prevTime = performance.now();
function onProgressCallback( progress ) {
const currTime = performance.now();
if ( currTime - prevTime >= 10 || progress === 1.0 ) {
postMessage({
error: null,
serialized: null,
position: null,
progress,
});
prevTime = currTime;
}
}
const { index, position, options } = data;
try {
const geometry = new BufferGeometry();
geometry.setAttribute( 'position', new BufferAttribute( position, 3, false ) );
if ( index ) {
geometry.setIndex( new BufferAttribute( index, 1, false ) );
}
if ( options.includedProgressCallback ) {
options.onProgress = onProgressCallback;
}
if ( options.groups ) {
const groups = options.groups;
for ( const i in groups ) {
const group = groups[ i ];
geometry.addGroup( group.start, group.count, group.materialIndex );
}
}
const bvh = new MeshBVH( geometry, options );
const serialized = MeshBVH.serialize( bvh, { copyIndexBuffer: false } );
postMessage( {
error: null,
serialized,
position,
progress: 1,
}, [ serialized.index.buffer, position.buffer, ...serialized.roots ] );
} catch ( error ) {
postMessage( {
error,
serialized: null,
position: null,
progress: 1,
} );
}
};
@@ -0,0 +1,140 @@
import {Group, Mesh, MeshBasicMaterial, Matrix4, InstancedMesh} from "three";
import {ObjectLoader} from "@/core/loader/ObjectLoader";
import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
import {StaticGeometryGenerator} from "three-mesh-bvh";
import {TYPED_ARRAYS} from "../../../config/global";
import DBStorage from "../../../core/utils/DBStorage";
/**
* 解析attr
* @param attr Geometry Attributes
*/
function parseAttr(attr) {
// 遍历attr的key,组织为新对象返回
const newAttr = {};
for (const key in attr) {
const item = attr[key];
if (item.array) {
newAttr[key] = {
itemSize: item.itemSize,
array: item.array,
count: item.count
}
item.array = new TYPED_ARRAYS[item.array.constructor.name]([]);
}
}
return newAttr;
}
let basicMaterial = new MeshBasicMaterial();
function getMeshByInstancedMesh(instancedMesh){
const meshes = [];
const matrixWorld = instancedMesh.matrixWorld;
const count = instancedMesh.count;
for (let instanceId = 0; instanceId < count; instanceId++) {
const _mesh = new Mesh();
const _instanceLocalMatrix = new Matrix4();
const _instanceWorldMatrix = new Matrix4();
_mesh.geometry = instancedMesh.geometry;
_mesh.material = instancedMesh.material;
// 计算每个实例的世界矩阵
instancedMesh.getMatrixAt(instanceId, _instanceLocalMatrix);
_instanceWorldMatrix.multiplyMatrices(matrixWorld, _instanceLocalMatrix);
// 网格表示这个单一实例
_mesh.matrixWorld = _instanceWorldMatrix;
meshes.push(_mesh);
}
return meshes;
}
onmessage = async function ({data}) {
const {indexedDBName,table,uuids} = data;
let loader = new ObjectLoader();
let environment = new Group();
const db = new DBStorage(indexedDBName,table);
for (const uuid of uuids) {
const arr = await db.getItem(uuid);
const visualGeometries = [];
arr.forEach((meshJson) => {
const m = new Matrix4();
const matrixWorld = m.fromArray(meshJson.matrixWorld);
meshJson.matrixWorld = undefined;
const mesh = loader.parse(meshJson);
const cloneGeom = (me) => {
const geom = me.geometry.clone();
geom.applyMatrix4(me.matrixWorld);
visualGeometries.push(geom);
}
// @ts-ignore
if (!mesh.isInstancedMesh) {
cloneGeom(mesh);
} else {
const meshes = getMeshByInstancedMesh(mesh);
meshes.forEach((m) => {
cloneGeom(m);
});
}
});
if (visualGeometries.length) {
const newGeom = BufferGeometryUtils.mergeGeometries(visualGeometries);
let n_geo = newGeom.clone().toJSON();
const newMesh = new Mesh(newGeom, basicMaterial);
environment.add(newMesh);
postMessage({
type: "env",
materialUuid:uuid,
geometryJson: n_geo
})
n_geo = undefined;
}
await db.removeItem(uuid);
}
const staticGenerator = new StaticGeometryGenerator(environment);
staticGenerator.attributes = ['position'];
const mergedGeometry = staticGenerator.generate();
const gArray = {
...parseAttr(mergedGeometry.attributes),
index: {
array:mergedGeometry.index.array,
itemSize:mergedGeometry.index.itemSize
}
}
mergedGeometry.index.array = new TYPED_ARRAYS[gArray.index.array.constructor.name]([]);
const mergedGeometryJson = mergedGeometry.toJSON();
const transfer = Object.values(gArray).map(arrayBuffer => arrayBuffer.array.buffer);
postMessage({
type: "collider",
mergedGeometryJson: mergedGeometryJson,
gArray: gArray
}, transfer);
basicMaterial = undefined;
environment = undefined;
}