Add wireframe render mode for model instances and viewport display
This commit is contained in:
@@ -10,6 +10,8 @@ const MODEL_PLACEHOLDER_COLOR = 0x89b6ff;
|
||||
const MODEL_SELECTION_COLOR = 0xf7d2aa;
|
||||
const MODEL_PREVIEW_SHELL_OPACITY = 0.5;
|
||||
|
||||
export type ModelInstanceRenderMode = "normal" | "wireframe";
|
||||
|
||||
interface ModelInstanceBounds {
|
||||
center: Vec3;
|
||||
size: Vec3;
|
||||
@@ -60,6 +62,40 @@ function createWireframeBox(size: Vec3, color: number, opacity: number): Mesh {
|
||||
);
|
||||
}
|
||||
|
||||
function createWireframeMaterial(material: Material): MeshBasicMaterial {
|
||||
const source = material as Material & {
|
||||
color?: { getHex(): number };
|
||||
opacity?: number;
|
||||
transparent?: boolean;
|
||||
};
|
||||
const opacity = typeof source.opacity === "number" ? source.opacity : 1;
|
||||
|
||||
return new MeshBasicMaterial({
|
||||
color: source.color?.getHex() ?? MODEL_PLACEHOLDER_COLOR,
|
||||
wireframe: true,
|
||||
transparent: source.transparent === true || opacity < 1,
|
||||
opacity,
|
||||
depthWrite: false
|
||||
});
|
||||
}
|
||||
|
||||
function applyWireframeMaterialPresentation(group: Group) {
|
||||
group.traverse((object) => {
|
||||
const maybeMesh = object as Mesh & { isMesh?: boolean };
|
||||
|
||||
if (maybeMesh.isMesh !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(maybeMesh.material)) {
|
||||
maybeMesh.material = maybeMesh.material.map((material) => createWireframeMaterial(material));
|
||||
return;
|
||||
}
|
||||
|
||||
maybeMesh.material = createWireframeMaterial(maybeMesh.material);
|
||||
});
|
||||
}
|
||||
|
||||
function disposeTexture(texture: Texture, seenTextures: Set<Texture>) {
|
||||
if (seenTextures.has(texture)) {
|
||||
return;
|
||||
@@ -118,7 +154,8 @@ export function createModelInstanceRenderGroup(
|
||||
asset: ProjectAssetRecord | undefined,
|
||||
loadedAsset: LoadedModelAsset | undefined,
|
||||
selected = false,
|
||||
previewShellColor?: number
|
||||
previewShellColor?: number,
|
||||
renderMode: ModelInstanceRenderMode = "normal"
|
||||
): Group {
|
||||
const bounds = getLocalModelBounds(asset);
|
||||
const group = new Group();
|
||||
@@ -134,7 +171,13 @@ export function createModelInstanceRenderGroup(
|
||||
group.userData.assetId = modelInstance.assetId;
|
||||
|
||||
if (loadedAsset !== undefined) {
|
||||
group.add(instantiateModelTemplate(loadedAsset.template));
|
||||
const instantiatedModel = instantiateModelTemplate(loadedAsset.template);
|
||||
|
||||
if (renderMode === "wireframe") {
|
||||
applyWireframeMaterialPresentation(instantiatedModel);
|
||||
}
|
||||
|
||||
group.add(instantiatedModel);
|
||||
} else {
|
||||
const placeholder = createWireframeBox(bounds.size, previewShellColor ?? MODEL_PLACEHOLDER_COLOR, previewShellColor === undefined ? 0.28 : MODEL_PREVIEW_SHELL_OPACITY);
|
||||
placeholder.position.set(bounds.center.x, bounds.center.y, bounds.center.z);
|
||||
|
||||
@@ -700,6 +700,33 @@ export class ViewportHost {
|
||||
this.applyOrthographicCameraPose();
|
||||
}
|
||||
|
||||
private createWireframeDisplayMaterial(material: MeshStandardMaterial | MeshBasicMaterial): MeshBasicMaterial {
|
||||
return new MeshBasicMaterial({
|
||||
color: material.color.getHex(),
|
||||
wireframe: true,
|
||||
transparent: material.transparent === true || material.opacity < 1,
|
||||
opacity: material.opacity,
|
||||
depthWrite: false
|
||||
});
|
||||
}
|
||||
|
||||
private applyWireframePresentation(object: Object3D) {
|
||||
object.traverse((child) => {
|
||||
const maybeMesh = child as Mesh & { isMesh?: boolean };
|
||||
|
||||
if (maybeMesh.isMesh !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(maybeMesh.material)) {
|
||||
maybeMesh.material = maybeMesh.material.map((material) => this.createWireframeDisplayMaterial(material));
|
||||
return;
|
||||
}
|
||||
|
||||
maybeMesh.material = this.createWireframeDisplayMaterial(maybeMesh.material);
|
||||
});
|
||||
}
|
||||
|
||||
private getBoxCreatePlane() {
|
||||
switch (this.viewMode) {
|
||||
case "perspective":
|
||||
@@ -721,7 +748,7 @@ export class ViewportHost {
|
||||
|
||||
const world = this.currentWorld;
|
||||
const rendererSettings =
|
||||
this.displayMode === "authoring"
|
||||
this.displayMode !== "normal"
|
||||
? {
|
||||
...cloneAdvancedRenderingSettings(world.advancedRendering),
|
||||
enabled: false
|
||||
@@ -732,8 +759,11 @@ export class ViewportHost {
|
||||
this.sunLight.color.set(world.sunLight.colorHex);
|
||||
this.sunLight.intensity = world.sunLight.intensity;
|
||||
this.sunLight.position.set(world.sunLight.direction.x, world.sunLight.direction.y, world.sunLight.direction.z).normalize().multiplyScalar(18);
|
||||
this.ambientLight.visible = this.displayMode !== "wireframe";
|
||||
this.sunLight.visible = this.displayMode !== "wireframe";
|
||||
this.localLightGroup.visible = this.displayMode !== "wireframe";
|
||||
|
||||
if (this.displayMode === "authoring") {
|
||||
if (this.displayMode !== "normal") {
|
||||
this.scene.background = null;
|
||||
this.scene.environment = null;
|
||||
this.scene.environmentIntensity = 1;
|
||||
@@ -1675,6 +1705,10 @@ export class ViewportHost {
|
||||
const selected = selection.kind === "entities" && selection.ids.includes(entity.id);
|
||||
const renderObjects = this.createEntityRenderObjects(entity, selected);
|
||||
|
||||
if (this.displayMode === "wireframe") {
|
||||
this.applyWireframePresentation(renderObjects.group);
|
||||
}
|
||||
|
||||
this.entityGroup.add(renderObjects.group);
|
||||
this.entityRenderObjects.set(entity.id, renderObjects);
|
||||
}
|
||||
@@ -1687,7 +1721,14 @@ export class ViewportHost {
|
||||
const selected = isModelInstanceSelected(selection, modelInstance.id);
|
||||
const asset = this.projectAssets[modelInstance.assetId];
|
||||
const loadedAsset = this.loadedModelAssets[modelInstance.assetId];
|
||||
const renderGroup = createModelInstanceRenderGroup(modelInstance, asset, loadedAsset, selected);
|
||||
const renderGroup = createModelInstanceRenderGroup(
|
||||
modelInstance,
|
||||
asset,
|
||||
loadedAsset,
|
||||
selected,
|
||||
undefined,
|
||||
this.displayMode === "wireframe" ? "wireframe" : "normal"
|
||||
);
|
||||
|
||||
if (asset?.kind === "model" && modelInstance.collision.visible) {
|
||||
try {
|
||||
@@ -2175,6 +2216,25 @@ export class ViewportHost {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.displayMode === "wireframe") {
|
||||
const colorHex =
|
||||
material === undefined || face.materialId === null
|
||||
? selectedFace
|
||||
? SELECTED_FACE_FALLBACK_COLOR
|
||||
: FALLBACK_FACE_COLOR
|
||||
: selectedFace
|
||||
? material.accentColorHex
|
||||
: material.baseColorHex;
|
||||
|
||||
return new MeshBasicMaterial({
|
||||
color: colorHex,
|
||||
wireframe: true,
|
||||
transparent: true,
|
||||
opacity: selectedFace ? 0.95 : 0.76,
|
||||
depthWrite: false
|
||||
});
|
||||
}
|
||||
|
||||
if (material === undefined || face.materialId === null) {
|
||||
return new MeshStandardMaterial({
|
||||
color: selectedFace ? SELECTED_FACE_FALLBACK_COLOR : FALLBACK_FACE_COLOR,
|
||||
|
||||
Reference in New Issue
Block a user