auto-git:

[change] src/app/App.tsx
 [change] src/document/migrate-scene-document.ts
 [change] src/document/scene-document-validation.ts
 [change] src/rendering/world-background-renderer.ts
 [change] src/rendering/world-shader-sky.ts
 [change] tests/domain/scene-document-validation.test.ts
This commit is contained in:
2026-04-25 01:37:00 +02:00
parent a8f89f8007
commit 013c944fc4
7 changed files with 198 additions and 187 deletions

View File

@@ -2539,9 +2539,7 @@ export function App({ store, initialStatusMessage }: AppProps) {
label: getScenePathLabel(path, index) label: getScenePathLabel(path, index)
})); }));
const getCameraRigPathById = (pathId: string) => const getCameraRigPathById = (pathId: string) =>
editorState.document.paths[pathId] ?? editorState.document.paths[pathId] ?? cameraRigPathOptions[0]?.path ?? null;
cameraRigPathOptions[0]?.path ??
null;
const getDefaultCameraRigRailMappingDraft = (pathId: string) => { const getDefaultCameraRigRailMappingDraft = (pathId: string) => {
const authoredPath = getCameraRigPathById(pathId); const authoredPath = getCameraRigPathById(pathId);
const startPoint = const startPoint =
@@ -2657,12 +2655,10 @@ export function App({ store, initialStatusMessage }: AppProps) {
const [cameraRigRigTypeDraft, setCameraRigRigTypeDraft] = const [cameraRigRigTypeDraft, setCameraRigRigTypeDraft] =
useState<CameraRigType>("fixed"); useState<CameraRigType>("fixed");
const [cameraRigPathIdDraft, setCameraRigPathIdDraft] = useState(""); const [cameraRigPathIdDraft, setCameraRigPathIdDraft] = useState("");
const [ const [cameraRigRailPlacementModeDraft, setCameraRigRailPlacementModeDraft] =
cameraRigRailPlacementModeDraft, useState<CameraRigRailPlacementMode>(
setCameraRigRailPlacementModeDraft DEFAULT_CAMERA_RIG_RAIL_PLACEMENT_MODE
] = useState<CameraRigRailPlacementMode>( );
DEFAULT_CAMERA_RIG_RAIL_PLACEMENT_MODE
);
const [cameraRigTrackStartPointDraft, setCameraRigTrackStartPointDraft] = const [cameraRigTrackStartPointDraft, setCameraRigTrackStartPointDraft] =
useState(createVec3Draft(DEFAULT_CAMERA_RIG_TRACK_START_POINT)); useState(createVec3Draft(DEFAULT_CAMERA_RIG_TRACK_START_POINT));
const [cameraRigTrackEndPointDraft, setCameraRigTrackEndPointDraft] = const [cameraRigTrackEndPointDraft, setCameraRigTrackEndPointDraft] =
@@ -3682,14 +3678,14 @@ export function App({ store, initialStatusMessage }: AppProps) {
const selectedEntityPosition = const selectedEntityPosition =
selectedEntity.kind === "cameraRig" selectedEntity.kind === "cameraRig"
? resolveCameraRigDocumentPosition( ? (resolveCameraRigDocumentPosition(
selectedEntity, selectedEntity,
editorState.document.entities, editorState.document.entities,
editorState.document.paths, editorState.document.paths,
{ {
fallbackToPathStart: true fallbackToPathStart: true
} }
) ?? DEFAULT_ENTITY_POSITION ) ?? DEFAULT_ENTITY_POSITION)
: selectedEntity.position; : selectedEntity.position;
setEntityPositionDraft(createVec3Draft(selectedEntityPosition)); setEntityPositionDraft(createVec3Draft(selectedEntityPosition));
@@ -9443,7 +9439,10 @@ export function App({ store, initialStatusMessage }: AppProps) {
const effectOptionId = const effectOptionId =
options.effectOptionId ?? options.effectOptionId ??
effectOptions.find((effectOption) => { effectOptions.find((effectOption) => {
if (options.previousEffect === undefined || options.previousEffect === null) { if (
options.previousEffect === undefined ||
options.previousEffect === null
) {
return false; return false;
} }
@@ -9563,7 +9562,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
createUpsertInteractionLinkCommand({ createUpsertInteractionLinkCommand({
link: createInteractionControlLinkFromTargetOption({ link: createInteractionControlLinkFromTargetOption({
sourceEntity: selectedInteractionSource, sourceEntity: selectedInteractionSource,
trigger: getDefaultInteractionLinkTrigger(selectedInteractionSource), trigger: getDefaultInteractionLinkTrigger(
selectedInteractionSource
),
targetOption targetOption
}), }),
label: "Add control interaction link" label: "Add control interaction link"
@@ -10413,9 +10414,12 @@ export function App({ store, initialStatusMessage }: AppProps) {
return; return;
} }
const effectOptions = listProjectInteractionControlEffectOptions(targetOption); const effectOptions =
listProjectInteractionControlEffectOptions(targetOption);
if (!effectOptions.some((effectOption) => effectOption.id === effectOptionId)) { if (
!effectOptions.some((effectOption) => effectOption.id === effectOptionId)
) {
setStatusMessage( setStatusMessage(
"Selected control effect is not available for the current target." "Selected control effect is not available for the current target."
); );
@@ -11086,7 +11090,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
const effectOptions = const effectOptions =
targetOption === null targetOption === null
? [] ? []
: listProjectInteractionControlEffectOptions(targetOption); : listProjectInteractionControlEffectOptions(
targetOption
);
const effectOptionId = const effectOptionId =
targetOption === null targetOption === null
? null ? null
@@ -11137,7 +11143,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
<input <input
className="text-input" className="text-input"
type="text" type="text"
value={formatControlEffectValue(link.action.effect)} value={formatControlEffectValue(
link.action.effect
)}
readOnly readOnly
/> />
</label> </label>
@@ -11205,7 +11213,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
min={selectedEffectOption.min ?? 0} min={selectedEffectOption.min ?? 0}
step={selectedEffectOption.step ?? 0.1} step={selectedEffectOption.step ?? 0.1}
defaultValue={ defaultValue={
getControlEffectNumericValue(link.action.effect) ?? 0 getControlEffectNumericValue(
link.action.effect
) ?? 0
} }
onBlur={(event) => onBlur={(event) =>
updateControlInteractionLinkNumericValue( updateControlInteractionLinkNumericValue(
@@ -11264,10 +11274,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
) )
} }
> >
{( {(link.action.effect.type === "playActorAnimation"
link.action.effect.type === "playActorAnimation" ? targetOption.defaults.actorAnimationClipNames
? targetOption.defaults.actorAnimationClipNames : targetOption.defaults.animationClipNames
: targetOption.defaults.animationClipNames
)?.map((clipName) => ( )?.map((clipName) => (
<option key={clipName} value={clipName}> <option key={clipName} value={clipName}>
{clipName} {clipName}
@@ -19028,138 +19037,138 @@ export function App({ store, initialStatusMessage }: AppProps) {
} }
/> />
</label> </label>
<div className="material-summary"> <div className="material-summary">
Hidden entities stay active for authored runtime behavior. Hidden entities stay active for authored runtime behavior.
Disable them to remove them from the editor viewport, Disable them to remove them from the editor viewport,
picking, and runtime build. picking, and runtime build.
</div>
</div> </div>
</div>
{selectedEntity.kind !== "cameraRig" || {selectedEntity.kind !== "cameraRig" ||
selectedEntity.rigType === "fixed" ? ( selectedEntity.rigType === "fixed" ? (
<div className="form-section"> <div className="form-section">
<div className="label">Position</div> <div className="label">Position</div>
<div className="vector-inputs"> <div className="vector-inputs">
<label className="form-field"> <label className="form-field">
<span className="label">X</span> <span className="label">X</span>
<input <input
data-testid={ data-testid={
selectedEntity.kind === "playerStart" selectedEntity.kind === "playerStart"
? "player-start-position-x" ? "player-start-position-x"
: `${selectedEntity.kind}-position-x` : `${selectedEntity.kind}-position-x`
} }
className="text-input" className="text-input"
type="number" type="number"
step={DEFAULT_GRID_SIZE} step={DEFAULT_GRID_SIZE}
value={entityPositionDraft.x} value={entityPositionDraft.x}
onChange={(event) => { onChange={(event) => {
const nextValue = event.currentTarget.value; const nextValue = event.currentTarget.value;
setEntityPositionDraft((draft) => ({ setEntityPositionDraft((draft) => ({
...draft, ...draft,
x: nextValue x: nextValue
})); }));
}} }}
onBlur={applySelectedEntityDraftChange} onBlur={applySelectedEntityDraftChange}
onKeyDown={(event) => onKeyDown={(event) =>
handleDraftVectorKeyDown( handleDraftVectorKeyDown(
event, event,
applySelectedEntityDraftChange applySelectedEntityDraftChange
) )
} }
onKeyUp={(event) => onKeyUp={(event) =>
handleNumberInputKeyUp( handleNumberInputKeyUp(
event, event,
applySelectedEntityDraftChange applySelectedEntityDraftChange
) )
} }
onPointerUp={(event) => onPointerUp={(event) =>
handleNumberInputPointerUp( handleNumberInputPointerUp(
event, event,
applySelectedEntityDraftChange applySelectedEntityDraftChange
) )
} }
/> />
</label> </label>
<label className="form-field"> <label className="form-field">
<span className="label">Y</span> <span className="label">Y</span>
<input <input
data-testid={ data-testid={
selectedEntity.kind === "playerStart" selectedEntity.kind === "playerStart"
? "player-start-position-y" ? "player-start-position-y"
: `${selectedEntity.kind}-position-y` : `${selectedEntity.kind}-position-y`
} }
className="text-input" className="text-input"
type="number" type="number"
step={DEFAULT_GRID_SIZE} step={DEFAULT_GRID_SIZE}
value={entityPositionDraft.y} value={entityPositionDraft.y}
onChange={(event) => { onChange={(event) => {
const nextValue = event.currentTarget.value; const nextValue = event.currentTarget.value;
setEntityPositionDraft((draft) => ({ setEntityPositionDraft((draft) => ({
...draft, ...draft,
y: nextValue y: nextValue
})); }));
}} }}
onBlur={applySelectedEntityDraftChange} onBlur={applySelectedEntityDraftChange}
onKeyDown={(event) => onKeyDown={(event) =>
handleDraftVectorKeyDown( handleDraftVectorKeyDown(
event, event,
applySelectedEntityDraftChange applySelectedEntityDraftChange
) )
} }
onKeyUp={(event) => onKeyUp={(event) =>
handleNumberInputKeyUp( handleNumberInputKeyUp(
event, event,
applySelectedEntityDraftChange applySelectedEntityDraftChange
) )
} }
onPointerUp={(event) => onPointerUp={(event) =>
handleNumberInputPointerUp( handleNumberInputPointerUp(
event, event,
applySelectedEntityDraftChange applySelectedEntityDraftChange
) )
} }
/> />
</label> </label>
<label className="form-field"> <label className="form-field">
<span className="label">Z</span> <span className="label">Z</span>
<input <input
data-testid={ data-testid={
selectedEntity.kind === "playerStart" selectedEntity.kind === "playerStart"
? "player-start-position-z" ? "player-start-position-z"
: `${selectedEntity.kind}-position-z` : `${selectedEntity.kind}-position-z`
} }
className="text-input" className="text-input"
type="number" type="number"
step={DEFAULT_GRID_SIZE} step={DEFAULT_GRID_SIZE}
value={entityPositionDraft.z} value={entityPositionDraft.z}
onChange={(event) => { onChange={(event) => {
const nextValue = event.currentTarget.value; const nextValue = event.currentTarget.value;
setEntityPositionDraft((draft) => ({ setEntityPositionDraft((draft) => ({
...draft, ...draft,
z: nextValue z: nextValue
})); }));
}} }}
onBlur={applySelectedEntityDraftChange} onBlur={applySelectedEntityDraftChange}
onKeyDown={(event) => onKeyDown={(event) =>
handleDraftVectorKeyDown( handleDraftVectorKeyDown(
event, event,
applySelectedEntityDraftChange applySelectedEntityDraftChange
) )
} }
onKeyUp={(event) => onKeyUp={(event) =>
handleNumberInputKeyUp( handleNumberInputKeyUp(
event, event,
applySelectedEntityDraftChange applySelectedEntityDraftChange
) )
} }
onPointerUp={(event) => onPointerUp={(event) =>
handleNumberInputPointerUp( handleNumberInputPointerUp(
event, event,
applySelectedEntityDraftChange applySelectedEntityDraftChange
) )
} }
/> />
</label> </label>
</div> </div>
</div> </div>
) : null} ) : null}

View File

@@ -3374,7 +3374,10 @@ function readCameraRigEntity(value: unknown, label: string): EntityInstance {
trackStartPoint: trackStartPoint:
value.trackStartPoint === undefined value.trackStartPoint === undefined
? undefined ? undefined
: readVec3(value.trackStartPoint, `${label}.trackStartPoint`), : readVec3(
value.trackStartPoint,
`${label}.trackStartPoint`
),
trackEndPoint: trackEndPoint:
value.trackEndPoint === undefined value.trackEndPoint === undefined
? undefined ? undefined

View File

@@ -3878,7 +3878,10 @@ function validateCameraRigControlTarget(
return; return;
} }
if (targetEntity.kind !== target.entityKind || targetEntity.kind !== "cameraRig") { if (
targetEntity.kind !== target.entityKind ||
targetEntity.kind !== "cameraRig"
) {
diagnostics.push( diagnostics.push(
createDiagnostic( createDiagnostic(
"error", "error",

View File

@@ -904,14 +904,11 @@ function applyShaderSkyStateToMaterial(
material.uniforms.uAuroraHeight.value = state.aurora.height; material.uniforms.uAuroraHeight.value = state.aurora.height;
material.uniforms.uAuroraThickness.value = state.aurora.thickness; material.uniforms.uAuroraThickness.value = state.aurora.thickness;
material.uniforms.uAuroraSpeed.value = state.aurora.speed; material.uniforms.uAuroraSpeed.value = state.aurora.speed;
material.uniforms.uAuroraPrimaryColor.value.set( material.uniforms.uAuroraPrimaryColor.value.set(state.aurora.primaryColorHex);
state.aurora.primaryColorHex
);
material.uniforms.uAuroraSecondaryColor.value.set( material.uniforms.uAuroraSecondaryColor.value.set(
state.aurora.secondaryColorHex state.aurora.secondaryColorHex
); );
material.uniforms.uAuroraRotationRadians.value = material.uniforms.uAuroraRotationRadians.value = state.aurora.rotationRadians;
state.aurora.rotationRadians;
material.uniforms.uAuroraTimeHours.value = state.aurora.timeHours; material.uniforms.uAuroraTimeHours.value = state.aurora.timeHours;
} }

View File

@@ -315,11 +315,10 @@ export function resolveWorldShaderSkyRenderState(
const starVisibility = const starVisibility =
clamp(phaseWeights.night + twilightFactor * 0.45, 0, 1) * clamp(phaseWeights.night + twilightFactor * 0.45, 0, 1) *
clamp(1 - resolvedWorld.daylightFactor * 0.85, 0, 1); clamp(1 - resolvedWorld.daylightFactor * 0.85, 0, 1);
const auroraVisibility = const auroraVisibility = world.shaderSky.aurora.enabled
world.shaderSky.aurora.enabled ? clamp(phaseWeights.night + twilightFactor * 0.32, 0, 1) *
? clamp(phaseWeights.night + twilightFactor * 0.32, 0, 1) * clamp(1 - resolvedWorld.daylightFactor * 1.15, 0, 1)
clamp(1 - resolvedWorld.daylightFactor * 1.15, 0, 1) : 0;
: 0;
const cloudDriftDirectionRadians = const cloudDriftDirectionRadians =
(world.shaderSky.clouds.driftDirectionDegrees * Math.PI) / 180; (world.shaderSky.clouds.driftDirectionDegrees * Math.PI) / 180;
const cloudDriftDistance = const cloudDriftDistance =

View File

@@ -445,15 +445,14 @@ describe("validateSceneDocument", () => {
target: createCameraRigControlTargetRef(cameraRig.id) target: createCameraRigControlTargetRef(cameraRig.id)
}) })
}); });
document.interactionLinks["link-camera-override"] = createControlInteractionLink( document.interactionLinks["link-camera-override"] =
{ createControlInteractionLink({
id: "link-camera-override", id: "link-camera-override",
sourceEntityId: triggerVolume.id, sourceEntityId: triggerVolume.id,
effect: createActivateCameraRigOverrideControlEffect({ effect: createActivateCameraRigOverrideControlEffect({
target: createCameraRigControlTargetRef(cameraRig.id) target: createCameraRigControlTargetRef(cameraRig.id)
}) })
} });
);
const validation = validateSceneDocument(document); const validation = validateSceneDocument(document);
@@ -1421,7 +1420,8 @@ describe("validateSceneDocument", () => {
document.world.shaderSky.celestial.sunDiscSizeDegrees = 0; document.world.shaderSky.celestial.sunDiscSizeDegrees = 0;
document.world.shaderSky.clouds.coverage = 2; document.world.shaderSky.clouds.coverage = 2;
document.world.shaderSky.aurora.intensity = -1; document.world.shaderSky.aurora.intensity = -1;
document.world.shaderSky.aurora.primaryColorHex = "bad-color" as `#${string}`; document.world.shaderSky.aurora.primaryColorHex =
"bad-color" as `#${string}`;
document.world.timeOfDay.dawn.background = { document.world.timeOfDay.dawn.background = {
mode: "shader" mode: "shader"
} as typeof document.world.timeOfDay.dawn.background; } as typeof document.world.timeOfDay.dawn.background;

View File

@@ -17,12 +17,12 @@ import { createScenePath } from "../../src/document/paths";
import { createDefaultProjectTimeSettings } from "../../src/document/project-time-settings"; import { createDefaultProjectTimeSettings } from "../../src/document/project-time-settings";
import { createTerrain } from "../../src/document/terrains"; import { createTerrain } from "../../src/document/terrains";
import { import {
AUTHORED_TERRAIN_PAINT_SCENE_DOCUMENT_VERSION, AUTHORED_TERRAIN_PAINT_SCENE_DOCUMENT_VERSION,
AUTHORED_TERRAIN_FOUNDATION_SCENE_DOCUMENT_VERSION, AUTHORED_TERRAIN_FOUNDATION_SCENE_DOCUMENT_VERSION,
CAMERA_RIG_MAPPED_RAIL_SCENE_DOCUMENT_VERSION, CAMERA_RIG_MAPPED_RAIL_SCENE_DOCUMENT_VERSION,
CAMERA_RIG_CONTROL_SURFACE_SCENE_DOCUMENT_VERSION, CAMERA_RIG_CONTROL_SURFACE_SCENE_DOCUMENT_VERSION,
CAMERA_RIG_ENTITY_SCENE_DOCUMENT_VERSION, CAMERA_RIG_ENTITY_SCENE_DOCUMENT_VERSION,
CELESTIAL_BODY_OVERLAY_SCENE_DOCUMENT_VERSION, CELESTIAL_BODY_OVERLAY_SCENE_DOCUMENT_VERSION,
DAWN_DUSK_BACKGROUND_IMAGE_SCENE_DOCUMENT_VERSION, DAWN_DUSK_BACKGROUND_IMAGE_SCENE_DOCUMENT_VERSION,
FOLLOW_ACTOR_PATH_SMOOTH_SCENE_DOCUMENT_VERSION, FOLLOW_ACTOR_PATH_SMOOTH_SCENE_DOCUMENT_VERSION,
ANIMATION_PLAYBACK_SCENE_DOCUMENT_VERSION, ANIMATION_PLAYBACK_SCENE_DOCUMENT_VERSION,
@@ -141,28 +141,28 @@ describe("scene document JSON", () => {
const document = createEmptySceneDocument({ name: "Camera Control Scene" }); const document = createEmptySceneDocument({ name: "Camera Control Scene" });
document.entities[cameraRig.id] = cameraRig; document.entities[cameraRig.id] = cameraRig;
document.entities[triggerVolume.id] = triggerVolume; document.entities[triggerVolume.id] = triggerVolume;
document.interactionLinks["link-camera-activate"] = createControlInteractionLink( document.interactionLinks["link-camera-activate"] =
{ createControlInteractionLink({
id: "link-camera-activate", id: "link-camera-activate",
sourceEntityId: triggerVolume.id, sourceEntityId: triggerVolume.id,
effect: createActivateCameraRigOverrideControlEffect({ effect: createActivateCameraRigOverrideControlEffect({
target: createCameraRigControlTargetRef(cameraRig.id) target: createCameraRigControlTargetRef(cameraRig.id)
}) })
} });
); document.sequences.sequences["sequence-camera-clear"] =
document.sequences.sequences["sequence-camera-clear"] = createProjectSequence({ createProjectSequence({
id: "sequence-camera-clear", id: "sequence-camera-clear",
title: "Camera Clear", title: "Camera Clear",
steps: [ steps: [
{ {
stepClass: "held", stepClass: "held",
type: "controlEffect", type: "controlEffect",
effect: createClearCameraRigOverrideControlEffect({ effect: createClearCameraRigOverrideControlEffect({
target: createCameraRigControlTargetRef(cameraRig.id) target: createCameraRigControlTargetRef(cameraRig.id)
}) })
} }
] ]
}); });
document.scheduler.routines["routine-camera-activate"] = document.scheduler.routines["routine-camera-activate"] =
createProjectScheduleRoutine({ createProjectScheduleRoutine({
id: "routine-camera-activate", id: "routine-camera-activate",