create-aura3d 3.0.1 → 4.0.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 (396) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-skills.d.ts +38 -0
  3. package/dist/agent-skills.d.ts.map +1 -0
  4. package/dist/agent-skills.js +103 -0
  5. package/dist/agent-skills.js.map +1 -0
  6. package/dist/cli.js +32 -5
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.d.ts +32 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +52 -10
  11. package/dist/index.js.map +1 -1
  12. package/dist/showcase-spec-artifacts.d.ts +1 -1
  13. package/dist/showcase-spec-game-geometry-extractor.js +8 -11
  14. package/dist/showcase-spec-game-geometry-extractor.js.map +1 -1
  15. package/dist/template-hdri.d.ts +12 -0
  16. package/dist/template-hdri.d.ts.map +1 -0
  17. package/dist/template-hdri.js +49 -0
  18. package/dist/template-hdri.js.map +1 -0
  19. package/hdri/ATTRIBUTION.md +8 -0
  20. package/hdri/autumn_field_puresky_1k.hdr +0 -0
  21. package/hdri/kloppenheim_06_puresky_1k.hdr +0 -0
  22. package/hdri/night-2k.hdr +0 -0
  23. package/hdri/studio_small_08_1k.hdr +0 -0
  24. package/package.json +14 -10
  25. package/skills/agent-files/AGENTS.md +3 -0
  26. package/skills/aura3d-animation-studio/SKILL.md +153 -0
  27. package/skills/aura3d-animation-studio/references/shot-looks.md +26 -0
  28. package/skills/aura3d-art-direction/SKILL.md +84 -0
  29. package/skills/aura3d-art-direction/references/failure-gallery.md +64 -0
  30. package/skills/aura3d-art-direction/references/framing-and-shots.md +45 -0
  31. package/skills/aura3d-art-direction/references/look-recipes.md +32 -0
  32. package/skills/aura3d-art-direction/references/quality-bar.md +103 -0
  33. package/skills/aura3d-art-direction/references/reference-frames.md +82 -0
  34. package/skills/aura3d-assets/SKILL.md +132 -0
  35. package/skills/aura3d-assets/references/preview-under-look.md +26 -0
  36. package/skills/aura3d-browser-game/SKILL.md +127 -0
  37. package/skills/aura3d-browser-game/references/genre-looks.md +52 -0
  38. package/skills/aura3d-character-animation/SKILL.md +129 -0
  39. package/skills/aura3d-character-animation/references/stage-and-lighting.md +31 -0
  40. package/skills/aura3d-core/SKILL.md +121 -0
  41. package/skills/aura3d-core/references/boundaries.md +91 -0
  42. package/skills/aura3d-evidence-review/SKILL.md +106 -0
  43. package/skills/aura3d-game-art/SKILL.md +118 -0
  44. package/skills/aura3d-game-art/references/sheet-check.mjs +58 -0
  45. package/skills/aura3d-game-art/references/sprite-palettes.md +25 -0
  46. package/skills/aura3d-materials-environments/SKILL.md +139 -0
  47. package/skills/aura3d-performance/SKILL.md +97 -0
  48. package/skills/aura3d-retexture/SKILL.md +115 -0
  49. package/skills/aura3d-retexture/references/geometry-hash.mjs +89 -0
  50. package/skills/aura3d-scene-authoring/SKILL.md +132 -0
  51. package/skills/aura3d-scene-authoring/references/look-by-family.md +28 -0
  52. package/skills/aura3d-threejs-migration/SKILL.md +125 -0
  53. package/skills/aura3d-threejs-migration/references/look-mapping.md +27 -0
  54. package/skills/llms.txt +117 -0
  55. package/skills/manifest.json +163 -0
  56. package/skills/meshy-cli/SKILL.md +133 -0
  57. package/templates/animation-channel/.claude/CLAUDE.md +3 -0
  58. package/templates/animation-channel/.github/workflows/aura3d-lookdev.yml +67 -0
  59. package/templates/animation-channel/AGENTS.md +3 -0
  60. package/templates/animation-channel/package.json +3 -2
  61. package/templates/animation-channel/src/main.ts +5 -6
  62. package/templates/animation-channel/src/render-plan.ts +5 -3
  63. package/templates/animation-channel/tests/look-floor.ts +270 -0
  64. package/templates/animation-channel/tests/route-health.spec.ts +1 -1
  65. package/templates/animation-channel/tests/screenshot.spec.ts +10 -5
  66. package/templates/animation-channel/vite.config.ts +7 -0
  67. package/templates/animation-studio/.claude/CLAUDE.md +3 -0
  68. package/templates/animation-studio/.github/workflows/aura3d-lookdev.yml +67 -0
  69. package/templates/animation-studio/AGENTS.md +2 -70
  70. package/templates/animation-studio/package.json +4 -3
  71. package/templates/animation-studio/scripts/build-clip-library.ts +6 -5
  72. package/templates/animation-studio/scripts/resolve-asset.ts +7 -4
  73. package/templates/animation-studio/src/scene-player.ts +6 -6
  74. package/templates/animation-studio/src/set-templates.ts +13 -9
  75. package/templates/animation-studio/tests/look-floor.ts +270 -0
  76. package/templates/animation-studio/tests/route-health.spec.ts +2 -2
  77. package/templates/animation-studio/tests/screenshot.spec.ts +18 -10
  78. package/templates/animation-studio/vite.config.ts +2 -0
  79. package/templates/architecture-interior/.claude/CLAUDE.md +3 -0
  80. package/templates/architecture-interior/.github/workflows/aura3d-lookdev.yml +67 -0
  81. package/templates/architecture-interior/AGENTS.md +3 -0
  82. package/templates/architecture-interior/README.md +16 -0
  83. package/templates/architecture-interior/aura.assets.json +24 -0
  84. package/templates/architecture-interior/package.json +24 -0
  85. package/templates/architecture-interior/public/aura-assets/kenneyModularDungeonRoomSmall.a7e74a5f.glb +0 -0
  86. package/templates/architecture-interior/src/aura-assets.ts +16 -0
  87. package/templates/architecture-interior/src/main.ts +23 -0
  88. package/templates/architecture-interior/tests/look-floor.ts +270 -0
  89. package/templates/{three-compat-architecture-interior → architecture-interior}/tests/route-health.spec.ts +3 -3
  90. package/templates/architecture-interior/tests/screenshot.spec.ts +21 -0
  91. package/templates/architecture-interior/vite.config.ts +7 -0
  92. package/templates/arena-shooter/.claude/CLAUDE.md +3 -0
  93. package/templates/arena-shooter/.github/workflows/aura3d-lookdev.yml +67 -0
  94. package/templates/arena-shooter/AGENTS.md +3 -0
  95. package/templates/arena-shooter/README.md +45 -0
  96. package/templates/arena-shooter/aura.assets.json +62 -0
  97. package/templates/arena-shooter/index.html +32 -0
  98. package/templates/arena-shooter/package.json +23 -0
  99. package/templates/arena-shooter/playwright.config.ts +15 -0
  100. package/templates/arena-shooter/public/aura-assets/craft_miner.de323205.glb +0 -0
  101. package/templates/arena-shooter/public/aura-assets/craft_racer.bce8cb3d.glb +0 -0
  102. package/templates/arena-shooter/public/aura-assets/meteor_detailed.f30379ab.glb +0 -0
  103. package/templates/arena-shooter/src/aura-assets.ts +56 -0
  104. package/templates/arena-shooter/src/main.ts +616 -0
  105. package/templates/arena-shooter/tests/look-floor.ts +270 -0
  106. package/templates/arena-shooter/tests/playable.spec.ts +56 -0
  107. package/templates/arena-shooter/tests/route-health.spec.ts +87 -0
  108. package/templates/arena-shooter/tests/screenshot.spec.ts +22 -0
  109. package/templates/arena-shooter/tsconfig.json +15 -0
  110. package/templates/arena-shooter/vite.config.ts +7 -0
  111. package/templates/asset-inspector/.claude/CLAUDE.md +3 -0
  112. package/templates/asset-inspector/.github/workflows/aura3d-lookdev.yml +67 -0
  113. package/templates/asset-inspector/AGENTS.md +3 -0
  114. package/templates/asset-inspector/README.md +13 -0
  115. package/templates/asset-inspector/aura.assets.json +24 -0
  116. package/templates/{three-compat-custom-threejs-migration → asset-inspector}/package.json +5 -3
  117. package/templates/asset-inspector/public/aura-assets/kenneyCarKitSedanSports.2889c428.glb +0 -0
  118. package/templates/asset-inspector/src/aura-assets.ts +16 -0
  119. package/templates/asset-inspector/src/main.ts +23 -0
  120. package/templates/asset-inspector/tests/look-floor.ts +270 -0
  121. package/templates/{three-compat-character-viewer → asset-inspector}/tests/route-health.spec.ts +3 -3
  122. package/templates/asset-inspector/tests/screenshot.spec.ts +21 -0
  123. package/templates/asset-inspector/vite.config.ts +7 -0
  124. package/templates/character-controller/.claude/CLAUDE.md +3 -0
  125. package/templates/character-controller/.github/workflows/aura3d-lookdev.yml +67 -0
  126. package/templates/character-controller/AGENTS.md +3 -0
  127. package/templates/character-controller/README.md +7 -1
  128. package/templates/character-controller/aura.assets.json +29 -22
  129. package/templates/character-controller/index.html +4 -5
  130. package/templates/character-controller/package.json +5 -4
  131. package/templates/character-controller/public/aura-assets/auraclash-player-rig.glb +0 -0
  132. package/templates/character-controller/src/aura-assets.ts +25 -7
  133. package/templates/character-controller/src/main.ts +190 -47
  134. package/templates/character-controller/tests/certified-rig.spec.ts +2 -2
  135. package/templates/character-controller/tests/look-floor.ts +270 -0
  136. package/templates/character-controller/tests/route-health.spec.ts +31 -6
  137. package/templates/character-controller/tests/screenshot.spec.ts +17 -5
  138. package/templates/character-controller/vite.config.ts +7 -0
  139. package/templates/character-viewer/.claude/CLAUDE.md +3 -0
  140. package/templates/character-viewer/.github/workflows/aura3d-lookdev.yml +67 -0
  141. package/templates/character-viewer/AGENTS.md +3 -0
  142. package/templates/character-viewer/README.md +13 -0
  143. package/templates/character-viewer/aura.assets.json +24 -0
  144. package/templates/{three-compat-asset-inspector → character-viewer}/package.json +5 -3
  145. package/templates/character-viewer/public/aura-assets/kenneyMiniForestCharacterArcher.0fd0f92e.glb +0 -0
  146. package/templates/character-viewer/src/aura-assets.ts +16 -0
  147. package/templates/character-viewer/src/main.ts +21 -0
  148. package/templates/character-viewer/tests/look-floor.ts +270 -0
  149. package/templates/{three-compat-custom-threejs-migration → character-viewer}/tests/route-health.spec.ts +3 -3
  150. package/templates/character-viewer/tests/screenshot.spec.ts +21 -0
  151. package/templates/character-viewer/vite.config.ts +7 -0
  152. package/templates/cinematic-scene/.claude/CLAUDE.md +3 -0
  153. package/templates/cinematic-scene/.github/workflows/aura3d-lookdev.yml +67 -0
  154. package/templates/cinematic-scene/AGENTS.md +3 -0
  155. package/templates/cinematic-scene/README.md +11 -2
  156. package/templates/cinematic-scene/package.json +3 -2
  157. package/templates/cinematic-scene/src/main.ts +30 -7
  158. package/templates/cinematic-scene/tests/look-floor.ts +270 -0
  159. package/templates/cinematic-scene/tests/route-health.spec.ts +31 -3
  160. package/templates/cinematic-scene/tests/screenshot.spec.ts +34 -45
  161. package/templates/cinematic-scene/vite.config.ts +7 -0
  162. package/templates/custom-scene/.claude/CLAUDE.md +3 -0
  163. package/templates/custom-scene/.github/workflows/aura3d-lookdev.yml +67 -0
  164. package/templates/custom-scene/AGENTS.md +3 -0
  165. package/templates/custom-scene/README.md +13 -0
  166. package/templates/custom-scene/aura.assets.json +24 -0
  167. package/templates/{three-compat-character-viewer → custom-scene}/package.json +5 -3
  168. package/templates/custom-scene/public/aura-assets/kenneyPirateKitShipMedium.279ea019.glb +0 -0
  169. package/templates/custom-scene/src/aura-assets.ts +16 -0
  170. package/templates/custom-scene/src/main.ts +23 -0
  171. package/templates/custom-scene/tests/look-floor.ts +270 -0
  172. package/templates/{three-compat-asset-inspector → custom-scene}/tests/route-health.spec.ts +3 -3
  173. package/templates/custom-scene/tests/screenshot.spec.ts +21 -0
  174. package/templates/custom-scene/vite.config.ts +7 -0
  175. package/templates/episode-builder/.claude/CLAUDE.md +3 -0
  176. package/templates/episode-builder/.github/workflows/aura3d-lookdev.yml +67 -0
  177. package/templates/episode-builder/AGENTS.md +3 -0
  178. package/templates/episode-builder/package.json +3 -2
  179. package/templates/episode-builder/src/main.ts +5 -3
  180. package/templates/episode-builder/src/render-plan.ts +5 -3
  181. package/templates/episode-builder/tests/look-floor.ts +270 -0
  182. package/templates/episode-builder/tests/screenshot.spec.ts +10 -4
  183. package/templates/episode-builder/vite.config.ts +7 -0
  184. package/templates/falling-blocks-starter/.claude/CLAUDE.md +3 -0
  185. package/templates/falling-blocks-starter/.github/workflows/aura3d-lookdev.yml +67 -0
  186. package/templates/falling-blocks-starter/AGENTS.md +3 -0
  187. package/templates/falling-blocks-starter/README.md +10 -1
  188. package/templates/falling-blocks-starter/aura.assets.json +40 -23
  189. package/templates/falling-blocks-starter/package.json +3 -2
  190. package/templates/falling-blocks-starter/public/aura-assets/quaternius-cube-crate.glb +0 -0
  191. package/templates/falling-blocks-starter/public/aura-assets/quaternius-wall-modular.glb +0 -0
  192. package/templates/falling-blocks-starter/src/aura-assets.ts +33 -10
  193. package/templates/falling-blocks-starter/src/main.ts +316 -117
  194. package/templates/falling-blocks-starter/tests/look-floor.ts +270 -0
  195. package/templates/falling-blocks-starter/tests/playable.spec.ts +51 -18
  196. package/templates/falling-blocks-starter/tests/route-health.spec.ts +17 -7
  197. package/templates/falling-blocks-starter/tests/screenshot.spec.ts +21 -0
  198. package/templates/falling-blocks-starter/vite.config.ts +7 -0
  199. package/templates/fighting-game/.claude/CLAUDE.md +3 -0
  200. package/templates/fighting-game/.github/workflows/aura3d-lookdev.yml +67 -0
  201. package/templates/fighting-game/AGENTS.md +3 -0
  202. package/templates/fighting-game/README.md +2 -0
  203. package/templates/fighting-game/aura.assets.json +56 -46
  204. package/templates/fighting-game/package.json +4 -2
  205. package/templates/fighting-game/public/aura-assets/auraclash-player-rig.glb +0 -0
  206. package/templates/fighting-game/public/aura-assets/auraclash-rival-rig.glb +0 -0
  207. package/templates/fighting-game/src/aura-assets.ts +54 -20
  208. package/templates/fighting-game/src/game/fighters.ts +57 -12
  209. package/templates/fighting-game/src/main.ts +135 -53
  210. package/templates/fighting-game/tests/certified-rig.spec.ts +1 -1
  211. package/templates/fighting-game/tests/gameplay-smoke.spec.ts +2 -8
  212. package/templates/fighting-game/tests/look-floor.ts +270 -0
  213. package/templates/fighting-game/tests/route-health.spec.ts +12 -4
  214. package/templates/fighting-game/tests/screenshot.spec.ts +10 -4
  215. package/templates/fighting-game/vite.config.ts +7 -0
  216. package/templates/large-scene/.claude/CLAUDE.md +3 -0
  217. package/templates/large-scene/.github/workflows/aura3d-lookdev.yml +67 -0
  218. package/templates/large-scene/AGENTS.md +3 -0
  219. package/templates/large-scene/README.md +13 -0
  220. package/templates/large-scene/aura.assets.json +40 -0
  221. package/templates/{three-compat-architecture-interior → large-scene}/package.json +5 -3
  222. package/templates/large-scene/public/aura-assets/kenneyMiniForestRocksHigh.226728a0.glb +0 -0
  223. package/templates/large-scene/public/aura-assets/kenneyMiniForestTree.0075d505.glb +0 -0
  224. package/templates/large-scene/src/aura-assets.ts +28 -0
  225. package/templates/large-scene/src/main.ts +43 -0
  226. package/templates/large-scene/tests/look-floor.ts +270 -0
  227. package/templates/large-scene/tests/route-health.spec.ts +16 -0
  228. package/templates/large-scene/tests/screenshot.spec.ts +21 -0
  229. package/templates/large-scene/vite.config.ts +7 -0
  230. package/templates/material-authoring/.claude/CLAUDE.md +3 -0
  231. package/templates/material-authoring/.github/workflows/aura3d-lookdev.yml +67 -0
  232. package/templates/material-authoring/AGENTS.md +3 -0
  233. package/templates/material-authoring/README.md +15 -0
  234. package/templates/material-authoring/aura.assets.json +24 -0
  235. package/templates/material-authoring/package.json +24 -0
  236. package/templates/material-authoring/public/aura-assets/kenneyCarKitSedanSports.2889c428.glb +0 -0
  237. package/templates/material-authoring/src/aura-assets.ts +16 -0
  238. package/templates/material-authoring/src/main.ts +78 -0
  239. package/templates/material-authoring/tests/look-floor.ts +270 -0
  240. package/templates/material-authoring/tests/route-health.spec.ts +16 -0
  241. package/templates/material-authoring/tests/screenshot.spec.ts +21 -0
  242. package/templates/material-authoring/vite.config.ts +7 -0
  243. package/templates/mini-game/.claude/CLAUDE.md +3 -0
  244. package/templates/mini-game/.github/workflows/aura3d-lookdev.yml +67 -0
  245. package/templates/mini-game/AGENTS.md +3 -0
  246. package/templates/mini-game/README.md +9 -6
  247. package/templates/mini-game/aura.assets.json +177 -36
  248. package/templates/mini-game/package.json +3 -1
  249. package/templates/mini-game/public/aura-assets/kaykit-barbarian.glb +0 -0
  250. package/templates/mini-game/public/aura-assets/kenneyPlatformerKitKey.2e930961.glb +0 -0
  251. package/templates/mini-game/public/aura-assets/kenneyPlatformerKitStar.a102a1b9.glb +0 -0
  252. package/templates/mini-game/public/aura-assets/quaternius-crate.glb +0 -0
  253. package/templates/mini-game/public/aura-assets/quaternius-cube-crate.glb +0 -0
  254. package/templates/mini-game/src/aura-assets.ts +140 -32
  255. package/templates/mini-game/src/main.ts +204 -71
  256. package/templates/mini-game/tests/certified-rig.spec.ts +2 -2
  257. package/templates/mini-game/tests/look-floor.ts +270 -0
  258. package/templates/mini-game/tests/playable.spec.ts +23 -2
  259. package/templates/mini-game/tests/route-health.spec.ts +2 -2
  260. package/templates/mini-game/tests/screenshot.spec.ts +44 -53
  261. package/templates/mini-game/vite.config.ts +7 -0
  262. package/templates/postprocess-scene/.claude/CLAUDE.md +3 -0
  263. package/templates/postprocess-scene/.github/workflows/aura3d-lookdev.yml +67 -0
  264. package/templates/postprocess-scene/AGENTS.md +3 -0
  265. package/templates/postprocess-scene/README.md +17 -0
  266. package/templates/postprocess-scene/aura.assets.json +38 -0
  267. package/templates/postprocess-scene/package.json +24 -0
  268. package/templates/postprocess-scene/public/aura-assets/showcaseLuma.ef389217.glb +0 -0
  269. package/templates/postprocess-scene/public/aura-assets/showcaseMiko.fcdc2b75.glb +0 -0
  270. package/templates/postprocess-scene/src/aura-assets.ts +28 -0
  271. package/templates/postprocess-scene/src/main.ts +29 -0
  272. package/templates/postprocess-scene/tests/look-floor.ts +270 -0
  273. package/templates/postprocess-scene/tests/route-health.spec.ts +16 -0
  274. package/templates/postprocess-scene/tests/screenshot.spec.ts +21 -0
  275. package/templates/postprocess-scene/vite.config.ts +7 -0
  276. package/templates/premium-product-viewer/.claude/CLAUDE.md +3 -0
  277. package/templates/premium-product-viewer/.github/workflows/aura3d-lookdev.yml +67 -0
  278. package/templates/premium-product-viewer/AGENTS.md +3 -0
  279. package/templates/premium-product-viewer/README.md +18 -0
  280. package/templates/premium-product-viewer/aura.assets.json +24 -0
  281. package/templates/premium-product-viewer/package.json +24 -0
  282. package/templates/premium-product-viewer/public/aura-assets/khronosToyCar.01a60862.glb +0 -0
  283. package/templates/premium-product-viewer/src/aura-assets.ts +16 -0
  284. package/templates/premium-product-viewer/src/main.ts +33 -0
  285. package/templates/premium-product-viewer/tests/look-floor.ts +270 -0
  286. package/templates/premium-product-viewer/tests/route-health.spec.ts +16 -0
  287. package/templates/premium-product-viewer/tests/screenshot.spec.ts +21 -0
  288. package/templates/premium-product-viewer/vite.config.ts +7 -0
  289. package/templates/product-viewer/.claude/CLAUDE.md +3 -0
  290. package/templates/product-viewer/.github/workflows/aura3d-lookdev.yml +67 -0
  291. package/templates/product-viewer/AGENTS.md +3 -0
  292. package/templates/product-viewer/README.md +10 -8
  293. package/templates/product-viewer/package.json +3 -1
  294. package/templates/product-viewer/src/aura-assets.ts +1 -1
  295. package/templates/product-viewer/src/main.ts +78 -19
  296. package/templates/product-viewer/tests/look-floor.ts +270 -0
  297. package/templates/product-viewer/tests/route-health.spec.ts +19 -1
  298. package/templates/product-viewer/tests/screenshot.spec.ts +35 -41
  299. package/templates/product-viewer/vite.config.ts +7 -0
  300. package/templates/prompt-animation-channel/.claude/CLAUDE.md +3 -0
  301. package/templates/prompt-animation-channel/.github/workflows/aura3d-lookdev.yml +67 -0
  302. package/templates/prompt-animation-channel/AGENTS.md +3 -0
  303. package/templates/prompt-animation-channel/package.json +3 -2
  304. package/templates/prompt-animation-channel/src/main.ts +5 -3
  305. package/templates/prompt-animation-channel/src/render-plan.ts +5 -3
  306. package/templates/prompt-animation-channel/tests/look-floor.ts +270 -0
  307. package/templates/prompt-animation-channel/tests/route-health.spec.ts +1 -1
  308. package/templates/prompt-animation-channel/tests/screenshot.spec.ts +10 -4
  309. package/templates/prompt-animation-channel/tests/storyboard-playback.spec.ts +1 -1
  310. package/templates/prompt-animation-channel/vite.config.ts +7 -0
  311. package/templates/racing-starter/.claude/CLAUDE.md +3 -0
  312. package/templates/racing-starter/.github/workflows/aura3d-lookdev.yml +67 -0
  313. package/templates/racing-starter/AGENTS.md +3 -0
  314. package/templates/racing-starter/README.md +8 -6
  315. package/templates/racing-starter/package.json +3 -2
  316. package/templates/racing-starter/src/main.ts +168 -146
  317. package/templates/racing-starter/tests/geometry-certification.json +34 -0
  318. package/templates/racing-starter/tests/look-floor.ts +270 -0
  319. package/templates/racing-starter/tests/playable.spec.ts +63 -80
  320. package/templates/racing-starter/tests/route-health.spec.ts +14 -12
  321. package/templates/racing-starter/tests/screenshot.spec.ts +21 -0
  322. package/templates/racing-starter/vite.config.ts +7 -0
  323. package/templates/character-controller/public/aura-assets/showcaseWalkAnimatedGirl.93872fc2.glb +0 -0
  324. package/templates/character-controller/public/aura-assets/showcaseWalkAnimatedGirl.thumb.svg +0 -1
  325. package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.679d52fe.glb +0 -0
  326. package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.thumb.svg +0 -1
  327. package/templates/fighting-game/public/aura-assets/showcaseRunnerRobot.252b3a16.glb +0 -0
  328. package/templates/fighting-game/public/aura-assets/showcaseRunnerRobot.thumb.svg +0 -1
  329. package/templates/fighting-game/public/aura-assets/showcaseWalkAnimatedGirl.93872fc2.glb +0 -0
  330. package/templates/fighting-game/public/aura-assets/showcaseWalkAnimatedGirl.thumb.svg +0 -1
  331. package/templates/mini-game/public/aura-assets/showcaseKenneyOobiPlatformerHero.3f821141.glb +0 -0
  332. package/templates/mini-game/public/aura-assets/showcaseKenneyOobiPlatformerHero.thumb.svg +0 -1
  333. package/templates/three-compat-architecture-interior/aura.assets.json +0 -7
  334. package/templates/three-compat-architecture-interior/src/aura-assets.ts +0 -3
  335. package/templates/three-compat-architecture-interior/src/main.ts +0 -27
  336. package/templates/three-compat-architecture-interior/tests/screenshot.spec.ts +0 -15
  337. package/templates/three-compat-asset-inspector/aura.assets.json +0 -7
  338. package/templates/three-compat-asset-inspector/src/aura-assets.ts +0 -3
  339. package/templates/three-compat-asset-inspector/src/main.ts +0 -17
  340. package/templates/three-compat-asset-inspector/tests/screenshot.spec.ts +0 -15
  341. package/templates/three-compat-character-viewer/aura.assets.json +0 -7
  342. package/templates/three-compat-character-viewer/src/aura-assets.ts +0 -3
  343. package/templates/three-compat-character-viewer/src/main.ts +0 -17
  344. package/templates/three-compat-character-viewer/tests/screenshot.spec.ts +0 -15
  345. package/templates/three-compat-custom-threejs-migration/aura.assets.json +0 -7
  346. package/templates/three-compat-custom-threejs-migration/src/aura-assets.ts +0 -3
  347. package/templates/three-compat-custom-threejs-migration/src/main.ts +0 -16
  348. package/templates/three-compat-custom-threejs-migration/tests/screenshot.spec.ts +0 -15
  349. package/templates/three-compat-large-scene/aura.assets.json +0 -7
  350. package/templates/three-compat-large-scene/package.json +0 -22
  351. package/templates/three-compat-large-scene/src/aura-assets.ts +0 -3
  352. package/templates/three-compat-large-scene/src/main.ts +0 -31
  353. package/templates/three-compat-large-scene/tests/route-health.spec.ts +0 -16
  354. package/templates/three-compat-large-scene/tests/screenshot.spec.ts +0 -15
  355. package/templates/three-compat-material-authoring/aura.assets.json +0 -7
  356. package/templates/three-compat-material-authoring/package.json +0 -22
  357. package/templates/three-compat-material-authoring/src/aura-assets.ts +0 -3
  358. package/templates/three-compat-material-authoring/src/main.ts +0 -33
  359. package/templates/three-compat-material-authoring/tests/route-health.spec.ts +0 -16
  360. package/templates/three-compat-material-authoring/tests/screenshot.spec.ts +0 -15
  361. package/templates/three-compat-postprocess-scene/aura.assets.json +0 -7
  362. package/templates/three-compat-postprocess-scene/package.json +0 -22
  363. package/templates/three-compat-postprocess-scene/src/aura-assets.ts +0 -3
  364. package/templates/three-compat-postprocess-scene/src/main.ts +0 -17
  365. package/templates/three-compat-postprocess-scene/tests/route-health.spec.ts +0 -16
  366. package/templates/three-compat-postprocess-scene/tests/screenshot.spec.ts +0 -15
  367. package/templates/three-compat-premium-product-viewer/aura.assets.json +0 -7
  368. package/templates/three-compat-premium-product-viewer/package.json +0 -22
  369. package/templates/three-compat-premium-product-viewer/src/aura-assets.ts +0 -3
  370. package/templates/three-compat-premium-product-viewer/src/main.ts +0 -18
  371. package/templates/three-compat-premium-product-viewer/tests/route-health.spec.ts +0 -16
  372. package/templates/three-compat-premium-product-viewer/tests/screenshot.spec.ts +0 -15
  373. /package/templates/{three-compat-architecture-interior → architecture-interior}/index.html +0 -0
  374. /package/templates/{three-compat-architecture-interior → architecture-interior}/playwright.config.ts +0 -0
  375. /package/templates/{three-compat-architecture-interior → architecture-interior}/tsconfig.json +0 -0
  376. /package/templates/{three-compat-asset-inspector → asset-inspector}/index.html +0 -0
  377. /package/templates/{three-compat-asset-inspector → asset-inspector}/playwright.config.ts +0 -0
  378. /package/templates/{three-compat-asset-inspector → asset-inspector}/tsconfig.json +0 -0
  379. /package/templates/{three-compat-character-viewer → character-viewer}/index.html +0 -0
  380. /package/templates/{three-compat-character-viewer → character-viewer}/playwright.config.ts +0 -0
  381. /package/templates/{three-compat-character-viewer → character-viewer}/tsconfig.json +0 -0
  382. /package/templates/{three-compat-custom-threejs-migration → custom-scene}/index.html +0 -0
  383. /package/templates/{three-compat-custom-threejs-migration → custom-scene}/playwright.config.ts +0 -0
  384. /package/templates/{three-compat-custom-threejs-migration → custom-scene}/tsconfig.json +0 -0
  385. /package/templates/{three-compat-large-scene → large-scene}/index.html +0 -0
  386. /package/templates/{three-compat-large-scene → large-scene}/playwright.config.ts +0 -0
  387. /package/templates/{three-compat-large-scene → large-scene}/tsconfig.json +0 -0
  388. /package/templates/{three-compat-material-authoring → material-authoring}/index.html +0 -0
  389. /package/templates/{three-compat-material-authoring → material-authoring}/playwright.config.ts +0 -0
  390. /package/templates/{three-compat-material-authoring → material-authoring}/tsconfig.json +0 -0
  391. /package/templates/{three-compat-postprocess-scene → postprocess-scene}/index.html +0 -0
  392. /package/templates/{three-compat-postprocess-scene → postprocess-scene}/playwright.config.ts +0 -0
  393. /package/templates/{three-compat-postprocess-scene → postprocess-scene}/tsconfig.json +0 -0
  394. /package/templates/{three-compat-premium-product-viewer → premium-product-viewer}/index.html +0 -0
  395. /package/templates/{three-compat-premium-product-viewer → premium-product-viewer}/playwright.config.ts +0 -0
  396. /package/templates/{three-compat-premium-product-viewer → premium-product-viewer}/tsconfig.json +0 -0
