create-aura3d 1.3.2 → 1.4.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.
Files changed (226) hide show
  1. package/dist/cli.js +10 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +11 -2
  6. package/dist/index.js.map +1 -1
  7. package/dist/showcase-spec-artifacts.d.ts +104 -0
  8. package/dist/showcase-spec-artifacts.d.ts.map +1 -0
  9. package/dist/showcase-spec-artifacts.js +142 -0
  10. package/dist/showcase-spec-artifacts.js.map +1 -0
  11. package/dist/showcase-spec-category-plan.d.ts +15 -0
  12. package/dist/showcase-spec-category-plan.d.ts.map +1 -0
  13. package/dist/showcase-spec-category-plan.js +45 -0
  14. package/dist/showcase-spec-category-plan.js.map +1 -0
  15. package/dist/showcase-spec-compiler.d.ts +5 -0
  16. package/dist/showcase-spec-compiler.d.ts.map +1 -0
  17. package/dist/showcase-spec-compiler.js +240 -0
  18. package/dist/showcase-spec-compiler.js.map +1 -0
  19. package/dist/showcase-spec-evidence.d.ts +7 -0
  20. package/dist/showcase-spec-evidence.d.ts.map +1 -0
  21. package/dist/showcase-spec-evidence.js +815 -0
  22. package/dist/showcase-spec-evidence.js.map +1 -0
  23. package/dist/showcase-spec-game-geometry-extractor.d.ts +21 -0
  24. package/dist/showcase-spec-game-geometry-extractor.d.ts.map +1 -0
  25. package/dist/showcase-spec-game-geometry-extractor.js +725 -0
  26. package/dist/showcase-spec-game-geometry-extractor.js.map +1 -0
  27. package/dist/showcase-spec-game-replacement-evidence.d.ts +14 -0
  28. package/dist/showcase-spec-game-replacement-evidence.d.ts.map +1 -0
  29. package/dist/showcase-spec-game-replacement-evidence.js +219 -0
  30. package/dist/showcase-spec-game-replacement-evidence.js.map +1 -0
  31. package/dist/showcase-spec-game-template-evidence.d.ts +72 -0
  32. package/dist/showcase-spec-game-template-evidence.d.ts.map +1 -0
  33. package/dist/showcase-spec-game-template-evidence.js +435 -0
  34. package/dist/showcase-spec-game-template-evidence.js.map +1 -0
  35. package/dist/showcase-spec-gameplay-evidence.d.ts +3 -0
  36. package/dist/showcase-spec-gameplay-evidence.d.ts.map +1 -0
  37. package/dist/showcase-spec-gameplay-evidence.js +108 -0
  38. package/dist/showcase-spec-gameplay-evidence.js.map +1 -0
  39. package/dist/showcase-spec-parser.d.ts +3 -0
  40. package/dist/showcase-spec-parser.d.ts.map +1 -0
  41. package/dist/showcase-spec-parser.js +662 -0
  42. package/dist/showcase-spec-parser.js.map +1 -0
  43. package/dist/showcase-spec-platformer-artifacts.d.ts +3 -0
  44. package/dist/showcase-spec-platformer-artifacts.d.ts.map +1 -0
  45. package/dist/showcase-spec-platformer-artifacts.js +276 -0
  46. package/dist/showcase-spec-platformer-artifacts.js.map +1 -0
  47. package/dist/showcase-spec-racing-artifacts.d.ts +3 -0
  48. package/dist/showcase-spec-racing-artifacts.d.ts.map +1 -0
  49. package/dist/showcase-spec-racing-artifacts.js +273 -0
  50. package/dist/showcase-spec-racing-artifacts.js.map +1 -0
  51. package/dist/showcase-spec-replacement-apply.d.ts +10 -0
  52. package/dist/showcase-spec-replacement-apply.d.ts.map +1 -0
  53. package/dist/showcase-spec-replacement-apply.js +166 -0
  54. package/dist/showcase-spec-replacement-apply.js.map +1 -0
  55. package/dist/showcase-spec-replacement-candidates.d.ts +16 -0
  56. package/dist/showcase-spec-replacement-candidates.d.ts.map +1 -0
  57. package/dist/showcase-spec-replacement-candidates.js +244 -0
  58. package/dist/showcase-spec-replacement-candidates.js.map +1 -0
  59. package/dist/showcase-spec-replacement-game-geometry.d.ts +4 -0
  60. package/dist/showcase-spec-replacement-game-geometry.d.ts.map +1 -0
  61. package/dist/showcase-spec-replacement-game-geometry.js +175 -0
  62. package/dist/showcase-spec-replacement-game-geometry.js.map +1 -0
  63. package/dist/showcase-spec-replacement-manifest.d.ts +41 -0
  64. package/dist/showcase-spec-replacement-manifest.d.ts.map +1 -0
  65. package/dist/showcase-spec-replacement-manifest.js +62 -0
  66. package/dist/showcase-spec-replacement-manifest.js.map +1 -0
  67. package/dist/showcase-spec-replacement-values.d.ts +9 -0
  68. package/dist/showcase-spec-replacement-values.d.ts.map +1 -0
  69. package/dist/showcase-spec-replacement-values.js +41 -0
  70. package/dist/showcase-spec-replacement-values.js.map +1 -0
  71. package/dist/showcase-spec-replacement.d.ts +13 -0
  72. package/dist/showcase-spec-replacement.d.ts.map +1 -0
  73. package/dist/showcase-spec-replacement.js +144 -0
  74. package/dist/showcase-spec-replacement.js.map +1 -0
  75. package/dist/showcase-spec-types.d.ts +304 -0
  76. package/dist/showcase-spec-types.d.ts.map +1 -0
  77. package/dist/showcase-spec-types.js +2 -0
  78. package/dist/showcase-spec-types.js.map +1 -0
  79. package/package.json +21 -2
  80. package/templates/animation-channel/package.json +1 -1
  81. package/templates/animation-channel/tests/screenshot.spec.ts +16 -0
  82. package/templates/animation-studio/index.html +12 -0
  83. package/templates/animation-studio/node_modules.publish-aside/.vite/deps/_metadata.json +8 -0
  84. package/templates/animation-studio/node_modules.publish-aside/.vite/deps/package.json +3 -0
  85. package/templates/animation-studio/package.json +1 -1
  86. package/templates/animation-studio/scripts/animation-scene.ts +1 -1
  87. package/templates/animation-studio/scripts/asset-motion-probe.ts +7 -1
  88. package/templates/animation-studio/scripts/author-office-scene.ts +1 -1
  89. package/templates/animation-studio/scripts/build-characters.ts +1 -1
  90. package/templates/animation-studio/scripts/render-live.ts +24 -0
  91. package/templates/animation-studio/src/director/director-heuristics.ts +2 -2
  92. package/templates/animation-studio/src/director/{prompt-to-scene.ts → prompt-scene.ts} +74 -6
  93. package/templates/animation-studio/studio/src/App.tsx +146 -78
  94. package/templates/animation-studio/studio/src/components/Console.tsx +8 -2
  95. package/templates/animation-studio/studio/src/components/Inspector.tsx +57 -1
  96. package/templates/animation-studio/studio/src/components/Stage.tsx +10 -5
  97. package/templates/animation-studio/studio/src/components/Timeline.tsx +49 -2
  98. package/templates/animation-studio/studio/src/state/mapDocument.ts +13 -2
  99. package/templates/animation-studio/studio/src/state/sceneTool.ts +20 -9
  100. package/templates/animation-studio/studio/src/state/types.ts +8 -0
  101. package/templates/animation-studio/tests/route-health.spec.ts +33 -0
  102. package/templates/animation-studio/tests/screenshot.spec.ts +30 -0
  103. package/templates/character-controller/package.json +1 -1
  104. package/templates/character-controller/src/main.ts +18 -1
  105. package/templates/cinematic-scene/package.json +1 -1
  106. package/templates/cinematic-scene/tests/screenshot.spec.ts +3 -3
  107. package/templates/episode-builder/package.json +1 -1
  108. package/templates/episode-builder/tests/screenshot.spec.ts +15 -0
  109. package/templates/falling-blocks-starter/README.md +18 -0
  110. package/templates/falling-blocks-starter/index.html +23 -0
  111. package/templates/falling-blocks-starter/package.json +21 -0
  112. package/templates/falling-blocks-starter/playwright.config.ts +12 -0
  113. package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.679d52fe.glb +0 -0
  114. package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.thumb.svg +1 -0
  115. package/templates/falling-blocks-starter/src/aura-assets.ts +20 -0
  116. package/templates/falling-blocks-starter/src/main.ts +358 -0
  117. package/templates/falling-blocks-starter/tests/playable.spec.ts +85 -0
  118. package/templates/falling-blocks-starter/tests/route-health.spec.ts +34 -0
  119. package/templates/falling-blocks-starter/tsconfig.json +13 -0
  120. package/templates/fighting-game/package.json +1 -1
  121. package/templates/fighting-game/tests/screenshot.spec.ts +15 -0
  122. package/templates/mini-game/README.md +13 -0
  123. package/templates/mini-game/index.html +1 -1
  124. package/templates/mini-game/package.json +2 -2
  125. package/templates/mini-game/src/main.ts +251 -21
  126. package/templates/mini-game/tests/playable.spec.ts +49 -0
  127. package/templates/mini-game/tests/screenshot.spec.ts +37 -32
  128. package/templates/product-viewer/package.json +1 -1
  129. package/templates/product-viewer/tests/screenshot.spec.ts +4 -4
  130. package/templates/prompt-animation-channel/package.json +1 -1
  131. package/templates/prompt-animation-channel/tests/screenshot.spec.ts +15 -0
  132. package/templates/racing-starter/README.md +27 -0
  133. package/templates/racing-starter/index.html +23 -0
  134. package/templates/racing-starter/package.json +21 -0
  135. package/templates/racing-starter/playwright.config.ts +12 -0
  136. package/templates/racing-starter/public/aura-assets/showcaseCleanSportsCar.1c69283f.glb +0 -0
  137. package/templates/racing-starter/public/aura-assets/showcaseCleanSportsCar.thumb.svg +1 -0
  138. package/templates/racing-starter/public/aura-assets/showcaseReadableKartCircuit.5cbb912e.glb +0 -0
  139. package/templates/racing-starter/public/aura-assets/showcaseReadableKartCircuit.thumb.svg +1 -0
  140. package/templates/racing-starter/src/aura-assets.ts +36 -0
  141. package/templates/racing-starter/src/main.ts +280 -0
  142. package/templates/racing-starter/tests/playable.spec.ts +93 -0
  143. package/templates/racing-starter/tests/route-health.spec.ts +40 -0
  144. package/templates/racing-starter/tsconfig.json +13 -0
  145. package/templates/three-compat-architecture-interior/index.html +16 -0
  146. package/templates/three-compat-architecture-interior/package.json +21 -0
  147. package/templates/three-compat-architecture-interior/playwright.config.ts +14 -0
  148. package/templates/three-compat-architecture-interior/src/main.ts +27 -0
  149. package/templates/three-compat-architecture-interior/tests/route-health.spec.ts +16 -0
  150. package/templates/three-compat-architecture-interior/tests/screenshot.spec.ts +15 -0
  151. package/templates/three-compat-architecture-interior/tsconfig.json +19 -0
  152. package/templates/three-compat-asset-inspector/index.html +16 -0
  153. package/templates/three-compat-asset-inspector/package.json +21 -0
  154. package/templates/three-compat-asset-inspector/playwright.config.ts +14 -0
  155. package/templates/three-compat-asset-inspector/src/main.ts +17 -0
  156. package/templates/three-compat-asset-inspector/tests/route-health.spec.ts +16 -0
  157. package/templates/three-compat-asset-inspector/tests/screenshot.spec.ts +15 -0
  158. package/templates/three-compat-asset-inspector/tsconfig.json +19 -0
  159. package/templates/three-compat-character-viewer/index.html +16 -0
  160. package/templates/three-compat-character-viewer/package.json +21 -0
  161. package/templates/three-compat-character-viewer/playwright.config.ts +14 -0
  162. package/templates/three-compat-character-viewer/src/main.ts +17 -0
  163. package/templates/three-compat-character-viewer/tests/route-health.spec.ts +16 -0
  164. package/templates/three-compat-character-viewer/tests/screenshot.spec.ts +15 -0
  165. package/templates/three-compat-character-viewer/tsconfig.json +19 -0
  166. package/templates/three-compat-custom-threejs-migration/index.html +16 -0
  167. package/templates/three-compat-custom-threejs-migration/package.json +21 -0
  168. package/templates/three-compat-custom-threejs-migration/playwright.config.ts +14 -0
  169. package/templates/three-compat-custom-threejs-migration/src/main.ts +16 -0
  170. package/templates/three-compat-custom-threejs-migration/tests/route-health.spec.ts +16 -0
  171. package/templates/three-compat-custom-threejs-migration/tests/screenshot.spec.ts +15 -0
  172. package/templates/three-compat-custom-threejs-migration/tsconfig.json +19 -0
  173. package/templates/three-compat-large-scene/index.html +16 -0
  174. package/templates/three-compat-large-scene/package.json +21 -0
  175. package/templates/three-compat-large-scene/playwright.config.ts +14 -0
  176. package/templates/three-compat-large-scene/src/main.ts +31 -0
  177. package/templates/three-compat-large-scene/tests/route-health.spec.ts +16 -0
  178. package/templates/three-compat-large-scene/tests/screenshot.spec.ts +15 -0
  179. package/templates/three-compat-large-scene/tsconfig.json +19 -0
  180. package/templates/three-compat-material-authoring/index.html +16 -0
  181. package/templates/three-compat-material-authoring/package.json +21 -0
  182. package/templates/three-compat-material-authoring/playwright.config.ts +14 -0
  183. package/templates/three-compat-material-authoring/src/main.ts +33 -0
  184. package/templates/three-compat-material-authoring/tests/route-health.spec.ts +16 -0
  185. package/templates/three-compat-material-authoring/tests/screenshot.spec.ts +15 -0
  186. package/templates/three-compat-material-authoring/tsconfig.json +19 -0
  187. package/templates/three-compat-postprocess-scene/index.html +16 -0
  188. package/templates/three-compat-postprocess-scene/package.json +21 -0
  189. package/templates/three-compat-postprocess-scene/playwright.config.ts +14 -0
  190. package/templates/three-compat-postprocess-scene/src/main.ts +17 -0
  191. package/templates/three-compat-postprocess-scene/tests/route-health.spec.ts +16 -0
  192. package/templates/three-compat-postprocess-scene/tests/screenshot.spec.ts +15 -0
  193. package/templates/three-compat-postprocess-scene/tsconfig.json +19 -0
  194. package/templates/three-compat-premium-product-viewer/index.html +16 -0
  195. package/templates/three-compat-premium-product-viewer/package.json +21 -0
  196. package/templates/three-compat-premium-product-viewer/playwright.config.ts +14 -0
  197. package/templates/three-compat-premium-product-viewer/src/main.ts +18 -0
  198. package/templates/three-compat-premium-product-viewer/tests/route-health.spec.ts +16 -0
  199. package/templates/three-compat-premium-product-viewer/tests/screenshot.spec.ts +15 -0
  200. package/templates/three-compat-premium-product-viewer/tsconfig.json +19 -0
  201. package/templates/animation-channel/public/aura-assets/moon-garden-feature-frame.png +0 -0
  202. package/templates/animation-channel/tests/reports/prompt-animation/cartoon-sample-episode.png +0 -0
  203. package/templates/animation-studio/dist/episodes/scene/episode-3d.webm +0 -0
  204. package/templates/animation-studio/dist/episodes/scene/frames/action.png +0 -0
  205. package/templates/animation-studio/dist/episodes/scene/frames/dialogue.png +0 -0
  206. package/templates/animation-studio/dist/episodes/scene/frames/final.png +0 -0
  207. package/templates/animation-studio/dist/episodes/scene/frames/first.png +0 -0
  208. package/templates/animation-studio/dist/episodes/scene/frames/mouth-closed.png +0 -0
  209. package/templates/animation-studio/dist/episodes/scene/frames/mouth-open.png +0 -0
  210. package/templates/animation-studio/dist/episodes/scene/render-live-summary.json +0 -5616
  211. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/broken.png +0 -0
  212. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/chefs.png +0 -0
  213. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/customer.png +0 -0
  214. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-1.png +0 -0
  215. package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-2.png +0 -0
  216. package/templates/animation-studio/dist/generated/second-scene.document.json +0 -1210
  217. package/templates/animation-studio/dist/generated/studio-edited.document.json +0 -1196
  218. package/templates/animation-studio/dist/scene/inshader-accept.document.json +0 -1
  219. package/templates/animation-studio/dist/scene/inshader-test.document.json +0 -1
  220. package/templates/animation-studio/dist/scene/integ.document.json +0 -1
  221. package/templates/animation-studio/dist/scene/working.document.json +0 -594
  222. package/templates/animation-studio/dist/scene/working.history.json +0 -1
  223. package/templates/animation-studio/studio/dist/assets/index-i8URxOOt.css +0 -1
  224. package/templates/animation-studio/studio/dist/assets/index-pC9hdZ-B.js +0 -11
  225. package/templates/animation-studio/studio/dist/index.html +0 -19
  226. package/templates/animation-studio/tests/reports/animation-studio/hifi-cast-validation.json +0 -938
