@aura3d/engine 1.0.2 → 1.0.5

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 (296) hide show
  1. package/README.md +331 -16
  2. package/dist/animation/AnimationClipEvents.d.ts +57 -0
  3. package/dist/animation/AnimationClipEvents.d.ts.map +1 -0
  4. package/dist/animation/AnimationClipEvents.js +171 -0
  5. package/dist/animation/AnimationClipEvents.js.map +1 -0
  6. package/dist/animation/AnimationClipRegistry.d.ts +76 -0
  7. package/dist/animation/AnimationClipRegistry.d.ts.map +1 -0
  8. package/dist/animation/AnimationClipRegistry.js +130 -0
  9. package/dist/animation/AnimationClipRegistry.js.map +1 -0
  10. package/dist/animation/AnimationController.d.ts +168 -0
  11. package/dist/animation/AnimationController.d.ts.map +1 -0
  12. package/dist/animation/AnimationController.js +619 -0
  13. package/dist/animation/AnimationController.js.map +1 -0
  14. package/dist/animation/HumanoidRetargeting.d.ts +76 -0
  15. package/dist/animation/HumanoidRetargeting.d.ts.map +1 -0
  16. package/dist/animation/HumanoidRetargeting.js +331 -0
  17. package/dist/animation/HumanoidRetargeting.js.map +1 -0
  18. package/dist/animation/browser-index.d.ts +18 -0
  19. package/dist/animation/browser-index.d.ts.map +1 -1
  20. package/dist/animation/browser-index.js +13 -0
  21. package/dist/animation/browser-index.js.map +1 -1
  22. package/dist/animation/index.d.ts +16 -1
  23. package/dist/animation/index.d.ts.map +1 -1
  24. package/dist/animation/index.js +11 -1
  25. package/dist/animation/index.js.map +1 -1
  26. package/dist/animation/threejs-compatibility/AnimationDiagnostics.d.ts.map +1 -1
  27. package/dist/animation/threejs-compatibility/AnimationDiagnostics.js +3 -5
  28. package/dist/animation/threejs-compatibility/AnimationDiagnostics.js.map +1 -1
  29. package/dist/assets/GLTFAnimationRuntime.js +1 -1
  30. package/dist/assets/GLTFLoader.js +1 -1
  31. package/dist/aura3d-cli/cli.js +194 -8
  32. package/dist/aura3d-cli/cli.js.map +1 -1
  33. package/dist/aura3d-cli/index.d.ts +280 -3
  34. package/dist/aura3d-cli/index.d.ts.map +1 -1
  35. package/dist/aura3d-cli/index.js +886 -4
  36. package/dist/aura3d-cli/index.js.map +1 -1
  37. package/dist/aura3d-cli/pull-bridge.d.ts +95 -0
  38. package/dist/aura3d-cli/pull-bridge.d.ts.map +1 -0
  39. package/dist/aura3d-cli/pull-bridge.js +247 -0
  40. package/dist/aura3d-cli/pull-bridge.js.map +1 -0
  41. package/dist/create-aura3d/index.d.ts +1 -1
  42. package/dist/create-aura3d/index.d.ts.map +1 -1
  43. package/dist/create-aura3d/index.js +9 -2
  44. package/dist/create-aura3d/index.js.map +1 -1
  45. package/dist/editor-runtime/ProjectSerializer.d.ts +74 -1
  46. package/dist/editor-runtime/ProjectSerializer.d.ts.map +1 -1
  47. package/dist/editor-runtime/ProjectSerializer.js +123 -6
  48. package/dist/editor-runtime/ProjectSerializer.js.map +1 -1
  49. package/dist/editor-runtime/TimelineModel.d.ts +18 -0
  50. package/dist/editor-runtime/TimelineModel.d.ts.map +1 -1
  51. package/dist/editor-runtime/TimelineModel.js +67 -3
  52. package/dist/editor-runtime/TimelineModel.js.map +1 -1
  53. package/dist/editor-runtime/TimelineRuntimeBridge.d.ts +98 -0
  54. package/dist/editor-runtime/TimelineRuntimeBridge.d.ts.map +1 -0
  55. package/dist/editor-runtime/TimelineRuntimeBridge.js +186 -0
  56. package/dist/editor-runtime/TimelineRuntimeBridge.js.map +1 -0
  57. package/dist/editor-runtime/index.d.ts +3 -1
  58. package/dist/editor-runtime/index.d.ts.map +1 -1
  59. package/dist/editor-runtime/index.js +1 -0
  60. package/dist/editor-runtime/index.js.map +1 -1
  61. package/dist/engine/agent-api/AnimationController.d.ts +607 -0
  62. package/dist/engine/agent-api/AnimationController.d.ts.map +1 -0
  63. package/dist/engine/agent-api/AnimationController.js +2192 -0
  64. package/dist/engine/agent-api/AnimationController.js.map +1 -0
  65. package/dist/engine/agent-api/AssetEvidence.d.ts +88 -0
  66. package/dist/engine/agent-api/AssetEvidence.d.ts.map +1 -0
  67. package/dist/engine/agent-api/AssetEvidence.js +157 -0
  68. package/dist/engine/agent-api/AssetEvidence.js.map +1 -0
  69. package/dist/engine/agent-api/AuraAppHandle.d.ts +55 -0
  70. package/dist/engine/agent-api/AuraAppHandle.d.ts.map +1 -0
  71. package/dist/engine/agent-api/AuraAppHandle.js +15 -0
  72. package/dist/engine/agent-api/AuraAppHandle.js.map +1 -0
  73. package/dist/engine/agent-api/AuraVoiceBridge.d.ts +96 -0
  74. package/dist/engine/agent-api/AuraVoiceBridge.d.ts.map +1 -0
  75. package/dist/engine/agent-api/AuraVoiceBridge.js +370 -0
  76. package/dist/engine/agent-api/AuraVoiceBridge.js.map +1 -0
  77. package/dist/engine/agent-api/CartoonDirector.d.ts +95 -0
  78. package/dist/engine/agent-api/CartoonDirector.d.ts.map +1 -0
  79. package/dist/engine/agent-api/CartoonDirector.js +342 -0
  80. package/dist/engine/agent-api/CartoonDirector.js.map +1 -0
  81. package/dist/engine/agent-api/CartoonPerformance.d.ts +149 -0
  82. package/dist/engine/agent-api/CartoonPerformance.d.ts.map +1 -0
  83. package/dist/engine/agent-api/CartoonPerformance.js +317 -0
  84. package/dist/engine/agent-api/CartoonPerformance.js.map +1 -0
  85. package/dist/engine/agent-api/CartoonRenderQueue.d.ts +132 -0
  86. package/dist/engine/agent-api/CartoonRenderQueue.d.ts.map +1 -0
  87. package/dist/engine/agent-api/CartoonRenderQueue.js +385 -0
  88. package/dist/engine/agent-api/CartoonRenderQueue.js.map +1 -0
  89. package/dist/engine/agent-api/CharacterAssembly.d.ts +126 -0
  90. package/dist/engine/agent-api/CharacterAssembly.d.ts.map +1 -0
  91. package/dist/engine/agent-api/CharacterAssembly.js +280 -0
  92. package/dist/engine/agent-api/CharacterAssembly.js.map +1 -0
  93. package/dist/engine/agent-api/DialoguePerformance.d.ts +150 -0
  94. package/dist/engine/agent-api/DialoguePerformance.d.ts.map +1 -0
  95. package/dist/engine/agent-api/DialoguePerformance.js +335 -0
  96. package/dist/engine/agent-api/DialoguePerformance.js.map +1 -0
  97. package/dist/engine/agent-api/FrameLoop.d.ts +70 -0
  98. package/dist/engine/agent-api/FrameLoop.d.ts.map +1 -0
  99. package/dist/engine/agent-api/FrameLoop.js +165 -0
  100. package/dist/engine/agent-api/FrameLoop.js.map +1 -0
  101. package/dist/engine/agent-api/GameAssetValidation.d.ts +279 -0
  102. package/dist/engine/agent-api/GameAssetValidation.d.ts.map +1 -0
  103. package/dist/engine/agent-api/GameAssetValidation.js +719 -0
  104. package/dist/engine/agent-api/GameAssetValidation.js.map +1 -0
  105. package/dist/engine/agent-api/GameEvidence.d.ts +148 -0
  106. package/dist/engine/agent-api/GameEvidence.d.ts.map +1 -0
  107. package/dist/engine/agent-api/GameEvidence.js +269 -0
  108. package/dist/engine/agent-api/GameEvidence.js.map +1 -0
  109. package/dist/engine/agent-api/GameRuntime.d.ts +931 -0
  110. package/dist/engine/agent-api/GameRuntime.d.ts.map +1 -0
  111. package/dist/engine/agent-api/GameRuntime.js +2229 -0
  112. package/dist/engine/agent-api/GameRuntime.js.map +1 -0
  113. package/dist/engine/agent-api/GameSceneBridge.d.ts +54 -0
  114. package/dist/engine/agent-api/GameSceneBridge.d.ts.map +1 -0
  115. package/dist/engine/agent-api/GameSceneBridge.js +110 -0
  116. package/dist/engine/agent-api/GameSceneBridge.js.map +1 -0
  117. package/dist/engine/agent-api/PromptAnimationContract.d.ts +278 -0
  118. package/dist/engine/agent-api/PromptAnimationContract.d.ts.map +1 -0
  119. package/dist/engine/agent-api/PromptAnimationContract.js +238 -0
  120. package/dist/engine/agent-api/PromptAnimationContract.js.map +1 -0
  121. package/dist/engine/agent-api/PromptAnimationEvidence.d.ts +183 -0
  122. package/dist/engine/agent-api/PromptAnimationEvidence.d.ts.map +1 -0
  123. package/dist/engine/agent-api/PromptAnimationEvidence.js +454 -0
  124. package/dist/engine/agent-api/PromptAnimationEvidence.js.map +1 -0
  125. package/dist/engine/agent-api/RuntimeNodeHandle.d.ts +100 -0
  126. package/dist/engine/agent-api/RuntimeNodeHandle.d.ts.map +1 -0
  127. package/dist/engine/agent-api/RuntimeNodeHandle.js +36 -0
  128. package/dist/engine/agent-api/RuntimeNodeHandle.js.map +1 -0
  129. package/dist/engine/agent-api/ShotTimeline.d.ts +179 -0
  130. package/dist/engine/agent-api/ShotTimeline.d.ts.map +1 -0
  131. package/dist/engine/agent-api/ShotTimeline.js +264 -0
  132. package/dist/engine/agent-api/ShotTimeline.js.map +1 -0
  133. package/dist/engine/agent-api/VisemeController.d.ts +89 -0
  134. package/dist/engine/agent-api/VisemeController.d.ts.map +1 -0
  135. package/dist/engine/agent-api/VisemeController.js +207 -0
  136. package/dist/engine/agent-api/VisemeController.js.map +1 -0
  137. package/dist/engine/agent-api/game-kits/fighting.d.ts +123 -0
  138. package/dist/engine/agent-api/game-kits/fighting.d.ts.map +1 -0
  139. package/dist/engine/agent-api/game-kits/fighting.js +483 -0
  140. package/dist/engine/agent-api/game-kits/fighting.js.map +1 -0
  141. package/dist/engine/agent-api/game-kits/index.d.ts +15 -0
  142. package/dist/engine/agent-api/game-kits/index.d.ts.map +1 -0
  143. package/dist/engine/agent-api/game-kits/index.js +6 -0
  144. package/dist/engine/agent-api/game-kits/index.js.map +1 -0
  145. package/dist/engine/agent-api/humanoid-walk-runtime.d.ts +18 -81
  146. package/dist/engine/agent-api/humanoid-walk-runtime.d.ts.map +1 -1
  147. package/dist/engine/agent-api/humanoid-walk-runtime.js +4 -279
  148. package/dist/engine/agent-api/humanoid-walk-runtime.js.map +1 -1
  149. package/dist/engine/agent-api/index.d.ts +490 -4
  150. package/dist/engine/agent-api/index.d.ts.map +1 -1
  151. package/dist/engine/agent-api/index.js +759 -1802
  152. package/dist/engine/agent-api/index.js.map +1 -1
  153. package/dist/engine/agent-api/particle-fountain-runtime.d.ts +5 -80
  154. package/dist/engine/agent-api/particle-fountain-runtime.d.ts.map +1 -1
  155. package/dist/engine/agent-api/particle-fountain-runtime.js +7 -291
  156. package/dist/engine/agent-api/particle-fountain-runtime.js.map +1 -1
  157. package/dist/engine/agent-api/product-viewer-runtime.d.ts +17 -107
  158. package/dist/engine/agent-api/product-viewer-runtime.d.ts.map +1 -1
  159. package/dist/engine/agent-api/product-viewer-runtime.js +4 -330
  160. package/dist/engine/agent-api/product-viewer-runtime.js.map +1 -1
  161. package/dist/index.d.ts +1 -0
  162. package/dist/index.js +1 -0
  163. package/dist/physics/CollisionVolumes.d.ts +57 -0
  164. package/dist/physics/CollisionVolumes.d.ts.map +1 -0
  165. package/dist/physics/CollisionVolumes.js +159 -0
  166. package/dist/physics/CollisionVolumes.js.map +1 -0
  167. package/dist/physics/HitboxWorld.d.ts +229 -0
  168. package/dist/physics/HitboxWorld.d.ts.map +1 -0
  169. package/dist/physics/HitboxWorld.js +640 -0
  170. package/dist/physics/HitboxWorld.js.map +1 -0
  171. package/dist/physics/KinematicBody.d.ts +157 -0
  172. package/dist/physics/KinematicBody.d.ts.map +1 -0
  173. package/dist/physics/KinematicBody.js +405 -0
  174. package/dist/physics/KinematicBody.js.map +1 -0
  175. package/dist/physics/KinematicWorld.d.ts +58 -0
  176. package/dist/physics/KinematicWorld.d.ts.map +1 -0
  177. package/dist/physics/KinematicWorld.js +246 -0
  178. package/dist/physics/KinematicWorld.js.map +1 -0
  179. package/dist/physics/index.d.ts +4 -0
  180. package/dist/physics/index.d.ts.map +1 -1
  181. package/dist/physics/index.js +4 -0
  182. package/dist/physics/index.js.map +1 -1
  183. package/dist/rendering/ForwardPass.js +2 -2
  184. package/dist/rendering/ShaderLibrary.js +2 -2
  185. package/dist/rendering/SkinnedLitMaterial.js +3 -3
  186. package/dist/rendering/SkinnedUnlitMaterial.js +3 -3
  187. package/dist/scene/Renderable.js +2 -2
  188. package/dist/scripting/VisualGraph.d.ts +2 -1
  189. package/dist/scripting/VisualGraph.d.ts.map +1 -1
  190. package/dist/scripting/VisualGraph.js +118 -1
  191. package/dist/scripting/VisualGraph.js.map +1 -1
  192. package/dist/scripting/VisualGraphContext.d.ts +123 -0
  193. package/dist/scripting/VisualGraphContext.d.ts.map +1 -0
  194. package/dist/scripting/VisualGraphContext.js +2 -0
  195. package/dist/scripting/VisualGraphContext.js.map +1 -0
  196. package/dist/scripting/VisualGraphExecutor.d.ts +6 -1
  197. package/dist/scripting/VisualGraphExecutor.d.ts.map +1 -1
  198. package/dist/scripting/VisualGraphExecutor.js +364 -7
  199. package/dist/scripting/VisualGraphExecutor.js.map +1 -1
  200. package/dist/scripting/VisualNodeCatalog.d.ts +1 -1
  201. package/dist/scripting/VisualNodeCatalog.d.ts.map +1 -1
  202. package/dist/scripting/VisualNodeCatalog.js +61 -1
  203. package/dist/scripting/VisualNodeCatalog.js.map +1 -1
  204. package/dist/scripting/index.d.ts +1 -0
  205. package/dist/scripting/index.d.ts.map +1 -1
  206. package/dist/scripting/index.js.map +1 -1
  207. package/package.json +193 -121
  208. package/templates/product-viewer/src/main.ts +1 -0
  209. package/dist/three-compat/ThreeApiInventory.d.ts +0 -18
  210. package/dist/three-compat/ThreeApiInventory.d.ts.map +0 -1
  211. package/dist/three-compat/ThreeApiInventory.js +0 -99
  212. package/dist/three-compat/ThreeApiInventory.js.map +0 -1
  213. package/dist/three-compat/ThreeCompatibilityMatrix.d.ts +0 -30
  214. package/dist/three-compat/ThreeCompatibilityMatrix.d.ts.map +0 -1
  215. package/dist/three-compat/ThreeCompatibilityMatrix.js +0 -69
  216. package/dist/three-compat/ThreeCompatibilityMatrix.js.map +0 -1
  217. package/dist/three-compat/animation/index.d.ts +0 -3
  218. package/dist/three-compat/animation/index.d.ts.map +0 -1
  219. package/dist/three-compat/animation/index.js +0 -2
  220. package/dist/three-compat/animation/index.js.map +0 -1
  221. package/dist/three-compat/cameras/index.d.ts +0 -24
  222. package/dist/three-compat/cameras/index.d.ts.map +0 -1
  223. package/dist/three-compat/cameras/index.js +0 -44
  224. package/dist/three-compat/cameras/index.js.map +0 -1
  225. package/dist/three-compat/controls/index.d.ts +0 -3
  226. package/dist/three-compat/controls/index.d.ts.map +0 -1
  227. package/dist/three-compat/controls/index.js +0 -2
  228. package/dist/three-compat/controls/index.js.map +0 -1
  229. package/dist/three-compat/core/Object3DCompat.d.ts +0 -62
  230. package/dist/three-compat/core/Object3DCompat.d.ts.map +0 -1
  231. package/dist/three-compat/core/Object3DCompat.js +0 -112
  232. package/dist/three-compat/core/Object3DCompat.js.map +0 -1
  233. package/dist/three-compat/core/RaycasterCompat.d.ts +0 -18
  234. package/dist/three-compat/core/RaycasterCompat.d.ts.map +0 -1
  235. package/dist/three-compat/core/RaycasterCompat.js +0 -28
  236. package/dist/three-compat/core/RaycasterCompat.js.map +0 -1
  237. package/dist/three-compat/core/SceneCompat.d.ts +0 -8
  238. package/dist/three-compat/core/SceneCompat.d.ts.map +0 -1
  239. package/dist/three-compat/core/SceneCompat.js +0 -8
  240. package/dist/three-compat/core/SceneCompat.js.map +0 -1
  241. package/dist/three-compat/geometries/index.d.ts +0 -67
  242. package/dist/three-compat/geometries/index.d.ts.map +0 -1
  243. package/dist/three-compat/geometries/index.js +0 -114
  244. package/dist/three-compat/geometries/index.js.map +0 -1
  245. package/dist/three-compat/helpers/index.d.ts +0 -40
  246. package/dist/three-compat/helpers/index.d.ts.map +0 -1
  247. package/dist/three-compat/helpers/index.js +0 -129
  248. package/dist/three-compat/helpers/index.js.map +0 -1
  249. package/dist/three-compat/index.d.ts +0 -32
  250. package/dist/three-compat/index.d.ts.map +0 -1
  251. package/dist/three-compat/index.js +0 -22
  252. package/dist/three-compat/index.js.map +0 -1
  253. package/dist/three-compat/lights/index.d.ts +0 -35
  254. package/dist/three-compat/lights/index.d.ts.map +0 -1
  255. package/dist/three-compat/lights/index.js +0 -39
  256. package/dist/three-compat/lights/index.js.map +0 -1
  257. package/dist/three-compat/loaders/index.d.ts +0 -54
  258. package/dist/three-compat/loaders/index.d.ts.map +0 -1
  259. package/dist/three-compat/loaders/index.js +0 -79
  260. package/dist/three-compat/loaders/index.js.map +0 -1
  261. package/dist/three-compat/materials/index.d.ts +0 -70
  262. package/dist/three-compat/materials/index.d.ts.map +0 -1
  263. package/dist/three-compat/materials/index.js +0 -85
  264. package/dist/three-compat/materials/index.js.map +0 -1
  265. package/dist/three-compat/math/index.d.ts +0 -34
  266. package/dist/three-compat/math/index.d.ts.map +0 -1
  267. package/dist/three-compat/math/index.js +0 -90
  268. package/dist/three-compat/math/index.js.map +0 -1
  269. package/dist/three-compat/migration/CompatibilityWarnings.d.ts +0 -6
  270. package/dist/three-compat/migration/CompatibilityWarnings.d.ts.map +0 -1
  271. package/dist/three-compat/migration/CompatibilityWarnings.js +0 -8
  272. package/dist/three-compat/migration/CompatibilityWarnings.js.map +0 -1
  273. package/dist/three-compat/migration/ImportMap.d.ts +0 -2
  274. package/dist/three-compat/migration/ImportMap.d.ts.map +0 -1
  275. package/dist/three-compat/migration/ImportMap.js +0 -10
  276. package/dist/three-compat/migration/ImportMap.js.map +0 -1
  277. package/dist/three-compat/migration/ThreeToA3DAdapter.d.ts +0 -8
  278. package/dist/three-compat/migration/ThreeToA3DAdapter.d.ts.map +0 -1
  279. package/dist/three-compat/migration/ThreeToA3DAdapter.js +0 -20
  280. package/dist/three-compat/migration/ThreeToA3DAdapter.js.map +0 -1
  281. package/dist/three-compat/postprocessing/index.d.ts +0 -2
  282. package/dist/three-compat/postprocessing/index.d.ts.map +0 -1
  283. package/dist/three-compat/postprocessing/index.js +0 -2
  284. package/dist/three-compat/postprocessing/index.js.map +0 -1
  285. package/dist/three-compat/render-targets/index.d.ts +0 -17
  286. package/dist/three-compat/render-targets/index.d.ts.map +0 -1
  287. package/dist/three-compat/render-targets/index.js +0 -29
  288. package/dist/three-compat/render-targets/index.js.map +0 -1
  289. package/dist/three-compat/shaders/index.d.ts +0 -3
  290. package/dist/three-compat/shaders/index.d.ts.map +0 -1
  291. package/dist/three-compat/shaders/index.js +0 -3
  292. package/dist/three-compat/shaders/index.js.map +0 -1
  293. package/dist/three-compat/textures/index.d.ts +0 -19
  294. package/dist/three-compat/textures/index.d.ts.map +0 -1
  295. package/dist/three-compat/textures/index.js +0 -24
  296. package/dist/three-compat/textures/index.js.map +0 -1
