layered three view
game object geometries layer
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import * as THREE from 'three';
|
||||
import { MobxReconcilerMap, type ReconcilerMap } from '../utils/reconciler';
|
||||
import type { ObjectGeometryViewItem, ObjectTypeGeometryViewItem } from '../state/viewState';
|
||||
|
||||
export class ObjectGeometryList {
|
||||
private items: ReconcilerMap<ObjectGeometryViewItem>;
|
||||
private readonly typeRefCounts = new Map<string, number>();
|
||||
|
||||
private readonly matrixObj = new THREE.Object3D();
|
||||
private readonly _p = new THREE.Vector3();
|
||||
private readonly _e = new THREE.Euler();
|
||||
private readonly _q = new THREE.Quaternion();
|
||||
|
||||
constructor(scene: THREE.Scene, geometiries: Iterable<ObjectGeometryViewItem>) {
|
||||
this.items = new MobxReconcilerMap<ObjectGeometryViewItem>({
|
||||
reaction: () => geometiries,
|
||||
keyOf: (item) => item.object.id,
|
||||
initializeItem: (item) => {
|
||||
const typeId = item.objectTypeGeometry.objectType.id;
|
||||
const count = (this.typeRefCounts.get(typeId) ?? 0) + 1;
|
||||
this.typeRefCounts.set(typeId, count);
|
||||
if (count === 1) {
|
||||
for (const mesh of item.objectTypeGeometry.meshes ?? [])
|
||||
scene.add(mesh);
|
||||
}
|
||||
},
|
||||
disposeItem: (item) => {
|
||||
const typeId = item.objectTypeGeometry.objectType.id;
|
||||
const count = (this.typeRefCounts.get(typeId) ?? 1) - 1;
|
||||
this.typeRefCounts.set(typeId, count);
|
||||
if (count === 0) {
|
||||
for (const mesh of item.objectTypeGeometry.meshes ?? [])
|
||||
scene.remove(mesh);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.items?.dispose();
|
||||
}
|
||||
|
||||
// Called once per frame — reads MobX observables directly outside any reactive
|
||||
// context, so no subscriptions are created and no re-renders are triggered.
|
||||
tick(): void {
|
||||
const touchedObjectTypes: Record<string, { typeGeometry: ObjectTypeGeometryViewItem, instanceCount: number }> = {};
|
||||
|
||||
this.items.values.forEach((item) => {
|
||||
const obj = item.object;
|
||||
|
||||
this._p.set(obj.position[0], obj.position[1], obj.position[2]);
|
||||
this._e.set(obj.rotation[0], obj.rotation[1], obj.rotation[2]);
|
||||
this._q.setFromEuler(this._e);
|
||||
|
||||
const typeId = item.objectTypeGeometry.objectType.id;
|
||||
if (!touchedObjectTypes[typeId])
|
||||
touchedObjectTypes[typeId] = { typeGeometry: item.objectTypeGeometry, instanceCount: 0 };
|
||||
const instanceIdx = touchedObjectTypes[typeId].instanceCount++;
|
||||
|
||||
for (const mesh of item.objectTypeGeometry.meshes ?? []) {
|
||||
this.matrixObj.position.copy(this._p);
|
||||
this.matrixObj.quaternion.copy(this._q);
|
||||
this.matrixObj.scale.set(obj.scale[0], obj.scale[1], obj.scale[2]);
|
||||
this.matrixObj.updateMatrix();
|
||||
mesh.setMatrixAt(instanceIdx, this.matrixObj.matrix);
|
||||
}
|
||||
});
|
||||
|
||||
for (const objectType of Object.values(touchedObjectTypes)) {
|
||||
for (const mesh of objectType.typeGeometry.meshes ?? []) {
|
||||
mesh.count = objectType.instanceCount;
|
||||
mesh.instanceMatrix.needsUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user