@@ -0,0 +1,270 @@
1
+ /**
2
+ * PRD-13 T3.12/T3.13 — the shared template look floor.
3
+ *
4
+ * Every create-aura3d template's `tests/screenshot.spec.ts` calls
5
+ * `assertTemplateLookFloor(page, ...)` after readiness: the same five checks
6
+ * everywhere (PRD §6.5/T3.12):
7
+ *
8
+ * 1. non-blank — sampled WebGL pixels are lit and varied (not a uniform
9
+ * clear/void frame);
10
+ * 2. `diagnostics().look.lint` carries no `severity === "error"` finding
11
+ * (empty when A3D_QR_LOOKS is off — the check never silently skips);
12
+ * 3. `appliedLook.environment.specularIntensity > 0` — an environment map
13
+ * actually reached the frame;
14
+ * 4. `appliedLook.shadows.strength >= 0.8` when the look declares a shadow
15
+ * caster (a null ShadowReport means the renderer produced no shadow
16
+ * evidence — recorded, not failed);
17
+ * 5. `appliedLook.pixelRatio >= min(devicePixelRatio, tierCap)` — the
18
+ * renderer used the display density up to the tier cap (2).
19
+ *
20
+ * …plus the optional §T3.12 subject check: the lit-pixel bounding box of the
21
+ * canvas' central region must sit within `tolerance` of the expected subject
22
+ * bounds (default ±10% of frame).
23
+ *
24
+ * Templates that mount through `createAuraApp` surface `appliedLook`/`look`
25
+ * via the global `__AURA3D_LIVE_APPS__` registry — no per-template wiring.
26
+ * `skipAppDiagnostics` covers the bespoke-renderer template (animation-studio),
27
+ * which reports its stage environment through its own readiness evidence.
28
+ */
29
+
30
+ import { expect, type Page } from "@playwright/test";
31
+
32
+ /** Normalized canvas-space bounds the lit subject mass is expected to occupy. */
33
+ export interface LookFloorSubjectBounds {
34
+ readonly x: number;
35
+ readonly y: number;
36
+ readonly width: number;
37
+ readonly height: number;
38
+ }
39
+
40
+ export interface LookFloorOptions {
41
+ /** Expected subject bounds in normalized canvas units. Omit to skip. */
42
+ readonly subject?: LookFloorSubjectBounds;
43
+ /** Fractional tolerance on each bound axis. Default 0.1 (±10%). */
44
+ readonly tolerance?: number;
45
+ /** Template has no `__AURA3D_LIVE_APPS__` app (bespoke renderer). */
46
+ readonly skipAppDiagnostics?: boolean;
47
+ }
48
+
49
+ export interface LookFloorReport {
50
+ readonly sampledPixels: number;
51
+ readonly brightPixels: number;
52
+ readonly uniqueBuckets: number;
53
+ readonly subjectBounds: LookFloorSubjectBounds | null;
54
+ readonly appliedLook: {
55
+ readonly specularIntensity: number;
56
+ readonly shadowStrength: number | null;
57
+ readonly pixelRatio: number;
58
+ readonly renderPath: string;
59
+ readonly background: string | null;
60
+ } | null;
61
+ readonly lookLintErrors: readonly string[];
62
+ readonly devicePixelRatio: number;
63
+ }
64
+
65
+ const MIN_BRIGHT_FRACTION = 0.02;
66
+ const MIN_UNIQUE_BUCKETS = 8;
67
+
68
+ export async function assertTemplateLookFloor(page: Page, options: LookFloorOptions = {}): Promise<LookFloorReport> {
69
+ const canvas = page.locator("canvas").first();
70
+ await expect(canvas).toBeVisible();
71
+
72
+ /*
73
+ * Deterministic frame before profiling: pause every live app, rewind its
74
+ * runtime clock and step a fixed 45 frames at 1/60 — through `stepAsync`,
75
+ * not the synchronous registry `settle()`. The production renderer mounts
76
+ * asynchronously, so a sync step loop renders nothing while the mount is in
77
+ * flight and never yields for it to finish (the failure mode on software-GL
78
+ * runners: blank frames, `environment.intensity` still NaN in diagnostics).
79
+ * Awaiting each step also lets pending environment/IBL/asset promises resolve
80
+ * between frames, and one final dt=0 step re-renders the same simulated
81
+ * instant once that work has applied.
82
+ */
83
+ const report = await page.evaluate(async (): Promise<LookFloorReport> => {
84
+ interface LiveApp {
85
+ pause?: () => void;
86
+ resetRuntimeClock?: () => void;
87
+ step?: (dt?: number) => void;
88
+ stepAsync?: (dt?: number) => Promise<unknown>;
89
+ diagnostics?: () => Record<string, unknown>;
90
+ }
91
+ const empty = (code: string): LookFloorReport => ({
92
+ sampledPixels: 0,
93
+ brightPixels: 0,
94
+ uniqueBuckets: 0,
95
+ subjectBounds: null,
96
+ appliedLook: null,
97
+ lookLintErrors: [code],
98
+ devicePixelRatio: globalThis.devicePixelRatio ?? 1
99
+ });
100
+ const registry = (globalThis as { __AURA3D_LIVE_APPS__?: { all?: () => readonly LiveApp[] } }).__AURA3D_LIVE_APPS__;
101
+ const apps = [...(registry?.all?.() ?? [])];
102
+ const stepApp = async (app: LiveApp, dt: number): Promise<void> => {
103
+ if (app.stepAsync) {
104
+ try {
105
+ await Promise.race([
106
+ app.stepAsync(dt),
107
+ new Promise((resolvePromise) => setTimeout(resolvePromise, 30_000))
108
+ ]);
109
+ return;
110
+ } catch {
111
+ // synchronous step fallback below
112
+ }
113
+ }
114
+ app.step?.(dt);
115
+ };
116
+ /*
117
+ * Wall-clock bound on the settle loop: on software-GL CI runners a single
118
+ * stepped render of a heavy scene costs several seconds, so an unbounded
119
+ * 45-step loop outlives the test budget and fails with a bare timeout and
120
+ * no report. Cap the stepping and always take the dt=0 settle frame — a
121
+ * partially settled scene still yields a readable floor report.
122
+ */
123
+ const STEP_WALLCLOCK_BUDGET_MS = 420_000;
124
+ const settleStartedAt = Date.now();
125
+ for (const app of apps) {
126
+ app.pause?.();
127
+ app.resetRuntimeClock?.();
128
+ for (let index = 0; index < 45; index += 1) {
129
+ if (Date.now() - settleStartedAt > STEP_WALLCLOCK_BUDGET_MS) break;
130
+ await stepApp(app, 1 / 60);
131
+ }
132
+ await stepApp(app, 0);
133
+ }
134
+ /*
135
+ * Renderers outside the liveApps registry (bespoke renderers, e.g.
136
+ * animation-studio's A3DRenderer) cannot be stepped. Two animation
137
+ * frames guarantee at least one full app render finished inside this
138
+ * evaluate task, so the readPixels below sees a real presented frame
139
+ * instead of a compositor-cleared buffer.
140
+ */
141
+ if (apps.length === 0) {
142
+ await new Promise<void>((resolve) => {
143
+ requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
144
+ });
145
+ }
146
+
147
+ const diagnostics = apps[0]?.diagnostics?.() as
148
+ | { appliedLook?: { environment?: { specularIntensity?: number; background?: string }; shadows?: { strength?: number | null } | null; pixelRatio?: number; renderPath?: string }; look?: { lint?: readonly { severity?: string; code?: string }[] } }
149
+ | undefined;
150
+ const appliedLook = diagnostics?.appliedLook
151
+ ? {
152
+ specularIntensity: diagnostics.appliedLook.environment?.specularIntensity ?? 0,
153
+ shadowStrength: diagnostics.appliedLook.shadows?.strength ?? null,
154
+ pixelRatio: diagnostics.appliedLook.pixelRatio ?? 0,
155
+ renderPath: diagnostics.appliedLook.renderPath ?? "unknown",
156
+ background: diagnostics.appliedLook.environment?.background ?? null
157
+ }
158
+ : null;
159
+ const lookLintErrors = (diagnostics?.look?.lint ?? [])
160
+ .filter((finding) => finding.severity === "error")
161
+ .map((finding) => finding.code ?? "unknown");
162
+ const devicePixelRatio = globalThis.devicePixelRatio ?? 1;
163
+
164
+ /*
165
+ * The app's WebGL context is not preserveDrawingBuffer, so readPixels must
166
+ * run in the same task as the stepped render, before compositing clears the
167
+ * drawing buffer. That also removes the composited element screenshot that
168
+ * software-GL runners could not produce inside a test timeout.
169
+ */
170
+ const target = [...document.querySelectorAll("canvas")]
171
+ .map((element) => ({
172
+ element,
173
+ gl: (element.getContext("webgl2") ?? element.getContext("webgl")) as WebGL2RenderingContext | WebGLRenderingContext | null
174
+ }))
175
+ .find((entry) => entry.gl !== null);
176
+ if (!target?.gl) return { ...empty("no-webgl-context"), appliedLook, lookLintErrors };
177
+ const gl = target.gl;
178
+ const width = gl.drawingBufferWidth;
179
+ const height = gl.drawingBufferHeight;
180
+ if (width === 0 || height === 0) return { ...empty("empty-drawing-buffer"), appliedLook, lookLintErrors };
181
+ // readPixels reads the currently bound framebuffer: renderers that end a
182
+ // frame with an offscreen target bound (shadow/post FBOs — e.g.
183
+ // animation-studio's bespoke renderer) would return that target's pixels
184
+ // instead of the canvas backbuffer. Rebind the default framebuffer first;
185
+ // every renderer rebinds its own targets at the start of its next frame.
186
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
187
+ const pixels = new Uint8Array(width * height * 4);
188
+ gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
189
+
190
+ const BRIGHT_LUMA = 48;
191
+ const stride = 4; // pixels — 16×16 sample grid
192
+ let sampled = 0;
193
+ let bright = 0;
194
+ const buckets = new Set<number>();
195
+ // Central-region lit-pixel bbox: edges carry floor/backdrop, the subject
196
+ // mass is what every §6.4 template frames mid-frame.
197
+ const x0 = Math.floor(width * 0.1);
198
+ const x1 = Math.floor(width * 0.9);
199
+ const y0 = Math.floor(height * 0.1);
200
+ const y1 = Math.floor(height * 0.9);
201
+ let minX = Number.POSITIVE_INFINITY;
202
+ let maxX = Number.NEGATIVE_INFINITY;
203
+ let minY = Number.POSITIVE_INFINITY;
204
+ let maxY = Number.NEGATIVE_INFINITY;
205
+ for (let y = 0; y < height; y += stride) {
206
+ for (let x = 0; x < width; x += stride) {
207
+ const offset = (y * width + x) * 4;
208
+ const r = pixels[offset] ?? 0;
209
+ const g = pixels[offset + 1] ?? 0;
210
+ const b = pixels[offset + 2] ?? 0;
211
+ sampled += 1;
212
+ buckets.add(((r >> 4) << 8) | ((g >> 4) << 4) | (b >> 4));
213
+ const luma = 0.2126 * r + 0.7152 * g + 0.0722 * b;
214
+ if (luma > BRIGHT_LUMA) {
215
+ bright += 1;
216
+ if (x >= x0 && x <= x1 && y >= y0 && y <= y1) {
217
+ minX = Math.min(minX, x);
218
+ maxX = Math.max(maxX, x);
219
+ minY = Math.min(minY, y);
220
+ maxY = Math.max(maxY, y);
221
+ }
222
+ }
223
+ }
224
+ }
225
+ // readPixels rows run bottom-up; report `y` in the same top-down
226
+ // convention the composited-screenshot path produced so stored
227
+ // expectations stay comparable across the switch.
228
+ const subjectBounds = Number.isFinite(minX)
229
+ ? { x: minX / width, y: 1 - maxY / height, width: (maxX - minX) / width, height: (maxY - minY) / height }
230
+ : null;
231
+
232
+ return {
233
+ sampledPixels: sampled,
234
+ brightPixels: bright,
235
+ uniqueBuckets: buckets.size,
236
+ subjectBounds,
237
+ appliedLook,
238
+ lookLintErrors,
239
+ devicePixelRatio
240
+ };
241
+ });
242
+
243
+ // 1. Non-blank.
244
+ expect(report.brightPixels / Math.max(1, report.sampledPixels)).toBeGreaterThan(MIN_BRIGHT_FRACTION);
245
+ expect(report.uniqueBuckets).toBeGreaterThan(MIN_UNIQUE_BUCKETS);
246
+
247
+ if (!options.skipAppDiagnostics) {
248
+ // 2–5. Renderer/look evidence via __AURA3D_LIVE_APPS__.
249
+ expect(report.appliedLook, "appliedLook diagnostics missing — is the app mounted via createAuraApp?").not.toBeNull();
250
+ expect(report.lookLintErrors).toEqual([]);
251
+ expect(report.appliedLook!.specularIntensity).toBeGreaterThan(0);
252
+ if (report.appliedLook!.shadowStrength !== null) {
253
+ expect(report.appliedLook!.shadowStrength!).toBeGreaterThanOrEqual(0.8);
254
+ }
255
+ expect(report.appliedLook!.pixelRatio).toBeGreaterThanOrEqual(Math.min(report.devicePixelRatio, 2));
256
+ }
257
+
258
+ // 6. Subject bounds ±10%.
259
+ if (options.subject) {
260
+ const tolerance = options.tolerance ?? 0.1;
261
+ const measured = `measured ${JSON.stringify(report.subjectBounds)}`;
262
+ expect(report.subjectBounds, "no lit subject mass in the central frame").not.toBeNull();
263
+ expect(Math.abs(report.subjectBounds!.x - options.subject.x), measured).toBeLessThanOrEqual(tolerance);
264
+ expect(Math.abs(report.subjectBounds!.y - options.subject.y), measured).toBeLessThanOrEqual(tolerance);
265
+ expect(Math.abs(report.subjectBounds!.width - options.subject.width), measured).toBeLessThanOrEqual(tolerance);
266
+ expect(Math.abs(report.subjectBounds!.height - options.subject.height), measured).toBeLessThanOrEqual(tolerance);
267
+ }
268
+
269
+ return report;
270
+ }
@@ -3,7 +3,7 @@ import { resolve } from "node:path";
3
3
  import { expect, test } from "@playwright/test";
