SCENEGOD/templates/record-store.json
type-two 586a3ecbd2 [m7-C] starter templates: read-only API + three working scenes
templates/ ships with the repo (two-hander-golden-hour, record-store,
empty-stage), GET /templates + /templates/{name} behind the same slug and
path-traversal guards as /scenes; presentation metadata rides in a template
key that validate_scene ignores and toJSON drops, so loading then saving
yields plain scene JSON.

Test group asserts the three list in order, round-trip, traversal/unknown
rejected, write verbs refused, and every template passes validate_scene with
every referenced asset present on disk - the check that stops a rotten
template shipping. 10 groups green.

Verified live: two-hander loads 7 entities, 8s, both performers animating,
cut fires cam1 to cam2 at 4.5s; other two load clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 22:34:53 +10:00

93 lines
2.6 KiB
JSON

{
"version": 1,
"name": "record-store",
"template": {
"title": "Record store",
"description": "One character in a warm record-shop interior plate, single camera, 6 seconds. The simplest one-shot to build a scene on top of.",
"thumb": "backdrops/video/scenegod_plates/record_store_interior.jpg",
"order": 2
},
"fps": 30,
"duration": 6,
"entities": [
{
"id": "plate",
"kind": "backdrop",
"label": "record store interior",
"source": {
"type": "assets",
"path": "backdrops/video/scenegod_plates/record_store_interior.mp4"
},
"params": {
"mode": "video",
"video": "backdrops/video/scenegod_plates/record_store_interior.mp4",
"width": 12,
"fit": "frustum",
"fitDist": 12,
"groundTint": true,
"videoStart": 0
},
"transform": { "pos": [0, 0, -7], "rot": [0, 0, 0], "scale": 1 },
"tracks": { "transform": [], "params": [], "clips": [] }
},
{
"id": "man",
"kind": "character",
"label": "man",
"source": { "type": "assets", "path": "characters/test/man.fbx" },
"params": {},
"transform": { "pos": [0, 0, 0], "rot": [0, 0.18, 0], "scale": 1 },
"tracks": {
"transform": [],
"params": [],
"clips": [
{
"path": "animations/test/carrying.fbx",
"clipIndex": 0,
"start": 0,
"in": 0,
"out": 6,
"loop": 1,
"fade": 0
}
]
}
},
{
"id": "cam1",
"kind": "camera",
"label": "shot",
"source": { "type": "none" },
"params": { "fov": 38 },
"transform": { "pos": [0.6, 1.45, 3.4], "rot": [-0.1171, 0.1735, 0.0203], "scale": 1 },
"tracks": { "transform": [], "params": [], "clips": [] }
},
{
"id": "sun",
"kind": "light",
"label": "key",
"source": { "type": "none" },
"params": { "type": "key", "color": "#ffcf9a", "intensity": 2, "castShadow": true },
"transform": { "pos": [-4.85, 8.03, 10.39], "rot": [-0.5949, -0.3689, -0.2393], "scale": 1 },
"tracks": { "transform": [], "params": [], "clips": [] }
},
{
"id": "amb",
"kind": "light",
"label": "ambient",
"source": { "type": "none" },
"params": {
"type": "ambient",
"color": "#ffdcb4",
"intensity": 0.75,
"bg": "#2a1d14",
"fog": 0.012
},
"transform": { "pos": [0, 3, 0], "rot": [0, 0, 0], "scale": 1 },
"tracks": { "transform": [], "params": [], "clips": [] }
}
],
"cameraCuts": [{ "t": 0, "camera": "cam1" }],
"audio": []
}