UNPKG

threepipe

Version:

A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.

130 lines 5.38 kB
import { BufferAttribute, InstancedMesh, Matrix4, Quaternion, Vector3 } from 'three'; // noinspection ES6PreferShortImport import { copyObject3DUserData } from '../../utils/serialization'; export function autoGPUInstanceMeshes(matOrGeom) { if (!matOrGeom.isMaterial && !matOrGeom.isBufferGeometry) return; const meshes = Array.from(matOrGeom.appliedMeshes).filter((m) => !m.isInstancedMesh && !!m.parent && m.children.length === 0 && !Array.isArray(m.material)); if (meshes.length < 2) return; const getKey = (m) => { return m.parent.uuid + '_' + m.geometry?.uuid + '_' + m.material?.uuid; // + '_' + (m.matrix.determinant()<0) }; const keyMeshMap = new Map(); for (const mesh1 of meshes) { const key = getKey(mesh1); if (!keyMeshMap.has(key)) keyMeshMap.set(key, []); keyMeshMap.get(key).push(mesh1); mesh1.updateMatrix(); } const keys = keyMeshMap.keys(); for (const key of keys) { const iMeshes = keyMeshMap.get(key); const baseMesh = iMeshes[0]; if (!baseMesh) continue; if (iMeshes.length < 2) continue; const inst = new InstancedMesh(baseMesh.geometry, baseMesh.material, iMeshes.length); const ud = baseMesh.userData; baseMesh.userData = {}; inst.copy(baseMesh); copyObject3DUserData(inst.userData, ud); const parent = baseMesh.parent; inst.position.set(0, 0, 0); inst.rotation.set(0, 0, 0); inst.scale.set(1, 1, 1); inst.updateMatrix(); const translationAttr = new Float32Array(inst.count * 3); const rotationAttr = new Float32Array(inst.count * 4); const scaleAttr = new Float32Array(inst.count * 3); // const pos = new Vector3() // const quat = new Quaternion() // const scale = new Vector3() for (let i = 0; i < iMeshes.length; i++) { const m = iMeshes[i]; // const mat = inst.matrix.clone().invert().multiply(m.matrix) const mat = m.matrix; // mat.decompose(pos, quat, scale) if (mat.determinant() < 0) { mat.elements[0] *= -1; mat.elements[1] *= -1; mat.elements[2] *= -1; } inst.setMatrixAt(i, mat); m.position.toArray(translationAttr, i * 3); m.quaternion.toArray(rotationAttr, i * 4); m.scale.toArray(scaleAttr, i * 3); m.removeFromParent(); // ;(m.material as any)?.appliedMeshes?.delete(m) // m.geometry?.appliedMeshes?.delete(m) m.material = undefined; m.geometry = undefined; } // (inst.material as IMaterial).appliedMeshes?.add(inst) // inst.geometry.userData.__appliedMeshes.add(inst) // todo set position to center of all instances // @ts-expect-error todo not in ts inst.sourceTrs = { TRANSLATION: new BufferAttribute(translationAttr, 3), ROTATION: new BufferAttribute(rotationAttr, 4), SCALE: new BufferAttribute(scaleAttr, 3), }; inst.instanceMatrix.needsUpdate = true; parent.add(inst); parent.setDirty(); } } export class GLTFMeshGpuInstancingExporter { constructor(writer) { this.writer = writer; this.name = 'EXT_mesh_gpu_instancing'; } writeNode(object, nodeDef) { if (!object.isInstancedMesh) return; const writer = this.writer; const mesh = object; // @ts-expect-error not in ts let attributes = mesh.sourceTrs; if (!attributes) { const translationAttr = new Float32Array(mesh.count * 3); const rotationAttr = new Float32Array(mesh.count * 4); const scaleAttr = new Float32Array(mesh.count * 3); const matrix = new Matrix4(); const position = new Vector3(); const quaternion = new Quaternion(); const scale = new Vector3(); for (let i = 0; i < mesh.count; i++) { mesh.getMatrixAt(i, matrix); matrix.decompose(position, quaternion, scale); position.toArray(translationAttr, i * 3); quaternion.toArray(rotationAttr, i * 4); scale.toArray(scaleAttr, i * 3); } attributes = { TRANSLATION: new BufferAttribute(translationAttr, 3), ROTATION: new BufferAttribute(rotationAttr, 4), SCALE: new BufferAttribute(scaleAttr, 3), }; } attributes = { // @ts-expect-error todo add to ts TRANSLATION: writer.processAccessor(attributes.TRANSLATION), ROTATION: writer.processAccessor(attributes.ROTATION), SCALE: writer.processAccessor(attributes.SCALE), }; if (mesh.instanceColor) attributes._COLOR_0 = writer.processAccessor(mesh.instanceColor); writer.extensionsUsed[this.name] = true; // @ts-expect-error todo add to ts writer.extensionsRequired[this.name] = true; nodeDef.extensions = nodeDef.extensions || {}; nodeDef.extensions[this.name] = { attributes }; } } //# sourceMappingURL=gpu-instancing.js.map