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:
191
src/app/App.tsx
191
src/app/App.tsx
@@ -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>
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user