auto-git:

[change] src/app/App.tsx
 [change] src/commands/commit-transform-session-command.ts
 [change] src/core/transform-session.ts
 [change] src/document/migrate-scene-document.ts
 [change] src/entities/entity-instances.ts
 [change] src/runtime-three/runtime-host.ts
This commit is contained in:
2026-05-11 14:14:50 +02:00
parent b6cadc27f9
commit 01ef204dcc
7 changed files with 216 additions and 185 deletions

View File

@@ -253,9 +253,7 @@ import {
TERRAIN_LAYER_COUNT,
type Terrain
} from "../document/terrains";
import {
BUNDLED_FOLIAGE_PROTOTYPES
} from "../foliage/bundled-foliage-manifest";
import { BUNDLED_FOLIAGE_PROTOTYPES } from "../foliage/bundled-foliage-manifest";
import {
createFoliageLayer,
type FoliageLayer,
@@ -852,12 +850,7 @@ const TERRAIN_SCULPT_BRUSH_TOOLS: Exclude<
| "foliageErase"
| "foliageBlockerPaint"
| "foliageBlockerErase"
>[] = [
"raise",
"lower",
"smooth",
"flatten"
];
>[] = ["raise", "lower", "smooth", "flatten"];
const MIN_VIEWPORT_QUAD_SPLIT = 0.2;
const MAX_VIEWPORT_QUAD_SPLIT = 0.8;
@@ -2474,7 +2467,8 @@ function getFoliageLayerList(
foliageLayers: FoliageLayerRegistry
): FoliageLayer[] {
return Object.values(foliageLayers).sort(
(left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id)
(left, right) =>
left.name.localeCompare(right.name) || left.id.localeCompare(right.id)
);
}
@@ -2511,9 +2505,7 @@ function formatMeters(value: number): string {
return Number.isInteger(value) ? `${value}m` : `${value.toFixed(1)}m`;
}
function formatFoliagePrototypeLodStatus(
prototype: FoliagePrototype
): string {
function formatFoliagePrototypeLodStatus(prototype: FoliagePrototype): string {
const levels = prototype.lods.map((lod) => lod.level).sort();
return `${levels.length} LOD${levels.length === 1 ? "" : "s"} · LOD ${levels.join(", ")}`;
@@ -2610,7 +2602,11 @@ function createProjectDownloadName(projectName: string): string {
return `${slug.length > 0 ? slug : "project"}${PROJECT_PACKAGE_FILE_EXTENSION}`;
}
export function App({ store, draftStorage = null, initialStatusMessage }: AppProps) {
export function App({
store,
draftStorage = null,
initialStatusMessage
}: AppProps) {
const editorState = useEditorStoreState(store);
const sceneList = Object.values(editorState.projectDocument.scenes);
const activeProjectScene =
@@ -2631,9 +2627,7 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
const customFoliagePrototypeList = useMemo(
() =>
Object.values(editorState.document.foliagePrototypes)
.filter(
(prototype) => !BUNDLED_FOLIAGE_PROTOTYPE_IDS.has(prototype.id)
)
.filter((prototype) => !BUNDLED_FOLIAGE_PROTOTYPE_IDS.has(prototype.id))
.sort(
(left, right) =>
left.category.localeCompare(right.category) ||
@@ -3308,10 +3302,8 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
const [terrainSampleCountXDraft, setTerrainSampleCountXDraft] = useState("9");
const [terrainSampleCountZDraft, setTerrainSampleCountZDraft] = useState("9");
const [terrainCellSizeDraft, setTerrainCellSizeDraft] = useState("1");
const [
terrainLodGridVisibleTerrainIds,
setTerrainLodGridVisibleTerrainIds
] = useState<readonly string[]>([]);
const [terrainLodGridVisibleTerrainIds, setTerrainLodGridVisibleTerrainIds] =
useState<readonly string[]>([]);
const [activeFoliageLayerId, setActiveFoliageLayerId] = useState<
string | null
>(null);
@@ -3527,8 +3519,7 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
setAdvancedRenderingFoliageMaxDistanceMultiplierDraft
] = useState(
String(
editorState.document.world.advancedRendering.foliage
.maxDistanceMultiplier
editorState.document.world.advancedRendering.foliage.maxDistanceMultiplier
)
);
const [
@@ -3635,7 +3626,9 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
advancedRenderingDistanceFogNearDistanceDraft,
setAdvancedRenderingDistanceFogNearDistanceDraft
] = useState(
String(editorState.document.world.advancedRendering.distanceFog.nearDistance)
String(
editorState.document.world.advancedRendering.distanceFog.nearDistance
)
);
const [
advancedRenderingDistanceFogFarDistanceDraft,
@@ -5078,10 +5071,7 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
}
autosaveControllerRef.current?.schedule("document");
}, [
autosaveAvailable,
editorState.projectDocument
]);
}, [autosaveAvailable, editorState.projectDocument]);
useEffect(() => {
if (!autosaveAvailable) {
@@ -9406,7 +9396,7 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
? ` for ${activeFoliageLayer?.name ?? "the active foliage layer"}`
: tool === "foliageBlockerPaint" || tool === "foliageBlockerErase"
? " for the global foliage blocker"
: "";
: "";
setStatusMessage(
`Armed ${getTerrainBrushToolLabel(tool)} terrain brush${paintLayerLabel} for ${getTerrainLabelById(selectedTerrain.id, terrainList)}. Drag in the viewport to edit the selected terrain.`
);
@@ -15879,11 +15869,10 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
onCameraStateChange={(cameraState) => {
const previousCameraState =
editorState.viewportPanels[panelId].cameraState;
const cameraStatesEqual =
areViewportPanelCameraStatesEqual(
previousCameraState,
cameraState
);
const cameraStatesEqual = areViewportPanelCameraStatesEqual(
previousCameraState,
cameraState
);
traceUpdateLoopEvent("App.onCameraStateChange", {
panelId,
@@ -16463,9 +16452,7 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
activeFoliageLayerId === layer.id
? "outliner-item--selected"
: ""
} ${
layer.enabled ? "" : "outliner-item--disabled"
}`}
} ${layer.enabled ? "" : "outliner-item--disabled"}`}
>
<div className="outliner-item__row">
<input
@@ -16529,18 +16516,14 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
data-testid="foliage-layer-name"
value={foliageLayerNameDraft}
onChange={(event) =>
setFoliageLayerNameDraft(
event.currentTarget.value
)
setFoliageLayerNameDraft(event.currentTarget.value)
}
onBlur={applyActiveFoliageLayerName}
onKeyDown={(event) => {
if (event.key === "Enter") {
applyActiveFoliageLayerName();
} else if (event.key === "Escape") {
setFoliageLayerNameDraft(
activeFoliageLayer.name
);
setFoliageLayerNameDraft(activeFoliageLayer.name);
}
}}
/>
@@ -16586,9 +16569,7 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
)
}
onBlur={() =>
applyActiveFoliageLayerNumericField(
input.field
)
applyActiveFoliageLayerNumericField(input.field)
}
onKeyDown={(event) =>
handleDraftVectorKeyDown(event, () =>
@@ -16623,42 +16604,38 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
className="foliage-prototype-list"
data-testid="foliage-prototype-mix"
>
{SORTED_BUNDLED_FOLIAGE_PROTOTYPES.map(
(prototype) => (
<label
key={prototype.id}
className="outliner-item outliner-item--compact foliage-prototype-choice"
>
<div className="outliner-item__row">
<input
className="outliner-item__toggle"
type="checkbox"
data-testid={`foliage-prototype-checkbox-${prototype.id}`}
checked={activeFoliageLayer.prototypeIds.includes(
prototype.id
)}
onChange={(event) =>
handleFoliagePrototypeSelectionChange(
prototype.id,
event.currentTarget.checked
)
}
/>
<span className="outliner-item__select">
<span className="outliner-item__title">
{prototype.label}
</span>
<span className="outliner-item__meta">
{prototype.category} ·{" "}
{formatFoliagePrototypeLodStatus(
prototype
)}
</span>
{SORTED_BUNDLED_FOLIAGE_PROTOTYPES.map((prototype) => (
<label
key={prototype.id}
className="outliner-item outliner-item--compact foliage-prototype-choice"
>
<div className="outliner-item__row">
<input
className="outliner-item__toggle"
type="checkbox"
data-testid={`foliage-prototype-checkbox-${prototype.id}`}
checked={activeFoliageLayer.prototypeIds.includes(
prototype.id
)}
onChange={(event) =>
handleFoliagePrototypeSelectionChange(
prototype.id,
event.currentTarget.checked
)
}
/>
<span className="outliner-item__select">
<span className="outliner-item__title">
{prototype.label}
</span>
</div>
</label>
)
)}
<span className="outliner-item__meta">
{prototype.category} ·{" "}
{formatFoliagePrototypeLodStatus(prototype)}
</span>
</span>
</div>
</label>
))}
</div>
</div>
</>
@@ -19112,12 +19089,8 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
<input
className="text-input"
type="number"
min={
MIN_FOLIAGE_QUALITY_MAX_DISTANCE_MULTIPLIER
}
max={
MAX_FOLIAGE_QUALITY_MAX_DISTANCE_MULTIPLIER
}
min={MIN_FOLIAGE_QUALITY_MAX_DISTANCE_MULTIPLIER}
max={MAX_FOLIAGE_QUALITY_MAX_DISTANCE_MULTIPLIER}
step="0.05"
value={
advancedRenderingFoliageMaxDistanceMultiplierDraft
@@ -19978,7 +19951,9 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
min="0"
max="1"
step="0.05"
value={advancedRenderingDistanceFogStrengthDraft}
value={
advancedRenderingDistanceFogStrengthDraft
}
onChange={(event) =>
setAdvancedRenderingDistanceFogStrengthDraft(
event.currentTarget.value
@@ -20095,7 +20070,9 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
min="0"
max="1"
step="0.05"
value={advancedRenderingDistanceFogSkyBlendDraft}
value={
advancedRenderingDistanceFogSkyBlendDraft
}
onChange={(event) =>
setAdvancedRenderingDistanceFogSkyBlendDraft(
event.currentTarget.value
@@ -20406,17 +20383,13 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
min="0.25"
max="3"
step="0.05"
value={
advancedRenderingGodRaysSourceSizeDraft
}
value={advancedRenderingGodRaysSourceSizeDraft}
onChange={(event) =>
setAdvancedRenderingGodRaysSourceSizeDraft(
event.currentTarget.value
)
}
onBlur={
applyAdvancedRenderingGodRaysSourceSize
}
onBlur={applyAdvancedRenderingGodRaysSourceSize}
onKeyDown={(event) =>
handleDraftVectorKeyDown(
event,
@@ -20849,10 +20822,9 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
armedTerrainBrushTool === "foliageErase"
? `${getTerrainBrushToolLabel(armedTerrainBrushTool)} is armed for ${getTerrainLabelById(selectedTerrain.id, terrainList)} on ${activeFoliageLayer?.name ?? "no foliage layer"}.`
: armedTerrainBrushTool === "foliageBlockerPaint" ||
armedTerrainBrushTool ===
"foliageBlockerErase"
armedTerrainBrushTool === "foliageBlockerErase"
? `${getTerrainBrushToolLabel(armedTerrainBrushTool)} is armed for ${getTerrainLabelById(selectedTerrain.id, terrainList)}.`
: `${getTerrainBrushToolLabel(armedTerrainBrushTool)} is armed for ${getTerrainLabelById(selectedTerrain.id, terrainList)}. Click the active tool again to disarm it.`}
: `${getTerrainBrushToolLabel(armedTerrainBrushTool)} is armed for ${getTerrainLabelById(selectedTerrain.id, terrainList)}. Click the active tool again to disarm it.`}
</div>
</div>
@@ -20943,7 +20915,9 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
data-testid="terrain-brush-tool-foliage-paint"
aria-pressed={armedTerrainBrushTool === "foliagePaint"}
disabled={activeFoliageLayer === null}
onClick={() => handleArmTerrainBrushTool("foliagePaint")}
onClick={() =>
handleArmTerrainBrushTool("foliagePaint")
}
>
Paint Mask
</button>
@@ -20953,7 +20927,9 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
data-testid="terrain-brush-tool-foliage-erase"
aria-pressed={armedTerrainBrushTool === "foliageErase"}
disabled={activeFoliageLayer === null}
onClick={() => handleArmTerrainBrushTool("foliageErase")}
onClick={() =>
handleArmTerrainBrushTool("foliageErase")
}
>
Erase Mask
</button>
@@ -25084,7 +25060,10 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
handleNumberInputKeyUp(event, applyNpcChange)
}
onPointerUp={(event) =>
handleNumberInputPointerUp(event, applyNpcChange)
handleNumberInputPointerUp(
event,
applyNpcChange
)
}
/>
</label>
@@ -25112,7 +25091,10 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
handleNumberInputKeyUp(event, applyNpcChange)
}
onPointerUp={(event) =>
handleNumberInputPointerUp(event, applyNpcChange)
handleNumberInputPointerUp(
event,
applyNpcChange
)
}
/>
</label>
@@ -25140,7 +25122,10 @@ export function App({ store, draftStorage = null, initialStatusMessage }: AppPro
handleNumberInputKeyUp(event, applyNpcChange)
}
onPointerUp={(event) =>
handleNumberInputPointerUp(event, applyNpcChange)
handleNumberInputPointerUp(
event,
applyNpcChange
)
}
/>
</label>

View File

@@ -57,19 +57,19 @@ function createTransformCommandLabel(session: ActiveTransformSession): string {
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"
? "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"
@@ -213,7 +213,10 @@ function createUpdatedBrushFromPreview(
});
}
export function createCommitTransformSessionCommand(document: SceneDocument, session: ActiveTransformSession): EditorCommand {
export function createCommitTransformSessionCommand(
document: SceneDocument,
session: ActiveTransformSession
): EditorCommand {
switch (session.target.kind) {
case "brush": {
if (session.preview.kind !== "brush") {
@@ -332,10 +335,13 @@ export function createCommitTransformSessionCommand(document: SceneDocument, ses
throw new Error("Model instance transform preview is invalid.");
}
const modelInstance = document.modelInstances[session.target.modelInstanceId];
const modelInstance =
document.modelInstances[session.target.modelInstanceId];
if (modelInstance === undefined) {
throw new Error(`Model instance ${session.target.modelInstanceId} does not exist.`);
throw new Error(
`Model instance ${session.target.modelInstanceId} does not exist.`
);
}
return createUpsertModelInstanceCommand({

View File

@@ -1,8 +1,5 @@
import { createOpaqueId } from "./ids";
import {
resolveSelectionActiveId,
type EditorSelection
} from "./selection";
import { resolveSelectionActiveId, type EditorSelection } from "./selection";
import type { WhiteboxSelectionMode } from "./whitebox-selection-mode";
import type { Vec3 } from "./vector";
import {
@@ -1139,7 +1136,10 @@ export function doesTransformSessionChangeTarget(
targetItem.initialRotationDegrees
) ||
!areVec3Equal(item.size, targetItem.initialSize) ||
!areBrushGeometriesEqual(item.geometry, targetItem.initialGeometry)
!areBrushGeometriesEqual(
item.geometry,
targetItem.initialGeometry
)
);
}))
);
@@ -1184,10 +1184,7 @@ export function doesTransformSessionChangeTarget(
case "pathPoint":
return (
session.preview.kind === "pathPoint" &&
!areVec3Equal(
session.preview.position,
session.target.initialPosition
)
!areVec3Equal(session.preview.position, session.target.initialPosition)
);
case "entity":
return (
@@ -1387,8 +1384,7 @@ export function supportsTransformAxisConstraint(
if (
session.target.kind === "modelInstance" ||
session.target.kind === "modelInstances" ||
session.target.kind === "brush"
||
session.target.kind === "brush" ||
session.target.kind === "brushes"
) {
return true;
@@ -1424,9 +1420,10 @@ export function supportsTransformAxisConstraint(
return false;
}
return getBrushEdgeScaleAxes(brushTarget, session.target.edgeId).includes(
axis
);
return getBrushEdgeScaleAxes(
brushTarget,
session.target.edgeId
).includes(axis);
}
return false;
@@ -1622,7 +1619,10 @@ function createBrushesTransformTarget(
for (const brushId of brushIds) {
const itemResolution = createBrushTransformTarget(document, brushId);
if (itemResolution.target === null || itemResolution.target.kind !== "brush") {
if (
itemResolution.target === null ||
itemResolution.target.kind !== "brush"
) {
return itemResolution;
}
@@ -1812,7 +1812,10 @@ function createEntitiesTransformTarget(
for (const entityId of entityIds) {
const itemResolution = createEntityTransformTarget(document, entityId);
if (itemResolution.target === null || itemResolution.target.kind !== "entity") {
if (
itemResolution.target === null ||
itemResolution.target.kind !== "entity"
) {
return itemResolution;
}
@@ -2021,7 +2024,11 @@ export function resolveTransformTarget(
return selection.ids.length === 1
? createBrushTransformTarget(document, selection.ids[0])
: createBrushesTransformTarget(document, selection.ids, activeSelectionId);
: createBrushesTransformTarget(
document,
selection.ids,
activeSelectionId
);
case "terrains":
return {
target: null,
@@ -2038,12 +2045,15 @@ export function resolveTransformTarget(
return selection.ids.length === 1
? createEntityTransformTarget(document, selection.ids[0])
: createEntitiesTransformTarget(document, selection.ids, activeSelectionId);
: createEntitiesTransformTarget(
document,
selection.ids,
activeSelectionId
);
case "paths":
return {
target: null,
message:
"Select a path point before transforming a path."
message: "Select a path point before transforming a path."
};
case "pathPoint":
return createPathPointTransformTarget(

View File

@@ -2305,7 +2305,10 @@ function readTerrain(value: unknown, label: string): Terrain {
return [
layerId,
{
layerId: expectString(maskValue.layerId, `${maskLabel}.layerId`),
layerId: expectString(
maskValue.layerId,
`${maskLabel}.layerId`
),
resolutionX: expectFiniteNumber(
maskValue.resolutionX,
`${maskLabel}.resolutionX`
@@ -2438,7 +2441,10 @@ function readFoliagePrototypeLod(
}
if (source === "projectAsset") {
const modelAssetId = expectString(value.modelAssetId, `${label}.modelAssetId`);
const modelAssetId = expectString(
value.modelAssetId,
`${label}.modelAssetId`
);
const asset = assets[modelAssetId];
if (asset === undefined) {
@@ -2498,7 +2504,10 @@ function readFoliagePrototype(
`${label}.maxScale`
),
randomYaw: expectBoolean(value.randomYaw, `${label}.randomYaw`),
alignToNormal: expectFiniteNumber(value.alignToNormal, `${label}.alignToNormal`),
alignToNormal: expectFiniteNumber(
value.alignToNormal,
`${label}.alignToNormal`
),
densityWeight: expectNonNegativeFiniteNumber(
value.densityWeight,
`${label}.densityWeight`
@@ -2563,10 +2572,19 @@ function readFoliageLayer(value: unknown, label: string): FoliageLayer {
return createFoliageLayer({
id: expectString(value.id, `${label}.id`),
name: expectString(value.name, `${label}.name`),
prototypeIds: expectStringArray(value.prototypeIds, `${label}.prototypeIds`),
prototypeIds: expectStringArray(
value.prototypeIds,
`${label}.prototypeIds`
),
density: expectNonNegativeFiniteNumber(value.density, `${label}.density`),
minScale: expectNonNegativeFiniteNumber(value.minScale, `${label}.minScale`),
maxScale: expectNonNegativeFiniteNumber(value.maxScale, `${label}.maxScale`),
minScale: expectNonNegativeFiniteNumber(
value.minScale,
`${label}.minScale`
),
maxScale: expectNonNegativeFiniteNumber(
value.maxScale,
`${label}.maxScale`
),
minSlopeDegrees: expectFiniteNumber(
value.minSlopeDegrees,
`${label}.minSlopeDegrees`
@@ -2615,7 +2633,9 @@ function readFoliageLayers(value: unknown): SceneDocument["foliageLayers"] {
const layer = readFoliageLayer(layerValue, `foliageLayers.${layerId}`);
if (layer.id !== layerId) {
throw new Error(`foliageLayers.${layerId}.id must match the registry key.`);
throw new Error(
`foliageLayers.${layerId}.id must match the registry key.`
);
}
layers[layerId] = layer;

View File

@@ -1242,8 +1242,7 @@ export function createPlayerStartInputBindings(
overrides.keyboard?.crouch ??
DEFAULT_PLAYER_START_KEYBOARD_BINDINGS.crouch,
climb:
overrides.keyboard?.climb ??
DEFAULT_PLAYER_START_KEYBOARD_BINDINGS.climb,
overrides.keyboard?.climb ?? DEFAULT_PLAYER_START_KEYBOARD_BINDINGS.climb,
interact:
overrides.keyboard?.interact ??
DEFAULT_PLAYER_START_KEYBOARD_BINDINGS.interact,
@@ -1485,8 +1484,7 @@ export function createPlayerStartMovementTemplate(
const edgeAssist: PlayerStartEdgeAssistSettings = {
enabled: overrides.edgeAssist?.enabled ?? preset.edgeAssist.enabled,
pushToTopHeight:
overrides.edgeAssist?.pushToTopHeight ??
preset.edgeAssist.pushToTopHeight
overrides.edgeAssist?.pushToTopHeight ?? preset.edgeAssist.pushToTopHeight
};
assertPositiveFiniteNumber(moveSpeed, "Player Start move speed");

View File

@@ -747,7 +747,10 @@ export class RuntimeHost {
string,
Mesh<BufferGeometry, Material[]>
>();
private readonly terrainMeshes = new Map<string, RuntimeTerrainRenderObjects>();
private readonly terrainMeshes = new Map<
string,
RuntimeTerrainRenderObjects
>();
private volumeTime = 0;
private readonly volumeAnimatedUniforms: Array<{ value: number }> = [];
private readonly runtimeWaterContactUniforms: RuntimeWaterContactUniformBinding[] =
@@ -4126,7 +4129,11 @@ export class RuntimeHost {
const group = new Group();
const chunks: RuntimeTerrainRenderChunkObjects[] = [];
group.position.set(terrain.position.x, terrain.position.y, terrain.position.z);
group.position.set(
terrain.position.x,
terrain.position.y,
terrain.position.z
);
group.visible = terrain.visible;
for (const chunk of lodMeshData.chunks) {
@@ -4214,7 +4221,9 @@ export class RuntimeHost {
});
}
private createRuntimeTerrainDistantMaterial(terrain: RuntimeTerrain): Material {
private createRuntimeTerrainDistantMaterial(
terrain: RuntimeTerrain
): Material {
const layerColors = terrain.layers.map((layer) =>
getTerrainLayerPreviewColor(layer.material)
) as [number, number, number, number];
@@ -5948,7 +5957,9 @@ export class RuntimeHost {
}
private resolveRuntimePlayerInputBindings() {
return createPlayerStartInputBindings(this.runtimeScene?.playerInputBindings);
return createPlayerStartInputBindings(
this.runtimeScene?.playerInputBindings
);
}
private resolveRuntimeTargetVisibilityClearance(target: {
@@ -6033,8 +6044,7 @@ export class RuntimeHost {
const sampleClearance =
Math.max(radius, TARGETING_VISIBILITY_TARGET_CLEARANCE) +
TARGETING_VISIBILITY_TARGET_CLEARANCE_PADDING;
const yAt = (factor: number) =>
npc.position.y + height * factor;
const yAt = (factor: number) => npc.position.y + height * factor;
return [
{
@@ -6060,13 +6070,9 @@ export class RuntimeHost {
z: collider.size.z * npc.scale.z
};
const sampleClearance =
clampScalar(
Math.max(size.x, size.y, size.z) * 0.25,
0.35,
0.75
) + TARGETING_VISIBILITY_TARGET_CLEARANCE_PADDING;
const yAt = (factor: number) =>
npc.position.y + size.y * factor;
clampScalar(Math.max(size.x, size.y, size.z) * 0.25, 0.35, 0.75) +
TARGETING_VISIBILITY_TARGET_CLEARANCE_PADDING;
const yAt = (factor: number) => npc.position.y + size.y * factor;
return [
{
@@ -6272,7 +6278,9 @@ export class RuntimeHost {
return false;
}
const screenPoint = this.resolveRuntimeTargetScreenPoint(candidate.center);
const screenPoint = this.resolveRuntimeTargetScreenPoint(
candidate.center
);
return (
screenPoint !== null &&

View File

@@ -1,9 +1,7 @@
import type { Vec3 } from "../core/vector";
import { Euler, Quaternion, Vector3 } from "three";
import type { ControlEffect } from "../controls/control-surface";
import {
type InteractionLink
} from "../interactions/interaction-links";
import { type InteractionLink } from "../interactions/interaction-links";
import {
getInteractionLinkImpulseSteps,
type ImpulseSequenceStep,
@@ -380,7 +378,9 @@ function resolveEffectiveInteractionTrigger(
runtimeScene.entities.interactables.some(
(entity) => entity.entityId === link.sourceEntityId
) ||
runtimeScene.entities.npcs.some((entity) => entity.entityId === link.sourceEntityId)
runtimeScene.entities.npcs.some(
(entity) => entity.entityId === link.sourceEntityId
)
) {
return "click";
}
@@ -402,10 +402,7 @@ function getInteractableTargetRadius(
return Math.min(DEFAULT_INTERACTABLE_TARGET_RADIUS, interactable.radius);
}
function getNpcDialoguePrompt(
npc: RuntimeNpc,
hasClickLinks: boolean
): string {
function getNpcDialoguePrompt(npc: RuntimeNpc, hasClickLinks: boolean): string {
const trimmedName = npc.name?.trim() ?? "";
const hasNpcDialogue =
npc.defaultDialogueId !== null || npc.dialogues.length > 0;
@@ -451,10 +448,7 @@ function getNpcDialogueTargetBounds(npc: RuntimeNpc): {
y: npc.position.y + height * 0.5,
z: npc.position.z
},
range: Math.max(
DEFAULT_NPC_DIALOGUE_TARGET_RADIUS,
height * 0.5
)
range: Math.max(DEFAULT_NPC_DIALOGUE_TARGET_RADIUS, height * 0.5)
};
}
case "box": {
@@ -558,10 +552,9 @@ function getNpcHorizontalTargetRadius(
) * 0.5
);
case "none":
return Math.max(
bounds.max.x - bounds.min.x,
bounds.max.z - bounds.min.z
) * 0.5;
return (
Math.max(bounds.max.x - bounds.min.x, bounds.max.z - bounds.min.z) * 0.5
);
}
}
@@ -583,7 +576,10 @@ function collectRuntimeInteractionTargetSources(
continue;
}
const distance = distanceBetweenVec3(interactionOrigin, interactable.position);
const distance = distanceBetweenVec3(
interactionOrigin,
interactable.position
);
const horizontalDistance = distanceBetweenVec2(
{
x: interactionOrigin.x,
@@ -708,7 +704,10 @@ export function resolveRuntimeTargetCandidates(options: {
const interactionDistanceScore =
1 / (1 + source.distance / Math.max(source.range, 0.001));
const acquisitionDistanceScore =
1 - clampUnitInterval(source.distance / Math.max(source.acquisitionRange, 0.001));
1 -
clampUnitInterval(
source.distance / Math.max(source.acquisitionRange, 0.001)
);
const cameraDistanceScore = 1 / (1 + cameraDistance * 0.12);
const stabilityBonus = source.entityId === previousId ? 0.12 : 0;
const score =
@@ -919,7 +918,8 @@ export class RuntimeInteractionSystem {
const offsetX = candidate.center.x - interactionOrigin.x;
const offsetZ = candidate.center.z - interactionOrigin.z;
const forwardDistance =
offsetX * horizontalViewDirection.x + offsetZ * horizontalViewDirection.y;
offsetX * horizontalViewDirection.x +
offsetZ * horizontalViewDirection.y;
const lateralDistance =
offsetX * -horizontalViewDirection.y +
offsetZ * horizontalViewDirection.x;
@@ -930,7 +930,8 @@ export class RuntimeInteractionSystem {
coneSlope * paddedForwardDistance +
candidate.horizontalRadius;
const minForwardDistance =
-DEFAULT_INTERACTION_PROMPT_NEAR_FIELD_RADIUS - candidate.horizontalRadius;
-DEFAULT_INTERACTION_PROMPT_NEAR_FIELD_RADIUS -
candidate.horizontalRadius;
const maxForwardDistance =
interactionReachMeters +
DEFAULT_INTERACTION_PROMPT_NEAR_FIELD_RADIUS +
@@ -1009,7 +1010,10 @@ export class RuntimeInteractionSystem {
continue;
}
for (const step of getInteractionLinkImpulseSteps(link, runtimeScene.sequences)) {
for (const step of getInteractionLinkImpulseSteps(
link,
runtimeScene.sequences
)) {
this.dispatchSequenceStep(step, link, runtimeScene, dispatcher);
}
}