@@ -1,4 +1,42 @@
1
1
  import { PhysicsDebugDraw, PhysicsStepper, PhysicsWorld, ScenePhysicsBridge, Shape as PhysicsShapeFactory } from "../../physics/index.js";
2
+ import { createFrameLoop } from "./FrameLoop.js";
3
+ import { createCombatWorld, createGameCameraDirector, createGameEffects, applyGameCombatEventsToRuntime, createGameAccessibilityFocus, createGameAccessibilityLabel, createGameAccessibilityRuntimeSettings, createGameHighContrastSource, createGameHudBindings, createGameHudComboBinding, createGameHudDebugToggleBinding, createGameHudHealthBinding, createGameHudMeterBinding, createGameHudRoundBinding, createGameHudSnapshot, createGameHudTimerBinding, createGameBoxCollider, createGameCapsuleCollider, createGameColliderDebugGeometry, createGameCombatDebugGeometry, createGameDebugOverlayData, createGameDebugSceneNodes, createGameHitboxDebugGeometry, createGameFighting2DRules, createGameInput, createGameInputReplay, createGameInputReplayDriver, createGameJumpAssist, createGameKinematicBody, createGamePauseControlsSource, createGameRectCollider, createGameReducedFlashSource, createGameReducedMotionSource, createGameSphereCollider, createGameTouchControlLayout, gameColliderAabb, gameColliders, gameEffectPresets, gameGuardboxes, gameHitboxes, gameHurtboxes, gameInputReplayEventsAt, gamePushboxes, gameTriggerVolumes } from "./GameRuntime.js";
4
+ import { collectGameRuntimeEvidence as collectGameRuntimeEvidenceV105 } from "./GameEvidence.js";
5
+ import { calculateRuntimeNodeBounds } from "./RuntimeNodeHandle.js";
6
+ import { createRuntimeNodeSpec } from "./GameSceneBridge.js";
7
+ import { createFightingGameKit, fighting as fightingGameKit } from "./game-kits/fighting.js";
8
+ import { createPromptAnimationEpisodePlan, createPromptAnimationStoryBible, definePromptAnimationStoryboard } from "./PromptAnimationContract.js";
9
+ import { applyShotPlaybackFrame, createShotPlaybackPlan, createShotTimeline, installShotPlayback, sampleShotPlaybackPlan } from "./ShotTimeline.js";
10
+ import { captionCueAtTime, createCaptionTimingProof, deriveCaptionTrackFromDialogue } from "./DialoguePerformance.js";
11
+ import { createAuraVoiceVisemeTrack, createGlbBlendshapeVisemeCue, createPrimitiveMouthVisemeCues, sampleVisemeTrack } from "./VisemeController.js";
12
+ import { createAuraVoiceBridgePackage, createAuraVoiceDubRerenderProof, createAuraVoiceRerenderPlan, sampleAuraVoiceBridgeAtTime } from "./AuraVoiceBridge.js";
13
+ import { createCartoonDirectorPlan } from "./CartoonDirector.js";
14
+ import { createCartoonPerformance } from "./CartoonPerformance.js";
15
+ import { createCartoonRenderOutputPackageMetadata, createCartoonRenderQueue } from "./CartoonRenderQueue.js";
16
+ import { collectPromptAnimationEvidence } from "./PromptAnimationEvidence.js";
17
+ export { Engine } from "../../core/index.js";
18
+ export { asAuraAppHandle, isAuraAppHandle } from "./AuraAppHandle.js";
19
+ export { FrameLoop, createFrameLoop } from "./FrameLoop.js";
20
+ export { createCombatWorld, createGameCameraDirector, createGameEffects, applyGameCombatEventsToRuntime, createGameAccessibilityFocus, createGameAccessibilityLabel, createGameAccessibilityRuntimeSettings, createGameHighContrastSource, createGameHudBindings, createGameHudComboBinding, createGameHudDebugToggleBinding, createGameHudHealthBinding, createGameHudMeterBinding, createGameHudRoundBinding, createGameHudSnapshot, createGameHudTimerBinding, createGameBoxCollider, createGameCapsuleCollider, createGameColliderDebugGeometry, createGameCombatDebugGeometry, createGameDebugOverlayData, createGameDebugSceneNodes, createGameHitboxDebugGeometry, createGameInput, createGameInputReplay, createGameInputReplayDriver, createGameJumpAssist, createGameKinematicBody, createGamePauseControlsSource, createGameRectCollider, createGameReducedFlashSource, createGameReducedMotionSource, createGameSphereCollider, createGameTouchControlLayout, gameColliderAabb, gameColliders, gameInputReplayEventsAt, createGameLoopPlan } from "./GameRuntime.js";
21
+ export { collectGameSceneRuntimeNodes, createGameSceneBridge, createRuntimeNodeSpec } from "./GameSceneBridge.js";
22
+ export { calculateRuntimeNodeBounds, createRuntimeNodeEffectAttachment, runtimeNodeHasTag } from "./RuntimeNodeHandle.js";
23
+ export { createFightingGameKit, fighting, fighterRuntimeNode } from "./game-kits/fighting.js";
24
+ export { gameKits } from "./game-kits/index.js";
25
+ export * from "./GameAssetValidation.js";
26
+ export * from "./CharacterAssembly.js";
27
+ export * from "./AssetEvidence.js";
28
+ export * from "./AnimationController.js";
29
+ export { gameAssetValidation, quaterniusGameReadyFighterValidationContract, validateQuaterniusGameReadyFighterAsset } from "./GameAssetValidation.js";
30
+ export { createAnimationController } from "./AnimationController.js";
31
+ export * from "./PromptAnimationContract.js";
32
+ export * from "./AuraVoiceBridge.js";
33
+ export * from "./ShotTimeline.js";
34
+ export * from "./DialoguePerformance.js";
35
+ export * from "./VisemeController.js";
36
+ export * from "./PromptAnimationEvidence.js";
37
+ export * from "./CartoonDirector.js";
38
+ export * from "./CartoonPerformance.js";
39
+ export * from "./CartoonRenderQueue.js";
2
40
  const auraAssetRefBrand = Symbol("AuraAssetRef");
