create-aura3d 1.1.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/package.json +11 -9
- package/templates/{cartoon-channel → animation-channel}/README.md +11 -11
- package/templates/{cartoon-studio → animation-channel}/index.html +1 -1
- package/templates/{cartoon-channel → animation-channel}/package.json +2 -2
- package/templates/{cartoon-channel → animation-channel}/src/episode.ts +22 -22
- package/templates/{cartoon-channel → animation-channel}/src/experimental/README.md +5 -5
- package/templates/{cartoon-channel → animation-channel}/src/experimental/concept-episode-2-5d.css +3 -3
- package/templates/{cartoon-channel → animation-channel}/src/experimental/concept-episode-2-5d.ts +7 -7
- package/templates/{cartoon-channel → animation-channel}/src/experimental/image-puppet-episode.css +4 -4
- package/templates/{cartoon-channel → animation-channel}/src/experimental/image-puppet-episode.ts +6 -6
- package/templates/{cartoon-channel → animation-channel}/src/experimental/puppet-episode-2d.css +3 -3
- package/templates/{cartoon-channel → animation-channel}/src/experimental/puppet-episode-2d.ts +7 -7
- package/templates/{cartoon-channel → animation-channel}/src/main.ts +32 -32
- package/templates/{cartoon-channel → animation-channel}/src/render-plan.ts +13 -13
- package/templates/{cartoon-channel → animation-channel}/src/sample-episode-visual.css +34 -34
- package/templates/{cartoon-channel → animation-channel}/src/sample-episode-visual.ts +20 -20
- package/templates/{cartoon-channel → animation-channel}/tests/concept-2-5d-animation.spec.ts +3 -3
- package/templates/{cartoon-channel → animation-channel}/tests/concept-2-5d.spec.ts +6 -6
- package/templates/{cartoon-channel → animation-channel}/tests/image-puppet-animation.spec.ts +3 -3
- package/templates/{cartoon-channel → animation-channel}/tests/image-puppet.spec.ts +5 -5
- package/templates/{cartoon-channel → animation-channel}/tests/puppet-2d-animation.spec.ts +3 -3
- package/templates/{cartoon-channel → animation-channel}/tests/puppet-2d.spec.ts +4 -4
- package/templates/{prompt-cartoon-channel → animation-channel}/tests/route-health.spec.ts +1 -1
- package/templates/{cartoon-channel → animation-channel}/tests/sample-episode-visual.spec.ts +5 -5
- package/templates/{cartoon-channel → animation-channel}/tests/storyboard-playback.spec.ts +7 -7
- package/templates/animation-studio/AGENTS.md +71 -0
- package/templates/{cartoon-studio → animation-studio}/ASSET-LICENSES.md +7 -7
- package/templates/{cartoon-studio → animation-studio}/README.md +6 -6
- package/templates/animation-studio/aura.assets.json +669 -0
- package/templates/animation-studio/dist/episodes/scene/episode-3d.webm +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/action.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/dialogue.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/final.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/first.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/mouth-closed.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/mouth-open.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/render-live-summary.json +10284 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-1.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-2.png +0 -0
- package/templates/animation-studio/dist/generated/second-scene.document.json +1210 -0
- package/templates/animation-studio/dist/generated/studio-edited.document.json +1196 -0
- package/templates/animation-studio/dist/scene/inshader-accept.document.json +1 -0
- package/templates/animation-studio/dist/scene/inshader-test.document.json +1 -0
- package/templates/animation-studio/dist/scene/integ.document.json +1 -0
- package/templates/animation-studio/dist/scene/working.document.json +723 -0
- package/templates/animation-studio/dist/scene/working.history.json +1 -0
- package/templates/{cartoon-studio → animation-studio}/live-route.html +1 -1
- package/templates/{cartoon-studio → animation-studio}/package-lock.json +2 -2
- package/templates/animation-studio/package.json +32 -0
- package/templates/animation-studio/public/aura-assets/cast-a.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/cast-b.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/luma.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/luma2.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/miko.catalog.glb +0 -0
- package/templates/{cartoon-studio → animation-studio}/public/aura-assets/moonGarden.gltf +1 -1
- package/templates/animation-studio/public/aura-assets/mushroom.catalog.glb +0 -0
- package/templates/animation-studio/public/aura-assets/rusty.catalog.glb +0 -0
- package/templates/animation-studio/public/cast-library/cast-library.json +71 -0
- package/templates/animation-studio/public/clip-library/gesture.json +173903 -0
- package/templates/animation-studio/public/clip-library/idle.json +23035 -0
- package/templates/animation-studio/public/clip-library/manifest.json +154 -0
- package/templates/animation-studio/public/clip-library/nod.json +173903 -0
- package/templates/animation-studio/public/clip-library/point.json +69879 -0
- package/templates/animation-studio/public/clip-library/react.json +66084 -0
- package/templates/animation-studio/public/clip-library/run.json +9648 -0
- package/templates/animation-studio/public/clip-library/talk.json +69879 -0
- package/templates/animation-studio/public/clip-library/walk.json +8283 -0
- package/templates/animation-studio/public/hifi-cast/BrainStem.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/CesiumMan.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/RiggedFigure.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Barbarian.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Knight.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Mage.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Rogue.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/kaykit/Rogue_Hooded.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/khronos-report.json +295 -0
- package/templates/animation-studio/public/hifi-cast/quaternius-report.json +449 -0
- package/templates/animation-studio/public/hifi-cast/rpm/cesiumman.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/rpm/fox.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/rpm/riggedfigure.glb +0 -0
- package/templates/animation-studio/public/hifi-cast/rpm-report.json +301 -0
- package/templates/animation-studio/scripts/anim.ts +103 -0
- package/templates/animation-studio/scripts/animation-scene.ts +457 -0
- package/templates/animation-studio/scripts/asset-motion-probe.ts +450 -0
- package/templates/animation-studio/scripts/asset-render-probe.ts +152 -0
- package/templates/animation-studio/scripts/author-office-scene.ts +65 -0
- package/templates/animation-studio/scripts/build-cast-library.ts +97 -0
- package/templates/animation-studio/scripts/build-characters.ts +1543 -0
- package/templates/animation-studio/scripts/build-clip-library.ts +972 -0
- package/templates/animation-studio/scripts/clip-decision-cli.ts +51 -0
- package/templates/animation-studio/scripts/determinism-check.ts +48 -0
- package/templates/animation-studio/scripts/generate-scene.ts +95 -0
- package/templates/animation-studio/scripts/hifi-cast-validate.ts +572 -0
- package/templates/animation-studio/scripts/motion-evidence-cli.ts +150 -0
- package/templates/animation-studio/scripts/motion-evidence.ts +362 -0
- package/templates/animation-studio/scripts/preview-server.ts +52 -0
- package/templates/animation-studio/scripts/regen-manifest.ts +156 -0
- package/templates/animation-studio/scripts/render-core.ts +360 -0
- package/templates/animation-studio/scripts/render-live.ts +813 -0
- package/templates/animation-studio/scripts/render-parallel.ts +70 -0
- package/templates/animation-studio/scripts/render-server.ts +119 -0
- package/templates/animation-studio/scripts/resolve-asset.ts +669 -0
- package/templates/animation-studio/scripts/skeleton-overlay-cli.ts +82 -0
- package/templates/animation-studio/scripts/skeleton-overlay.ts +293 -0
- package/templates/animation-studio/scripts/studio-tools-demo.ts +45 -0
- package/templates/animation-studio/scripts/texture-bake.ts +297 -0
- package/templates/{cartoon-studio → animation-studio}/scripts/validate-characters.ts +0 -0
- package/templates/animation-studio/scripts/verify-extracted-overlay-cli.ts +264 -0
- package/templates/animation-studio/src/animation-episode-validator.ts +97 -0
- package/templates/animation-studio/src/animation-performance.ts +556 -0
- package/templates/animation-studio/src/aura-assets.ts +71 -0
- package/templates/animation-studio/src/character.ts +40 -0
- package/templates/animation-studio/src/director/compile-episode-document.ts +45 -0
- package/templates/animation-studio/src/director/director-heuristics.ts +697 -0
- package/templates/animation-studio/src/director/prompt-to-scene.ts +369 -0
- package/templates/animation-studio/src/empty-document.ts +77 -0
- package/templates/animation-studio/src/episode-document.ts +442 -0
- package/templates/animation-studio/src/examples/moon-garden.example.ts +183 -0
- package/templates/animation-studio/src/fidelity.ts +129 -0
- package/templates/animation-studio/src/graph.ts +14 -0
- package/templates/animation-studio/src/procedural-hdri.ts +243 -0
- package/templates/animation-studio/src/profile.ts +97 -0
- package/templates/animation-studio/src/render-live-route.ts +42 -0
- package/templates/animation-studio/src/render-modes.ts +137 -0
- package/templates/animation-studio/src/scene-player.ts +867 -0
- package/templates/animation-studio/src/set-templates.ts +361 -0
- package/templates/animation-studio/src/studio/episode-document-store.ts +72 -0
- package/templates/animation-studio/src/studio/scene-tool-registry.ts +86 -0
- package/templates/animation-studio/src/studio/scene-tools.ts +169 -0
- package/templates/animation-studio/tests/reports/animation-studio/hifi-cast-validation.json +938 -0
- package/templates/animation-studio/tsconfig.json +26 -0
- package/templates/{cartoon-studio → animation-studio}/vite.config.ts +6 -1
- package/templates/character-controller/README.md +33 -0
- package/templates/character-controller/index.html +19 -0
- package/templates/character-controller/package.json +23 -0
- package/templates/character-controller/playwright.config.ts +14 -0
- package/templates/character-controller/src/controller.ts +44 -0
- package/templates/character-controller/src/main.ts +62 -0
- package/templates/character-controller/tests/route-health.spec.ts +26 -0
- package/templates/character-controller/tests/screenshot.spec.ts +9 -0
- package/templates/character-controller/tsconfig.json +15 -0
- package/templates/cinematic-scene/package.json +1 -1
- package/templates/episode-builder/README.md +4 -4
- package/templates/episode-builder/package.json +1 -1
- package/templates/episode-builder/src/aura-assets.ts +1 -1
- package/templates/episode-builder/src/builder.ts +4 -4
- package/templates/episode-builder/src/episode.ts +21 -21
- package/templates/episode-builder/src/main.ts +20 -20
- package/templates/episode-builder/src/render-plan.ts +13 -13
- package/templates/episode-builder/tests/storyboard-playback.spec.ts +7 -7
- package/templates/fighting-game/package.json +1 -1
- package/templates/mini-game/package.json +1 -1
- package/templates/product-viewer/package.json +1 -1
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/README.md +5 -5
- package/templates/{cartoon-channel → prompt-animation-channel}/index.html +1 -1
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/package.json +2 -2
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/aura-assets.ts +1 -1
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/episode.ts +22 -22
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/main.ts +28 -28
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/render-plan.ts +13 -13
- package/templates/{cartoon-channel → prompt-animation-channel}/tests/route-health.spec.ts +1 -1
- package/templates/{prompt-cartoon-channel → prompt-animation-channel}/tests/storyboard-playback.spec.ts +8 -8
- package/templates/cartoon-studio/aura.assets.json +0 -400
- package/templates/cartoon-studio/package.json +0 -33
- package/templates/cartoon-studio/public/aura-assets/luma.authored.glb +0 -0
- package/templates/cartoon-studio/public/aura-assets/miko.authored.glb +0 -0
- package/templates/cartoon-studio/scripts/build-characters.ts +0 -738
- package/templates/cartoon-studio/scripts/build-dialogue-audio.ts +0 -262
- package/templates/cartoon-studio/scripts/episode.ts +0 -323
- package/templates/cartoon-studio/scripts/render-live.ts +0 -873
- package/templates/cartoon-studio/src/aura-assets.ts +0 -64
- package/templates/cartoon-studio/src/characters.ts +0 -65
- package/templates/cartoon-studio/src/episode-renderer.ts +0 -605
- package/templates/cartoon-studio/src/episode.ts +0 -306
- package/templates/cartoon-studio/src/main.ts +0 -400
- package/templates/cartoon-studio/src/render-live-route.ts +0 -913
- package/templates/cartoon-studio/src/render-plan.ts +0 -567
- package/templates/cartoon-studio/src/review.ts +0 -99
- package/templates/cartoon-studio/src/studio.ts +0 -101
- package/templates/cartoon-studio/test-results/.last-run.json +0 -4
- package/templates/cartoon-studio/tests/episode-render.spec.ts +0 -44
- package/templates/cartoon-studio/tests/motion-quality.spec.ts +0 -45
- package/templates/cartoon-studio/tests/route-health.spec.ts +0 -7
- package/templates/cartoon-studio/tests/storyboard-playback.spec.ts +0 -99
- package/templates/cartoon-studio/tests/visual-review.spec.ts +0 -21
- package/templates/cartoon-studio/tsconfig.json +0 -14
- package/templates/prompt-cartoon-channel/index.html +0 -13
- package/templates/prompt-cartoon-channel/src/sets.ts +0 -14
- /package/templates/{cartoon-channel → animation-channel}/aura.assets.json +0 -0
- /package/templates/{cartoon-channel → animation-channel}/playwright.config.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/luma.047f5e5f.glb +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/luma.thumb.svg +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/miko.047f5e5f.glb +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/miko.thumb.svg +0 -0
- /package/templates/{cartoon-channel → animation-channel}/public/aura-assets/moon-garden-feature-frame.png +0 -0
- /package/templates/{cartoon-channel → animation-channel}/src/aura-assets.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/src/characters.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/src/contract.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/src/sets.ts +0 -0
- /package/templates/{cartoon-channel → animation-channel}/test-results/.last-run.json +0 -0
- /package/templates/{cartoon-channel → animation-channel}/tests/reports/prompt-animation/cartoon-sample-episode.png +0 -0
- /package/templates/{cartoon-channel → animation-channel}/tsconfig.json +0 -0
- /package/templates/{cartoon-studio → animation-studio}/playwright.config.ts +0 -0
- /package/templates/{cartoon-studio → animation-studio}/public/aura-assets/luma.thumb.svg +0 -0
- /package/templates/{cartoon-studio → animation-studio}/public/aura-assets/miko.thumb.svg +0 -0
- /package/templates/{cartoon-studio → animation-studio}/src/contract.ts +0 -0
- /package/templates/{prompt-cartoon-channel → prompt-animation-channel}/playwright.config.ts +0 -0
- /package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/characters.ts +0 -0
- /package/templates/{prompt-cartoon-channel → prompt-animation-channel}/src/contract.ts +0 -0
- /package/templates/{cartoon-studio → prompt-animation-channel}/src/sets.ts +0 -0
- /package/templates/{prompt-cartoon-channel → prompt-animation-channel}/tsconfig.json +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scene-tools.ts — pure EpisodeDocument transforms.
|
|
3
|
+
*
|
|
4
|
+
* Each function returns a NEW document; the store validates + commits (or rejects). The
|
|
5
|
+
* agent never edits raw code — only these typed, validated edits.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { CameraPresetId } from "@aura3d/engine";
|
|
9
|
+
import type { BlockingWaypoint, CharacterBlocking, DialogueLine, EpisodeDocument, PropPlacement, ShotBlocking, ShotSpec, Vec3 } from "../episode-document";
|
|
10
|
+
import { estimateSpeechDuration } from "../episode-document";
|
|
11
|
+
|
|
12
|
+
/** Small breath between consecutive lines so speech doesn't run together. */
|
|
13
|
+
const INTER_LINE_GAP = 0.15;
|
|
14
|
+
|
|
15
|
+
/** Re-lay shots contiguously from t=0 (preserving each shot's duration); update the
|
|
16
|
+
* document duration + world-state span to match. Shots must cover the timeline with no
|
|
17
|
+
* gaps, so every shot edit re-contiguizes. */
|
|
18
|
+
function withShots(doc: EpisodeDocument, shots: readonly ShotSpec[]): EpisodeDocument {
|
|
19
|
+
let t = 0;
|
|
20
|
+
const rec = shots.map((s) => {
|
|
21
|
+
const dur = Math.max(0.1, s.endTime - s.startTime);
|
|
22
|
+
const out = { ...s, startTime: t, endTime: t + dur };
|
|
23
|
+
t += dur;
|
|
24
|
+
return out;
|
|
25
|
+
});
|
|
26
|
+
const duration = rec.length ? rec[rec.length - 1]!.endTime : doc.duration;
|
|
27
|
+
return { ...doc, shots: rec, duration, worldState: { glowSpanSeconds: duration } };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Append a shot (given a duration); the timeline re-contiguizes. */
|
|
31
|
+
export function addShot(doc: EpisodeDocument, shotId: string, presetId: CameraPresetId, duration: number, cameraSubject: Vec3): EpisodeDocument {
|
|
32
|
+
return withShots(doc, [...doc.shots, { shotId, presetId, startTime: 0, endTime: duration, cameraSubject }]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Remove a shot; the timeline closes up. */
|
|
36
|
+
export function removeShot(doc: EpisodeDocument, shotId: string): EpisodeDocument {
|
|
37
|
+
return withShots(doc, doc.shots.filter((s) => s.shotId !== shotId));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Change a shot's duration; following shots shift to stay contiguous. */
|
|
41
|
+
export function retimeShot(doc: EpisodeDocument, shotId: string, duration: number): EpisodeDocument {
|
|
42
|
+
return withShots(doc, doc.shots.map((s) => (s.shotId === shotId ? { ...s, endTime: s.startTime + Math.max(0.1, duration) } : s)));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function upsertCharacterShot(
|
|
46
|
+
blocking: readonly CharacterBlocking[],
|
|
47
|
+
characterId: string,
|
|
48
|
+
shotId: string,
|
|
49
|
+
update: (beat: ShotBlocking) => ShotBlocking
|
|
50
|
+
): CharacterBlocking[] {
|
|
51
|
+
const defaultBeat: ShotBlocking = { shotId, clip: "idle", waypoints: [] };
|
|
52
|
+
const existing = blocking.find((b) => b.characterId === characterId);
|
|
53
|
+
if (!existing) {
|
|
54
|
+
return [...blocking, { characterId, shots: [update(defaultBeat)] }];
|
|
55
|
+
}
|
|
56
|
+
return blocking.map((b) =>
|
|
57
|
+
b.characterId !== characterId
|
|
58
|
+
? b
|
|
59
|
+
: {
|
|
60
|
+
...b,
|
|
61
|
+
shots: b.shots.some((s) => s.shotId === shotId)
|
|
62
|
+
? b.shots.map((s) => (s.shotId !== shotId ? s : update(s)))
|
|
63
|
+
: [...b.shots, update(defaultBeat)]
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Set a character's blocking waypoints (and optionally clip) for a shot. */
|
|
69
|
+
export function setBlocking(
|
|
70
|
+
doc: EpisodeDocument,
|
|
71
|
+
characterId: string,
|
|
72
|
+
shotId: string,
|
|
73
|
+
waypoints: readonly BlockingWaypoint[],
|
|
74
|
+
clip?: string
|
|
75
|
+
): EpisodeDocument {
|
|
76
|
+
const blocking = upsertCharacterShot(doc.blocking, characterId, shotId, (beat) => ({
|
|
77
|
+
...beat,
|
|
78
|
+
clip: clip ?? beat.clip,
|
|
79
|
+
waypoints: [...waypoints]
|
|
80
|
+
}));
|
|
81
|
+
return { ...doc, blocking };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Schedule a clip/gesture for a character in a shot (keeps the existing waypoints). */
|
|
85
|
+
export function setGesture(doc: EpisodeDocument, characterId: string, shotId: string, clip: string): EpisodeDocument {
|
|
86
|
+
const blocking = upsertCharacterShot(doc.blocking, characterId, shotId, (beat) => ({ ...beat, clip }));
|
|
87
|
+
return { ...doc, blocking };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Set a shot's camera framing (preset and/or subject). */
|
|
91
|
+
export function setCamera(
|
|
92
|
+
doc: EpisodeDocument,
|
|
93
|
+
shotId: string,
|
|
94
|
+
opts: { preset?: CameraPresetId; subject?: Vec3 }
|
|
95
|
+
): EpisodeDocument {
|
|
96
|
+
return {
|
|
97
|
+
...doc,
|
|
98
|
+
shots: doc.shots.map((s) =>
|
|
99
|
+
s.shotId !== shotId ? s : { ...s, presetId: opts.preset ?? s.presetId, cameraSubject: opts.subject ?? s.cameraSubject }
|
|
100
|
+
)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Add a prop instance to the set dressing. */
|
|
105
|
+
export function placeProp(doc: EpisodeDocument, placement: PropPlacement): EpisodeDocument {
|
|
106
|
+
return { ...doc, setDressing: [...doc.setDressing, placement] };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Clear set dressing (all, or just one prop type). */
|
|
110
|
+
export function clearProps(doc: EpisodeDocument, propId?: string): EpisodeDocument {
|
|
111
|
+
return { ...doc, setDressing: propId ? doc.setDressing.filter((d) => d.propId !== propId) : [] };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Set a character's uniform scale (sizing a resolved GLB to read at the right height). */
|
|
115
|
+
export function setCharacterScale(doc: EpisodeDocument, characterId: string, scale: number): EpisodeDocument {
|
|
116
|
+
return { ...doc, assets: { ...doc.assets, characters: doc.assets.characters.map((c) => (c.id === characterId ? { ...c, scale } : c)) } };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Add or update a dialogue line (the scene's caption + lip-sync source / AuraVoice
|
|
121
|
+
* contract). When `endTime` is not explicitly provided (omitted, or <= startTime — the
|
|
122
|
+
* CLI's "unset" sentinel of 0), it is auto-computed as
|
|
123
|
+
* `startTime + estimateSpeechDuration(text)` so captions show only while the line would
|
|
124
|
+
* be spoken instead of lingering on a fixed window.
|
|
125
|
+
*/
|
|
126
|
+
export function setDialogueLine(
|
|
127
|
+
doc: EpisodeDocument,
|
|
128
|
+
line: Omit<DialogueLine, "endTime" | "startTime"> & { startTime?: number; endTime?: number }
|
|
129
|
+
): EpisodeDocument {
|
|
130
|
+
const existing = doc.dialogue ?? { language: "en", lines: [] };
|
|
131
|
+
const startTime = typeof line.startTime === "number" ? line.startTime : 0;
|
|
132
|
+
const endProvided = typeof line.endTime === "number" && line.endTime > startTime;
|
|
133
|
+
const resolved: DialogueLine = {
|
|
134
|
+
lineId: line.lineId,
|
|
135
|
+
speakerId: line.speakerId,
|
|
136
|
+
text: line.text,
|
|
137
|
+
startTime,
|
|
138
|
+
endTime: endProvided ? (line.endTime as number) : startTime + estimateSpeechDuration(line.text)
|
|
139
|
+
};
|
|
140
|
+
const lines = existing.lines.some((l) => l.lineId === resolved.lineId)
|
|
141
|
+
? existing.lines.map((l) => (l.lineId === resolved.lineId ? resolved : l))
|
|
142
|
+
: [...existing.lines, resolved];
|
|
143
|
+
return { ...doc, dialogue: { language: existing.language, lines: [...lines].sort((a, b) => a.startTime - b.startTime) } };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Re-sequence ALL dialogue lines back-to-back from t=0 using estimateSpeechDuration, so
|
|
148
|
+
* captions are paced by speech length with a small inter-line gap and never overlap.
|
|
149
|
+
* Lines keep their existing document order (already startTime-sorted by setDialogueLine);
|
|
150
|
+
* each line's start = previous line's end + gap, end = start + estimated speech duration.
|
|
151
|
+
* Returns a NEW document; the computed start/end are stored in the dialogue track.
|
|
152
|
+
*/
|
|
153
|
+
export function retimeDialogue(doc: EpisodeDocument): EpisodeDocument {
|
|
154
|
+
if (!doc.dialogue || doc.dialogue.lines.length === 0) return doc;
|
|
155
|
+
let t = 0;
|
|
156
|
+
const lines: DialogueLine[] = doc.dialogue.lines.map((l, i) => {
|
|
157
|
+
const start = i === 0 ? 0 : t + INTER_LINE_GAP;
|
|
158
|
+
const end = start + estimateSpeechDuration(l.text);
|
|
159
|
+
t = end;
|
|
160
|
+
return { ...l, startTime: start, endTime: end };
|
|
161
|
+
});
|
|
162
|
+
return { ...doc, dialogue: { ...doc.dialogue, lines } };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Remove a dialogue line. */
|
|
166
|
+
export function removeDialogueLine(doc: EpisodeDocument, lineId: string): EpisodeDocument {
|
|
167
|
+
if (!doc.dialogue) return doc;
|
|
168
|
+
return { ...doc, dialogue: { ...doc.dialogue, lines: doc.dialogue.lines.filter((l) => l.lineId !== lineId) } };
|
|
169
|
+
}
|