4
4
 
5
5
  test("animation channel route loads", async ({ page }) => {
6
- test.setTimeout(90_000);
6
+ test.setTimeout(240_000);
7
7
  await page.goto("/");
8
8
  await expect.poll(() => page.locator("body").getAttribute("data-aura3d-ready"), { timeout: 60_000 }).toBe("true");
9
9
  await expect(page.locator("#app")).toBeVisible();
@@ -1,16 +1,21 @@
1
1
  import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { expect, test } from "@playwright/test";
4
+ import { assertTemplateLookFloor } from "./look-floor";
4
5
 
5
- test.setTimeout(60_000);
6
+ test.setTimeout(600_000);
6
7
 
7
- test("Aura3D animation channel screenshot shows content", async ({ page }) => {
8
+ // PRD-13 T3.12 — look floor: non-blank, look.lint error-free, appliedLook
9
+ // environment specular > 0, shadow strength >= 0.8, pixelRatio at tier cap,
10
+ // subject bounds within ±10% — plus this template's bespoke assertions.
11
+ test("Aura3D animation channel screenshot clears the look floor", async ({ page }) => {
8
12
  await page.goto("/");
9
- await expect.poll(() => page.locator("body").getAttribute("data-aura3d-ready"), { timeout: 45_000 }).toBe("true");
10
- const canvas = page.locator("canvas");
13
+ await expect.poll(() => page.locator("body").getAttribute("data-aura3d-ready"), { timeout: 150_000 }).toBe("true");
14
+
15
+ const floor = await assertTemplateLookFloor(page, { subject: { x: 0.15, y: 0.3, width: 0.7, height: 0.55 } });
11
16
  const screenshot = await page.screenshot({ fullPage: false });
12
17
  mkdirSync(resolve("tests/reports"), { recursive: true });
13
18
  writeFileSync(resolve("tests/reports/screenshot.png"), screenshot);
14
- writeFileSync(resolve("tests/reports/screenshot.json"), `${JSON.stringify({ bytes: screenshot.byteLength }, null, 2)}\n`);
19
+ writeFileSync(resolve("tests/reports/screenshot.json"), `${JSON.stringify({ bytes: screenshot.byteLength, floor }, null, 2)}\n`);
15
20
  expect(screenshot.byteLength).toBeGreaterThan(1000);
16
21
  });
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "vite";
2
+ import { auraDecodersPlugin } from "@aura3d/assets/vite";
3
+
4
+ // F-05-04 (05-PKG): ship + serve the decoder vendor tree at /aura-decoders/
5
+ // so compressed (KTX2/draco/meshopt) GLBs decode same-origin with correct
6
+ // MIME in both `vite dev` and `vite preview`/deployed builds.
7
+ export default defineConfig({ plugins: [auraDecodersPlugin()] });
@@ -0,0 +1,3 @@
1
+ # Aura3D agent instructions
2
+
3
+ Read ./llms.txt first. Read the `aura3d-art-direction` skill. Every scene gets a look. Run the look-dev loop (`aura3d look capture` → judge → iterate) before calling a scene done.
@@ -0,0 +1,67 @@
1
+ name: Aura3D look-dev
2
+
3
+ # Captures this scene's screenshot tests on an Apple-silicon GPU runner
4
+ # (ANGLE Metal) and uploads the report as the `lookdev` artifact.
5
+ # `aura3d look capture --runner gh-actions` dispatches this workflow and
6
+ # downloads the artifacts; it can also be run manually from the Actions tab.
7
+ # No secrets are used; workflow_dispatch only — never runs forked code.
8
+
9
+ on:
10
+ workflow_dispatch:
11
+ inputs:
12
+ shots:
13
+ description: "Shot names to capture (comma-separated; the template's defaults when empty)"
14
+ required: false
15
+ default: ""
16
+ viewports:
17
+ description: "Viewport names (comma-separated; the template's defaults when empty)"
18
+ required: false
19
+ default: ""
20
+
21
+ permissions:
22
+ contents: read
23
+
24
+ jobs:
25
+ capture:
26
+ name: Look-dev captures (macos-14, ANGLE Metal)
27
+ runs-on: macos-14
28
+ timeout-minutes: 45
29
+ env:
30
+ QRC_GPU_ARGS: "--use-angle=metal --enable-gpu --ignore-gpu-blocklist"
31
+ A3D_LOOKDEV_SHOTS: ${{ inputs.shots }}
32
+ A3D_LOOKDEV_VIEWPORTS: ${{ inputs.viewports }}
33
+ steps:
34
+ - name: Checkout
35
+ uses: actions/checkout@v4
36
+ with:
37
+ lfs: false
38
+
39
+ - name: Setup Node.js
40
+ uses: actions/setup-node@v4
41
+ with:
42
+ node-version: '22'
43
+ cache: 'npm'
44
+
45
+ - name: Install dependencies
46
+ run: npm ci
47
+
48
+ - name: Build the app
49
+ run: npm run build
50
+
51
+ - name: Install Playwright Chromium
52
+ run: npx playwright install chromium
53
+
54
+ - name: Capture screenshots
55
+ run: npx playwright test tests/screenshot.spec.ts --reporter=line
56
+ env:
57
+ CHROMIUM_FLAGS: ${{ env.QRC_GPU_ARGS }}
58
+
59
+ - name: Upload look-dev artifacts
60
+ if: always()
61
+ uses: actions/upload-artifact@v4
62
+ with:
63
+ name: lookdev
64
+ path: |
65
+ tests/reports/**
66
+ if-no-files-found: warn
67
+ retention-days: 30
@@ -1,71 +1,3 @@
1
- # Animation Studio — you are the director
1
+ # Aura3D agent instructions
2
2
 
3
- **You** (Claude Code, Codex, Cursor, the API, whatever coding agent the user is already
4
- running) are the director. There is **no separate AI model and no API key** wired into this
5
- studio. The user gives a prompt; **you** turn it into a finished scene by driving the
6
- Scene-Tool CLI. You generate everything — the cast, the script/subtitles, the staging, the
7
- performance. The CLI is your hands; the renderer is deterministic.
8
-
9
- Every command edits one working document (`dist/scene/working.document.json`), is **validated**
10
- before it commits, and is **rejected** if it would break the scene (off-set, missing clip,
11
- bad framing). You cannot produce a broken document.
12
-
13
- ## The loop (run these as shell commands)
14
-
15
- ```bash
16
- S="pnpm exec tsx --tsconfig tsconfig.base.json packages/create-aura3d/templates/animation-studio/scripts/animation-scene.ts"
17
- # or, from an installed project: aura3d animation scene <args...>
18
- ```
19
-
20
- 1. **Start a skeleton from the prompt** (picks a SET template + shots + timeline; NO dialogue —
21
- you write that):
22
- ```bash
23
- $S new --prompt "two robots argue on a space station"
24
- ```
25
-
26
- 2. **Cast real characters** — resolve them from the live asset catalog by description. Use
27
- DISTINCT queries so they look and move differently (each GLB brings its own clips):
28
- ```bash
29
- $S cast add --id rusty --query "rusty industrial robot"
30
- $S cast add --id sleek --query "sleek white android robot"
31
- ```
32
- Provenance/license/scale are recorded automatically. `--scale N` overrides auto-fit.
33
-
34
- 3. **Write the script** — this is the story AND the subtitles AND the AuraVoice lip-sync
35
- contract, all one timed track. Author every line yourself; make it an actual arc:
36
- ```bash
37
- $S dialogue --line l1 --speaker rusty --text "You rerouted the coolant without telling me." --start 1 --end 6
38
- $S dialogue --line l2 --speaker sleek --text "Because you'd have said no. The core was overheating." --start 7 --end 13
39
- $S dialogue --line l3 --speaker rusty --text "That was MY system to balance." --start 21 --end 27
40
- # …carry the argument to a turn and a resolution across the shots.
41
- ```
42
-
43
- 4. **Direct distinct performance per beat** — make the two characters DO different things so
44
- it reads as an argument, not two idle loops. Move one in, push one back, face them off,
45
- assign gestures. Motion is bounded by each GLB's clips — pick the clip that fits the beat:
46
- ```bash
47
- $S block --character rusty --shot shot-1 --to -1.2,0 --yaw 1.2 --clip walk
48
- $S block --character sleek --shot shot-1 --to 1.2,0 --yaw -1.2
49
- $S gesture --character rusty --shot shot-3 --clip <a-clip-the-GLB-has>
50
- $S shot retime --id shot-2 --duration 24 # give the escalation room
51
- ```
52
- Use `$S show` to see the cast's real clip names before assigning them.
53
-
54
- 5. **Render** and review:
55
- ```bash
56
- $S render
57
- ```
58
-
59
- ## Honest boundaries (do not oversell to the user)
60
-
61
- - **You write the story.** The skeleton has no dialogue on purpose. If you skip step 3 there
62
- are no captions — that is correct, not a bug.
63
- - **Performance is limited by the asset's clips.** You can stage position, facing, camera, who
64
- moves, and which existing clip plays — but you cannot synthesize motion a GLB doesn't contain.
65
- If both characters look the same, you cast look-alike GLBs or left them on one clip; fix it by
66
- casting distinct characters (step 2) and assigning per-beat clips/blocking (step 4).
67
- - **Subtitles ≠ voice.** Aura3D never does TTS. The dialogue track you write is the timed
68
- contract AuraVoice consumes later to generate the voice locked to this timeline.
69
-
70
- The user's measure is "is this a watchable animation with a real story." That is **your** job as
71
- director — the tools guarantee a *valid* document, not a *good* one. Make it good.
3
+ Read ./llms.txt first. Read the `aura3d-art-direction` skill. Every scene gets a look. Run the look-dev loop (`aura3d look capture` → judge → iterate) before calling a scene done.
@@ -16,9 +16,10 @@
16
16
  "preview": "vite preview --host 0.0.0.0"
17
17
  },
18
18
  "dependencies": {
19
- "@aura3d/animation": "3.0.1",
20
- "@aura3d/engine": "3.0.1",
21
- "@aura3d/navigation-recast": "3.0.1",
19
+ "@aura3d/animation": "4.0.0",
20
+ "@aura3d/assets": "4.0.0",
21
+ "@aura3d/engine": "4.0.0",
22
+ "@aura3d/navigation-recast": "4.0.0",
22
23
  "react": "^18.3.1",
23
24
  "react-dom": "^18.3.1",
24
25
  "sharp": "^0.33.5"
@@ -31,6 +31,7 @@ import { createHash } from "node:crypto";
31
31
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
32
32
  import { dirname, resolve } from "node:path";
33
33
  import { fileURLToPath } from "node:url";
34
+ import { parseGlbDocument } from "@aura3d/assets";
34
35
  import {
35
36
  AnimationClipRegistry,
36
37
  AnimationTrack,
@@ -137,13 +138,13 @@ interface GltfAnimation {
137
138
  }
138
139
 
139
140
  function parseGlb(buf: Buffer): ParsedGlb | null {
140
- if (buf.length < 20 || buf.readUInt32LE(0) !== 0x46546c67) return null; // "glTF"
141
- const jsonLen = buf.readUInt32LE(12);
142
- const jsonStart = 20;
143
- const json = JSON.parse(buf.slice(jsonStart, jsonStart + jsonLen).toString("utf8")) as GltfJson;
141
+ if (buf.length < 20 || buf.readUInt32LE(0) !== 0x46546c67) return null; // not "glTF" — skip
142
+ // Malformed GLBs past the magic throw from the shared parser, as the local JSON.parse did.
143
+ const doc = parseGlbDocument(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer, "clip-library://parse");
144
+ const json = doc.json as GltfJson;
144
145
  // BIN chunk follows the JSON chunk (8-byte chunk header: length + type 0x004E4942).
145
146
  let bin = Buffer.alloc(0);
146
- const binHeader = jsonStart + jsonLen;
147
+ const binHeader = 20 + doc.jsonChunkBytes;
147
148
  if (binHeader + 8 <= buf.length && buf.readUInt32LE(binHeader + 4) === 0x004e4942) {
148
149
  const binLen = buf.readUInt32LE(binHeader);
149
150
  bin = buf.slice(binHeader + 8, binHeader + 8 + binLen);
@@ -11,6 +11,7 @@ import { createHash } from "node:crypto";
11
11
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
12
12
  import { dirname, resolve } from "node:path";
13
13
  import { fileURLToPath } from "node:url";
14
+ import { parseGlbDocument } from "@aura3d/assets";
14
15
  import { ProbeSession } from "./asset-render-probe.js";
15
16
  import { probeEmbeddedMotion, type ClipMotionScore, type MotionProbeReport } from "./asset-motion-probe.js";
16
17
 
@@ -185,9 +186,11 @@ export function combineFidelity(rig: RigGrade, mesh: MeshGrade): RigGrade {
185
186
  const HUMANOID_BONE_HINTS = ["hips", "spine", "neck", "head", "shoulder", "arm", "forearm", "hand", "upleg", "leg", "foot", "thigh", "calf", "pelvis", "clavicle"];
186
187
 
187
188
  function inspectGlb(buf: Buffer): GlbInfo | null {
188
- if (buf.readUInt32LE(0) !== 0x46546c67) return null;
189
- const jlen = buf.readUInt32LE(12);
190
- const json = JSON.parse(buf.slice(20, 20 + jlen).toString("utf8")) as Record<string, any>;
189
+ if (buf.length < 20 || buf.readUInt32LE(0) !== 0x46546c67) return null; // not "glTF"
190
+ // Malformed GLBs past the magic throw from the shared parser, as the local JSON.parse did.
191
+ const doc = parseGlbDocument(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer, "resolve-asset://inspect");
192
+ const json = doc.json as Record<string, any>;
193
+ const jsonChunkBytes = doc.jsonChunkBytes;
191
194
  const prims = (json.meshes ?? []).flatMap((m: any) => m.primitives ?? []);
192
195
  const imgs = json.images ?? [];
193
196
  const min = [1e9, 1e9, 1e9];
@@ -203,7 +206,7 @@ function inspectGlb(buf: Buffer): GlbInfo | null {
203
206
  }
204
207
  // ── Texture resolution: parse PNG/JPEG headers embedded in the BIN to read pixel dimensions.
205
208
  // glTF images don't carry width/height, so we sniff the actual encoded bytes. Largest wins.
206
- const binStart = 20 + jlen + 8; // glb header(12) + json chunk header(8) + json + bin chunk header(8)
209
+ const binStart = 20 + jsonChunkBytes + 8; // glb header(12) + json chunk header(8) + json + bin chunk header(8)
207
210
  const views = (json.bufferViews ?? []) as any[];
208
211
  let textureMaxDim = 0;
209
212
  for (const img of imgs) {
@@ -9,15 +9,15 @@
9
9
  * Dialogue/caption/viseme still come from the episode (`render-plan.ts`) — that timed
10
10
  * track is the AuraVoice contract and is shared across the pipeline.
11
11
  *
12
- * Imports resolve from the published `@aura3d/engine` (advanced-runtime A3DRenderer +
12
+ * Imports resolve from the published `@aura3d/engine` (renderer-subpath Renderer +
13
13
  * production-runtime typed GLB actors + rendering primitives), exactly as the old route.
14
14
  */
15
15
 
16
- import { A3DRenderer, TextureBinding as EngineTextureBinding } from "@aura3d/engine/advanced-runtime";
16
+ import { Renderer, TextureBinding as EngineTextureBinding } from "@aura3d/engine/renderer";
17
17
  import { createPerformanceGovernor, createSideViewGameRenderPreset, createStudioLighting, createTypedGLBActor, type TypedGLBActor } from "@aura3d/engine/production-runtime";
18
18
  import { createCameraPathFromPreset, sampleCameraPath, type CameraPath } from "@aura3d/engine";
19
19
  // I1 clean-room correctness: import the rendering/scene PRIMITIVES from the engine's own subpaths
20
- // (`@aura3d/engine/rendering` / `@aura3d/engine/scene`) — the SAME copy the A3DRenderer uses — NOT
20
+ // (`@aura3d/engine/rendering` / `@aura3d/engine/scene`) — the SAME copy the Renderer uses — NOT
21
21
  // the standalone `@aura3d/rendering` / `@aura3d/scene` packages. In a clean-room (tarball) install
22
22
  // those standalone packages are a SECOND copy of the rendering code, so a `Geometry`/`PBRMaterial`/
23
23
  // `TextureBinding` built from them is a DIFFERENT class than the renderer's, and the renderer's
@@ -537,7 +537,7 @@ export async function mountScenePlayer(doc: EpisodeDocument): Promise<void> {
537
537
  );
538
538
 
539
539
  // 7. Renderer + render source.
540
- const renderer = await A3DRenderer.create({
540
+ const renderer = await Renderer.create({
541
541
  canvas,
542
542
  width,
543
543
  height,
@@ -569,7 +569,7 @@ export async function mountScenePlayer(doc: EpisodeDocument): Promise<void> {
569
569
  // mip chain, and its BRDF-LUT — is the SAME class and the renderer's `MaterialBinding.bind`
570
570
  // (`value instanceof TextureBinding`, see rendering/MaterialBinding.ts) accepts them. In a CLEAN-ROOM
571
571
  // install from tarballs the engine resolves into SEVERAL Vite-prebundled chunks: the app's
572
- // `@aura3d/rendering` (which builds our env map) and `@aura3d/engine/advanced-runtime` (the renderer,
572
+ // `@aura3d/rendering` (which builds our env map) and `@aura3d/engine/renderer` (the renderer,
573
573
  // which builds the internal env-map/BRDF-LUT bindings) get DIFFERENT `TextureBinding` class objects,
574
574
  // so the `instanceof` check fails and the renderer throws `MaterialBindingError`
575
575
  // ("u_environmentMapTexture must be texture2d" / "Unsupported uniform value"). This is unfixable from
@@ -579,7 +579,7 @@ export async function mountScenePlayer(doc: EpisodeDocument): Promise<void> {
579
579
  //
580
580
  // The reliable, NON-POISONING signal is exactly that class-identity divergence: if our baked binding
581
581
  // (from `@aura3d/rendering`) is NOT `instanceof` the engine renderer's own `TextureBinding` (from
582
- // `@aura3d/engine/advanced-runtime`), the engine is multi-chunk and its internal env bindings will be
582
+ // `@aura3d/engine/renderer`), the engine is multi-chunk and its internal env bindings will be
583
583
  // rejected too → skip IBL. When they ARE the same class (the monorepo / a correctly-deduped install)
584
584
  // IBL stays on, so this never changes the monorepo render's success.
585
585
  let iblBinding:
@@ -170,15 +170,18 @@ const MEADOW: SetSpec = {
170
170
  // themed keyword. This guarantees a non-matching prompt never silently inherits the
171
171
  // Moon Garden look; it gets a plain studio set instead.
172
172
  // ---------------------------------------------------------------------------
173
+ // PRD-13 T3.10: the default stage adopts the `interior-warm` look's environment
174
+ // and key (warm key light, warm softbox IBL, warm-grade background) instead of a
175
+ // neutral grey rig. Episode tooling beyond this stage setup is out of scope.
173
176
  const STUDIO: SetSpec = {
174
- clearColor: [0.06, 0.07, 0.09, 1],
177
+ clearColor: [0.11, 0.09, 0.07, 1], // interior-warm v0 background (#1c1712)
175
178
  studioLightingScale: 0.5,
176
179
  environment: {
177
- color: [0.5, 0.52, 0.55],
178
- intensity: 0.4,
179
- proceduralMap: { skyColor: [0.18, 0.19, 0.21], horizonColor: [0.24, 0.25, 0.27], groundColor: [0.08, 0.08, 0.09], specularColor: [0.8, 0.82, 0.85], intensity: 0.4, specularIntensity: 0.5 },
180
- // Neutral grey studio softbox IBL — even overhead key, no coloured cast (a clean cyc look).
181
- hdri: { skyColor: [0.7, 0.71, 0.74], horizonColor: [0.34, 0.35, 0.38], groundColor: [0.12, 0.12, 0.14], sun: { color: [1, 1, 1], intensity: 1.4, azimuth: -0.3, elevation: 1.1, angularRadius: 0.22 }, intensity: 0.6, specularIntensity: 0.55 }
180
+ color: [0.55, 0.49, 0.42],
181
+ intensity: 0.45,
182
+ proceduralMap: { skyColor: [0.24, 0.2, 0.16], horizonColor: [0.3, 0.25, 0.2], groundColor: [0.1, 0.09, 0.08], specularColor: [0.9, 0.82, 0.72], intensity: 0.45, specularIntensity: 0.6 },
183
+ // Warm studio softbox IBL — interior-warm key (#ffe3c2) as the sun source.
184
+ hdri: { skyColor: [0.62, 0.55, 0.46], horizonColor: [0.38, 0.32, 0.26], groundColor: [0.14, 0.11, 0.09], sun: { color: [1, 0.89, 0.76], intensity: 1.9, azimuth: 0.87, elevation: 0.44, angularRadius: 0.22 }, intensity: 0.65, specularIntensity: 0.6 }
182
185
  },
183
186
  pieces: [
184
187
  // A FAR backdrop wall placed well behind the stage (not a giant enclosing dome): it reads as an
@@ -191,9 +194,10 @@ const STUDIO: SetSpec = {
191
194
  { id: "pillar-r", geometry: "cube", position: [3.4, 1.1, -2.6], scale: [0.42, 2.2, 0.42], baseColor: [0.16, 0.17, 0.2, 1], metallic: 0.3, roughness: 0.6, emissiveColor: [0.05, 0.05, 0.06], emissiveStrength: 0.12 }
192
195
  ],
193
196
  lights: [
194
- { id: "key", kind: "point", color: [1, 0.97, 0.9], position: [-2.6, 3.8, 3.0], intensity: 4.4, range: 20 },
195
- { id: "fill", kind: "point", color: [0.82, 0.84, 0.92], position: [2.8, 2.2, 1.6], intensity: 2.2, range: 14 },
196
- { id: "rim", kind: "point", color: [0.9, 0.9, 1], position: [0.3, 2.2, -2.4], intensity: 2.4, range: 13 }
197
+ // interior-warm key/rim pair: warm key + warm accent rim (accent #e0a45c).
198
+ { id: "key", kind: "point", color: [1, 0.89, 0.76], position: [-2.6, 3.8, 3.0], intensity: 4.2, range: 20 },
199
+ { id: "fill", kind: "point", color: [0.85, 0.8, 0.72], position: [2.8, 2.2, 1.6], intensity: 2.0, range: 14 },
200
+ { id: "rim", kind: "point", color: [0.88, 0.64, 0.36], position: [0.3, 2.2, -2.4], intensity: 2.6, range: 13 }
197
201
  ]
198
202
  };
199
203