mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
Localize a bootstrap shot title without a source pack only when the Nepal pack marker binds that title to the shot's id, so an operator shot renamed to a bootstrap title in the built-in scene stays as typed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
1105 lines
27 KiB
JavaScript
1105 lines
27 KiB
JavaScript
/**
|
|
* Scene recipes optimized for short social clips.
|
|
* Each recipe is deterministic so repeated runs produce similar footage.
|
|
*/
|
|
|
|
import { expandNepalEvidencePack } from './nepalEvidencePack.js';
|
|
import { msg, t } from '../i18n/index.js';
|
|
|
|
const BHOTE_KOSHI_NEPAL_BASE_RECIPE = Object.freeze({
|
|
id: 'bhote-koshi-nepal-evidence-pack',
|
|
version: 12,
|
|
title: msg('Bhote Koshi Evidence Sequence'),
|
|
style: 'normal',
|
|
ui: { hidePanels: false, hudMode: 'full', safeFrame: '16:9' },
|
|
layers: {
|
|
'bhote-koshi-locator': false,
|
|
},
|
|
releaseLayerIds: ['bhote-koshi-2026', 'bhote-koshi-locator'],
|
|
post: {
|
|
bloom: 0,
|
|
sharpen: true,
|
|
detectionMode: 'OFF',
|
|
mapStack: 'photoreal',
|
|
},
|
|
legacySceneBootstrap: {
|
|
targetSceneTitle: 'Nepal Flood Incident',
|
|
fromShotTitles: ['Shot 1', 'Shot 2', 'Shot 3'],
|
|
cameraPath: [
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Global Incident Context',
|
|
lat: 27.5962,
|
|
lon: 78.5718,
|
|
alt: 15628002,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'nepal-context' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Nepal-Focused Globe Rotation',
|
|
lat: 28.7248,
|
|
lon: 81.3226,
|
|
alt: 2068966,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'nepal-context' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Regional Approach',
|
|
lat: 27.0354,
|
|
lon: 84.6287,
|
|
alt: 809543,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-regional' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Nearby Cities',
|
|
lat: 27.891,
|
|
lon: 85.428,
|
|
alt: 225734,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-city-context' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Incident Corridor',
|
|
lat: 28.0529,
|
|
lon: 85.2189,
|
|
alt: 126931,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 11,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-incident-places' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Flood Path',
|
|
lat: 28.1691,
|
|
lon: 85.352,
|
|
alt: 60328,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-flood-path' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Corridor Overview',
|
|
lat: 27.8986,
|
|
lon: 84.8723,
|
|
alt: 53247,
|
|
heading: 60,
|
|
pitch: -48,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0.9,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-path-overview' },
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Upper Valley',
|
|
lat: 28.4187,
|
|
lon: 85.4341,
|
|
alt: 7977,
|
|
heading: 155,
|
|
pitch: -28,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 8,
|
|
layers: {
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: { presentation: 'bhote-koshi-trigger-record' },
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
requiredShotTitles: [
|
|
'Global Incident Context',
|
|
'Nepal-Focused Globe Rotation',
|
|
'Bhote Koshi Regional Approach',
|
|
'Bhote Koshi Nearby Cities',
|
|
'Bhote Koshi Incident Corridor',
|
|
'Bhote Koshi Flood Path',
|
|
'Bhote Koshi Corridor Overview',
|
|
'Bhote Koshi Upper Valley',
|
|
'Upper Valley Collapse',
|
|
'Debris-Dammed Lake',
|
|
'Nepal-China Border Gate',
|
|
'Timure Evidence Cluster',
|
|
'Syabru Besi Passage',
|
|
'Bidur / Trishuli Consequence',
|
|
],
|
|
requiredSourcePackBeatIds: [
|
|
'immediate-collapse-viewpoint',
|
|
'debris-dammed-lake',
|
|
'gyirong-border-gate',
|
|
'timure-cluster',
|
|
'syabru-besi',
|
|
'bidur-trishuli-bridge',
|
|
],
|
|
requiredSourcePackLayerId: 'bhote-koshi-2026',
|
|
photorealSurfaceBeatIds: [
|
|
'gyirong-border-gate',
|
|
'timure-cluster',
|
|
'syabru-besi',
|
|
'bidur-trishuli-bridge',
|
|
],
|
|
runtimeControlsByBeat: {
|
|
'immediate-collapse-viewpoint': { evidenceMediaAutoplay: true },
|
|
'debris-dammed-lake': { evidenceMediaAutoplay: true },
|
|
'gyirong-border-gate': {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 2.5,
|
|
deferEvidenceUntilCameraSettled: true,
|
|
evidenceRevealDurationSec: 2.5,
|
|
},
|
|
'timure-cluster': {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
'syabru-besi': {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
'bidur-trishuli-bridge': {
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
},
|
|
shotPatches: [
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Global Incident Context',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'immediate-collapse-viewpoint',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Nepal-Focused Globe Rotation',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'debris-dammed-lake',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Regional Approach',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'gyirong-border-gate',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Nearby Cities',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'timure-cluster',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Incident Corridor',
|
|
camera: {
|
|
lat: 28.0529,
|
|
lon: 85.2189,
|
|
alt: 126931,
|
|
heading: 0,
|
|
pitch: -90,
|
|
roll: 0,
|
|
},
|
|
holdSec: 11,
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'syabru-besi',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Flood Path',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'bidur-trishuli-bridge',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Corridor Overview',
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'gyirong-border-gate',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bhote Koshi Upper Valley',
|
|
holdSec: 8,
|
|
visual: { mapStack: 'photoreal' },
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'immediate-collapse-viewpoint',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
},
|
|
},
|
|
'bhote-koshi-locator': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'bhote-koshi-trigger-record',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Upper Valley Collapse',
|
|
camera: {
|
|
lat: 28.417,
|
|
lon: 85.3937,
|
|
alt: 9850,
|
|
heading: 146,
|
|
pitch: -34,
|
|
roll: 0,
|
|
},
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'immediate-collapse-viewpoint',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: { evidenceMediaAutoplay: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Nepal-China Border Gate',
|
|
visual: { mapStack: 'photoreal' },
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Timure Evidence Cluster',
|
|
visual: { mapStack: 'photoreal' },
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Syabru Besi Passage',
|
|
visual: { mapStack: 'photoreal' },
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bidur / Trishuli Consequence',
|
|
visual: { mapStack: 'photoreal' },
|
|
},
|
|
],
|
|
cameraPath: [
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Upper Valley Collapse',
|
|
lat: 28.417,
|
|
lon: 85.3937,
|
|
alt: 9850,
|
|
heading: 146,
|
|
pitch: -34,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 1.4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'immediate-collapse-viewpoint',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: { evidenceMediaAutoplay: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Debris-Dammed Lake',
|
|
lat: 28.392914,
|
|
lon: 85.570921,
|
|
alt: 10744,
|
|
heading: 232,
|
|
pitch: -37,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 1.4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'debris-dammed-lake',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: { evidenceMediaAutoplay: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Nepal-China Border Gate',
|
|
mapStack: 'photoreal',
|
|
lat: 28.316789,
|
|
lon: 85.399734,
|
|
alt: 5900,
|
|
heading: 208,
|
|
pitch: -42,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 2.5,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'gyirong-border-gate',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 2.5,
|
|
deferEvidenceUntilCameraSettled: true,
|
|
evidenceRevealDurationSec: 2.5,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Timure Evidence Cluster',
|
|
mapStack: 'photoreal',
|
|
lat: 28.285968,
|
|
lon: 85.379136,
|
|
alt: 5032,
|
|
heading: 198,
|
|
pitch: -40,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'timure-cluster',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Syabru Besi Passage',
|
|
mapStack: 'photoreal',
|
|
lat: 28.210306,
|
|
lon: 85.361527,
|
|
alt: 5893,
|
|
heading: 205,
|
|
pitch: -38,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'syabru-besi',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: {
|
|
imageryComparison: true,
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// i18n-ignore: stored shot title, matched verbatim
|
|
title: 'Bidur / Trishuli Consequence',
|
|
mapStack: 'photoreal',
|
|
lat: 27.981737,
|
|
lon: 85.196273,
|
|
alt: 7422,
|
|
heading: 215,
|
|
pitch: -40,
|
|
roll: 0,
|
|
duration: 4.2,
|
|
hold: 4,
|
|
layers: {
|
|
'bhote-koshi-2026': {
|
|
enabled: true,
|
|
params: {
|
|
presentation: 'scene-beat',
|
|
beatId: 'bidur-trishuli-bridge',
|
|
beatReveal: 0.36,
|
|
split: 0.5,
|
|
sceneControls: {
|
|
evidenceSequence: true,
|
|
evidenceSequenceDurationSec: 4,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
const BHOTE_KOSHI_NEPAL_APPEND_RECIPE = BHOTE_KOSHI_NEPAL_BASE_RECIPE
|
|
? expandNepalEvidencePack(BHOTE_KOSHI_NEPAL_BASE_RECIPE)
|
|
: null;
|
|
|
|
// Fresh browser origins have no legacy three-shot Nepal project for the
|
|
// append recipe to upgrade. Seed that exact legacy shape so SceneDirector can
|
|
// run the same idempotent bootstrap used by existing browser-saved projects.
|
|
const BHOTE_KOSHI_NEPAL_BOOTSTRAP_RECIPE = BHOTE_KOSHI_NEPAL_APPEND_RECIPE
|
|
? Object.freeze({
|
|
id: 'bhote-koshi-nepal-scene',
|
|
title: msg('Nepal Flood Incident'),
|
|
durationSec: 15,
|
|
style: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.style,
|
|
ui: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.ui,
|
|
layers: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.layers,
|
|
releaseLayerIds: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.releaseLayerIds,
|
|
post: BHOTE_KOSHI_NEPAL_APPEND_RECIPE.post,
|
|
installAlongsideSceneId: 'bhote-koshi-flood',
|
|
installAlongsideFallbackSceneId: 'flights-radar',
|
|
cameraPath:
|
|
BHOTE_KOSHI_NEPAL_APPEND_RECIPE.legacySceneBootstrap.cameraPath
|
|
.slice(0, 3)
|
|
.map((keyframe, index) => ({
|
|
...keyframe,
|
|
// i18n-ignore: legacy shot titles the bootstrap matches verbatim
|
|
title: `Shot ${index + 1}`,
|
|
})),
|
|
})
|
|
: null;
|
|
|
|
const EVENT_RECIPES = [BHOTE_KOSHI_NEPAL_BOOTSTRAP_RECIPE].filter(Boolean);
|
|
|
|
const PUBLIC_SCENE_RECIPES = [
|
|
{
|
|
id: 'flights-radar',
|
|
title: msg('Global Flights Radar'),
|
|
durationSec: 30,
|
|
style: 'retro',
|
|
ui: { hidePanels: true, hudMode: 'minimal', safeFrame: '16:9' },
|
|
layers: {
|
|
flights: true,
|
|
satellites: false,
|
|
earthquakes: false,
|
|
traffic: false,
|
|
},
|
|
post: {
|
|
bloom: 62,
|
|
sharpen: true,
|
|
detectionMode: 'OFF',
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 20.0,
|
|
lon: -30.0,
|
|
alt: 19000000,
|
|
heading: 25,
|
|
pitch: -65,
|
|
roll: 0,
|
|
duration: 6,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 46.0,
|
|
lon: 2.0,
|
|
alt: 8000000,
|
|
heading: 40,
|
|
pitch: -52,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.0,
|
|
lon: 139.0,
|
|
alt: 4200000,
|
|
heading: 22,
|
|
pitch: -46,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 37.6,
|
|
lon: -122.4,
|
|
alt: 1700000,
|
|
heading: 8,
|
|
pitch: -40,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 0.0,
|
|
lon: -20.0,
|
|
alt: 12000000,
|
|
heading: -10,
|
|
pitch: -70,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'orbital-watch',
|
|
title: msg('Orbital Watch'),
|
|
durationSec: 32,
|
|
style: 'surveillance',
|
|
ui: { hidePanels: true, hudMode: 'full', safeFrame: '16:9' },
|
|
layers: {
|
|
flights: false,
|
|
satellites: true,
|
|
earthquakes: false,
|
|
traffic: false,
|
|
},
|
|
post: {
|
|
bloom: 58,
|
|
sharpen: false,
|
|
detectionMode: 'SPARSE',
|
|
styleParams: {
|
|
surveillance: {
|
|
gain: 0.62,
|
|
bloom: 0.38,
|
|
scanlineStr: 0.9,
|
|
pixelation: 2.1,
|
|
},
|
|
},
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 28.0,
|
|
lon: -82.0,
|
|
alt: 22000000,
|
|
heading: 0,
|
|
pitch: -82,
|
|
roll: 0,
|
|
duration: 7,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 10.0,
|
|
lon: 20.0,
|
|
alt: 12000000,
|
|
heading: 18,
|
|
pitch: -74,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.7,
|
|
lon: 139.7,
|
|
alt: 6500000,
|
|
heading: 24,
|
|
pitch: -64,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 48.8,
|
|
lon: 2.3,
|
|
alt: 3000000,
|
|
heading: 32,
|
|
pitch: -58,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 30.0,
|
|
lon: -25.0,
|
|
alt: 15000000,
|
|
heading: -20,
|
|
pitch: -78,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'thermal-threats',
|
|
title: msg('Thermal Threat Board'),
|
|
durationSec: 26,
|
|
style: 'thermal',
|
|
ui: { hidePanels: true, hudMode: 'full', safeFrame: '16:9' },
|
|
layers: {
|
|
flights: false,
|
|
satellites: false,
|
|
earthquakes: true,
|
|
traffic: false,
|
|
},
|
|
post: {
|
|
bloom: 72,
|
|
sharpen: true,
|
|
detectionMode: 'OFF',
|
|
styleParams: {
|
|
thermal: {
|
|
sensitivity: 0.84,
|
|
bloom: 0.78,
|
|
mode: 0.0,
|
|
pixelation: 1.9,
|
|
},
|
|
},
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 37.0,
|
|
lon: -121.0,
|
|
alt: 5200000,
|
|
heading: 20,
|
|
pitch: -62,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.7,
|
|
lon: 140.0,
|
|
alt: 3100000,
|
|
heading: 5,
|
|
pitch: -55,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: -36.8,
|
|
lon: 174.7,
|
|
alt: 2600000,
|
|
heading: -12,
|
|
pitch: -50,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 38.0,
|
|
lon: -10.0,
|
|
alt: 9000000,
|
|
heading: 12,
|
|
pitch: -70,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'city-overload',
|
|
title: msg('City Overload'),
|
|
durationSec: 30,
|
|
style: 'surveillance',
|
|
ui: { hidePanels: true, hudMode: 'minimal', safeFrame: '9:16' },
|
|
layers: {
|
|
flights: true,
|
|
satellites: true,
|
|
earthquakes: false,
|
|
traffic: true,
|
|
},
|
|
post: {
|
|
bloom: 65,
|
|
sharpen: true,
|
|
detectionMode: 'PANOPTIC',
|
|
styleParams: {
|
|
surveillance: {
|
|
gain: 0.68,
|
|
bloom: 0.42,
|
|
scanlineStr: 1.0,
|
|
pixelation: 2.4,
|
|
},
|
|
},
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 40.73,
|
|
lon: -74.0,
|
|
alt: 1500000,
|
|
heading: 25,
|
|
pitch: -44,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 40.73,
|
|
lon: -74.0,
|
|
alt: 550000,
|
|
heading: 58,
|
|
pitch: -36,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 40.73,
|
|
lon: -74.0,
|
|
alt: 260000,
|
|
heading: 102,
|
|
pitch: -33,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 34.05,
|
|
lon: -118.24,
|
|
alt: 1200000,
|
|
heading: 45,
|
|
pitch: -42,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 51.5,
|
|
lon: -0.12,
|
|
alt: 1200000,
|
|
heading: 22,
|
|
pitch: -44,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'omniscience-pullback',
|
|
title: msg('Omniscience Pullback'),
|
|
durationSec: 36,
|
|
style: 'retro',
|
|
ui: { hidePanels: true, hudMode: 'full', safeFrame: '16:9' },
|
|
layers: {
|
|
flights: true,
|
|
satellites: true,
|
|
earthquakes: true,
|
|
traffic: true,
|
|
},
|
|
post: {
|
|
bloom: 68,
|
|
sharpen: true,
|
|
detectionMode: 'SPARSE',
|
|
styleParams: {
|
|
retro: {
|
|
pixelation: 4.4,
|
|
distortion: 0.42,
|
|
instability: 0.58,
|
|
},
|
|
},
|
|
},
|
|
cameraPath: [
|
|
{
|
|
lat: 35.68,
|
|
lon: 139.76,
|
|
alt: 280000,
|
|
heading: 30,
|
|
pitch: -26,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.68,
|
|
lon: 139.76,
|
|
alt: 900000,
|
|
heading: 26,
|
|
pitch: -38,
|
|
roll: 0,
|
|
duration: 4,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 35.68,
|
|
lon: 139.76,
|
|
alt: 3800000,
|
|
heading: 18,
|
|
pitch: -52,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 20.0,
|
|
lon: 110.0,
|
|
alt: 9500000,
|
|
heading: 8,
|
|
pitch: -66,
|
|
roll: 0,
|
|
duration: 5,
|
|
hold: 1,
|
|
},
|
|
{
|
|
lat: 5.0,
|
|
lon: 30.0,
|
|
alt: 18000000,
|
|
heading: -8,
|
|
pitch: -78,
|
|
roll: 0,
|
|
duration: 6,
|
|
hold: 0,
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
/** Build the recipe list without mutating stored user-authored projects. */
|
|
function createSceneRecipes({ localDemoRecipes = EVENT_RECIPES } = {}) {
|
|
return [...localDemoRecipes, ...PUBLIC_SCENE_RECIPES];
|
|
}
|
|
|
|
export const SCENE_RECIPES = createSceneRecipes();
|
|
|
|
export const SCENE_APPEND_RECIPES = BHOTE_KOSHI_NEPAL_APPEND_RECIPE
|
|
? [BHOTE_KOSHI_NEPAL_APPEND_RECIPE]
|
|
: [];
|
|
|
|
function getSceneRecipeById(id) {
|
|
return SCENE_RECIPES.find((recipe) => recipe.id === id) || null;
|
|
}
|
|
|
|
export function getSceneAppendRecipeById(id) {
|
|
return SCENE_APPEND_RECIPES.find((recipe) => recipe.id === id) || null;
|
|
}
|
|
|
|
// Built-in shot titles stay English in recipes and saved projects (upgrades
|
|
// and bindings match them verbatim). These are their display names; a title
|
|
// the user typed shows as typed. Place names inside them stay Latin.
|
|
const BUILTIN_SHOT_TITLES = new Set([
|
|
msg('Global Incident Context'),
|
|
msg('Nepal-Focused Globe Rotation'),
|
|
msg('Bhote Koshi Regional Approach'),
|
|
msg('Bhote Koshi Nearby Cities'),
|
|
msg('Bhote Koshi Incident Corridor'),
|
|
msg('Bhote Koshi Flood Path'),
|
|
msg('Bhote Koshi Corridor Overview'),
|
|
msg('Bhote Koshi Upper Valley'),
|
|
msg('Upper Valley Collapse'),
|
|
msg('Debris-Dammed Lake'),
|
|
msg('Nepal-China Border Gate'),
|
|
msg('Timure Evidence Cluster'),
|
|
msg('Syabru Besi Passage'),
|
|
msg('Bidur / Trishuli Consequence'),
|
|
msg('Second landslide'),
|
|
msg('Mailung / Upper Trishuli bridge'),
|
|
msg('Dandagaun viewpoint restaurant'),
|
|
msg('Devighat / Taadi Khola Bridge'),
|
|
msg('Final view'),
|
|
]);
|
|
|
|
/**
|
|
* Display title for a scene: the localized recipe name while the stored title
|
|
* still matches the built-in verbatim, the stored title otherwise.
|
|
* @param {{id?: string, title?: string}} scene
|
|
* @returns {string}
|
|
*/
|
|
export function displaySceneTitle(scene) {
|
|
const builtin = SCENE_RECIPES.find((recipe) => recipe.id === scene?.id);
|
|
if (builtin?.title && scene.title === builtin.title) return t(builtin.title);
|
|
const title = scene?.title || '';
|
|
// Only the generated "Scene N" localizes; an operator-typed "Scene 007"
|
|
// keeps its digits, so the number stays a string (no Number() round trip).
|
|
const numbered = /^Scene ([1-9]\d*)$/.exec(title);
|
|
return numbered ? t('Scene {n}', { n: numbered[1] }) : title;
|
|
}
|
|
|
|
/**
|
|
* Display title for a shot: built-in titles and the numbered "Shot N"
|
|
* fallback localize; any other title shows as stored.
|
|
*
|
|
* With `shot`, a built-in title localizes only for a recipe shot
|
|
* (`sourcePackId`), so a typed title that matches a built-in one stays as
|
|
* typed. Without `shot`, the title alone decides.
|
|
* @param {string} title
|
|
* @param {{id?: string, sourcePackId?: string}} [shot]
|
|
* @param {{id?: string, appliedShotPacks?: Array<{id?: string, shotBindings?: Object<string, string>}>}} [scene]
|
|
* The shot's scene, for projects saved before shots recorded their source
|
|
* pack.
|
|
* @returns {string}
|
|
*/
|
|
export function displayShotTitle(title, shot = undefined, scene = undefined) {
|
|
const text = typeof title === 'string' ? title : '';
|
|
const fromRecipe = shot
|
|
? Boolean(shot.sourcePackId) || recipeDeclaresShotTitle(scene, shot, text)
|
|
: true;
|
|
if (fromRecipe && BUILTIN_SHOT_TITLES.has(text)) return t(text);
|
|
const numbered = /^Shot ([1-9]\d*)$/.exec(text);
|
|
return numbered ? t('Shot {n}', { n: numbered[1] }) : text;
|
|
}
|
|
|
|
/** Whether the built-in recipe behind a scene declares this shot's title. */
|
|
function recipeDeclaresShotTitle(scene, shot, title) {
|
|
const recipe = scene?.id ? getSceneRecipeById(scene.id) : null;
|
|
if (!recipe) return false;
|
|
const declares = (path) =>
|
|
Array.isArray(path) && path.some((keyframe) => keyframe?.title === title);
|
|
if (declares(recipe.cameraPath)) return true;
|
|
// A bootstrap recipe seeds legacy "Shot N" keyframes; the upgrade replaces
|
|
// them with the append recipe's bootstrap shots, which carry no source pack.
|
|
// The pack marker binds each of those titles to its shot id, so a shot the
|
|
// operator later gave the same title stays as typed.
|
|
return SCENE_APPEND_RECIPES.some(
|
|
(append) =>
|
|
append.legacySceneBootstrap?.targetSceneTitle === recipe.title &&
|
|
declares(append.legacySceneBootstrap.cameraPath) &&
|
|
Boolean(shot?.id) &&
|
|
(Array.isArray(scene.appliedShotPacks)
|
|
? scene.appliedShotPacks
|
|
: []
|
|
).some(
|
|
(marker) =>
|
|
marker?.id === append.id && marker.shotBindings?.[title] === shot.id,
|
|
),
|
|
);
|
|
}
|