@@ -1,27 +1,257 @@
1
- import { createAuraApp, definePromptPlan, promptPlanToScene } from "@aura3d/engine";
1
+ import {
2
+ camera,
3
+ createAuraApp,
4
+ game,
5
+ lights,
6
+ material,
7
+ model,
8
+ primitives,
9
+ scene,
10
+ type AuraNodeInput
11
+ } from "@aura3d/engine";
2
12
  import { assets } from "./aura-assets";
3
13
 
4
- const plan = definePromptPlan({
5
- sceneType: "mini-game",
6
- subject: { asset: assets.playerModel, label: "player" },
7
- style: "readable neon collect-and-dodge arena",
8
- environment: "bounded board with rails, lane markings, coins, hazards, and portal goal",
9
- camera: { preset: "game-board" },
10
- lighting: { preset: "game-readable" },
11
- effects: ["motion-trail", "hud", "bloom"],
12
- interaction: "keyboard",
13
- acceptanceCriteria: [
14
- "player, coins, hazards, and portal are all visible",
15
- "arena boundaries and lane direction are readable",
16
- "motion trail and shield communicate active game state"
14
+ declare global {
15
+ interface Window {
16
+ __AURA3D_MINI_GAME__?: MiniGameEvidence;
17
+ }
18
+ }
19
+
20
+ interface MiniGameEvidence {
21
+ readonly status: string;
22
+ readonly frame: number;
23
+ readonly score: number;
24
+ readonly deaths: number;
25
+ readonly checkpointId: string;
26
+ readonly collected: readonly string[];
27
+ readonly player: {
28
+ readonly x: number;
29
+ readonly y: number;
30
+ readonly grounded: boolean;
31
+ };
32
+ readonly events: readonly string[];
33
+ readonly evidence: unknown;
34
+ }
35
+
36
+ const level = {
37
+ start: { x: 0, y: 0.35 },
38
+ finish: { x: 12.4, y: 0.35 },
39
+ platforms: [
40
+ { id: "launch", x: -0.6, y: 0, width: 4.2, height: 0.35 },
41
+ { id: "middle", x: 3.8, y: 0.62, width: 3.2, height: 0.3 },
42
+ { id: "finish", x: 8.2, y: 0.18, width: 4.8, height: 0.35 }
43
+ ],
44
+ movingPlatforms: [
45
+ { id: "lift", x: 6.9, y: 1.05, width: 1.35, height: 0.22, axis: "y" as const, amplitude: 0.42, period: 2.4 }
46
+ ],
47
+ collectibles: [
48
+ { id: "coin-01", x: 1.4, y: 1.15, value: 50, radius: 0.55 },
49
+ { id: "coin-02", x: 4.8, y: 1.45, value: 75, radius: 0.55 },
50
+ { id: "coin-03", x: 9.8, y: 1.05, value: 100, radius: 0.55 }
51
+ ],
52
+ hazards: [
53
+ { id: "spikes", x: 7.7, y: 0.42, width: 0.85, height: 0.24 }
54
+ ],
55
+ checkpoints: [
56
+ { id: "mid", x: 5.2, y: 0.92, radius: 0.8 }
17
57
  ],
18
- negativeCriteria: [
19
- "do not accept random primitives without readable game state",
20
- "do not accept a character on an empty grid as a mini-game"
21
- ]
22
- } as const);
58
+ lowerBound: -2.4,
59
+ moveSpeed: 5.2,
60
+ jumpVelocity: 8,
61
+ dashSpeed: 9.5
62
+ };
23
63
 
24
- createAuraApp("#app", {
64
+ const input = game.input({
65
+ actions: {
66
+ left: ["KeyA", "ArrowLeft"],
67
+ right: ["KeyD", "ArrowRight"],
68
+ jump: ["Space", "KeyW", "ArrowUp"],
69
+ dash: ["ShiftLeft", "ShiftRight"],
70
+ down: ["KeyS", "ArrowDown"],
71
+ reset: ["KeyR"]
72
+ },
73
+ axes: {
74
+ moveX: { negative: "left", positive: "right" }
75
+ },
76
+ bufferMs: 140
77
+ });
78
+
79
+ const platformer = game.platformer(level);
80
+ const routeEvents = game.eventLog({ label: "mini game route events", maxEvents: 16 });
81
+ const hud = game.hud.bindings([
82
+ game.hud.score({ valuePath: "appState.score" }),
83
+ game.hud.lives({ valuePath: "appState.lives" }),
84
+ game.hud.objective({ valuePath: "appState.objective" }),
85
+ game.hud.checkpoint({ valuePath: "appState.checkpoint" }),
86
+ game.hud.eventLog({ valuePath: "appState.events" })
87
+ ]);
88
+
89
+ const app = createAuraApp("#app", {
25
90
  diagnostics: { overlay: true, performancePanel: true },
26
- scene: promptPlanToScene(plan)
91
+ scene: buildScene()
92
+ });
93
+
94
+ const player = app.nodes.require("mini-player");
95
+ const lift = app.nodes.require("platform-lift");
96
+ const coins = level.collectibles.map((coin) => [coin.id, app.nodes.require(`coin-${coin.id}`)] as const);
97
+ const checkpoint = app.nodes.require("checkpoint-mid");
98
+ const goal = app.nodes.require("goal");
99
+ const hudRoot = createHud();
100
+
101
+ let objective = "Collect coins, avoid spikes, reach the goal";
102
+
103
+ app.onFrame(({ dt }: { readonly dt: number }) => {
104
+ input.update(dt);
105
+ if (input.pressed("reset")) {
106
+ platformer.reset();
107
+ routeEvents.push({ type: "reset", label: "Route reset" });
108
+ objective = "Collect coins, avoid spikes, reach the goal";
109
+ }
110
+
111
+ const state = platformer.step(dt, {
112
+ moveX: input.axis("moveX"),
113
+ jumpPressed: input.buffered("jump"),
114
+ jumpHeld: input.held("jump"),
115
+ dashPressed: input.pressed("dash"),
116
+ fastFall: input.held("down")
117
+ });
118
+
119
+ for (const event of state.events) {
120
+ routeEvents.push({
121
+ type: event.type,
122
+ label: event.id ? `${event.type}:${event.id}` : event.type,
123
+ targetId: event.id,
124
+ severity: event.type === "complete" || event.type === "collect" ? "success" : event.type === "hazard" || event.type === "fall" ? "warning" : "info",
125
+ frame: event.frame,
126
+ time: event.time
127
+ });
128
+ if (event.type === "checkpoint") objective = "Checkpoint reached. Finish the route.";
129
+ if (event.type === "complete") objective = "Finished. Press R to replay.";
130
+ if (event.type === "respawn") objective = "Respawned. Take the safer route.";
131
+ }
132
+
133
+ player.setPosition(state.player.x, state.player.y + 0.54, 0);
134
+ lift.setPosition(level.movingPlatforms[0].x + level.movingPlatforms[0].width / 2, movingLiftY(state.time), 0);
135
+ for (const [id, node] of coins) node.setVisible(!state.collected.includes(id));
136
+ checkpoint.setVisible(!state.activatedCheckpoints.includes("mid"));
137
+ goal.setScale(state.status === "completed" ? [0.28, 1.08, 0.18] : [0.22, 0.92, 0.16]);
138
+
139
+ renderHud(state);
140
+ publishEvidence(state);
27
141
  });
142
+
143
+ publishEvidence(platformer.snapshot());
144
+ renderHud(platformer.snapshot());
145
+
146
+ function buildScene() {
147
+ const nodes: AuraNodeInput[] = [
148
+ model(assets.playerModel, { name: "typed mini-game player", castShadow: true })
149
+ .position(level.start.x, level.start.y + 0.54, 0)
150
+ .scale(0.42)
151
+ .runtime(game.runtimeNode("mini-player", { tags: ["player", "typed-asset", "runtime"] })),
152
+ ...level.platforms.map((platform) =>
153
+ primitives.box({ name: `${platform.id} platform`, material: material.neon({ color: "#62d8ef", emissive: "#62d8ef", emissiveIntensity: 0.18 }) })
154
+ .position(platform.x + platform.width / 2, platform.y + platform.height / 2, -0.05)
155
+ .scale([platform.width, platform.height, 0.22])
156
+ ),
157
+ primitives.box({ name: "moving lift platform", material: material.neon({ color: "#b5f77d", emissive: "#b5f77d", emissiveIntensity: 0.35 }) })
158
+ .position(level.movingPlatforms[0].x + level.movingPlatforms[0].width / 2, movingLiftY(0), 0)
159
+ .scale([level.movingPlatforms[0].width, level.movingPlatforms[0].height, 0.22])
160
+ .runtime(game.runtimeNode("platform-lift", { tags: ["moving-platform", "runtime"] })),
161
+ ...level.collectibles.map((coin) =>
162
+ primitives.sphere({ name: `${coin.id} collectible`, material: material.neon({ color: "#f7d76b", emissive: "#f7d76b", emissiveIntensity: 0.7 }) })
163
+ .position(coin.x, coin.y, 0.02)
164
+ .scale(0.18)
165
+ .runtime(game.runtimeNode(`coin-${coin.id}`, { tags: ["coin", "runtime"] }))
166
+ ),
167
+ primitives.box({ name: "hazard spikes", material: material.neon({ color: "#f06b7a", emissive: "#f06b7a", emissiveIntensity: 0.7 }) })
168
+ .position(level.hazards[0].x + level.hazards[0].width / 2, level.hazards[0].y + level.hazards[0].height / 2, 0.05)
169
+ .scale([level.hazards[0].width, level.hazards[0].height, 0.2]),
170
+ primitives.box({ name: "checkpoint marker", material: material.neon({ color: "#9df59e", emissive: "#9df59e", emissiveIntensity: 0.65 }) })
171
+ .position(level.checkpoints[0].x, level.checkpoints[0].y + 0.42, 0.08)
172
+ .scale([0.14, 0.85, 0.14])
173
+ .runtime(game.runtimeNode("checkpoint-mid", { tags: ["checkpoint", "runtime"] })),
174
+ primitives.box({ name: "finish portal", material: material.neon({ color: "#ffad57", emissive: "#ffad57", emissiveIntensity: 0.82 }) })
175
+ .position(level.finish.x, level.finish.y + 0.7, 0.08)
176
+ .scale([0.22, 0.92, 0.16])
177
+ .runtime(game.runtimeNode("goal", { tags: ["finish", "runtime"] }))
178
+ ];
179
+
180
+ return scene()
181
+ .background("#071015")
182
+ .addMany(nodes)
183
+ .add(lights.ambient({ name: "game ambient", intensity: 0.38, color: "#d9f8ff" }))
184
+ .add(lights.directional({ name: "game key", position: [4, 6, 5], intensity: 1.2, color: "#ffffff" }))
185
+ .camera(camera.perspective({ position: [5.8, 4.3, 8.5], target: [5.8, 0.9, 0], fov: 44 }));
186
+ }
187
+
188
+ function movingLiftY(time: number): number {
189
+ const platform = level.movingPlatforms[0];
190
+ return platform.y + platform.height / 2 + Math.sin((time / platform.period) * Math.PI * 2) * platform.amplitude;
191
+ }
192
+
193
+ function createHud(): HTMLElement {
194
+ const root = document.createElement("aside");
195
+ root.id = "mini-game-hud";
196
+ root.style.cssText = [
197
+ "position:absolute",
198
+ "left:16px",
199
+ "top:16px",
200
+ "z-index:5",
201
+ "min-width:260px",
202
+ "font:600 13px/1.35 Inter, system-ui, sans-serif",
203
+ "color:#f5fbff",
204
+ "background:rgba(3,9,14,0.78)",
205
+ "border:1px solid rgba(125,220,235,0.34)",
206
+ "border-radius:8px",
207
+ "padding:12px",
208
+ "pointer-events:none"
209
+ ].join(";");
210
+ document.body.append(root);
211
+ return root;
212
+ }
213
+
214
+ function renderHud(state: ReturnType<typeof platformer.snapshot>): void {
215
+ hudRoot.innerHTML = [
216
+ `<strong>Aura3D Mini Game</strong>`,
217
+ `<div>Score ${state.score} | Lives ${state.lives} | Deaths ${state.deaths}</div>`,
218
+ `<div>Checkpoint ${state.checkpointId}</div>`,
219
+ `<div>${objective}</div>`,
220
+ `<div>Move A/D or arrows. Jump Space. Dash Shift. Reset R.</div>`
221
+ ].join("");
222
+ }
223
+
224
+ function publishEvidence(state: ReturnType<typeof platformer.snapshot>): void {
225
+ const evidence = app.evidence({
226
+ input,
227
+ events: routeEvents,
228
+ hud,
229
+ appState: {
230
+ score: state.score,
231
+ lives: state.lives,
232
+ objective,
233
+ checkpoint: state.checkpointId,
234
+ events: routeEvents.events().map((event: { readonly label: string }) => event.label)
235
+ },
236
+ assets: {
237
+ typedAssets: Object.keys(assets).length,
238
+ missingAssets: []
239
+ },
240
+ source: { expectsGame: true }
241
+ });
242
+ window.__AURA3D_MINI_GAME__ = {
243
+ status: state.status,
244
+ frame: state.frame,
245
+ score: state.score,
246
+ deaths: state.deaths,
247
+ checkpointId: state.checkpointId,
248
+ collected: state.collected,
249
+ player: {
250
+ x: state.player.x,
251
+ y: state.player.y,
252
+ grounded: state.player.grounded
253
+ },
254
+ events: routeEvents.events().map((event: { readonly label: string }) => event.label),
255
+ evidence
256
+ };
257
+ }
@@ -0,0 +1,49 @@
1
+ import { expect, test } from "@playwright/test";
2
+
3
+ test.setTimeout(60_000);
4
+
5
+ type MiniGameState = {
6
+ readonly score: number;
7
+ readonly events: readonly string[];
8
+ readonly player: {
9
+ readonly x: number;
10
+ readonly y: number;
11
+ };
12
+ };
13
+
14
+ function readMiniGameState(): MiniGameState | undefined {
15
+ return (window as unknown as { readonly __AURA3D_MINI_GAME__?: MiniGameState }).__AURA3D_MINI_GAME__;
16
+ }
17
+
18
+ test("Aura3D mini game responds to keyboard input, scoring, and reset", async ({ page }) => {
19
+ await page.goto("/");
20
+ await expect.poll(() => page.locator("body").getAttribute("data-aura3d-ready"), { timeout: 45_000 }).toBe("true");
21
+
22
+ const initial = await page.evaluate(readMiniGameState);
23
+ expect(initial?.player.x).toBeLessThan(0.2);
24
+
25
+ await page.keyboard.down("ArrowRight");
26
+ await page.waitForTimeout(650);
27
+ await page.keyboard.up("ArrowRight");
28
+
29
+ const moved = await page.evaluate(readMiniGameState);
30
+ expect(moved?.player.x ?? 0).toBeGreaterThan((initial?.player.x ?? 0) + 1.2);
31
+
32
+ await page.keyboard.press("Space");
33
+ await page.waitForTimeout(180);
34
+ const jumped = await page.evaluate(readMiniGameState);
35
+ expect(jumped?.player.y ?? 0).toBeGreaterThan(moved?.player.y ?? 0);
36
+
37
+ await page.keyboard.down("ArrowRight");
38
+ await page.waitForTimeout(1600);
39
+ await page.keyboard.up("ArrowRight");
40
+ const progressed = await page.evaluate(readMiniGameState);
41
+ expect(progressed?.score ?? 0).toBeGreaterThanOrEqual(50);
42
+ expect(progressed?.events.length ?? 0).toBeGreaterThan(0);
43
+
44
+ await page.keyboard.press("KeyR");
45
+ await page.waitForTimeout(120);
46
+ const reset = await page.evaluate(readMiniGameState);
47
+ expect(reset?.player.x ?? 99).toBeLessThan(0.3);
48
+ expect(reset?.events.some((event) => event.includes("reset"))).toBe(true);
49
+ });
@@ -4,60 +4,65 @@ import { expect, test } from "@playwright/test";
4
4
 
5
5
  test.setTimeout(60_000);
6
6
 
7
- test("Aura3D mini game screenshot shows a playable collect-and-dodge scene", async ({ page }) => {
7
+ test("Aura3D mini game screenshot shows a readable playable scene", async ({ page }) => {
8
8
  await page.goto("/");
9
9
  await expect.poll(() => page.locator("body").getAttribute("data-aura3d-ready"), { timeout: 45_000 }).toBe("true");
10
+
11
+ await page.keyboard.down("ArrowRight");
12
+ await page.waitForTimeout(550);
13
+ await page.keyboard.up("ArrowRight");
14
+
15
+ const state = await page.evaluate(() =>
16
+ (window as unknown as {
17
+ readonly __AURA3D_MINI_GAME__?: {
18
+ readonly score: number;
19
+ readonly player: { readonly x: number };
20
+ };
21
+ }).__AURA3D_MINI_GAME__
22
+ );
23
+ expect(state?.player.x ?? 0).toBeGreaterThan(0.8);
24
+
10
25
  const canvas = page.locator("canvas");
11
26
  const profile = await canvas.evaluate((element) => {
12
27
  const target = element as HTMLCanvasElement;
13
28
  const gl = target.getContext("webgl2", { preserveDrawingBuffer: true });
14
- if (!gl) return { error: "missing-webgl2", robotArmorPixels: 0, robotJointPixels: 0, boostPixels: 0, coinPixels: 0, hazardPixels: 0, portalPixels: 0, cyanTrailPixels: 0, arenaPixels: 0, uniqueBuckets: 0 };
29
+ if (!gl) return { error: "missing-webgl2", brightPixels: 0, warmPixels: 0, cyanPixels: 0, redPixels: 0, uniqueBuckets: 0 };
15
30
  const pixels = new Uint8Array(target.width * target.height * 4);
16
31
  gl.readPixels(0, 0, target.width, target.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
17
32
  const buckets = new Set<string>();
18
- let robotArmorPixels = 0;
19
- let robotJointPixels = 0;
20
- let boostPixels = 0;
21
- let coinPixels = 0;
22
- let hazardPixels = 0;
23
- let portalPixels = 0;
24
- let cyanTrailPixels = 0;
25
- let arenaPixels = 0;
33
+ let brightPixels = 0;
34
+ let warmPixels = 0;
35
+ let cyanPixels = 0;
36
+ let redPixels = 0;
26
37
  for (let y = 0; y < target.height; y += 4) {
27
38
  for (let x = 0; x < target.width; x += 4) {
28
- if (x > target.width * 0.76 && y > target.height * 0.74) continue;
29
39
  const offset = (y * target.width + x) * 4;
30
40
  const r = pixels[offset] ?? 0;
31
41
  const g = pixels[offset + 1] ?? 0;
32
42
  const b = pixels[offset + 2] ?? 0;
33
43
  const luminance = r * 0.2126 + g * 0.7152 + b * 0.0722;
34
- if (luminance > 32) buckets.add(`${r >> 5}-${g >> 5}-${b >> 5}`);
35
- const inPlayerWindow = x > target.width * 0.2 && x < target.width * 0.42 && y > target.height * 0.34 && y < target.height * 0.7;
36
- if (inPlayerWindow && r > 120 && g > 118 && b > 86 && Math.abs(r - g) < 70) robotArmorPixels += 1;
37
- if (inPlayerWindow && r < 95 && g < 115 && b < 125 && luminance > 18) robotJointPixels += 1;
38
- if (inPlayerWindow && r > 150 && g > 80 && b < 82) boostPixels += 1;
39
- if (r > 170 && g > 130 && b < 110) coinPixels += 1;
40
- if (r > 165 && g < 125 && b < 145) hazardPixels += 1;
41
- if (r > 165 && g > 95 && b < 125) portalPixels += 1;
42
- if (g > 140 && b > 150 && r < 150) cyanTrailPixels += 1;
43
- if (g > 42 && b > 50 && b > r * 1.08 && luminance > 24 && luminance < 100) arenaPixels += 1;
44
+ if (luminance > 30) {
45
+ brightPixels += 1;
46
+ buckets.add(`${r >> 5}-${g >> 5}-${b >> 5}`);
47
+ }
48
+ if (r > 130 && g > 105 && b < 155 && r >= g * 0.9) warmPixels += 1;
49
+ if (g > 115 && b > 105 && r < 190 && b >= r * 0.78) cyanPixels += 1;
50
+ if (r > 120 && g < 150 && b < 180 && r > g * 1.03) redPixels += 1;
44
51
  }
45
52
  }
46
- return { robotArmorPixels, robotJointPixels, boostPixels, coinPixels, hazardPixels, portalPixels, cyanTrailPixels, arenaPixels, uniqueBuckets: buckets.size };
53
+ return { brightPixels, warmPixels, cyanPixels, redPixels, uniqueBuckets: buckets.size };
47
54
  });
55
+
48
56
  const screenshot = await page.screenshot({ fullPage: false });
49
57
  mkdirSync(resolve("tests/reports"), { recursive: true });
50
58
  writeFileSync(resolve("tests/reports/screenshot.png"), screenshot);
51
- writeFileSync(resolve("tests/reports/screenshot.json"), `${JSON.stringify({ bytes: screenshot.byteLength, profile }, null, 2)}\n`);
59
+ writeFileSync(resolve("tests/reports/screenshot.json"), `${JSON.stringify({ bytes: screenshot.byteLength, profile, state }, null, 2)}\n`);
60
+
52
61
  expect(profile.error).toBeUndefined();
53
- expect(profile.robotArmorPixels).toBeGreaterThan(90);
54
- expect(profile.robotJointPixels).toBeGreaterThan(18);
55
- expect(profile.boostPixels).toBeGreaterThan(8);
56
- expect(profile.coinPixels).toBeGreaterThan(35);
57
- expect(profile.hazardPixels).toBeGreaterThan(45);
58
- expect(profile.portalPixels).toBeGreaterThan(45);
59
- expect(profile.cyanTrailPixels).toBeGreaterThan(90);
60
- expect(profile.arenaPixels).toBeGreaterThan(600);
61
- expect(profile.uniqueBuckets).toBeGreaterThan(22);
62
+ expect(profile.brightPixels).toBeGreaterThan(900);
63
+ expect(profile.cyanPixels).toBeGreaterThan(20);
64
+ expect(profile.warmPixels).toBeGreaterThan(10);
65
+ expect(profile.redPixels).toBeGreaterThan(5);
66
+ expect(profile.uniqueBuckets).toBeGreaterThan(10);
62
67
  expect(screenshot.byteLength).toBeGreaterThan(1000);
63
68
  });
@@ -11,7 +11,7 @@
11
11
  "test": "playwright test tests/route-health.spec.ts tests/screenshot.spec.ts --workers=1"
12
12
  },
13
13
  "dependencies": {
14
- "@aura3d/engine": "1.3.2"
14
+ "@aura3d/engine": "1.3.3"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@playwright/test": "^1.52.0",
@@ -31,8 +31,8 @@ test("Aura3D product viewer screenshot shows a prompt-aligned studio product", a
31
31
  if (luminance > 32) buckets.add(`${r >> 5}-${g >> 5}-${b >> 5}`);
32
32
  const inProductWindow = x > target.width * 0.34 && x < target.width * 0.68 && y > target.height * 0.24 && y < target.height * 0.78;
33
33
  if (inProductWindow && r > 145 && g > 145 && b > 138 && Math.abs(r - g) < 32 && Math.abs(g - b) < 40) metalPixels += 1;
34
- if (r > 95 && g > 62 && b < 78 && r > g * 1.12) warmAccentPixels += 1;
35
- if (Math.max(r, g, b) - Math.min(r, g, b) > 45 && luminance > 42) saturatedStudioPixels += 1;
34
+ if (r > 70 && g > 45 && b < 120 && r >= g && g >= b * 0.75) warmAccentPixels += 1;
35
+ if (Math.max(r, g, b) - Math.min(r, g, b) > 32 && luminance > 38) saturatedStudioPixels += 1;
36
36
  if (inProductWindow && luminance > 32) centerObjectPixels += 1;
37
37
  }
38
38
  }
@@ -44,9 +44,9 @@ test("Aura3D product viewer screenshot shows a prompt-aligned studio product", a
44
44
  writeFileSync(resolve("tests/reports/screenshot.json"), `${JSON.stringify({ bytes: screenshot.byteLength, profile }, null, 2)}\n`);
45
45
  expect(profile.error).toBeUndefined();
46
46
  expect(profile.metalPixels).toBeGreaterThan(5);
47
- expect(profile.warmAccentPixels).toBeGreaterThan(20);
47
+ expect(profile.warmAccentPixels).toBeGreaterThan(8);
48
48
  expect(profile.centerObjectPixels).toBeGreaterThan(650);
49
- expect(profile.saturatedStudioPixels).toBeGreaterThan(900);
49
+ expect(profile.saturatedStudioPixels).toBeGreaterThan(800);
50
50
  expect(profile.uniqueBuckets).toBeGreaterThan(18);
51
51
  expect(screenshot.byteLength).toBeGreaterThan(1000);
52
52
  });
@@ -11,7 +11,7 @@
11
11
  "test": "playwright test tests/route-health.spec.ts tests/storyboard-playback.spec.ts --workers=1"
12
12
  },
13
13
  "dependencies": {
14
- "@aura3d/engine": "1.3.2"
14
+ "@aura3d/engine": "1.3.3"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@playwright/test": "^1.52.0",
@@ -0,0 +1,15 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { expect, test } from "@playwright/test";
4
+
5
+ test.setTimeout(60_000);
6
+
7
+ test("Aura3D prompt animation channel screenshot shows content", async ({ page }) => {
8
+ await page.goto("/");
9
+ await expect.poll(() => page.locator("body").getAttribute("data-aura3d-ready"), { timeout: 45_000 }).toBe("true");
10
+ const screenshot = await page.screenshot({ fullPage: false });
11
+ mkdirSync(resolve("tests/reports"), { recursive: true });
12
+ writeFileSync(resolve("tests/reports/screenshot.png"), screenshot);
13
+ writeFileSync(resolve("tests/reports/screenshot.json"), `${JSON.stringify({ bytes: screenshot.byteLength }, null, 2)}\n`);
14
+ expect(screenshot.byteLength).toBeGreaterThan(1000);
15
+ });
@@ -0,0 +1,27 @@
1
+ # Aura3D Racing Starter
2
+
3
+ Keyboard-playable racing starter using only the public `@aura3d/engine` API.
4
+
5
+ Status: source-level prototype starter, not a public-quality racing game claim.
6
+ It proves input, route progress, checkpoints, lap/reset state, and deployable
7
+ source structure. It does not prove that an arbitrary track GLB has certified
8
+ road topology, car-to-road binding, camera-safe race composition, or public
9
+ visual quality.
10
+
11
+ - Typed vehicle and track assets are defined in `src/aura-assets.ts`.
12
+ - `game.racing(...)` owns route progress, throttle, steering, drift, checkpoint,
13
+ lap, reset, and camera proof state.
14
+ - `tests/playable.spec.ts` drives keyboard input and verifies checkpoint/lap
15
+ progression.
16
+
17
+ Run:
18
+
19
+ ```bash
20
+ npm install
21
+ npm run dev
22
+ npm test
23
+ ```
24
+
25
+ Before presenting a generated racing route as a public example, add retained
26
+ racing topology evidence, prove the car is visibly bound to the road surface,
27
+ capture a readable public screenshot, and pass visual review.
@@ -0,0 +1,23 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Aura3D Racing Starter</title>
7
+ <style>
8
+ html,
9
+ body,
10
+ #app {
11
+ width: 100%;
12
+ height: 100%;
13
+ margin: 0;
14
+ overflow: hidden;
15
+ background: #05090c;
16
+ }
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <div id="app"></div>
21
+ <script type="module" src="./src/main.ts"></script>
22
+ </body>
23
+ </html>
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "aura3d-racing-starter",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --host 127.0.0.1",
8
+ "typecheck": "tsc --noEmit",
9
+ "build": "npm run typecheck && vite build",
10
+ "preview": "vite preview --host 127.0.0.1",
11
+ "test": "playwright test tests/playable.spec.ts --workers=1"
12
+ },
13
+ "dependencies": {
14
+ "@aura3d/engine": "1.0.0"
15
+ },
16
+ "devDependencies": {
17
+ "@playwright/test": "^1.52.0",
18
+ "typescript": "^5.8.3",
19
+ "vite": "^7.3.2"
20
+ }
21
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "@playwright/test";
2
+
3
+ export default defineConfig({
4
+ webServer: {
5
+ command: "npm run dev -- --port 5178",
6
+ url: "http://127.0.0.1:5178",
7
+ reuseExistingServer: !process.env.CI
8
+ },
9
+ use: {
10
+ baseURL: "http://127.0.0.1:5178"
11
+ }
12
+ });
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 180"><rect width="320" height="180" fill="#101720"/><path d="M80 126 160 46l80 80-80 28z" fill="#77a7ff"/><text x="160" y="160" text-anchor="middle" font-family="Arial" font-size="18" fill="#f4f7fb">showcaseCleanSportsCar 3.455x3.428x2.206</text></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 180"><rect width="320" height="180" fill="#101720"/><path d="M80 126 160 46l80 80-80 28z" fill="#77a7ff"/><text x="160" y="160" text-anchor="middle" font-family="Arial" font-size="18" fill="#f4f7fb">showcaseReadableKartCircuit 24.651x24.647x2.073</text></svg>
@@ -0,0 +1,36 @@
1
+ import { defineAuraAssets } from "@aura3d/engine";
2
+
3
+ export const assets = defineAuraAssets({
4
+ carModel: {
5
+ type: "model",
6
+ format: "glb",
7
+ url: "/aura-assets/showcaseCleanSportsCar.1c69283f.glb",
8
+ bounds: [3.455, 3.428, 2.206],
9
+ hash: "sha256-1c69283fa2b3fb96b3dc3418f2d0dede842e6984f1776a8ca00b099836fc9c14",
10
+ metadata: {
11
+ materials: ["Material.001", "Material.002", "Material.003", "Material.004"],
12
+ animations: [],
13
+ textures: [],
14
+ license: "CC-BY-4.0",
15
+ author: "JUFF",
16
+ sourcePage: "https://huggingface.co/datasets/allenai/objaverse/blob/main/glbs/000-008/23dfdeb55dc24970b36065afaab7a8a5.glb",
17
+ thumbnailUrl: "/aura-assets/showcaseCleanSportsCar.thumb.svg"
18
+ }
19
+ },
20
+ trackModel: {
21
+ type: "model",
22
+ format: "glb",
23
+ url: "/aura-assets/showcaseReadableKartCircuit.5cbb912e.glb",
24
+ bounds: [24.651, 24.647, 2.073],
25
+ hash: "sha256-5cbb912e511e5e82363c347210096d59dd3007db12c363fd0c9f07919f5ebbd0",
26
+ metadata: {
27
+ materials: ["LINEAS", "gris", "verde", "rojo", "azul", "amarillo"],
28
+ animations: [],
29
+ textures: [],
30
+ license: "CC-BY-4.0",
31
+ author: "Puendiz",
32
+ sourcePage: "https://huggingface.co/datasets/allenai/objaverse/blob/main/glbs/000-034/2fc89fcd8c034bc9ba05eac24c9bf537.glb",
33
+ thumbnailUrl: "/aura-assets/showcaseReadableKartCircuit.thumb.svg"
34
+ }
35
+ }
36
+ } as const);