3
41
  export function defineAuraAssets(definitions) {
4
42
  const refs = {};
@@ -44,6 +82,9 @@ export class AuraNodeBuilder {
44
82
  physics(spec) {
45
83
  return this.with({ physics: spec });
46
84
  }
85
+ runtime(spec) {
86
+ return this.with({ runtime: { mutable: true, ...spec } });
87
+ }
47
88
  toJSON() {
48
89
  return this.value;
49
90
  }
@@ -1145,7 +1186,7 @@ const rendererColorManagementPreset = {
1145
1186
  toneMapping: "aces-filmic",
1146
1187
  defaultExposure: 1.05,
1147
1188
  notes: [
1148
- "Three.js renderer uses SRGBColorSpace output and ACESFilmicToneMapping.",
1189
+ "Aura3D WebGL2 renderer uses sRGB output and ACES filmic tone mapping.",
1149
1190
  "Exposure is selected by scene category to avoid blown-out product/material whites and crushed dark scenes."
1150
1191
  ]
1151
1192
  };
@@ -1237,7 +1278,7 @@ function createRendererDiagnosticReport(snapshot, runtime) {
1237
1278
  intensity: environment?.intensity,
1238
1279
  evidence: environment
1239
1280
  ? `${environment.environment} IBL requested at intensity ${environment.intensity}`
1240
- : "procedural fallback environment requested only; runtime PMREM status is unavailable until render"
1281
+ : "procedural fallback environment requested only; runtime environment prefilter status is unavailable until render"
1241
1282
  };
1242
1283
  const postprocessEvidence = !postprocessRequested
1243
1284
  ? "no renderer postprocess effects requested"
@@ -2875,6 +2916,8 @@ export const games = {
2875
2916
  miniGolfCourse: () => prefabs.miniGolfCourse(),
2876
2917
  createMiniGolfState: () => createMiniGolfStateController(),
2877
2918
  miniGolfPointerShot: miniGolfPointerShotFromDrag,
2919
+ fighting: fightingGameKit,
2920
+ createFightingGameKit,
2878
2921
  miniGolfScene: () => scene()
2879
2922
  .background("#12321d")
2880
2923
  .addMany(prefabs.miniGolfHole())
@@ -2882,6 +2925,321 @@ export const games = {
2882
2925
  .camera(camera.follow({ targetNode: "white physics golf ball", distance: 4.2 }))
2883
2926
  .timeline(timeline.loop({ seconds: 8 }))
2884
2927
  };
2928
+ export function collectGameRuntimeEvidence(app, options = {}) {
2929
+ return collectGameRuntimeEvidenceV105(app, options);
2930
+ }
2931
+ function createGameInputController(options) {
2932
+ const actions = options.actions;
2933
+ const axes = options.axes ?? {};
2934
+ const bufferMs = options.bufferMs ?? 120;
2935
+ const activeBindings = new Set();
2936
+ const activeActionOverrides = new Set();
2937
+ const previousHeld = new Map();
2938
+ const currentHeld = new Map();
2939
+ const pressedEdges = new Set();
2940
+ const releasedEdges = new Set();
2941
+ const lastPressedAt = new Map();
2942
+ const replayEvents = [];
2943
+ let frame = 0;
2944
+ let time = 0;
2945
+ let latestSnapshot = {
2946
+ kind: "aura-game-input-snapshot",
2947
+ frame,
2948
+ time,
2949
+ activeBindings: [],
2950
+ actions: {}
2951
+ };
2952
+ const resolveHeld = (action) => {
2953
+ if (activeActionOverrides.has(action))
2954
+ return true;
2955
+ const bindings = actions[action] ?? [];
2956
+ return bindings.some((binding) => activeBindings.has(binding));
2957
+ };
2958
+ const record = (type, binding) => {
2959
+ replayEvents.push({ frame, time, type, binding });
2960
+ };
2961
+ const pressBinding = (binding, shouldRecord = true) => {
2962
+ activeBindings.add(binding);
2963
+ if (actions[binding])
2964
+ activeActionOverrides.add(binding);
2965
+ if (shouldRecord)
2966
+ record("press", binding);
2967
+ };
2968
+ const releaseBinding = (binding, shouldRecord = true) => {
2969
+ activeBindings.delete(binding);
2970
+ activeActionOverrides.delete(binding);
2971
+ if (shouldRecord)
2972
+ record("release", binding);
2973
+ };
2974
+ const toSnapshot = () => {
2975
+ const actionStates = {};
2976
+ const nowMs = time * 1000;
2977
+ for (const action of Object.keys(actions)) {
2978
+ const held = currentHeld.get(action) ?? false;
2979
+ actionStates[action] = {
2980
+ pressed: pressedEdges.has(action),
2981
+ held,
2982
+ released: releasedEdges.has(action),
2983
+ buffered: pressedEdges.has(action) || nowMs - (lastPressedAt.get(action) ?? Number.NEGATIVE_INFINITY) <= bufferMs,
2984
+ value: held ? 1 : 0
2985
+ };
2986
+ }
2987
+ return {
2988
+ kind: "aura-game-input-snapshot",
2989
+ frame,
2990
+ time,
2991
+ activeBindings: [...activeBindings].sort(),
2992
+ actions: actionStates
2993
+ };
2994
+ };
2995
+ const update = (dt = 1 / 60) => {
2996
+ frame += 1;
2997
+ time += Math.max(0, dt);
2998
+ pressedEdges.clear();
2999
+ releasedEdges.clear();
3000
+ for (const action of Object.keys(actions)) {
3001
+ const held = resolveHeld(action);
3002
+ const wasHeld = previousHeld.get(action) ?? false;
3003
+ currentHeld.set(action, held);
3004
+ if (held && !wasHeld) {
3005
+ pressedEdges.add(action);
3006
+ lastPressedAt.set(action, time * 1000);
3007
+ }
3008
+ if (!held && wasHeld)
3009
+ releasedEdges.add(action);
3010
+ previousHeld.set(action, held);
3011
+ }
3012
+ latestSnapshot = toSnapshot();
3013
+ return latestSnapshot;
3014
+ };
3015
+ const target = options.target ?? (typeof window !== "undefined" ? window : undefined);
3016
+ const onKeyDown = (event) => {
3017
+ const keyboard = event;
3018
+ if (keyboard.repeat)
3019
+ return;
3020
+ if (keyboard.code)
3021
+ pressBinding(keyboard.code);
3022
+ if (keyboard.key && keyboard.key !== keyboard.code)
3023
+ pressBinding(keyboard.key);
3024
+ };
3025
+ const onKeyUp = (event) => {
3026
+ const keyboard = event;
3027
+ if (keyboard.code)
3028
+ releaseBinding(keyboard.code);
3029
+ if (keyboard.key && keyboard.key !== keyboard.code)
3030
+ releaseBinding(keyboard.key);
3031
+ };
3032
+ if (options.autoListen !== false && target?.addEventListener) {
3033
+ target.addEventListener("keydown", onKeyDown);
3034
+ target.addEventListener("keyup", onKeyUp);
3035
+ }
3036
+ return {
3037
+ kind: "aura-game-input-plan",
3038
+ actions,
3039
+ axes,
3040
+ bufferMs,
3041
+ update,
3042
+ snapshot() {
3043
+ return latestSnapshot;
3044
+ },
3045
+ pressed(action) {
3046
+ return pressedEdges.has(action);
3047
+ },
3048
+ held(action) {
3049
+ return currentHeld.get(action) ?? resolveHeld(action);
3050
+ },
3051
+ released(action) {
3052
+ return releasedEdges.has(action);
3053
+ },
3054
+ buffered(action, windowMs = bufferMs) {
3055
+ return pressedEdges.has(action) || time * 1000 - (lastPressedAt.get(action) ?? Number.NEGATIVE_INFINITY) <= windowMs;
3056
+ },
3057
+ axis(name, negativeAction, positiveAction) {
3058
+ const binding = axes[name];
3059
+ const negative = negativeAction ?? binding?.negative;
3060
+ const positive = positiveAction ?? binding?.positive;
3061
+ if (!negative && !positive)
3062
+ return this.held(name) ? 1 : 0;
3063
+ return (positive && this.held(positive) ? 1 : 0) - (negative && this.held(negative) ? 1 : 0);
3064
+ },
3065
+ press(binding) {
3066
+ pressBinding(binding);
3067
+ },
3068
+ release(binding) {
3069
+ releaseBinding(binding);
3070
+ },
3071
+ setAction(action, held) {
3072
+ if (held) {
3073
+ activeActionOverrides.add(action);
3074
+ record("press", action);
3075
+ }
3076
+ else {
3077
+ activeActionOverrides.delete(action);
3078
+ record("release", action);
3079
+ }
3080
+ },
3081
+ recorded() {
3082
+ return [...replayEvents];
3083
+ },
3084
+ replay(events) {
3085
+ activeBindings.clear();
3086
+ activeActionOverrides.clear();
3087
+ for (const event of events) {
3088
+ if (event.type === "press")
3089
+ pressBinding(event.binding, false);
3090
+ else
3091
+ releaseBinding(event.binding, false);
3092
+ }
3093
+ return update(0);
3094
+ },
3095
+ clearReplay() {
3096
+ replayEvents.length = 0;
3097
+ },
3098
+ dispose() {
3099
+ if (target?.removeEventListener) {
3100
+ target.removeEventListener("keydown", onKeyDown);
3101
+ target.removeEventListener("keyup", onKeyUp);
3102
+ }
3103
+ activeBindings.clear();
3104
+ activeActionOverrides.clear();
3105
+ previousHeld.clear();
3106
+ currentHeld.clear();
3107
+ pressedEdges.clear();
3108
+ releasedEdges.clear();
3109
+ }
3110
+ };
3111
+ }
3112
+ export function createAuraGameRules(options = {}) {
3113
+ return {
3114
+ kind: "aura-game-rules",
3115
+ gravity: options.gravity ?? 24,
3116
+ roundSeconds: options.roundSeconds ?? 90,
3117
+ maxHealth: options.maxHealth ?? 100,
3118
+ maxGuard: options.maxGuard ?? 100,
3119
+ maxMeter: options.maxMeter ?? 100,
3120
+ stageBounds: options.stageBounds ?? {
3121
+ minX: -4.5,
3122
+ maxX: 4.5
3123
+ }
3124
+ };
3125
+ }
3126
+ export const gameRules = Object.assign(createAuraGameRules, {
3127
+ fighting2D: createGameFighting2DRules
3128
+ });
3129
+ export function createAuraGameRuntime(options = {}) {
3130
+ return {
3131
+ kind: "aura-game-runtime",
3132
+ loop: {
3133
+ kind: "aura-game-loop-plan",
3134
+ fixedDt: options.loop?.fixedDt ?? 1 / 60,
3135
+ maxSubSteps: options.loop?.maxSubSteps ?? 5,
3136
+ timeScale: options.loop?.timeScale ?? 1
3137
+ },
3138
+ rules: createAuraGameRules(options.rules),
3139
+ input: options.input ? createGameInput(options.input) : undefined,
3140
+ combat: createCombatWorld(),
3141
+ camera: createGameCameraDirector({
3142
+ stageBounds: {
3143
+ minX: options.rules?.stageBounds?.minX ?? -4.5,
3144
+ maxX: options.rules?.stageBounds?.maxX ?? 4.5
3145
+ }
3146
+ }),
3147
+ effects: createGameEffects({ poolSize: options.effectPoolSize }),
3148
+ bodies: []
3149
+ };
3150
+ }
3151
+ export const game = {
3152
+ createRuntime: createAuraGameRuntime,
3153
+ rules: gameRules,
3154
+ loop: (options = {}) => ({
3155
+ kind: "aura-game-loop-plan",
3156
+ fixedDt: options.fixedDt ?? 1 / 60,
3157
+ maxSubSteps: options.maxSubSteps ?? 5,
3158
+ timeScale: options.timeScale ?? 1
3159
+ }),
3160
+ frameLoop: createFrameLoop,
3161
+ runtimeNode: createRuntimeNodeSpec,
3162
+ input: createGameInput,
3163
+ inputReplay: createGameInputReplay,
3164
+ inputReplayDriver: createGameInputReplayDriver,
3165
+ inputReplayEventsAt: gameInputReplayEventsAt,
3166
+ touchControls: createGameTouchControlLayout,
3167
+ kinematicBody: createGameKinematicBody,
3168
+ jumpAssist: createGameJumpAssist,
3169
+ collider: {
3170
+ box: createGameBoxCollider,
3171
+ sphere: createGameSphereCollider,
3172
+ capsule: createGameCapsuleCollider,
3173
+ rect: createGameRectCollider,
3174
+ aabb: gameColliderAabb,
3175
+ factories: gameColliders
3176
+ },
3177
+ hitbox: gameHitboxes,
3178
+ hurtbox: gameHurtboxes,
3179
+ guardbox: gameGuardboxes,
3180
+ pushbox: gamePushboxes,
3181
+ trigger: gameTriggerVolumes,
3182
+ combatWorld: createCombatWorld,
3183
+ combatEvents: applyGameCombatEventsToRuntime,
3184
+ cameraDirector: createGameCameraDirector,
3185
+ effects: createGameEffects,
3186
+ effectPresets: gameEffectPresets,
3187
+ debug: {
3188
+ colliders: createGameColliderDebugGeometry,
3189
+ hitboxes: createGameHitboxDebugGeometry,
3190
+ combat: createGameCombatDebugGeometry,
3191
+ overlay: createGameDebugOverlayData,
3192
+ sceneNodes: createGameDebugSceneNodes
3193
+ },
3194
+ hud: {
3195
+ health: createGameHudHealthBinding,
3196
+ meter: createGameHudMeterBinding,
3197
+ timer: createGameHudTimerBinding,
3198
+ combo: createGameHudComboBinding,
3199
+ round: createGameHudRoundBinding,
3200
+ debugToggle: createGameHudDebugToggleBinding,
3201
+ bindings: createGameHudBindings,
3202
+ snapshot: createGameHudSnapshot
3203
+ },
3204
+ accessibility: {
3205
+ label: createGameAccessibilityLabel,
3206
+ focus: createGameAccessibilityFocus,
3207
+ reducedMotion: createGameReducedMotionSource,
3208
+ reducedFlash: createGameReducedFlashSource,
3209
+ highContrast: createGameHighContrastSource,
3210
+ pauseControls: createGamePauseControlsSource,
3211
+ settings: createGameAccessibilityRuntimeSettings
3212
+ },
3213
+ fighting: createFightingGameKit,
3214
+ evidence: collectGameRuntimeEvidence
3215
+ };
3216
+ export const cartoon = {
3217
+ episodePlan: createPromptAnimationEpisodePlan,
3218
+ storyBible: createPromptAnimationStoryBible,
3219
+ storyboard: definePromptAnimationStoryboard,
3220
+ shotTimeline: createShotTimeline,
3221
+ shotPlaybackPlan: createShotPlaybackPlan,
3222
+ sampleShotPlaybackPlan,
3223
+ applyShotPlaybackFrame,
3224
+ installShotPlayback,
3225
+ captionsFromDialogue: deriveCaptionTrackFromDialogue,
3226
+ captionCueAtTime,
3227
+ captionTimingProof: createCaptionTimingProof,
3228
+ visemeTrack: createAuraVoiceVisemeTrack,
3229
+ primitiveMouthVisemes: createPrimitiveMouthVisemeCues,
3230
+ glbBlendshapeViseme: createGlbBlendshapeVisemeCue,
3231
+ sampleVisemeTrack,
3232
+ auraVoiceBridgePackage: createAuraVoiceBridgePackage,
3233
+ sampleAuraVoiceBridgeAtTime,
3234
+ auraVoiceRerenderPlan: createAuraVoiceRerenderPlan,
3235
+ auraVoiceDubRerenderProof: createAuraVoiceDubRerenderProof,
3236
+ director: createCartoonDirectorPlan,
3237
+ performance: createCartoonPerformance,
3238
+ renderQueue: createCartoonRenderQueue,
3239
+ renderOutputPackage: createCartoonRenderOutputPackageMetadata,
3240
+ evidence: collectPromptAnimationEvidence
3241
+ };
3242
+ export const animationStudio = cartoon;
2885
3243
  function collectParticleBudgetDiagnostics(nodes) {
2886
3244
  const flattened = groups.flatten(nodes);
2887
3245
  const particleEffects = flattened.filter((node) => node.kind === "effect" && node.effect === "particles");
@@ -3499,6 +3857,7 @@ export const character = {
3499
3857
  lowPolyHumanoid: (options = {}) => createLowPolyHumanoid(options),
3500
3858
  authoredHumanoid: (options = {}) => createAuthoredLowPolyHumanoid(options),
3501
3859
  primitiveHumanoid: (options = {}) => createHierarchicalPrimitiveHumanoid(options),
3860
+ performance: createCartoonPerformance,
3502
3861
  importedRigRuntime: async (options) => {
3503
3862
  markAuraLazySystemRequested("character-rig", "character.importedRigRuntime");
3504
3863
  const started = performanceNow();
@@ -3550,7 +3909,7 @@ function collectCityInstancingPlan(nodes) {
3550
3909
  ].filter(Boolean);
3551
3910
  return {
3552
3911
  kind: "aura-city-instancing-plan",
3553
- rendererPath: "createThreePrimitiveBatches",
3912
+ rendererPath: "auraWebGL2PrimitiveBatches",
3554
3913
  windows,
3555
3914
  props,
3556
3915
  roadMarkings,
@@ -4277,17 +4636,46 @@ export const sceneKits = {
4277
4636
  humanoidWalk: (options = {}) => makeSceneKit("humanoidWalk", options),
4278
4637
  productViewer: (asset, options = {}) => makeSceneKit("productViewer", { ...options, asset })
4279
4638
  };
4639
+ export function promptSubjectIsResolved(s) {
4640
+ return "asset" in s;
4641
+ }
4642
+ export async function resolvePromptPlanSubject(plan, resolver) {
4643
+ if (promptSubjectIsResolved(plan.subject)) {
4644
+ return plan;
4645
+ }
4646
+ const intent = plan.subject;
4647
+ const result = await resolver.resolve({ text: intent.intent, constraints: intent.constraints });
4648
+ if (!result) {
4649
+ throw new Error(`No auto-pullable asset matched the prompt-plan intent "${intent.intent}". ` +
4650
+ "Refine the intent or constraints (maxTriangles/license/animated), or provide a concrete typed asset " +
4651
+ "(e.g. a file via `assets add` / a typed asset ref) before compiling.");
4652
+ }
4653
+ const resolvedSubject = {
4654
+ asset: result.asset,
4655
+ ...(intent.label !== undefined ? { label: intent.label } : {})
4656
+ };
4657
+ return { ...plan, subject: resolvedSubject };
4658
+ }
4280
4659
  export function definePromptPlan(plan) {
4281
4660
  return plan;
4282
4661
  }
4662
+ function requireResolvedPromptSubject(plan) {
4663
+ if (!promptSubjectIsResolved(plan.subject)) {
4664
+ throw new Error(`Cannot compile a prompt plan whose subject is still an unresolved intent ("${plan.subject.intent}"). ` +
4665
+ "Resolve the prompt-plan subject first via resolvePromptPlanSubject(...) or the CLI `assets search`; " +
4666
+ "compile needs a concrete typed asset.");
4667
+ }
4668
+ return plan.subject;
4669
+ }
4283
4670
  export function compilePromptPlan(plan) {
4284
- const sceneBuilder = promptRecipes[plan.sceneType](plan.subject.asset, plan);
4671
+ const subject = requireResolvedPromptSubject(plan);
4672
+ const sceneBuilder = promptRecipes[plan.sceneType](subject.asset, plan);
4285
4673
  return {
4286
4674
  scene: sceneBuilder,
4287
4675
  report: {
4288
4676
  schema: "aura3d-prompt-plan-report/1.0",
4289
4677
  sceneType: plan.sceneType,
4290
- subjectAssetId: plan.subject.asset.id,
4678
+ subjectAssetId: subject.asset.id,
4291
4679
  recipe: plan.sceneType,
4292
4680
  cameraPreset: plan.camera?.preset ?? defaultCameraPreset(plan.sceneType),
4293
4681
  lightingPreset: plan.lighting?.preset ?? defaultLightingPreset(plan.sceneType),
@@ -4484,6 +4872,10 @@ function repairHintsForPromptPlan(plan) {
4484
4872
  function promptPlanWarnings(plan) {
4485
4873
  const warnings = [];
4486
4874
  const acceptanceCriteria = plan.acceptanceCriteria.map((item) => item.trim()).filter(Boolean);
4875
+ if (!promptSubjectIsResolved(plan.subject)) {
4876
+ warnings.push(`PromptPlan subject is still an unresolved intent ("${plan.subject.intent}"); resolve it via resolvePromptPlanSubject(...) ` +
4877
+ "or the CLI `assets search` to a concrete typed asset before compiling.");
4878
+ }
4487
4879
  if (!plan.subject.label?.trim()) {
4488
4880
  warnings.push("PromptPlan subject is missing a human-readable label; add one so reports and diagnostics describe the visible subject.");
4489
4881
  }
@@ -4521,6 +4913,217 @@ export class AuraRuntimeError extends Error {
4521
4913
  this.code = code;
4522
4914
  }
4523
4915
  }
4916
+ function createAuraRuntimeNodeRegistry(snapshot) {
4917
+ let handles = new Map();
4918
+ const registry = {
4919
+ get(id) {
4920
+ return handles.get(id);
4921
+ },
4922
+ require(id) {
4923
+ const handle = handles.get(id);
4924
+ if (!handle) {
4925
+ throw new AuraRuntimeError("missing-asset", `Aura3D runtime node "${id}" was not found. Suggested fix: add .runtime({ id: "${id}" }) to the model, primitive, group, or label you want to mutate.`);
4926
+ }
4927
+ return handle;
4928
+ },
4929
+ has(id) {
4930
+ return handles.has(id);
4931
+ },
4932
+ ids() {
4933
+ return [...handles.keys()];
4934
+ },
4935
+ all() {
4936
+ return [...handles.values()];
4937
+ },
4938
+ reset(nextSnapshot) {
4939
+ handles = collectRuntimeNodeHandles(nextSnapshot);
4940
+ }
4941
+ };
4942
+ registry.reset(snapshot);
4943
+ return registry;
4944
+ }
4945
+ function collectRuntimeNodeHandles(snapshot) {
4946
+ const next = new Map();
4947
+ for (const node of snapshot.nodes) {
4948
+ const runtime = "runtime" in node ? node.runtime : undefined;
4949
+ if (!runtime?.id)
4950
+ continue;
4951
+ next.set(runtime.id, createRuntimeNodeHandle(node, runtime));
4952
+ }
4953
+ return next;
4954
+ }
4955
+ function createRuntimeNodeHandle(node, runtime) {
4956
+ const tags = runtime.tags ?? [];
4957
+ const attachedEffects = [];
4958
+ const morphTargetWeights = new Map();
4959
+ let animationBinding;
4960
+ let animationPose;
4961
+ let animationPoseBinding;
4962
+ const getVisible = () => node.kind === "model" ? node.visible !== false : node.visible !== false;
4963
+ const getBounds = () => calculateRuntimeNodeBounds({
4964
+ position: node.position,
4965
+ scale: node.scale,
4966
+ size: "size" in node ? node.size : undefined
4967
+ });
4968
+ return {
4969
+ id: runtime.id,
4970
+ kind: node.kind,
4971
+ name: "name" in node ? node.name : undefined,
4972
+ tags,
4973
+ get position() {
4974
+ return node.position ?? [0, 0, 0];
4975
+ },
4976
+ set position(next) {
4977
+ node.position = next;
4978
+ },
4979
+ get rotation() {
4980
+ return node.rotation ?? [0, 0, 0];
4981
+ },
4982
+ set rotation(next) {
4983
+ node.rotation = next;
4984
+ },
4985
+ get scale() {
4986
+ return node.scale ?? 1;
4987
+ },
4988
+ set scale(next) {
4989
+ node.scale = next;
4990
+ },
4991
+ get visible() {
4992
+ return getVisible();
4993
+ },
4994
+ set visible(next) {
4995
+ node.visible = next;
4996
+ },
4997
+ setPosition(x, y, z) {
4998
+ node.position = [x, y, z];
4999
+ return this;
5000
+ },
5001
+ translate(x, y, z) {
5002
+ const current = node.position ?? [0, 0, 0];
5003
+ node.position = [current[0] + x, current[1] + y, current[2] + z];
5004
+ return this;
5005
+ },
5006
+ setRotation(x, y, z) {
5007
+ node.rotation = [x, y, z];
5008
+ return this;
5009
+ },
5010
+ setScale(scale) {
5011
+ node.scale = scale;
5012
+ return this;
5013
+ },
5014
+ setVisible(visible) {
5015
+ node.visible = visible;
5016
+ return this;
5017
+ },
5018
+ setMaterial(nextMaterial) {
5019
+ node.material = nextMaterial;
5020
+ return this;
5021
+ },
5022
+ play(clip, options = {}) {
5023
+ node.animation = { ...options, clip };
5024
+ return this;
5025
+ },
5026
+ setAnimation(animation) {
5027
+ node.animation = animation;
5028
+ return this;
5029
+ },
5030
+ setAnimationBinding(binding) {
5031
+ animationBinding = binding;
5032
+ return this;
5033
+ },
5034
+ setAnimationPose(pose, metadata) {
5035
+ animationPose = pose ? cloneRuntimeAnimationPose(pose) : undefined;
5036
+ animationPoseBinding = pose ? metadata : undefined;
5037
+ if (pose?.morphTargets) {
5038
+ for (const [name, weight] of Object.entries(pose.morphTargets)) {
5039
+ const normalizedName = name.trim();
5040
+ if (normalizedName) {
5041
+ morphTargetWeights.set(normalizedName, sanitizeRuntimeMorphWeight(weight));
5042
+ }
5043
+ }
5044
+ }
5045
+ return this;
5046
+ },
5047
+ animationPose() {
5048
+ return animationPose ? cloneRuntimeAnimationPose(animationPose) : undefined;
5049
+ },
5050
+ setMorphTarget(name, weight) {
5051
+ const normalizedName = name.trim();
5052
+ if (!normalizedName) {
5053
+ throw new AuraRuntimeError("missing-asset", "Aura3D morph target name is required.");
5054
+ }
5055
+ morphTargetWeights.set(normalizedName, sanitizeRuntimeMorphWeight(weight));
5056
+ return this;
5057
+ },
5058
+ setMorphTargets(weights) {
5059
+ morphTargetWeights.clear();
5060
+ for (const [name, weight] of Object.entries(weights)) {
5061
+ const normalizedName = name.trim();
5062
+ if (normalizedName) {
5063
+ morphTargetWeights.set(normalizedName, sanitizeRuntimeMorphWeight(weight));
5064
+ }
5065
+ }
5066
+ return this;
5067
+ },
5068
+ morphTargets() {
5069
+ return Object.fromEntries(morphTargetWeights.entries());
5070
+ },
5071
+ bounds() {
5072
+ return getBounds();
5073
+ },
5074
+ attachEffect(effect) {
5075
+ attachedEffects.push(effect);
5076
+ return this;
5077
+ },
5078
+ effects() {
5079
+ return [...attachedEffects];
5080
+ },
5081
+ snapshot() {
5082
+ return {
5083
+ id: runtime.id,
5084
+ kind: node.kind,
5085
+ name: "name" in node ? node.name : undefined,
5086
+ tags,
5087
+ position: node.position ?? [0, 0, 0],
5088
+ rotation: node.rotation ?? [0, 0, 0],
5089
+ scale: node.scale ?? 1,
5090
+ visible: getVisible(),
5091
+ animation: node.animation,
5092
+ animationBinding,
5093
+ animationPose: animationPose ? cloneRuntimeAnimationPose(animationPose) : undefined,
5094
+ animationPoseBinding,
5095
+ morphTargets: Object.fromEntries(morphTargetWeights.entries()),
5096
+ bounds: getBounds(),
5097
+ effects: [...attachedEffects]
5098
+ };
5099
+ }
5100
+ };
5101
+ }
5102
+ function cloneRuntimeAnimationPose(pose) {
5103
+ return {
5104
+ bones: Object.fromEntries(Object.entries(pose.bones ?? {}).map(([bone, transform]) => [
5105
+ bone,
5106
+ {
5107
+ position: transform.position ? { ...transform.position } : undefined,
5108
+ rotation: transform.rotation ? { ...transform.rotation } : undefined,
5109
+ scale: transform.scale ? { ...transform.scale } : undefined
5110
+ }
5111
+ ])),
5112
+ morphTargets: pose.morphTargets ? { ...pose.morphTargets } : undefined,
5113
+ rootMotion: pose.rootMotion
5114
+ ? {
5115
+ translation: pose.rootMotion.translation ? { ...pose.rootMotion.translation } : undefined,
5116
+ rotation: pose.rootMotion.rotation ? { ...pose.rootMotion.rotation } : undefined
5117
+ }
5118
+ : undefined,
5119
+ metadata: pose.metadata ? { ...pose.metadata } : undefined
5120
+ };
5121
+ }
5122
+ function sanitizeRuntimeMorphWeight(weight) {
5123
+ if (!Number.isFinite(weight))
5124
+ return 0;
5125
+ return Math.max(0, Math.min(1, weight));
5126
+ }
4524
5127
  export function createAuraApp(target, options) {
4525
5128
  let snapshot = normalizeSceneSnapshot(options.scene);
4526
5129
  let renderSnapshot = flattenSceneSnapshot(snapshot);
@@ -4536,6 +5139,32 @@ export function createAuraApp(target, options) {
4536
5139
  let lastTime = 0;
4537
5140
  let mountRevision = 0;
4538
5141
  let canvasRuntimePhysics;
5142
+ const runtimeNodes = createAuraRuntimeNodeRegistry(renderSnapshot);
5143
+ const frameCallbacks = new Set();
5144
+ let runtimePaused = options.autoStart === false;
5145
+ let runtimeFrame = 0;
5146
+ let runtimeTime = 0;
5147
+ const runtimeFixedDt = 1 / 60;
5148
+ let runtimeAlpha = 0;
5149
+ const ownedInputControllers = new Set();
5150
+ const runRuntimeFrame = (dt, source) => {
5151
+ runtimeFrame += 1;
5152
+ runtimeTime += dt;
5153
+ runtimeAlpha = runtimeFixedDt > 0 ? Math.max(0, Math.min(1, (dt % runtimeFixedDt) / runtimeFixedDt)) : 0;
5154
+ const frame = {
5155
+ dt,
5156
+ fixedDt: runtimeFixedDt,
5157
+ time: runtimeTime,
5158
+ frame: runtimeFrame,
5159
+ alpha: runtimeAlpha,
5160
+ paused: runtimePaused,
5161
+ source,
5162
+ substep: 1,
5163
+ substeps: 1
5164
+ };
5165
+ for (const callback of [...frameCallbacks])
5166
+ callback(frame);
5167
+ };
4539
5168
  const shouldUseProductionRendererForCurrentScene = () => Boolean(canvas && renderSnapshot.nodes.some(isWebGLRenderableNode) && typeof window !== "undefined");
4540
5169
  const resetDiagnosticsForCurrentScene = (backend) => {
4541
5170
  const fresh = createInitialDiagnostics(renderSnapshot);
@@ -4556,7 +5185,11 @@ export function createAuraApp(target, options) {
4556
5185
  return;
4557
5186
  const delta = lastTime > 0 ? Math.max(1, time - lastTime) : 16.67;
4558
5187
  lastTime = time;
4559
- canvasRuntimePhysics?.step(delta / 1000);
5188
+ const dt = delta / 1000;
5189
+ if (!runtimePaused) {
5190
+ runRuntimeFrame(dt, "raf");
5191
+ canvasRuntimePhysics?.step(dt);
5192
+ }
4560
5193
  diagnosticsState.evidence = collectAuraSceneEvidence(renderSnapshot);
4561
5194
  diagnosticsState.fps = Math.round(1000 / delta);
4562
5195
  diagnosticsState.drawCalls = renderSceneToCanvas(canvas, renderSnapshot, time);
@@ -4583,7 +5216,7 @@ export function createAuraApp(target, options) {
4583
5216
  overlay?.update();
4584
5217
  const revision = ++mountRevision;
4585
5218
  if (shouldUseProductionRenderer && canvas) {
4586
- void startProductionRender(canvas, renderSnapshot, diagnosticsState, options, overlay)
5219
+ void startProductionRender(canvas, renderSnapshot, diagnosticsState, options, overlay, runRuntimeFrame, () => runtimePaused)
4587
5220
  .then((controller) => {
4588
5221
  if (disposed || revision !== mountRevision) {
4589
5222
  controller.dispose();
@@ -4617,11 +5250,71 @@ export function createAuraApp(target, options) {
4617
5250
  setScene(nextScene) {
4618
5251
  snapshot = normalizeSceneSnapshot(nextScene);
4619
5252
  renderSnapshot = flattenSceneSnapshot(snapshot);
5253
+ runtimeNodes.reset(renderSnapshot);
4620
5254
  mountCurrentScene();
4621
5255
  },
5256
+ nodes: runtimeNodes,
5257
+ get runtime() {
5258
+ return {
5259
+ paused: runtimePaused,
5260
+ frame: runtimeFrame,
5261
+ time: runtimeTime,
5262
+ fixedDt: runtimeFixedDt,
5263
+ alpha: runtimeAlpha
5264
+ };
5265
+ },
5266
+ onFrame(callback) {
5267
+ frameCallbacks.add(callback);
5268
+ return () => {
5269
+ frameCallbacks.delete(callback);
5270
+ };
5271
+ },
5272
+ offFrame(callback) {
5273
+ frameCallbacks.delete(callback);
5274
+ },
5275
+ input(inputOptions) {
5276
+ const controller = createGameInput(inputOptions);
5277
+ ownedInputControllers.add(controller);
5278
+ return controller;
5279
+ },
5280
+ pause() {
5281
+ runtimePaused = true;
5282
+ },
5283
+ resume() {
5284
+ runtimePaused = false;
5285
+ if (!animationHandle && !productionController && options.autoStart !== false && typeof requestAnimationFrame !== "undefined") {
5286
+ animationHandle = requestAnimationFrame(render);
5287
+ }
5288
+ },
5289
+ step(dt = 1 / 60) {
5290
+ const seconds = Math.max(0, dt);
5291
+ runRuntimeFrame(seconds, "manual");
5292
+ canvasRuntimePhysics?.step(seconds);
5293
+ const previousPaused = runtimePaused;
5294
+ runtimePaused = true;
5295
+ if (productionController) {
5296
+ productionController.render(performanceNow());
5297
+ }
5298
+ else {
5299
+ render(performanceNow());
5300
+ }
5301
+ runtimePaused = previousPaused;
5302
+ },
4622
5303
  diagnostics() {
4623
5304
  return snapshotDiagnostics(diagnosticsState);
4624
5305
  },
5306
+ evidence(evidenceOptions = {}) {
5307
+ return collectGameRuntimeEvidenceV105({
5308
+ runtime: {
5309
+ paused: runtimePaused,
5310
+ frame: runtimeFrame,
5311
+ time: runtimeTime,
5312
+ fixedDt: runtimeFixedDt,
5313
+ alpha: runtimeAlpha
5314
+ },
5315
+ nodes: runtimeNodes
5316
+ }, evidenceOptions);
5317
+ },
4625
5318
  screenshot() {
4626
5319
  return captureAuraScreenshot(canvas);
4627
5320
  },
@@ -4629,6 +5322,9 @@ export function createAuraApp(target, options) {
4629
5322
  disposed = true;
4630
5323
  if (animationHandle && typeof cancelAnimationFrame !== "undefined")
4631
5324
  cancelAnimationFrame(animationHandle);
5325
+ for (const controller of ownedInputControllers)
5326
+ controller.dispose();
5327
+ ownedInputControllers.clear();
4632
5328
  productionController?.dispose();
4633
5329
  overlay?.dispose();
4634
5330
  }
@@ -4652,6 +5348,10 @@ export function collectAuraSceneEvidence(sceneValue) {
4652
5348
  const assetProvenance = snapshot.nodes
4653
5349
  .filter((node) => node.kind === "model")
4654
5350
  .map((node) => createAssetProvenance(node.asset));
5351
+ const runtimeNodeIds = snapshot.nodes
5352
+ .map((node) => "runtime" in node ? node.runtime?.id : undefined)
5353
+ .filter((id) => Boolean(id));
5354
+ const expectsGameRuntime = runtimeNodeIds.length > 0 || interactionNodes.some((node) => node.mode === "keyboard" || node.mode === "drag-vector" || node.mode === "click-impulse");
4655
5355
  return {
4656
5356
  physics: {
4657
5357
  worldAttached: Boolean(snapshot.physics),
@@ -4689,6 +5389,31 @@ export function collectAuraSceneEvidence(sceneValue) {
4689
5389
  collisionAvoidance: labelNodes.filter((node) => node.collisionAvoidance === true).length
4690
5390
  },
4691
5391
  performance: createPerformanceEvidence(snapshot),
5392
+ gameRuntime: collectGameRuntimeEvidenceV105({
5393
+ runtime: {
5394
+ frame: 0,
5395
+ time: 0,
5396
+ paused: true
5397
+ },
5398
+ nodes: {
5399
+ ids: () => runtimeNodeIds
5400
+ }
5401
+ }, {
5402
+ animation: {
5403
+ controllers: animatedNodes.length,
5404
+ activeClips: clips,
5405
+ eventCount: 0
5406
+ },
5407
+ assets: {
5408
+ typedAssets: assetProvenance.filter((asset) => asset.source === "typed-aura-assets-manifest").length,
5409
+ missingAssets: []
5410
+ },
5411
+ source: {
5412
+ mode: "scene-source",
5413
+ expectsGame: expectsGameRuntime,
5414
+ label: "collectAuraSceneEvidence"
5415
+ }
5416
+ }),
4692
5417
  rendering: createRendererDiagnosticReport(snapshot),
4693
5418
  assets: assetProvenance
4694
5419
  };
@@ -4730,7 +5455,11 @@ function collectHelperPerformanceBudgets(nodes) {
4730
5455
  const helpers = new Set();
4731
5456
  if (hasName("visible rigid body cube") || hasName("physics collider debug line"))
4732
5457
  helpers.add("physicsPlayground");
4733
- if (hasName("particle collision ground plane") || hasName("gravity fountain plume"))
5458
+ if (hasName("particle collision ground plane") ||
5459
+ hasName("fountain collision ground plane") ||
5460
+ hasName("particle emission nozzle") ||
5461
+ hasName("fountain droplet plume") ||
5462
+ hasName("gravity fountain plume"))
4734
5463
  helpers.add("particleFountain");
4735
5464
  if (hasName("smooth orbit ring") || hasName("readable planet label"))
4736
5465
  helpers.add("solarSystem");
@@ -4805,7 +5534,7 @@ function isRenderableModelNode(node) {
4805
5534
  function isWebGLRenderableNode(node) {
4806
5535
  return isRenderableModelNode(node) || node.kind === "primitive";
4807
5536
  }
4808
- async function startProductionRender(canvas, snapshot, diagnosticsState, options, overlay) {
5537
+ async function startProductionRender(canvas, snapshot, diagnosticsState, options, overlay, beforeRender, isPaused = () => false) {
4809
5538
  const renderableNode = snapshot.nodes.find(isWebGLRenderableNode);
4810
5539
  if (!renderableNode) {
4811
5540
  throw new AuraRuntimeError("missing-asset", "Aura3D production rendering requires at least one typed model asset or primitive. Suggested fix: add model(assets.product), primitives.box(), primitives.sphere(), primitives.cylinder(), or primitives.plane().");
@@ -4815,9 +5544,14 @@ async function startProductionRender(canvas, snapshot, diagnosticsState, options
4815
5544
  const continuousRender = shouldContinuouslyRender(snapshot);
4816
5545
  let disposed = false;
4817
5546
  let animationHandle = 0;
5547
+ let lastTime = 0;
4818
5548
  const renderFrame = (time = performanceNow()) => {
4819
5549
  if (disposed)
4820
5550
  return;
5551
+ const delta = lastTime > 0 ? Math.max(1, time - lastTime) : 16.67;
5552
+ lastTime = time;
5553
+ if (!isPaused())
5554
+ beforeRender?.(delta / 1000, "raf");
4821
5555
  const drawCalls = renderer.render(time);
4822
5556
  diagnosticsState.backend = "webgl2";
4823
5557
  diagnosticsState.fps = diagnosticsState.fps || 60;
@@ -4832,6 +5566,9 @@ async function startProductionRender(canvas, snapshot, diagnosticsState, options
4832
5566
  };
4833
5567
  renderFrame();
4834
5568
  return {
5569
+ render(time = performanceNow()) {
5570
+ renderFrame(time);
5571
+ },
4835
5572
  dispose() {
4836
5573
  disposed = true;
4837
5574
  if (animationHandle && typeof cancelAnimationFrame !== "undefined")
@@ -4846,6 +5583,8 @@ function shouldContinuouslyRender(snapshot) {
4846
5583
  if (snapshot.camera.mode === "dolly" || snapshot.camera.mode === "follow" || snapshot.camera.mode === "path" || snapshot.camera.mode === "flythrough")
4847
5584
  return true;
4848
5585
  return snapshot.nodes.some((node) => {
5586
+ if ("runtime" in node && node.runtime?.mutable !== false)
5587
+ return true;
4849
5588
  if ((node.kind === "model" || node.kind === "primitive") && node.animation)
4850
5589
  return true;
4851
5590
  if (node.kind !== "effect")
@@ -4854,14 +5593,7 @@ function shouldContinuouslyRender(snapshot) {
4854
5593
  });
4855
5594
  }
4856
5595
  async function createProductionSceneRenderer(canvas, snapshot) {
4857
- try {
4858
- return await createThreeSceneRenderer(canvas, snapshot);
4859
- }
4860
- catch (error) {
4861
- if (typeof console !== "undefined")
4862
- console.warn("Aura3D Three.js renderer fallback:", error);
4863
- return await createWebGLSceneRenderer(canvas, snapshot);
4864
- }
5596
+ return await createWebGLSceneRenderer(canvas, snapshot);
4865
5597
  }
4866
5598
  function colorToClearColor(color) {
4867
5599
  if (typeof color === "string" && /^#[0-9a-f]{6}$/i.test(color)) {
@@ -4870,672 +5602,10 @@ function colorToClearColor(color) {
4870
5602
  }
4871
5603
  return [0.02, 0.025, 0.035, 1];
4872
5604
  }
4873
- function resolveThreeToneMappingExposure(snapshot) {
4874
- const names = groups.flatten(snapshot.nodes).map((node) => "name" in node ? node.name?.toLowerCase() ?? "" : "");
4875
- const category = resolveRendererSceneCategory(snapshot, names);
4876
- if (sceneExposurePresets[category])
4877
- return sceneExposurePresets[category].exposure;
4878
- const [r, g, b] = colorToClearColor(snapshot.background);
4879
- const luminance = r * 0.2126 + g * 0.7152 + b * 0.0722;
4880
- const hasProductOrMaterialLighting = snapshot.nodes.some((node) => node.kind === "environment" && (node.environment === "studio" || node.environment === "material-lab" || node.environment === "product-hero"));
4881
- const hasDarkCinematicCues = snapshot.nodes.some((node) => (node.kind === "effect" && (node.effect === "fog" || node.effect === "bloom")) ||
4882
- (node.kind === "primitive" && node.material?.emissive !== undefined));
4883
- if (hasProductOrMaterialLighting || luminance > 0.72)
4884
- return 0.92;
4885
- if (luminance < 0.08 && hasDarkCinematicCues)
4886
- return 1.34;
4887
- if (luminance < 0.16)
4888
- return 1.18;
4889
- return 1.05;
4890
- }
4891
5605
  function colorWithAlpha(color, alpha) {
4892
5606
  const [r, g, b] = colorToClearColor(color);
4893
5607
  return `rgba(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)},${Math.min(1, Math.max(0, alpha))})`;
4894
5608
  }
4895
- async function createThreeSceneRenderer(canvas, snapshot) {
4896
- const THREE = await import("three");
4897
- const hasRenderableModels = groups.flatten(snapshot.nodes).some((node) => node.kind === "model" && isRenderableModelNode(node));
4898
- const GLTFLoader = hasRenderableModels
4899
- ? (await import("three/examples/jsm/loaders/GLTFLoader.js")).GLTFLoader
4900
- : undefined;
4901
- const renderer = new THREE.WebGLRenderer({
4902
- canvas,
4903
- antialias: true,
4904
- alpha: false,
4905
- preserveDrawingBuffer: true,
4906
- powerPreference: "high-performance"
4907
- });
4908
- renderer.setPixelRatio(Math.min(2, Math.max(1, window.devicePixelRatio || 1)));
4909
- renderer.setSize(canvas.width, canvas.height, false);
4910
- renderer.outputColorSpace = THREE.SRGBColorSpace;
4911
- renderer.toneMapping = THREE.ACESFilmicToneMapping;
4912
- renderer.toneMappingExposure = resolveThreeToneMappingExposure(snapshot);
4913
- renderer.shadowMap.enabled = true;
4914
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
4915
- const threeScene = new THREE.Scene();
4916
- threeScene.background = new THREE.Color(snapshot.background);
4917
- const materialInspectionEnvironment = await createThreeMaterialInspectionEnvironment(THREE, renderer, snapshot);
4918
- if (materialInspectionEnvironment) {
4919
- threeScene.environment = materialInspectionEnvironment.texture;
4920
- if ("environmentIntensity" in threeScene) {
4921
- threeScene.environmentIntensity = materialInspectionEnvironment.intensity;
4922
- }
4923
- }
4924
- const fog = snapshot.nodes.find((node) => node.kind === "effect" && node.effect === "fog");
4925
- if (fog) {
4926
- threeScene.fog = new THREE.FogExp2(new THREE.Color(fog.color ?? "#9fb7d9"), Math.max(0.004, (fog.density ?? 0.1) * 0.045));
4927
- }
4928
- addThreeLights(THREE, threeScene, snapshot.nodes);
4929
- const loader = GLTFLoader ? new GLTFLoader() : undefined;
4930
- const disposables = materialInspectionEnvironment
4931
- ? [materialInspectionEnvironment.texture, materialInspectionEnvironment.generator]
4932
- : [];
4933
- const frameUpdaters = [];
4934
- const primitiveBatchNodes = [];
4935
- const namedObjects = new Map();
4936
- const hoverRuntimeNames = collectThreeHoverRuntimeObjectNames(snapshot);
4937
- const miniGolfRuntimeRequested = hasThreeMiniGolfRuntimeInteraction(snapshot);
4938
- const runtimePhysics = miniGolfRuntimeRequested ? undefined : createRuntimeScenePhysics(snapshot);
4939
- const effectNodes = collectThreeEffectNodes(snapshot);
4940
- for (const node of snapshot.nodes) {
4941
- if (node.kind === "label") {
4942
- // HUD labels are screen-space overlays, not world geometry. Rendering them as
4943
- // 3D sprites produced giant text clipping across the scene, so skip them here.
4944
- if (node.label === "hud")
4945
- continue;
4946
- const label = createThreeLabelNode(THREE, node);
4947
- threeScene.add(label);
4948
- registerThreeNamedObject(namedObjects, node, label);
4949
- registerThreeNodeAnimation(label, node, frameUpdaters);
4950
- disposables.push(label, label.material, label.material.map);
4951
- continue;
4952
- }
4953
- if (node.kind === "model" && isRenderableModelNode(node)) {
4954
- if (!loader) {
4955
- throw createAuraAssetLoadError(node.asset, "the GLB loader was not initialized for this scene");
4956
- }
4957
- const gltf = await loader.loadAsync(new URL(node.asset.url, document.baseURI).href);
4958
- const modelRoot = gltf.scene ?? gltf.scenes?.[0];
4959
- if (!modelRoot) {
4960
- throw createAuraAssetLoadError(node.asset, "the GLB loaded but did not contain a default scene");
4961
- }
4962
- modelRoot.traverse((child) => {
4963
- if (!child.isMesh)
4964
- return;
4965
- child.castShadow = node.castShadow;
4966
- child.receiveShadow = node.receiveShadow;
4967
- if (node.material)
4968
- child.material = createThreeMaterial(THREE, node.material);
4969
- else if (child.material)
4970
- enhanceLoadedMaterial(child.material);
4971
- disposables.push(child.geometry, child.material);
4972
- });
4973
- const pivot = normalizeThreeModel(THREE, modelRoot, node);
4974
- threeScene.add(pivot);
4975
- registerThreeNamedObject(namedObjects, node, pivot);
4976
- runtimePhysics?.bindObject(node, pivot);
4977
- registerThreeModelClipAnimation(THREE, gltf, modelRoot, node, frameUpdaters, disposables);
4978
- registerThreeNodeAnimation(pivot, node, frameUpdaters);
4979
- disposables.push(modelRoot);
4980
- continue;
4981
- }
4982
- if (node.kind === "primitive") {
4983
- if (isThreeReadableTextLabel(node)) {
4984
- const label = createThreeReadableTextLabel(THREE, node);
4985
- threeScene.add(label);
4986
- registerThreeNamedObject(namedObjects, node, label);
4987
- disposables.push(label, label.material, label.material.map);
4988
- continue;
4989
- }
4990
- if (!node.animation && !node.physics && !isThreeMiniGolfRuntimePrimitive(node) && !hoverRuntimeNames.has(node.name ?? "")) {
4991
- primitiveBatchNodes.push(node);
4992
- continue;
4993
- }
4994
- const mesh = createThreePrimitive(THREE, node);
4995
- threeScene.add(mesh);
4996
- registerThreeNamedObject(namedObjects, node, mesh);
4997
- runtimePhysics?.bindObject(node, mesh);
4998
- registerThreeNodeAnimation(mesh, node, frameUpdaters);
4999
- disposables.push(mesh.geometry, mesh.material);
5000
- continue;
5001
- }
5002
- }
5003
- for (const batch of createThreePrimitiveBatches(THREE, primitiveBatchNodes)) {
5004
- threeScene.add(batch);
5005
- disposables.push(batch, batch.geometry, batch.material);
5006
- }
5007
- const contactOcclusionEffect = effectNodes.find((node) => node.effect === "contact-occlusion");
5008
- let contactReceiverAdded = false;
5009
- if (contactOcclusionEffect) {
5010
- const contactReceiver = createThreeContactShadowReceiver(THREE, snapshot, contactOcclusionEffect);
5011
- threeScene.add(contactReceiver);
5012
- disposables.push(contactReceiver, contactReceiver.geometry, contactReceiver.material);
5013
- contactReceiverAdded = true;
5014
- }
5015
- const bloomEffect = effectNodes.find((node) => node.effect === "bloom");
5016
- const rainEffect = effectNodes.find((node) => node.effect === "rain");
5017
- if (rainEffect) {
5018
- const rain = createThreeRain(THREE, rainEffect);
5019
- threeScene.add(rain);
5020
- disposables.push(rain, rain.userData.mistTexture);
5021
- if (typeof rain.userData.update === "function")
5022
- frameUpdaters.push(rain.userData.update);
5023
- }
5024
- const particleEffects = effectNodes.filter((node) => node.effect === "particles");
5025
- for (const particleEffect of particleEffects) {
5026
- const particles = createThreeParticles(THREE, particleEffect);
5027
- threeScene.add(particles);
5028
- disposables.push(particles, particles.geometry, particles.material);
5029
- if (typeof particles.userData.update === "function")
5030
- frameUpdaters.push(particles.userData.update);
5031
- }
5032
- const cameraObject = new THREE.PerspectiveCamera(snapshot.camera.fov ?? 45, canvas.width / Math.max(1, canvas.height), 0.05, 100);
5033
- const postProcess = hasThreePostProcessEffects(effectNodes)
5034
- ? await createThreePostProcessPipeline(THREE, renderer, threeScene, cameraObject, snapshot)
5035
- : null;
5036
- let fallbackBloomAdded = false;
5037
- if (bloomEffect && !postProcess) {
5038
- const bloom = createThreeBloom(THREE, snapshot, bloomEffect);
5039
- threeScene.add(bloom);
5040
- disposables.push(bloom, bloom.userData.texture);
5041
- if (typeof bloom.userData.update === "function")
5042
- frameUpdaters.push(bloom.userData.update);
5043
- fallbackBloomAdded = true;
5044
- }
5045
- const miniGolfRuntime = createThreeMiniGolfRuntime(canvas, snapshot, namedObjects);
5046
- if (miniGolfRuntime)
5047
- frameUpdaters.push((time) => miniGolfRuntime.update(time));
5048
- const hoverRuntime = createThreeHoverRuntime(THREE, canvas, cameraObject, snapshot, namedObjects);
5049
- const actualPasses = [
5050
- ...(postProcess?.passNames ?? []),
5051
- ...(contactReceiverAdded ? ["contact-shadow-receiver"] : [])
5052
- ];
5053
- const fallbackPasses = fallbackBloomAdded ? ["sprite-bloom-halo"] : [];
5054
- const runtimeRendererDiagnostics = createRendererDiagnosticReport(snapshot, {
5055
- mounted: true,
5056
- backend: "three-webgl",
5057
- postprocess: {
5058
- renderPass: postProcess?.passNames.includes("render") ?? false,
5059
- outputPass: postProcess?.passNames.includes("output") ?? false,
5060
- bloomPass: postProcess?.passNames.includes("bloom") ?? false,
5061
- ambientOcclusionPass: postProcess?.passNames.includes("ssao") ?? false,
5062
- contactOcclusionReceiver: contactReceiverAdded,
5063
- pixelBacked: actualPasses.length > 0,
5064
- actualPasses,
5065
- fallbackPasses
5066
- },
5067
- environment: {
5068
- enabled: Boolean(materialInspectionEnvironment),
5069
- preset: materialInspectionEnvironment?.preset,
5070
- intensity: materialInspectionEnvironment?.intensity,
5071
- evidence: materialInspectionEnvironment
5072
- ? `runtime PMREM environment initialized from ${materialInspectionEnvironment.preset}`
5073
- : "no runtime PMREM/IBL environment initialized"
5074
- },
5075
- warnings: postProcess?.warnings ?? []
5076
- });
5077
- let lastPhysicsTime = 0;
5078
- return {
5079
- diagnostics: runtimeRendererDiagnostics,
5080
- render(time) {
5081
- if (renderer.domElement.width !== canvas.width || renderer.domElement.height !== canvas.height) {
5082
- renderer.setSize(canvas.width, canvas.height, false);
5083
- postProcess?.setSize(canvas.width, canvas.height);
5084
- }
5085
- for (const update of frameUpdaters)
5086
- update(time);
5087
- const physicsDelta = lastPhysicsTime > 0 ? Math.max(0, (time - lastPhysicsTime) / 1000) : 1 / 60;
5088
- lastPhysicsTime = time;
5089
- runtimePhysics?.step(physicsDelta);
5090
- updateThreeCamera(THREE, cameraObject, snapshot, canvas, time);
5091
- if (postProcess)
5092
- postProcess.render();
5093
- else
5094
- renderer.render(threeScene, cameraObject);
5095
- return Math.max(1, renderer.info.render.calls);
5096
- },
5097
- dispose() {
5098
- miniGolfRuntime?.dispose();
5099
- hoverRuntime?.dispose();
5100
- for (const item of disposables)
5101
- disposeThreeResource(item);
5102
- postProcess?.dispose();
5103
- renderer.dispose();
5104
- }
5105
- };
5106
- }
5107
- function registerThreeNamedObject(registry, node, object) {
5108
- const name = "name" in node ? node.name : undefined;
5109
- if (name)
5110
- registry.set(name, object);
5111
- }
5112
- function collectThreeHoverRuntimeInteractions(snapshot) {
5113
- return groups.flatten(snapshot.nodes).filter((node) => node.kind === "interaction" &&
5114
- node.mode === "hover" &&
5115
- Boolean(node.target ?? node.selected));
5116
- }
5117
- function collectThreeHoverRuntimeObjectNames(snapshot) {
5118
- const names = new Set();
5119
- for (const interaction of collectThreeHoverRuntimeInteractions(snapshot)) {
5120
- if (interaction.target)
5121
- names.add(interaction.target);
5122
- if (interaction.selected)
5123
- names.add(interaction.selected);
5124
- }
5125
- return names;
5126
- }
5127
- function createThreeHoverRuntime(THREE, canvas, cameraObject, snapshot, objects) {
5128
- const interactions = collectThreeHoverRuntimeInteractions(snapshot);
5129
- if (interactions.length === 0)
5130
- return undefined;
5131
- const entries = interactions
5132
- .map((interaction) => {
5133
- const targetName = interaction.target ?? interaction.selected;
5134
- const selectedName = interaction.selected ?? interaction.target;
5135
- return {
5136
- targetName,
5137
- selectedName,
5138
- target: targetName ? objects.get(targetName) : undefined,
5139
- selected: selectedName ? objects.get(selectedName) : undefined
5140
- };
5141
- })
5142
- .filter((entry) => Boolean(entry.targetName && entry.selectedName && entry.target && entry.selected));
5143
- if (entries.length === 0)
5144
- return undefined;
5145
- const ownerWindow = canvas.ownerDocument?.defaultView;
5146
- const pointer = new THREE.Vector2();
5147
- const raycaster = new THREE.Raycaster();
5148
- const baseScales = new Map();
5149
- let activeEntry;
5150
- let pointerState = { x: 0, y: 0 };
5151
- const setHighlighted = (object, highlighted) => {
5152
- if (!object)
5153
- return;
5154
- if (!baseScales.has(object) && typeof object.scale?.clone === "function")
5155
- baseScales.set(object, object.scale.clone());
5156
- const baseScale = baseScales.get(object);
5157
- if (baseScale && object.scale) {
5158
- object.scale.copy(baseScale);
5159
- if (highlighted)
5160
- object.scale.multiplyScalar(1.1);
5161
- }
5162
- object.traverse?.((child) => {
5163
- const materials = Array.isArray(child.material) ? child.material : child.material ? [child.material] : [];
5164
- for (const materialValue of materials) {
5165
- if (!materialValue || typeof materialValue !== "object")
5166
- continue;
5167
- if (materialValue.userData?.auraHoverBaseEmissiveIntensity === undefined) {
5168
- materialValue.userData = materialValue.userData ?? {};
5169
- materialValue.userData.auraHoverBaseEmissiveIntensity = materialValue.emissiveIntensity ?? 0;
5170
- }
5171
- if ("emissiveIntensity" in materialValue) {
5172
- const base = Number(materialValue.userData.auraHoverBaseEmissiveIntensity ?? 0);
5173
- materialValue.emissiveIntensity = highlighted ? Math.max(base, 0.9) : base;
5174
- }
5175
- materialValue.needsUpdate = true;
5176
- }
5177
- });
5178
- };
5179
- const publish = () => {
5180
- const state = {
5181
- kind: "aura-hover-browser-runtime",
5182
- mounted: true,
5183
- active: Boolean(activeEntry),
5184
- target: activeEntry?.targetName,
5185
- selected: activeEntry?.selectedName,
5186
- pointer: pointerState
5187
- };
5188
- canvas.dataset.auraHoverRuntime = "mounted";
5189
- canvas.dataset.auraHoverActive = String(state.active);
5190
- if (state.target)
5191
- canvas.dataset.auraHoverTarget = state.target;
5192
- else
5193
- delete canvas.dataset.auraHoverTarget;
5194
- if (state.selected)
5195
- canvas.dataset.auraHoverSelected = state.selected;
5196
- else
5197
- delete canvas.dataset.auraHoverSelected;
5198
- if (ownerWindow) {
5199
- ownerWindow.__AURA3D_HOVER__ = state;
5200
- }
5201
- };
5202
- const setActiveEntry = (next) => {
5203
- if (activeEntry?.selected === next?.selected) {
5204
- publish();
5205
- return;
5206
- }
5207
- if (activeEntry)
5208
- setHighlighted(activeEntry.selected, false);
5209
- activeEntry = next;
5210
- if (activeEntry)
5211
- setHighlighted(activeEntry.selected, true);
5212
- publish();
5213
- };
5214
- const resolveHitEntry = (hitObject) => entries.find((entry) => isThreeObjectOrDescendant(hitObject, entry.target));
5215
- const onPointerMove = (event) => {
5216
- const rect = canvas.getBoundingClientRect();
5217
- const width = Math.max(1, rect.width);
5218
- const height = Math.max(1, rect.height);
5219
- pointerState = { x: event.clientX - rect.left, y: event.clientY - rect.top };
5220
- pointer.x = (pointerState.x / width) * 2 - 1;
5221
- pointer.y = -(pointerState.y / height) * 2 + 1;
5222
- raycaster.setFromCamera(pointer, cameraObject);
5223
- const hits = raycaster.intersectObjects(entries.map((entry) => entry.target), true);
5224
- setActiveEntry(hits.length > 0 ? resolveHitEntry(hits[0]?.object) : undefined);
5225
- };
5226
- const onPointerLeave = () => setActiveEntry(undefined);
5227
- canvas.addEventListener("pointermove", onPointerMove);
5228
- canvas.addEventListener("pointerleave", onPointerLeave);
5229
- publish();
5230
- return {
5231
- dispose() {
5232
- canvas.removeEventListener("pointermove", onPointerMove);
5233
- canvas.removeEventListener("pointerleave", onPointerLeave);
5234
- setActiveEntry(undefined);
5235
- if (ownerWindow) {
5236
- const target = ownerWindow;
5237
- if (target.__AURA3D_HOVER__?.kind === "aura-hover-browser-runtime")
5238
- delete target.__AURA3D_HOVER__;
5239
- }
5240
- }
5241
- };
5242
- }
5243
- function isThreeObjectOrDescendant(object, root) {
5244
- let current = object;
5245
- while (current) {
5246
- if (current === root)
5247
- return true;
5248
- current = current.parent;
5249
- }
5250
- return false;
5251
- }
5252
- function hasThreeMiniGolfRuntimeInteraction(snapshot) {
5253
- return snapshot.nodes.some((node) => node.kind === "interaction" &&
5254
- (node.mode === "drag-vector" || node.mode === "click-impulse") &&
5255
- node.target === "white physics golf ball");
5256
- }
5257
- function isThreeMiniGolfRuntimePrimitive(node) {
5258
- return [
5259
- "white physics golf ball",
5260
- "ball contact shadow on felt",
5261
- "ball aim selection ring",
5262
- "cyan aim direction line",
5263
- "shot power meter fill",
5264
- "follow camera target beacon above ball"
5265
- ].includes(node.name ?? "");
5266
- }
5267
- function createThreeMiniGolfRuntime(canvas, snapshot, objects) {
5268
- if (!hasThreeMiniGolfRuntimeInteraction(snapshot))
5269
- return undefined;
5270
- const state = createMiniGolfStateController();
5271
- const ownerWindow = canvas.ownerDocument?.defaultView;
5272
- let metrics = state.snapshot();
5273
- let pointerStart;
5274
- let inputMode = "idle";
5275
- let lastShot = { vector: metrics.aimVector, power: 0 };
5276
- let lastUpdateTime = 0;
5277
- const setObjectPosition = (name, position) => {
5278
- const object = objects.get(name);
5279
- object?.position?.set(position[0], position[1], position[2]);
5280
- };
5281
- const syncObjects = () => {
5282
- const ball = metrics.ballPosition;
5283
- setObjectPosition("white physics golf ball", ball);
5284
- setObjectPosition("ball contact shadow on felt", [ball[0], 0.018, ball[2]]);
5285
- setObjectPosition("ball aim selection ring", [ball[0], 0.035, ball[2]]);
5286
- setObjectPosition("follow camera target beacon above ball", [ball[0], ball[1] + 0.46, ball[2]]);
5287
- setObjectPosition("cyan aim direction line", [ball[0] + metrics.aimVector[0] * 0.54, 0.085, ball[2] + metrics.aimVector[2] * 0.54]);
5288
- };
5289
- const publish = () => {
5290
- const runtimeState = {
5291
- kind: "aura-mini-golf-browser-runtime",
5292
- mounted: true,
5293
- inputMode,
5294
- shotVector: lastShot.vector,
5295
- shotPower: lastShot.power,
5296
- ...metrics
5297
- };
5298
- canvas.dataset.auraMiniGolfRuntime = "mounted";
5299
- canvas.dataset.auraMiniGolfShots = String(metrics.shots);
5300
- canvas.dataset.auraMiniGolfScore = String(metrics.score);
5301
- canvas.dataset.auraMiniGolfCupTriggered = String(metrics.cupTriggered);
5302
- if (ownerWindow) {
5303
- ownerWindow.__AURA3D_MINI_GOLF__ = runtimeState;
5304
- }
5305
- };
5306
- const onPointerDown = (event) => {
5307
- pointerStart = { x: event.clientX, y: event.clientY };
5308
- inputMode = "aiming";
5309
- canvas.setPointerCapture?.(event.pointerId);
5310
- publish();
5311
- };
5312
- const onPointerMove = (event) => {
5313
- if (!pointerStart)
5314
- return;
5315
- lastShot = miniGolfPointerShotFromDrag(pointerStart, { x: event.clientX, y: event.clientY });
5316
- inputMode = "aiming";
5317
- publish();
5318
- };
5319
- const onPointerUp = (event) => {
5320
- const start = pointerStart ?? { x: event.clientX - 64, y: event.clientY + 36 };
5321
- pointerStart = undefined;
5322
- if (metrics.cupTriggered)
5323
- metrics = state.reset();
5324
- lastShot = miniGolfPointerShotFromDrag(start, { x: event.clientX, y: event.clientY });
5325
- metrics = state.shoot(lastShot);
5326
- inputMode = "shot";
5327
- syncObjects();
5328
- publish();
5329
- canvas.releasePointerCapture?.(event.pointerId);
5330
- };
5331
- canvas.addEventListener("pointerdown", onPointerDown);
5332
- canvas.addEventListener("pointermove", onPointerMove);
5333
- canvas.addEventListener("pointerup", onPointerUp);
5334
- canvas.addEventListener("pointercancel", onPointerUp);
5335
- syncObjects();
5336
- publish();
5337
- return {
5338
- update(time) {
5339
- const elapsed = lastUpdateTime > 0 ? Math.max(0, time - lastUpdateTime) : 16.67;
5340
- lastUpdateTime = time;
5341
- if (metrics.shots > 0 && !metrics.settled && !metrics.cupTriggered) {
5342
- const steps = Math.max(1, Math.min(10, Math.round(elapsed / 16.67)));
5343
- metrics = state.step(steps);
5344
- inputMode = metrics.settled ? "idle" : "shot";
5345
- syncObjects();
5346
- publish();
5347
- }
5348
- },
5349
- dispose() {
5350
- canvas.removeEventListener("pointerdown", onPointerDown);
5351
- canvas.removeEventListener("pointermove", onPointerMove);
5352
- canvas.removeEventListener("pointerup", onPointerUp);
5353
- canvas.removeEventListener("pointercancel", onPointerUp);
5354
- if (ownerWindow) {
5355
- const target = ownerWindow;
5356
- if (target.__AURA3D_MINI_GOLF__?.kind === "aura-mini-golf-browser-runtime")
5357
- delete target.__AURA3D_MINI_GOLF__;
5358
- }
5359
- }
5360
- };
5361
- }
5362
- function addThreeLights(THREE, threeScene, nodes) {
5363
- const lightNodes = nodes.filter((node) => node.kind === "light");
5364
- if (lightNodes.length === 0) {
5365
- const emissivePostScene = nodes.some((node) => node.kind === "effect" && node.effect === "bloom") &&
5366
- nodes.some((node) => node.kind === "primitive" && Boolean(node.material?.emissive));
5367
- if (emissivePostScene) {
5368
- threeScene.add(new THREE.HemisphereLight("#9fc7ff", "#020617", 0.18));
5369
- return;
5370
- }
5371
- threeScene.add(new THREE.HemisphereLight("#dfefff", "#071019", 1.35));
5372
- const key = new THREE.DirectionalLight("#ffffff", 2.4);
5373
- key.position.set(3.8, 5.6, 4.2);
5374
- key.castShadow = true;
5375
- configureThreeShadowLight(key);
5376
- threeScene.add(key);
5377
- return;
5378
- }
5379
- for (const node of lightNodes) {
5380
- const color = new THREE.Color(node.color ?? "#ffffff");
5381
- if (node.light === "ambient") {
5382
- threeScene.add(new THREE.HemisphereLight(color, new THREE.Color("#05070b"), Math.max(0.05, node.intensity * 1.8)));
5383
- continue;
5384
- }
5385
- if (node.light === "point") {
5386
- const light = new THREE.PointLight(color, Math.max(0.1, node.intensity * 26), 16, 1.65);
5387
- const position = node.position ?? [2, 2.5, 1.5];
5388
- light.position.set(position[0], position[1], position[2]);
5389
- light.castShadow = true;
5390
- configureThreeShadowLight(light);
5391
- threeScene.add(light);
5392
- continue;
5393
- }
5394
- if (node.light === "rect" || node.light === "softbox") {
5395
- const light = new THREE.RectAreaLight(color, Math.max(0.1, node.intensity * 6), node.width ?? 2, node.height ?? 1);
5396
- const position = node.position ?? [0, 2.4, 2.2];
5397
- light.position.set(position[0], position[1], position[2]);
5398
- light.lookAt(0, 0.55, 0);
5399
- threeScene.add(light);
5400
- continue;
5401
- }
5402
- if (node.light === "studio") {
5403
- threeScene.add(new THREE.HemisphereLight(color, new THREE.Color("#111827"), Math.max(0.05, node.intensity * 1.3)));
5404
- const key = new THREE.DirectionalLight(color, Math.max(0.1, node.intensity * 2.4));
5405
- const position = node.position ?? [3, 4, 3];
5406
- key.position.set(position[0], position[1], position[2]);
5407
- key.castShadow = true;
5408
- configureThreeShadowLight(key);
5409
- threeScene.add(key);
5410
- continue;
5411
- }
5412
- const light = new THREE.DirectionalLight(color, Math.max(0.1, node.intensity * 2.1));
5413
- const position = node.position ?? [3, 4, 3];
5414
- light.position.set(position[0], position[1], position[2]);
5415
- light.castShadow = true;
5416
- configureThreeShadowLight(light);
5417
- threeScene.add(light);
5418
- }
5419
- }
5420
- function configureThreeShadowLight(light) {
5421
- if (!light.shadow)
5422
- return;
5423
- light.shadow.mapSize.width = 1024;
5424
- light.shadow.mapSize.height = 1024;
5425
- if (light.shadow.camera) {
5426
- light.shadow.camera.near = 0.1;
5427
- light.shadow.camera.far = Math.max(light.shadow.camera.far ?? 24, 24);
5428
- if ("left" in light.shadow.camera) {
5429
- light.shadow.camera.left = -6;
5430
- light.shadow.camera.right = 6;
5431
- light.shadow.camera.top = 6;
5432
- light.shadow.camera.bottom = -6;
5433
- }
5434
- }
5435
- }
5436
- function createThreePrimitiveBatches(THREE, nodes) {
5437
- const groups = new Map();
5438
- for (const node of nodes) {
5439
- const key = primitiveBatchKey(node);
5440
- const existing = groups.get(key);
5441
- if (existing)
5442
- existing.push(node);
5443
- else
5444
- groups.set(key, [node]);
5445
- }
5446
- const meshes = [];
5447
- for (const group of groups.values()) {
5448
- const first = group[0];
5449
- if (!first)
5450
- continue;
5451
- if (group.length === 1) {
5452
- meshes.push(createThreePrimitive(THREE, first));
5453
- continue;
5454
- }
5455
- const geometry = createThreePrimitiveGeometry(THREE, first.primitive);
5456
- const materialValue = createThreeMaterial(THREE, first.material ?? material.pbr());
5457
- const mesh = new THREE.InstancedMesh(geometry, materialValue, group.length);
5458
- mesh.name = `aura-instanced-${first.primitive}-${group.length}`;
5459
- mesh.castShadow = first.castShadow ?? (first.primitive !== "plane" && !first.material?.emissive);
5460
- mesh.receiveShadow = first.receiveShadow ?? true;
5461
- const dummy = new THREE.Object3D();
5462
- group.forEach((node, index) => {
5463
- applyThreeTransform(dummy, node, primitiveSize(node));
5464
- dummy.updateMatrix();
5465
- mesh.setMatrixAt(index, dummy.matrix);
5466
- });
5467
- mesh.instanceMatrix.needsUpdate = true;
5468
- meshes.push(mesh);
5469
- }
5470
- return meshes;
5471
- }
5472
- function primitiveBatchKey(node) {
5473
- return JSON.stringify({
5474
- primitive: node.primitive,
5475
- material: node.material ?? material.pbr(),
5476
- castShadow: node.castShadow ?? (node.primitive !== "plane" && !node.material?.emissive),
5477
- receiveShadow: node.receiveShadow ?? true
5478
- });
5479
- }
5480
- function createThreePrimitiveGeometry(THREE, primitiveName) {
5481
- return primitiveName === "sphere"
5482
- ? new THREE.SphereGeometry(0.5, 24, 12)
5483
- : primitiveName === "capsule"
5484
- ? new THREE.CapsuleGeometry(0.32, 0.68, 8, 16)
5485
- : primitiveName === "torus"
5486
- ? new THREE.TorusGeometry(0.5, 0.035, 12, 64)
5487
- : primitiveName === "box"
5488
- ? new THREE.BoxGeometry(1, 1, 1)
5489
- : primitiveName === "cylinder"
5490
- ? new THREE.CylinderGeometry(0.5, 0.5, 1, 24, 1)
5491
- : new THREE.PlaneGeometry(1, 1, 1, 1).rotateX(-Math.PI / 2);
5492
- }
5493
- function createThreePrimitive(THREE, node) {
5494
- const geometry = createThreePrimitiveGeometry(THREE, node.primitive);
5495
- const materialValue = createThreeMaterial(THREE, node.material ?? material.pbr());
5496
- const mesh = new THREE.Mesh(geometry, materialValue);
5497
- mesh.castShadow = node.castShadow ?? (node.primitive !== "plane" && !node.material?.emissive);
5498
- mesh.receiveShadow = node.receiveShadow ?? true;
5499
- applyThreeTransform(mesh, node, primitiveSize(node));
5500
- return mesh;
5501
- }
5502
- function isThreeReadableTextLabel(node) {
5503
- return node.primitive === "plane" && node.name?.endsWith("readable planet label") === true;
5504
- }
5505
- function createThreeReadableTextLabel(THREE, node) {
5506
- const label = (node.name ?? "label").replace(/\s*readable planet label$/, "");
5507
- const color = String(node.material?.emissive ?? node.material?.color ?? "#ffffff");
5508
- const texture = createThreeTextTexture(THREE, label, color);
5509
- const sprite = new THREE.Sprite(new THREE.SpriteMaterial({
5510
- map: texture,
5511
- transparent: true,
5512
- depthWrite: false,
5513
- depthTest: false
5514
- }));
5515
- const position = node.position ?? [0, 0.4, 0];
5516
- const scale = typeof node.scale === "number" ? [node.scale, node.scale, node.scale] : node.scale ?? [0.8, 1, 0.24];
5517
- sprite.name = node.name ?? label;
5518
- sprite.position.set(position[0], position[1], position[2]);
5519
- sprite.scale.set(scale[0], scale[2] * 1.35, 1);
5520
- return sprite;
5521
- }
5522
- function createThreeLabelNode(THREE, node) {
5523
- const color = String(node.color ?? "#e0f2fe");
5524
- const texture = createThreeTextTexture(THREE, node.text, color, String(node.background ?? "#020617"));
5525
- const sprite = new THREE.Sprite(new THREE.SpriteMaterial({
5526
- map: texture,
5527
- transparent: true,
5528
- depthWrite: false,
5529
- depthTest: node.occlusionAware === true
5530
- }));
5531
- const position = node.position ?? labelDefaultPosition(node);
5532
- const size = Math.max(0.16, node.size ?? 0.36);
5533
- const width = Math.max(0.56, Math.min(2.8, node.text.length * size * 0.22));
5534
- sprite.name = node.name ?? `${node.label} label`;
5535
- sprite.position.set(position[0], position[1], position[2]);
5536
- sprite.scale.set(width, size * 0.62, 1);
5537
- return sprite;
5538
- }
5539
5609
  function labelDefaultPosition(node) {
5540
5610
  if (node.screenAnchor === "top-right")
5541
5611
  return [1.35, 1.85, -0.2];
@@ -5547,485 +5617,6 @@ function labelDefaultPosition(node) {
5547
5617
  return [-1.35, 1.85, -0.2];
5548
5618
  return [0, 1.28, 0];
5549
5619
  }
5550
- function createThreeTextTexture(THREE, text, color, background = "#020617") {
5551
- const canvas = document.createElement("canvas");
5552
- canvas.width = 512;
5553
- canvas.height = 160;
5554
- const context = canvas.getContext("2d");
5555
- if (context) {
5556
- context.clearRect(0, 0, canvas.width, canvas.height);
5557
- context.fillStyle = colorWithAlpha(background, 0.86);
5558
- context.fillRect(8, 18, canvas.width - 16, canvas.height - 36);
5559
- context.strokeStyle = color;
5560
- context.lineWidth = 8;
5561
- context.strokeRect(12, 22, canvas.width - 24, canvas.height - 44);
5562
- context.fillStyle = color;
5563
- context.font = "700 64px system-ui, -apple-system, BlinkMacSystemFont, sans-serif";
5564
- context.textAlign = "center";
5565
- context.textBaseline = "middle";
5566
- context.fillText(text, canvas.width / 2, canvas.height / 2 + 2);
5567
- }
5568
- const texture = new THREE.CanvasTexture(canvas);
5569
- texture.colorSpace = THREE.SRGBColorSpace;
5570
- texture.needsUpdate = true;
5571
- return texture;
5572
- }
5573
- async function createThreeMaterialInspectionEnvironment(THREE, renderer, snapshot) {
5574
- const environmentNode = snapshot.nodes.find((node) => node.kind === "environment");
5575
- const needsInspectionEnvironment = Boolean(environmentNode) || snapshot.nodes.some((node) => node.kind === "environment" ||
5576
- (node.kind === "primitive" && (node.name === "mirror chrome metal swatch" ||
5577
- node.name === "transparent cyan glass swatch" ||
5578
- node.name === "red automotive clearcoat swatch")));
5579
- if (!needsInspectionEnvironment)
5580
- return undefined;
5581
- try {
5582
- const generator = new THREE.PMREMGenerator(renderer);
5583
- const profile = resolveThreeEnvironmentProfile(environmentNode);
5584
- const environment = createThreeProceduralIblScene(THREE, profile);
5585
- const texture = generator.fromScene(environment, profile.blur).texture;
5586
- disposeThreeResource(environment);
5587
- return { texture, generator, intensity: profile.intensity, preset: profile.preset };
5588
- }
5589
- catch {
5590
- try {
5591
- const { RoomEnvironment } = await import("three/examples/jsm/environments/RoomEnvironment.js");
5592
- const generator = new THREE.PMREMGenerator(renderer);
5593
- const environment = new RoomEnvironment(renderer);
5594
- const texture = generator.fromScene(environment, 0.04).texture;
5595
- disposeThreeResource(environment);
5596
- return { texture, generator, intensity: environmentNode?.intensity ?? 1, preset: environmentNode?.environment ?? "room-fallback" };
5597
- }
5598
- catch {
5599
- return undefined;
5600
- }
5601
- }
5602
- }
5603
- function resolveThreeEnvironmentProfile(node) {
5604
- const preset = node?.environment ?? "studio";
5605
- const fallback = environmentMapPresets.find((entry) => entry.id === preset);
5606
- const intensity = node?.intensity ?? fallback?.intensity ?? 1;
5607
- const roomColor = node?.color ?? fallback?.color ?? "#eef4ff";
5608
- const profiles = {
5609
- studio: [
5610
- { name: "left broad studio softbox", color: "#ffffff", position: [-3.4, 2.2, 1.8], rotation: [0, Math.PI * 0.38, 0], scale: [2.5, 1.15, 1] },
5611
- { name: "right cool studio fill", color: "#cfe9ff", position: [3.2, 1.75, 1.2], rotation: [0, -Math.PI * 0.36, 0], scale: [1.8, 0.95, 1] },
5612
- { name: "low dark reflection card", color: "#111827", position: [0, 0.55, -2.8], rotation: [0, 0, 0], scale: [3.8, 0.65, 1] }
5613
- ],
5614
- "material-lab": [
5615
- { name: "white material strip reflection", color: "#ffffff", position: [0, 2.7, -2.9], rotation: [0, 0, 0], scale: [4.6, 0.42, 1] },
5616
- { name: "black material contrast card", color: "#030712", position: [0, 1.65, -3.0], rotation: [0, 0, 0], scale: [4.1, 0.34, 1] },
5617
- { name: "cool cyan material card", color: "#67e8f9", position: [-3.1, 1.65, -0.8], rotation: [0, Math.PI * 0.42, 0], scale: [0.68, 2.1, 1] },
5618
- { name: "warm gold material card", color: "#fbbf24", position: [3.1, 1.65, -0.8], rotation: [0, -Math.PI * 0.42, 0], scale: [0.68, 2.1, 1] }
5619
- ],
5620
- "product-hero": [
5621
- { name: "large product key softbox", color: "#ffffff", position: [-2.4, 2.8, 2.4], rotation: [-0.25, Math.PI * 0.3, 0], scale: [2.7, 1.45, 1] },
5622
- { name: "thin product rim strip", color: "#dbeafe", position: [2.8, 1.55, -1.9], rotation: [0, -Math.PI * 0.34, 0], scale: [0.55, 2.0, 1] },
5623
- { name: "product floor dark bounce", color: "#172033", position: [0, 0.32, 2.8], rotation: [0, Math.PI, 0], scale: [3.4, 0.5, 1] }
5624
- ],
5625
- "night-cinematic": [
5626
- { name: "cyan night tunnel reflection", color: "#22d3ee", position: [-3.2, 1.55, 0.2], rotation: [0, Math.PI * 0.42, 0], scale: [0.44, 2.4, 1] },
5627
- { name: "magenta night tunnel reflection", color: "#ff42c8", position: [3.2, 1.1, -0.6], rotation: [0, -Math.PI * 0.42, 0], scale: [0.44, 1.9, 1] },
5628
- { name: "dim blue vanishing reflection", color: "#1d4ed8", position: [0, 0.95, -3.0], rotation: [0, 0, 0], scale: [2.2, 0.75, 1] }
5629
- ],
5630
- "metal-studio": [
5631
- { name: "hard white metal reflection bar", color: "#ffffff", position: [0, 2.3, -2.9], rotation: [0, 0, 0], scale: [5.2, 0.28, 1] },
5632
- { name: "deep black metal reflection bar", color: "#020617", position: [0, 1.65, -3.0], rotation: [0, 0, 0], scale: [4.6, 0.22, 1] },
5633
- { name: "blue metal side card", color: "#93c5fd", position: [-3.25, 1.35, -0.4], rotation: [0, Math.PI * 0.43, 0], scale: [0.42, 2.2, 1] },
5634
- { name: "amber metal side card", color: "#f59e0b", position: [3.25, 1.35, -0.4], rotation: [0, -Math.PI * 0.43, 0], scale: [0.42, 2.2, 1] }
5635
- ],
5636
- "glass-studio": [
5637
- { name: "glass white caustic strip", color: "#ffffff", position: [-1.1, 2.35, -2.7], rotation: [0, 0.08, 0], scale: [2.5, 0.32, 1] },
5638
- { name: "glass cyan refraction panel", color: "#99f6e4", position: [2.9, 1.55, -0.6], rotation: [0, -Math.PI * 0.4, 0], scale: [0.56, 2.0, 1] },
5639
- { name: "glass dark edge card", color: "#06111c", position: [-2.8, 1.18, 0.2], rotation: [0, Math.PI * 0.4, 0], scale: [0.5, 1.6, 1] }
5640
- ]
5641
- };
5642
- return {
5643
- preset,
5644
- intensity,
5645
- blur: preset === "night-cinematic" ? 0.02 : 0.04,
5646
- roomColor,
5647
- panels: profiles[preset] ?? profiles.studio
5648
- };
5649
- }
5650
- function createThreeProceduralIblScene(THREE, profile) {
5651
- const environment = new THREE.Scene();
5652
- const room = new THREE.Mesh(new THREE.BoxGeometry(18, 18, 18), new THREE.MeshBasicMaterial({ color: new THREE.Color(profile.roomColor), side: THREE.BackSide }));
5653
- environment.add(room);
5654
- for (const panel of profile.panels) {
5655
- const mesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), new THREE.MeshBasicMaterial({ color: new THREE.Color(panel.color), side: THREE.DoubleSide }));
5656
- mesh.name = panel.name;
5657
- mesh.position.set(panel.position[0], panel.position[1], panel.position[2]);
5658
- mesh.rotation.set(panel.rotation[0], panel.rotation[1], panel.rotation[2]);
5659
- mesh.scale.set(panel.scale[0], panel.scale[1], panel.scale[2]);
5660
- environment.add(mesh);
5661
- }
5662
- return environment;
5663
- }
5664
- function createThreeMaterial(THREE, spec) {
5665
- if (spec.shader === "solar-sun")
5666
- return createThreeSolarSunMaterial(THREE, spec);
5667
- if (spec.shader === "solar-corona")
5668
- return createThreeSolarCoronaMaterial(THREE, spec);
5669
- const color = new THREE.Color(spec.color ?? "#d7dee8");
5670
- const metalness = spec.metalness ?? spec.metallic ?? 0;
5671
- const usePhysical = spec.transmission !== undefined ||
5672
- spec.clearcoat !== undefined ||
5673
- spec.sheen !== undefined ||
5674
- spec.iridescence !== undefined ||
5675
- spec.anisotropy !== undefined;
5676
- const transparent = spec.opacity !== undefined && spec.opacity < 1;
5677
- const materialValue = usePhysical
5678
- ? new THREE.MeshPhysicalMaterial({
5679
- color,
5680
- roughness: spec.roughness ?? 0.54,
5681
- metalness,
5682
- transparent,
5683
- opacity: spec.opacity ?? 1,
5684
- transmission: spec.transmission ?? 0,
5685
- clearcoat: spec.clearcoat ?? 0,
5686
- clearcoatRoughness: spec.clearcoatRoughness ?? 0.18,
5687
- thickness: spec.thickness ?? 0,
5688
- ior: spec.ior ?? 1.5,
5689
- attenuationColor: new THREE.Color(spec.attenuationColor ?? spec.color ?? "#ffffff"),
5690
- attenuationDistance: spec.attenuationDistance ?? Infinity,
5691
- envMapIntensity: spec.envMapIntensity ?? 1,
5692
- depthWrite: !transparent,
5693
- side: transparent ? THREE.DoubleSide : THREE.FrontSide
5694
- })
5695
- : new THREE.MeshStandardMaterial({
5696
- color,
5697
- roughness: spec.roughness ?? 0.54,
5698
- metalness,
5699
- transparent,
5700
- opacity: spec.opacity ?? 1,
5701
- envMapIntensity: spec.envMapIntensity ?? 1,
5702
- depthWrite: !transparent,
5703
- side: transparent ? THREE.DoubleSide : THREE.FrontSide
5704
- });
5705
- if (spec.emissive) {
5706
- materialValue.emissive = new THREE.Color(spec.emissive);
5707
- materialValue.emissiveIntensity = spec.emissiveIntensity ?? 1.55;
5708
- }
5709
- if ("sheen" in materialValue && spec.sheen !== undefined)
5710
- materialValue.sheen = clampMaterialScalar(spec.sheen, 0);
5711
- if ("sheenRoughness" in materialValue && spec.sheenRoughness !== undefined)
5712
- materialValue.sheenRoughness = clampMaterialScalar(spec.sheenRoughness, 0.5);
5713
- if ("sheenColor" in materialValue && spec.sheenColor)
5714
- materialValue.sheenColor = new THREE.Color(spec.sheenColor);
5715
- if ("iridescence" in materialValue && spec.iridescence !== undefined)
5716
- materialValue.iridescence = clampMaterialScalar(spec.iridescence, 0);
5717
- if ("iridescenceIOR" in materialValue && spec.iridescenceIOR !== undefined)
5718
- materialValue.iridescenceIOR = spec.iridescenceIOR;
5719
- if ("iridescenceThicknessRange" in materialValue && spec.iridescenceThicknessRange)
5720
- materialValue.iridescenceThicknessRange = spec.iridescenceThicknessRange;
5721
- if ("anisotropy" in materialValue && spec.anisotropy !== undefined)
5722
- materialValue.anisotropy = clampMaterialScalar(spec.anisotropy, 0, -1, 1);
5723
- if ("anisotropyRotation" in materialValue && spec.anisotropyRotation !== undefined)
5724
- materialValue.anisotropyRotation = spec.anisotropyRotation;
5725
- const normalMap = createThreeTextureFromMaterialInput(THREE, spec.normal, "normal");
5726
- if (normalMap) {
5727
- materialValue.normalMap = normalMap;
5728
- materialValue.normalScale = new THREE.Vector2(spec.normalScale ?? 1, spec.normalScale ?? 1);
5729
- }
5730
- const roughnessMap = createThreeTextureFromMaterialInput(THREE, spec.roughnessMap, "roughness");
5731
- if (roughnessMap)
5732
- materialValue.roughnessMap = roughnessMap;
5733
- const metalnessMap = createThreeTextureFromMaterialInput(THREE, spec.metalnessMap, "metalness");
5734
- if (metalnessMap)
5735
- materialValue.metalnessMap = metalnessMap;
5736
- materialValue.needsUpdate = true;
5737
- return materialValue;
5738
- }
5739
- function createThreeSolarSunMaterial(THREE, spec) {
5740
- const materialValue = new THREE.ShaderMaterial({
5741
- transparent: false,
5742
- depthWrite: true,
5743
- uniforms: {
5744
- uColor: { value: new THREE.Color(spec.color ?? "#ffd166") },
5745
- uCoreColor: { value: new THREE.Color(spec.coreColor ?? "#fff7ad") },
5746
- uRimColor: { value: new THREE.Color(spec.rimColor ?? "#f97316") },
5747
- uIntensity: { value: spec.emissiveIntensity ?? 2.45 },
5748
- uNoiseStrength: { value: spec.noiseStrength ?? 0.18 }
5749
- },
5750
- vertexShader: `
5751
- varying vec3 vNormalW;
5752
- varying vec3 vPositionW;
5753
- void main() {
5754
- vec4 worldPosition = modelMatrix * vec4(position, 1.0);
5755
- vPositionW = worldPosition.xyz;
5756
- vNormalW = normalize(mat3(modelMatrix) * normal);
5757
- gl_Position = projectionMatrix * viewMatrix * worldPosition;
5758
- }
5759
- `,
5760
- fragmentShader: `
5761
- uniform vec3 uColor;
5762
- uniform vec3 uCoreColor;
5763
- uniform vec3 uRimColor;
5764
- uniform float uIntensity;
5765
- uniform float uNoiseStrength;
5766
- varying vec3 vNormalW;
5767
- varying vec3 vPositionW;
5768
- float auraHash(vec3 p) {
5769
- return fract(sin(dot(p, vec3(17.17, 43.31, 91.73))) * 43758.5453);
5770
- }
5771
- void main() {
5772
- vec3 n = normalize(vNormalW);
5773
- vec3 viewDir = normalize(cameraPosition - vPositionW);
5774
- float facing = clamp(dot(n, viewDir), 0.0, 1.0);
5775
- float rim = pow(1.0 - facing, 2.1);
5776
- float grain = auraHash(n * 3.7) * 0.55 + auraHash(n * 9.3 + 4.2) * 0.45;
5777
- vec3 hotCore = mix(uColor, uCoreColor, smoothstep(0.16, 0.92, facing));
5778
- vec3 color = mix(hotCore, uRimColor, rim * 0.82);
5779
- color *= uIntensity * (0.88 + grain * uNoiseStrength);
5780
- gl_FragColor = vec4(color, 1.0);
5781
- }
5782
- `
5783
- });
5784
- return materialValue;
5785
- }
5786
- function createThreeSolarCoronaMaterial(THREE, spec) {
5787
- const materialValue = new THREE.ShaderMaterial({
5788
- transparent: true,
5789
- depthWrite: false,
5790
- blending: THREE.AdditiveBlending,
5791
- side: THREE.BackSide,
5792
- uniforms: {
5793
- uColor: { value: new THREE.Color(spec.color ?? "#ff9f1c") },
5794
- uCoreColor: { value: new THREE.Color(spec.coreColor ?? "#ffd166") },
5795
- uRimColor: { value: new THREE.Color(spec.rimColor ?? "#f97316") },
5796
- uOpacity: { value: spec.opacity ?? 0.34 },
5797
- uIntensity: { value: spec.emissiveIntensity ?? 1.55 },
5798
- uFalloff: { value: spec.falloff ?? 2.7 },
5799
- uNoiseStrength: { value: spec.noiseStrength ?? 0.14 }
5800
- },
5801
- vertexShader: `
5802
- varying vec3 vNormalW;
5803
- varying vec3 vPositionW;
5804
- void main() {
5805
- vec4 worldPosition = modelMatrix * vec4(position, 1.0);
5806
- vPositionW = worldPosition.xyz;
5807
- vNormalW = normalize(mat3(modelMatrix) * normal);
5808
- gl_Position = projectionMatrix * viewMatrix * worldPosition;
5809
- }
5810
- `,
5811
- fragmentShader: `
5812
- uniform vec3 uColor;
5813
- uniform vec3 uCoreColor;
5814
- uniform vec3 uRimColor;
5815
- uniform float uOpacity;
5816
- uniform float uIntensity;
5817
- uniform float uFalloff;
5818
- uniform float uNoiseStrength;
5819
- varying vec3 vNormalW;
5820
- varying vec3 vPositionW;
5821
- float auraHash(vec3 p) {
5822
- return fract(sin(dot(p, vec3(23.11, 59.37, 101.19))) * 24634.6345);
5823
- }
5824
- void main() {
5825
- vec3 n = normalize(vNormalW);
5826
- vec3 viewDir = normalize(cameraPosition - vPositionW);
5827
- float facing = clamp(dot(n, viewDir), 0.0, 1.0);
5828
- float rim = pow(1.0 - facing, uFalloff);
5829
- float grain = auraHash(n * 5.0) * 0.6 + auraHash(n * 13.0 + 1.7) * 0.4;
5830
- float alpha = clamp((rim * 0.9 + grain * uNoiseStrength) * uOpacity, 0.0, 0.62);
5831
- vec3 color = mix(uCoreColor, mix(uColor, uRimColor, rim), 0.78) * uIntensity;
5832
- gl_FragColor = vec4(color, alpha);
5833
- }
5834
- `
5835
- });
5836
- return materialValue;
5837
- }
5838
- function createThreeTextureFromMaterialInput(THREE, input, usage) {
5839
- if (!input || !("kind" in input) || input.kind !== "aura-procedural-texture")
5840
- return undefined;
5841
- return createThreeProceduralMaterialTexture(THREE, input, usage);
5842
- }
5843
- function createThreeProceduralMaterialTexture(THREE, spec, usage) {
5844
- if (typeof document === "undefined")
5845
- return undefined;
5846
- const canvas = document.createElement("canvas");
5847
- canvas.width = 128;
5848
- canvas.height = 128;
5849
- const context = canvas.getContext("2d");
5850
- if (!context)
5851
- return undefined;
5852
- const image = context.createImageData(canvas.width, canvas.height);
5853
- const scale = Math.max(1, spec.scale);
5854
- const strength = Math.max(0, spec.strength);
5855
- const contrast = spec.contrast ?? 0.5;
5856
- for (let y = 0; y < canvas.height; y += 1) {
5857
- for (let x = 0; x < canvas.width; x += 1) {
5858
- const index = (y * canvas.width + x) * 4;
5859
- const u = x / canvas.width;
5860
- const v = y / canvas.height;
5861
- const grain = proceduralTextureNoise(u, v, scale, spec.texture);
5862
- const stripe = Math.sin((spec.texture === "brushed-metal-anisotropy" ? u : v) * scale * Math.PI * 2) * 0.5 + 0.5;
5863
- const micro = Math.max(0, Math.min(1, grain * (1 - contrast) + stripe * contrast));
5864
- if (usage === "normal") {
5865
- const nx = Math.round(128 + (micro - 0.5) * 96 * strength);
5866
- const ny = Math.round(128 + (grain - 0.5) * 74 * strength);
5867
- image.data[index] = nx;
5868
- image.data[index + 1] = spec.texture === "brushed-metal-anisotropy" ? 128 : ny;
5869
- image.data[index + 2] = 255;
5870
- image.data[index + 3] = 255;
5871
- }
5872
- else {
5873
- const value = Math.round(255 * Math.max(0, Math.min(1, 0.28 + micro * 0.62 * strength)));
5874
- image.data[index] = value;
5875
- image.data[index + 1] = value;
5876
- image.data[index + 2] = value;
5877
- image.data[index + 3] = 255;
5878
- }
5879
- }
5880
- }
5881
- context.putImageData(image, 0, 0);
5882
- const texture = new THREE.CanvasTexture(canvas);
5883
- texture.wrapS = THREE.RepeatWrapping;
5884
- texture.wrapT = THREE.RepeatWrapping;
5885
- texture.repeat.set(1, 1);
5886
- texture.needsUpdate = true;
5887
- return texture;
5888
- }
5889
- function proceduralTextureNoise(u, v, scale, kind) {
5890
- const salt = kind === "fabric-normal"
5891
- ? 19.17
5892
- : kind === "rubber-roughness"
5893
- ? 37.41
5894
- : kind === "brushed-metal-anisotropy"
5895
- ? 53.29
5896
- : 71.83;
5897
- const a = Math.sin((u * scale + salt) * 12.9898 + (v * scale - salt) * 78.233) * 43758.5453;
5898
- const b = Math.sin((u * scale * 0.37 - salt) * 93.989 + (v * scale * 0.61 + salt) * 67.345) * 24634.6345;
5899
- const value = (a - Math.floor(a)) * 0.58 + (b - Math.floor(b)) * 0.42;
5900
- if (kind === "fabric-normal") {
5901
- const weave = (Math.sin(u * scale * Math.PI * 2) + Math.sin(v * scale * Math.PI * 2)) * 0.25 + 0.5;
5902
- return value * 0.44 + weave * 0.56;
5903
- }
5904
- if (kind === "brushed-metal-anisotropy") {
5905
- const brush = Math.sin(u * scale * Math.PI * 4) * 0.18 + 0.5;
5906
- return value * 0.25 + brush * 0.75;
5907
- }
5908
- return value;
5909
- }
5910
- function enhanceLoadedMaterial(materialValue) {
5911
- const materials = Array.isArray(materialValue) ? materialValue : [materialValue];
5912
- for (const entry of materials) {
5913
- if (!entry || typeof entry !== "object")
5914
- continue;
5915
- if ("roughness" in entry && typeof entry.roughness === "number")
5916
- entry.roughness = Math.min(entry.roughness, 0.82);
5917
- if ("metalness" in entry && typeof entry.metalness === "number")
5918
- entry.metalness = Math.max(entry.metalness, 0.02);
5919
- entry.needsUpdate = true;
5920
- }
5921
- }
5922
- function normalizeThreeModel(THREE, modelRoot, node) {
5923
- const box = new THREE.Box3().setFromObject(modelRoot);
5924
- const size = box.getSize(new THREE.Vector3());
5925
- const center = box.getCenter(new THREE.Vector3());
5926
- const fitScale = 1.55 / Math.max(0.001, size.x, size.y, size.z);
5927
- modelRoot.position.sub(new THREE.Vector3(center.x, box.min.y, center.z));
5928
- const pivot = new THREE.Group();
5929
- pivot.add(modelRoot);
5930
- applyThreeTransform(pivot, node, [fitScale, fitScale, fitScale]);
5931
- return pivot;
5932
- }
5933
- function registerThreeModelClipAnimation(THREE, gltf, modelRoot, node, frameUpdaters, disposables) {
5934
- const clips = Array.isArray(gltf.animations) ? gltf.animations : [];
5935
- if (clips.length === 0)
5936
- return;
5937
- const requestedClip = String(node.animation?.clip ?? "").toLowerCase();
5938
- const clip = clips.find((entry) => String(entry.name ?? "").toLowerCase() === requestedClip) ??
5939
- clips.find((entry) => requestedClip && String(entry.name ?? "").toLowerCase().includes(requestedClip)) ??
5940
- clips[0];
5941
- if (!clip)
5942
- return;
5943
- const mixer = new THREE.AnimationMixer(modelRoot);
5944
- const action = mixer.clipAction(clip);
5945
- action.reset();
5946
- action.enabled = true;
5947
- action.play();
5948
- const speed = Math.max(0.05, node.animation?.speed ?? 1);
5949
- frameUpdaters.push((time) => {
5950
- const seconds = node.animation ? resolveAnimationSeconds(node.animation, time) : time * 0.001;
5951
- const duration = Math.max(0.001, clip.duration ?? 1);
5952
- mixer.setTime((seconds * speed) % duration);
5953
- });
5954
- disposables.push({ dispose: () => mixer.uncacheRoot(modelRoot) });
5955
- }
5956
- function applyThreeTransform(object, node, baseScale) {
5957
- const position = node.position ?? [0, 0, 0];
5958
- const rotation = node.rotation ?? [0, 0, 0];
5959
- const scaleValue = typeof node.scale === "number"
5960
- ? [node.scale, node.scale, node.scale]
5961
- : node.scale ?? [1, 1, 1];
5962
- object.position.set(position[0], position[1], position[2]);
5963
- object.rotation.set(rotation[0], rotation[1], rotation[2]);
5964
- object.scale.set(baseScale[0] * scaleValue[0], baseScale[1] * scaleValue[1], baseScale[2] * scaleValue[2]);
5965
- if (node.lookAt)
5966
- object.lookAt(node.lookAt[0], node.lookAt[1], node.lookAt[2]);
5967
- }
5968
- function registerThreeNodeAnimation(object, node, frameUpdaters) {
5969
- if (!node.animation)
5970
- return;
5971
- const basePosition = node.position ?? [0, 0, 0];
5972
- const baseRotation = node.rotation ?? [0, 0, 0];
5973
- const baseScale = typeof object.scale?.clone === "function" ? object.scale.clone() : undefined;
5974
- const speed = Math.max(0.05, node.animation.speed ?? 1);
5975
- frameUpdaters.push((time) => {
5976
- const seconds = resolveAnimationSeconds(node.animation, time);
5977
- if (node.animation?.clip === "orbit") {
5978
- const position = orbitAnimatedPosition(node.animation, basePosition, seconds, speed);
5979
- object.position.x = position[0];
5980
- object.position.y = position[1];
5981
- object.position.z = position[2];
5982
- if (object.rotation)
5983
- object.rotation.y = baseRotation[1] + orbitAnimatedAngle(seconds, speed);
5984
- return;
5985
- }
5986
- if (node.animation?.clip === "bar-height-grow") {
5987
- const duration = Math.max(0.05, node.animation.duration ?? 1.1);
5988
- const linear = Math.min(1, seconds / duration);
5989
- const eased = node.animation.easing === "linear" ? linear : 0.5 - Math.cos(linear * Math.PI) * 0.5;
5990
- if (baseScale && object.scale) {
5991
- object.scale.x = baseScale.x;
5992
- object.scale.y = Math.max(baseScale.y * eased, baseScale.y * 0.06);
5993
- object.scale.z = baseScale.z;
5994
- }
5995
- object.position.y = basePosition[1] * eased;
5996
- return;
5997
- }
5998
- if (node.animation?.clip === "walk") {
5999
- const phase = seconds * speed * Math.PI * 2;
6000
- const travel = Math.sin(seconds * speed * 0.72) * 0.24;
6001
- const bodyBob = Math.abs(Math.sin(phase)) * 0.028;
6002
- object.position.x = basePosition[0] + travel;
6003
- object.position.y = basePosition[1] + bodyBob;
6004
- object.position.z = basePosition[2];
6005
- object.rotation.x = baseRotation[0];
6006
- object.rotation.y = baseRotation[1];
6007
- object.rotation.z = baseRotation[2];
6008
- return;
6009
- }
6010
- if (node.animation?.clip === "float") {
6011
- object.position.y = (node.position?.[1] ?? 0) + Math.sin(seconds * speed) * 0.08;
6012
- object.rotation.y = baseRotation[1] + seconds * speed * 0.28;
6013
- return;
6014
- }
6015
- if (node.animation?.clip === "turntable") {
6016
- object.position.y = basePosition[1];
6017
- object.rotation.y = baseRotation[1] + seconds * speed * 0.72;
6018
- return;
6019
- }
6020
- if (node.animation?.clip === "pulse") {
6021
- const pulse = 1 + Math.sin(seconds * speed * 2) * 0.08;
6022
- object.scale.multiplyScalar(pulse / (object.userData.lastAuraPulse ?? 1));
6023
- object.userData.lastAuraPulse = pulse;
6024
- return;
6025
- }
6026
- object.rotation.y = baseRotation[1] + seconds * speed;
6027
- });
6028
- }
6029
5620
  function resolveAnimationSeconds(animation, time) {
6030
5621
  if (!animation)
6031
5622
  return time / 1000;
@@ -6058,602 +5649,6 @@ function orbitAnimatedPosition(animation, basePosition, seconds, speed) {
6058
5649
  center[2] + Math.sin(angle) * radius
6059
5650
  ];
6060
5651
  }
6061
- function collectThreeEffectNodes(snapshot) {
6062
- return groups.flatten(snapshot.nodes).filter((node) => node.kind === "effect");
6063
- }
6064
- function hasThreePostProcessEffects(effectNodes) {
6065
- return effectNodes.some((node) => node.effect === "bloom" || node.effect === "ambient-occlusion" || node.effect === "contact-occlusion");
6066
- }
6067
- async function createThreePostProcessPipeline(THREE, renderer, threeScene, cameraObject, snapshot) {
6068
- try {
6069
- const [{ EffectComposer }, { RenderPass }] = await Promise.all([
6070
- import("three/examples/jsm/postprocessing/EffectComposer.js"),
6071
- import("three/examples/jsm/postprocessing/RenderPass.js")
6072
- ]);
6073
- const effectNodes = collectThreeEffectNodes(snapshot);
6074
- const bloomEffect = effectNodes.find((node) => node.effect === "bloom");
6075
- const occlusionEffect = effectNodes.find((node) => node.effect === "ambient-occlusion" || node.effect === "contact-occlusion");
6076
- const composer = new EffectComposer(renderer);
6077
- const renderPass = new RenderPass(threeScene, cameraObject);
6078
- const width = renderer.domElement.width || 1;
6079
- const height = renderer.domElement.height || 1;
6080
- let bloomPass;
6081
- let ssaoPass;
6082
- let outputPass;
6083
- let effectPasses = 0;
6084
- const passNames = ["render"];
6085
- const warnings = [];
6086
- composer.setSize(width, height);
6087
- if (typeof composer.setPixelRatio === "function") {
6088
- composer.setPixelRatio(Math.min(2, Math.max(1, window.devicePixelRatio || 1)));
6089
- }
6090
- composer.addPass(renderPass);
6091
- if (occlusionEffect) {
6092
- try {
6093
- const { SSAOPass } = await import("three/examples/jsm/postprocessing/SSAOPass.js");
6094
- ssaoPass = new SSAOPass(threeScene, cameraObject, width, height);
6095
- ssaoPass.kernelRadius = Math.max(4, Math.min(24, Math.round((occlusionEffect.radius ?? 0.64) * 24)));
6096
- ssaoPass.minDistance = 0.001;
6097
- ssaoPass.maxDistance = Math.max(0.04, Math.min(0.28, (occlusionEffect.radius ?? 0.64) * 0.22));
6098
- composer.addPass(ssaoPass);
6099
- effectPasses += 1;
6100
- passNames.push("ssao");
6101
- }
6102
- catch (error) {
6103
- warnings.push("SSAO pass unavailable; contact receiver shadows remain active");
6104
- console.warn("Aura3D SSAO pass unavailable; contact receiver shadows remain active", error);
6105
- }
6106
- }
6107
- if (bloomEffect) {
6108
- try {
6109
- const { UnrealBloomPass } = await import("three/examples/jsm/postprocessing/UnrealBloomPass.js");
6110
- bloomPass = new UnrealBloomPass(new THREE.Vector2(width, height), Math.max(0, Math.min(bloomEffect.maxIntensity ?? 0.92, bloomEffect.intensity ?? 0.35)), Math.max(0.05, Math.min(1.2, bloomEffect.radius ?? 0.38)), Math.max(0, Math.min(1, bloomEffect.threshold ?? 0.7)));
6111
- composer.addPass(bloomPass);
6112
- effectPasses += 1;
6113
- passNames.push("bloom");
6114
- }
6115
- catch (error) {
6116
- warnings.push("UnrealBloomPass unavailable; emissive sprite fallback may be used instead of true bloom");
6117
- console.warn("Aura3D bloom pass unavailable; using direct-render fallback", error);
6118
- }
6119
- }
6120
- if (effectPasses === 0) {
6121
- if (typeof renderPass.dispose === "function")
6122
- renderPass.dispose();
6123
- if (typeof composer.dispose === "function")
6124
- composer.dispose();
6125
- return null;
6126
- }
6127
- try {
6128
- const { OutputPass } = await import("three/examples/jsm/postprocessing/OutputPass.js");
6129
- outputPass = new OutputPass();
6130
- composer.addPass(outputPass);
6131
- passNames.push("output");
6132
- }
6133
- catch {
6134
- warnings.push("OutputPass unavailable; renderer tone mapping remains configured without an explicit output pass");
6135
- // Older Three.js builds may not ship OutputPass; renderer tone mapping remains configured.
6136
- }
6137
- return {
6138
- passNames,
6139
- warnings,
6140
- render: () => composer.render(),
6141
- setSize: (nextWidth, nextHeight) => {
6142
- composer.setSize(nextWidth, nextHeight);
6143
- if (typeof bloomPass?.setSize === "function")
6144
- bloomPass.setSize(nextWidth, nextHeight);
6145
- if (typeof ssaoPass?.setSize === "function")
6146
- ssaoPass.setSize(nextWidth, nextHeight);
6147
- },
6148
- dispose: () => {
6149
- if (typeof bloomPass?.dispose === "function")
6150
- bloomPass.dispose();
6151
- if (typeof ssaoPass?.dispose === "function")
6152
- ssaoPass.dispose();
6153
- if (typeof outputPass?.dispose === "function")
6154
- outputPass.dispose();
6155
- if (typeof renderPass.dispose === "function")
6156
- renderPass.dispose();
6157
- if (typeof composer.dispose === "function")
6158
- composer.dispose();
6159
- }
6160
- };
6161
- }
6162
- catch (error) {
6163
- console.warn("Aura3D postprocess composer unavailable; falling back to direct render", error);
6164
- return null;
6165
- }
6166
- }
6167
- function createThreeContactShadowReceiver(THREE, snapshot, effect) {
6168
- const footprint = estimateSceneFootprint(snapshot.nodes);
6169
- const geometry = new THREE.PlaneGeometry(footprint.width, footprint.depth).rotateX(-Math.PI / 2);
6170
- const shadowMaterial = new THREE.ShadowMaterial({
6171
- color: new THREE.Color(effect.color ?? "#020617"),
6172
- opacity: Math.max(0.08, Math.min(0.42, effect.intensity ?? 0.28)),
6173
- transparent: true,
6174
- depthWrite: false
6175
- });
6176
- const receiver = new THREE.Mesh(geometry, shadowMaterial);
6177
- receiver.name = "aura-pixel-contact-shadow-receiver";
6178
- receiver.position.set(footprint.centerX, estimateSceneFloorTop(snapshot.nodes) + 0.006, footprint.centerZ);
6179
- receiver.receiveShadow = true;
6180
- receiver.castShadow = false;
6181
- return receiver;
6182
- }
6183
- function estimateSceneFloorTop(nodes) {
6184
- let top = 0;
6185
- for (const node of groups.flatten(nodes)) {
6186
- if (node.kind !== "primitive")
6187
- continue;
6188
- const name = (node.name ?? "").toLowerCase();
6189
- const isFloorLike = name.includes("floor") || name.includes("ground") || name.includes("stage") || name.includes("course") || name.includes("court");
6190
- if (!isFloorLike)
6191
- continue;
6192
- const y = node.position?.[1] ?? 0;
6193
- if (node.primitive === "box") {
6194
- const scale = scaleToVec3(node.scale);
6195
- top = Math.max(top, y + scale[1] * 0.5);
6196
- continue;
6197
- }
6198
- if (node.primitive === "plane") {
6199
- top = Math.max(top, y);
6200
- }
6201
- }
6202
- return top;
6203
- }
6204
- function estimateSceneFootprint(nodes) {
6205
- let minX = Number.POSITIVE_INFINITY;
6206
- let maxX = Number.NEGATIVE_INFINITY;
6207
- let minZ = Number.POSITIVE_INFINITY;
6208
- let maxZ = Number.NEGATIVE_INFINITY;
6209
- for (const node of groups.flatten(nodes)) {
6210
- if (node.kind !== "primitive" && node.kind !== "model")
6211
- continue;
6212
- const name = String(node.name ?? "").toLowerCase();
6213
- if (node.kind === "primitive" && node.primitive === "plane")
6214
- continue;
6215
- if (name.includes("sky") || name.includes("backdrop") || name.includes("wall") || name.includes("ground plane"))
6216
- continue;
6217
- const position = node.position ?? [0, 0, 0];
6218
- const scaleValue = typeof node.scale === "number"
6219
- ? [node.scale, node.scale, node.scale]
6220
- : node.scale ?? [1, 1, 1];
6221
- const baseSize = node.kind === "primitive" ? primitiveSize(node) : node.asset.bounds ?? [1, 1, 1];
6222
- const width = Math.max(0.08, Math.abs(baseSize[0] * scaleValue[0]));
6223
- const depth = Math.max(0.08, Math.abs(baseSize[2] * scaleValue[2]));
6224
- minX = Math.min(minX, position[0] - width * 0.55);
6225
- maxX = Math.max(maxX, position[0] + width * 0.55);
6226
- minZ = Math.min(minZ, position[2] - depth * 0.55);
6227
- maxZ = Math.max(maxZ, position[2] + depth * 0.55);
6228
- }
6229
- if (!Number.isFinite(minX) || !Number.isFinite(minZ)) {
6230
- return { centerX: 0, centerZ: 0, width: 4, depth: 3 };
6231
- }
6232
- const width = Math.max(2, Math.min(8, maxX - minX + 0.9));
6233
- const depth = Math.max(1.8, Math.min(7, maxZ - minZ + 0.9));
6234
- return { centerX: (minX + maxX) * 0.5, centerZ: (minZ + maxZ) * 0.5, width, depth };
6235
- }
6236
- function createThreeBloom(THREE, snapshot, effect) {
6237
- const group = new THREE.Group();
6238
- const intensity = Math.max(0.05, Math.min(1.4, effect.intensity ?? 0.35));
6239
- const texture = createThreeRadialTexture(THREE, effect.color ?? "#ffffff");
6240
- group.userData.texture = texture;
6241
- const anchors = collectBloomAnchors(snapshot);
6242
- anchors.forEach((anchor, index) => {
6243
- const materialValue = new THREE.SpriteMaterial({
6244
- map: texture,
6245
- color: new THREE.Color(anchor.color ?? effect.color ?? "#ffffff"),
6246
- transparent: true,
6247
- opacity: Math.min(0.46, 0.12 + intensity * anchor.opacity),
6248
- depthWrite: false,
6249
- depthTest: false,
6250
- blending: THREE.AdditiveBlending
6251
- });
6252
- const sprite = new THREE.Sprite(materialValue);
6253
- sprite.name = `aura-bloom-halo-${index}`;
6254
- sprite.position.set(anchor.position[0], anchor.position[1], anchor.position[2]);
6255
- sprite.scale.setScalar(anchor.size * (0.85 + intensity * 1.4));
6256
- group.add(sprite);
6257
- });
6258
- group.userData.update = (time) => {
6259
- const pulse = 1 + Math.sin(time * 0.0012) * 0.035 * intensity;
6260
- group.children.forEach((child, index) => {
6261
- const base = anchors[index]?.size ?? 1;
6262
- child.scale.setScalar(base * (0.85 + intensity * 1.4) * pulse);
6263
- });
6264
- };
6265
- return group;
6266
- }
6267
- function collectBloomAnchors(snapshot) {
6268
- const anchors = [];
6269
- for (const node of snapshot.nodes) {
6270
- if (node.kind === "primitive" && node.material?.emissive) {
6271
- const size = primitiveSize(node);
6272
- const scaleValue = typeof node.scale === "number" ? [node.scale, node.scale, node.scale] : node.scale ?? [1, 1, 1];
6273
- anchors.push({
6274
- position: node.position ?? [0, 0.65, -0.6],
6275
- size: Math.max(0.34, Math.max(size[0] * scaleValue[0], size[1] * scaleValue[1], size[2] * scaleValue[2]) * 1.45),
6276
- opacity: 0.24,
6277
- color: node.material.emissive
6278
- });
6279
- }
6280
- if (node.kind === "light" && node.light === "point") {
6281
- anchors.push({
6282
- position: node.position ?? [0, 1.4, 0.6],
6283
- size: Math.max(0.55, 0.28 + node.intensity * 0.32),
6284
- opacity: 0.18,
6285
- color: node.color
6286
- });
6287
- }
6288
- }
6289
- const modelNode = snapshot.nodes.find((node) => node.kind === "model");
6290
- if (modelNode) {
6291
- const position = modelNode.position ?? [0, 0.42, -0.65];
6292
- anchors.push({
6293
- position: [position[0], position[1] + 0.42, position[2] + 0.08],
6294
- size: 1.2,
6295
- opacity: 0.12
6296
- });
6297
- }
6298
- return anchors.length > 0 ? anchors.slice(0, 10) : [{ position: [0, 0.75, -0.75], size: 1.6, opacity: 0.16 }];
6299
- }
6300
- function createThreeRadialTexture(THREE, color) {
6301
- const canvas = document.createElement("canvas");
6302
- canvas.width = 128;
6303
- canvas.height = 128;
6304
- const context = canvas.getContext("2d");
6305
- if (context) {
6306
- const gradient = context.createRadialGradient(64, 64, 0, 64, 64, 64);
6307
- gradient.addColorStop(0, toAlphaColor(String(color), 0.95));
6308
- gradient.addColorStop(0.18, toAlphaColor(String(color), 0.42));
6309
- gradient.addColorStop(0.52, toAlphaColor(String(color), 0.14));
6310
- gradient.addColorStop(1, toAlphaColor(String(color), 0));
6311
- context.fillStyle = gradient;
6312
- context.fillRect(0, 0, canvas.width, canvas.height);
6313
- }
6314
- const texture = new THREE.CanvasTexture(canvas);
6315
- texture.colorSpace = THREE.SRGBColorSpace;
6316
- return texture;
6317
- }
6318
- function createThreeRain(THREE, effect) {
6319
- const group = new THREE.Group();
6320
- const density = Math.max(0.2, Math.min(1.6, effect.density ?? effect.intensity ?? 0.72));
6321
- const intensity = Math.max(0.1, Math.min(1.4, effect.intensity ?? 0.4));
6322
- const color = effect.color ?? "#c9e8ff";
6323
- const wind = effect.wind ?? [-0.32, -5.4, -0.16];
6324
- const requestedCount = effect.particleCount ?? Math.round(520 * density);
6325
- const dummy = new THREE.Object3D();
6326
- const layers = [];
6327
- const makeLayer = (name, count, zMin, zMax, length, width, opacity, speed) => {
6328
- const geometry = new THREE.PlaneGeometry(width, length);
6329
- const materialValue = new THREE.MeshBasicMaterial({
6330
- color: new THREE.Color(color),
6331
- transparent: true,
6332
- opacity,
6333
- depthWrite: false,
6334
- side: THREE.DoubleSide,
6335
- blending: THREE.AdditiveBlending
6336
- });
6337
- const mesh = new THREE.InstancedMesh(geometry, materialValue, count);
6338
- mesh.name = name;
6339
- mesh.userData.entries = [];
6340
- for (let index = 0; index < count; index += 1) {
6341
- const entry = {
6342
- x: seededRange(index, 11, -4.2, 4.2),
6343
- y: seededRange(index, 23, 0.22, 3.25),
6344
- z: seededRange(index, 37, zMin, zMax),
6345
- scale: seededRange(index, 41, 0.72, 1.32),
6346
- phase: seededRange(index, 53, 0, 1)
6347
- };
6348
- mesh.userData.entries.push(entry);
6349
- applyRainInstance(dummy, mesh, index, entry, 0, length, wind, speed);
6350
- }
6351
- mesh.instanceMatrix.needsUpdate = true;
6352
- layers.push({ mesh, length, speed });
6353
- group.add(mesh);
6354
- };
6355
- makeLayer("aura-rain-background-volume", Math.round(requestedCount * 0.42), -3.8, -1.1, 0.36, 0.018, Math.min(0.34, intensity * 0.36), 0.6);
6356
- makeLayer("aura-rain-midground-volume", Math.round(requestedCount * 0.36), -1.3, 0.9, 0.58, 0.026, Math.min(0.48, intensity * 0.54), 0.92);
6357
- makeLayer("aura-rain-foreground-streaks", Math.round(requestedCount * 0.22), 0.75, 2.35, 0.82, 0.034, Math.min(0.66, intensity * 0.72), 1.22);
6358
- const splashMaterial = new THREE.MeshBasicMaterial({
6359
- color: new THREE.Color(color),
6360
- transparent: true,
6361
- opacity: effect.splashes === false ? 0 : 0.48,
6362
- depthWrite: false,
6363
- side: THREE.DoubleSide,
6364
- blending: THREE.AdditiveBlending
6365
- });
6366
- const splashGeometry = new THREE.RingGeometry(0.028, 0.046, 20);
6367
- const splashCount = effect.splashes === false ? 0 : Math.round(64 * density);
6368
- const splashes = new THREE.InstancedMesh(splashGeometry, splashMaterial, splashCount);
6369
- splashes.name = "aura-rain-floor-splash-ripples";
6370
- splashes.userData.entries = [];
6371
- for (let index = 0; index < splashCount; index += 1) {
6372
- const entry = {
6373
- x: seededRange(index, 71, -3.2, 3.2),
6374
- z: seededRange(index, 89, -1.95, 1.5),
6375
- scale: seededRange(index, 97, 0.58, 1.65),
6376
- phase: seededRange(index, 109, 0, 1)
6377
- };
6378
- splashes.userData.entries.push(entry);
6379
- applySplashInstance(dummy, splashes, index, entry, 0);
6380
- }
6381
- splashes.instanceMatrix.needsUpdate = true;
6382
- group.add(splashes);
6383
- if (effect.mist !== false) {
6384
- const mistTexture = createThreeRadialTexture(THREE, color);
6385
- group.userData.mistTexture = mistTexture;
6386
- const mistMaterial = new THREE.SpriteMaterial({
6387
- map: mistTexture,
6388
- color: new THREE.Color(color),
6389
- transparent: true,
6390
- opacity: Math.min(0.18, 0.08 + intensity * 0.08),
6391
- depthWrite: false,
6392
- depthTest: false,
6393
- blending: THREE.AdditiveBlending
6394
- });
6395
- for (let index = 0; index < 5; index += 1) {
6396
- const sprite = new THREE.Sprite(mistMaterial.clone());
6397
- sprite.name = `aura-rain-mist-bank-${index}`;
6398
- sprite.position.set(-2.4 + index * 1.2, 0.32 + (index % 2) * 0.12, -1.5 + (index % 3) * 0.48);
6399
- sprite.scale.set(1.4 + index * 0.18, 0.42, 1);
6400
- group.add(sprite);
6401
- }
6402
- }
6403
- group.userData.update = (time) => {
6404
- const seconds = time / 1000;
6405
- for (const layer of layers) {
6406
- const entries = layer.mesh.userData.entries;
6407
- entries.forEach((entry, index) => {
6408
- applyRainInstance(dummy, layer.mesh, index, entry, seconds, layer.length, wind, layer.speed * (effect.speed ?? 1));
6409
- });
6410
- layer.mesh.instanceMatrix.needsUpdate = true;
6411
- }
6412
- const splashEntries = splashes.userData.entries;
6413
- splashEntries.forEach((entry, index) => applySplashInstance(dummy, splashes, index, entry, seconds));
6414
- splashes.instanceMatrix.needsUpdate = true;
6415
- };
6416
- return group;
6417
- }
6418
- function createThreeParticles(THREE, effect) {
6419
- const isFountain = effect.emitter === "fountain";
6420
- const requestedCount = effect.particleCount ?? 900;
6421
- const count = isFountain ? Math.max(24, Math.min(900, requestedCount)) : Math.max(120, Math.min(6000, requestedCount));
6422
- const radius = Math.max(0.1, effect.radius ?? 1.15);
6423
- const height = Math.max(0.2, effect.height ?? 2.4);
6424
- const intensity = Math.max(0.1, Math.min(1.8, effect.intensity ?? 0.8));
6425
- const materialMode = effect.materialMode ?? "soft-alpha";
6426
- const turbulence = Math.max(0, Math.min(1, effect.turbulence ?? effect.noise ?? 0));
6427
- const fountainLayer = !isFountain
6428
- ? "plume"
6429
- : effect.materialMode === "soft-alpha" || effect.name?.includes("mist")
6430
- ? "mist"
6431
- : effect.name?.includes("splash") || effect.name?.includes("collision")
6432
- ? "splash"
6433
- : "plume";
6434
- const texturedBillboard = effect.texturedBillboard !== false && !isFountain;
6435
- const multicolor = effect.name?.includes("multicolor") || effect.emitter === "swirl";
6436
- const lifetimeColor = isFountain || effect.name?.includes("lifetime");
6437
- const geometry = new THREE.BufferGeometry();
6438
- const positions = new Float32Array(count * 3);
6439
- const colors = new Float32Array(count * 3);
6440
- const baseColor = new THREE.Color(effect.color ?? "#7dfcff");
6441
- const accentColor = new THREE.Color(isFountain ? "#bae6fd" : "#ffd166");
6442
- const defaultLifetimeRamp = isFountain
6443
- ? ["#fff7ad", "#fef08a", "#fb923c", "#60a5fa", "#38bdf8", "#fb7185"]
6444
- : ["#fff7ad", "#ffd166", baseColor.getStyle(), "#ff7ad9", "#60a5fa"];
6445
- const lifetimeRampSource = effect.lifetimeColorRamp?.length ? effect.lifetimeColorRamp : defaultLifetimeRamp;
6446
- const lifetimeRamp = lifetimeRampSource.map((value) => new THREE.Color(value));
6447
- const scratchColor = new THREE.Color();
6448
- const sizeCurve = effect.sizeOverLife ?? [0.35, 1, 0.58];
6449
- const alphaCurve = effect.alphaOverLife ?? [0, 0.92, materialMode === "smoke" || materialMode === "dust" ? 0.18 : 0];
6450
- const writeColor = (index, seconds) => {
6451
- if (lifetimeColor) {
6452
- const life = getParticleLife(index, seconds, effect.emitter ?? "swirl");
6453
- const maxIndex = Math.max(0, lifetimeRamp.length - 1);
6454
- if (isFountain) {
6455
- const rampIndex = Math.min(maxIndex, (index + Math.floor(seededRange(index, 283, 0, 1) * lifetimeRamp.length)) % lifetimeRamp.length);
6456
- const nextIndex = Math.min(maxIndex, (rampIndex + 1) % lifetimeRamp.length);
6457
- scratchColor.copy(lifetimeRamp[rampIndex]).lerp(lifetimeRamp[nextIndex], seededRange(index, 307, 0, 0.16));
6458
- }
6459
- else {
6460
- const colorLife = isFountain
6461
- ? seededRange(index, 283, 0, 0.92) * 0.82 + life * 0.18
6462
- : life;
6463
- const scaledLife = Math.min(maxIndex, Math.max(0, colorLife * maxIndex));
6464
- const rampIndex = Math.min(maxIndex, Math.floor(scaledLife));
6465
- const nextIndex = Math.min(maxIndex, rampIndex + 1);
6466
- scratchColor.copy(lifetimeRamp[rampIndex]).lerp(lifetimeRamp[nextIndex], scaledLife - rampIndex);
6467
- }
6468
- }
6469
- else if (multicolor) {
6470
- scratchColor.setHSL(seededRange(index, 173, 0, 1), 0.84, 0.64);
6471
- }
6472
- else {
6473
- scratchColor.copy(baseColor).lerp(accentColor, seededRange(index, 173, 0, 0.42));
6474
- }
6475
- colors[index * 3] = scratchColor.r;
6476
- colors[index * 3 + 1] = scratchColor.g;
6477
- colors[index * 3 + 2] = scratchColor.b;
6478
- };
6479
- for (let index = 0; index < count; index += 1) {
6480
- writeParticlePosition(positions, index, 0, effect.emitter ?? "swirl", radius, height, index, turbulence, effect.gravity ?? 0, effect.groundCollision ?? false, fountainLayer);
6481
- writeColor(index, 0);
6482
- }
6483
- geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
6484
- geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
6485
- const spriteTexture = texturedBillboard ? createParticleSpriteTexture(THREE, materialMode) : undefined;
6486
- const baseSize = isFountain ? 0.07 + intensity * 0.032 : (multicolor ? 0.042 : 0.028) + intensity * (multicolor ? 0.024 : 0.018);
6487
- const opacity = isFountain
6488
- ? Math.min(0.94, Math.max(0.48, alphaCurve[1] ?? (0.58 + intensity * 0.24)))
6489
- : Math.min(0.98, Math.max(0.12, alphaCurve[1] ?? ((multicolor ? 0.62 : 0.48) + intensity * 0.22)));
6490
- const blending = isFountain || materialMode === "soft-alpha" || materialMode === "smoke" || materialMode === "dust" ? THREE.NormalBlending : THREE.AdditiveBlending;
6491
- if (spriteTexture) {
6492
- const plane = new THREE.PlaneGeometry(1, 1);
6493
- const materialValue = new THREE.MeshBasicMaterial({
6494
- map: spriteTexture,
6495
- vertexColors: true,
6496
- transparent: true,
6497
- opacity,
6498
- depthWrite: false,
6499
- alphaTest: 0.02,
6500
- blending,
6501
- side: THREE.DoubleSide
6502
- });
6503
- const mesh = new THREE.InstancedMesh(plane, materialValue, count);
6504
- const dummy = new THREE.Object3D();
6505
- const instanceColor = new THREE.Color();
6506
- const writeInstance = (index, seconds) => {
6507
- const offset = index * 3;
6508
- const life = getParticleLife(index, seconds, effect.emitter ?? "swirl");
6509
- const sizeLife = life < 0.5
6510
- ? (sizeCurve[0] ?? 0.35) + ((sizeCurve[1] ?? 1) - (sizeCurve[0] ?? 0.35)) * (life / 0.5)
6511
- : (sizeCurve[1] ?? 1) + ((sizeCurve[2] ?? 0.58) - (sizeCurve[1] ?? 1)) * ((life - 0.5) / 0.5);
6512
- const size = baseSize * 3.2 * Math.max(0.22, sizeLife);
6513
- dummy.position.set(positions[offset], positions[offset + 1], positions[offset + 2]);
6514
- dummy.rotation.set(0, 0, seededRange(index, 911, -Math.PI, Math.PI) + seconds * 0.18);
6515
- const elongatedWater = isFountain && materialMode !== "soft-alpha";
6516
- dummy.scale.set(size * (elongatedWater ? 1.05 : 1), size * (elongatedWater ? 1.18 : 1), size);
6517
- dummy.updateMatrix();
6518
- mesh.setMatrixAt(index, dummy.matrix);
6519
- instanceColor.setRGB(colors[offset], colors[offset + 1], colors[offset + 2]);
6520
- mesh.setColorAt(index, instanceColor);
6521
- };
6522
- for (let index = 0; index < count; index += 1)
6523
- writeInstance(index, 0);
6524
- mesh.name = effect.name ?? "aura-instanced-textured-particle-system";
6525
- mesh.instanceMatrix.needsUpdate = true;
6526
- if (mesh.instanceColor)
6527
- mesh.instanceColor.needsUpdate = true;
6528
- mesh.userData.update = (time) => {
6529
- const seconds = time / 1000 * (effect.speed ?? 1);
6530
- for (let index = 0; index < count; index += 1) {
6531
- writeParticlePosition(positions, index, seconds, effect.emitter ?? "swirl", radius, height, index, turbulence, effect.gravity ?? 0, effect.groundCollision ?? false, fountainLayer);
6532
- if (lifetimeColor)
6533
- writeColor(index, seconds);
6534
- writeInstance(index, seconds);
6535
- }
6536
- mesh.instanceMatrix.needsUpdate = true;
6537
- if (mesh.instanceColor)
6538
- mesh.instanceColor.needsUpdate = true;
6539
- };
6540
- return mesh;
6541
- }
6542
- if (isFountain && effect.texturedBillboard === false) {
6543
- const sphere = new THREE.IcosahedronGeometry(1, 1);
6544
- const sphereVertexColors = new Float32Array(sphere.getAttribute("position").count * 3);
6545
- sphereVertexColors.fill(1);
6546
- sphere.setAttribute("color", new THREE.BufferAttribute(sphereVertexColors, 3));
6547
- const materialValue = new THREE.MeshBasicMaterial({
6548
- color: "#ffffff",
6549
- vertexColors: true,
6550
- transparent: fountainLayer === "mist",
6551
- opacity: fountainLayer === "mist" ? 0.2 : 1,
6552
- depthWrite: fountainLayer !== "mist",
6553
- depthTest: true
6554
- });
6555
- materialValue.toneMapped = false;
6556
- const mesh = new THREE.InstancedMesh(sphere, materialValue, count);
6557
- mesh.frustumCulled = false;
6558
- const dummy = new THREE.Object3D();
6559
- const instanceColor = new THREE.Color();
6560
- const writeInstance = (index, seconds) => {
6561
- const offset = index * 3;
6562
- const life = getParticleLife(index, seconds, effect.emitter ?? "swirl");
6563
- const sizeLife = life < 0.5
6564
- ? (sizeCurve[0] ?? 0.35) + ((sizeCurve[1] ?? 1) - (sizeCurve[0] ?? 0.35)) * (life / 0.5)
6565
- : (sizeCurve[1] ?? 1) + ((sizeCurve[2] ?? 0.58) - (sizeCurve[1] ?? 1)) * ((life - 0.5) / 0.5);
6566
- const layerScale = fountainLayer === "mist" ? 0.22 : fountainLayer === "splash" ? 0.38 : 0.44;
6567
- const size = baseSize * layerScale * Math.max(0.24, sizeLife);
6568
- dummy.position.set(positions[offset], positions[offset + 1], positions[offset + 2]);
6569
- dummy.rotation.set(0, seededRange(index, 911, -Math.PI, Math.PI), seededRange(index, 917, -Math.PI, Math.PI));
6570
- dummy.scale.set(size, size, size);
6571
- dummy.updateMatrix();
6572
- mesh.setMatrixAt(index, dummy.matrix);
6573
- instanceColor.setRGB(colors[offset], colors[offset + 1], colors[offset + 2]);
6574
- mesh.setColorAt(index, instanceColor);
6575
- };
6576
- for (let index = 0; index < count; index += 1)
6577
- writeInstance(index, 0);
6578
- mesh.name = effect.name ?? "aura-instanced-fountain-particle-system";
6579
- mesh.instanceMatrix.needsUpdate = true;
6580
- if (mesh.instanceColor)
6581
- mesh.instanceColor.needsUpdate = true;
6582
- mesh.userData.update = (time) => {
6583
- const seconds = time / 1000 * (effect.speed ?? 1);
6584
- for (let index = 0; index < count; index += 1) {
6585
- writeParticlePosition(positions, index, seconds, effect.emitter ?? "swirl", radius, height, index, turbulence, effect.gravity ?? 0, effect.groundCollision ?? false, fountainLayer);
6586
- if (lifetimeColor)
6587
- writeColor(index, seconds);
6588
- writeInstance(index, seconds);
6589
- }
6590
- mesh.instanceMatrix.needsUpdate = true;
6591
- if (mesh.instanceColor)
6592
- mesh.instanceColor.needsUpdate = true;
6593
- };
6594
- return mesh;
6595
- }
6596
- const pointsTexture = isFountain ? createParticleSpriteTexture(THREE, materialMode) : undefined;
6597
- const materialValue = new THREE.PointsMaterial({
6598
- map: pointsTexture,
6599
- size: isFountain ? baseSize * (fountainLayer === "mist" ? 0.44 : fountainLayer === "splash" ? 0.82 : 1.08) * Math.max(0.52, sizeCurve[1] ?? 1) : baseSize * Math.max(0.25, sizeCurve[1] ?? 1),
6600
- vertexColors: true,
6601
- transparent: true,
6602
- opacity: isFountain ? Math.min(fountainLayer === "mist" ? 0.2 : fountainLayer === "splash" ? 0.78 : 0.9, opacity) : opacity,
6603
- depthWrite: false,
6604
- depthTest: true,
6605
- alphaTest: isFountain ? 0.035 : 0,
6606
- blending
6607
- });
6608
- const points = new THREE.Points(geometry, materialValue);
6609
- points.name = effect.name ?? "aura-particle-system";
6610
- points.userData.update = (time) => {
6611
- const seconds = time / 1000 * (effect.speed ?? 1);
6612
- const attribute = geometry.getAttribute("position");
6613
- const values = attribute.array;
6614
- const colorAttribute = geometry.getAttribute("color");
6615
- for (let index = 0; index < count; index += 1) {
6616
- writeParticlePosition(values, index, seconds, effect.emitter ?? "swirl", radius, height, index, turbulence, effect.gravity ?? 0, effect.groundCollision ?? false, fountainLayer);
6617
- if (lifetimeColor)
6618
- writeColor(index, seconds);
6619
- }
6620
- attribute.needsUpdate = true;
6621
- if (lifetimeColor)
6622
- colorAttribute.needsUpdate = true;
6623
- };
6624
- return points;
6625
- }
6626
- function createParticleSpriteTexture(THREE, mode) {
6627
- if (typeof document === "undefined")
6628
- return undefined;
6629
- const canvas = document.createElement("canvas");
6630
- canvas.width = 32;
6631
- canvas.height = 32;
6632
- const context = canvas.getContext("2d");
6633
- if (!context)
6634
- return undefined;
6635
- const gradient = context.createRadialGradient(16, 16, mode === "star" || mode === "spark" ? 1.5 : 0, 16, 16, 15.5);
6636
- const core = mode === "smoke" || mode === "dust" ? "rgba(220,230,240,0.68)" : "rgba(255,255,255,1)";
6637
- const mid = mode === "splash" ? "rgba(125,211,252,0.72)" : mode === "smoke" ? "rgba(148,163,184,0.32)" : "rgba(255,230,150,0.5)";
6638
- gradient.addColorStop(0, core);
6639
- gradient.addColorStop(0.42, mid);
6640
- gradient.addColorStop(1, "rgba(255,255,255,0)");
6641
- context.fillStyle = gradient;
6642
- context.fillRect(0, 0, 32, 32);
6643
- if (mode === "star" || mode === "spark") {
6644
- context.strokeStyle = "rgba(255,255,255,0.82)";
6645
- context.lineWidth = 1.2;
6646
- context.beginPath();
6647
- context.moveTo(16, 3);
6648
- context.lineTo(16, 29);
6649
- context.moveTo(3, 16);
6650
- context.lineTo(29, 16);
6651
- context.stroke();
6652
- }
6653
- const texture = new THREE.CanvasTexture(canvas);
6654
- texture.needsUpdate = true;
6655
- return texture;
6656
- }
6657
5652
  function getParticleLife(seedIndex, seconds, emitter) {
6658
5653
  if (emitter !== "fountain")
6659
5654
  return (seededRange(seedIndex, 181, 0, 1) + seconds * 0.18) % 1;
@@ -6714,23 +5709,6 @@ function writeParticlePosition(positions, index, seconds, emitter, radius, heigh
6714
5709
  positions[index * 3 + 1] = y;
6715
5710
  positions[index * 3 + 2] = z;
6716
5711
  }
6717
- function applyRainInstance(dummy, mesh, index, entry, seconds, length, wind, speed) {
6718
- const fall = ((seconds * speed + entry.phase) % 1) * 3.6;
6719
- const y = 3.15 - ((3.15 - entry.y + fall) % 3.4);
6720
- dummy.position.set(entry.x + wind[0] * fall * 0.06, y, entry.z + wind[2] * fall * 0.08);
6721
- dummy.rotation.set(0, 0, -0.17 + wind[0] * 0.055);
6722
- dummy.scale.set(entry.scale, entry.scale, entry.scale);
6723
- dummy.updateMatrix();
6724
- mesh.setMatrixAt(index, dummy.matrix);
6725
- }
6726
- function applySplashInstance(dummy, mesh, index, entry, seconds) {
6727
- const ripple = 0.35 + (((seconds * 1.8 + entry.phase) % 1) * 1.25);
6728
- dummy.position.set(entry.x, 0.022, entry.z);
6729
- dummy.rotation.set(-Math.PI / 2, 0, seededRange(index, 131, 0, Math.PI));
6730
- dummy.scale.set(entry.scale * ripple, entry.scale * ripple, entry.scale * ripple);
6731
- dummy.updateMatrix();
6732
- mesh.setMatrixAt(index, dummy.matrix);
6733
- }
6734
5712
  function seededRange(index, salt, min, max) {
6735
5713
  const value = Math.sin((index + 1) * 12.9898 + salt * 78.233) * 43758.5453;
6736
5714
  const normalized = value - Math.floor(value);
@@ -6775,32 +5753,11 @@ function resolveCameraEye(snapshot, cameraSpec, time) {
6775
5753
  }
6776
5754
  return eye;
6777
5755
  }
6778
- function updateThreeCamera(THREE, cameraObject, snapshot, canvas, time) {
6779
- const cameraSpec = snapshot.camera;
6780
- const target = resolveCameraTarget(snapshot, cameraSpec);
6781
- const eye = resolveCameraEye(snapshot, cameraSpec, time);
6782
- cameraObject.aspect = canvas.width / Math.max(1, canvas.height);
6783
- cameraObject.fov = cameraSpec.fov ?? 45;
6784
- cameraObject.position.set(eye[0], eye[1], eye[2]);
6785
- cameraObject.lookAt(new THREE.Vector3(target[0], target[1], target[2]));
6786
- cameraObject.updateProjectionMatrix();
6787
- }
6788
- function disposeThreeResource(item) {
6789
- if (!item)
6790
- return;
6791
- if (Array.isArray(item)) {
6792
- for (const entry of item)
6793
- disposeThreeResource(entry);
6794
- return;
6795
- }
6796
- if (typeof item.traverse === "function") {
6797
- item.traverse((child) => {
6798
- disposeThreeResource(child.geometry);
6799
- disposeThreeResource(child.material);
6800
- });
6801
- }
6802
- if (typeof item.dispose === "function")
6803
- item.dispose();
5756
+ function collectRuntimeEffectNodes(snapshot) {
5757
+ return groups.flatten(snapshot.nodes).filter((node) => node.kind === "effect");
5758
+ }
5759
+ function hasRuntimePostProcessEffects(effectNodes) {
5760
+ return effectNodes.some((node) => node.effect === "bloom" || node.effect === "ambient-occlusion" || node.effect === "contact-occlusion");
6804
5761
  }
6805
5762
  async function createWebGLSceneRenderer(canvas, snapshot) {
6806
5763
  const gl = canvas.getContext("webgl2", { antialias: true, preserveDrawingBuffer: true });
@@ -6824,10 +5781,10 @@ async function createWebGLSceneRenderer(canvas, snapshot) {
6824
5781
  const background = colorToClearColor(snapshot.background);
6825
5782
  gl.enable(gl.DEPTH_TEST);
6826
5783
  gl.disable(gl.CULL_FACE);
6827
- const requestedEffectNodes = collectThreeEffectNodes(snapshot);
5784
+ const requestedEffectNodes = collectRuntimeEffectNodes(snapshot);
6828
5785
  const runtimeRendererDiagnostics = createRendererDiagnosticReport(snapshot, {
6829
5786
  mounted: true,
6830
- backend: "webgl2-fallback",
5787
+ backend: "webgl2-agent-runtime",
6831
5788
  postprocess: {
6832
5789
  renderPass: false,
6833
5790
  outputPass: false,
@@ -6836,9 +5793,9 @@ async function createWebGLSceneRenderer(canvas, snapshot) {
6836
5793
  contactOcclusionReceiver: false,
6837
5794
  pixelBacked: false,
6838
5795
  actualPasses: [],
6839
- fallbackPasses: hasThreePostProcessEffects(requestedEffectNodes) ? ["minimal-webgl-direct-render"] : []
5796
+ fallbackPasses: hasRuntimePostProcessEffects(requestedEffectNodes) ? ["webgl2-direct-render"] : []
6840
5797
  },
6841
- warnings: ["Minimal WebGL fallback does not run the Three.js composer, PMREM environment, shadow maps, or GLB animation mixers."]
5798
+ warnings: ["Aura3D WebGL2 agent runtime renders typed models, primitives, and basic effects; advanced postprocess, environment prefiltering, shadow maps, and GLB animation mixers are reported as unsupported until the production renderer adapter covers them."]
6842
5799
  });
6843
5800
  return {
6844
5801
  diagnostics: runtimeRendererDiagnostics,
@@ -7956,7 +6913,7 @@ function normalizeSceneSnapshot(value) {
7956
6913
  }
7957
6914
  function resolveCanvas(target) {
7958
6915
  if (!target) {
7959
- throw new AuraRuntimeError("missing-canvas", "Aura3D could not mount because the app target was null or undefined. Suggested fix: pass a selector like createAuraApp(\"#app\", ...) or check document.querySelector before mounting.");
6916
+ return undefined;
7960
6917
  }
7961
6918
  if (typeof target === "string") {
7962
6919
  if (typeof document === "undefined") {