Refactor viewport tool preview handling for creation and placement modes
This commit is contained in:
@@ -1303,28 +1303,18 @@ export class ViewportHost {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.toolMode === "box-create") {
|
if (this.toolMode === "create" && this.creationPreview !== null) {
|
||||||
const previewCenter = this.getBoxCreatePreviewCenter(event);
|
const previewCenter = this.getCreationPreviewCenter(event, this.creationPreview.target);
|
||||||
|
const nextCreationPreview = {
|
||||||
|
...this.creationPreview,
|
||||||
|
center: previewCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
this.syncCreationPreview(nextCreationPreview);
|
||||||
|
this.creationPreviewChangeHandler?.(nextCreationPreview);
|
||||||
|
|
||||||
if (previewCenter !== null) {
|
if (previewCenter !== null) {
|
||||||
this.createBoxBrushHandler?.(previewCenter);
|
this.creationCommitHandler?.(nextCreationPreview);
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.toolMode === "place" && this.placementPreview !== null) {
|
|
||||||
const previewCenter = this.getPlacementPreviewCenter(event);
|
|
||||||
|
|
||||||
if (previewCenter !== null) {
|
|
||||||
const nextPlacementPreview = {
|
|
||||||
...this.placementPreview,
|
|
||||||
center: previewCenter
|
|
||||||
} as PlacementViewportToolPreview;
|
|
||||||
|
|
||||||
this.syncPlacementPreview(nextPlacementPreview);
|
|
||||||
this.placementPreviewChangeHandler?.(nextPlacementPreview);
|
|
||||||
this.placementCommitHandler?.(nextPlacementPreview);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -1471,22 +1461,18 @@ export class ViewportHost {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.toolMode !== "box-create") {
|
if (this.toolMode !== "create" || this.creationPreview === null) {
|
||||||
if (this.toolMode !== "place" || this.placementPreview === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const previewCenter = this.getPlacementPreviewCenter(event);
|
|
||||||
const nextPlacementPreview = previewCenter === null ? { kind: "none" } : { ...this.placementPreview, center: previewCenter };
|
|
||||||
|
|
||||||
this.syncPlacementPreview(previewCenter === null ? null : (nextPlacementPreview as PlacementViewportToolPreview));
|
|
||||||
this.placementPreviewChangeHandler?.(nextPlacementPreview as ViewportToolPreview);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previewCenter = this.getBoxCreatePreviewCenter(event);
|
const previewCenter = this.getCreationPreviewCenter(event, this.creationPreview.target);
|
||||||
this.syncBoxCreatePreview(previewCenter);
|
const nextCreationPreview = {
|
||||||
this.boxCreatePreviewChangeHandler?.(previewCenter);
|
...this.creationPreview,
|
||||||
|
center: previewCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
this.syncCreationPreview(nextCreationPreview);
|
||||||
|
this.creationPreviewChangeHandler?.(nextCreationPreview);
|
||||||
};
|
};
|
||||||
|
|
||||||
private handlePointerUp = (event: PointerEvent) => {
|
private handlePointerUp = (event: PointerEvent) => {
|
||||||
@@ -1506,19 +1492,6 @@ export class ViewportHost {
|
|||||||
if (this.activeCameraDragPointerId !== null) {
|
if (this.activeCameraDragPointerId !== null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.toolMode !== "box-create" && this.toolMode !== "place") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.toolMode === "box-create") {
|
|
||||||
this.syncBoxCreatePreview(null);
|
|
||||||
this.boxCreatePreviewChangeHandler?.(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.syncPlacementPreview(null);
|
|
||||||
this.placementPreviewChangeHandler?.({ kind: "none" });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleWheel = (event: WheelEvent) => {
|
private handleWheel = (event: WheelEvent) => {
|
||||||
@@ -1606,47 +1579,41 @@ export class ViewportHost {
|
|||||||
this.applyOrthographicCameraPose();
|
this.applyOrthographicCameraPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getBoxCreatePreviewCenter(event: PointerEvent): Vec3 | null {
|
private getCreationPreviewCenter(event: PointerEvent, target: CreationTarget): Vec3 | null {
|
||||||
return this.getBoxPlacementPreviewCenter(event, DEFAULT_BOX_BRUSH_SIZE);
|
switch (target.kind) {
|
||||||
}
|
case "box-brush":
|
||||||
|
return this.getBoxCreationPreviewCenter(event, DEFAULT_BOX_BRUSH_SIZE);
|
||||||
|
case "entity":
|
||||||
|
switch (target.entityKind) {
|
||||||
|
case "triggerVolume":
|
||||||
|
return this.getBoxCreationPreviewCenter(event, DEFAULT_TRIGGER_VOLUME_SIZE);
|
||||||
|
case "pointLight":
|
||||||
|
case "playerStart":
|
||||||
|
case "soundEmitter":
|
||||||
|
case "teleportTarget":
|
||||||
|
case "interactable":
|
||||||
|
case "spotLight":
|
||||||
|
return this.getPlanarCreationAnchor(event);
|
||||||
|
}
|
||||||
|
case "model-instance": {
|
||||||
|
const anchor = this.getPlanarCreationAnchor(event);
|
||||||
|
|
||||||
private getPlacementPreviewCenter(event: PointerEvent): Vec3 | null {
|
if (anchor === null) {
|
||||||
if (this.placementPreview === null) {
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const target = this.placementPreview.target;
|
const asset = this.projectAssets[target.assetId];
|
||||||
|
|
||||||
if (target.kind === "model-instance") {
|
if (asset === undefined || asset.kind !== "model") {
|
||||||
const anchor = this.getPlanarPlacementAnchor(event);
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (anchor === null) {
|
return createModelInstancePlacementPosition(asset, anchor);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const asset = this.projectAssets[target.assetId];
|
|
||||||
|
|
||||||
if (asset === undefined || asset.kind !== "model") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return createModelInstancePlacementPosition(asset, anchor);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (target.entityKind) {
|
|
||||||
case "triggerVolume":
|
|
||||||
return this.getBoxPlacementPreviewCenter(event, DEFAULT_TRIGGER_VOLUME_SIZE);
|
|
||||||
case "pointLight":
|
|
||||||
case "playerStart":
|
|
||||||
case "soundEmitter":
|
|
||||||
case "teleportTarget":
|
|
||||||
case "interactable":
|
|
||||||
case "spotLight":
|
|
||||||
return this.getPlanarPlacementAnchor(event);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPlanarPlacementAnchor(event: PointerEvent): Vec3 | null {
|
private getPlanarCreationAnchor(event: PointerEvent): Vec3 | null {
|
||||||
const bounds = this.renderer.domElement.getBoundingClientRect();
|
const bounds = this.renderer.domElement.getBoundingClientRect();
|
||||||
|
|
||||||
if (bounds.width === 0 || bounds.height === 0) {
|
if (bounds.width === 0 || bounds.height === 0) {
|
||||||
@@ -1685,7 +1652,7 @@ export class ViewportHost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getBoxPlacementPreviewCenter(event: PointerEvent, size: Vec3): Vec3 | null {
|
private getBoxCreationPreviewCenter(event: PointerEvent, size: Vec3): Vec3 | null {
|
||||||
const bounds = this.renderer.domElement.getBoundingClientRect();
|
const bounds = this.renderer.domElement.getBoundingClientRect();
|
||||||
|
|
||||||
if (bounds.width === 0 || bounds.height === 0) {
|
if (bounds.width === 0 || bounds.height === 0) {
|
||||||
@@ -1724,49 +1691,30 @@ export class ViewportHost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private syncBoxCreatePreview(center: Vec3 | null) {
|
private getCreationPreviewTargetKey(target: CreationTarget): string {
|
||||||
if (
|
switch (target.kind) {
|
||||||
(center === null && this.lastBoxCreatePreviewCenter === null) ||
|
case "box-brush":
|
||||||
(center !== null &&
|
return "box-brush";
|
||||||
this.lastBoxCreatePreviewCenter !== null &&
|
case "entity":
|
||||||
center.x === this.lastBoxCreatePreviewCenter.x &&
|
return `entity:${target.entityKind}:${target.audioAssetId}`;
|
||||||
center.y === this.lastBoxCreatePreviewCenter.y &&
|
case "model-instance":
|
||||||
center.z === this.lastBoxCreatePreviewCenter.z)
|
return `model-instance:${target.assetId}`;
|
||||||
) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearCreationPreviewObject() {
|
||||||
|
if (this.creationPreviewObject === null) {
|
||||||
|
this.creationPreviewTargetKey = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastBoxCreatePreviewCenter = center === null ? null : { ...center };
|
this.scene.remove(this.creationPreviewObject);
|
||||||
this.boxCreatePreviewMesh.visible = center !== null;
|
disposeModelInstance(this.creationPreviewObject);
|
||||||
this.boxCreatePreviewEdges.visible = center !== null;
|
this.creationPreviewObject = null;
|
||||||
|
this.creationPreviewTargetKey = null;
|
||||||
if (center !== null) {
|
|
||||||
this.boxCreatePreviewMesh.position.set(center.x, center.y, center.z);
|
|
||||||
this.boxCreatePreviewEdges.position.set(center.x, center.y, center.z);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPlacementPreviewTargetKey(toolPreview: PlacementViewportToolPreview): string {
|
private createCreationPreviewObject(toolPreview: CreationViewportToolPreview): Group {
|
||||||
if (toolPreview.target.kind === "entity") {
|
|
||||||
return `entity:${toolPreview.target.entityKind}:${toolPreview.target.audioAssetId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `model-instance:${toolPreview.target.assetId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private clearPlacementPreviewObject() {
|
|
||||||
if (this.placementPreviewObject === null) {
|
|
||||||
this.placementPreviewTargetKey = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.scene.remove(this.placementPreviewObject);
|
|
||||||
disposeModelInstance(this.placementPreviewObject);
|
|
||||||
this.placementPreviewObject = null;
|
|
||||||
this.placementPreviewTargetKey = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private createPlacementPreviewObject(toolPreview: PlacementViewportToolPreview): Group {
|
|
||||||
const previewPosition = toolPreview.center ?? {
|
const previewPosition = toolPreview.center ?? {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
@@ -1774,11 +1722,16 @@ export class ViewportHost {
|
|||||||
};
|
};
|
||||||
|
|
||||||
switch (toolPreview.target.kind) {
|
switch (toolPreview.target.kind) {
|
||||||
|
case "box-brush": {
|
||||||
|
const fallbackGroup = new Group();
|
||||||
|
fallbackGroup.visible = false;
|
||||||
|
return fallbackGroup;
|
||||||
|
}
|
||||||
case "entity":
|
case "entity":
|
||||||
switch (toolPreview.target.entityKind) {
|
switch (toolPreview.target.entityKind) {
|
||||||
case "pointLight":
|
case "pointLight":
|
||||||
return this.createPointLightGizmoRenderObjects(
|
return this.createPointLightGizmoRenderObjects(
|
||||||
"placement-preview",
|
"creation-preview",
|
||||||
previewPosition,
|
previewPosition,
|
||||||
DEFAULT_POINT_LIGHT_DISTANCE,
|
DEFAULT_POINT_LIGHT_DISTANCE,
|
||||||
PLACEMENT_PREVIEW_COLOR_HEX,
|
PLACEMENT_PREVIEW_COLOR_HEX,
|
||||||
@@ -1786,7 +1739,7 @@ export class ViewportHost {
|
|||||||
).group;
|
).group;
|
||||||
case "spotLight":
|
case "spotLight":
|
||||||
return this.createSpotLightGizmoRenderObjects(
|
return this.createSpotLightGizmoRenderObjects(
|
||||||
"placement-preview",
|
"creation-preview",
|
||||||
previewPosition,
|
previewPosition,
|
||||||
DEFAULT_SPOT_LIGHT_DIRECTION,
|
DEFAULT_SPOT_LIGHT_DIRECTION,
|
||||||
DEFAULT_SPOT_LIGHT_DISTANCE,
|
DEFAULT_SPOT_LIGHT_DISTANCE,
|
||||||
@@ -1795,10 +1748,10 @@ export class ViewportHost {
|
|||||||
false
|
false
|
||||||
).group;
|
).group;
|
||||||
case "playerStart":
|
case "playerStart":
|
||||||
return this.createPlayerStartRenderObjects("placement-preview", previewPosition, DEFAULT_PLAYER_START_YAW_DEGREES, false).group;
|
return this.createPlayerStartRenderObjects("creation-preview", previewPosition, DEFAULT_PLAYER_START_YAW_DEGREES, false).group;
|
||||||
case "soundEmitter":
|
case "soundEmitter":
|
||||||
return this.createSoundEmitterRenderObjects(
|
return this.createSoundEmitterRenderObjects(
|
||||||
"placement-preview",
|
"creation-preview",
|
||||||
previewPosition,
|
previewPosition,
|
||||||
DEFAULT_SOUND_EMITTER_REF_DISTANCE,
|
DEFAULT_SOUND_EMITTER_REF_DISTANCE,
|
||||||
DEFAULT_SOUND_EMITTER_MAX_DISTANCE,
|
DEFAULT_SOUND_EMITTER_MAX_DISTANCE,
|
||||||
@@ -1807,7 +1760,7 @@ export class ViewportHost {
|
|||||||
).group;
|
).group;
|
||||||
case "triggerVolume":
|
case "triggerVolume":
|
||||||
return this.createTriggerVolumeRenderObjects(
|
return this.createTriggerVolumeRenderObjects(
|
||||||
"placement-preview",
|
"creation-preview",
|
||||||
previewPosition,
|
previewPosition,
|
||||||
DEFAULT_TRIGGER_VOLUME_SIZE,
|
DEFAULT_TRIGGER_VOLUME_SIZE,
|
||||||
false,
|
false,
|
||||||
@@ -1815,7 +1768,7 @@ export class ViewportHost {
|
|||||||
).group;
|
).group;
|
||||||
case "teleportTarget":
|
case "teleportTarget":
|
||||||
return this.createTeleportTargetRenderObjects(
|
return this.createTeleportTargetRenderObjects(
|
||||||
"placement-preview",
|
"creation-preview",
|
||||||
previewPosition,
|
previewPosition,
|
||||||
DEFAULT_TELEPORT_TARGET_YAW_DEGREES,
|
DEFAULT_TELEPORT_TARGET_YAW_DEGREES,
|
||||||
false,
|
false,
|
||||||
@@ -1823,7 +1776,7 @@ export class ViewportHost {
|
|||||||
).group;
|
).group;
|
||||||
case "interactable":
|
case "interactable":
|
||||||
return this.createInteractableRenderObjects(
|
return this.createInteractableRenderObjects(
|
||||||
"placement-preview",
|
"creation-preview",
|
||||||
previewPosition,
|
previewPosition,
|
||||||
DEFAULT_INTERACTABLE_RADIUS,
|
DEFAULT_INTERACTABLE_RADIUS,
|
||||||
false,
|
false,
|
||||||
@@ -1850,19 +1803,19 @@ export class ViewportHost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("Unsupported placement preview target.");
|
throw new Error("Unsupported creation preview target.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private syncPlacementPreview(toolPreview: PlacementViewportToolPreview | null) {
|
private syncCreationPreview(toolPreview: CreationViewportToolPreview | null) {
|
||||||
const currentToolPreview = this.placementPreview === null ? { kind: "none" as const } : this.placementPreview;
|
const currentToolPreview = this.creationPreview === null ? { kind: "none" as const } : this.creationPreview;
|
||||||
const nextToolPreview = toolPreview === null ? { kind: "none" as const } : toolPreview;
|
const nextToolPreview = toolPreview === null ? { kind: "none" as const } : toolPreview;
|
||||||
|
|
||||||
if (areViewportToolPreviewsEqual(currentToolPreview, nextToolPreview)) {
|
if (areViewportToolPreviewsEqual(currentToolPreview, nextToolPreview)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.placementPreview = toolPreview === null ? null : {
|
this.creationPreview = toolPreview === null ? null : {
|
||||||
kind: "placement",
|
kind: "create",
|
||||||
sourcePanelId: toolPreview.sourcePanelId,
|
sourcePanelId: toolPreview.sourcePanelId,
|
||||||
target:
|
target:
|
||||||
toolPreview.target.kind === "entity"
|
toolPreview.target.kind === "entity"
|
||||||
@@ -1874,34 +1827,56 @@ export class ViewportHost {
|
|||||||
: {
|
: {
|
||||||
kind: "model-instance",
|
kind: "model-instance",
|
||||||
assetId: toolPreview.target.assetId
|
assetId: toolPreview.target.assetId
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
kind: "box-brush"
|
||||||
},
|
},
|
||||||
center: toolPreview.center === null ? null : { ...toolPreview.center }
|
center: toolPreview.center === null ? null : { ...toolPreview.center }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (toolPreview === null) {
|
if (toolPreview === null) {
|
||||||
this.clearPlacementPreviewObject();
|
this.boxCreatePreviewMesh.visible = false;
|
||||||
|
this.boxCreatePreviewEdges.visible = false;
|
||||||
|
this.clearCreationPreviewObject();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextTargetKey = this.getPlacementPreviewTargetKey(toolPreview);
|
if (toolPreview.target.kind === "box-brush") {
|
||||||
|
this.boxCreatePreviewMesh.visible = toolPreview.center !== null;
|
||||||
if (this.placementPreviewObject !== null && this.placementPreviewTargetKey === nextTargetKey) {
|
this.boxCreatePreviewEdges.visible = toolPreview.center !== null;
|
||||||
this.placementPreviewObject.visible = toolPreview.center !== null;
|
|
||||||
|
|
||||||
if (toolPreview.center !== null) {
|
if (toolPreview.center !== null) {
|
||||||
this.placementPreviewObject.position.set(toolPreview.center.x, toolPreview.center.y, toolPreview.center.z);
|
this.boxCreatePreviewMesh.position.set(toolPreview.center.x, toolPreview.center.y, toolPreview.center.z);
|
||||||
|
this.boxCreatePreviewEdges.position.set(toolPreview.center.x, toolPreview.center.y, toolPreview.center.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.placementPreviewTargetKey = nextTargetKey;
|
this.clearCreationPreviewObject();
|
||||||
|
this.creationPreviewTargetKey = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clearPlacementPreviewObject();
|
const nextTargetKey = this.getCreationPreviewTargetKey(toolPreview.target);
|
||||||
|
|
||||||
const placementPreviewObject = this.createPlacementPreviewObject(toolPreview);
|
this.boxCreatePreviewMesh.visible = false;
|
||||||
placementPreviewObject.visible = toolPreview.center !== null;
|
this.boxCreatePreviewEdges.visible = false;
|
||||||
this.scene.add(placementPreviewObject);
|
|
||||||
this.placementPreviewObject = placementPreviewObject;
|
if (this.creationPreviewObject !== null && this.creationPreviewTargetKey === nextTargetKey) {
|
||||||
|
this.creationPreviewObject.visible = toolPreview.center !== null;
|
||||||
|
|
||||||
|
if (toolPreview.center !== null) {
|
||||||
|
this.creationPreviewObject.position.set(toolPreview.center.x, toolPreview.center.y, toolPreview.center.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.creationPreviewTargetKey = nextTargetKey;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clearCreationPreviewObject();
|
||||||
|
|
||||||
|
const creationPreviewObject = this.createCreationPreviewObject(toolPreview);
|
||||||
|
creationPreviewObject.visible = toolPreview.center !== null;
|
||||||
|
this.scene.add(creationPreviewObject);
|
||||||
|
this.creationPreviewObject = creationPreviewObject;
|
||||||
this.placementPreviewTargetKey = nextTargetKey;
|
this.placementPreviewTargetKey = nextTargetKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user