Files
webeditor3d/src/commands/commit-transform-session-command.ts

424 lines
14 KiB
TypeScript
Raw Normal View History

import { createMoveBoxBrushCommand } from "./move-box-brush-command";
import { createApplyBatchSelectionTransformCommand } from "./apply-batch-selection-transform-command";
import { createResizeBoxBrushCommand } from "./resize-box-brush-command";
import { createRotateBoxBrushCommand } from "./rotate-box-brush-command";
import { createSetPathPointPositionCommand } from "./set-path-point-position-command";
import { createSetBoxBrushTransformCommand } from "./set-box-brush-transform-command";
import { createUpsertEntityCommand } from "./upsert-entity-command";
import { createUpsertModelInstanceCommand } from "./upsert-model-instance-command";
import type { EditorCommand } from "./command";
import type { SceneDocument } from "../document/scene-document";
import { createModelInstance } from "../assets/model-instances";
import { updateBrush, type Brush } from "../document/brushes";
import {
createCameraRigEntity,
createInteractableEntity,
createNpcEntity,
createPlayerStartEntity,
createPointLightEntity,
createSceneEntryEntity,
createSoundEmitterEntity,
createSpotLightEntity,
createTeleportTargetEntity,
createTriggerVolumeEntity
} from "../entities/entity-instances";
import {
getTransformOperationLabel,
type ActiveTransformSession
} from "../core/transform-session";
function createTransformCommandLabel(session: ActiveTransformSession): string {
let targetLabel = "model instance";
switch (session.target.kind) {
case "brush":
targetLabel = "whitebox box";
break;
case "brushes":
targetLabel =
session.target.items.length === 1
? "whitebox solid"
: `${session.target.items.length} whitebox solids`;
break;
case "brushFace":
targetLabel = "whitebox face";
break;
case "brushEdge":
targetLabel = "whitebox edge";
break;
case "brushVertex":
targetLabel = "whitebox vertex";
break;
case "pathPoint":
targetLabel = "path point";
break;
case "entity":
targetLabel =
session.target.entityKind === "cameraRig"
? "camera rig"
: session.target.entityKind === "playerStart"
? "player start"
: session.target.entityKind === "npc"
? "NPC"
: session.target.entityKind === "pointLight"
? "point light"
: session.target.entityKind === "spotLight"
? "spot light"
: session.target.entityKind === "soundEmitter"
? "sound emitter"
: session.target.entityKind === "triggerVolume"
? "trigger volume"
: session.target.entityKind === "sceneEntry"
? "scene entry"
: session.target.entityKind === "teleportTarget"
? "teleport target"
: session.target.entityKind === "interactable"
? "interactable"
: "entity";
break;
case "entities":
targetLabel =
session.target.items.length === 1
? "entity"
: `${session.target.items.length} entities`;
break;
case "modelInstance":
break;
case "modelInstances":
targetLabel =
session.target.items.length === 1
? "model instance"
: `${session.target.items.length} model instances`;
break;
}
return `${getTransformOperationLabel(session.operation)} ${targetLabel}`;
}
function createUpdatedEntityFromPreview(
entity: SceneDocument["entities"][string],
preview: {
position: { x: number; y: number; z: number };
rotation:
| { kind: "none" }
| { kind: "yaw"; yawDegrees: number }
| { kind: "direction"; direction: { x: number; y: number; z: number } }
| {
kind: "euler";
rotationDegrees: { x: number; y: number; z: number };
};
scale:
| { kind: "none" }
| { kind: "scale"; scale: { x: number; y: number; z: number } }
| { kind: "size"; size: { x: number; y: number; z: number } };
}
) {
switch (entity.kind) {
case "pointLight":
return createPointLightEntity({
...entity,
position: preview.position
});
case "cameraRig":
return entity.rigType === "fixed"
? createCameraRigEntity({
...entity,
position: preview.position
})
: createCameraRigEntity(entity);
case "spotLight":
return createSpotLightEntity({
...entity,
position: preview.position,
direction:
preview.rotation.kind === "direction"
? preview.rotation.direction
: entity.direction
});
case "playerStart":
return createPlayerStartEntity({
...entity,
position: preview.position,
yawDegrees:
preview.rotation.kind === "yaw"
? preview.rotation.yawDegrees
: entity.yawDegrees
});
case "sceneEntry":
return createSceneEntryEntity({
...entity,
position: preview.position,
yawDegrees:
preview.rotation.kind === "yaw"
? preview.rotation.yawDegrees
: entity.yawDegrees
});
case "npc":
return createNpcEntity({
...entity,
position: preview.position,
yawDegrees:
preview.rotation.kind === "yaw"
? preview.rotation.yawDegrees
: entity.yawDegrees,
scale:
preview.scale.kind === "scale" ? preview.scale.scale : entity.scale
});
case "soundEmitter":
return createSoundEmitterEntity({
...entity,
position: preview.position
});
case "triggerVolume":
return createTriggerVolumeEntity({
...entity,
position: preview.position,
rotationDegrees:
preview.rotation.kind === "euler"
? preview.rotation.rotationDegrees
: entity.rotationDegrees,
size: preview.scale.kind === "size" ? preview.scale.size : entity.size
});
case "teleportTarget":
return createTeleportTargetEntity({
...entity,
position: preview.position,
yawDegrees:
preview.rotation.kind === "yaw"
? preview.rotation.yawDegrees
: entity.yawDegrees
});
case "interactable":
return createInteractableEntity({
...entity,
position: preview.position
});
}
}
function createUpdatedBrushFromPreview(
brush: Brush,
preview: {
center: { x: number; y: number; z: number };
rotationDegrees: { x: number; y: number; z: number };
size: { x: number; y: number; z: number };
geometry: Brush["geometry"];
}
): Brush {
return updateBrush(brush, {
center: preview.center,
rotationDegrees: preview.rotationDegrees,
size: preview.size,
geometry: preview.geometry as never
});
}
export function createCommitTransformSessionCommand(
document: SceneDocument,
session: ActiveTransformSession
): EditorCommand {
switch (session.target.kind) {
case "brush": {
if (session.preview.kind !== "brush") {
throw new Error("Brush transform preview is invalid.");
}
switch (session.operation) {
case "translate":
return createMoveBoxBrushCommand({
brushId: session.target.brushId,
center: session.preview.center,
snapToGrid: false,
label: createTransformCommandLabel(session)
});
case "rotate":
return createRotateBoxBrushCommand({
brushId: session.target.brushId,
rotationDegrees: session.preview.rotationDegrees,
label: createTransformCommandLabel(session)
});
case "scale":
return createResizeBoxBrushCommand({
brushId: session.target.brushId,
size: session.preview.size,
snapToGrid: false,
label: createTransformCommandLabel(session)
});
}
throw new Error(`Unsupported transform operation: ${session.operation}.`);
}
case "brushes":
if (session.preview.kind !== "brushes") {
throw new Error("Whitebox multi-transform preview is invalid.");
}
return createApplyBatchSelectionTransformCommand({
selection: {
kind: "brushes",
ids: session.target.items.map((item) => item.brushId)
},
brushes: session.preview.items.map((item) => {
const brush = document.brushes[item.brushId];
if (brush === undefined) {
throw new Error(`Whitebox solid ${item.brushId} does not exist.`);
}
return createUpdatedBrushFromPreview(brush, item);
}),
label: createTransformCommandLabel(session)
});
case "brushFace":
if (session.preview.kind !== "brush") {
throw new Error("Whitebox face transform preview is invalid.");
}
return createSetBoxBrushTransformCommand({
selection: {
kind: "brushFace",
brushId: session.target.brushId,
faceId: session.target.faceId
},
center: session.preview.center,
rotationDegrees: session.preview.rotationDegrees,
size: session.preview.size,
geometry: session.preview.geometry,
label: createTransformCommandLabel(session)
});
case "brushEdge":
if (session.preview.kind !== "brush") {
throw new Error("Whitebox edge transform preview is invalid.");
}
return createSetBoxBrushTransformCommand({
selection: {
kind: "brushEdge",
brushId: session.target.brushId,
edgeId: session.target.edgeId
},
center: session.preview.center,
rotationDegrees: session.preview.rotationDegrees,
size: session.preview.size,
geometry: session.preview.geometry,
label: createTransformCommandLabel(session)
});
case "brushVertex":
if (session.preview.kind !== "brush") {
throw new Error("Whitebox vertex transform preview is invalid.");
}
return createSetBoxBrushTransformCommand({
selection: {
kind: "brushVertex",
brushId: session.target.brushId,
vertexId: session.target.vertexId
},
center: session.preview.center,
rotationDegrees: session.preview.rotationDegrees,
size: session.preview.size,
geometry: session.preview.geometry,
label: createTransformCommandLabel(session)
});
case "pathPoint":
if (session.preview.kind !== "pathPoint") {
throw new Error("Path point transform preview is invalid.");
}
return createSetPathPointPositionCommand({
pathId: session.target.pathId,
pointId: session.target.pointId,
position: session.preview.position,
label: createTransformCommandLabel(session)
});
case "modelInstance": {
if (session.preview.kind !== "modelInstance") {
throw new Error("Model instance transform preview is invalid.");
}
const modelInstance =
document.modelInstances[session.target.modelInstanceId];
if (modelInstance === undefined) {
throw new Error(
`Model instance ${session.target.modelInstanceId} does not exist.`
);
}
return createUpsertModelInstanceCommand({
modelInstance: createModelInstance({
...modelInstance,
position: session.preview.position,
rotationDegrees: session.preview.rotationDegrees,
scale: session.preview.scale
}),
label: createTransformCommandLabel(session)
});
}
case "modelInstances":
if (session.preview.kind !== "modelInstances") {
throw new Error("Model instance multi-transform preview is invalid.");
}
return createApplyBatchSelectionTransformCommand({
selection: {
kind: "modelInstances",
ids: session.target.items.map((item) => item.modelInstanceId)
},
modelInstances: session.preview.items.map((item) => {
const modelInstance = document.modelInstances[item.modelInstanceId];
if (modelInstance === undefined) {
throw new Error(
`Model instance ${item.modelInstanceId} does not exist.`
);
}
return createModelInstance({
...modelInstance,
position: item.position,
rotationDegrees: item.rotationDegrees,
scale: item.scale
});
}),
label: createTransformCommandLabel(session)
});
case "entity": {
if (session.preview.kind !== "entity") {
throw new Error("Entity transform preview is invalid.");
}
const entity = document.entities[session.target.entityId];
if (entity === undefined) {
throw new Error(`Entity ${session.target.entityId} does not exist.`);
}
return createUpsertEntityCommand({
entity: createUpdatedEntityFromPreview(entity, session.preview),
label: createTransformCommandLabel(session)
});
}
case "entities":
if (session.preview.kind !== "entities") {
throw new Error("Entity multi-transform preview is invalid.");
}
return createApplyBatchSelectionTransformCommand({
selection: {
kind: "entities",
ids: session.target.items.map((item) => item.entityId)
},
entities: session.preview.items.map((item) => {
const entity = document.entities[item.entityId];
if (entity === undefined) {
throw new Error(`Entity ${item.entityId} does not exist.`);
}
return createUpdatedEntityFromPreview(entity, item);
}),
label: createTransformCommandLabel(session)
});
}
}