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

View File

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

View File

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

View File

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

View File

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

View File

@@ -445,15 +445,14 @@ describe("validateSceneDocument", () => {
target: createCameraRigControlTargetRef(cameraRig.id)
})
});
document.interactionLinks["link-camera-override"] = createControlInteractionLink(
{
document.interactionLinks["link-camera-override"] =
createControlInteractionLink({
id: "link-camera-override",
sourceEntityId: triggerVolume.id,
effect: createActivateCameraRigOverrideControlEffect({
target: createCameraRigControlTargetRef(cameraRig.id)
})
}
);
});
const validation = validateSceneDocument(document);
@@ -1421,7 +1420,8 @@ describe("validateSceneDocument", () => {
document.world.shaderSky.celestial.sunDiscSizeDegrees = 0;
document.world.shaderSky.clouds.coverage = 2;
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 = {
mode: "shader"
} 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 { createTerrain } from "../../src/document/terrains";
import {
AUTHORED_TERRAIN_PAINT_SCENE_DOCUMENT_VERSION,
AUTHORED_TERRAIN_FOUNDATION_SCENE_DOCUMENT_VERSION,
CAMERA_RIG_MAPPED_RAIL_SCENE_DOCUMENT_VERSION,
CAMERA_RIG_CONTROL_SURFACE_SCENE_DOCUMENT_VERSION,
CAMERA_RIG_ENTITY_SCENE_DOCUMENT_VERSION,
CELESTIAL_BODY_OVERLAY_SCENE_DOCUMENT_VERSION,
AUTHORED_TERRAIN_PAINT_SCENE_DOCUMENT_VERSION,
AUTHORED_TERRAIN_FOUNDATION_SCENE_DOCUMENT_VERSION,
CAMERA_RIG_MAPPED_RAIL_SCENE_DOCUMENT_VERSION,
CAMERA_RIG_CONTROL_SURFACE_SCENE_DOCUMENT_VERSION,
CAMERA_RIG_ENTITY_SCENE_DOCUMENT_VERSION,
CELESTIAL_BODY_OVERLAY_SCENE_DOCUMENT_VERSION,
DAWN_DUSK_BACKGROUND_IMAGE_SCENE_DOCUMENT_VERSION,
FOLLOW_ACTOR_PATH_SMOOTH_SCENE_DOCUMENT_VERSION,
ANIMATION_PLAYBACK_SCENE_DOCUMENT_VERSION,
@@ -141,28 +141,28 @@ describe("scene document JSON", () => {
const document = createEmptySceneDocument({ name: "Camera Control Scene" });
document.entities[cameraRig.id] = cameraRig;
document.entities[triggerVolume.id] = triggerVolume;
document.interactionLinks["link-camera-activate"] = createControlInteractionLink(
{
document.interactionLinks["link-camera-activate"] =
createControlInteractionLink({
id: "link-camera-activate",
sourceEntityId: triggerVolume.id,
effect: createActivateCameraRigOverrideControlEffect({
target: createCameraRigControlTargetRef(cameraRig.id)
})
}
);
document.sequences.sequences["sequence-camera-clear"] = createProjectSequence({
id: "sequence-camera-clear",
title: "Camera Clear",
steps: [
{
stepClass: "held",
type: "controlEffect",
effect: createClearCameraRigOverrideControlEffect({
target: createCameraRigControlTargetRef(cameraRig.id)
})
}
]
});
});
document.sequences.sequences["sequence-camera-clear"] =
createProjectSequence({
id: "sequence-camera-clear",
title: "Camera Clear",
steps: [
{
stepClass: "held",
type: "controlEffect",
effect: createClearCameraRigOverrideControlEffect({
target: createCameraRigControlTargetRef(cameraRig.id)
})
}
]
});
document.scheduler.routines["routine-camera-activate"] =
createProjectScheduleRoutine({
id: "routine-camera-activate",