Enhance transform session handling by including geometry and update viewport rendering logic accordingly

This commit is contained in:
2026-04-05 03:05:58 +02:00
parent eb07782253
commit acc225aea9
3 changed files with 176 additions and 166 deletions

View File

@@ -1,7 +1,14 @@
import { createOpaqueId } from "./ids";
import { BOX_EDGE_LABELS, BOX_FACE_LABELS, BOX_VERTEX_LABELS } from "../document/brushes";
import { BOX_VERTEX_IDS, BOX_EDGE_LABELS, BOX_FACE_LABELS, BOX_VERTEX_LABELS, cloneBoxBrushGeometry } from "../document/brushes";
import { cloneEntityInstance, getEntityKindLabel } from "../entities/entity-instances";
import { cloneModelInstance, getModelInstanceKindLabel } from "../assets/model-instances";
function areBrushGeometriesEqual(left, right) {
return BOX_VERTEX_IDS.every((vertexId) => {
const leftVertex = left.vertices[vertexId];
const rightVertex = right.vertices[vertexId];
return areVec3Equal(leftVertex, rightVertex);
});
}
function cloneVec3(vector) {
return {
x: vector.x,
@@ -56,7 +63,8 @@ export function cloneTransformTarget(target) {
brushId: target.brushId,
initialCenter: cloneVec3(target.initialCenter),
initialRotationDegrees: cloneVec3(target.initialRotationDegrees),
initialSize: cloneVec3(target.initialSize)
initialSize: cloneVec3(target.initialSize),
initialGeometry: cloneBoxBrushGeometry(target.initialGeometry)
};
case "brushFace":
return {
@@ -65,7 +73,8 @@ export function cloneTransformTarget(target) {
faceId: target.faceId,
initialCenter: cloneVec3(target.initialCenter),
initialRotationDegrees: cloneVec3(target.initialRotationDegrees),
initialSize: cloneVec3(target.initialSize)
initialSize: cloneVec3(target.initialSize),
initialGeometry: cloneBoxBrushGeometry(target.initialGeometry)
};
case "brushEdge":
return {
@@ -74,7 +83,8 @@ export function cloneTransformTarget(target) {
edgeId: target.edgeId,
initialCenter: cloneVec3(target.initialCenter),
initialRotationDegrees: cloneVec3(target.initialRotationDegrees),
initialSize: cloneVec3(target.initialSize)
initialSize: cloneVec3(target.initialSize),
initialGeometry: cloneBoxBrushGeometry(target.initialGeometry)
};
case "brushVertex":
return {
@@ -83,7 +93,8 @@ export function cloneTransformTarget(target) {
vertexId: target.vertexId,
initialCenter: cloneVec3(target.initialCenter),
initialRotationDegrees: cloneVec3(target.initialRotationDegrees),
initialSize: cloneVec3(target.initialSize)
initialSize: cloneVec3(target.initialSize),
initialGeometry: cloneBoxBrushGeometry(target.initialGeometry)
};
case "modelInstance":
return {
@@ -111,7 +122,8 @@ export function cloneTransformPreview(preview) {
kind: "brush",
center: cloneVec3(preview.center),
rotationDegrees: cloneVec3(preview.rotationDegrees),
size: cloneVec3(preview.size)
size: cloneVec3(preview.size),
geometry: cloneBoxBrushGeometry(preview.geometry)
};
case "modelInstance":
return {
@@ -168,28 +180,32 @@ function areTransformTargetsEqual(left, right) {
left.brushId === right.brushId &&
areVec3Equal(left.initialCenter, right.initialCenter) &&
areVec3Equal(left.initialRotationDegrees, right.initialRotationDegrees) &&
areVec3Equal(left.initialSize, right.initialSize));
areVec3Equal(left.initialSize, right.initialSize) &&
areBrushGeometriesEqual(left.initialGeometry, right.initialGeometry));
case "brushFace":
return (right.kind === "brushFace" &&
left.brushId === right.brushId &&
left.faceId === right.faceId &&
areVec3Equal(left.initialCenter, right.initialCenter) &&
areVec3Equal(left.initialRotationDegrees, right.initialRotationDegrees) &&
areVec3Equal(left.initialSize, right.initialSize));
areVec3Equal(left.initialSize, right.initialSize) &&
areBrushGeometriesEqual(left.initialGeometry, right.initialGeometry));
case "brushEdge":
return (right.kind === "brushEdge" &&
left.brushId === right.brushId &&
left.edgeId === right.edgeId &&
areVec3Equal(left.initialCenter, right.initialCenter) &&
areVec3Equal(left.initialRotationDegrees, right.initialRotationDegrees) &&
areVec3Equal(left.initialSize, right.initialSize));
areVec3Equal(left.initialSize, right.initialSize) &&
areBrushGeometriesEqual(left.initialGeometry, right.initialGeometry));
case "brushVertex":
return (right.kind === "brushVertex" &&
left.brushId === right.brushId &&
left.vertexId === right.vertexId &&
areVec3Equal(left.initialCenter, right.initialCenter) &&
areVec3Equal(left.initialRotationDegrees, right.initialRotationDegrees) &&
areVec3Equal(left.initialSize, right.initialSize));
areVec3Equal(left.initialSize, right.initialSize) &&
areBrushGeometriesEqual(left.initialGeometry, right.initialGeometry));
case "modelInstance":
return (right.kind === "modelInstance" &&
left.modelInstanceId === right.modelInstanceId &&
@@ -214,7 +230,8 @@ function areTransformPreviewsEqual(left, right) {
return (right.kind === "brush" &&
areVec3Equal(left.center, right.center) &&
areVec3Equal(left.rotationDegrees, right.rotationDegrees) &&
areVec3Equal(left.size, right.size));
areVec3Equal(left.size, right.size) &&
areBrushGeometriesEqual(left.geometry, right.geometry));
case "modelInstance":
return (right.kind === "modelInstance" &&
areVec3Equal(left.position, right.position) &&
@@ -246,7 +263,8 @@ export function createTransformPreviewFromTarget(target) {
kind: "brush",
center: cloneVec3(target.initialCenter),
rotationDegrees: cloneVec3(target.initialRotationDegrees),
size: cloneVec3(target.initialSize)
size: cloneVec3(target.initialSize),
geometry: cloneBoxBrushGeometry(target.initialGeometry)
};
case "modelInstance":
return {
@@ -272,7 +290,8 @@ export function doesTransformSessionChangeTarget(session) {
return (session.preview.kind === "brush" &&
(!areVec3Equal(session.preview.center, session.target.initialCenter) ||
!areVec3Equal(session.preview.rotationDegrees, session.target.initialRotationDegrees) ||
!areVec3Equal(session.preview.size, session.target.initialSize)));
!areVec3Equal(session.preview.size, session.target.initialSize) ||
!areBrushGeometriesEqual(session.preview.geometry, session.target.initialGeometry)));
case "modelInstance":
return (session.preview.kind === "modelInstance" &&
(!areVec3Equal(session.preview.position, session.target.initialPosition) ||
@@ -411,7 +430,8 @@ function createBrushTransformTarget(document, brushId) {
brushId: brush.id,
initialCenter: cloneVec3(brush.center),
initialRotationDegrees: cloneVec3(brush.rotationDegrees),
initialSize: cloneVec3(brush.size)
initialSize: cloneVec3(brush.size),
initialGeometry: cloneBoxBrushGeometry(brush.geometry)
},
message: null
};
@@ -428,7 +448,8 @@ function createBrushFaceTransformTarget(document, brushId, faceId) {
faceId,
initialCenter: cloneVec3(brushResolution.target.initialCenter),
initialRotationDegrees: cloneVec3(brushResolution.target.initialRotationDegrees),
initialSize: cloneVec3(brushResolution.target.initialSize)
initialSize: cloneVec3(brushResolution.target.initialSize),
initialGeometry: cloneBoxBrushGeometry(brushResolution.target.initialGeometry)
},
message: null
};
@@ -445,7 +466,8 @@ function createBrushEdgeTransformTarget(document, brushId, edgeId) {
edgeId,
initialCenter: cloneVec3(brushResolution.target.initialCenter),
initialRotationDegrees: cloneVec3(brushResolution.target.initialRotationDegrees),
initialSize: cloneVec3(brushResolution.target.initialSize)
initialSize: cloneVec3(brushResolution.target.initialSize),
initialGeometry: cloneBoxBrushGeometry(brushResolution.target.initialGeometry)
},
message: null
};
@@ -462,7 +484,8 @@ function createBrushVertexTransformTarget(document, brushId, vertexId) {
vertexId,
initialCenter: cloneVec3(brushResolution.target.initialCenter),
initialRotationDegrees: cloneVec3(brushResolution.target.initialRotationDegrees),
initialSize: cloneVec3(brushResolution.target.initialSize)
initialSize: cloneVec3(brushResolution.target.initialSize),
initialGeometry: cloneBoxBrushGeometry(brushResolution.target.initialGeometry)
},
message: null
};

View File

@@ -96,6 +96,16 @@ export function transformBoxBrushWorldVectorToLocal(brush, worldVector) {
z: localVector.z
};
}
export function transformBoxBrushWorldPointToLocal(brush, worldPoint) {
const rotation = createBrushRotationEuler(brush);
const inverseRotation = new Quaternion().setFromEuler(rotation).invert();
const localPoint = new Vector3(worldPoint.x - brush.center.x, worldPoint.y - brush.center.y, worldPoint.z - brush.center.z).applyQuaternion(inverseRotation);
return {
x: localPoint.x,
y: localPoint.y,
z: localPoint.z
};
}
export function transformBoxBrushLocalPointToWorld(brush, localPoint) {
const rotation = createBrushRotationEuler(brush);
const rotatedOffset = new Vector3(localPoint.x, localPoint.y, localPoint.z).applyEuler(rotation);

View File

@@ -1,5 +1,4 @@
import { AmbientLight, AxesHelper, BufferGeometry, BoxGeometry, CanvasTexture, CapsuleGeometry, ConeGeometry, CylinderGeometry, DirectionalLight, EdgesGeometry, GridHelper, Group, Line, LineBasicMaterial, LineSegments, Material, Mesh, MeshBasicMaterial, MeshStandardMaterial, Object3D, OrthographicCamera, Plane, PerspectiveCamera, PointLight, Quaternion, Raycaster, Scene, SphereGeometry, Spherical, TorusGeometry, SpotLight, Vector2, Vector3, WebGLRenderer } from "three";
import { EffectComposer } from "postprocessing";
import { AmbientLight, AxesHelper, BufferGeometry, BoxGeometry, CapsuleGeometry, ConeGeometry, CylinderGeometry, DirectionalLight, EdgesGeometry, GridHelper, Group, Line, LineBasicMaterial, LineSegments, Mesh, MeshBasicMaterial, MeshStandardMaterial, OrthographicCamera, Plane, PerspectiveCamera, PointLight, Quaternion, Raycaster, Scene, SphereGeometry, Spherical, TorusGeometry, SpotLight, Vector2, Vector3, WebGLRenderer } from "three";
import { areEditorSelectionsEqual, isBrushEdgeSelected, isBrushFaceSelected, isBrushSelected, isBrushVertexSelected, isModelInstanceSelected } from "../core/selection";
import { getWhiteboxSelectionFeedbackLabel } from "../core/whitebox-selection-feedback";
import { cloneTransformSession, createInactiveTransformSession, createTransformPreviewFromTarget, createTransformSession, resolveTransformTarget, supportsTransformOperation, supportsTransformAxisConstraint } from "../core/transform-session";
@@ -7,9 +6,9 @@ import { createModelInstanceRenderGroup, disposeModelInstance } from "../assets/
import { createModelInstance, createModelInstancePlacementPosition, DEFAULT_MODEL_INSTANCE_ROTATION_DEGREES, DEFAULT_MODEL_INSTANCE_SCALE, getModelInstances } from "../assets/model-instances";
import { areAdvancedRenderingSettingsEqual, cloneAdvancedRenderingSettings } from "../document/world-settings";
import { DEFAULT_INTERACTABLE_RADIUS, DEFAULT_PLAYER_START_BOX_SIZE, DEFAULT_PLAYER_START_CAPSULE_HEIGHT, DEFAULT_PLAYER_START_CAPSULE_RADIUS, DEFAULT_PLAYER_START_EYE_HEIGHT, DEFAULT_PLAYER_START_YAW_DEGREES, DEFAULT_POINT_LIGHT_DISTANCE, DEFAULT_SOUND_EMITTER_MAX_DISTANCE, DEFAULT_SOUND_EMITTER_REF_DISTANCE, DEFAULT_SPOT_LIGHT_ANGLE_DEGREES, DEFAULT_SPOT_LIGHT_DIRECTION, DEFAULT_SPOT_LIGHT_DISTANCE, DEFAULT_TELEPORT_TARGET_YAW_DEGREES, DEFAULT_TRIGGER_VOLUME_SIZE, getPlayerStartColliderHeight, getEntityInstances, normalizeYawDegrees } from "../entities/entity-instances";
import { BOX_EDGE_IDS, BOX_FACE_IDS, BOX_VERTEX_IDS, DEFAULT_BOX_BRUSH_SIZE } from "../document/brushes";
import { getBoxBrushEdgeAxis, getBoxBrushEdgeTransformMeta, getBoxBrushEdgeWorldSegment, getBoxBrushFaceAxis, getBoxBrushFaceTransformMeta, getBoxBrushFaceWorldCenter, getBoxBrushVertexSigns, getBoxBrushVertexWorldPosition, transformBoxBrushLocalPointToWorld, transformBoxBrushWorldVectorToLocal } from "../geometry/box-brush-components";
import { applyBoxBrushFaceUvsToGeometry } from "../geometry/box-face-uvs";
import { BOX_EDGE_IDS, BOX_FACE_IDS, BOX_VERTEX_IDS, cloneBoxBrushGeometry, deriveBoxBrushSizeFromGeometry, scaleBoxBrushGeometryToSize, DEFAULT_BOX_BRUSH_SIZE } from "../document/brushes";
import { getBoxBrushEdgeAxis, getBoxBrushEdgeTransformMeta, getBoxBrushEdgeWorldSegment, getBoxBrushFaceAxis, getBoxBrushFaceTransformMeta, getBoxBrushFaceWorldCenter, getBoxBrushVertexWorldPosition, transformBoxBrushWorldPointToLocal, transformBoxBrushWorldVectorToLocal } from "../geometry/box-brush-components";
import { buildBoxBrushDerivedMeshData, getBoxBrushEdgeVertexIds, getBoxBrushFaceVertexIds, getBoxBrushLocalVertexPosition } from "../geometry/box-brush-mesh";
import { createModelColliderDebugGroup } from "../geometry/model-instance-collider-debug-mesh";
import { buildGeneratedModelCollider } from "../geometry/model-instance-collider-generation";
import { DEFAULT_GRID_SIZE, snapValueToGrid } from "../geometry/grid-snapping";
@@ -782,7 +781,8 @@ export class ViewportHost {
},
size: {
...session.preview.size
}
},
geometry: cloneBoxBrushGeometry(session.preview.geometry)
};
}
clearTransformGizmo() {
@@ -1090,7 +1090,8 @@ export class ViewportHost {
},
size: {
...session.target.initialSize
}
},
geometry: cloneBoxBrushGeometry(session.target.initialGeometry)
};
}
if (session.target.kind === "modelInstance") {
@@ -1144,7 +1145,8 @@ export class ViewportHost {
rotationDegrees: nextRotationDegrees,
size: {
...session.target.initialSize
}
},
geometry: cloneBoxBrushGeometry(session.target.initialGeometry)
};
}
if (session.target.kind === "modelInstance") {
@@ -1234,7 +1236,8 @@ export class ViewportHost {
rotationDegrees: {
...session.target.initialRotationDegrees
},
size: nextSize
size: nextSize,
geometry: scaleBoxBrushGeometryToSize(session.target.initialGeometry, nextSize)
};
}
if (session.target.kind !== "modelInstance") {
@@ -1285,51 +1288,47 @@ export class ViewportHost {
},
size: {
...session.target.initialSize
}
},
geometry: cloneBoxBrushGeometry(session.target.initialGeometry)
};
}
createBrushPreviewFromExtents(brush, extents, rotationDegrees) {
const localCenter = {
x: (extents.min.x + extents.max.x) * 0.5,
y: (extents.min.y + extents.max.y) * 0.5,
z: (extents.min.z + extents.max.z) * 0.5
};
const centerOffset = transformBoxBrushLocalPointToWorld({
...brush,
center: { x: 0, y: 0, z: 0 },
rotationDegrees: rotationDegrees ?? brush.rotationDegrees
}, localCenter);
const nextSize = {
x: this.snapWhiteboxSizeValue(extents.max.x - extents.min.x),
y: this.snapWhiteboxSizeValue(extents.max.y - extents.min.y),
z: this.snapWhiteboxSizeValue(extents.max.z - extents.min.z)
};
createBrushPreviewFromGeometry(brush, geometry) {
const nextGeometry = cloneBoxBrushGeometry(geometry);
return {
kind: "brush",
center: {
x: this.snapWhiteboxPositionValue(brush.center.x + centerOffset.x),
y: this.snapWhiteboxPositionValue(brush.center.y + centerOffset.y),
z: this.snapWhiteboxPositionValue(brush.center.z + centerOffset.z)
...brush.center
},
rotationDegrees: {
...(rotationDegrees ?? brush.rotationDegrees)
...brush.rotationDegrees
},
size: nextSize
size: deriveBoxBrushSizeFromGeometry(nextGeometry),
geometry: nextGeometry
};
}
getInitialBrushLocalExtents(brush) {
return {
min: {
x: -brush.size.x * 0.5,
y: -brush.size.y * 0.5,
z: -brush.size.z * 0.5
},
max: {
x: brush.size.x * 0.5,
y: brush.size.y * 0.5,
z: brush.size.z * 0.5
getComponentTargetVertexIds(target) {
switch (target.kind) {
case "brushFace":
return [...getBoxBrushFaceVertexIds(target.faceId)];
case "brushEdge": {
const [start, end] = getBoxBrushEdgeVertexIds(target.edgeId);
return [start, end];
}
};
case "brushVertex":
return [target.vertexId];
default:
return [];
}
}
applyDeltaToVertices(brush, vertexIds, delta) {
const nextGeometry = cloneBoxBrushGeometry(brush.geometry);
for (const vertexId of vertexIds) {
const vertex = nextGeometry.vertices[vertexId];
vertex.x = this.snapWhiteboxPositionValue(vertex.x + delta.x);
vertex.y = this.snapWhiteboxPositionValue(vertex.y + delta.y);
vertex.z = this.snapWhiteboxPositionValue(vertex.z + delta.z);
}
return nextGeometry;
}
buildComponentTranslatedBrushPreview(session, origin, current, axisConstraint) {
const initialBrush = this.createTargetPreviewBrush(session);
@@ -1342,7 +1341,8 @@ export class ViewportHost {
kind: "brush",
center: { ...initialBrush.center },
rotationDegrees: { ...initialBrush.rotationDegrees },
size: { ...initialBrush.size }
size: { ...initialBrush.size },
geometry: cloneBoxBrushGeometry(initialBrush.geometry)
}
});
let worldDelta = {
@@ -1368,55 +1368,9 @@ export class ViewportHost {
worldDelta = this.setAxisComponent(worldDelta, axisConstraint, axisDelta);
}
const localDelta = transformBoxBrushWorldVectorToLocal(initialBrush, worldDelta);
const extents = this.getInitialBrushLocalExtents(initialBrush);
if (session.target.kind === "brushFace") {
const meta = getBoxBrushFaceTransformMeta(session.target.faceId);
for (const axis of ["x", "y", "z"]) {
const axisDelta = localDelta[axis];
if (axis === meta.axis) {
if (meta.sign > 0) {
extents.max[axis] += axisDelta;
}
else {
extents.min[axis] += axisDelta;
}
}
else {
extents.min[axis] += axisDelta;
extents.max[axis] += axisDelta;
}
}
}
else if (session.target.kind === "brushEdge") {
const meta = getBoxBrushEdgeTransformMeta(session.target.edgeId);
for (const axis of ["x", "y", "z"]) {
const axisDelta = localDelta[axis];
const axisSign = meta.signs[axis];
if (axisSign === null) {
extents.min[axis] += axisDelta;
extents.max[axis] += axisDelta;
continue;
}
if (axisSign > 0) {
extents.max[axis] += axisDelta;
}
else {
extents.min[axis] += axisDelta;
}
}
}
else if (session.target.kind === "brushVertex") {
const signs = getBoxBrushVertexSigns(session.target.vertexId);
for (const axis of ["x", "y", "z"]) {
if (signs[axis] > 0) {
extents.max[axis] += localDelta[axis];
}
else {
extents.min[axis] += localDelta[axis];
}
}
}
return this.createBrushPreviewFromExtents(initialBrush, extents);
const vertexIds = this.getComponentTargetVertexIds(session.target);
const nextGeometry = this.applyDeltaToVertices(initialBrush, vertexIds, localDelta);
return this.createBrushPreviewFromGeometry(initialBrush, nextGeometry);
}
buildComponentRotatedBrushPreview(session, origin, current, axisConstraint) {
const initialBrush = this.createTargetPreviewBrush(session);
@@ -1425,89 +1379,108 @@ export class ViewportHost {
}
const effectiveAxis = axisConstraint ?? this.getEffectiveRotationAxis(session);
const pointerDeltaDegrees = (current.x - origin.x - (current.y - origin.y)) * 0.5;
const pivot = this.getTransformPivotPosition({
const pivotWorld = this.getTransformPivotPosition({
...session,
preview: {
kind: "brush",
center: { ...initialBrush.center },
rotationDegrees: { ...initialBrush.rotationDegrees },
size: { ...initialBrush.size }
size: { ...initialBrush.size },
geometry: cloneBoxBrushGeometry(initialBrush.geometry)
}
});
const axisVector = this.axisVector(effectiveAxis).normalize();
const centerVector = new Vector3(initialBrush.center.x, initialBrush.center.y, initialBrush.center.z);
const pivotVector = new Vector3(pivot.x, pivot.y, pivot.z);
const nextCenter = centerVector
.sub(pivotVector)
.applyAxisAngle(axisVector, (pointerDeltaDegrees * Math.PI) / 180)
.add(pivotVector);
const nextRotationDegrees = {
...initialBrush.rotationDegrees
};
nextRotationDegrees[effectiveAxis] = this.normalizeDegrees(nextRotationDegrees[effectiveAxis] + pointerDeltaDegrees);
return {
kind: "brush",
center: {
x: this.snapWhiteboxPositionValue(nextCenter.x),
y: this.snapWhiteboxPositionValue(nextCenter.y),
z: this.snapWhiteboxPositionValue(nextCenter.z)
},
rotationDegrees: nextRotationDegrees,
size: {
...initialBrush.size
}
};
const pivotLocal = transformBoxBrushWorldPointToLocal(initialBrush, pivotWorld);
const rotationAxis = this.axisVector(effectiveAxis).normalize();
const vertexIds = this.getComponentTargetVertexIds(session.target);
const nextGeometry = cloneBoxBrushGeometry(initialBrush.geometry);
for (const vertexId of vertexIds) {
const vertex = getBoxBrushLocalVertexPosition(initialBrush, vertexId);
const next = new Vector3(vertex.x - pivotLocal.x, vertex.y - pivotLocal.y, vertex.z - pivotLocal.z)
.applyAxisAngle(rotationAxis, (pointerDeltaDegrees * Math.PI) / 180)
.add(new Vector3(pivotLocal.x, pivotLocal.y, pivotLocal.z));
nextGeometry.vertices[vertexId] = {
x: this.snapWhiteboxPositionValue(next.x),
y: this.snapWhiteboxPositionValue(next.y),
z: this.snapWhiteboxPositionValue(next.z)
};
}
return this.createBrushPreviewFromGeometry(initialBrush, nextGeometry);
}
buildComponentScaledBrushPreview(session, origin, current, axisConstraint) {
const initialBrush = this.createTargetPreviewBrush(session);
if (initialBrush === null) {
throw new Error("Cannot build a component scale preview without a box brush target.");
}
const extents = this.getInitialBrushLocalExtents(initialBrush);
const pivotWorld = this.getTransformPivotPosition({
...session,
preview: {
kind: "brush",
center: { ...initialBrush.center },
rotationDegrees: { ...initialBrush.rotationDegrees },
size: { ...initialBrush.size },
geometry: cloneBoxBrushGeometry(initialBrush.geometry)
}
});
const pivotLocal = transformBoxBrushWorldPointToLocal(initialBrush, pivotWorld);
const nextGeometry = cloneBoxBrushGeometry(initialBrush.geometry);
const vertexIds = this.getComponentTargetVertexIds(session.target);
if (session.target.kind === "brushFace") {
const meta = getBoxBrushFaceTransformMeta(session.target.faceId);
const scaleFactor = 1 +
this.getAxisMovementDistance(axisConstraint ?? meta.axis, this.getTransformPivotPosition(session), origin, current) *
0.45;
if (meta.sign > 0) {
extents.max[meta.axis] = extents.min[meta.axis] + (extents.max[meta.axis] - extents.min[meta.axis]) * scaleFactor;
}
else {
extents.min[meta.axis] = extents.max[meta.axis] - (extents.max[meta.axis] - extents.min[meta.axis]) * scaleFactor;
const axis = axisConstraint ?? meta.axis;
const scaleFactor = 1 + this.getAxisMovementDistance(axis, pivotWorld, origin, current) * 0.45;
for (const vertexId of vertexIds) {
const vertex = nextGeometry.vertices[vertexId];
vertex[axis] = this.snapWhiteboxPositionValue(pivotLocal[axis] + (vertex[axis] - pivotLocal[axis]) * scaleFactor);
}
}
else if (session.target.kind === "brushEdge") {
const meta = getBoxBrushEdgeTransformMeta(session.target.edgeId);
const affectedAxes = ["x", "y", "z"].filter((axis) => meta.signs[axis] !== null && (axisConstraint === null || axisConstraint === axis));
const pivot = this.getTransformPivotPosition(session);
for (const axis of affectedAxes) {
const sign = meta.signs[axis];
if (sign === null) {
continue;
}
const scaleFactor = 1 + this.getAxisMovementDistance(axis, pivot, origin, current) * 0.45;
if (sign > 0) {
extents.max[axis] = extents.min[axis] + (extents.max[axis] - extents.min[axis]) * scaleFactor;
}
else {
extents.min[axis] = extents.max[axis] - (extents.max[axis] - extents.min[axis]) * scaleFactor;
const scaleFactor = 1 + this.getAxisMovementDistance(axis, pivotWorld, origin, current) * 0.45;
for (const vertexId of vertexIds) {
const vertex = nextGeometry.vertices[vertexId];
vertex[axis] = this.snapWhiteboxPositionValue(pivotLocal[axis] + (vertex[axis] - pivotLocal[axis]) * scaleFactor);
}
}
}
return this.createBrushPreviewFromExtents(initialBrush, extents);
return this.createBrushPreviewFromGeometry(initialBrush, nextGeometry);
}
applyBrushRenderObjectTransform(brushId, center, rotationDegrees, size) {
updateBrushRenderObjectGeometry(brush) {
const renderObjects = this.brushRenderObjects.get(brush.id);
if (renderObjects === undefined) {
return;
}
const nextGeometry = buildBoxBrushDerivedMeshData(brush).geometry;
renderObjects.mesh.geometry.dispose();
renderObjects.mesh.geometry = nextGeometry;
renderObjects.edges.geometry.dispose();
renderObjects.edges.geometry = new EdgesGeometry(nextGeometry);
for (const edgeHelper of renderObjects.edgeHelpers) {
const segment = getBoxBrushEdgeWorldSegment(brush, edgeHelper.id);
const nextEdgeGeometry = new BufferGeometry().setFromPoints([
new Vector3(segment.start.x, segment.start.y, segment.start.z),
new Vector3(segment.end.x, segment.end.y, segment.end.z)
]);
edgeHelper.line.geometry.dispose();
edgeHelper.line.geometry = nextEdgeGeometry;
}
for (const vertexHelper of renderObjects.vertexHelpers) {
const vertex = getBoxBrushVertexWorldPosition(brush, vertexHelper.id);
vertexHelper.mesh.position.set(vertex.x, vertex.y, vertex.z);
}
}
applyBrushRenderObjectTransform(brushId, center, rotationDegrees) {
const renderObjects = this.brushRenderObjects.get(brushId);
const brush = this.currentDocument?.brushes[brushId];
if (renderObjects === undefined || brush === undefined) {
if (renderObjects === undefined) {
return;
}
renderObjects.mesh.position.set(center.x, center.y, center.z);
renderObjects.mesh.rotation.set((rotationDegrees.x * Math.PI) / 180, (rotationDegrees.y * Math.PI) / 180, (rotationDegrees.z * Math.PI) / 180);
renderObjects.mesh.scale.set(size.x / brush.size.x, size.y / brush.size.y, size.z / brush.size.z);
renderObjects.mesh.scale.set(1, 1, 1);
renderObjects.edges.position.set(center.x, center.y, center.z);
renderObjects.edges.rotation.set((rotationDegrees.x * Math.PI) / 180, (rotationDegrees.y * Math.PI) / 180, (rotationDegrees.z * Math.PI) / 180);
renderObjects.edges.scale.set(size.x / brush.size.x, size.y / brush.size.y, size.z / brush.size.z);
renderObjects.edges.scale.set(1, 1, 1);
}
applySpotLightGroupTransform(group, position, direction) {
const forward = new Vector3(direction.x, direction.y, direction.z).normalize();
@@ -1553,7 +1526,8 @@ export class ViewportHost {
return;
}
for (const brush of Object.values(this.currentDocument.brushes)) {
this.applyBrushRenderObjectTransform(brush.id, brush.center, brush.rotationDegrees, brush.size);
this.updateBrushRenderObjectGeometry(brush);
this.applyBrushRenderObjectTransform(brush.id, brush.center, brush.rotationDegrees);
}
for (const entity of getEntityInstances(this.currentDocument.entities)) {
this.applyEntityRenderObjectTransform(entity);
@@ -1573,7 +1547,11 @@ export class ViewportHost {
case "brushEdge":
case "brushVertex":
if (this.currentTransformSession.preview.kind === "brush") {
this.applyBrushRenderObjectTransform(this.currentTransformSession.target.brushId, this.currentTransformSession.preview.center, this.currentTransformSession.preview.rotationDegrees, this.currentTransformSession.preview.size);
const previewBrush = this.createPreviewBrushForSession(this.currentTransformSession);
if (previewBrush !== null) {
this.updateBrushRenderObjectGeometry(previewBrush);
}
this.applyBrushRenderObjectTransform(this.currentTransformSession.target.brushId, this.currentTransformSession.preview.center, this.currentTransformSession.preview.rotationDegrees);
}
break;
case "modelInstance":
@@ -1654,8 +1632,7 @@ export class ViewportHost {
rebuildBrushMeshes(document, selection) {
this.clearBrushMeshes();
for (const brush of Object.values(document.brushes)) {
const geometry = new BoxGeometry(brush.size.x, brush.size.y, brush.size.z);
applyBoxBrushFaceUvsToGeometry(geometry, brush);
const geometry = buildBoxBrushDerivedMeshData(brush).geometry;
const materials = BOX_FACE_IDS.map((faceId) => this.createFaceMaterial(brush, faceId, document.materials[brush.faces[faceId].materialId ?? ""], this.getFaceHighlightState(brush.id, faceId)));
const mesh = new Mesh(geometry, materials);
const brushSelected = isBrushSelected(selection, brush.id);
@@ -1682,7 +1659,7 @@ export class ViewportHost {
edgeHelpers,
vertexHelpers
});
this.applyBrushRenderObjectTransform(brush.id, brush.center, brush.rotationDegrees, brush.size);
this.applyBrushRenderObjectTransform(brush.id, brush.center, brush.rotationDegrees);
}
this.refreshBrushPresentation();
this.applyShadowState();