109 lines
3.0 KiB
TypeScript
109 lines
3.0 KiB
TypeScript
import * as THREE from 'three';
|
|
import type { GeometryCache } from './geometryCache';
|
|
import { meshToDto, type MeshDto } from '../backend/dto/mesh';
|
|
import { model } from '../model/model';
|
|
import { Geometry } from '../backend/geometry/geometry';
|
|
import type { Id } from '../types';
|
|
|
|
export class SceneSync {
|
|
private scene: THREE.Scene;
|
|
private meshByFace: Record<Id, THREE.Mesh> = {}; // faceId → THREE.Mesh
|
|
private cache: GeometryCache;
|
|
|
|
private _selectedFaceIds: Id[] = [];
|
|
|
|
private readonly baseMaterial = new THREE.MeshPhongMaterial({
|
|
color: 0x4a7fc8, shininess: 40, specular: 0x223344, wireframe: false,
|
|
});
|
|
|
|
constructor(scene: THREE.Scene, cache: GeometryCache) {
|
|
this.scene = scene;
|
|
this.cache = cache;
|
|
}
|
|
|
|
public get selectedFaceIds() {
|
|
return this._selectedFaceIds;
|
|
}
|
|
|
|
public get meshes(): THREE.Mesh[] {
|
|
return Object.values(this.meshByFace);
|
|
}
|
|
|
|
public get items(): Record<Id, THREE.Mesh> {
|
|
return this.meshByFace;
|
|
}
|
|
|
|
addWholeModel() {
|
|
for (const id of Object.keys(model.solids)) {
|
|
const meshes = Geometry
|
|
.tessellateSolid(id)
|
|
.map(meshToDto);
|
|
this.addSolid(meshes[0]); // bottom
|
|
this.addSolid(meshes[3]); // front
|
|
// for (const mesh of meshes)
|
|
// this.addSolid(mesh);
|
|
}
|
|
}
|
|
|
|
// Called when FE scene graph syncs from BE
|
|
addSolid(dto: MeshDto) {
|
|
const faceId = dto.faceId;
|
|
|
|
if (this.meshByFace[faceId])
|
|
return;
|
|
|
|
const geo = this.cache.getOrCreate(faceId, 0, dto);
|
|
const mesh = new THREE.Mesh(geo, this.baseMaterial.clone());
|
|
mesh.userData.faceId = faceId;
|
|
mesh.userData.vertexIds = dto.vertexIds;
|
|
mesh.userData.surfaceId = dto.surfaceId;
|
|
mesh.userData.solidId = dto.solidId;
|
|
|
|
this.scene.add(mesh);
|
|
this.meshByFace[faceId] = mesh;
|
|
}
|
|
|
|
setSelected(faceIds: Id[]) {
|
|
this._selectedFaceIds = faceIds;
|
|
|
|
for (const [sid, mesh] of Object.entries(this.meshByFace)) {
|
|
const mat = mesh.material as THREE.MeshPhongMaterial;
|
|
|
|
if (faceIds.includes(sid)) {
|
|
mat.color.setHex(0xf0a040);
|
|
mat.emissive.setHex(0x221100);
|
|
}
|
|
else {
|
|
mat.color.setHex(0x4a7fc8);
|
|
mat.emissive.setHex(0x000000);
|
|
}
|
|
}
|
|
}
|
|
|
|
public disposeMesh(faceId: Id) {
|
|
const mesh = this.meshByFace[faceId];
|
|
if (!mesh)
|
|
return;
|
|
|
|
this.scene.remove(mesh);
|
|
|
|
mesh.geometry.dispose();
|
|
|
|
if (Array.isArray(mesh.material)) {
|
|
for (const mat of mesh.material)
|
|
mat.dispose();
|
|
}
|
|
else
|
|
mesh.material.dispose();
|
|
|
|
this.cache.unset(faceId, 0);
|
|
delete (this.meshByFace[faceId]);
|
|
}
|
|
|
|
public dispose() {
|
|
for (const faceId of Object.keys(this.meshByFace))
|
|
this.disposeMesh(faceId);
|
|
|
|
this.baseMaterial.dispose();
|
|
}
|
|
} |