Add model instance rendering functionality
This commit is contained in:
104
src/assets/model-instance-rendering.ts
Normal file
104
src/assets/model-instance-rendering.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { BoxGeometry, Group, Mesh, MeshBasicMaterial } from "three";
|
||||
|
||||
import type { Vec3 } from "../core/vector";
|
||||
|
||||
import { instantiateModelTemplate, type LoadedModelAsset } from "./gltf-model-import";
|
||||
import type { ModelInstance } from "./model-instances";
|
||||
import type { ProjectAssetRecord } from "./project-assets";
|
||||
|
||||
const MODEL_PLACEHOLDER_COLOR = 0x89b6ff;
|
||||
const MODEL_SELECTION_COLOR = 0xf7d2aa;
|
||||
|
||||
interface ModelInstanceBounds {
|
||||
center: Vec3;
|
||||
size: Vec3;
|
||||
}
|
||||
|
||||
function cloneVec3(vector: Vec3): Vec3 {
|
||||
return {
|
||||
x: vector.x,
|
||||
y: vector.y,
|
||||
z: vector.z
|
||||
};
|
||||
}
|
||||
|
||||
function getLocalModelBounds(asset: ProjectAssetRecord | undefined): ModelInstanceBounds {
|
||||
if (asset?.kind === "model" && asset.metadata.boundingBox !== null) {
|
||||
const boundingBox = asset.metadata.boundingBox;
|
||||
|
||||
return {
|
||||
center: {
|
||||
x: (boundingBox.min.x + boundingBox.max.x) * 0.5,
|
||||
y: (boundingBox.min.y + boundingBox.max.y) * 0.5,
|
||||
z: (boundingBox.min.z + boundingBox.max.z) * 0.5
|
||||
},
|
||||
size: {
|
||||
x: Math.max(0.1, Math.abs(boundingBox.max.x - boundingBox.min.x)),
|
||||
y: Math.max(0.1, Math.abs(boundingBox.max.y - boundingBox.min.y)),
|
||||
z: Math.max(0.1, Math.abs(boundingBox.max.z - boundingBox.min.z))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
center: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
size: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
z: 1
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createWireframeBox(size: Vec3, color: number, opacity: number): Mesh {
|
||||
return new Mesh(
|
||||
new BoxGeometry(size.x, size.y, size.z),
|
||||
new MeshBasicMaterial({
|
||||
color,
|
||||
wireframe: true,
|
||||
transparent: true,
|
||||
opacity,
|
||||
depthWrite: false
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function createModelInstanceRenderGroup(
|
||||
modelInstance: ModelInstance,
|
||||
asset: ProjectAssetRecord | undefined,
|
||||
loadedAsset: LoadedModelAsset | undefined,
|
||||
selected = false
|
||||
): Group {
|
||||
const bounds = getLocalModelBounds(asset);
|
||||
const group = new Group();
|
||||
|
||||
group.position.copy(cloneVec3(modelInstance.position));
|
||||
group.rotation.set(
|
||||
(modelInstance.rotationDegrees.x * Math.PI) / 180,
|
||||
(modelInstance.rotationDegrees.y * Math.PI) / 180,
|
||||
(modelInstance.rotationDegrees.z * Math.PI) / 180
|
||||
);
|
||||
group.scale.set(modelInstance.scale.x, modelInstance.scale.y, modelInstance.scale.z);
|
||||
group.userData.modelInstanceId = modelInstance.id;
|
||||
group.userData.assetId = modelInstance.assetId;
|
||||
|
||||
if (loadedAsset !== undefined) {
|
||||
group.add(instantiateModelTemplate(loadedAsset.template));
|
||||
} else {
|
||||
const placeholder = createWireframeBox(bounds.size, MODEL_PLACEHOLDER_COLOR, 0.28);
|
||||
placeholder.position.set(bounds.center.x, bounds.center.y, bounds.center.z);
|
||||
group.add(placeholder);
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
const selectionShell = createWireframeBox(bounds.size, MODEL_SELECTION_COLOR, 0.8);
|
||||
selectionShell.position.set(bounds.center.x, bounds.center.y, bounds.center.z);
|
||||
group.add(selectionShell);
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
Reference in New Issue
Block a user