Optimize placement preview handling in ViewportHost

This commit is contained in:
2026-04-02 23:58:12 +02:00
parent 3dd75e3496
commit 30de189cfa

View File

@@ -89,7 +89,11 @@ import {
type ViewportViewMode type ViewportViewMode
} from "./viewport-view-modes"; } from "./viewport-view-modes";
import type { ViewportDisplayMode } from "./viewport-layout"; import type { ViewportDisplayMode } from "./viewport-layout";
import type { PlacementViewportToolPreview, ViewportToolPreview } from "./viewport-transient-state"; import {
areViewportToolPreviewsEqual,
type PlacementViewportToolPreview,
type ViewportToolPreview
} from "./viewport-transient-state";
interface BrushRenderObjects { interface BrushRenderObjects {
mesh: Mesh<BoxGeometry, Array<MeshStandardMaterial | MeshBasicMaterial>>; mesh: Mesh<BoxGeometry, Array<MeshStandardMaterial | MeshBasicMaterial>>;
@@ -113,6 +117,7 @@ const INTERACTABLE_COLOR = 0x92de7e;
const INTERACTABLE_SELECTED_COLOR = 0xf1cf7e; const INTERACTABLE_SELECTED_COLOR = 0xf1cf7e;
const BOX_CREATE_PREVIEW_FILL = 0x89b6ff; const BOX_CREATE_PREVIEW_FILL = 0x89b6ff;
const BOX_CREATE_PREVIEW_EDGE = 0xf3be8f; const BOX_CREATE_PREVIEW_EDGE = 0xf3be8f;
const PLACEMENT_PREVIEW_COLOR_HEX = "#89b6ff";
const MIN_CAMERA_DISTANCE = 1.5; const MIN_CAMERA_DISTANCE = 1.5;
const MAX_CAMERA_DISTANCE = 400; const MAX_CAMERA_DISTANCE = 400;
const ORBIT_ROTATION_SPEED = 0.0085; const ORBIT_ROTATION_SPEED = 0.0085;
@@ -211,7 +216,6 @@ export class ViewportHost {
private displayMode: ViewportDisplayMode = "normal"; private displayMode: ViewportDisplayMode = "normal";
private lastBoxCreatePreviewCenter: Vec3 | null = null; private lastBoxCreatePreviewCenter: Vec3 | null = null;
private placementPreview: PlacementViewportToolPreview | null = null; private placementPreview: PlacementViewportToolPreview | null = null;
private placementPreviewTargetKey: string | null = null;
private placementPreviewObject: Group | null = null; private placementPreviewObject: Group | null = null;
private activeCameraDragPointerId: number | null = null; private activeCameraDragPointerId: number | null = null;
private lastCameraDragClientPosition: { x: number; y: number } | null = null; private lastCameraDragClientPosition: { x: number; y: number } | null = null;
@@ -1771,14 +1775,12 @@ export class ViewportHost {
private clearPlacementPreviewObject() { private clearPlacementPreviewObject() {
if (this.placementPreviewObject === null) { if (this.placementPreviewObject === null) {
this.placementPreviewTargetKey = null;
return; return;
} }
this.scene.remove(this.placementPreviewObject); this.scene.remove(this.placementPreviewObject);
disposeModelInstance(this.placementPreviewObject); disposeModelInstance(this.placementPreviewObject);
this.placementPreviewObject = null; this.placementPreviewObject = null;
this.placementPreviewTargetKey = null;
} }
private createPlacementPreviewObject(toolPreview: PlacementViewportToolPreview): Group { private createPlacementPreviewObject(toolPreview: PlacementViewportToolPreview): Group {
@@ -1796,7 +1798,7 @@ export class ViewportHost {
"placement-preview", "placement-preview",
previewPosition, previewPosition,
DEFAULT_POINT_LIGHT_DISTANCE, DEFAULT_POINT_LIGHT_DISTANCE,
BOX_CREATE_PREVIEW_FILL, PLACEMENT_PREVIEW_COLOR_HEX,
false false
).group; ).group;
case "spotLight": case "spotLight":
@@ -1806,7 +1808,7 @@ export class ViewportHost {
DEFAULT_SPOT_LIGHT_DIRECTION, DEFAULT_SPOT_LIGHT_DIRECTION,
DEFAULT_SPOT_LIGHT_DISTANCE, DEFAULT_SPOT_LIGHT_DISTANCE,
DEFAULT_SPOT_LIGHT_ANGLE_DEGREES, DEFAULT_SPOT_LIGHT_ANGLE_DEGREES,
BOX_CREATE_PREVIEW_FILL, PLACEMENT_PREVIEW_COLOR_HEX,
false false
).group; ).group;
case "playerStart": case "playerStart":
@@ -1818,7 +1820,7 @@ export class ViewportHost {
DEFAULT_SOUND_EMITTER_REF_DISTANCE, DEFAULT_SOUND_EMITTER_REF_DISTANCE,
DEFAULT_SOUND_EMITTER_MAX_DISTANCE, DEFAULT_SOUND_EMITTER_MAX_DISTANCE,
false, false,
BOX_CREATE_PREVIEW_FILL PLACEMENT_PREVIEW_COLOR_HEX
).group; ).group;
case "triggerVolume": case "triggerVolume":
return this.createTriggerVolumeRenderObjects( return this.createTriggerVolumeRenderObjects(
@@ -1826,7 +1828,7 @@ export class ViewportHost {
previewPosition, previewPosition,
DEFAULT_TRIGGER_VOLUME_SIZE, DEFAULT_TRIGGER_VOLUME_SIZE,
false, false,
BOX_CREATE_PREVIEW_FILL PLACEMENT_PREVIEW_COLOR_HEX
).group; ).group;
case "teleportTarget": case "teleportTarget":
return this.createTeleportTargetRenderObjects( return this.createTeleportTargetRenderObjects(
@@ -1834,7 +1836,7 @@ export class ViewportHost {
previewPosition, previewPosition,
DEFAULT_TELEPORT_TARGET_YAW_DEGREES, DEFAULT_TELEPORT_TARGET_YAW_DEGREES,
false, false,
BOX_CREATE_PREVIEW_FILL PLACEMENT_PREVIEW_COLOR_HEX
).group; ).group;
case "interactable": case "interactable":
return this.createInteractableRenderObjects( return this.createInteractableRenderObjects(
@@ -1842,7 +1844,7 @@ export class ViewportHost {
previewPosition, previewPosition,
DEFAULT_INTERACTABLE_RADIUS, DEFAULT_INTERACTABLE_RADIUS,
false, false,
BOX_CREATE_PREVIEW_FILL PLACEMENT_PREVIEW_COLOR_HEX
).group; ).group;
} }
case "model-instance": { case "model-instance": {
@@ -1864,6 +1866,8 @@ export class ViewportHost {
return createModelInstanceRenderGroup(dummyModelInstance, asset, undefined, false); return createModelInstanceRenderGroup(dummyModelInstance, asset, undefined, false);
} }
} }
throw new Error("Unsupported placement preview target.");
} }
private syncPlacementPreview(toolPreview: PlacementViewportToolPreview | null) { private syncPlacementPreview(toolPreview: PlacementViewportToolPreview | null) {
@@ -1891,17 +1895,31 @@ export class ViewportHost {
center: toolPreview.center === null ? null : { ...toolPreview.center } center: toolPreview.center === null ? null : { ...toolPreview.center }
}; };
this.clearPlacementPreviewObject();
if (toolPreview === null) { if (toolPreview === null) {
this.clearPlacementPreviewObject();
return; return;
} }
const nextTargetKey = this.getPlacementPreviewTargetKey(toolPreview);
if (this.placementPreviewObject !== null && this.placementPreviewTargetKey === nextTargetKey) {
this.placementPreviewObject.visible = toolPreview.center !== null;
if (toolPreview.center !== null) {
this.placementPreviewObject.position.set(toolPreview.center.x, toolPreview.center.y, toolPreview.center.z);
}
this.placementPreviewTargetKey = nextTargetKey;
return;
}
this.clearPlacementPreviewObject();
const placementPreviewObject = this.createPlacementPreviewObject(toolPreview); const placementPreviewObject = this.createPlacementPreviewObject(toolPreview);
placementPreviewObject.visible = toolPreview.center !== null; placementPreviewObject.visible = toolPreview.center !== null;
this.scene.add(placementPreviewObject); this.scene.add(placementPreviewObject);
this.placementPreviewObject = placementPreviewObject; this.placementPreviewObject = placementPreviewObject;
this.placementPreviewTargetKey = this.getPlacementPreviewTargetKey(toolPreview); this.placementPreviewTargetKey = nextTargetKey;
} }
private render = () => { private render = () => {