mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
fix(director): restore layer visibility on same-shot seeks
This commit is contained in:
1 parent
1c234ce027
commit
1644acd2a3
5 files changed
+133
-4
No files matched your search
@@ -0,0 +1,3 @@
|
||||
- Restore a shot's declared layer visibility when seeking within it after manual
|
||||
layer changes. Refused restoration reports a failed seek, while undeclared
|
||||
operator layers and the unchanged-stack shortcut are preserved (@oneill9).
|
||||
@@ -506,6 +506,10 @@ dateline while preserving both authored endpoint longitudes, and turns heading
|
||||
and roll the short way for any authored angles.
|
||||
A seek to a shot's start, as a shot action makes, selects that shot rather than
|
||||
the end of the previous one.
|
||||
Same-shot seeking restores declared layer visibility after a manual toggle,
|
||||
using the cancellable layer admission path and reporting refused restoration
|
||||
as a failed seek. Undeclared operator layers remain untouched, and unchanged
|
||||
declared visibility retains the lightweight seek path.
|
||||
See [camera directions](DIRECTOR-CAMERA.md).
|
||||
|
||||
Realtime voice composes separate connection, response/tool, Radio handoff, input
|
||||
|
||||
@@ -88,6 +88,57 @@ try {
|
||||
};
|
||||
});
|
||||
for (const [name, passed] of Object.entries(result)) check(name, passed);
|
||||
const visibilityRestored = await page.evaluate(async () => {
|
||||
const { sceneDirector: director, dataManager } = window.__godsEyeView;
|
||||
const layerId = 'director-seek-visibility-qa';
|
||||
window.__gevQaRegisterLayer(dataManager, {
|
||||
id: layerId,
|
||||
name: 'Director seek visibility QA',
|
||||
updateInterval: -1,
|
||||
init() {},
|
||||
enable() {},
|
||||
disable() {},
|
||||
update() {},
|
||||
destroy() {},
|
||||
getStats: () => ({ count: 0 }),
|
||||
});
|
||||
const scene = {
|
||||
id: 'visibility-qa',
|
||||
title: 'Visibility QA',
|
||||
shots: [
|
||||
{
|
||||
id: 'visibility-shot',
|
||||
title: 'Visibility shot',
|
||||
durationSec: 0.4,
|
||||
holdSec: 0,
|
||||
camera: director.styleManager.getCameraState(),
|
||||
visual: director.styleManager.getVisualState(),
|
||||
layers: { [layerId]: { enabled: true } },
|
||||
},
|
||||
],
|
||||
};
|
||||
director._project.scenes.push(scene);
|
||||
try {
|
||||
const loaded = await director.loadShot(scene.id, scene.shots[0].id, {
|
||||
flyDuration: 0.2,
|
||||
});
|
||||
await dataManager.setEnabled(layerId, false, { origin: 'user' });
|
||||
const sought = await director.seekScene(scene.id, 0.5);
|
||||
return (
|
||||
loaded?.started && sought && dataManager.layers.get(layerId).enabled
|
||||
);
|
||||
} finally {
|
||||
director.stopScene();
|
||||
director._project.scenes = director._project.scenes.filter(
|
||||
({ id }) => id !== scene.id,
|
||||
);
|
||||
await window.__gevQaUnregisterLayer(dataManager, layerId);
|
||||
}
|
||||
});
|
||||
check(
|
||||
'same-shot seek restores manually changed layer visibility',
|
||||
visibilityRestored,
|
||||
);
|
||||
const content = await page.evaluate(async () => {
|
||||
const d = window.__godsEyeView.sceneDirector;
|
||||
const scene = d._project.scenes.find(({ title }) =>
|
||||
|
||||
+15
-4
@@ -1529,16 +1529,27 @@ export class SceneDirector {
|
||||
this._selectedShotId !== shot.id
|
||||
)
|
||||
return false;
|
||||
const states = this._layerStatesForShot(scene, shot, {
|
||||
cameraSettled: seekState.cameraProgress >= 1,
|
||||
sceneSeek: seekState,
|
||||
});
|
||||
const layers = new Map(
|
||||
this.dataManager.getAll().map((layer) => [layer.id, layer.enabled]),
|
||||
);
|
||||
// A manual toggle invalidates the loaded stack. Let LOAD reconcile it
|
||||
// through the normal cancellable admission path before reporting success.
|
||||
if (
|
||||
sceneLayerPlan(states, new Set(layers.keys())).some(
|
||||
({ id, enabled }) => Boolean(layers.get(id)) !== enabled,
|
||||
)
|
||||
)
|
||||
return false;
|
||||
if (!this._claimCameraOwnership()) return false;
|
||||
this._cancelActiveSceneTravel();
|
||||
this._loadAbort?.abort();
|
||||
this._loadAbort = null;
|
||||
this._loadGeneration += 1;
|
||||
this._clock.stopShot();
|
||||
const states = this._layerStatesForShot(scene, shot, {
|
||||
cameraSettled: seekState.cameraProgress >= 1,
|
||||
sceneSeek: seekState,
|
||||
});
|
||||
let applied = true;
|
||||
for (const [layerId, state] of Object.entries(states)) {
|
||||
if (!state?.enabled || !state.params) continue;
|
||||
|
||||
@@ -174,6 +174,66 @@ test('scene clock seek resolves the exact shot phase and camera in both directio
|
||||
}
|
||||
});
|
||||
|
||||
test('same-shot seeking restores declared layer visibility after manual changes', async () => {
|
||||
const { director, dataManager, restore } = makeDirector();
|
||||
try {
|
||||
const shot = director._project.scenes[0].shots[0];
|
||||
shot.layers = { flights: { enabled: true }, traffic: { enabled: false } };
|
||||
await director.loadShot('scene-1', 'shot-a', { flyDuration: 0 });
|
||||
await dataManager.setEnabled('flights', false);
|
||||
await dataManager.setEnabled('traffic', true);
|
||||
await dataManager.setEnabled('satellites', true);
|
||||
dataManager.setEnabledCalls.length = 0;
|
||||
|
||||
assert.equal(await director.seekScene('scene-1', 0.25), true);
|
||||
const enabled = new Map(dataManager.getAll().map(({ id, enabled }) => [id, enabled]));
|
||||
assert.equal(enabled.get('flights'), true, 'restore the shot\'s enabled layer');
|
||||
assert.equal(enabled.get('traffic'), false, 'restore the shot\'s disabled layer');
|
||||
assert.equal(enabled.get('satellites'), true, 'preserve undeclared operator layers');
|
||||
assert.ok(dataManager.setEnabledCalls.every(({ id }) => id !== 'satellites'));
|
||||
} finally {
|
||||
await director.destroy();
|
||||
restore();
|
||||
}
|
||||
});
|
||||
|
||||
test('same-shot seeking reports refusal when a manually disabled layer cannot be restored', async () => {
|
||||
let refuse = false;
|
||||
const { director, dataManager, restore } = makeDirector({
|
||||
data: { refuse: () => refuse },
|
||||
});
|
||||
try {
|
||||
await director.loadShot('scene-1', 'shot-a', { flyDuration: 0 });
|
||||
await dataManager.setEnabled('flights', false);
|
||||
refuse = true;
|
||||
|
||||
assert.equal(await director.seekScene('scene-1', 0.25), false);
|
||||
assert.equal(dataManager.getAll().find(({ id }) => id === 'flights').enabled, false);
|
||||
assert.equal(director.getPlaybackTimingState().activeTimers, 0);
|
||||
} finally {
|
||||
await director.destroy();
|
||||
restore();
|
||||
}
|
||||
});
|
||||
|
||||
test('same-shot seeking keeps the shortcut when declared visibility is unchanged', async () => {
|
||||
const { director, dataManager, styleManager, restore } = makeDirector();
|
||||
try {
|
||||
await director.loadShot('scene-1', 'shot-a', { flyDuration: 0 });
|
||||
await dataManager.setEnabled('satellites', true);
|
||||
dataManager.setEnabledCalls.length = 0;
|
||||
const visualCalls = styleManager.visualCalls.length;
|
||||
|
||||
assert.equal(await director.seekScene('scene-1', 0.25), true);
|
||||
assert.deepEqual(dataManager.setEnabledCalls, []);
|
||||
assert.equal(styleManager.visualCalls.length, visualCalls);
|
||||
assert.equal(director.getPlaybackTimingState().snapshot.sceneProgress, 0.25);
|
||||
} finally {
|
||||
await director.destroy();
|
||||
restore();
|
||||
}
|
||||
});
|
||||
|
||||
test('scene clock subscribers receive authoritative forward playback snapshots', () => {
|
||||
const { director, restore } = makeDirector();
|
||||
const snapshots = [];
|
||||
|
||||
Reference in new issue
Block a user