@aura3d/engine 3.0.0 → 3.0.1
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.
- package/README.md +24 -5
- package/dist/animation/AnimationMixer.d.ts +4 -1
- package/dist/animation/AnimationMixer.d.ts.map +1 -1
- package/dist/animation/AnimationMixer.js +36 -29
- package/dist/animation/AnimationMixer.js.map +1 -1
- package/dist/animation/FootIk.d.ts +9 -0
- package/dist/animation/FootIk.d.ts.map +1 -1
- package/dist/animation/FootIk.js +22 -8
- package/dist/animation/FootIk.js.map +1 -1
- package/dist/animation/HumanoidRetargeting.d.ts +6 -2
- package/dist/animation/HumanoidRetargeting.d.ts.map +1 -1
- package/dist/animation/HumanoidRetargeting.js +18 -8
- package/dist/animation/HumanoidRetargeting.js.map +1 -1
- package/dist/animation/LocomotionEvidence.d.ts +34 -0
- package/dist/animation/LocomotionEvidence.d.ts.map +1 -0
- package/dist/animation/LocomotionEvidence.js +48 -0
- package/dist/animation/LocomotionEvidence.js.map +1 -0
- package/dist/animation/RootMotion.d.ts +13 -4
- package/dist/animation/RootMotion.d.ts.map +1 -1
- package/dist/animation/RootMotion.js +21 -1
- package/dist/animation/RootMotion.js.map +1 -1
- package/dist/animation/index.d.ts +1 -0
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/animation/index.js +1 -0
- package/dist/animation/index.js.map +1 -1
- package/dist/assets/GLTFAnimationRuntime.d.ts +84 -4
- package/dist/assets/GLTFAnimationRuntime.d.ts.map +1 -1
- package/dist/assets/GLTFAnimationRuntime.js +308 -23
- package/dist/assets/GLTFAnimationRuntime.js.map +1 -1
- package/dist/assets/GLTFCompressionDecoders.d.ts +1 -0
- package/dist/assets/GLTFCompressionDecoders.d.ts.map +1 -1
- package/dist/assets/GLTFCompressionDecoders.js +20 -5
- package/dist/assets/GLTFCompressionDecoders.js.map +1 -1
- package/dist/assets/GLTFLoader.d.ts.map +1 -1
- package/dist/assets/GLTFLoader.js +21 -7
- package/dist/assets/GLTFLoader.js.map +1 -1
- package/dist/assets/KTX2BasisTextureTranscoder.d.ts.map +1 -1
- package/dist/assets/KTX2BasisTextureTranscoder.js +8 -0
- package/dist/assets/KTX2BasisTextureTranscoder.js.map +1 -1
- package/dist/controls/MapControls.d.ts +1 -1
- package/dist/controls/MapControls.d.ts.map +1 -1
- package/dist/controls/MapControls.js +3 -3
- package/dist/controls/MapControls.js.map +1 -1
- package/dist/controls/OrbitControls.d.ts +17 -3
- package/dist/controls/OrbitControls.d.ts.map +1 -1
- package/dist/controls/OrbitControls.js +24 -7
- package/dist/controls/OrbitControls.js.map +1 -1
- package/dist/controls/TrackballControls.d.ts.map +1 -1
- package/dist/controls/TrackballControls.js +2 -0
- package/dist/controls/TrackballControls.js.map +1 -1
- package/dist/create-aura3d/index.js +1 -1
- package/dist/engine/agent-api/AnimationController.d.ts +5 -1
- package/dist/engine/agent-api/AnimationController.d.ts.map +1 -1
- package/dist/engine/agent-api/AnimationController.js +5 -0
- package/dist/engine/agent-api/AnimationController.js.map +1 -1
- package/dist/engine/agent-api/CameraProjection.d.ts +2 -0
- package/dist/engine/agent-api/CameraProjection.d.ts.map +1 -0
- package/dist/engine/agent-api/CameraProjection.js +2 -0
- package/dist/engine/agent-api/CameraProjection.js.map +1 -0
- package/dist/engine/agent-api/DeferredFrameResources.d.ts +2 -0
- package/dist/engine/agent-api/DeferredFrameResources.d.ts.map +1 -0
- package/dist/engine/agent-api/DeferredFrameResources.js +2 -0
- package/dist/engine/agent-api/DeferredFrameResources.js.map +1 -0
- package/dist/engine/agent-api/FootPlanting.d.ts +6 -0
- package/dist/engine/agent-api/FootPlanting.d.ts.map +1 -1
- package/dist/engine/agent-api/FootPlanting.js +2 -1
- package/dist/engine/agent-api/FootPlanting.js.map +1 -1
- package/dist/engine/agent-api/GameCameraRigs.d.ts.map +1 -1
- package/dist/engine/agent-api/GameCameraRigs.js +7 -2
- package/dist/engine/agent-api/GameCameraRigs.js.map +1 -1
- package/dist/engine/agent-api/LabelTelemetry.d.ts +8 -2
- package/dist/engine/agent-api/LabelTelemetry.d.ts.map +1 -1
- package/dist/engine/agent-api/LabelTelemetry.js +18 -4
- package/dist/engine/agent-api/LabelTelemetry.js.map +1 -1
- package/dist/engine/agent-api/ModelInstanceMatrices.d.ts +2 -0
- package/dist/engine/agent-api/ModelInstanceMatrices.d.ts.map +1 -0
- package/dist/engine/agent-api/ModelInstanceMatrices.js +2 -0
- package/dist/engine/agent-api/ModelInstanceMatrices.js.map +1 -0
- package/dist/engine/agent-api/NavigationCrowds.d.ts +28 -0
- package/dist/engine/agent-api/NavigationCrowds.d.ts.map +1 -1
- package/dist/engine/agent-api/NavigationCrowds.js +73 -1
- package/dist/engine/agent-api/NavigationCrowds.js.map +1 -1
- package/dist/engine/agent-api/PngTexturePixels.d.ts +9 -0
- package/dist/engine/agent-api/PngTexturePixels.d.ts.map +1 -0
- package/dist/engine/agent-api/PngTexturePixels.js +53 -0
- package/dist/engine/agent-api/PngTexturePixels.js.map +1 -0
- package/dist/engine/agent-api/RootDiagnosticSnapshot.d.ts +2 -0
- package/dist/engine/agent-api/RootDiagnosticSnapshot.d.ts.map +1 -0
- package/dist/engine/agent-api/RootDiagnosticSnapshot.js +2 -0
- package/dist/engine/agent-api/RootDiagnosticSnapshot.js.map +1 -0
- package/dist/engine/agent-api/RootFrameClock.d.ts +2 -0
- package/dist/engine/agent-api/RootFrameClock.d.ts.map +1 -0
- package/dist/engine/agent-api/RootFrameClock.js +2 -0
- package/dist/engine/agent-api/RootFrameClock.js.map +1 -0
- package/dist/engine/agent-api/RootPerformanceQuality.d.ts +2 -0
- package/dist/engine/agent-api/RootPerformanceQuality.d.ts.map +1 -0
- package/dist/engine/agent-api/RootPerformanceQuality.js +2 -0
- package/dist/engine/agent-api/RootPerformanceQuality.js.map +1 -0
- package/dist/engine/agent-api/RootRuntimeSupport.d.ts +61 -0
- package/dist/engine/agent-api/RootRuntimeSupport.d.ts.map +1 -0
- package/dist/engine/agent-api/RootRuntimeSupport.js +127 -0
- package/dist/engine/agent-api/RootRuntimeSupport.js.map +1 -0
- package/dist/engine/agent-api/RuntimeNodeHandle.d.ts +22 -0
- package/dist/engine/agent-api/RuntimeNodeHandle.d.ts.map +1 -1
- package/dist/engine/agent-api/RuntimeNodeHandle.js.map +1 -1
- package/dist/engine/agent-api/WorldLabelRenderer.d.ts +13 -2
- package/dist/engine/agent-api/WorldLabelRenderer.d.ts.map +1 -1
- package/dist/engine/agent-api/WorldLabelRenderer.js +58 -26
- package/dist/engine/agent-api/WorldLabelRenderer.js.map +1 -1
- package/dist/engine/agent-api/index.d.ts +103 -8
- package/dist/engine/agent-api/index.d.ts.map +1 -1
- package/dist/engine/agent-api/index.js +750 -242
- package/dist/engine/agent-api/index.js.map +1 -1
- package/dist/engine/agent-api/product-viewer-runtime.d.ts.map +1 -1
- package/dist/engine/production-runtime/RootGpuParticleWorkload.d.ts +34 -0
- package/dist/engine/production-runtime/RootGpuParticleWorkload.d.ts.map +1 -0
- package/dist/engine/production-runtime/RootGpuParticleWorkload.js +117 -0
- package/dist/engine/production-runtime/RootGpuParticleWorkload.js.map +1 -0
- package/dist/engine/production-runtime/RootRenderSourceBridge.d.ts +2 -0
- package/dist/engine/production-runtime/RootRenderSourceBridge.d.ts.map +1 -0
- package/dist/engine/production-runtime/RootRenderSourceBridge.js +2 -0
- package/dist/engine/production-runtime/RootRenderSourceBridge.js.map +1 -0
- package/dist/engine/production-runtime/TypedGLBActor.d.ts +6 -0
- package/dist/engine/production-runtime/TypedGLBActor.d.ts.map +1 -1
- package/dist/engine/production-runtime/TypedGLBActor.js +13 -0
- package/dist/engine/production-runtime/TypedGLBActor.js.map +1 -1
- package/dist/engine/production-runtime/index.d.ts +2 -0
- package/dist/engine/production-runtime/index.d.ts.map +1 -1
- package/dist/engine/production-runtime/index.js +2 -0
- package/dist/engine/production-runtime/index.js.map +1 -1
- package/dist/input/Haptics.d.ts +1 -1
- package/dist/input/Haptics.js +1 -1
- package/dist/input/InputSystem.d.ts.map +1 -1
- package/dist/input/InputSystem.js +1 -0
- package/dist/input/InputSystem.js.map +1 -1
- package/dist/input/PointerDevice.d.ts +1 -0
- package/dist/input/PointerDevice.d.ts.map +1 -1
- package/dist/input/PointerDevice.js +1 -1
- package/dist/input/PointerDevice.js.map +1 -1
- package/dist/input/controls/OrbitControls.d.ts +44 -1
- package/dist/input/controls/OrbitControls.d.ts.map +1 -1
- package/dist/input/controls/OrbitControls.js +151 -12
- package/dist/input/controls/OrbitControls.js.map +1 -1
- package/dist/lean/base.d.ts +1 -1
- package/dist/lean/base.d.ts.map +1 -1
- package/dist/lean/base.js +1 -1
- package/dist/lean/base.js.map +1 -1
- package/dist/lean/index.js +1 -1
- package/dist/lean/index.js.map +1 -1
- package/dist/navigation-recast/index.d.ts +116 -0
- package/dist/navigation-recast/index.d.ts.map +1 -0
- package/dist/navigation-recast/index.js +247 -0
- package/dist/navigation-recast/index.js.map +1 -0
- package/dist/physics-rapier/HeightfieldLayout.d.ts +12 -0
- package/dist/physics-rapier/HeightfieldLayout.d.ts.map +1 -0
- package/dist/physics-rapier/HeightfieldLayout.js +19 -0
- package/dist/physics-rapier/HeightfieldLayout.js.map +1 -0
- package/dist/physics-rapier/index.d.ts.map +1 -1
- package/dist/physics-rapier/index.js +2 -1
- package/dist/physics-rapier/index.js.map +1 -1
- package/dist/rendering/CascadedShadowMaps.d.ts.map +1 -1
- package/dist/rendering/CascadedShadowMaps.js +3 -1
- package/dist/rendering/CascadedShadowMaps.js.map +1 -1
- package/dist/rendering/ExtensionScalarAtlas.d.ts +18 -0
- package/dist/rendering/ExtensionScalarAtlas.d.ts.map +1 -0
- package/dist/rendering/ExtensionScalarAtlas.js +107 -0
- package/dist/rendering/ExtensionScalarAtlas.js.map +1 -0
- package/dist/rendering/LeanWebGL2Device.d.ts +265 -0
- package/dist/rendering/LeanWebGL2Device.d.ts.map +1 -0
- package/dist/rendering/LeanWebGL2Device.js +4001 -0
- package/dist/rendering/LeanWebGL2Device.js.map +1 -0
- package/dist/rendering/MaterialBinding.js +1 -1
- package/dist/rendering/MaterialBinding.js.map +1 -1
- package/dist/rendering/PlanarReflection.d.ts +2 -2
- package/dist/rendering/PlanarReflection.js +3 -3
- package/dist/rendering/PlanarReflection.js.map +1 -1
- package/dist/rendering/PostProcessPass.d.ts +19 -0
- package/dist/rendering/PostProcessPass.d.ts.map +1 -1
- package/dist/rendering/PostProcessPass.js +64 -32
- package/dist/rendering/PostProcessPass.js.map +1 -1
- package/dist/rendering/ProjectionMath.d.ts +3 -0
- package/dist/rendering/ProjectionMath.d.ts.map +1 -0
- package/dist/rendering/ProjectionMath.js +26 -0
- package/dist/rendering/ProjectionMath.js.map +1 -0
- package/dist/rendering/ReflectionSurfaces.d.ts +4 -1
- package/dist/rendering/ReflectionSurfaces.d.ts.map +1 -1
- package/dist/rendering/ReflectionSurfaces.js +10 -1
- package/dist/rendering/ReflectionSurfaces.js.map +1 -1
- package/dist/rendering/RenderDevice.d.ts +5 -0
- package/dist/rendering/RenderDevice.d.ts.map +1 -1
- package/dist/rendering/RenderDevice.js.map +1 -1
- package/dist/rendering/Renderer.d.ts +9 -0
- package/dist/rendering/Renderer.d.ts.map +1 -1
- package/dist/rendering/Renderer.js +94 -8
- package/dist/rendering/Renderer.js.map +1 -1
- package/dist/rendering/RendererPostprocessPlan.d.ts +16 -3
- package/dist/rendering/RendererPostprocessPlan.d.ts.map +1 -1
- package/dist/rendering/RendererPostprocessPlan.js +16 -2
- package/dist/rendering/RendererPostprocessPlan.js.map +1 -1
- package/dist/rendering/ScreenSpaceReflectionPass.d.ts +43 -0
- package/dist/rendering/ScreenSpaceReflectionPass.d.ts.map +1 -0
- package/dist/rendering/ScreenSpaceReflectionPass.js +58 -0
- package/dist/rendering/ScreenSpaceReflectionPass.js.map +1 -0
- package/dist/rendering/ShaderLibrary.d.ts.map +1 -1
- package/dist/rendering/ShaderLibrary.js +168 -197
- package/dist/rendering/ShaderLibrary.js.map +1 -1
- package/dist/rendering/ShaderLibraryCore.d.ts +3 -0
- package/dist/rendering/ShaderLibraryCore.d.ts.map +1 -1
- package/dist/rendering/ShaderLibraryCore.js +3 -0
- package/dist/rendering/ShaderLibraryCore.js.map +1 -1
- package/dist/rendering/TemporalHistory.d.ts +37 -0
- package/dist/rendering/TemporalHistory.d.ts.map +1 -0
- package/dist/rendering/TemporalHistory.js +135 -0
- package/dist/rendering/TemporalHistory.js.map +1 -0
- package/dist/rendering/TemporalMath.d.ts +3 -0
- package/dist/rendering/TemporalMath.d.ts.map +1 -0
- package/dist/rendering/TemporalMath.js +9 -0
- package/dist/rendering/TemporalMath.js.map +1 -0
- package/dist/rendering/TexturedPBRMaterial.d.ts +3 -0
- package/dist/rendering/TexturedPBRMaterial.d.ts.map +1 -1
- package/dist/rendering/TexturedPBRMaterial.js +65 -3
- package/dist/rendering/TexturedPBRMaterial.js.map +1 -1
- package/dist/rendering/WebGL2Device.d.ts +15 -0
- package/dist/rendering/WebGL2Device.d.ts.map +1 -1
- package/dist/rendering/WebGL2Device.js +328 -90
- package/dist/rendering/WebGL2Device.js.map +1 -1
- package/dist/rendering/WebGPUDevice.d.ts +16 -5
- package/dist/rendering/WebGPUDevice.d.ts.map +1 -1
- package/dist/rendering/WebGPUDevice.js +357 -58
- package/dist/rendering/WebGPUDevice.js.map +1 -1
- package/dist/rendering/WebGPUExtensionAtlas.d.ts +6 -0
- package/dist/rendering/WebGPUExtensionAtlas.d.ts.map +1 -0
- package/dist/rendering/WebGPUExtensionAtlas.js +225 -0
- package/dist/rendering/WebGPUExtensionAtlas.js.map +1 -0
- package/dist/rendering/effects/GPUParticleBackend.d.ts +4 -2
- package/dist/rendering/effects/GPUParticleBackend.d.ts.map +1 -1
- package/dist/rendering/effects/GPUParticleBackend.js +20 -2
- package/dist/rendering/effects/GPUParticleBackend.js.map +1 -1
- package/dist/rendering/effects/ResidentGPUParticleRenderer.d.ts +158 -0
- package/dist/rendering/effects/ResidentGPUParticleRenderer.d.ts.map +1 -0
- package/dist/rendering/effects/ResidentGPUParticleRenderer.js +576 -0
- package/dist/rendering/effects/ResidentGPUParticleRenderer.js.map +1 -0
- package/dist/rendering/extension-scalar-atlas.d.ts +3 -0
- package/dist/rendering/extension-scalar-atlas.d.ts.map +1 -0
- package/dist/rendering/extension-scalar-atlas.js +2 -0
- package/dist/rendering/extension-scalar-atlas.js.map +1 -0
- package/dist/rendering/index.d.ts +1 -0
- package/dist/rendering/index.d.ts.map +1 -1
- package/dist/rendering/index.js +1 -0
- package/dist/rendering/index.js.map +1 -1
- package/dist/rendering/lean/LeanProductionRenderer.d.ts.map +1 -1
- package/dist/rendering/lean/LeanProductionRenderer.js +2 -2
- package/dist/rendering/lean/LeanProductionRenderer.js.map +1 -1
- package/dist/rendering/lean-core-runtime.d.ts +13 -0
- package/dist/rendering/lean-core-runtime.d.ts.map +1 -0
- package/dist/rendering/lean-core-runtime.js +10 -0
- package/dist/rendering/lean-core-runtime.js.map +1 -0
- package/dist/rendering/postprocess/NativeBloomPyramid.d.ts +17 -1
- package/dist/rendering/postprocess/NativeBloomPyramid.d.ts.map +1 -1
- package/dist/rendering/postprocess/NativeBloomPyramid.js +31 -1
- package/dist/rendering/postprocess/NativeBloomPyramid.js.map +1 -1
- package/dist/rendering/production-runtime/ProductionRendererTypes.d.ts +1 -0
- package/dist/rendering/production-runtime/ProductionRendererTypes.d.ts.map +1 -1
- package/dist/rendering/production-runtime/ProductionRendererTypes.js.map +1 -1
- package/dist/rendering/production-runtime/ProductionRuntimeRenderer.d.ts +3 -0
- package/dist/rendering/production-runtime/ProductionRuntimeRenderer.d.ts.map +1 -1
- package/dist/rendering/production-runtime/ProductionRuntimeRenderer.js +8 -1
- package/dist/rendering/production-runtime/ProductionRuntimeRenderer.js.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGL2Renderer.d.ts +4 -0
- package/dist/rendering/production-runtime/ProductionWebGL2Renderer.d.ts.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGL2Renderer.js +15 -0
- package/dist/rendering/production-runtime/ProductionWebGL2Renderer.js.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.d.ts +7 -1
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.js +28 -2
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.js.map +1 -1
- package/dist/rendering/production-runtime/framegraph/FrameGraph.d.ts +4 -2
- package/dist/rendering/production-runtime/framegraph/FrameGraph.d.ts.map +1 -1
- package/dist/rendering/production-runtime/framegraph/FrameGraph.js +62 -4
- package/dist/rendering/production-runtime/framegraph/FrameGraph.js.map +1 -1
- package/dist/rendering/production-runtime/framegraph/NativeFrameGraphBindings.d.ts +32 -0
- package/dist/rendering/production-runtime/framegraph/NativeFrameGraphBindings.d.ts.map +1 -0
- package/dist/rendering/production-runtime/framegraph/NativeFrameGraphBindings.js +111 -0
- package/dist/rendering/production-runtime/framegraph/NativeFrameGraphBindings.js.map +1 -0
- package/dist/rendering/production-runtime/framegraph/RenderPass.d.ts +23 -0
- package/dist/rendering/production-runtime/framegraph/RenderPass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/framegraph/RenderPass.js +87 -1
- package/dist/rendering/production-runtime/framegraph/RenderPass.js.map +1 -1
- package/dist/rendering/production-runtime/index.d.ts +2 -0
- package/dist/rendering/production-runtime/index.d.ts.map +1 -1
- package/dist/rendering/production-runtime/index.js +1 -0
- package/dist/rendering/production-runtime/index.js.map +1 -1
- package/dist/rendering/production-runtime/passes/DepthPrepass.d.ts +4 -12
- package/dist/rendering/production-runtime/passes/DepthPrepass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/DepthPrepass.js +3 -11
- package/dist/rendering/production-runtime/passes/DepthPrepass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/FramegraphTopology.js +1 -1
- package/dist/rendering/production-runtime/passes/FramegraphTopology.js.map +1 -1
- package/dist/rendering/production-runtime/passes/OpaquePass.d.ts +3 -8
- package/dist/rendering/production-runtime/passes/OpaquePass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/OpaquePass.js +3 -1
- package/dist/rendering/production-runtime/passes/OpaquePass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/ShadowPass.d.ts +3 -8
- package/dist/rendering/production-runtime/passes/ShadowPass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/ShadowPass.js +2 -0
- package/dist/rendering/production-runtime/passes/ShadowPass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/SkyboxPass.d.ts +3 -7
- package/dist/rendering/production-runtime/passes/SkyboxPass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/SkyboxPass.js +2 -0
- package/dist/rendering/production-runtime/passes/SkyboxPass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/ToneMappingPass.d.ts +7 -10
- package/dist/rendering/production-runtime/passes/ToneMappingPass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/ToneMappingPass.js +9 -0
- package/dist/rendering/production-runtime/passes/ToneMappingPass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/TransparentPass.d.ts +4 -9
- package/dist/rendering/production-runtime/passes/TransparentPass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/TransparentPass.js +6 -1
- package/dist/rendering/production-runtime/passes/TransparentPass.js.map +1 -1
- package/dist/rendering/reflection-surfaces.d.ts +5 -0
- package/dist/rendering/reflection-surfaces.d.ts.map +1 -0
- package/dist/rendering/reflection-surfaces.js +3 -0
- package/dist/rendering/reflection-surfaces.js.map +1 -0
- package/dist/rendering/webgpu/WebGPUPostShaders.d.ts +2 -3
- package/dist/rendering/webgpu/WebGPUPostShaders.d.ts.map +1 -1
- package/dist/rendering/webgpu/WebGPUPostShaders.js +2 -3
- package/dist/rendering/webgpu/WebGPUPostShaders.js.map +1 -1
- package/dist/rendering/webgpu/WebGPUTemporal.d.ts +17 -0
- package/dist/rendering/webgpu/WebGPUTemporal.d.ts.map +1 -0
- package/dist/rendering/webgpu/WebGPUTemporal.js +64 -0
- package/dist/rendering/webgpu/WebGPUTemporal.js.map +1 -0
- package/dist/rendering/webgpu.d.ts +6 -0
- package/dist/rendering/webgpu.d.ts.map +1 -1
- package/dist/rendering/webgpu.js +4 -0
- package/dist/rendering/webgpu.js.map +1 -1
- package/package.json +37 -24
- package/templates/cinematic-scene/package.json +2 -2
- package/templates/cinematic-scene/src/main.ts +14 -2
- package/templates/mini-game/package.json +2 -2
- package/templates/product-viewer/package.json +2 -2
- package/templates/production-asset-inspector/package.json +1 -1
- package/templates/production-material-studio/package.json +1 -1
- package/templates/production-product-configurator/package.json +1 -1
- package/templates/production-product-viewer/package.json +1 -1
- package/templates/cinematic-scene/node_modules/.vite/deps/_metadata.json +0 -8
- package/templates/cinematic-scene/node_modules/.vite/deps/package.json +0 -3
- package/templates/cinematic-scene/test-results/.last-run.json +0 -6
- package/templates/cinematic-scene/test-results/route-health-Aura3D-cinematic-scene-reaches-ready-state/error-context.md +0 -64
- package/templates/product-viewer/node_modules/.vite/deps/_metadata.json +0 -8
- package/templates/product-viewer/node_modules/.vite/deps/package.json +0 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { applyRootParticleQuality, composeModelInstanceMatrices, createCameraProjection, DeferredFrameResources, getRootPerformanceBaseSize, getRootPerformanceQuality, getRootRenderSource, hasRootRenderableContent, includeRootSourceMetadata, initializeRootPerformanceQuality, readRootDiagnosticSnapshot, resolveCameraClipping, resolveRootRenderTime, setRootPerformanceQuality, supportsRootParticleQuality, validateRootPerformanceQuality } from "./RootRuntimeSupport.js";
|
|
1
2
|
/*
|
|
2
3
|
* WS-2.2 — module-scope physics values come from the SOLVERLESS entry.
|
|
3
4
|
*
|
|
@@ -24,9 +25,9 @@ import { PhysicsDebugDraw, PhysicsStepper, ScenePhysicsBridge, Shape as PhysicsS
|
|
|
24
25
|
import { PhysicsWorld } from "../../physics/world.js";
|
|
25
26
|
import { createBeamDescriptor, createDayNightSky, createDualProbeEnvironmentLightingResources, createExternalParityEnvironmentLighting, createProductionEnvironmentLightingResources, createProductionPbrHdrPipelineFromRadiance, createSdfFontAtlas, createSdfTextQuadMesh, createSpotShadowProjection, createWaterSurface, createWeatherState, describeSdfTextPixelBacking, describeWetMaterial, layoutSdfText, rasterizeSdfTextLabelImage, resolveFlipbookUv, resolveSdfTextFrameOpacity, resolveVolumetricFog, selectSpotShadowAtlasTier, sampleOceanFixture, Geometry, IndexBuffer, InstancedPBRMaterial, PBRMaterial, ProductionRuntimeRenderer, resolveSamplerAnisotropy, Sampler, srgbToLinearChannel, Texture, TexturedPBRMaterial, VertexBuffer, VertexFormat } from "../../rendering/index.js";
|
|
26
27
|
import { DirectionalLight, PointLight, SpotLight } from "../../scene/index.js";
|
|
27
|
-
import { lookAtMat4, multiplyMat4
|
|
28
|
+
import { identityMat4, lookAtMat4, multiplyMat4 } from "../../scene/MathTypes.js";
|
|
28
29
|
import { AURA_NORMALIZED_MODEL_MAX_DIMENSION, boundsFromAsset, boundsHeight, boundsMaxDimension, boundsSize } from "./SceneGroundingUtils.js";
|
|
29
|
-
import { collectLabelTelemetry, summarizeTextBuckets } from "./LabelTelemetry.js";
|
|
30
|
+
import { collectLabelTelemetry, labelTelemetryRoleFor, summarizeTextBuckets } from "./LabelTelemetry.js";
|
|
30
31
|
import { createWorldLabelLayer } from "./WorldLabelRenderer.js";
|
|
31
32
|
import { createAuraText3DGeometry, defineAuraCustomGeometry, selectAuraRootLodLevel } from "./RootGeometry.js";
|
|
32
33
|
export * from "./RootGeometry.js";
|
|
@@ -1193,7 +1194,8 @@ export const lights = {
|
|
|
1193
1194
|
name: options.name,
|
|
1194
1195
|
position: options.position ?? [3, 4, 3],
|
|
1195
1196
|
intensity: options.intensity ?? 1.5,
|
|
1196
|
-
color: options.color ?? "#ffffff"
|
|
1197
|
+
color: options.color ?? "#ffffff",
|
|
1198
|
+
shadow: options.shadow
|
|
1197
1199
|
}),
|
|
1198
1200
|
point: (options = {}) => new AuraNodeBuilder({
|
|
1199
1201
|
kind: "light",
|
|
@@ -1269,6 +1271,7 @@ export const lights = {
|
|
|
1269
1271
|
export const camera = {
|
|
1270
1272
|
perspective: (options = {}) => ({
|
|
1271
1273
|
mode: "perspective",
|
|
1274
|
+
...resolveCameraClipping(options),
|
|
1272
1275
|
position: options.position ?? [0, 1.4, 4],
|
|
1273
1276
|
target: options.target ?? [0, 0.8, 0],
|
|
1274
1277
|
fov: options.fov ?? 45
|
|
@@ -1278,6 +1281,7 @@ export const camera = {
|
|
|
1278
1281
|
const target = options.target ?? [0, 0.8, 0];
|
|
1279
1282
|
return {
|
|
1280
1283
|
mode: "orbit",
|
|
1284
|
+
...resolveCameraClipping(options),
|
|
1281
1285
|
distance,
|
|
1282
1286
|
target,
|
|
1283
1287
|
position: options.position ?? [
|
|
@@ -1290,6 +1294,7 @@ export const camera = {
|
|
|
1290
1294
|
},
|
|
1291
1295
|
dolly: (options) => ({
|
|
1292
1296
|
mode: "dolly",
|
|
1297
|
+
...resolveCameraClipping(options),
|
|
1293
1298
|
from: options.from,
|
|
1294
1299
|
to: options.to,
|
|
1295
1300
|
target: options.target ?? [0, 0.8, 0],
|
|
@@ -1299,6 +1304,7 @@ export const camera = {
|
|
|
1299
1304
|
}),
|
|
1300
1305
|
follow: (options) => ({
|
|
1301
1306
|
mode: "follow",
|
|
1307
|
+
...resolveCameraClipping(options),
|
|
1302
1308
|
targetNode: options.targetNode,
|
|
1303
1309
|
distance: options.distance ?? 5,
|
|
1304
1310
|
position: options.position,
|
|
@@ -1314,6 +1320,7 @@ export const camera = {
|
|
|
1314
1320
|
}),
|
|
1315
1321
|
path: (options) => ({
|
|
1316
1322
|
mode: "path",
|
|
1323
|
+
...resolveCameraClipping(options),
|
|
1317
1324
|
from: options.from,
|
|
1318
1325
|
to: options.to,
|
|
1319
1326
|
target: options.target ?? [0, 0.8, 0],
|
|
@@ -1324,6 +1331,7 @@ export const camera = {
|
|
|
1324
1331
|
}),
|
|
1325
1332
|
flythrough: (options = {}) => ({
|
|
1326
1333
|
mode: "flythrough",
|
|
1334
|
+
...resolveCameraClipping(options),
|
|
1327
1335
|
from: options.from ?? [0, 0.36, 1.6],
|
|
1328
1336
|
to: options.to ?? [0, 0.36, -4.4],
|
|
1329
1337
|
target: options.target ?? [0, 0.28, -5.8],
|
|
@@ -1344,6 +1352,7 @@ export const camera = {
|
|
|
1344
1352
|
*/
|
|
1345
1353
|
orthographic: (options = {}) => ({
|
|
1346
1354
|
mode: "orthographic",
|
|
1355
|
+
...resolveCameraClipping(options),
|
|
1347
1356
|
position: options.position ?? [0, 1.4, 4],
|
|
1348
1357
|
target: options.target ?? [0, 0.8, 0],
|
|
1349
1358
|
orthographicSize: options.orthographicSize ?? 1.4
|
|
@@ -1366,6 +1375,7 @@ export const camera = {
|
|
|
1366
1375
|
const horizontal = Math.cos(elevation) * distance;
|
|
1367
1376
|
return {
|
|
1368
1377
|
mode: "isometric",
|
|
1378
|
+
...resolveCameraClipping(options),
|
|
1369
1379
|
target,
|
|
1370
1380
|
distance,
|
|
1371
1381
|
orthographicSize: options.orthographicSize ?? 6,
|
|
@@ -1497,7 +1507,10 @@ export const effects = {
|
|
|
1497
1507
|
radius: options.radius ?? 0.42,
|
|
1498
1508
|
threshold: options.threshold ?? 0.72,
|
|
1499
1509
|
antiBlowout: options.antiBlowout ?? true,
|
|
1500
|
-
maxIntensity: options.maxIntensity ?? 0.92
|
|
1510
|
+
maxIntensity: options.maxIntensity ?? 0.92,
|
|
1511
|
+
...(options.quality !== undefined ? { quality: options.quality } : {}),
|
|
1512
|
+
...(options.softKnee !== undefined ? { softKnee: options.softKnee } : {}),
|
|
1513
|
+
...(options.shoulder !== undefined ? { shoulder: options.shoulder } : {})
|
|
1501
1514
|
}),
|
|
1502
1515
|
neonBloom: (options = {}) => effects.bloom({
|
|
1503
1516
|
intensity: options.intensity ?? 0.72,
|
|
@@ -1505,7 +1518,10 @@ export const effects = {
|
|
|
1505
1518
|
radius: options.radius ?? 0.48,
|
|
1506
1519
|
threshold: options.threshold ?? 0.68,
|
|
1507
1520
|
antiBlowout: options.antiBlowout ?? true,
|
|
1508
|
-
maxIntensity: options.maxIntensity ?? 0.92
|
|
1521
|
+
maxIntensity: options.maxIntensity ?? 0.92,
|
|
1522
|
+
...(options.quality !== undefined ? { quality: options.quality } : {}),
|
|
1523
|
+
...(options.softKnee !== undefined ? { softKnee: options.softKnee } : {}),
|
|
1524
|
+
...(options.shoulder !== undefined ? { shoulder: options.shoulder } : {})
|
|
1509
1525
|
}),
|
|
1510
1526
|
/**
|
|
1511
1527
|
* A5 volumetric fog (muse3jsparity-PRD): builds a DISTINCT "volumetric-fog"
|
|
@@ -1566,8 +1582,8 @@ export const effects = {
|
|
|
1566
1582
|
}),
|
|
1567
1583
|
/**
|
|
1568
1584
|
* Root anti-alias node (muse3jsparity-PRD A3). `fxaa` executes natively;
|
|
1569
|
-
* `off` submits nothing; `taa`
|
|
1570
|
-
*
|
|
1585
|
+
* `off` submits nothing; `taa` uses renderer-owned velocity/history for opaque
|
|
1586
|
+
* rigid triangles. Unsupported deforming/transparent geometry emits a named warning.
|
|
1571
1587
|
*/
|
|
1572
1588
|
antiAlias: (options = {}) => new AuraNodeBuilder({
|
|
1573
1589
|
kind: "effect",
|
|
@@ -1614,8 +1630,8 @@ export const effects = {
|
|
|
1614
1630
|
intensity: options.intensity ?? 1
|
|
1615
1631
|
}),
|
|
1616
1632
|
/**
|
|
1617
|
-
* Root motion-blur node
|
|
1618
|
-
*
|
|
1633
|
+
* Root motion-blur node: renderer-owned GPU velocity for opaque rigid geometry.
|
|
1634
|
+
* Unsupported deforming/transparent/instanced geometry emits a named warning.
|
|
1619
1635
|
*/
|
|
1620
1636
|
motionBlur: (options = {}) => new AuraNodeBuilder({
|
|
1621
1637
|
kind: "effect",
|
|
@@ -2313,9 +2329,8 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
2313
2329
|
const contactShadows = names.filter((name) => name.includes("contact shadow") || name.includes("footprint") || name.includes("glow pool")).length;
|
|
2314
2330
|
const ambientOcclusion = flattened.some((node) => node.kind === "effect" && node.effect === "ambient-occlusion");
|
|
2315
2331
|
const contactOcclusion = flattened.some((node) => node.kind === "effect" && node.effect === "contact-occlusion") || contactShadows > 0;
|
|
2316
|
-
//
|
|
2317
|
-
//
|
|
2318
|
-
// at root) — requestedPasses names the intent, actualPasses the execution.
|
|
2332
|
+
// Authored temporal intent is separate from device-observed execution.
|
|
2333
|
+
// Missing submissions remain visible without claiming an unmounted scene rendered.
|
|
2319
2334
|
const colorGradeNode = flattened.find((node) => node.kind === "effect" && node.effect === "color-grade");
|
|
2320
2335
|
const antiAliasNode = flattened.find((node) => node.kind === "effect" && node.effect === "anti-alias");
|
|
2321
2336
|
const outlineNode = flattened.find((node) => node.kind === "effect" && node.effect === "outline");
|
|
@@ -2326,13 +2341,14 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
2326
2341
|
const beamNode = flattened.find((node) => node.kind === "effect" && node.effect === "light-beam");
|
|
2327
2342
|
const sdfTextRequested = flattened.some((node) => node.kind === "primitive" && node.text3D?.backend === "sdf");
|
|
2328
2343
|
const fxaaRequested = (antiAliasNode?.mode ?? "fxaa") === "fxaa" && Boolean(antiAliasNode);
|
|
2329
|
-
const
|
|
2330
|
-
const
|
|
2344
|
+
const taaRequested = (antiAliasNode?.mode ?? "fxaa") === "taa";
|
|
2345
|
+
const taaWithheld = taaRequested && Boolean(runtime?.mounted) && !(runtime?.postprocess?.actualPasses ?? []).includes("taa");
|
|
2346
|
+
const motionBlurWithheld = Boolean(motionBlurNode) && Boolean(runtime?.mounted) && !(runtime?.postprocess?.actualPasses ?? []).includes("motion-blur");
|
|
2331
2347
|
const runtimePostprocess = runtime?.postprocess;
|
|
2332
2348
|
const runtimePasses = runtimePostprocess?.actualPasses ?? [];
|
|
2333
2349
|
const postprocessRequested = Boolean(bloom) || ambientOcclusion || contactOcclusion
|
|
2334
2350
|
|| Boolean(colorGradeNode) || Boolean(antiAliasNode) || Boolean(outlineNode)
|
|
2335
|
-
|| Boolean(ssrNode) || Boolean(dofNode) ||
|
|
2351
|
+
|| Boolean(ssrNode) || Boolean(dofNode) || Boolean(motionBlurNode) || runtimePasses.length > 0;
|
|
2336
2352
|
const requestedPasses = runtimePasses.length > 0
|
|
2337
2353
|
? runtimePasses
|
|
2338
2354
|
: requestedRendererPostProcessPasses(Boolean(bloom), ambientOcclusion, contactOcclusion, {
|
|
@@ -2341,8 +2357,8 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
2341
2357
|
outline: Boolean(outlineNode),
|
|
2342
2358
|
ssr: Boolean(ssrNode),
|
|
2343
2359
|
depthOfField: Boolean(dofNode),
|
|
2344
|
-
motionBlurWithheld,
|
|
2345
|
-
taaWithheld
|
|
2360
|
+
motionBlurWithheld: Boolean(motionBlurNode),
|
|
2361
|
+
taaWithheld: taaRequested
|
|
2346
2362
|
});
|
|
2347
2363
|
const runtimeStatus = !postprocessRequested
|
|
2348
2364
|
? "disabled"
|
|
@@ -2359,7 +2375,7 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
2359
2375
|
if (bloom && (bloom.intensity ?? 0) > 0.95 && bloom.antiBlowout !== true)
|
|
2360
2376
|
warnings.push("bloom is high without anti-blowout safeguards");
|
|
2361
2377
|
if (motionBlurWithheld)
|
|
2362
|
-
warnings.push("motion-blur
|
|
2378
|
+
warnings.push("TEMPORAL_INPUTS_UNAVAILABLE: motion-blur has no native submission");
|
|
2363
2379
|
if (flipbookNode)
|
|
2364
2380
|
warnings.push("flipbook-sprite is recorded but withheld: root has no native sprite-sheet sampler yet, so no flipbook pass is submitted");
|
|
2365
2381
|
if (beamNode)
|
|
@@ -2373,7 +2389,7 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
2373
2389
|
: "text3D sdf backend is recorded but unmounted: the production bridge samples the SDF atlas at mount, so textPixelBacked stays false until render");
|
|
2374
2390
|
}
|
|
2375
2391
|
if (taaWithheld)
|
|
2376
|
-
warnings.push("
|
|
2392
|
+
warnings.push("TEMPORAL_INPUTS_UNAVAILABLE: taa has no native submission");
|
|
2377
2393
|
if (colorGradeNode && (colorGradeNode.exposure ?? 1) !== 1)
|
|
2378
2394
|
warnings.push("color-grade exposure is recorded but has no native grade target yet; contrast/saturation execute");
|
|
2379
2395
|
if (colorGradeNode && (colorGradeNode.shadows !== undefined || colorGradeNode.highlights !== undefined))
|
|
@@ -2444,6 +2460,7 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
2444
2460
|
label: runtime?.shadow?.label,
|
|
2445
2461
|
nativeShadowMapBindings: runtime?.shadow?.nativeShadowMapBindings ?? 0,
|
|
2446
2462
|
shadowRenderTargetsAllocated: runtime?.shadow?.shadowRenderTargetsAllocated ?? 0,
|
|
2463
|
+
observed: runtime?.mounted ? runtime.shadow?.observed ?? null : null,
|
|
2447
2464
|
contactShadows,
|
|
2448
2465
|
mapType: "pcf-soft",
|
|
2449
2466
|
...(runtime?.shadow?.spot === undefined ? {} : { spot: runtime.shadow.spot })
|
|
@@ -2489,6 +2506,8 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
2489
2506
|
passNames: runtimePostprocess?.actualPasses ?? [],
|
|
2490
2507
|
warnings: runtime?.warnings ?? [],
|
|
2491
2508
|
nativeInstancedSubmissions: runtime?.deviceDiagnostics?.nativeInstancedSubmissions ?? 0,
|
|
2509
|
+
nativeTemporalPasses: runtime?.deviceDiagnostics?.nativeTemporalPasses ?? 0,
|
|
2510
|
+
nativeTemporalBindings: runtime?.deviceDiagnostics?.nativeTemporalBindings ?? 0,
|
|
2492
2511
|
submittedObjects: runtime?.deviceDiagnostics?.submittedObjects ?? 0,
|
|
2493
2512
|
visibleObjects: runtime?.deviceDiagnostics?.visibleObjects ?? 0,
|
|
2494
2513
|
culledObjects: runtime?.deviceDiagnostics?.culledObjects ?? 0,
|
|
@@ -2555,9 +2574,9 @@ function requestedRendererPostProcessPasses(hasBloom, ambientOcclusion, contactO
|
|
|
2555
2574
|
// Withheld intents stay visible in the request list with an explicit suffix
|
|
2556
2575
|
// so requested-vs-actual never silently agrees on an unexecuted pass.
|
|
2557
2576
|
if (extra.motionBlurWithheld)
|
|
2558
|
-
passes.push("motion-blur
|
|
2577
|
+
passes.push("motion-blur");
|
|
2559
2578
|
if (extra.taaWithheld)
|
|
2560
|
-
passes.push("taa
|
|
2579
|
+
passes.push("taa");
|
|
2561
2580
|
passes.push("output");
|
|
2562
2581
|
return passes;
|
|
2563
2582
|
}
|
|
@@ -2618,6 +2637,7 @@ export class AuraSceneBuilder {
|
|
|
2618
2637
|
return this;
|
|
2619
2638
|
}
|
|
2620
2639
|
camera(next) {
|
|
2640
|
+
resolveCameraClipping(next);
|
|
2621
2641
|
this.cameraSpec = next;
|
|
2622
2642
|
return this;
|
|
2623
2643
|
}
|
|
@@ -6976,6 +6996,9 @@ export function createRuntimeNodeImportedAssetEvidence(input) {
|
|
|
6976
6996
|
...(input.lastFootPlantingTargetError !== undefined ? { lastFootPlantingTargetError: input.lastFootPlantingTargetError } : {}),
|
|
6977
6997
|
...(input.lastFootPlantingHipOffset !== undefined ? { lastFootPlantingHipOffset: input.lastFootPlantingHipOffset } : {}),
|
|
6978
6998
|
...(input.lastFootPlantingMissingLegs !== undefined ? { lastFootPlantingMissingLegs: [...input.lastFootPlantingMissingLegs] } : {}),
|
|
6999
|
+
...(input.lastFootPlantingDeformation !== undefined ? { lastFootPlantingDeformation: input.lastFootPlantingDeformation.map(leg => ({ ...leg })) } : {}),
|
|
7000
|
+
...(input.lastFootPlantingSurfaces !== undefined ? { lastFootPlantingSurfaces: input.lastFootPlantingSurfaces.map(point => ({ ...point, worldPosition: [...point.worldPosition] })) } : {}),
|
|
7001
|
+
...(input.lastFootPlantingFeet !== undefined ? { lastFootPlantingFeet: input.lastFootPlantingFeet.map(foot => ({ ...foot, worldPosition: [...foot.worldPosition] })) } : {}),
|
|
6979
7002
|
...(input.footPlantingConfigured !== undefined ? { footPlantingConfigured: input.footPlantingConfigured } : {}),
|
|
6980
7003
|
diagnostics
|
|
6981
7004
|
};
|
|
@@ -7253,11 +7276,26 @@ export function createAuraApp(target, options) {
|
|
|
7253
7276
|
const canvas = resolveCanvas(target);
|
|
7254
7277
|
if (canvas) {
|
|
7255
7278
|
configureCanvas(canvas, options.pixelRatio ?? rendererSelection.profile.pixelRatio ?? devicePixelRatioSafe(), options.resize ?? true);
|
|
7279
|
+
initializeRootPerformanceQuality(canvas);
|
|
7280
|
+
// Apply a caller's device/capture budget before the production controller
|
|
7281
|
+
// derives its first backing size, LODs, particle pool and shadow target.
|
|
7282
|
+
// Runtime mutations remain guarded by app.setPerformanceQuality().
|
|
7283
|
+
if (options.performanceQuality)
|
|
7284
|
+
setRootPerformanceQuality(canvas, options.performanceQuality);
|
|
7256
7285
|
}
|
|
7257
7286
|
const overlay = canvas && shouldRenderOverlay(options.diagnostics, snapshot) ? createDiagnosticsOverlay(canvas, diagnosticsState) : undefined;
|
|
7258
7287
|
let disposed = false;
|
|
7259
7288
|
let animationHandle = 0;
|
|
7260
7289
|
let productionController;
|
|
7290
|
+
let pendingAsyncSteps = 0;
|
|
7291
|
+
let asyncSteps = Promise.resolve();
|
|
7292
|
+
const assertMutableFrame = () => {
|
|
7293
|
+
if (disposed)
|
|
7294
|
+
throw new Error("Aura3D app is disposed.");
|
|
7295
|
+
if (pendingAsyncSteps > 0 || productionController?.busy?.()) {
|
|
7296
|
+
throw new Error("Aura3D frame submission is pending; await stepAsync() before synchronous mutation.");
|
|
7297
|
+
}
|
|
7298
|
+
};
|
|
7261
7299
|
/** WS-2.9: true from the moment a WebGL mount starts until the controller arrives or fails. */
|
|
7262
7300
|
let productionMountPending = false;
|
|
7263
7301
|
/**
|
|
@@ -7278,6 +7316,7 @@ export function createAuraApp(target, options) {
|
|
|
7278
7316
|
* any time and on any scene.
|
|
7279
7317
|
*/
|
|
7280
7318
|
let productionMountSettled = Promise.resolve();
|
|
7319
|
+
let productionMountTask = Promise.resolve();
|
|
7281
7320
|
/** Lets `dispose()` settle an in-flight mount; see the note there. */
|
|
7282
7321
|
let settleMountForDispose = () => undefined;
|
|
7283
7322
|
/*
|
|
@@ -7399,6 +7438,8 @@ export function createAuraApp(target, options) {
|
|
|
7399
7438
|
let runtimeAlpha = 0;
|
|
7400
7439
|
const ownedInputControllers = new Set();
|
|
7401
7440
|
const runRuntimeFrame = (dt, source) => {
|
|
7441
|
+
if (disposed)
|
|
7442
|
+
return;
|
|
7402
7443
|
runtimeFrame += 1;
|
|
7403
7444
|
runtimeTime += dt;
|
|
7404
7445
|
runtimeAlpha = runtimeFixedDt > 0 ? Math.max(0, Math.min(1, (dt % runtimeFixedDt) / runtimeFixedDt)) : 0;
|
|
@@ -7416,7 +7457,7 @@ export function createAuraApp(target, options) {
|
|
|
7416
7457
|
for (const callback of [...frameCallbacks])
|
|
7417
7458
|
callback(frame);
|
|
7418
7459
|
};
|
|
7419
|
-
const shouldUseProductionRendererForCurrentScene = () => Boolean(canvas && renderSnapshot.nodes.some(isWebGLRenderableNode) && typeof window !== "undefined");
|
|
7460
|
+
const shouldUseProductionRendererForCurrentScene = () => Boolean(canvas && hasRootRenderableContent(canvas, renderSnapshot.nodes.some(isWebGLRenderableNode)) && typeof window !== "undefined");
|
|
7420
7461
|
const resetDiagnosticsForCurrentScene = (backend) => {
|
|
7421
7462
|
const fresh = createInitialDiagnostics(renderSnapshot, options.renderer);
|
|
7422
7463
|
diagnosticsState.backend = backend;
|
|
@@ -7481,7 +7522,7 @@ export function createAuraApp(target, options) {
|
|
|
7481
7522
|
* So the two cases are separated. A renderable scene with no usable canvas now raises a diagnosable
|
|
7482
7523
|
* error naming the cause, instead of drawing something plausible.
|
|
7483
7524
|
*/
|
|
7484
|
-
const declaresRenderableContent = renderSnapshot.nodes.some(isWebGLRenderableNode);
|
|
7525
|
+
const declaresRenderableContent = hasRootRenderableContent(canvas, renderSnapshot.nodes.some(isWebGLRenderableNode));
|
|
7485
7526
|
/*
|
|
7486
7527
|
* Scoped to the case that is actually a lie: a canvas WAS supplied, so the caller is looking at
|
|
7487
7528
|
* pixels, and the scene has renderable content — but WebGL declined it, so those pixels would be a
|
|
@@ -7520,7 +7561,7 @@ export function createAuraApp(target, options) {
|
|
|
7520
7561
|
: Promise.resolve();
|
|
7521
7562
|
settleMountForDispose = settleMount;
|
|
7522
7563
|
if (shouldUseProductionRenderer && canvas) {
|
|
7523
|
-
|
|
7564
|
+
productionMountTask = startProductionRender(canvas, renderSnapshot, diagnosticsState, options, overlay, runRuntimeFrame, () => runtimePaused, runtimeNodes,
|
|
7524
7565
|
// A route that registered a frame callback, or that has bodies to simulate, needs
|
|
7525
7566
|
// frames regardless of what the scene declares.
|
|
7526
7567
|
/*
|
|
@@ -7534,7 +7575,6 @@ export function createAuraApp(target, options) {
|
|
|
7534
7575
|
() => runtimeTime * 1000)
|
|
7535
7576
|
.then((controller) => {
|
|
7536
7577
|
if (disposed || revision !== mountRevision) {
|
|
7537
|
-
productionMountPending = false;
|
|
7538
7578
|
settleMount();
|
|
7539
7579
|
controller.dispose();
|
|
7540
7580
|
return;
|
|
@@ -7546,6 +7586,7 @@ export function createAuraApp(target, options) {
|
|
|
7546
7586
|
markRouteReady(snapshot, diagnosticsState);
|
|
7547
7587
|
})
|
|
7548
7588
|
.catch((error) => {
|
|
7589
|
+
settleMount();
|
|
7549
7590
|
if (disposed || revision !== mountRevision)
|
|
7550
7591
|
return;
|
|
7551
7592
|
productionMountPending = false;
|
|
@@ -7570,7 +7611,18 @@ export function createAuraApp(target, options) {
|
|
|
7570
7611
|
get backend() {
|
|
7571
7612
|
return diagnosticsState.backend;
|
|
7572
7613
|
},
|
|
7614
|
+
setPerformanceQuality(settings) {
|
|
7615
|
+
assertMutableFrame();
|
|
7616
|
+
const quality = validateRootPerformanceQuality(settings);
|
|
7617
|
+
if (!canvas || !productionController?.setPerformanceQuality)
|
|
7618
|
+
throw new Error("PERFORMANCE_QUALITY_UNSUPPORTED: await a production renderer mount before changing quality.");
|
|
7619
|
+
if (quality.particleScale !== 1 && !supportsRootParticleQuality(canvas))
|
|
7620
|
+
throw new Error("PERFORMANCE_PARTICLE_OWNER_UNAVAILABLE: this root workload has no adaptive native particle owner.");
|
|
7621
|
+
applyRootParticleQuality(canvas, quality.particleScale);
|
|
7622
|
+
productionController.setPerformanceQuality(quality);
|
|
7623
|
+
},
|
|
7573
7624
|
setScene(nextScene) {
|
|
7625
|
+
assertMutableFrame();
|
|
7574
7626
|
snapshot = normalizeSceneSnapshot(nextScene);
|
|
7575
7627
|
renderSnapshot = flattenSceneSnapshot(snapshot);
|
|
7576
7628
|
runtimeNodes.reset(renderSnapshot);
|
|
@@ -7617,6 +7669,10 @@ export function createAuraApp(target, options) {
|
|
|
7617
7669
|
},
|
|
7618
7670
|
pause() {
|
|
7619
7671
|
runtimePaused = true;
|
|
7672
|
+
// Stop the production requestAnimationFrame owner as well as simulation.
|
|
7673
|
+
// Holding the clock alone still submitted the same expensive frame forever.
|
|
7674
|
+
productionController?.pause?.();
|
|
7675
|
+
productionController?.resetTemporalHistory?.("pause");
|
|
7620
7676
|
},
|
|
7621
7677
|
resetRuntimeClock() {
|
|
7622
7678
|
/*
|
|
@@ -7639,6 +7695,8 @@ export function createAuraApp(target, options) {
|
|
|
7639
7695
|
},
|
|
7640
7696
|
resume() {
|
|
7641
7697
|
runtimePaused = false;
|
|
7698
|
+
productionController?.resetTemporalHistory?.("resume");
|
|
7699
|
+
productionController?.resume?.();
|
|
7642
7700
|
if (!animationHandle && !productionController && options.autoStart !== false && typeof requestAnimationFrame !== "undefined") {
|
|
7643
7701
|
animationHandle = requestAnimationFrame(render);
|
|
7644
7702
|
}
|
|
@@ -7667,11 +7725,17 @@ export function createAuraApp(target, options) {
|
|
|
7667
7725
|
return productionController?.deviceLost?.() ?? false;
|
|
7668
7726
|
},
|
|
7669
7727
|
advance(dt = 1 / 60) {
|
|
7728
|
+
assertMutableFrame();
|
|
7670
7729
|
const seconds = Math.max(0, dt);
|
|
7671
7730
|
runRuntimeFrame(seconds, "manual");
|
|
7672
7731
|
canvasRuntimePhysics?.step(seconds);
|
|
7732
|
+
// Imported GLB animation, consumed root motion and foot planting live in the
|
|
7733
|
+
// production actor bridge. They are simulation state, so `advance()` must
|
|
7734
|
+
// update them even when no GPU frame is presented.
|
|
7735
|
+
productionController?.update?.(runtimeTime * 1000);
|
|
7673
7736
|
},
|
|
7674
7737
|
step(dt = 1 / 60) {
|
|
7738
|
+
assertMutableFrame();
|
|
7675
7739
|
const seconds = Math.max(0, dt);
|
|
7676
7740
|
app.advance(seconds);
|
|
7677
7741
|
const previousPaused = runtimePaused;
|
|
@@ -7731,7 +7795,38 @@ export function createAuraApp(target, options) {
|
|
|
7731
7795
|
}
|
|
7732
7796
|
runtimePaused = previousPaused;
|
|
7733
7797
|
},
|
|
7798
|
+
stepAsync(dt = 1 / 60) {
|
|
7799
|
+
if (!Number.isFinite(dt) || dt < 0)
|
|
7800
|
+
return Promise.reject(new Error("Aura3D stepAsync requires a finite non-negative delta."));
|
|
7801
|
+
if (disposed)
|
|
7802
|
+
return Promise.reject(new Error("Aura3D app is disposed."));
|
|
7803
|
+
pendingAsyncSteps += 1;
|
|
7804
|
+
const next = asyncSteps.then(async () => {
|
|
7805
|
+
await productionMountSettled;
|
|
7806
|
+
if (disposed)
|
|
7807
|
+
throw new Error("Aura3D app is disposed.");
|
|
7808
|
+
const controller = productionController;
|
|
7809
|
+
if (productionMountFailed || !controller?.renderAsync) {
|
|
7810
|
+
throw new Error(`Aura3D asynchronous submission unavailable: ${diagnosticsState.errors.join("; ") || "a production renderer is required"}`);
|
|
7811
|
+
}
|
|
7812
|
+
await controller.renderAsync(() => {
|
|
7813
|
+
runRuntimeFrame(dt, "manual");
|
|
7814
|
+
canvasRuntimePhysics?.step(dt);
|
|
7815
|
+
return runtimeTime * 1000;
|
|
7816
|
+
});
|
|
7817
|
+
}).finally(() => { pendingAsyncSteps -= 1; });
|
|
7818
|
+
asyncSteps = next.catch(() => undefined);
|
|
7819
|
+
return next;
|
|
7820
|
+
},
|
|
7821
|
+
async disposeAsync() {
|
|
7822
|
+
const controller = productionController;
|
|
7823
|
+
app.dispose();
|
|
7824
|
+
await asyncSteps;
|
|
7825
|
+
await productionMountTask;
|
|
7826
|
+
await controller?.whenIdle?.();
|
|
7827
|
+
},
|
|
7734
7828
|
diagnostics() {
|
|
7829
|
+
diagnosticsState.renderer = readRootDiagnosticSnapshot(diagnosticsState.renderer, productionController?.diagnostics, disposed, pendingAsyncSteps > 0 || Boolean(productionController?.busy?.()));
|
|
7735
7830
|
return snapshotDiagnostics(diagnosticsState);
|
|
7736
7831
|
},
|
|
7737
7832
|
evidence(evidenceOptions = {}) {
|
|
@@ -8071,6 +8166,7 @@ function worldLabelsFromSnapshot(snapshot, runtimeNodes) {
|
|
|
8071
8166
|
return {
|
|
8072
8167
|
id: node.name ?? `${node.label}-${index + 1}`,
|
|
8073
8168
|
text: node.text,
|
|
8169
|
+
role: labelTelemetryRoleFor(node.label),
|
|
8074
8170
|
anchor: [0, 0, 0],
|
|
8075
8171
|
offscreenPolicy: "hide"
|
|
8076
8172
|
};
|
|
@@ -8084,6 +8180,7 @@ function worldLabelsFromSnapshot(snapshot, runtimeNodes) {
|
|
|
8084
8180
|
return {
|
|
8085
8181
|
id: node.name ?? `${node.label}-${index + 1}`,
|
|
8086
8182
|
text: node.text,
|
|
8183
|
+
role: labelTelemetryRoleFor(node.label),
|
|
8087
8184
|
anchor: [position[0], position[1], position[2]],
|
|
8088
8185
|
leaderAnchor: [anchor[0], anchor[1], anchor[2]],
|
|
8089
8186
|
screenOffset: separated ? [0, 0] : [0, -26],
|
|
@@ -8237,8 +8334,10 @@ requiresFrames = () => false,
|
|
|
8237
8334
|
* wall-clock time. Without it, `pause()` froze the simulation but not time-driven rendering.
|
|
8238
8335
|
*/
|
|
8239
8336
|
pausedRenderTime = () => 0) {
|
|
8240
|
-
|
|
8241
|
-
|
|
8337
|
+
// Use the same ownership rule as initial mount and setScene. A compatibility
|
|
8338
|
+
// source continues supplying arena/fighter geometry when public nodes are
|
|
8339
|
+
// temporarily absent (for example the crowd-hidden negative control).
|
|
8340
|
+
if (!hasRootRenderableContent(canvas, snapshot.nodes.some(isWebGLRenderableNode))) {
|
|
8242
8341
|
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().");
|
|
8243
8342
|
}
|
|
8244
8343
|
const renderer = await createProductionSceneRenderer(canvas, snapshot, options.renderer, runtimeNodes);
|
|
@@ -8247,14 +8346,34 @@ pausedRenderTime = () => 0) {
|
|
|
8247
8346
|
const labelLayer = createSceneLabelLayer(canvas, snapshot);
|
|
8248
8347
|
const rendererSelection = normalizeCreateAppRendererOptions(options.renderer);
|
|
8249
8348
|
const pixelRatio = options.pixelRatio ?? rendererSelection.profile.pixelRatio ?? devicePixelRatioSafe();
|
|
8349
|
+
let disposed = false;
|
|
8350
|
+
let animationHandle = 0;
|
|
8351
|
+
let lastTime = 0;
|
|
8352
|
+
let pendingFrames = 0;
|
|
8353
|
+
let frameTail = Promise.resolve();
|
|
8354
|
+
const baseBackingSize = getRootPerformanceBaseSize(canvas);
|
|
8355
|
+
let resourcesReleased = false;
|
|
8356
|
+
const releaseResources = () => {
|
|
8357
|
+
if (resourcesReleased)
|
|
8358
|
+
return;
|
|
8359
|
+
resourcesReleased = true;
|
|
8360
|
+
labelLayer?.dispose();
|
|
8361
|
+
renderer.dispose();
|
|
8362
|
+
};
|
|
8250
8363
|
const resizeRenderer = () => {
|
|
8364
|
+
if (disposed)
|
|
8365
|
+
return;
|
|
8366
|
+
if (pendingFrames > 0) {
|
|
8367
|
+
resourceOwner.requestResize();
|
|
8368
|
+
return;
|
|
8369
|
+
}
|
|
8251
8370
|
if (options.resize === false)
|
|
8252
8371
|
return;
|
|
8253
8372
|
const parentRect = canvas.parentElement?.getBoundingClientRect();
|
|
8254
8373
|
const cssWidth = parentRect?.width || canvas.clientWidth || (typeof window !== "undefined" ? window.innerWidth : 960) || 960;
|
|
8255
8374
|
const cssHeight = parentRect?.height || canvas.clientHeight || (typeof window !== "undefined" ? window.innerHeight : 540) || 540;
|
|
8256
|
-
const width = Math.max(
|
|
8257
|
-
const height = Math.max(
|
|
8375
|
+
const width = Math.max(1, Math.round(Math.max(320, cssWidth * pixelRatio) * (getRootPerformanceQuality(canvas)?.resolutionScale ?? 1)));
|
|
8376
|
+
const height = Math.max(1, Math.round(Math.max(220, cssHeight * pixelRatio) * (getRootPerformanceQuality(canvas)?.resolutionScale ?? 1)));
|
|
8258
8377
|
if (canvas.width === width && canvas.height === height)
|
|
8259
8378
|
return;
|
|
8260
8379
|
canvas.style.width = `${cssWidth}px`;
|
|
@@ -8263,21 +8382,19 @@ pausedRenderTime = () => 0) {
|
|
|
8263
8382
|
canvas.height = height;
|
|
8264
8383
|
renderer.resize?.(width, height);
|
|
8265
8384
|
};
|
|
8385
|
+
const resourceOwner = new DeferredFrameResources(releaseResources, resizeRenderer);
|
|
8266
8386
|
const resizeObserver = options.resize !== false && typeof ResizeObserver !== "undefined" && canvas.parentElement
|
|
8267
8387
|
? new ResizeObserver(resizeRenderer)
|
|
8268
8388
|
: undefined;
|
|
8269
8389
|
resizeObserver?.observe(canvas.parentElement);
|
|
8270
8390
|
if (options.resize !== false && typeof window !== "undefined")
|
|
8271
8391
|
window.addEventListener("resize", resizeRenderer);
|
|
8272
|
-
|
|
8273
|
-
let animationHandle = 0;
|
|
8274
|
-
let lastTime = 0;
|
|
8275
|
-
const renderFrame = (time = performanceNow()) => {
|
|
8392
|
+
const renderFrame = (time = performanceNow(), submittedDrawCalls, explicit = false) => {
|
|
8276
8393
|
if (disposed)
|
|
8277
8394
|
return;
|
|
8278
8395
|
const delta = lastTime > 0 ? Math.max(1, time - lastTime) : 16.67;
|
|
8279
8396
|
lastTime = time;
|
|
8280
|
-
if (!isPaused())
|
|
8397
|
+
if (!explicit && !isPaused())
|
|
8281
8398
|
beforeRender?.(delta / 1000, "raf");
|
|
8282
8399
|
/*
|
|
8283
8400
|
* A paused app must not keep advancing time-driven rendering.
|
|
@@ -8292,8 +8409,8 @@ pausedRenderTime = () => 0) {
|
|
|
8292
8409
|
* `runtimeTime` and renders explicitly, so stepping still produces new frames — what stops is
|
|
8293
8410
|
* time passing on its own.
|
|
8294
8411
|
*/
|
|
8295
|
-
const renderTime =
|
|
8296
|
-
const drawCalls = renderer.render(renderTime);
|
|
8412
|
+
const renderTime = resolveRootRenderTime(time, explicit, isPaused, pausedRenderTime);
|
|
8413
|
+
const drawCalls = submittedDrawCalls ?? renderer.render(renderTime);
|
|
8297
8414
|
diagnosticsState.backend = renderer.backend;
|
|
8298
8415
|
diagnosticsState.fps = diagnosticsState.fps || 60;
|
|
8299
8416
|
diagnosticsState.drawCalls = drawCalls;
|
|
@@ -8321,12 +8438,91 @@ pausedRenderTime = () => 0) {
|
|
|
8321
8438
|
});
|
|
8322
8439
|
}
|
|
8323
8440
|
overlay?.update();
|
|
8324
|
-
if ((sceneWantsFrames || requiresFrames()) && options.autoStart !== false && typeof requestAnimationFrame !== "undefined") {
|
|
8325
|
-
animationHandle = requestAnimationFrame(
|
|
8441
|
+
if (!explicit && !isPaused() && (sceneWantsFrames || requiresFrames()) && options.autoStart !== false && typeof requestAnimationFrame !== "undefined") {
|
|
8442
|
+
animationHandle = requestAnimationFrame(scheduleFrame);
|
|
8326
8443
|
}
|
|
8327
8444
|
};
|
|
8328
|
-
|
|
8445
|
+
const submitAsync = (clock, automatic = false) => {
|
|
8446
|
+
if (disposed)
|
|
8447
|
+
return Promise.reject(new Error("Aura3D renderer is disposed."));
|
|
8448
|
+
if (!renderer.renderAsync)
|
|
8449
|
+
return Promise.reject(new Error("Aura3D asynchronous submission requires the production renderer."));
|
|
8450
|
+
pendingFrames += 1;
|
|
8451
|
+
const submitted = resourceOwner.run(() => frameTail.then(async () => {
|
|
8452
|
+
if (disposed)
|
|
8453
|
+
throw new Error("Aura3D renderer is disposed.");
|
|
8454
|
+
const time = clock();
|
|
8455
|
+
if (automatic) {
|
|
8456
|
+
const delta = lastTime > 0 ? Math.max(1, time - lastTime) : 16.67;
|
|
8457
|
+
lastTime = time;
|
|
8458
|
+
if (!isPaused())
|
|
8459
|
+
beforeRender?.(delta / 1000, "raf");
|
|
8460
|
+
}
|
|
8461
|
+
// User frame callbacks can dispose the app. Do not submit a new frame after
|
|
8462
|
+
// that callback; pending ownership still defers cleanup until this settles.
|
|
8463
|
+
if (disposed)
|
|
8464
|
+
throw new Error("Aura3D renderer is disposed.");
|
|
8465
|
+
const renderTime = resolveRootRenderTime(time, !automatic, isPaused, pausedRenderTime);
|
|
8466
|
+
const drawCalls = await renderer.renderAsync(renderTime);
|
|
8467
|
+
if (!disposed) {
|
|
8468
|
+
const previousLastTime = lastTime;
|
|
8469
|
+
renderFrame(renderTime, drawCalls, true);
|
|
8470
|
+
lastTime = previousLastTime;
|
|
8471
|
+
}
|
|
8472
|
+
}).finally(() => { pendingFrames -= 1; }));
|
|
8473
|
+
frameTail = submitted.catch(() => undefined);
|
|
8474
|
+
return submitted;
|
|
8475
|
+
};
|
|
8476
|
+
const scheduleFrame = (time) => {
|
|
8477
|
+
animationHandle = 0;
|
|
8478
|
+
if (disposed)
|
|
8479
|
+
return;
|
|
8480
|
+
if (options.frameMode === "async") {
|
|
8481
|
+
void submitAsync(() => time, true).then(() => {
|
|
8482
|
+
if (!disposed && !isPaused() && (sceneWantsFrames || requiresFrames()) && options.autoStart !== false && typeof requestAnimationFrame !== "undefined") {
|
|
8483
|
+
animationHandle = requestAnimationFrame(scheduleFrame);
|
|
8484
|
+
}
|
|
8485
|
+
}).catch((error) => {
|
|
8486
|
+
diagnosticsState.errors.push(productionRenderErrorMessage(error));
|
|
8487
|
+
overlay?.update();
|
|
8488
|
+
});
|
|
8489
|
+
}
|
|
8490
|
+
else if (pendingFrames > 0) {
|
|
8491
|
+
// A manual async frame owns the resources. Retry without advancing simulation.
|
|
8492
|
+
// A pause cancels automatic retries; the explicit owner completes independently.
|
|
8493
|
+
if (!isPaused())
|
|
8494
|
+
animationHandle = requestAnimationFrame(scheduleFrame);
|
|
8495
|
+
}
|
|
8496
|
+
else
|
|
8497
|
+
renderFrame(time);
|
|
8498
|
+
};
|
|
8499
|
+
try {
|
|
8500
|
+
if (options.frameMode === "async") {
|
|
8501
|
+
await submitAsync(() => performanceNow(), true);
|
|
8502
|
+
if (!isPaused() && (sceneWantsFrames || requiresFrames()) && options.autoStart !== false && typeof requestAnimationFrame !== "undefined") {
|
|
8503
|
+
animationHandle = requestAnimationFrame(scheduleFrame);
|
|
8504
|
+
}
|
|
8505
|
+
}
|
|
8506
|
+
else
|
|
8507
|
+
renderFrame();
|
|
8508
|
+
}
|
|
8509
|
+
catch (error) {
|
|
8510
|
+
// A failed first submission has no returned controller to dispose its resources.
|
|
8511
|
+
disposed = true;
|
|
8512
|
+
resizeObserver?.disconnect();
|
|
8513
|
+
if (typeof window !== "undefined")
|
|
8514
|
+
window.removeEventListener("resize", resizeRenderer);
|
|
8515
|
+
releaseResources();
|
|
8516
|
+
throw error;
|
|
8517
|
+
}
|
|
8329
8518
|
return {
|
|
8519
|
+
update(time) {
|
|
8520
|
+
if (disposed)
|
|
8521
|
+
throw new Error("Aura3D renderer is disposed.");
|
|
8522
|
+
if (pendingFrames > 0)
|
|
8523
|
+
throw new Error("Aura3D async frame is pending; await it before advancing renderer state.");
|
|
8524
|
+
renderer.update?.(time);
|
|
8525
|
+
},
|
|
8330
8526
|
render(time = performanceNow()) {
|
|
8331
8527
|
/*
|
|
8332
8528
|
* Preserve `lastTime` across an explicit render.
|
|
@@ -8337,10 +8533,50 @@ pausedRenderTime = () => 0) {
|
|
|
8337
8533
|
* different clock — a visible jump. Same defect the canvas2d path had; fixed identically so
|
|
8338
8534
|
* the two render paths cannot disagree about what `step` does.
|
|
8339
8535
|
*/
|
|
8536
|
+
if (pendingFrames > 0)
|
|
8537
|
+
throw new Error("Aura3D async frame is pending; await it before synchronous rendering.");
|
|
8340
8538
|
const previousLastTime = lastTime;
|
|
8341
|
-
renderFrame(time);
|
|
8539
|
+
renderFrame(time, undefined, true);
|
|
8342
8540
|
lastTime = previousLastTime;
|
|
8343
8541
|
},
|
|
8542
|
+
setPerformanceQuality(settings) {
|
|
8543
|
+
if (disposed || pendingFrames > 0)
|
|
8544
|
+
throw new Error("PERFORMANCE_QUALITY_BUSY: await the active frame before changing quality.");
|
|
8545
|
+
setRootPerformanceQuality(canvas, settings);
|
|
8546
|
+
if (options.resize !== false)
|
|
8547
|
+
resizeRenderer();
|
|
8548
|
+
else {
|
|
8549
|
+
const width = Math.max(1, Math.round(baseBackingSize.width * settings.resolutionScale));
|
|
8550
|
+
const height = Math.max(1, Math.round(baseBackingSize.height * settings.resolutionScale));
|
|
8551
|
+
canvas.width = width;
|
|
8552
|
+
canvas.height = height;
|
|
8553
|
+
renderer.resize?.(width, height);
|
|
8554
|
+
}
|
|
8555
|
+
renderer.resetTemporalHistory?.("performance-quality");
|
|
8556
|
+
},
|
|
8557
|
+
diagnostics: () => disposed || pendingFrames > 0 ? undefined : renderer.diagnostics,
|
|
8558
|
+
renderAsync: (clock) => submitAsync(clock),
|
|
8559
|
+
busy: () => pendingFrames > 0,
|
|
8560
|
+
whenIdle: () => frameTail,
|
|
8561
|
+
resetTemporalHistory(reason) {
|
|
8562
|
+
// Reset after the active submission, before the next one can consume history.
|
|
8563
|
+
if (pendingFrames > 0)
|
|
8564
|
+
frameTail = frameTail.then(() => { if (!disposed)
|
|
8565
|
+
renderer.resetTemporalHistory?.(reason); });
|
|
8566
|
+
else
|
|
8567
|
+
renderer.resetTemporalHistory?.(reason);
|
|
8568
|
+
},
|
|
8569
|
+
pause() {
|
|
8570
|
+
if (animationHandle && typeof cancelAnimationFrame !== "undefined")
|
|
8571
|
+
cancelAnimationFrame(animationHandle);
|
|
8572
|
+
animationHandle = 0;
|
|
8573
|
+
},
|
|
8574
|
+
resume() {
|
|
8575
|
+
if (disposed || animationHandle || options.autoStart === false || typeof requestAnimationFrame === "undefined")
|
|
8576
|
+
return;
|
|
8577
|
+
if (sceneWantsFrames || requiresFrames())
|
|
8578
|
+
animationHandle = requestAnimationFrame(scheduleFrame);
|
|
8579
|
+
},
|
|
8344
8580
|
// WS-2.6 — forward device-loss subscription from whichever renderer backs this controller.
|
|
8345
8581
|
onDeviceLost: renderer.onDeviceLost ? (listener) => renderer.onDeviceLost(listener) : undefined,
|
|
8346
8582
|
onDeviceRestored: renderer.onDeviceRestored ? (listener) => renderer.onDeviceRestored(listener) : undefined,
|
|
@@ -8352,8 +8588,7 @@ pausedRenderTime = () => 0) {
|
|
|
8352
8588
|
resizeObserver?.disconnect();
|
|
8353
8589
|
if (typeof window !== "undefined")
|
|
8354
8590
|
window.removeEventListener("resize", resizeRenderer);
|
|
8355
|
-
|
|
8356
|
-
renderer.dispose();
|
|
8591
|
+
resourceOwner.dispose();
|
|
8357
8592
|
}
|
|
8358
8593
|
};
|
|
8359
8594
|
}
|
|
@@ -8543,7 +8778,14 @@ function createProductionRuntimeEnvironmentFog(snapshot, lights = [], renderWidt
|
|
|
8543
8778
|
...(resolved.forward.heightReference !== undefined ? { heightReference: resolved.forward.heightReference } : {})
|
|
8544
8779
|
};
|
|
8545
8780
|
}
|
|
8546
|
-
|
|
8781
|
+
const productionTemporalSceneKeys = new WeakMap();
|
|
8782
|
+
let productionTemporalSceneSequence = 0;
|
|
8783
|
+
export function resolveNativeBloomRadius(authoredRadius) {
|
|
8784
|
+
const radius = authoredRadius ?? 0.38;
|
|
8785
|
+
const normalizedKernel = radius <= 1 ? Math.round(radius * 8) : Math.round(radius);
|
|
8786
|
+
return Math.max(1, Math.min(4, normalizedKernel));
|
|
8787
|
+
}
|
|
8788
|
+
function createProductionRuntimePostprocess(snapshot, lights = [], renderWidth = 1280, renderHeight = 720, temporalSupported = true) {
|
|
8547
8789
|
const nodes = groups.flatten(snapshot.nodes);
|
|
8548
8790
|
const authoredBloom = nodes.find((node) => node.kind === "effect" && node.effect === "bloom");
|
|
8549
8791
|
// A5: the DISTINCT volumetric-fog node submits the depth-aware inscatter
|
|
@@ -8569,26 +8811,42 @@ function createProductionRuntimePostprocess(snapshot, lights = [], renderWidth =
|
|
|
8569
8811
|
const authoredAmbientOcclusion = nodes.find((node) => node.kind === "effect" && node.effect === "ambient-occlusion");
|
|
8570
8812
|
const authoredContactOcclusion = nodes.find((node) => node.kind === "effect" && node.effect === "contact-occlusion");
|
|
8571
8813
|
const authoredOcclusion = authoredAmbientOcclusion ?? authoredContactOcclusion;
|
|
8572
|
-
//
|
|
8573
|
-
//
|
|
8574
|
-
// root binding): the status layer warns instead of submitting doomed options
|
|
8575
|
-
// that would fail the route with zero draw calls.
|
|
8814
|
+
// The renderer owns temporal GPU inputs. Root checks actual drawable
|
|
8815
|
+
// geometry before requesting them; unsupported scenes retain ordinary output.
|
|
8576
8816
|
const authoredColorGrade = nodes.find((node) => node.kind === "effect" && node.effect === "color-grade");
|
|
8577
8817
|
const authoredAntiAlias = nodes.find((node) => node.kind === "effect" && node.effect === "anti-alias");
|
|
8578
8818
|
const authoredOutline = nodes.find((node) => node.kind === "effect" && node.effect === "outline");
|
|
8579
8819
|
const authoredSsr = nodes.find((node) => node.kind === "effect" && node.effect === "screen-space-reflections");
|
|
8580
8820
|
const authoredDof = nodes.find((node) => node.kind === "effect" && node.effect === "depth-of-field");
|
|
8821
|
+
const authoredMotionBlur = nodes.find((node) => node.kind === "effect" && node.effect === "motion-blur");
|
|
8822
|
+
const temporalRequested = temporalSupported && (Boolean(authoredMotionBlur) || authoredAntiAlias?.mode === "taa");
|
|
8823
|
+
let sceneKey = productionTemporalSceneKeys.get(snapshot);
|
|
8824
|
+
if (!sceneKey) {
|
|
8825
|
+
sceneKey = `root-scene-${++productionTemporalSceneSequence}`;
|
|
8826
|
+
productionTemporalSceneKeys.set(snapshot, sceneKey);
|
|
8827
|
+
}
|
|
8581
8828
|
const fxaaRequested = (authoredAntiAlias?.mode ?? "fxaa") === "fxaa";
|
|
8582
8829
|
const outlineChannels = colorToRgba(authoredOutline?.color ?? "#ff9822");
|
|
8583
8830
|
return {
|
|
8584
8831
|
// Tone mapping requires unclamped linear input. RGBA8 quantized dark clear
|
|
8585
8832
|
// colors and clipped highlights before ACES, which produced washed-out output.
|
|
8586
8833
|
targetFormat: "rgba16f",
|
|
8834
|
+
...(temporalRequested ? {
|
|
8835
|
+
temporal: { sceneKey },
|
|
8836
|
+
...(authoredMotionBlur ? { motionBlur: { samples: 8, scale: clampNumber(authoredMotionBlur.intensity ?? .5, 0, 2) } } : {}),
|
|
8837
|
+
...(authoredAntiAlias?.mode === "taa" ? { taa: { blend: .9 } } : {})
|
|
8838
|
+
} : {}),
|
|
8587
8839
|
...(bloomRequested ? {
|
|
8588
8840
|
bloom: {
|
|
8589
8841
|
threshold: clampNumber(authoredBloom?.threshold ?? 0.78, 0, 1),
|
|
8590
8842
|
intensity: clampNumber(authoredBloom?.intensity ?? 0.3, 0, 2),
|
|
8591
|
-
|
|
8843
|
+
// Public bloom radius follows the normalized UnrealBloomPass-style
|
|
8844
|
+
// control used by routes and prefabs (normally 0..1), while the native
|
|
8845
|
+
// separable blur consumes an integer pixel kernel. Preserve legacy
|
|
8846
|
+
// explicit pixel kernels above 1 and spread normalized values across
|
|
8847
|
+
// 1..4 so common authored values such as 0.22/0.38 do not all collapse
|
|
8848
|
+
// to a one-pixel blur after rounding.
|
|
8849
|
+
radius: resolveNativeBloomRadius(authoredBloom?.radius),
|
|
8592
8850
|
...(authoredBloom?.quality !== undefined ? { quality: authoredBloom.quality } : {}),
|
|
8593
8851
|
...(authoredBloom?.softKnee !== undefined ? { softKnee: authoredBloom.softKnee } : {}),
|
|
8594
8852
|
...(authoredBloom?.shoulder !== undefined ? { shoulder: authoredBloom.shoulder } : {})
|
|
@@ -8665,11 +8923,11 @@ function createProductionRuntimeShadowOptions(snapshot, collectedLights) {
|
|
|
8665
8923
|
}, 1);
|
|
8666
8924
|
const shadowCaster = collectedLights.find((light) => light.castsShadow);
|
|
8667
8925
|
const size = sceneRadius > 30 ? 4096 : sceneRadius > 10 ? 2048 : 1024;
|
|
8668
|
-
const
|
|
8926
|
+
const tuning = resolveProductionRuntimeShadowTuning(shadowCaster?.kind, size, sceneRadius);
|
|
8669
8927
|
return {
|
|
8670
8928
|
enabled: Boolean(shadowCaster),
|
|
8671
8929
|
size,
|
|
8672
|
-
|
|
8930
|
+
...tuning,
|
|
8673
8931
|
strength: category === "city-day" ? 0.38
|
|
8674
8932
|
: category === "material" || category === "product" ? 0.24
|
|
8675
8933
|
: 0.32,
|
|
@@ -8679,6 +8937,30 @@ function createProductionRuntimeShadowOptions(snapshot, collectedLights) {
|
|
|
8679
8937
|
label: `aura3d-root-production-${category}-${size}px-shadow-map`
|
|
8680
8938
|
};
|
|
8681
8939
|
}
|
|
8940
|
+
/**
|
|
8941
|
+
* Shadow bias is compared in normalized light-depth space. Directional maps use
|
|
8942
|
+
* an orthographic projection, so their existing scene-radius/texel heuristic is
|
|
8943
|
+
* world-linear. A spot map is perspective: applying that world-space value at
|
|
8944
|
+
* the far end of its cone erased legitimate caster/receiver separation (Aura
|
|
8945
|
+
* Clash measured 0.00147 normalized depth versus the old 0.004 constant bias),
|
|
8946
|
+
* and the default slope term could erase the remainder on a vertical receiver.
|
|
8947
|
+
* Keep the established directional/point policy intact and use a bounded
|
|
8948
|
+
* fraction of one normalized spot-map texel for both perspective terms.
|
|
8949
|
+
*/
|
|
8950
|
+
export function resolveProductionRuntimeShadowTuning(kind, size, sceneRadius) {
|
|
8951
|
+
if (!Number.isInteger(size) || size <= 0)
|
|
8952
|
+
throw new RangeError("Shadow-map size must be a positive integer.");
|
|
8953
|
+
if (!Number.isFinite(sceneRadius) || sceneRadius <= 0)
|
|
8954
|
+
throw new RangeError("Shadow scene radius must be finite and positive.");
|
|
8955
|
+
if (kind === "spot") {
|
|
8956
|
+
return {
|
|
8957
|
+
bias: clampNumber(0.15 / size, 0.00005, 0.00035),
|
|
8958
|
+
slopeBias: 0.12
|
|
8959
|
+
};
|
|
8960
|
+
}
|
|
8961
|
+
const texelWorldSize = (sceneRadius * 2) / size;
|
|
8962
|
+
return { bias: clampNumber(texelWorldSize * 0.55, 0.00035, 0.004) };
|
|
8963
|
+
}
|
|
8682
8964
|
/**
|
|
8683
8965
|
* Device-observed shadow state. `requested` comes from the submitted shadow
|
|
8684
8966
|
* options, but `mapRendered`/`mapSampled` come only from what the device
|
|
@@ -8686,11 +8968,12 @@ function createProductionRuntimeShadowOptions(snapshot, collectedLights) {
|
|
|
8686
8968
|
* sample it. This is what stops the root report from publishing an unconditional
|
|
8687
8969
|
* `shadows.enabled: true`.
|
|
8688
8970
|
*/
|
|
8689
|
-
function createProductionRuntimeShadowObservation(shadowOptions, diagnostics) {
|
|
8971
|
+
function createProductionRuntimeShadowObservation(shadowOptions, diagnostics, observed) {
|
|
8690
8972
|
const nativeShadowMapBindings = diagnostics.nativeShadowMapBindings ?? 0;
|
|
8691
|
-
const mapRendered = Boolean(shadowOptions.enabled) && (diagnostics.shadowRenderTargetsAllocated ?? 0) > 0;
|
|
8973
|
+
const mapRendered = Boolean(shadowOptions.enabled) && observed !== null && (diagnostics.shadowRenderTargetsAllocated ?? 0) > 0;
|
|
8692
8974
|
return {
|
|
8693
8975
|
requested: Boolean(shadowOptions.enabled),
|
|
8976
|
+
observed,
|
|
8694
8977
|
mapRendered,
|
|
8695
8978
|
mapSampled: mapRendered && nativeShadowMapBindings > 0,
|
|
8696
8979
|
mapSize: shadowOptions.size,
|
|
@@ -8802,17 +9085,26 @@ function createProductionRuntimeCollectedLights(snapshot) {
|
|
|
8802
9085
|
* all-unrequested scenes keep the legacy priority-then-intensity order.
|
|
8803
9086
|
*/
|
|
8804
9087
|
export function resolveProductionShadowCasterIndex(descriptors) {
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
8815
|
-
|
|
9088
|
+
const explicitlyRequested = descriptors
|
|
9089
|
+
.map((descriptor, index) => ({ descriptor, index }))
|
|
9090
|
+
.filter(({ descriptor }) => descriptor.shadowRequested);
|
|
9091
|
+
// An authored false is scene-level opt-out when no light is explicitly
|
|
9092
|
+
// requested. Otherwise another unspecified light would silently take the
|
|
9093
|
+
// legacy caster slot and make `shadow: false` visually ineffective.
|
|
9094
|
+
if (explicitlyRequested.length === 0 && descriptors.some((descriptor) => descriptor.shadowDisabled === true)) {
|
|
9095
|
+
return -1;
|
|
9096
|
+
}
|
|
9097
|
+
const candidates = explicitlyRequested.length > 0
|
|
9098
|
+
? explicitlyRequested
|
|
9099
|
+
: descriptors.map((descriptor, index) => ({ descriptor, index }));
|
|
9100
|
+
return candidates.reduce((selected, candidate) => {
|
|
9101
|
+
if (selected === undefined)
|
|
9102
|
+
return candidate;
|
|
9103
|
+
if (candidate.descriptor.shadowPriority !== selected.descriptor.shadowPriority) {
|
|
9104
|
+
return candidate.descriptor.shadowPriority > selected.descriptor.shadowPriority ? candidate : selected;
|
|
9105
|
+
}
|
|
9106
|
+
return candidate.descriptor.intensity > selected.descriptor.intensity ? candidate : selected;
|
|
9107
|
+
}, undefined)?.index ?? -1;
|
|
8816
9108
|
}
|
|
8817
9109
|
function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
8818
9110
|
const name = node.name?.trim() || `aura-authored-${node.light}-${authoredIndex + 1}`;
|
|
@@ -8837,7 +9129,8 @@ function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
|
8837
9129
|
spotAngle: 0,
|
|
8838
9130
|
penumbra: 0,
|
|
8839
9131
|
shadowPriority: 3,
|
|
8840
|
-
shadowRequested:
|
|
9132
|
+
shadowRequested: node.shadow === true,
|
|
9133
|
+
shadowDisabled: node.shadow === false,
|
|
8841
9134
|
authoredLight: node.light
|
|
8842
9135
|
}];
|
|
8843
9136
|
}
|
|
@@ -8876,7 +9169,8 @@ function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
|
8876
9169
|
spotAngle: clampNumber(node.angle ?? Math.PI / 6, 0.08, Math.PI / 2 - 0.01),
|
|
8877
9170
|
penumbra: clampNumber(node.penumbra ?? 0.4, 0, 1),
|
|
8878
9171
|
shadowPriority: 2,
|
|
8879
|
-
shadowRequested: node.shadow
|
|
9172
|
+
shadowRequested: node.shadow === true,
|
|
9173
|
+
shadowDisabled: node.shadow === false,
|
|
8880
9174
|
authoredLight: node.light
|
|
8881
9175
|
}];
|
|
8882
9176
|
}
|
|
@@ -9164,6 +9458,7 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
9164
9458
|
height: canvas.height,
|
|
9165
9459
|
backend: "webgl2",
|
|
9166
9460
|
antialias: true,
|
|
9461
|
+
...(getRootRenderSource(canvas) ? { errorCheckMode: "frame" } : {}),
|
|
9167
9462
|
preserveDrawingBuffer: true,
|
|
9168
9463
|
// Background colors are display intent. Pre-invert the renderer's coupled
|
|
9169
9464
|
// matrix-fitted ACES transform so presentation preserves that authored color.
|
|
@@ -9181,7 +9476,7 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
9181
9476
|
const textureUpgradeWarnings = new Set();
|
|
9182
9477
|
void upgradeProductionPrimitiveTextures(primitiveEntries, (message) => {
|
|
9183
9478
|
textureUpgradeWarnings.add(message);
|
|
9184
|
-
}).catch((error) => {
|
|
9479
|
+
}, Number(canvas.getContext("webgl2")?.getParameter(WebGL2RenderingContext.MAX_TEXTURE_SIZE) ?? 4096)).catch((error) => {
|
|
9185
9480
|
textureUpgradeWarnings.add(`textured upgrade pass failed (${error instanceof Error ? error.message : String(error)}); scalar materials retained`);
|
|
9186
9481
|
});
|
|
9187
9482
|
const productionEnvironment = createProductionRuntimeEnvironment(snapshot);
|
|
@@ -9224,7 +9519,7 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
9224
9519
|
// N1 + G1 + M2 observations refresh on every diagnostics read from the
|
|
9225
9520
|
// live entries/device state — never cached intent.
|
|
9226
9521
|
const buildDiagnostics = () => {
|
|
9227
|
-
const shadowObservation = createProductionRuntimeShadowObservation(createProductionRuntimeShadowOptions(snapshot, productionRuntimeLights), latestDeviceDiagnostics);
|
|
9522
|
+
const shadowObservation = createProductionRuntimeShadowObservation({ ...createProductionRuntimeShadowOptions(snapshot, productionRuntimeLights), ...(getRootPerformanceQuality(canvas) ? { size: getRootPerformanceQuality(canvas).shadowSize } : {}) }, latestDeviceDiagnostics, productionRenderer.getShadowEvidence());
|
|
9228
9523
|
const spotCaster = productionRuntimeLights.find((light) => light.castsShadow);
|
|
9229
9524
|
const spotAngle = spotCaster?.kind === "spot" ? spotCaster.spotAngle : undefined;
|
|
9230
9525
|
const texturesObservation = createProductionTexturesObservation(primitiveEntries, latestCameraEye, rendererOptions?.textureBudgetBytes ?? normalizeTextureBudgetBytes(undefined));
|
|
@@ -9307,6 +9602,17 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
9307
9602
|
})))
|
|
9308
9603
|
}, rendererOptions);
|
|
9309
9604
|
};
|
|
9605
|
+
let preparedFrame;
|
|
9606
|
+
const buildFrame = (time) => {
|
|
9607
|
+
runtimeWarnings.clear();
|
|
9608
|
+
latestCameraEye = resolveCameraFrame(snapshot, snapshot.camera, time, runtimeNodes).eye;
|
|
9609
|
+
return createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, primitiveEntries, time, runtimeNodes, runtimeWarnings, currentEnvironmentLighting, productionRuntimeLights);
|
|
9610
|
+
};
|
|
9611
|
+
const takePreparedFrame = (time) => {
|
|
9612
|
+
const prepared = preparedFrame?.time === time ? preparedFrame.input : undefined;
|
|
9613
|
+
preparedFrame = undefined;
|
|
9614
|
+
return prepared ?? buildFrame(time);
|
|
9615
|
+
};
|
|
9310
9616
|
return {
|
|
9311
9617
|
get backend() {
|
|
9312
9618
|
return productionRenderer.backend;
|
|
@@ -9314,18 +9620,34 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
9314
9620
|
get diagnostics() {
|
|
9315
9621
|
return buildDiagnostics();
|
|
9316
9622
|
},
|
|
9623
|
+
update(time) {
|
|
9624
|
+
// Building the frame applies controller-bound actor clips, root-motion
|
|
9625
|
+
// consumption, foot IK, morphs and imported-asset evidence. Discarding the
|
|
9626
|
+
// returned input deliberately avoids a GPU/device submission.
|
|
9627
|
+
preparedFrame = { time, input: buildFrame(time) };
|
|
9628
|
+
},
|
|
9317
9629
|
render(time) {
|
|
9318
|
-
|
|
9319
|
-
latestCameraEye = resolveCameraFrame(snapshot, snapshot.camera, time, runtimeNodes).eye;
|
|
9320
|
-
const input = createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, primitiveEntries, time, runtimeNodes, runtimeWarnings, currentEnvironmentLighting, productionRuntimeLights);
|
|
9630
|
+
const input = takePreparedFrame(time);
|
|
9321
9631
|
const result = productionRenderer.renderInteractiveFrame(input);
|
|
9322
9632
|
latestDeviceDiagnostics = result.diagnostics;
|
|
9633
|
+
getRootRenderSource(canvas)?.onFrame?.(latestDeviceDiagnostics, [...(input.source.collectRenderItems?.() ?? [])]);
|
|
9634
|
+
latestFeatures = result.features;
|
|
9635
|
+
return latestDeviceDiagnostics.drawCalls;
|
|
9636
|
+
},
|
|
9637
|
+
async renderAsync(time) {
|
|
9638
|
+
const input = takePreparedFrame(time);
|
|
9639
|
+
const result = await productionRenderer.renderInteractiveFrameAsync(input);
|
|
9640
|
+
latestDeviceDiagnostics = result.diagnostics;
|
|
9641
|
+
getRootRenderSource(canvas)?.onFrame?.(latestDeviceDiagnostics, [...(input.source.collectRenderItems?.() ?? [])]);
|
|
9323
9642
|
latestFeatures = result.features;
|
|
9324
9643
|
return latestDeviceDiagnostics.drawCalls;
|
|
9325
9644
|
},
|
|
9326
9645
|
viewProjection(time) {
|
|
9327
9646
|
return createViewProjection(snapshot, canvas.width / Math.max(1, canvas.height), time, runtimeNodes);
|
|
9328
9647
|
},
|
|
9648
|
+
resetTemporalHistory(reason) {
|
|
9649
|
+
productionRenderer.resetTemporalHistory(reason);
|
|
9650
|
+
},
|
|
9329
9651
|
resize(width, height) {
|
|
9330
9652
|
productionRenderer.resize(width, height);
|
|
9331
9653
|
},
|
|
@@ -9344,7 +9666,8 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
9344
9666
|
for (const { actor } of actorEntries)
|
|
9345
9667
|
actor.dispose();
|
|
9346
9668
|
for (const { resources } of primitiveEntries) {
|
|
9347
|
-
for (const { geometry, material, texturedMaterial } of resources) {
|
|
9669
|
+
for (const { geometry, material, texturedMaterial, textureDisposer } of resources) {
|
|
9670
|
+
textureDisposer?.();
|
|
9348
9671
|
geometry.dispose();
|
|
9349
9672
|
material.dispose();
|
|
9350
9673
|
texturedMaterial?.dispose();
|
|
@@ -9354,19 +9677,36 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
9354
9677
|
};
|
|
9355
9678
|
}
|
|
9356
9679
|
function createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, primitiveEntries, time, runtimeNodes, runtimeWarnings, environmentLighting, collectedLights) {
|
|
9357
|
-
const
|
|
9680
|
+
const compatibility = getRootRenderSource(canvas);
|
|
9681
|
+
const attachedItems = compatibility ? [...(compatibility.source.collectRenderItems?.() ?? compatibility.source.renderItems ?? [])] : [];
|
|
9682
|
+
const items = [...attachedItems];
|
|
9358
9683
|
const viewProjectionMatrix = createViewProjection(snapshot, canvas.width / Math.max(1, canvas.height), time, runtimeNodes);
|
|
9359
9684
|
const cameraPosition = resolveCameraFrame(snapshot, snapshot.camera, time, runtimeNodes).eye;
|
|
9360
|
-
for (const entry of actorEntries) {
|
|
9361
|
-
|
|
9362
|
-
|
|
9685
|
+
for (const [actorIndex, entry] of actorEntries.entries()) {
|
|
9686
|
+
let currentState = resolveProductionActorRuntimeState(entry, runtimeNodes);
|
|
9687
|
+
let currentNode = currentState.node;
|
|
9363
9688
|
if (currentNode.visible === false)
|
|
9364
9689
|
continue;
|
|
9365
|
-
|
|
9690
|
+
let modelMatrix = [...createModelMatrix(currentNode, productionActorModelBounds(currentNode.asset, entry.actor), shouldNormalizeModelNode(currentNode), time)];
|
|
9366
9691
|
// The foot-planting post-pass solves in the same world space this matrix draws into;
|
|
9367
9692
|
// refresh its matrix before the clip plays so the solve uses this frame, not the last.
|
|
9368
9693
|
applyProductionActorFootPlanting(entry, currentState.animationBinding, modelMatrix, runtimeWarnings);
|
|
9369
|
-
|
|
9694
|
+
if (currentState.animationPose) {
|
|
9695
|
+
try {
|
|
9696
|
+
entry.actor.applyRetargetedPose(currentState.animationPose, currentState.animationPoseTime ?? time);
|
|
9697
|
+
entry.rootMotionCursors = undefined;
|
|
9698
|
+
}
|
|
9699
|
+
catch (error) {
|
|
9700
|
+
runtimeWarnings.add(`Typed GLB actor "${entry.actor.id}" failed to apply bound pose: ${productionRenderErrorMessage(error)}`);
|
|
9701
|
+
}
|
|
9702
|
+
}
|
|
9703
|
+
else
|
|
9704
|
+
applyProductionActorAnimation(entry, currentNode, currentState.animationBinding, time, runtimeWarnings, modelMatrix, () => {
|
|
9705
|
+
currentState = resolveProductionActorRuntimeState(entry, runtimeNodes);
|
|
9706
|
+
currentNode = currentState.node;
|
|
9707
|
+
modelMatrix = [...createModelMatrix(currentNode, productionActorModelBounds(currentNode.asset, entry.actor), shouldNormalizeModelNode(currentNode), time)];
|
|
9708
|
+
applyProductionActorFootPlanting(entry, currentState.animationBinding, modelMatrix, runtimeWarnings);
|
|
9709
|
+
});
|
|
9370
9710
|
applyProductionActorMorphTargets(entry, currentState.morphTargets, runtimeWarnings);
|
|
9371
9711
|
// Wrinkle detail (E1 face-rig demo): resolve morph weights through the model's hook.
|
|
9372
9712
|
// Absent hook (or empty weights) resolves to 0 = today's rendering exactly.
|
|
@@ -9403,7 +9743,25 @@ function createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, pr
|
|
|
9403
9743
|
}
|
|
9404
9744
|
if (currentNode.instancedModelWarning)
|
|
9405
9745
|
runtimeWarnings.add(currentNode.instancedModelWarning);
|
|
9406
|
-
|
|
9746
|
+
// A GLB mesh has its own authored local/world transform. Native instance
|
|
9747
|
+
// transforms must apply the complete normalized node placement BEFORE that
|
|
9748
|
+
// mesh transform, exactly like an individually mounted actor. The previous
|
|
9749
|
+
// base(actorRoot * mesh) * instance(actorRoot) order normalized twice and
|
|
9750
|
+
// rotated/scaled the instance translations in the mesh's coordinate system.
|
|
9751
|
+
const localActorItems = modelInstanceAttach.instanceTransforms
|
|
9752
|
+
? entry.actor.collectRenderItems({ modelMatrix: identityMat4(), ...(wrinkleStrength === undefined ? {} : { wrinkleStrength }) })
|
|
9753
|
+
: undefined;
|
|
9754
|
+
items.push(...actorItems.map((item, itemIndex) => {
|
|
9755
|
+
const nativeMatrices = modelInstanceAttach.instanceTransforms;
|
|
9756
|
+
const localItem = localActorItems?.[itemIndex];
|
|
9757
|
+
if (nativeMatrices && !localItem)
|
|
9758
|
+
throw new Error("Instanced GLB mesh collection changed within one frame");
|
|
9759
|
+
return { ...item, label: `actor-${actorIndex}:mesh-${itemIndex}:${item.label ?? "mesh"}`, castShadow: currentNode.castShadow, ...modelInstanceAttach,
|
|
9760
|
+
...(nativeMatrices && localItem ? {
|
|
9761
|
+
modelMatrix: identityMat4(),
|
|
9762
|
+
instanceTransforms: composeModelInstanceMatrices(nativeMatrices, localItem.modelMatrix ?? identityMat4())
|
|
9763
|
+
} : {}) };
|
|
9764
|
+
}));
|
|
9407
9765
|
attachProductionActorEvidence(currentNode, entry.actor, actorItems, runtimeNodes);
|
|
9408
9766
|
}
|
|
9409
9767
|
// G1 SDF occlusion test, built once per frame only when SDF quads exist
|
|
@@ -9412,11 +9770,11 @@ function createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, pr
|
|
|
9412
9770
|
const sdfOcclusionTest = sdfEntries.length > 0
|
|
9413
9771
|
? createSceneLabelOcclusionTest(snapshot, cameraPosition, runtimeNodes)
|
|
9414
9772
|
: undefined;
|
|
9415
|
-
for (const entry of primitiveEntries) {
|
|
9773
|
+
for (const [primitiveIndex, entry] of primitiveEntries.entries()) {
|
|
9416
9774
|
const currentState = resolveProductionPrimitiveRuntimeState(entry, runtimeNodes);
|
|
9417
9775
|
if (!currentState.visible)
|
|
9418
9776
|
continue;
|
|
9419
|
-
const resource = selectProductionPrimitiveResource(entry, currentState.node, cameraPosition);
|
|
9777
|
+
const resource = selectProductionPrimitiveResource(entry, currentState.node, cameraPosition, getRootPerformanceQuality(canvas)?.lodBias ?? 1);
|
|
9420
9778
|
if (resource.sdfText && resource.texturedMaterial) {
|
|
9421
9779
|
// G1 per-frame SDF opacity: LOD fade from the live camera distance
|
|
9422
9780
|
// times the scene occlusion policy, written to the quad material.
|
|
@@ -9445,32 +9803,43 @@ function createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, pr
|
|
|
9445
9803
|
geometry: resource.geometry,
|
|
9446
9804
|
material: resource.texturedMaterial ?? resource.material,
|
|
9447
9805
|
modelMatrix: createModelMatrix(currentState.node, resource.bounds, false, time),
|
|
9448
|
-
label: currentState.node.name ??
|
|
9806
|
+
label: `primitive-${primitiveIndex}:${resource.name}:${currentState.node.name ?? currentState.node.primitive}`,
|
|
9449
9807
|
castShadow: currentState.node.castShadow,
|
|
9450
9808
|
includeInAutoFrame: false,
|
|
9451
9809
|
...(currentState.node.instances ? { instanceTransforms: createProductionInstanceTransforms(currentState.node.instances, currentState.node), instanceColors: createProductionInstanceColors(currentState.node.instanceColors, currentState.node.instances.length) } : {})
|
|
9452
9810
|
});
|
|
9453
9811
|
}
|
|
9812
|
+
const unsupportedTemporal = items.find(item => {
|
|
9813
|
+
const base = item.material && "baseMaterial" in item.material ? item.material.baseMaterial : item.material;
|
|
9814
|
+
return Boolean(item.skinning || item.morphTargets?.length || item.instanceTransforms?.length || item.geometry.topology !== "triangles" || base?.renderState.blend);
|
|
9815
|
+
});
|
|
9816
|
+
const temporalRequested = groups.flatten(snapshot.nodes).some(node => node.kind === "effect" && (node.effect === "motion-blur" || (node.effect === "anti-alias" && node.mode === "taa")));
|
|
9817
|
+
for (const warning of runtimeWarnings)
|
|
9818
|
+
if (warning.startsWith("TEMPORAL_UNSUPPORTED_GEOMETRY:"))
|
|
9819
|
+
runtimeWarnings.delete(warning);
|
|
9820
|
+
if (temporalRequested && unsupportedTemporal)
|
|
9821
|
+
runtimeWarnings.add(`TEMPORAL_UNSUPPORTED_GEOMETRY: ${unsupportedTemporal.label}; requires opaque rigid noninstanced triangles`);
|
|
9454
9822
|
const source = {
|
|
9823
|
+
...(compatibility?.source ?? {}),
|
|
9455
9824
|
collectRenderItems: () => items,
|
|
9456
|
-
cameraPolicy: "require",
|
|
9457
|
-
staticBatching:
|
|
9825
|
+
cameraPolicy: compatibility?.source.cameraPolicy ?? "require",
|
|
9826
|
+
staticBatching: !temporalRequested,
|
|
9458
9827
|
frustumCulling: true,
|
|
9459
|
-
collectedLights,
|
|
9460
|
-
environmentLighting,
|
|
9828
|
+
collectedLights: [...collectedLights, ...(compatibility?.source.collectedLights ?? [])],
|
|
9829
|
+
environmentLighting: compatibility?.source.environmentLighting ?? environmentLighting,
|
|
9461
9830
|
// The production runtime owns the pixel-backed HDR target and pass chain for
|
|
9462
9831
|
// routes that request effects. The diagnostics are device-observed, so a
|
|
9463
9832
|
// compositor failure is reported as fallback rather than claimed as a pass.
|
|
9464
|
-
postprocess: createProductionRuntimePostprocess(snapshot, collectedLights, canvas.width, canvas.height),
|
|
9465
|
-
shadow: createProductionRuntimeShadowOptions(snapshot, collectedLights),
|
|
9466
|
-
environmentFog: createProductionRuntimeEnvironmentFog(snapshot, collectedLights, canvas.width, canvas.height),
|
|
9467
|
-
cameraPosition
|
|
9833
|
+
postprocess: compatibility?.source.postprocess ?? createProductionRuntimePostprocess(snapshot, collectedLights, canvas.width, canvas.height, !unsupportedTemporal),
|
|
9834
|
+
shadow: { ...createProductionRuntimeShadowOptions(snapshot, collectedLights), ...(getRootPerformanceQuality(canvas) ? { size: getRootPerformanceQuality(canvas).shadowSize } : {}) },
|
|
9835
|
+
environmentFog: compatibility?.source.environmentFog ?? createProductionRuntimeEnvironmentFog(snapshot, collectedLights, canvas.width, canvas.height),
|
|
9836
|
+
...(compatibility?.source.cameraPolicy === "auto-frame" ? {} : { cameraPosition })
|
|
9468
9837
|
};
|
|
9469
9838
|
const cameraLike = { viewProjectionMatrix };
|
|
9470
9839
|
return {
|
|
9471
9840
|
source,
|
|
9472
|
-
camera: cameraLike,
|
|
9473
|
-
metadata: createProductionRuntimeMetadata(actorEntries, primitiveEntries)
|
|
9841
|
+
...(compatibility?.source.cameraPolicy === "auto-frame" ? {} : { camera: cameraLike }),
|
|
9842
|
+
metadata: includeRootSourceMetadata(createProductionRuntimeMetadata(actorEntries, primitiveEntries), attachedItems)
|
|
9474
9843
|
};
|
|
9475
9844
|
}
|
|
9476
9845
|
function createProductionRuntimePrimitiveEntries(nodes) {
|
|
@@ -9486,6 +9855,7 @@ function blankProductionPrimitiveTextureState() {
|
|
|
9486
9855
|
* fetchable urls; procedural inputs have no rasterizer and are reported so
|
|
9487
9856
|
* the caller can warn instead of silently dropping them.
|
|
9488
9857
|
*/
|
|
9858
|
+
const ROOT_EXTENSION_TEXTURE_SLOTS = ["clearcoat", "clearcoatRoughness", "clearcoatNormal", "sheenColor", "sheenRoughness", "iridescence", "iridescenceThickness", "anisotropy"];
|
|
9489
9859
|
export function createProductionPrimitiveTextureIntent(materialSpec) {
|
|
9490
9860
|
const refUrl = (input) => input && typeof input === "object" && input.kind === "aura-asset-ref"
|
|
9491
9861
|
? input.url ?? undefined
|
|
@@ -9507,6 +9877,25 @@ export function createProductionPrimitiveTextureIntent(materialSpec) {
|
|
|
9507
9877
|
const metalnessUrl = refUrl(materialSpec?.metalnessMap);
|
|
9508
9878
|
const occlusionUrl = refUrl(materialSpec?.occlusionMap);
|
|
9509
9879
|
const emissiveUrl = refUrl(materialSpec?.emissiveMap);
|
|
9880
|
+
const extensionMaps = Object.fromEntries(ROOT_EXTENSION_TEXTURE_SLOTS.flatMap((slot) => {
|
|
9881
|
+
const input = materialSpec?.[`${slot}Map`];
|
|
9882
|
+
if (input === undefined)
|
|
9883
|
+
return [];
|
|
9884
|
+
if (!input || typeof input !== "object" || input.kind !== "aura-asset-ref" || input.type !== "texture") {
|
|
9885
|
+
throw new AuraRuntimeError("unsupported-texture", `${slot}Map requires a typed texture asset reference`);
|
|
9886
|
+
}
|
|
9887
|
+
if (!["png", "jpg", "jpeg", "webp"].includes(input.format) || !input.url) {
|
|
9888
|
+
throw new AuraRuntimeError("unsupported-texture", `${slot}Map requires a fetchable png, jpg, jpeg or webp texture`);
|
|
9889
|
+
}
|
|
9890
|
+
const texCoord = materialSpec?.texCoords?.[slot] ?? 0;
|
|
9891
|
+
if (texCoord !== 0 && texCoord !== 1)
|
|
9892
|
+
throw new RangeError(`${slot} texCoord must be 0 or 1`);
|
|
9893
|
+
const transform = materialSpec?.texTransforms?.[slot];
|
|
9894
|
+
if (transform && [...(transform.offset ?? [0, 0]), ...(transform.scale ?? [1, 1]), transform.rotation ?? 0].some((value) => !Number.isFinite(value))) {
|
|
9895
|
+
throw new RangeError(`${slot} texture transform must contain finite values`);
|
|
9896
|
+
}
|
|
9897
|
+
return [[slot, { url: input.url, colorSpace: slot === "sheenColor" ? "srgb" : "linear", texCoord, ...(transform ? { transform } : {}) }]];
|
|
9898
|
+
}));
|
|
9510
9899
|
return {
|
|
9511
9900
|
...(baseColorUrl ? { baseColorUrl } : {}),
|
|
9512
9901
|
...(normalUrl ? { normalUrl } : {}),
|
|
@@ -9514,6 +9903,7 @@ export function createProductionPrimitiveTextureIntent(materialSpec) {
|
|
|
9514
9903
|
...(metalnessUrl ? { metalnessUrl } : {}),
|
|
9515
9904
|
...(occlusionUrl ? { occlusionUrl } : {}),
|
|
9516
9905
|
...(emissiveUrl ? { emissiveUrl } : {}),
|
|
9906
|
+
...(Object.keys(extensionMaps).length ? { extensionMaps } : {}),
|
|
9517
9907
|
proceduralInputs: procedurals
|
|
9518
9908
|
};
|
|
9519
9909
|
}
|
|
@@ -9522,6 +9912,7 @@ export function createProductionPrimitiveTextureIntent(materialSpec) {
|
|
|
9522
9912
|
* R = occlusion (unused here, forced to 255), G = roughness, B = metallic.
|
|
9523
9913
|
* Missing channels fall back to the scalar spec values.
|
|
9524
9914
|
*/
|
|
9915
|
+
const productionPrimitiveBitmapPixels = new WeakMap();
|
|
9525
9916
|
async function loadProductionPrimitiveBitmap(url) {
|
|
9526
9917
|
if (typeof fetch !== "function" || typeof createImageBitmap !== "function") {
|
|
9527
9918
|
throw new Error("texture fetch requires fetch + createImageBitmap (browser production mount)");
|
|
@@ -9529,9 +9920,24 @@ async function loadProductionPrimitiveBitmap(url) {
|
|
|
9529
9920
|
const response = await fetch(url);
|
|
9530
9921
|
if (!response.ok)
|
|
9531
9922
|
throw new Error(`texture fetch failed with status ${response.status} for ${url}`);
|
|
9532
|
-
|
|
9923
|
+
const blob = await response.blob();
|
|
9924
|
+
const bitmap = await createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" });
|
|
9925
|
+
try {
|
|
9926
|
+
const { decodePngTexturePixels } = await import("./PngTexturePixels.js");
|
|
9927
|
+
const pixels = await decodePngTexturePixels(new Uint8Array(await blob.arrayBuffer()));
|
|
9928
|
+
if (pixels)
|
|
9929
|
+
productionPrimitiveBitmapPixels.set(bitmap, pixels);
|
|
9930
|
+
return bitmap;
|
|
9931
|
+
}
|
|
9932
|
+
catch (error) {
|
|
9933
|
+
bitmap.close();
|
|
9934
|
+
throw error;
|
|
9935
|
+
}
|
|
9533
9936
|
}
|
|
9534
9937
|
function bitmapRgbaPixels(bitmap) {
|
|
9938
|
+
const decoded = productionPrimitiveBitmapPixels.get(bitmap);
|
|
9939
|
+
if (decoded)
|
|
9940
|
+
return decoded;
|
|
9535
9941
|
if (typeof document === "undefined")
|
|
9536
9942
|
throw new Error("texture compositing requires a DOM canvas");
|
|
9537
9943
|
const canvas = document.createElement("canvas");
|
|
@@ -9550,7 +9956,7 @@ function bitmapRgbaPixels(bitmap) {
|
|
|
9550
9956
|
* textured, fallback, or skipped — is recorded on the resource with warnings.
|
|
9551
9957
|
* Procedural inputs have no rasterizer: recorded + warned, never faked.
|
|
9552
9958
|
*/
|
|
9553
|
-
export async function upgradeProductionPrimitiveTextures(entries, warn) {
|
|
9959
|
+
export async function upgradeProductionPrimitiveTextures(entries, warn, maxTextureSize = 4096) {
|
|
9554
9960
|
for (const entry of entries) {
|
|
9555
9961
|
for (const resource of entry.resources) {
|
|
9556
9962
|
const spec = resource.materialSpec;
|
|
@@ -9560,14 +9966,14 @@ export async function upgradeProductionPrimitiveTextures(entries, warn) {
|
|
|
9560
9966
|
// per-resource without duplicating into runtime warnings.
|
|
9561
9967
|
resource.textureWarnings.push(`procedural texture ${procedural} on "${resource.name}" has no rasterizer; recorded only, scalar material retained`);
|
|
9562
9968
|
}
|
|
9563
|
-
const urls = [intent.baseColorUrl, intent.normalUrl, intent.roughnessUrl, intent.metalnessUrl, intent.occlusionUrl, intent.emissiveUrl].filter((url) => typeof url === "string" && url.length > 0);
|
|
9969
|
+
const urls = [intent.baseColorUrl, intent.normalUrl, intent.roughnessUrl, intent.metalnessUrl, intent.occlusionUrl, intent.emissiveUrl, ...Object.values(intent.extensionMaps ?? {}).map((map) => map.url)].filter((url) => typeof url === "string" && url.length > 0);
|
|
9564
9970
|
if (urls.length === 0)
|
|
9565
9971
|
continue;
|
|
9566
9972
|
if (resource.textureStatus !== "none")
|
|
9567
9973
|
continue;
|
|
9568
9974
|
resource.textureStatus = "pending";
|
|
9569
9975
|
try {
|
|
9570
|
-
await upgradeProductionPrimitiveResource(resource, resource.sourceNode, spec, intent);
|
|
9976
|
+
await upgradeProductionPrimitiveResource(resource, resource.sourceNode, spec, intent, maxTextureSize);
|
|
9571
9977
|
}
|
|
9572
9978
|
catch (error) {
|
|
9573
9979
|
resource.textureStatus = "fallback";
|
|
@@ -9633,140 +10039,208 @@ export async function upgradeProductionEnvironmentHdri(url, intensity, reflectio
|
|
|
9633
10039
|
dualProbe: true
|
|
9634
10040
|
};
|
|
9635
10041
|
}
|
|
9636
|
-
async function upgradeProductionPrimitiveResource(resource, node, spec, intent) {
|
|
10042
|
+
async function upgradeProductionPrimitiveResource(resource, node, spec, intent, maxTextureSize) {
|
|
9637
10043
|
const fail = (message) => new Error(message);
|
|
9638
|
-
if (resource.material instanceof InstancedPBRMaterial) {
|
|
9639
|
-
throw fail(`instanced primitives have no textured material variant for "${resource.name}"`);
|
|
9640
|
-
}
|
|
9641
10044
|
if (!resource.geometry.vertexBuffer.format.hasAttribute("uv")) {
|
|
9642
10045
|
throw fail(`"${resource.name}" geometry carries no uv set; textured upgrade needs generated uvs`);
|
|
9643
10046
|
}
|
|
9644
10047
|
const scalars = resolveProductionPrimitiveScalars(node);
|
|
9645
|
-
const
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
9652
|
-
]
|
|
9653
|
-
const
|
|
9654
|
-
const
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
const
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
10048
|
+
const extensionEntries = Object.entries(intent.extensionMaps ?? {});
|
|
10049
|
+
const needsScalarAtlas = extensionEntries.some(([slot]) => slot.startsWith("clearcoat"))
|
|
10050
|
+
&& extensionEntries.some(([slot]) => slot.startsWith("sheen") || slot === "anisotropy")
|
|
10051
|
+
&& extensionEntries.some(([slot]) => slot.startsWith("iridescence"));
|
|
10052
|
+
if (extensionEntries.some(([, map]) => map.texCoord === 1) && !resource.geometry.vertexBuffer.format.hasAttribute("uv1")) {
|
|
10053
|
+
throw fail(`"${resource.name}" geometry carries no uv1 set requested by extension texture`);
|
|
10054
|
+
}
|
|
10055
|
+
const ownedBitmaps = [];
|
|
10056
|
+
const ownedTextures = [];
|
|
10057
|
+
const bitmapLoads = new Map();
|
|
10058
|
+
const load = (url) => {
|
|
10059
|
+
let pending = bitmapLoads.get(url);
|
|
10060
|
+
if (!pending) {
|
|
10061
|
+
pending = loadProductionPrimitiveBitmap(url).then((bitmap) => {
|
|
10062
|
+
ownedBitmaps.push(bitmap);
|
|
10063
|
+
return bitmap;
|
|
10064
|
+
});
|
|
10065
|
+
bitmapLoads.set(url, pending);
|
|
10066
|
+
}
|
|
10067
|
+
return pending;
|
|
10068
|
+
};
|
|
10069
|
+
const own = (texture) => { ownedTextures.push(texture); return texture; };
|
|
10070
|
+
const disposeTextures = () => {
|
|
10071
|
+
for (const texture of ownedTextures)
|
|
10072
|
+
texture.dispose();
|
|
10073
|
+
for (const bitmap of ownedBitmaps)
|
|
10074
|
+
bitmap.close();
|
|
10075
|
+
ownedTextures.length = 0;
|
|
10076
|
+
ownedBitmaps.length = 0;
|
|
10077
|
+
};
|
|
10078
|
+
try {
|
|
10079
|
+
const baseResults = await Promise.allSettled([
|
|
10080
|
+
intent.baseColorUrl ? load(intent.baseColorUrl) : Promise.resolve(undefined),
|
|
10081
|
+
intent.normalUrl ? load(intent.normalUrl) : Promise.resolve(undefined),
|
|
10082
|
+
intent.roughnessUrl ? load(intent.roughnessUrl) : Promise.resolve(undefined),
|
|
10083
|
+
intent.metalnessUrl ? load(intent.metalnessUrl) : Promise.resolve(undefined),
|
|
10084
|
+
intent.occlusionUrl ? load(intent.occlusionUrl) : Promise.resolve(undefined),
|
|
10085
|
+
intent.emissiveUrl ? load(intent.emissiveUrl) : Promise.resolve(undefined)
|
|
10086
|
+
]);
|
|
10087
|
+
const failure = baseResults.find((result) => result.status === "rejected");
|
|
10088
|
+
if (failure?.status === "rejected")
|
|
10089
|
+
throw failure.reason;
|
|
10090
|
+
const [baseColorSource, normalSource, roughnessSource, metalnessSource, occlusionSource, emissiveSource] = baseResults.map((result) => result.status === "fulfilled" ? result.value : undefined);
|
|
10091
|
+
const slots = [];
|
|
10092
|
+
const extensionOptions = {};
|
|
10093
|
+
const extensionSources = [];
|
|
10094
|
+
const atlasPixels = {};
|
|
10095
|
+
for (const [slot, map] of extensionEntries) {
|
|
10096
|
+
const source = await load(map.url);
|
|
10097
|
+
extensionSources.push(source);
|
|
10098
|
+
if (needsScalarAtlas && ["clearcoat", "clearcoatRoughness", "sheenRoughness", "iridescence", "iridescenceThickness"].includes(slot)) {
|
|
10099
|
+
Object.assign(atlasPixels, { [slot]: bitmapRgbaPixels(source) });
|
|
9681
10100
|
}
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
emissiveColor: spec?.emissive
|
|
9721
|
-
? [scalars.emissiveColor[0], scalars.emissiveColor[1], scalars.emissiveColor[2]]
|
|
9722
|
-
: emissiveTexture ? [1, 1, 1] : [scalars.emissiveColor[0], scalars.emissiveColor[1], scalars.emissiveColor[2]],
|
|
9723
|
-
emissiveStrength: Math.max(0, spec?.emissiveIntensity ?? (spec?.emissive || emissiveTexture ? 1.35 : 0)),
|
|
9724
|
-
occlusionStrength: clamp01(spec?.occlusionStrength ?? 1),
|
|
9725
|
-
clearcoatFactor: scalars.clearcoat,
|
|
9726
|
-
clearcoatRoughnessFactor: clamp01(spec?.clearcoatRoughness ?? 0.34),
|
|
9727
|
-
sheenColorFactor: [scalars.sheenColor[0], scalars.sheenColor[1], scalars.sheenColor[2]],
|
|
9728
|
-
sheenRoughnessFactor: clamp01(spec?.sheenRoughness ?? 0.3),
|
|
9729
|
-
anisotropyStrength: scalars.anisotropy,
|
|
9730
|
-
anisotropyRotation: spec?.anisotropyRotation ?? 0,
|
|
9731
|
-
iridescenceFactor: scalars.iridescence,
|
|
9732
|
-
iridescenceIor: Math.max(1, spec?.iridescenceIOR ?? 1.3),
|
|
9733
|
-
...(scalars.thicknessRange ? { iridescenceThicknessMinimum: scalars.thicknessRange[0], iridescenceThicknessMaximum: scalars.thicknessRange[1] } : {}),
|
|
9734
|
-
transmissionFactor: scalars.transmission,
|
|
9735
|
-
...(spec?.thickness === undefined ? {} : { volumeThicknessFactor: Math.max(0, spec.thickness) }),
|
|
9736
|
-
...(spec?.ior === undefined ? {} : { ior: Math.max(1, spec.ior) }),
|
|
9737
|
-
...(spec?.attenuationColor ? { volumeAttenuationColor: colorToLinearRgb(spec.attenuationColor) } : {}),
|
|
9738
|
-
...(spec?.attenuationDistance === undefined ? {} : { volumeAttenuationDistance: Math.max(0, spec.attenuationDistance) }),
|
|
9739
|
-
environmentIntensity: scalars.environmentIntensity,
|
|
9740
|
-
envMapIntensity: scalars.envMapIntensity,
|
|
9741
|
-
renderState: {
|
|
9742
|
-
blend: scalars.opacity < 0.999,
|
|
9743
|
-
depthWrite: scalars.opacity >= 0.999,
|
|
9744
|
-
cullMode: node.primitive === "plane" || scalars.opacity < 0.999 ? "none" : "back"
|
|
9745
|
-
},
|
|
9746
|
-
...(baseColorTexture ? { baseColorTexture, baseColorSampler: textureSampler } : {}),
|
|
9747
|
-
...(normalTexture ? { normalTexture, normalSampler: textureSampler, normalScale: spec?.normalScale ?? 1 } : {}),
|
|
9748
|
-
...(metallicRoughnessTexture ? { metallicRoughnessTexture, metallicRoughnessSampler: textureSampler } : {}),
|
|
9749
|
-
...(occlusionTexture ? { occlusionTexture, occlusionSampler: textureSampler } : {}),
|
|
9750
|
-
...(emissiveTexture ? { emissiveTexture, emissiveSampler: textureSampler } : {}),
|
|
9751
|
-
...(spec?.texTransforms?.baseColor ? { baseColorTextureTransform: { ...spec.texTransforms.baseColor } } : {}),
|
|
9752
|
-
...(spec?.texTransforms?.normal ? { normalTextureTransform: { ...spec.texTransforms.normal } } : {}),
|
|
9753
|
-
...(spec?.texTransforms?.metallicRoughness ? { metallicRoughnessTextureTransform: { ...spec.texTransforms.metallicRoughness } } : {}),
|
|
9754
|
-
...(spec?.texTransforms?.occlusion ? { occlusionTextureTransform: { ...spec.texTransforms.occlusion } } : {}),
|
|
9755
|
-
...(spec?.texTransforms?.emissive ? { emissiveTextureTransform: { ...spec.texTransforms.emissive } } : {}),
|
|
9756
|
-
...((texCoords?.baseColor ?? 0) > 0 || (texCoords?.normal ?? 0) > 0 || (texCoords?.metallicRoughness ?? 0) > 0 || (texCoords?.occlusion ?? 0) > 0 || (texCoords?.emissive ?? 0) > 0
|
|
9757
|
-
? {
|
|
9758
|
-
textureTexCoords: {
|
|
9759
|
-
...(baseColorTexture && (texCoords?.baseColor ?? 0) > 0 ? { baseColor: 1 } : {}),
|
|
9760
|
-
...(normalTexture && (texCoords?.normal ?? 0) > 0 ? { normal: 1 } : {}),
|
|
9761
|
-
...(metallicRoughnessTexture && (texCoords?.metallicRoughness ?? 0) > 0 ? { metallicRoughness: 1 } : {}),
|
|
9762
|
-
...(occlusionTexture && (texCoords?.occlusion ?? 0) > 0 ? { occlusion: 1 } : {}),
|
|
9763
|
-
...(emissiveTexture && (texCoords?.emissive ?? 0) > 0 ? { emissive: 1 } : {})
|
|
10101
|
+
extensionOptions[`${slot}Texture`] = own(new Texture({ width: source.width, height: source.height, source, colorSpace: map.colorSpace, label: `${resource.name}-${slot}` }));
|
|
10102
|
+
if (map.transform)
|
|
10103
|
+
extensionOptions[`${slot}TextureTransform`] = { ...map.transform };
|
|
10104
|
+
slots.push(slot);
|
|
10105
|
+
}
|
|
10106
|
+
if (needsScalarAtlas) {
|
|
10107
|
+
const { createExtensionScalarAtlas } = await import("../../rendering/extension-scalar-atlas.js");
|
|
10108
|
+
const atlas = createExtensionScalarAtlas(atlasPixels, maxTextureSize);
|
|
10109
|
+
own(atlas.texture);
|
|
10110
|
+
extensionOptions.extensionScalarAtlas = atlas;
|
|
10111
|
+
}
|
|
10112
|
+
const baseColorTexture = baseColorSource
|
|
10113
|
+
? own(new Texture({ width: baseColorSource.width, height: baseColorSource.height, source: baseColorSource, colorSpace: "srgb", label: `${resource.name}-basecolor` }))
|
|
10114
|
+
: undefined;
|
|
10115
|
+
if (baseColorTexture)
|
|
10116
|
+
slots.push("baseColor");
|
|
10117
|
+
const normalTexture = normalSource
|
|
10118
|
+
? own(new Texture({ width: normalSource.width, height: normalSource.height, source: normalSource, colorSpace: "linear", label: `${resource.name}-normal` }))
|
|
10119
|
+
: undefined;
|
|
10120
|
+
if (normalTexture)
|
|
10121
|
+
slots.push("normal");
|
|
10122
|
+
let metallicRoughnessTexture;
|
|
10123
|
+
if (roughnessSource ?? metalnessSource) {
|
|
10124
|
+
const base = roughnessSource ?? metalnessSource;
|
|
10125
|
+
const size = { width: base.width, height: base.height };
|
|
10126
|
+
const readPixels = (bitmap) => {
|
|
10127
|
+
if (!bitmap)
|
|
10128
|
+
return undefined;
|
|
10129
|
+
const decoded = bitmapRgbaPixels(bitmap);
|
|
10130
|
+
if (decoded.width !== size.width || decoded.height !== size.height) {
|
|
10131
|
+
const canvas = document.createElement("canvas");
|
|
10132
|
+
canvas.width = size.width;
|
|
10133
|
+
canvas.height = size.height;
|
|
10134
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
10135
|
+
if (!context)
|
|
10136
|
+
throw fail("2d canvas unavailable for texture compositing");
|
|
10137
|
+
context.drawImage(bitmap, 0, 0, size.width, size.height);
|
|
10138
|
+
return new Uint8Array(context.getImageData(0, 0, size.width, size.height).data);
|
|
9764
10139
|
}
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
10140
|
+
return decoded.data;
|
|
10141
|
+
};
|
|
10142
|
+
const composited = compositeMetallicRoughnessPixels(readPixels(roughnessSource), readPixels(metalnessSource), size.width * size.height, spec?.roughness ?? 0.58, spec?.metallic ?? spec?.metalness ?? 0);
|
|
10143
|
+
metallicRoughnessTexture = own(new Texture({ width: size.width, height: size.height, data: composited, colorSpace: "linear", label: `${resource.name}-metallicroughness` }));
|
|
10144
|
+
slots.push("metallicRoughness");
|
|
10145
|
+
}
|
|
10146
|
+
const occlusionTexture = occlusionSource
|
|
10147
|
+
? own(new Texture({ width: occlusionSource.width, height: occlusionSource.height, source: occlusionSource, colorSpace: "linear", label: `${resource.name}-occlusion` }))
|
|
10148
|
+
: undefined;
|
|
10149
|
+
if (occlusionTexture)
|
|
10150
|
+
slots.push("occlusion");
|
|
10151
|
+
const emissiveTexture = emissiveSource
|
|
10152
|
+
? own(new Texture({ width: emissiveSource.width, height: emissiveSource.height, source: emissiveSource, colorSpace: "srgb", label: `${resource.name}-emissive` }))
|
|
10153
|
+
: undefined;
|
|
10154
|
+
if (emissiveTexture)
|
|
10155
|
+
slots.push("emissive");
|
|
10156
|
+
if (slots.length === 0)
|
|
10157
|
+
throw fail(`no texture resolved for "${resource.name}"`);
|
|
10158
|
+
// M2 streaming table: resident bytes from the decoded sources (base +
|
|
10159
|
+
// full mip-chain estimate) with the chain keyed off the largest source.
|
|
10160
|
+
const loadedDims = [baseColorSource, normalSource, roughnessSource, metalnessSource, occlusionSource, emissiveSource, ...extensionSources]
|
|
10161
|
+
.filter((source) => source !== undefined)
|
|
10162
|
+
.map((source) => ({ width: source.width, height: source.height }));
|
|
10163
|
+
const loadedBaseBytes = loadedDims.reduce((total, dims) => total + dims.width * dims.height * 4, 0);
|
|
10164
|
+
const largestDims = loadedDims.reduce((largest, dims) => (dims.width * dims.height > largest.width * largest.height ? dims : largest), { width: 1, height: 1 });
|
|
10165
|
+
resource.textureBytes = Math.round(loadedBaseBytes * (4 / 3));
|
|
10166
|
+
resource.textureMipBytes = mipChainBytesCoarseToFine(largestDims.width, largestDims.height);
|
|
10167
|
+
const texCoords = spec?.texCoords;
|
|
10168
|
+
// C3: every root textured slot shares one capability-gated sampler request
|
|
10169
|
+
// (default 8x where supported; the renderer clamps to the device maximum).
|
|
10170
|
+
const textureSampler = new Sampler({
|
|
10171
|
+
maxAnisotropy: resolveSamplerAnisotropy({ desired: spec?.textureAnisotropy }).applied
|
|
10172
|
+
});
|
|
10173
|
+
for (const [slot] of extensionEntries)
|
|
10174
|
+
extensionOptions[`${slot}Sampler`] = textureSampler;
|
|
10175
|
+
resource.texturedMaterial = new TexturedPBRMaterial({
|
|
10176
|
+
name: `a3d-production-textured-primitive-${resource.name}`,
|
|
10177
|
+
baseColor: [scalars.baseColor[0], scalars.baseColor[1], scalars.baseColor[2], scalars.opacity],
|
|
10178
|
+
metallic: clamp01(spec?.metallic ?? spec?.metalness ?? 0),
|
|
10179
|
+
roughness: clamp01(spec?.roughness ?? 0.58),
|
|
10180
|
+
emissiveColor: spec?.emissive
|
|
10181
|
+
? [scalars.emissiveColor[0], scalars.emissiveColor[1], scalars.emissiveColor[2]]
|
|
10182
|
+
: emissiveTexture ? [1, 1, 1] : [scalars.emissiveColor[0], scalars.emissiveColor[1], scalars.emissiveColor[2]],
|
|
10183
|
+
emissiveStrength: Math.max(0, spec?.emissiveIntensity ?? (spec?.emissive || emissiveTexture ? 1.35 : 0)),
|
|
10184
|
+
occlusionStrength: clamp01(spec?.occlusionStrength ?? 1),
|
|
10185
|
+
clearcoatFactor: scalars.clearcoat,
|
|
10186
|
+
clearcoatRoughnessFactor: clamp01(spec?.clearcoatRoughness ?? 0.34),
|
|
10187
|
+
clearcoatNormalScale: Math.max(0, spec?.clearcoatNormalScale ?? 1),
|
|
10188
|
+
...extensionOptions,
|
|
10189
|
+
sheenColorFactor: [scalars.sheenColor[0], scalars.sheenColor[1], scalars.sheenColor[2]],
|
|
10190
|
+
sheenRoughnessFactor: clamp01(spec?.sheenRoughness ?? 0.3),
|
|
10191
|
+
anisotropyStrength: scalars.anisotropy,
|
|
10192
|
+
anisotropyRotation: spec?.anisotropyRotation ?? 0,
|
|
10193
|
+
iridescenceFactor: scalars.iridescence,
|
|
10194
|
+
iridescenceIor: Math.max(1, spec?.iridescenceIOR ?? 1.3),
|
|
10195
|
+
...(scalars.thicknessRange ? { iridescenceThicknessMinimum: scalars.thicknessRange[0], iridescenceThicknessMaximum: scalars.thicknessRange[1] } : {}),
|
|
10196
|
+
transmissionFactor: scalars.transmission,
|
|
10197
|
+
...(spec?.thickness === undefined ? {} : { volumeThicknessFactor: Math.max(0, spec.thickness) }),
|
|
10198
|
+
...(spec?.ior === undefined ? {} : { ior: Math.max(1, spec.ior) }),
|
|
10199
|
+
...(spec?.attenuationColor ? { volumeAttenuationColor: colorToLinearRgb(spec.attenuationColor) } : {}),
|
|
10200
|
+
...(spec?.attenuationDistance === undefined ? {} : { volumeAttenuationDistance: Math.max(0, spec.attenuationDistance) }),
|
|
10201
|
+
environmentIntensity: scalars.environmentIntensity,
|
|
10202
|
+
envMapIntensity: scalars.envMapIntensity,
|
|
10203
|
+
renderState: {
|
|
10204
|
+
blend: scalars.opacity < 0.999,
|
|
10205
|
+
depthWrite: scalars.opacity >= 0.999,
|
|
10206
|
+
cullMode: node.primitive === "plane" || scalars.opacity < 0.999 ? "none" : "back"
|
|
10207
|
+
},
|
|
10208
|
+
...(baseColorTexture ? { baseColorTexture, baseColorSampler: textureSampler } : {}),
|
|
10209
|
+
...(normalTexture ? { normalTexture, normalSampler: textureSampler, normalScale: spec?.normalScale ?? 1 } : {}),
|
|
10210
|
+
...(metallicRoughnessTexture ? { metallicRoughnessTexture, metallicRoughnessSampler: textureSampler } : {}),
|
|
10211
|
+
...(occlusionTexture ? { occlusionTexture, occlusionSampler: textureSampler } : {}),
|
|
10212
|
+
...(emissiveTexture ? { emissiveTexture, emissiveSampler: textureSampler } : {}),
|
|
10213
|
+
...(spec?.texTransforms?.baseColor ? { baseColorTextureTransform: { ...spec.texTransforms.baseColor } } : {}),
|
|
10214
|
+
...(spec?.texTransforms?.normal ? { normalTextureTransform: { ...spec.texTransforms.normal } } : {}),
|
|
10215
|
+
...(spec?.texTransforms?.metallicRoughness ? { metallicRoughnessTextureTransform: { ...spec.texTransforms.metallicRoughness } } : {}),
|
|
10216
|
+
...(spec?.texTransforms?.occlusion ? { occlusionTextureTransform: { ...spec.texTransforms.occlusion } } : {}),
|
|
10217
|
+
...(spec?.texTransforms?.emissive ? { emissiveTextureTransform: { ...spec.texTransforms.emissive } } : {}),
|
|
10218
|
+
...((texCoords?.baseColor ?? 0) > 0 || (texCoords?.normal ?? 0) > 0 || (texCoords?.metallicRoughness ?? 0) > 0 || (texCoords?.occlusion ?? 0) > 0 || (texCoords?.emissive ?? 0) > 0 || extensionEntries.length > 0
|
|
10219
|
+
? {
|
|
10220
|
+
textureTexCoords: {
|
|
10221
|
+
...Object.fromEntries(extensionEntries.map(([slot, map]) => [slot, map.texCoord])),
|
|
10222
|
+
...(baseColorTexture && (texCoords?.baseColor ?? 0) > 0 ? { baseColor: 1 } : {}),
|
|
10223
|
+
...(normalTexture && (texCoords?.normal ?? 0) > 0 ? { normal: 1 } : {}),
|
|
10224
|
+
...(metallicRoughnessTexture && (texCoords?.metallicRoughness ?? 0) > 0 ? { metallicRoughness: 1 } : {}),
|
|
10225
|
+
...(occlusionTexture && (texCoords?.occlusion ?? 0) > 0 ? { occlusion: 1 } : {}),
|
|
10226
|
+
...(emissiveTexture && (texCoords?.emissive ?? 0) > 0 ? { emissive: 1 } : {})
|
|
10227
|
+
}
|
|
10228
|
+
}
|
|
10229
|
+
: {})
|
|
10230
|
+
});
|
|
10231
|
+
if (resource.material.disposed) {
|
|
10232
|
+
resource.texturedMaterial.dispose();
|
|
10233
|
+
resource.texturedMaterial = null;
|
|
10234
|
+
throw fail("primitive disposed while texture upgrade was pending");
|
|
10235
|
+
}
|
|
10236
|
+
resource.textureDisposer = disposeTextures;
|
|
10237
|
+
resource.textureStatus = "textured";
|
|
10238
|
+
resource.textureSlots = slots;
|
|
10239
|
+
}
|
|
10240
|
+
catch (error) {
|
|
10241
|
+
disposeTextures();
|
|
10242
|
+
throw error;
|
|
10243
|
+
}
|
|
9770
10244
|
}
|
|
9771
10245
|
export function compositeMetallicRoughnessPixels(rough, metal, pixelCount, roughnessScalar, metalnessScalar) {
|
|
9772
10246
|
const out = new Uint8Array(pixelCount * 4);
|
|
@@ -10005,12 +10479,12 @@ function createProductionPrimitiveResources(node) {
|
|
|
10005
10479
|
return levelFallback;
|
|
10006
10480
|
});
|
|
10007
10481
|
}
|
|
10008
|
-
function selectProductionPrimitiveResource(entry, node, cameraPosition) {
|
|
10482
|
+
function selectProductionPrimitiveResource(entry, node, cameraPosition, lodBias = 1) {
|
|
10009
10483
|
if (!node.lod?.levels.length)
|
|
10010
10484
|
return entry.resources[0];
|
|
10011
10485
|
const position = node.position ?? [0, 0, 0];
|
|
10012
10486
|
const distance = Math.hypot(cameraPosition[0] - position[0], cameraPosition[1] - position[1], cameraPosition[2] - position[2]);
|
|
10013
|
-
const selection = selectAuraRootLodLevel(distance, node.lod.levels, entry.currentLodIndex, node.lod.hysteresis ?? 0);
|
|
10487
|
+
const selection = selectAuraRootLodLevel(distance * lodBias, node.lod.levels, entry.currentLodIndex, node.lod.hysteresis ?? 0);
|
|
10014
10488
|
entry.currentLodIndex = selection.levelIndex;
|
|
10015
10489
|
return entry.resources[selection.levelIndex] ?? entry.resources[entry.resources.length - 1];
|
|
10016
10490
|
}
|
|
@@ -10266,10 +10740,12 @@ function resolveProductionActorRuntimeState(entry, runtimeNodes) {
|
|
|
10266
10740
|
animation: runtimeSnapshot.animation ?? entry.node.animation
|
|
10267
10741
|
},
|
|
10268
10742
|
animationBinding: runtimeSnapshot.animationBinding,
|
|
10743
|
+
animationPose: runtimeSnapshot.animationPose,
|
|
10744
|
+
animationPoseTime: runtimeSnapshot.animationPoseBinding?.localTime ?? runtimeSnapshot.animationPoseBinding?.captureTime,
|
|
10269
10745
|
...(Object.keys(morphTargets).length > 0 ? { morphTargets } : {})
|
|
10270
10746
|
};
|
|
10271
10747
|
}
|
|
10272
|
-
function applyProductionActorAnimation(entry, node, animationBinding, time, runtimeWarnings) {
|
|
10748
|
+
function applyProductionActorAnimation(entry, node, animationBinding, time, runtimeWarnings, modelMatrix, refreshAfterMovement) {
|
|
10273
10749
|
const animation = node.animation;
|
|
10274
10750
|
if (!animation?.clip || isModelTransformAnimationClip(animation.clip))
|
|
10275
10751
|
return;
|
|
@@ -10279,7 +10755,46 @@ function applyProductionActorAnimation(entry, node, animationBinding, time, runt
|
|
|
10279
10755
|
return;
|
|
10280
10756
|
}
|
|
10281
10757
|
try {
|
|
10282
|
-
|
|
10758
|
+
const binding = animationBinding?.rootMotion;
|
|
10759
|
+
if (binding && animationBinding) {
|
|
10760
|
+
const observed = animationBinding.rootMotionSamples ?? [{
|
|
10761
|
+
playbackId: animationBinding.playbackId ?? clipName, clipName,
|
|
10762
|
+
time: animationBinding.rootMotionTime ?? 0, weight: 1,
|
|
10763
|
+
loop: animationBinding.loop ?? animation.loop ?? true, additive: false
|
|
10764
|
+
}];
|
|
10765
|
+
if (observed.length === 0) {
|
|
10766
|
+
entry.rootMotionCursors = undefined;
|
|
10767
|
+
return;
|
|
10768
|
+
}
|
|
10769
|
+
const cursors = entry.rootMotionCursors ?? new Map();
|
|
10770
|
+
const samples = observed.map(sample => {
|
|
10771
|
+
if (!Number.isFinite(sample.time) || sample.time < 0)
|
|
10772
|
+
throw new Error("Root motion requires a finite nonnegative unwrapped controller time.");
|
|
10773
|
+
const previous = cursors.get(sample.playbackId);
|
|
10774
|
+
return { clipName: sample.clipName, target: binding.target,
|
|
10775
|
+
fromTime: previous === undefined ? 0 : Math.min(previous, sample.time),
|
|
10776
|
+
toTime: sample.time, weight: sample.weight, loop: sample.loop, additive: sample.additive };
|
|
10777
|
+
});
|
|
10778
|
+
const commitCursors = () => { entry.rootMotionCursors = new Map(observed.map(sample => [sample.playbackId, sample.time])); };
|
|
10779
|
+
const result = entry.actor.playRootMotionClips(samples, {
|
|
10780
|
+
worldFromLocal: [...modelMatrix],
|
|
10781
|
+
move: requested => {
|
|
10782
|
+
if (samples.every(sample => sample.fromTime === sample.toTime))
|
|
10783
|
+
return [0, 0, 0];
|
|
10784
|
+
const accepted = binding.move(requested);
|
|
10785
|
+
// Advance cursors before pose/IK work: retrying a failed pose cannot move twice.
|
|
10786
|
+
commitCursors();
|
|
10787
|
+
refreshAfterMovement();
|
|
10788
|
+
return accepted;
|
|
10789
|
+
}
|
|
10790
|
+
});
|
|
10791
|
+
commitCursors();
|
|
10792
|
+
binding.onSample?.(result.motion);
|
|
10793
|
+
}
|
|
10794
|
+
else {
|
|
10795
|
+
entry.rootMotionCursors = undefined;
|
|
10796
|
+
entry.actor.playClip(clipName, resolveProductionActorAnimationSeconds(animation, animationBinding, time));
|
|
10797
|
+
}
|
|
10283
10798
|
}
|
|
10284
10799
|
catch (error) {
|
|
10285
10800
|
runtimeWarnings.add(`Typed GLB actor "${entry.actor.id}" failed to apply clip "${clipName}": ${productionRenderErrorMessage(error)}`);
|
|
@@ -10298,7 +10813,7 @@ function applyProductionActorFootPlanting(entry, animationBinding, modelMatrix,
|
|
|
10298
10813
|
try {
|
|
10299
10814
|
entry.actor.animation.setFootPlanting((footPlanting === undefined
|
|
10300
10815
|
? undefined
|
|
10301
|
-
: { ...footPlanting, ...spreadFootPlantingWorldMatrix(modelMatrix) }));
|
|
10816
|
+
: { ...footPlanting, legs: footPlanting.legs.map(leg => ({ ...leg, ...(leg.contactPhase ? { contact: leg.contactPhase(animationBinding?.localTime ?? 0) } : {}) })), ...spreadFootPlantingWorldMatrix(modelMatrix) }));
|
|
10302
10817
|
}
|
|
10303
10818
|
catch (error) {
|
|
10304
10819
|
runtimeWarnings.add(`Typed GLB actor "${entry.actor.id}" failed to apply foot planting: ${productionRenderErrorMessage(error)}`);
|
|
@@ -10372,6 +10887,9 @@ function createRuntimeEvidenceFromTypedGLBActor(node, actor, renderItems) {
|
|
|
10372
10887
|
lastFootPlantingTargetError: actorEvidence.lastFootPlantingTargetError,
|
|
10373
10888
|
lastFootPlantingHipOffset: actorEvidence.lastFootPlantingHipOffset,
|
|
10374
10889
|
lastFootPlantingMissingLegs: [...actorEvidence.lastFootPlantingMissingLegs],
|
|
10890
|
+
lastFootPlantingFeet: actorEvidence.lastFootPlantingFeet,
|
|
10891
|
+
lastFootPlantingSurfaces: actorEvidence.lastFootPlantingSurfaces,
|
|
10892
|
+
lastFootPlantingDeformation: actorEvidence.lastFootPlantingDeformation,
|
|
10375
10893
|
footPlantingConfigured: actorEvidence.footPlantingConfigured
|
|
10376
10894
|
});
|
|
10377
10895
|
}
|
|
@@ -12540,11 +13058,7 @@ function createViewProjection(snapshot, aspect, time, runtimeNodes) {
|
|
|
12540
13058
|
const cameraSpec = snapshot.camera;
|
|
12541
13059
|
const { target, eye } = resolveCameraFrame(snapshot, cameraSpec, time, runtimeNodes);
|
|
12542
13060
|
const view = lookAtMat4([...eye], [...target], [0, 1, 0]);
|
|
12543
|
-
const
|
|
12544
|
-
const halfWidth = Math.max(1e-4, halfHeight * aspect);
|
|
12545
|
-
const projection = isOrthographicCameraMode(cameraSpec.mode)
|
|
12546
|
-
? orthographicMat4(-halfWidth, halfWidth, -halfHeight, halfHeight, 0.05, 100)
|
|
12547
|
-
: perspectiveMat4(((cameraSpec.fov ?? 45) * Math.PI) / 180, aspect, 0.05, 100);
|
|
13061
|
+
const projection = createCameraProjection(cameraSpec, aspect);
|
|
12548
13062
|
return new Float32Array(multiplyMat4(projection, view));
|
|
12549
13063
|
}
|
|
12550
13064
|
function createModelMatrix(node, bounds, normalizeToUnit, time = 0) {
|
|
@@ -12617,6 +13131,10 @@ function isModelTransformAnimationClip(clip) {
|
|
|
12617
13131
|
return clip === "turntable" || clip === "float" || clip === "orbit";
|
|
12618
13132
|
}
|
|
12619
13133
|
function primitiveSize(node) {
|
|
13134
|
+
// SDF quad positions are already authored in world units by layoutSdfText.
|
|
13135
|
+
// Applying the public text size again here shrinks/grows the GPU quad twice.
|
|
13136
|
+
if (node.text3D?.backend === "sdf")
|
|
13137
|
+
return [1, 1, 1];
|
|
12620
13138
|
if (typeof node.size === "number")
|
|
12621
13139
|
return [node.size, node.size, node.size];
|
|
12622
13140
|
return node.size ?? [1, 1, 1];
|
|
@@ -12950,17 +13468,7 @@ function collectGeneratedCodeWarnings(snapshot) {
|
|
|
12950
13468
|
if (!snapshot.nodes.some((node) => node.kind === "interaction")) {
|
|
12951
13469
|
warnings.push("Scene has no interactions. Suggested fix: add interactions.orbit() for product/viewer scenes.");
|
|
12952
13470
|
}
|
|
12953
|
-
// muse3jsparity-PRD A3: withheld postprocess intents must warn on the
|
|
12954
|
-
// mounted channel too (plan-level warnings never reach app.diagnostics()).
|
|
12955
13471
|
const flatNodes = groups.flatten(snapshot.nodes);
|
|
12956
|
-
const hasEffect = (effect) => flatNodes.some((node) => node.kind === "effect" && node.effect === effect);
|
|
12957
|
-
if (hasEffect("motion-blur")) {
|
|
12958
|
-
warnings.push("motion-blur is recorded but withheld: root has no velocity binding, so no motion-blur pass is submitted");
|
|
12959
|
-
}
|
|
12960
|
-
const antiAlias = flatNodes.find((node) => node.kind === "effect" && node.effect === "anti-alias");
|
|
12961
|
-
if (antiAlias && (antiAlias.mode ?? "fxaa") === "taa") {
|
|
12962
|
-
warnings.push("anti-alias mode \"taa\" is recorded but withheld: root has no history binding, so no taa pass is submitted");
|
|
12963
|
-
}
|
|
12964
13472
|
// muse3jsparity-PRD C1: procedural texture inputs have no rasterizer. The
|
|
12965
13473
|
// intent is compile-time known, so warn here; fetch outcomes stay dynamic.
|
|
12966
13474
|
for (const node of flatNodes) {
|