@aura3d/engine 2.0.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -10
- package/dist/animation/AnimationAction.d.ts.map +1 -1
- package/dist/animation/AnimationAction.js +20 -2
- package/dist/animation/AnimationAction.js.map +1 -1
- package/dist/animation/AnimationLayer.d.ts +1 -0
- package/dist/animation/AnimationLayer.d.ts.map +1 -1
- package/dist/animation/AnimationLayer.js +24 -3
- package/dist/animation/AnimationLayer.js.map +1 -1
- package/dist/animation/AnimationMixer.d.ts +3 -0
- package/dist/animation/AnimationMixer.d.ts.map +1 -1
- package/dist/animation/AnimationMixer.js +69 -5
- package/dist/animation/AnimationMixer.js.map +1 -1
- package/dist/animation/AnimationTrack.d.ts.map +1 -1
- package/dist/animation/AnimationTrack.js +13 -0
- package/dist/animation/AnimationTrack.js.map +1 -1
- package/dist/animation/FootIk.d.ts +10 -0
- package/dist/animation/FootIk.d.ts.map +1 -1
- package/dist/animation/FootIk.js +95 -3
- package/dist/animation/FootIk.js.map +1 -1
- package/dist/animation/HumanoidRetargeting.d.ts +29 -0
- package/dist/animation/HumanoidRetargeting.d.ts.map +1 -1
- package/dist/animation/HumanoidRetargeting.js +41 -1
- package/dist/animation/HumanoidRetargeting.js.map +1 -1
- package/dist/animation/LocomotionKit.d.ts +6 -0
- package/dist/animation/LocomotionKit.d.ts.map +1 -1
- package/dist/animation/LocomotionKit.js +6 -0
- package/dist/animation/LocomotionKit.js.map +1 -1
- package/dist/animation/RootMotion.d.ts +27 -0
- package/dist/animation/RootMotion.d.ts.map +1 -1
- package/dist/animation/RootMotion.js +38 -0
- package/dist/animation/RootMotion.js.map +1 -1
- package/dist/animation/SpringBones.d.ts +28 -0
- package/dist/animation/SpringBones.d.ts.map +1 -1
- package/dist/animation/SpringBones.js +24 -0
- package/dist/animation/SpringBones.js.map +1 -1
- package/dist/assets/GLTFAnimationRuntime.d.ts +95 -2
- package/dist/assets/GLTFAnimationRuntime.d.ts.map +1 -1
- package/dist/assets/GLTFAnimationRuntime.js +328 -3
- package/dist/assets/GLTFAnimationRuntime.js.map +1 -1
- package/dist/assets/GLTFExtensionSupport.js +2 -2
- package/dist/assets/GLTFExtensionSupport.js.map +1 -1
- package/dist/assets/GLTFLoader.d.ts +360 -1
- package/dist/assets/GLTFLoader.d.ts.map +1 -1
- package/dist/assets/GLTFLoader.js +197 -1
- package/dist/assets/GLTFLoader.js.map +1 -1
- package/dist/assets/KTX2BasisTextureTranscoder.d.ts +39 -0
- package/dist/assets/KTX2BasisTextureTranscoder.d.ts.map +1 -1
- package/dist/assets/KTX2BasisTextureTranscoder.js +70 -0
- package/dist/assets/KTX2BasisTextureTranscoder.js.map +1 -1
- package/dist/assets/TexturePipeline.d.ts +20 -0
- package/dist/assets/TexturePipeline.d.ts.map +1 -1
- package/dist/assets/TexturePipeline.js +20 -0
- package/dist/assets/TexturePipeline.js.map +1 -1
- package/dist/assets/TextureStreaming.d.ts +30 -0
- package/dist/assets/TextureStreaming.d.ts.map +1 -1
- package/dist/assets/TextureStreaming.js +53 -0
- package/dist/assets/TextureStreaming.js.map +1 -1
- package/dist/assets/browser-index.d.ts +7 -3
- package/dist/assets/browser-index.d.ts.map +1 -1
- package/dist/assets/browser-index.js +3 -1
- package/dist/assets/browser-index.js.map +1 -1
- package/dist/assets/gltf-runtime.d.ts +1 -1
- package/dist/assets/gltf-runtime.d.ts.map +1 -1
- package/dist/assets/gltf-runtime.js.map +1 -1
- package/dist/assets/index.d.ts +7 -7
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/assets/index.js +3 -3
- package/dist/assets/index.js.map +1 -1
- package/dist/assets/loaders/HDRLoader.d.ts +40 -0
- package/dist/assets/loaders/HDRLoader.d.ts.map +1 -1
- package/dist/assets/loaders/HDRLoader.js +158 -0
- package/dist/assets/loaders/HDRLoader.js.map +1 -1
- package/dist/audio/AudioSource.d.ts +2 -0
- package/dist/audio/AudioSource.d.ts.map +1 -1
- package/dist/audio/AudioSource.js +7 -0
- package/dist/audio/AudioSource.js.map +1 -1
- package/dist/audio/Footsteps.d.ts +47 -0
- package/dist/audio/Footsteps.d.ts.map +1 -0
- package/dist/audio/Footsteps.js +73 -0
- package/dist/audio/Footsteps.js.map +1 -0
- package/dist/audio/GameMixer.d.ts +69 -0
- package/dist/audio/GameMixer.d.ts.map +1 -0
- package/dist/audio/GameMixer.js +141 -0
- package/dist/audio/GameMixer.js.map +1 -0
- package/dist/audio/PositionalEmitter.d.ts +95 -0
- package/dist/audio/PositionalEmitter.d.ts.map +1 -0
- package/dist/audio/PositionalEmitter.js +199 -0
- package/dist/audio/PositionalEmitter.js.map +1 -0
- package/dist/audio/index.d.ts +6 -0
- package/dist/audio/index.d.ts.map +1 -1
- package/dist/audio/index.js +3 -0
- package/dist/audio/index.js.map +1 -1
- package/dist/aura3d-cli/pull-bridge/profiles.d.ts +12 -2
- package/dist/aura3d-cli/pull-bridge/profiles.d.ts.map +1 -1
- package/dist/aura3d-cli/pull-bridge/profiles.js +35 -4
- package/dist/aura3d-cli/pull-bridge/profiles.js.map +1 -1
- package/dist/aura3d-cli/pull-bridge/provenance.d.ts +15 -1
- package/dist/aura3d-cli/pull-bridge/provenance.d.ts.map +1 -1
- package/dist/aura3d-cli/pull-bridge/provenance.js +60 -1
- package/dist/aura3d-cli/pull-bridge/provenance.js.map +1 -1
- package/dist/aura3d-cli/pull-bridge/scoring.d.ts +10 -1
- package/dist/aura3d-cli/pull-bridge/scoring.d.ts.map +1 -1
- package/dist/aura3d-cli/pull-bridge/scoring.js +9 -1
- package/dist/aura3d-cli/pull-bridge/scoring.js.map +1 -1
- package/dist/aura3d-cli/pull-bridge/search.js +2 -2
- package/dist/aura3d-cli/pull-bridge/search.js.map +1 -1
- package/dist/aura3d-cli/pull-bridge.d.ts +15 -3
- package/dist/aura3d-cli/pull-bridge.d.ts.map +1 -1
- package/dist/aura3d-cli/pull-bridge.js +68 -5
- package/dist/aura3d-cli/pull-bridge.js.map +1 -1
- package/dist/controls/ArcballControls.d.ts +92 -0
- package/dist/controls/ArcballControls.d.ts.map +1 -0
- package/dist/controls/ArcballControls.js +286 -0
- package/dist/controls/ArcballControls.js.map +1 -0
- package/dist/controls/DragControls.d.ts +13 -0
- package/dist/controls/DragControls.d.ts.map +1 -1
- package/dist/controls/DragControls.js +24 -1
- package/dist/controls/DragControls.js.map +1 -1
- package/dist/controls/FlyControls.d.ts +9 -0
- package/dist/controls/FlyControls.d.ts.map +1 -1
- package/dist/controls/FlyControls.js +20 -2
- package/dist/controls/FlyControls.js.map +1 -1
- package/dist/controls/FocusFrame.d.ts +34 -0
- package/dist/controls/FocusFrame.d.ts.map +1 -0
- package/dist/controls/FocusFrame.js +82 -0
- package/dist/controls/FocusFrame.js.map +1 -0
- package/dist/controls/HoverOutline.d.ts +46 -0
- package/dist/controls/HoverOutline.d.ts.map +1 -0
- package/dist/controls/HoverOutline.js +87 -0
- package/dist/controls/HoverOutline.js.map +1 -0
- package/dist/controls/InteractionControls.d.ts +7 -0
- package/dist/controls/InteractionControls.d.ts.map +1 -1
- package/dist/controls/InteractionControls.js +12 -1
- package/dist/controls/InteractionControls.js.map +1 -1
- package/dist/controls/NativeControlTypes.d.ts +17 -0
- package/dist/controls/NativeControlTypes.d.ts.map +1 -1
- package/dist/controls/NativeControlTypes.js.map +1 -1
- package/dist/controls/OrbitControls.d.ts +9 -0
- package/dist/controls/OrbitControls.d.ts.map +1 -1
- package/dist/controls/OrbitControls.js +26 -6
- package/dist/controls/OrbitControls.js.map +1 -1
- package/dist/controls/Picking.d.ts +15 -0
- package/dist/controls/Picking.d.ts.map +1 -1
- package/dist/controls/Picking.js +62 -23
- package/dist/controls/Picking.js.map +1 -1
- package/dist/controls/PointerLockControls.d.ts.map +1 -1
- package/dist/controls/PointerLockControls.js +4 -0
- package/dist/controls/PointerLockControls.js.map +1 -1
- package/dist/controls/SelectionManager.d.ts +8 -0
- package/dist/controls/SelectionManager.d.ts.map +1 -1
- package/dist/controls/SelectionManager.js +11 -0
- package/dist/controls/SelectionManager.js.map +1 -1
- package/dist/controls/TrackballControls.d.ts +7 -0
- package/dist/controls/TrackballControls.d.ts.map +1 -1
- package/dist/controls/TrackballControls.js +19 -5
- package/dist/controls/TrackballControls.js.map +1 -1
- package/dist/controls/TransformControls.d.ts +8 -0
- package/dist/controls/TransformControls.d.ts.map +1 -1
- package/dist/controls/TransformControls.js +15 -2
- package/dist/controls/TransformControls.js.map +1 -1
- package/dist/controls/index.d.ts +6 -0
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +3 -0
- package/dist/controls/index.js.map +1 -1
- package/dist/create-aura3d/index.js +1 -1
- package/dist/editor-runtime/RootEditorSurface.d.ts +65 -0
- package/dist/editor-runtime/RootEditorSurface.d.ts.map +1 -0
- package/dist/editor-runtime/RootEditorSurface.js +62 -0
- package/dist/editor-runtime/RootEditorSurface.js.map +1 -0
- package/dist/editor-runtime/index.d.ts +2 -0
- package/dist/editor-runtime/index.d.ts.map +1 -1
- package/dist/editor-runtime/index.js +1 -0
- package/dist/editor-runtime/index.js.map +1 -1
- package/dist/engine/agent-api/AnimationController.d.ts +9 -1
- package/dist/engine/agent-api/AnimationController.d.ts.map +1 -1
- package/dist/engine/agent-api/AnimationController.js +5 -1
- package/dist/engine/agent-api/AnimationController.js.map +1 -1
- package/dist/engine/agent-api/AnimationDebugOverlay.d.ts +56 -0
- package/dist/engine/agent-api/AnimationDebugOverlay.d.ts.map +1 -0
- package/dist/engine/agent-api/AnimationDebugOverlay.js +148 -0
- package/dist/engine/agent-api/AnimationDebugOverlay.js.map +1 -0
- package/dist/engine/agent-api/AnimationMixerBuilders.d.ts +41 -0
- package/dist/engine/agent-api/AnimationMixerBuilders.d.ts.map +1 -0
- package/dist/engine/agent-api/AnimationMixerBuilders.js +146 -0
- package/dist/engine/agent-api/AnimationMixerBuilders.js.map +1 -0
- package/dist/engine/agent-api/AssetDecoders.d.ts +17 -0
- package/dist/engine/agent-api/AssetDecoders.d.ts.map +1 -0
- package/dist/engine/agent-api/AssetDecoders.js +18 -0
- package/dist/engine/agent-api/AssetDecoders.js.map +1 -0
- package/dist/engine/agent-api/Decals.d.ts +174 -0
- package/dist/engine/agent-api/Decals.d.ts.map +1 -0
- package/dist/engine/agent-api/Decals.js +350 -0
- package/dist/engine/agent-api/Decals.js.map +1 -0
- package/dist/engine/agent-api/FootPlanting.d.ts +75 -0
- package/dist/engine/agent-api/FootPlanting.d.ts.map +1 -0
- package/dist/engine/agent-api/FootPlanting.js +60 -0
- package/dist/engine/agent-api/FootPlanting.js.map +1 -0
- package/dist/engine/agent-api/GameAppRuntime.d.ts +19 -0
- package/dist/engine/agent-api/GameAppRuntime.d.ts.map +1 -1
- package/dist/engine/agent-api/GameAppRuntime.js +37 -1
- package/dist/engine/agent-api/GameAppRuntime.js.map +1 -1
- package/dist/engine/agent-api/GameCameraRigs.d.ts +185 -0
- package/dist/engine/agent-api/GameCameraRigs.d.ts.map +1 -0
- package/dist/engine/agent-api/GameCameraRigs.js +414 -0
- package/dist/engine/agent-api/GameCameraRigs.js.map +1 -0
- package/dist/engine/agent-api/GameFeel.d.ts +100 -0
- package/dist/engine/agent-api/GameFeel.d.ts.map +1 -0
- package/dist/engine/agent-api/GameFeel.js +226 -0
- package/dist/engine/agent-api/GameFeel.js.map +1 -0
- package/dist/engine/agent-api/LabelTelemetry.d.ts +108 -0
- package/dist/engine/agent-api/LabelTelemetry.d.ts.map +1 -0
- package/dist/engine/agent-api/LabelTelemetry.js +116 -0
- package/dist/engine/agent-api/LabelTelemetry.js.map +1 -0
- package/dist/engine/agent-api/NavigationCrowds.d.ts +146 -0
- package/dist/engine/agent-api/NavigationCrowds.d.ts.map +1 -0
- package/dist/engine/agent-api/NavigationCrowds.js +104 -0
- package/dist/engine/agent-api/NavigationCrowds.js.map +1 -0
- package/dist/engine/agent-api/PhysicsRuntime.d.ts +61 -5
- package/dist/engine/agent-api/PhysicsRuntime.d.ts.map +1 -1
- package/dist/engine/agent-api/PhysicsRuntime.js +53 -21
- package/dist/engine/agent-api/PhysicsRuntime.js.map +1 -1
- package/dist/engine/agent-api/RootGeometry.d.ts +16 -1
- package/dist/engine/agent-api/RootGeometry.d.ts.map +1 -1
- package/dist/engine/agent-api/RootGeometry.js.map +1 -1
- package/dist/engine/agent-api/RuntimeNodeHandle.d.ts +6 -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/Scatter.d.ts +18 -0
- package/dist/engine/agent-api/Scatter.d.ts.map +1 -0
- package/dist/engine/agent-api/Scatter.js +18 -0
- package/dist/engine/agent-api/Scatter.js.map +1 -0
- package/dist/engine/agent-api/TouchControlBinding.d.ts +32 -0
- package/dist/engine/agent-api/TouchControlBinding.d.ts.map +1 -1
- package/dist/engine/agent-api/TouchControlBinding.js +36 -0
- package/dist/engine/agent-api/TouchControlBinding.js.map +1 -1
- package/dist/engine/agent-api/VehicleChassis.d.ts.map +1 -1
- package/dist/engine/agent-api/VehicleChassis.js +76 -13
- package/dist/engine/agent-api/VehicleChassis.js.map +1 -1
- package/dist/engine/agent-api/index.d.ts +708 -6
- package/dist/engine/agent-api/index.d.ts.map +1 -1
- package/dist/engine/agent-api/index.js +1757 -134
- 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/game/GameAudio.d.ts +66 -0
- package/dist/engine/game/GameAudio.d.ts.map +1 -1
- package/dist/engine/game/GameAudio.js +179 -44
- package/dist/engine/game/GameAudio.js.map +1 -1
- package/dist/engine/instances-model/InstancedModel.d.ts +111 -0
- package/dist/engine/instances-model/InstancedModel.d.ts.map +1 -0
- package/dist/engine/instances-model/InstancedModel.js +108 -0
- package/dist/engine/instances-model/InstancedModel.js.map +1 -0
- package/dist/engine/material-physical/PhysicalMaterialSpec.d.ts +72 -0
- package/dist/engine/material-physical/PhysicalMaterialSpec.d.ts.map +1 -0
- package/dist/engine/material-physical/PhysicalMaterialSpec.js +95 -0
- package/dist/engine/material-physical/PhysicalMaterialSpec.js.map +1 -0
- package/dist/engine/production-runtime/GameAppRuntime.d.ts +1 -1
- package/dist/engine/production-runtime/GameAppRuntime.d.ts.map +1 -1
- package/dist/engine/production-runtime/GameRenderPreset.d.ts +76 -0
- package/dist/engine/production-runtime/GameRenderPreset.d.ts.map +1 -1
- package/dist/engine/production-runtime/GameRenderPreset.js +167 -0
- package/dist/engine/production-runtime/GameRenderPreset.js.map +1 -1
- package/dist/engine/production-runtime/TypedGLBActor.d.ts +22 -2
- package/dist/engine/production-runtime/TypedGLBActor.d.ts.map +1 -1
- package/dist/engine/production-runtime/TypedGLBActor.js +33 -1
- package/dist/engine/production-runtime/TypedGLBActor.js.map +1 -1
- package/dist/engine/production-runtime/index.d.ts +4 -4
- package/dist/engine/production-runtime/index.d.ts.map +1 -1
- package/dist/engine/production-runtime/index.js +5 -3
- package/dist/engine/production-runtime/index.js.map +1 -1
- package/dist/input/ActionMap.d.ts +40 -0
- package/dist/input/ActionMap.d.ts.map +1 -1
- package/dist/input/ActionMap.js +179 -0
- package/dist/input/ActionMap.js.map +1 -1
- package/dist/input/ComboDetector.d.ts +49 -0
- package/dist/input/ComboDetector.d.ts.map +1 -0
- package/dist/input/ComboDetector.js +102 -0
- package/dist/input/ComboDetector.js.map +1 -0
- package/dist/input/Haptics.d.ts +46 -0
- package/dist/input/Haptics.d.ts.map +1 -0
- package/dist/input/Haptics.js +67 -0
- package/dist/input/Haptics.js.map +1 -0
- package/dist/input/TouchLayouts.d.ts +35 -0
- package/dist/input/TouchLayouts.d.ts.map +1 -0
- package/dist/input/TouchLayouts.js +88 -0
- package/dist/input/TouchLayouts.js.map +1 -0
- package/dist/input/WebXRSessionController.d.ts +53 -0
- package/dist/input/WebXRSessionController.d.ts.map +1 -1
- package/dist/input/WebXRSessionController.js +88 -2
- package/dist/input/WebXRSessionController.js.map +1 -1
- package/dist/input/index.d.ts +8 -2
- package/dist/input/index.d.ts.map +1 -1
- package/dist/input/index.js +3 -0
- package/dist/input/index.js.map +1 -1
- package/dist/lean/game.d.ts +98 -0
- package/dist/lean/game.d.ts.map +1 -1
- package/dist/lean/game.js +192 -0
- package/dist/lean/game.js.map +1 -1
- package/dist/materials/GameReadyMaterialLibrary.d.ts +39 -0
- package/dist/materials/GameReadyMaterialLibrary.d.ts.map +1 -0
- package/dist/materials/GameReadyMaterialLibrary.js +253 -0
- package/dist/materials/GameReadyMaterialLibrary.js.map +1 -0
- package/dist/materials/MaterialValidation.d.ts +21 -0
- package/dist/materials/MaterialValidation.d.ts.map +1 -1
- package/dist/materials/MaterialValidation.js +105 -0
- package/dist/materials/MaterialValidation.js.map +1 -1
- package/dist/materials/browser-index.d.ts +2 -0
- package/dist/materials/browser-index.d.ts.map +1 -1
- package/dist/materials/browser-index.js +1 -0
- package/dist/materials/browser-index.js.map +1 -1
- package/dist/materials/node.d.ts +2 -2
- package/dist/materials/node.d.ts.map +1 -1
- package/dist/materials/node.js +1 -1
- package/dist/materials/node.js.map +1 -1
- package/dist/physics/CollisionVolumes.d.ts +59 -0
- package/dist/physics/CollisionVolumes.d.ts.map +1 -1
- package/dist/physics/CollisionVolumes.js +87 -0
- package/dist/physics/CollisionVolumes.js.map +1 -1
- package/dist/physics/PhysicsDebugDraw.d.ts +37 -0
- package/dist/physics/PhysicsDebugDraw.d.ts.map +1 -1
- package/dist/physics/PhysicsDebugDraw.js +38 -1
- package/dist/physics/PhysicsDebugDraw.js.map +1 -1
- package/dist/physics/PhysicsWorld.d.ts +15 -0
- package/dist/physics/PhysicsWorld.d.ts.map +1 -1
- package/dist/physics/PhysicsWorld.js +8 -1
- package/dist/physics/PhysicsWorld.js.map +1 -1
- package/dist/physics/solverless.d.ts +1 -1
- package/dist/physics/solverless.d.ts.map +1 -1
- package/dist/physics/solverless.js.map +1 -1
- package/dist/physics-rapier/index.d.ts +7 -1
- package/dist/physics-rapier/index.d.ts.map +1 -1
- package/dist/physics-rapier/index.js +1 -1
- package/dist/physics-rapier/index.js.map +1 -1
- package/dist/react/index.d.ts +136 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +251 -10
- package/dist/react/index.js.map +1 -1
- package/dist/rendering/AtmosphereWetness.d.ts +61 -0
- package/dist/rendering/AtmosphereWetness.d.ts.map +1 -0
- package/dist/rendering/AtmosphereWetness.js +99 -0
- package/dist/rendering/AtmosphereWetness.js.map +1 -0
- package/dist/rendering/ClusteredForwardLighting.d.ts +25 -1
- package/dist/rendering/ClusteredForwardLighting.d.ts.map +1 -1
- package/dist/rendering/ClusteredForwardLighting.js +80 -3
- package/dist/rendering/ClusteredForwardLighting.js.map +1 -1
- package/dist/rendering/DayNightSky.d.ts +63 -0
- package/dist/rendering/DayNightSky.d.ts.map +1 -0
- package/dist/rendering/DayNightSky.js +156 -0
- package/dist/rendering/DayNightSky.js.map +1 -0
- package/dist/rendering/EnvironmentPresetPack.d.ts +30 -0
- package/dist/rendering/EnvironmentPresetPack.d.ts.map +1 -0
- package/dist/rendering/EnvironmentPresetPack.js +49 -0
- package/dist/rendering/EnvironmentPresetPack.js.map +1 -0
- package/dist/rendering/ForwardPass.d.ts +65 -0
- package/dist/rendering/ForwardPass.d.ts.map +1 -1
- package/dist/rendering/ForwardPass.js +196 -2
- package/dist/rendering/ForwardPass.js.map +1 -1
- package/dist/rendering/InstancedPBRMaterial.d.ts +1 -0
- package/dist/rendering/InstancedPBRMaterial.d.ts.map +1 -1
- package/dist/rendering/InstancedPBRMaterial.js +3 -0
- package/dist/rendering/InstancedPBRMaterial.js.map +1 -1
- package/dist/rendering/InstancingDiagnostics.d.ts +69 -0
- package/dist/rendering/InstancingDiagnostics.d.ts.map +1 -0
- package/dist/rendering/InstancingDiagnostics.js +98 -0
- package/dist/rendering/InstancingDiagnostics.js.map +1 -0
- package/dist/rendering/LightingRig.d.ts +19 -1
- package/dist/rendering/LightingRig.d.ts.map +1 -1
- package/dist/rendering/LightingRig.js +61 -1
- package/dist/rendering/LightingRig.js.map +1 -1
- package/dist/rendering/MeshConsolidation.d.ts +12 -0
- package/dist/rendering/MeshConsolidation.d.ts.map +1 -1
- package/dist/rendering/MeshConsolidation.js +28 -0
- package/dist/rendering/MeshConsolidation.js.map +1 -1
- package/dist/rendering/MorphTargetPlan.d.ts +29 -2
- package/dist/rendering/MorphTargetPlan.d.ts.map +1 -1
- package/dist/rendering/MorphTargetPlan.js +23 -4
- package/dist/rendering/MorphTargetPlan.js.map +1 -1
- package/dist/rendering/NormalMappedPBRMaterial.d.ts.map +1 -1
- package/dist/rendering/NormalMappedPBRMaterial.js +22 -2
- package/dist/rendering/NormalMappedPBRMaterial.js.map +1 -1
- package/dist/rendering/OceanSurface.d.ts +75 -0
- package/dist/rendering/OceanSurface.d.ts.map +1 -1
- package/dist/rendering/OceanSurface.js +211 -0
- package/dist/rendering/OceanSurface.js.map +1 -1
- package/dist/rendering/PBRMaterial.d.ts +1 -0
- package/dist/rendering/PBRMaterial.d.ts.map +1 -1
- package/dist/rendering/PBRMaterial.js +23 -2
- package/dist/rendering/PBRMaterial.js.map +1 -1
- package/dist/rendering/PbrReference.d.ts +48 -0
- package/dist/rendering/PbrReference.d.ts.map +1 -1
- package/dist/rendering/PbrReference.js +126 -0
- package/dist/rendering/PbrReference.js.map +1 -1
- package/dist/rendering/PlanarReflection.d.ts +221 -0
- package/dist/rendering/PlanarReflection.d.ts.map +1 -0
- package/dist/rendering/PlanarReflection.js +541 -0
- package/dist/rendering/PlanarReflection.js.map +1 -0
- package/dist/rendering/PostProcessPass.d.ts +13 -0
- package/dist/rendering/PostProcessPass.d.ts.map +1 -1
- package/dist/rendering/PostProcessPass.js +15 -4
- package/dist/rendering/PostProcessPass.js.map +1 -1
- package/dist/rendering/ReflectionSurfaces.d.ts +14 -0
- package/dist/rendering/ReflectionSurfaces.d.ts.map +1 -1
- package/dist/rendering/ReflectionSurfaces.js +93 -0
- package/dist/rendering/ReflectionSurfaces.js.map +1 -1
- package/dist/rendering/RenderDevice.d.ts +87 -0
- package/dist/rendering/RenderDevice.d.ts.map +1 -1
- package/dist/rendering/RenderDevice.js +85 -0
- package/dist/rendering/RenderDevice.js.map +1 -1
- package/dist/rendering/Renderer.js +3 -1
- package/dist/rendering/Renderer.js.map +1 -1
- package/dist/rendering/RendererPostprocessPlan.d.ts +48 -0
- package/dist/rendering/RendererPostprocessPlan.d.ts.map +1 -1
- package/dist/rendering/RendererPostprocessPlan.js +101 -11
- package/dist/rendering/RendererPostprocessPlan.js.map +1 -1
- package/dist/rendering/RendererVisualPipelineReport.d.ts.map +1 -1
- package/dist/rendering/RendererVisualPipelineReport.js +3 -1
- package/dist/rendering/RendererVisualPipelineReport.js.map +1 -1
- package/dist/rendering/Sampler.d.ts +29 -0
- package/dist/rendering/Sampler.d.ts.map +1 -1
- package/dist/rendering/Sampler.js +47 -0
- package/dist/rendering/Sampler.js.map +1 -1
- package/dist/rendering/SdfText.d.ts +250 -0
- package/dist/rendering/SdfText.d.ts.map +1 -0
- package/dist/rendering/SdfText.js +551 -0
- package/dist/rendering/SdfText.js.map +1 -0
- package/dist/rendering/ShaderChunks.d.ts.map +1 -1
- package/dist/rendering/ShaderChunks.js +30 -12
- package/dist/rendering/ShaderChunks.js.map +1 -1
- package/dist/rendering/ShaderLibrary.d.ts.map +1 -1
- package/dist/rendering/ShaderLibrary.js +478 -34
- package/dist/rendering/ShaderLibrary.js.map +1 -1
- package/dist/rendering/ShaderLibraryCore.d.ts.map +1 -1
- package/dist/rendering/ShaderLibraryCore.js +70 -12
- package/dist/rendering/ShaderLibraryCore.js.map +1 -1
- package/dist/rendering/ShadowMap.d.ts +24 -0
- package/dist/rendering/ShadowMap.d.ts.map +1 -1
- package/dist/rendering/ShadowMap.js +43 -0
- package/dist/rendering/ShadowMap.js.map +1 -1
- package/dist/rendering/SkinnedLitMaterial.d.ts.map +1 -1
- package/dist/rendering/SkinnedLitMaterial.js +2 -0
- package/dist/rendering/SkinnedLitMaterial.js.map +1 -1
- package/dist/rendering/SkinningBounds.d.ts +10 -0
- package/dist/rendering/SkinningBounds.d.ts.map +1 -1
- package/dist/rendering/SkinningBounds.js +27 -0
- package/dist/rendering/SkinningBounds.js.map +1 -1
- package/dist/rendering/SpriteFlipbook.d.ts +60 -0
- package/dist/rendering/SpriteFlipbook.d.ts.map +1 -0
- package/dist/rendering/SpriteFlipbook.js +128 -0
- package/dist/rendering/SpriteFlipbook.js.map +1 -0
- package/dist/rendering/TerrainTiles.d.ts +120 -0
- package/dist/rendering/TerrainTiles.d.ts.map +1 -0
- package/dist/rendering/TerrainTiles.js +221 -0
- package/dist/rendering/TerrainTiles.js.map +1 -0
- package/dist/rendering/TexturedPBRMaterial.d.ts +1 -0
- package/dist/rendering/TexturedPBRMaterial.d.ts.map +1 -1
- package/dist/rendering/TexturedPBRMaterial.js +23 -2
- package/dist/rendering/TexturedPBRMaterial.js.map +1 -1
- package/dist/rendering/TexturedUnlitMaterial.d.ts.map +1 -1
- package/dist/rendering/TexturedUnlitMaterial.js +20 -2
- package/dist/rendering/TexturedUnlitMaterial.js.map +1 -1
- package/dist/rendering/UnlitMaterial.d.ts.map +1 -1
- package/dist/rendering/UnlitMaterial.js +20 -2
- package/dist/rendering/UnlitMaterial.js.map +1 -1
- package/dist/rendering/VertexFormat.d.ts +6 -0
- package/dist/rendering/VertexFormat.d.ts.map +1 -1
- package/dist/rendering/VertexFormat.js +12 -0
- package/dist/rendering/VertexFormat.js.map +1 -1
- package/dist/rendering/VolumetricFog.d.ts +55 -0
- package/dist/rendering/VolumetricFog.d.ts.map +1 -0
- package/dist/rendering/VolumetricFog.js +91 -0
- package/dist/rendering/VolumetricFog.js.map +1 -0
- package/dist/rendering/WaterSurface.d.ts +77 -0
- package/dist/rendering/WaterSurface.d.ts.map +1 -0
- package/dist/rendering/WaterSurface.js +133 -0
- package/dist/rendering/WaterSurface.js.map +1 -0
- package/dist/rendering/WebGL2Device.d.ts +58 -0
- package/dist/rendering/WebGL2Device.d.ts.map +1 -1
- package/dist/rendering/WebGL2Device.js +536 -17
- package/dist/rendering/WebGL2Device.js.map +1 -1
- package/dist/rendering/WebGPUDevice.d.ts +50 -0
- package/dist/rendering/WebGPUDevice.d.ts.map +1 -1
- package/dist/rendering/WebGPUDevice.js +504 -10
- package/dist/rendering/WebGPUDevice.js.map +1 -1
- package/dist/rendering/WebGPUSkinningLimits.d.ts +40 -0
- package/dist/rendering/WebGPUSkinningLimits.d.ts.map +1 -1
- package/dist/rendering/WebGPUSkinningLimits.js +32 -0
- package/dist/rendering/WebGPUSkinningLimits.js.map +1 -1
- package/dist/rendering/effects/CollisionModule.d.ts +9 -0
- package/dist/rendering/effects/CollisionModule.d.ts.map +1 -1
- package/dist/rendering/effects/CollisionModule.js +10 -0
- package/dist/rendering/effects/CollisionModule.js.map +1 -1
- package/dist/rendering/effects/ColorModule.d.ts +7 -0
- package/dist/rendering/effects/ColorModule.d.ts.map +1 -1
- package/dist/rendering/effects/ColorModule.js +23 -0
- package/dist/rendering/effects/ColorModule.js.map +1 -1
- package/dist/rendering/effects/ForceModule.d.ts +35 -0
- package/dist/rendering/effects/ForceModule.d.ts.map +1 -1
- package/dist/rendering/effects/ForceModule.js +49 -0
- package/dist/rendering/effects/ForceModule.js.map +1 -1
- package/dist/rendering/effects/GPUParticleBackend.d.ts +186 -0
- package/dist/rendering/effects/GPUParticleBackend.d.ts.map +1 -1
- package/dist/rendering/effects/GPUParticleBackend.js +692 -1
- package/dist/rendering/effects/GPUParticleBackend.js.map +1 -1
- package/dist/rendering/effects/HeightfieldModule.d.ts +58 -0
- package/dist/rendering/effects/HeightfieldModule.d.ts.map +1 -0
- package/dist/rendering/effects/HeightfieldModule.js +129 -0
- package/dist/rendering/effects/HeightfieldModule.js.map +1 -0
- package/dist/rendering/effects/LightingModule.d.ts +34 -0
- package/dist/rendering/effects/LightingModule.d.ts.map +1 -0
- package/dist/rendering/effects/LightingModule.js +54 -0
- package/dist/rendering/effects/LightingModule.js.map +1 -0
- package/dist/rendering/effects/ParticleDiagnostics.d.ts +8 -0
- package/dist/rendering/effects/ParticleDiagnostics.d.ts.map +1 -1
- package/dist/rendering/effects/ParticleDiagnostics.js +3 -0
- package/dist/rendering/effects/ParticleDiagnostics.js.map +1 -1
- package/dist/rendering/effects/ParticleModule.d.ts +8 -0
- package/dist/rendering/effects/ParticleModule.d.ts.map +1 -1
- package/dist/rendering/effects/ParticleModule.js.map +1 -1
- package/dist/rendering/effects/ParticleRenderer.d.ts +20 -0
- package/dist/rendering/effects/ParticleRenderer.d.ts.map +1 -1
- package/dist/rendering/effects/ParticleRenderer.js +57 -7
- package/dist/rendering/effects/ParticleRenderer.js.map +1 -1
- package/dist/rendering/effects/ParticleSystem.d.ts +45 -1
- package/dist/rendering/effects/ParticleSystem.d.ts.map +1 -1
- package/dist/rendering/effects/ParticleSystem.js +373 -4
- package/dist/rendering/effects/ParticleSystem.js.map +1 -1
- package/dist/rendering/effects/SizeModule.d.ts +4 -0
- package/dist/rendering/effects/SizeModule.d.ts.map +1 -1
- package/dist/rendering/effects/SizeModule.js +16 -0
- package/dist/rendering/effects/SizeModule.js.map +1 -1
- package/dist/rendering/effects/SubEmitterModule.d.ts +61 -0
- package/dist/rendering/effects/SubEmitterModule.d.ts.map +1 -0
- package/dist/rendering/effects/SubEmitterModule.js +119 -0
- package/dist/rendering/effects/SubEmitterModule.js.map +1 -0
- package/dist/rendering/effects/TrailModule.d.ts +38 -0
- package/dist/rendering/effects/TrailModule.d.ts.map +1 -1
- package/dist/rendering/effects/TrailModule.js +95 -2
- package/dist/rendering/effects/TrailModule.js.map +1 -1
- package/dist/rendering/effects/TurbulenceModule.d.ts +43 -0
- package/dist/rendering/effects/TurbulenceModule.d.ts.map +1 -0
- package/dist/rendering/effects/TurbulenceModule.js +130 -0
- package/dist/rendering/effects/TurbulenceModule.js.map +1 -0
- package/dist/rendering/effects/index.d.ts +4 -0
- package/dist/rendering/effects/index.d.ts.map +1 -1
- package/dist/rendering/effects/index.js +4 -0
- package/dist/rendering/effects/index.js.map +1 -1
- package/dist/rendering/index.d.ts +57 -19
- package/dist/rendering/index.d.ts.map +1 -1
- package/dist/rendering/index.js +31 -10
- package/dist/rendering/index.js.map +1 -1
- package/dist/rendering/materials/MaterialExtensions.js +3 -3
- package/dist/rendering/materials/MaterialExtensions.js.map +1 -1
- package/dist/rendering/postprocess/NativeBloomPyramid.d.ts +28 -0
- package/dist/rendering/postprocess/NativeBloomPyramid.d.ts.map +1 -0
- package/dist/rendering/postprocess/NativeBloomPyramid.js +54 -0
- package/dist/rendering/postprocess/NativeBloomPyramid.js.map +1 -0
- package/dist/rendering/postprocess/NativeLdrEffectLuts.d.ts +10 -1
- package/dist/rendering/postprocess/NativeLdrEffectLuts.d.ts.map +1 -1
- package/dist/rendering/postprocess/NativeLdrEffectLuts.js +19 -2
- package/dist/rendering/postprocess/NativeLdrEffectLuts.js.map +1 -1
- package/dist/rendering/postprocess/index.d.ts +2 -0
- package/dist/rendering/postprocess/index.d.ts.map +1 -1
- package/dist/rendering/postprocess/index.js +1 -0
- package/dist/rendering/postprocess/index.js.map +1 -1
- package/dist/rendering/production-runtime/PBRHDRPipeline.d.ts +22 -0
- package/dist/rendering/production-runtime/PBRHDRPipeline.d.ts.map +1 -1
- package/dist/rendering/production-runtime/PBRHDRPipeline.js +125 -0
- package/dist/rendering/production-runtime/PBRHDRPipeline.js.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.d.ts +52 -0
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.js +101 -0
- package/dist/rendering/production-runtime/ProductionWebGPURenderer.js.map +1 -1
- package/dist/rendering/production-runtime/index.d.ts +5 -4
- package/dist/rendering/production-runtime/index.d.ts.map +1 -1
- package/dist/rendering/production-runtime/index.js +3 -2
- package/dist/rendering/production-runtime/index.js.map +1 -1
- package/dist/rendering/production-runtime/passes/DepthPrepass.d.ts +26 -1
- package/dist/rendering/production-runtime/passes/DepthPrepass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/DepthPrepass.js +49 -2
- package/dist/rendering/production-runtime/passes/DepthPrepass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/FramegraphTopology.d.ts +13 -0
- package/dist/rendering/production-runtime/passes/FramegraphTopology.d.ts.map +1 -0
- package/dist/rendering/production-runtime/passes/FramegraphTopology.js +59 -0
- package/dist/rendering/production-runtime/passes/FramegraphTopology.js.map +1 -0
- package/dist/rendering/production-runtime/passes/OpaquePass.d.ts +24 -1
- package/dist/rendering/production-runtime/passes/OpaquePass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/OpaquePass.js +41 -2
- package/dist/rendering/production-runtime/passes/OpaquePass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/ShadowPass.d.ts +24 -1
- package/dist/rendering/production-runtime/passes/ShadowPass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/ShadowPass.js +43 -2
- package/dist/rendering/production-runtime/passes/ShadowPass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/SkyboxPass.d.ts +21 -1
- package/dist/rendering/production-runtime/passes/SkyboxPass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/SkyboxPass.js +39 -2
- package/dist/rendering/production-runtime/passes/SkyboxPass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/ToneMappingPass.d.ts +29 -1
- package/dist/rendering/production-runtime/passes/ToneMappingPass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/ToneMappingPass.js +53 -2
- package/dist/rendering/production-runtime/passes/ToneMappingPass.js.map +1 -1
- package/dist/rendering/production-runtime/passes/TransparentPass.d.ts +24 -1
- package/dist/rendering/production-runtime/passes/TransparentPass.d.ts.map +1 -1
- package/dist/rendering/production-runtime/passes/TransparentPass.js +42 -2
- package/dist/rendering/production-runtime/passes/TransparentPass.js.map +1 -1
- package/dist/rendering/shadows/CascadeHysteresis.d.ts +67 -0
- package/dist/rendering/shadows/CascadeHysteresis.d.ts.map +1 -0
- package/dist/rendering/shadows/CascadeHysteresis.js +104 -0
- package/dist/rendering/shadows/CascadeHysteresis.js.map +1 -0
- package/dist/rendering/shadows/ContactShadows.d.ts +52 -0
- package/dist/rendering/shadows/ContactShadows.d.ts.map +1 -1
- package/dist/rendering/shadows/ContactShadows.js +122 -0
- package/dist/rendering/shadows/ContactShadows.js.map +1 -1
- package/dist/rendering/shadows/SpotShadowMaps.d.ts +77 -0
- package/dist/rendering/shadows/SpotShadowMaps.d.ts.map +1 -0
- package/dist/rendering/shadows/SpotShadowMaps.js +106 -0
- package/dist/rendering/shadows/SpotShadowMaps.js.map +1 -0
- package/dist/rendering/webgpu/WebGPUPostProcess.d.ts +2 -0
- package/dist/rendering/webgpu/WebGPUPostProcess.d.ts.map +1 -1
- package/dist/rendering/webgpu/WebGPUPostProcess.js +1 -0
- package/dist/rendering/webgpu/WebGPUPostProcess.js.map +1 -1
- package/dist/rendering/webgpu/WebGPUPostShaders.d.ts +78 -0
- package/dist/rendering/webgpu/WebGPUPostShaders.d.ts.map +1 -0
- package/dist/rendering/webgpu/WebGPUPostShaders.js +262 -0
- package/dist/rendering/webgpu/WebGPUPostShaders.js.map +1 -0
- package/dist/scene/Scene.d.ts +8 -1
- package/dist/scene/Scene.d.ts.map +1 -1
- package/dist/scene/Scene.js +12 -2
- package/dist/scene/Scene.js.map +1 -1
- package/dist/scripting/VisualGraphContext.d.ts +31 -0
- package/dist/scripting/VisualGraphContext.d.ts.map +1 -1
- package/dist/scripting/VisualGraphExecutor.d.ts.map +1 -1
- package/dist/scripting/VisualGraphExecutor.js +110 -0
- package/dist/scripting/VisualGraphExecutor.js.map +1 -1
- package/dist/scripting/VisualNodeCatalog.d.ts +1 -1
- package/dist/scripting/VisualNodeCatalog.d.ts.map +1 -1
- package/dist/scripting/VisualNodeCatalog.js +20 -0
- package/dist/scripting/VisualNodeCatalog.js.map +1 -1
- package/dist/scripting/VisualScriptingRoot.d.ts +96 -0
- package/dist/scripting/VisualScriptingRoot.d.ts.map +1 -0
- package/dist/scripting/VisualScriptingRoot.js +150 -0
- package/dist/scripting/VisualScriptingRoot.js.map +1 -0
- package/dist/scripting/index.d.ts +3 -1
- package/dist/scripting/index.d.ts.map +1 -1
- package/dist/scripting/index.js +1 -0
- package/dist/scripting/index.js.map +1 -1
- package/package.json +24 -22
- package/templates/cinematic-scene/node_modules/.vite/deps/_metadata.json +8 -0
- package/templates/cinematic-scene/node_modules/.vite/deps/package.json +3 -0
- package/templates/cinematic-scene/test-results/.last-run.json +6 -0
- package/templates/cinematic-scene/test-results/route-health-Aura3D-cinematic-scene-reaches-ready-state/error-context.md +64 -0
- package/templates/product-viewer/node_modules/.vite/deps/_metadata.json +8 -0
- package/templates/product-viewer/node_modules/.vite/deps/package.json +3 -0
|
@@ -22,10 +22,11 @@ import { PhysicsDebugDraw, PhysicsStepper, ScenePhysicsBridge, Shape as PhysicsS
|
|
|
22
22
|
* of which arrived just to reach one class.
|
|
23
23
|
*/
|
|
24
24
|
import { PhysicsWorld } from "../../physics/world.js";
|
|
25
|
-
import { createExternalParityEnvironmentLighting, Geometry, IndexBuffer, InstancedPBRMaterial, PBRMaterial, ProductionRuntimeRenderer, srgbToLinearChannel, VertexBuffer, VertexFormat } from "../../rendering/index.js";
|
|
25
|
+
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
26
|
import { DirectionalLight, PointLight, SpotLight } from "../../scene/index.js";
|
|
27
27
|
import { lookAtMat4, multiplyMat4, orthographicMat4, perspectiveMat4 } from "../../scene/MathTypes.js";
|
|
28
28
|
import { AURA_NORMALIZED_MODEL_MAX_DIMENSION, boundsFromAsset, boundsHeight, boundsMaxDimension, boundsSize } from "./SceneGroundingUtils.js";
|
|
29
|
+
import { collectLabelTelemetry, summarizeTextBuckets } from "./LabelTelemetry.js";
|
|
29
30
|
import { createWorldLabelLayer } from "./WorldLabelRenderer.js";
|
|
30
31
|
import { createAuraText3DGeometry, defineAuraCustomGeometry, selectAuraRootLodLevel } from "./RootGeometry.js";
|
|
31
32
|
export * from "./RootGeometry.js";
|
|
@@ -49,8 +50,18 @@ export * from "./PhysicsRuntime.js";
|
|
|
49
50
|
*/
|
|
50
51
|
export { buildMeshBVH, createMeshSurfaceQuery, raycastMesh } from "../../physics/solverless.js";
|
|
51
52
|
import { createPhysicsRuntime } from "./PhysicsRuntime.js";
|
|
53
|
+
import { gameCameraRigs } from "./GameCameraRigs.js";
|
|
54
|
+
import { gameFeelBuilders } from "./GameFeel.js";
|
|
52
55
|
export * from "./FocusSelection.js";
|
|
53
56
|
export * from "./WorldLabelRenderer.js";
|
|
57
|
+
export * from "./GameCameraRigs.js";
|
|
58
|
+
export * from "./GameFeel.js";
|
|
59
|
+
export * from "./LabelTelemetry.js";
|
|
60
|
+
export * from "./NavigationCrowds.js";
|
|
61
|
+
export * from "./FootPlanting.js";
|
|
62
|
+
export * from "./Decals.js";
|
|
63
|
+
export * from "./Scatter.js";
|
|
64
|
+
export * from "./AssetDecoders.js";
|
|
54
65
|
export * from "./VehicleChassis.js";
|
|
55
66
|
export * from "./VehicleDriverAi.js";
|
|
56
67
|
/**
|
|
@@ -83,7 +94,16 @@ export * from "./WebCodecsFrameEncoder.js";
|
|
|
83
94
|
*/
|
|
84
95
|
export { GameInspector, createGameInspector } from "./GameInspector.js";
|
|
85
96
|
import { createGameInspector } from "./GameInspector.js";
|
|
97
|
+
import { createPhysicalMaterialSpec } from "../material-physical/PhysicalMaterialSpec.js";
|
|
98
|
+
import { createInstancedModelNode } from "../instances-model/InstancedModel.js";
|
|
99
|
+
import { resolveWrinkleMapStrength, warnOnInstancingFallback } from "../../rendering/index.js";
|
|
86
100
|
export { createGameAudio } from "../game/GameAudio.js";
|
|
101
|
+
export { PositionalEmitter, FootstepPlayer, createGameMixer, attachFocusPolicy, computeDistanceAttenuation, computeDopplerShift, resolveOcclusion } from "../../audio/index.js";
|
|
102
|
+
export { ComboDetector, createTouchLayoutPreset, probeHaptics, playHaptic } from "../../input/index.js";
|
|
103
|
+
export { attachVisualScriptingGraph, createVisualScriptingGraph, listVisualScriptingNodeCatalog } from "../../scripting/index.js";
|
|
104
|
+
export { createRootEditorSurface } from "../../editor-runtime/index.js";
|
|
105
|
+
import { attachVisualScriptingGraph as attachVisualScriptingGraphFn, createVisualScriptingGraph as createVisualScriptingGraphFn, listVisualScriptingNodeCatalog as listVisualScriptingNodeCatalogFn } from "../../scripting/index.js";
|
|
106
|
+
import { createRootEditorSurface as createRootEditorSurfaceFn } from "../../editor-runtime/index.js";
|
|
87
107
|
export * from "./RenderProgressTracker.js";
|
|
88
108
|
export * from "./AudioVisemeAnalyzer.js";
|
|
89
109
|
export * from "./ExternalPhonemeAnalyzer.js";
|
|
@@ -114,6 +134,7 @@ export * from "./EpisodeTemplates.js";
|
|
|
114
134
|
export * from "./AnimationMotionQuality.js";
|
|
115
135
|
export * from "./AnimationRouteProof.js";
|
|
116
136
|
export * from "./AnimationEpisodePackage.js";
|
|
137
|
+
import { evaluateDistancePrioritizedMipResidency } from "../../assets/browser-index.js";
|
|
117
138
|
import { createFrameLoop } from "./FrameLoop.js";
|
|
118
139
|
import { createCombatWorld, createGameCameraDirector, createGameEffects, applyGameCombatEventsToRuntime, createGameAccessibilityFocus, createGameAccessibilityLabel, createGameAccessibilityRuntimeSettings, createGameEventLog, createGameHighContrastSource, createGameHudBindings, createGameHudComboBinding, createGameHudCheckpointBinding, createGameHudDebugToggleBinding, createGameHudEventLogBinding, createGameHudHealthBinding, createGameHudLivesBinding, createGameHudMeterBinding, createGameHudObjectiveBinding, createGameHudRoundBinding, createGameHudScoreBinding, createGameHudSnapshot, createGameHudTimerBinding, createGameHudValueBinding, createGameBoxCollider, createGameCapsuleCollider, createGameColliderDebugGeometry, createGameCombatDebugGeometry, createGameCollisionWorld, createGamePlanarCollisionWorld, createGameDebugOverlayData, createGameDebugSceneNodes, createGameHitboxDebugGeometry, createGameSimulation, exportGameInputReplay, createGameFighting2DRules, createGameInput, createGameInputReplay, createGameInputReplayDriver, importGameInputReplay, createGameJumpAssist, createGameKinematicBody, createGamePauseControlsSource, createGameRectCollider, createGameReducedFlashSource, createGameReducedMotionSource, createGameSphereCollider, createGameTouchControlLayout, gameColliderAabb, gameColliders, gameEffectPresets, gameGuardboxes, gameHitboxes, gameHurtboxes, gameInputReplayEventsAt, gamePushboxes, runGameSimulation, gameTriggerVolumes } from "./GameRuntime.js";
|
|
119
140
|
import { collectGameRuntimeEvidence as collectGameRuntimeEvidenceV105 } from "./GameEvidence.js";
|
|
@@ -153,6 +174,8 @@ export * from "./GameAssetValidation.js";
|
|
|
153
174
|
export * from "./CharacterAssembly.js";
|
|
154
175
|
export * from "./AssetEvidence.js";
|
|
155
176
|
export * from "./AnimationController.js";
|
|
177
|
+
export * from "./AnimationMixerBuilders.js";
|
|
178
|
+
export * from "./AnimationDebugOverlay.js";
|
|
156
179
|
export { gameAssetValidation, quaterniusGameReadyFighterValidationContract, validateQuaterniusGameReadyFighterAsset } from "./GameAssetValidation.js";
|
|
157
180
|
export { createAnimationController } from "./AnimationController.js";
|
|
158
181
|
export * from "./PromptAnimationContract.js";
|
|
@@ -244,7 +267,8 @@ export function model(asset, options = {}) {
|
|
|
244
267
|
targetMaxDimension: options.targetMaxDimension,
|
|
245
268
|
targetLength: options.targetLength,
|
|
246
269
|
physics: options.physics,
|
|
247
|
-
hiddenNodeNames: options.hiddenNodeNames
|
|
270
|
+
hiddenNodeNames: options.hiddenNodeNames,
|
|
271
|
+
wrinkle: options.wrinkle
|
|
248
272
|
});
|
|
249
273
|
}
|
|
250
274
|
export function unsafeModelUrl(url, options = {}) {
|
|
@@ -337,15 +361,102 @@ export const instances = {
|
|
|
337
361
|
cylinder: (options) => instancedPrimitive("cylinder", options),
|
|
338
362
|
capsule: (options) => instancedPrimitive("capsule", options),
|
|
339
363
|
torus: (options) => instancedPrimitive("torus", options),
|
|
340
|
-
custom: (spec, options) => instancedPrimitive("custom", { ...options, geometry: defineAuraCustomGeometry(spec) })
|
|
364
|
+
custom: (spec, options) => instancedPrimitive("custom", { ...options, geometry: defineAuraCustomGeometry(spec) }),
|
|
365
|
+
/**
|
|
366
|
+
* P2 instanced GLB models (muse3jsparity-PRD): one draw class for repeated
|
|
367
|
+
* static models. Skinned actors fall back with a D1 warning at mount (E1
|
|
368
|
+
* owns rigs); explicitly unaware materials warn at build.
|
|
369
|
+
*/
|
|
370
|
+
model: (asset, options) => {
|
|
371
|
+
const built = createInstancedModelNode({
|
|
372
|
+
asset,
|
|
373
|
+
...(options.name !== undefined ? { name: options.name } : {}),
|
|
374
|
+
transforms: options.transforms.map((transform) => ({
|
|
375
|
+
...(transform.position !== undefined ? { position: [...transform.position] } : {}),
|
|
376
|
+
...(transform.rotation !== undefined ? { rotation: [...transform.rotation] } : {}),
|
|
377
|
+
...(transform.scale !== undefined
|
|
378
|
+
? { scale: typeof transform.scale === "number" ? transform.scale : [...transform.scale] }
|
|
379
|
+
: {})
|
|
380
|
+
})),
|
|
381
|
+
...(options.colors ? { colors: [...options.colors] } : {}),
|
|
382
|
+
...(options.lod ? { lod: options.lod } : {}),
|
|
383
|
+
materialInstancingAware: options.instancingAware ?? true,
|
|
384
|
+
...(options.maxInstancesPerDraw !== undefined ? { maxInstancesPerDraw: options.maxInstancesPerDraw } : {}),
|
|
385
|
+
...(options.material?.name !== undefined ? { materialName: options.material.name } : {})
|
|
386
|
+
});
|
|
387
|
+
const base = model(asset, options).toJSON();
|
|
388
|
+
return new AuraNodeBuilder({
|
|
389
|
+
...base,
|
|
390
|
+
instances: built.node.instances.map((transform) => ({
|
|
391
|
+
...(transform.position ? { position: [...transform.position] } : {}),
|
|
392
|
+
...(transform.rotation ? { rotation: [...transform.rotation] } : {}),
|
|
393
|
+
...(transform.scale !== undefined ? { scale: transform.scale } : {})
|
|
394
|
+
})),
|
|
395
|
+
// Safe: the builder only accepts AuraColor strings above, so no RGB tuples reach the node.
|
|
396
|
+
...(built.node.instanceColors ? { instanceColors: [...built.node.instanceColors] } : {}),
|
|
397
|
+
...(built.node.instanceLod ? { instanceLod: built.node.instanceLod } : {}),
|
|
398
|
+
instanceCulling: {
|
|
399
|
+
instanceCount: built.node.instanceCulling.instanceCount,
|
|
400
|
+
centroid: [...built.node.instanceCulling.centroid],
|
|
401
|
+
boundingRadius: built.node.instanceCulling.boundingRadius,
|
|
402
|
+
cullable: true
|
|
403
|
+
},
|
|
404
|
+
...(built.diagnostics.fallbackWarning ? { instancedModelWarning: built.diagnostics.fallbackWarning.diagnostic } : {})
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
export const visualScripting = {
|
|
409
|
+
graph: createVisualScriptingGraphFn,
|
|
410
|
+
attach: attachVisualScriptingGraphFn,
|
|
411
|
+
catalog: listVisualScriptingNodeCatalogFn
|
|
412
|
+
};
|
|
413
|
+
const rootEditorSurface = createRootEditorSurfaceFn();
|
|
414
|
+
export const editor = {
|
|
415
|
+
undo: rootEditorSurface.undo,
|
|
416
|
+
redo: rootEditorSurface.redo,
|
|
417
|
+
gizmo: rootEditorSurface.attachGizmo,
|
|
418
|
+
playMode: { enter: rootEditorSurface.enterPlayMode, exit: rootEditorSurface.exitPlayMode },
|
|
419
|
+
outliner: rootEditorSurface.describeOutliner,
|
|
420
|
+
capabilityLabel: "editor"
|
|
341
421
|
};
|
|
342
422
|
export const geometry = {
|
|
343
423
|
define: defineAuraCustomGeometry,
|
|
344
424
|
custom: (spec, options = {}) => primitive("custom", { ...options, geometry: defineAuraCustomGeometry(spec) })
|
|
345
425
|
};
|
|
426
|
+
let cachedSdfFontAtlas;
|
|
427
|
+
function rootSdfFontAtlas() {
|
|
428
|
+
// Baked once per session (~500ms default res); the caller never bakes per frame.
|
|
429
|
+
cachedSdfFontAtlas ??= createSdfFontAtlas();
|
|
430
|
+
return cachedSdfFontAtlas;
|
|
431
|
+
}
|
|
346
432
|
export function text3D(text, options = {}) {
|
|
347
433
|
const built = createAuraText3DGeometry(text, options);
|
|
348
|
-
|
|
434
|
+
if ((options.backend ?? "extruded-mesh") === "extruded-mesh") {
|
|
435
|
+
return primitive("custom", { ...options, geometry: built.geometry, text3D: { text: built.text, glyphCount: built.glyphCount, unsupportedCharacters: built.unsupportedCharacters, method: built.method } });
|
|
436
|
+
}
|
|
437
|
+
// G1 SDF backend (muse3jsparity-PRD): the layout validates fail-loud and is
|
|
438
|
+
// recorded on the descriptor with the full author intent (size, spacing,
|
|
439
|
+
// style, occlusion policy) so the production bridge can replay the sampler
|
|
440
|
+
// at mount. The extruded mesh stays as the diagnosed fallback geometry.
|
|
441
|
+
const layout = layoutSdfText(text, rootSdfFontAtlas(), { size: options.size ?? 1, letterSpacing: options.letterSpacing, style: options.sdfStyle });
|
|
442
|
+
return primitive("custom", {
|
|
443
|
+
...options,
|
|
444
|
+
geometry: built.geometry,
|
|
445
|
+
text3D: {
|
|
446
|
+
text: built.text,
|
|
447
|
+
glyphCount: built.glyphCount,
|
|
448
|
+
unsupportedCharacters: [...new Set([...built.unsupportedCharacters, ...layout.unsupportedCharacters])],
|
|
449
|
+
method: "sdf-atlas-quad",
|
|
450
|
+
backend: "sdf",
|
|
451
|
+
sdfQuadCount: layout.quads.length,
|
|
452
|
+
sdfWidthWorld: layout.widthWorld,
|
|
453
|
+
sdfHeightWorld: layout.heightWorld,
|
|
454
|
+
sdfSize: options.size ?? 1,
|
|
455
|
+
...(options.letterSpacing === undefined ? {} : { sdfLetterSpacing: options.letterSpacing }),
|
|
456
|
+
...(options.sdfStyle === undefined ? {} : { sdfStyle: options.sdfStyle }),
|
|
457
|
+
sdfOcclusion: options.sdfOcclusion ?? "dim"
|
|
458
|
+
}
|
|
459
|
+
});
|
|
349
460
|
}
|
|
350
461
|
export function distanceLod(options) {
|
|
351
462
|
if (options.levels.length === 0)
|
|
@@ -397,6 +508,13 @@ function proceduralTexture(texture, options = {}) {
|
|
|
397
508
|
colorB: options.colorB
|
|
398
509
|
};
|
|
399
510
|
}
|
|
511
|
+
const PHYSICAL_SPEC_KEYS = [
|
|
512
|
+
"color", "roughness", "metallic", "metalness", "clearcoat", "clearcoatRoughness",
|
|
513
|
+
"sheen", "sheenColor", "sheenRoughness", "iridescence", "iridescenceIOR",
|
|
514
|
+
"iridescenceThicknessRange", "anisotropy", "anisotropyRotation", "transmission",
|
|
515
|
+
"thickness", "attenuationColor", "attenuationDistance", "ior", "specularIntensity",
|
|
516
|
+
"specularColor"
|
|
517
|
+
];
|
|
400
518
|
export const material = {
|
|
401
519
|
pbr: (options = {}) => ({
|
|
402
520
|
color: "#d7dee8",
|
|
@@ -405,7 +523,29 @@ export const material = {
|
|
|
405
523
|
metalness: options.metalness ?? options.metallic ?? 0,
|
|
406
524
|
...options
|
|
407
525
|
}),
|
|
408
|
-
physical: (options = {}) =>
|
|
526
|
+
physical: (options = {}) => {
|
|
527
|
+
// P3 (muse3jsparity-PRD): the sync factory stays scalar (C1 decision —
|
|
528
|
+
// no renderer change); extension params validate through the physical
|
|
529
|
+
// matrix and every non-supported request rides the spec as a warning.
|
|
530
|
+
const defined = PHYSICAL_SPEC_KEYS.reduce((picked, key) => {
|
|
531
|
+
const value = options[key];
|
|
532
|
+
if (value !== undefined)
|
|
533
|
+
picked[key] = value;
|
|
534
|
+
return picked;
|
|
535
|
+
}, {});
|
|
536
|
+
const result = createPhysicalMaterialSpec(defined);
|
|
537
|
+
const normalized = PHYSICAL_SPEC_KEYS.reduce((picked, key) => {
|
|
538
|
+
const value = result.spec[key];
|
|
539
|
+
if (value !== undefined && options[key] !== undefined)
|
|
540
|
+
picked[key] = value;
|
|
541
|
+
return picked;
|
|
542
|
+
}, {});
|
|
543
|
+
return {
|
|
544
|
+
...material.pbr(options),
|
|
545
|
+
...normalized,
|
|
546
|
+
...(result.boundedWarnings.length > 0 ? { physicalWarnings: [...result.boundedWarnings] } : {})
|
|
547
|
+
};
|
|
548
|
+
},
|
|
409
549
|
emissive: (options = {}) => ({
|
|
410
550
|
color: options.color ?? "#111827",
|
|
411
551
|
emissive: options.emissive ?? options.color ?? "#38d6ff",
|
|
@@ -699,33 +839,41 @@ const materialCapabilityCatalog = [
|
|
|
699
839
|
{
|
|
700
840
|
id: "base-color-texture",
|
|
701
841
|
label: "Base-color texture",
|
|
702
|
-
rootSafeApi: "
|
|
842
|
+
rootSafeApi: "supported",
|
|
703
843
|
productionRuntime: "supported",
|
|
704
|
-
evidence: "
|
|
844
|
+
evidence: "Root textured-PBR upgrade resolves AuraAssetRef texture urls to sRGB baseColor textures with controlled texture on/off pixel deltas (tests/browser/root-textured-c1.spec.ts, tests/reports/root-textured-c1/c1-probe.json).",
|
|
705
845
|
claimRule: "Can describe typed texture metadata and rendered textured assets; do not claim full texture-material parity without controlled root pixels."
|
|
706
846
|
},
|
|
707
847
|
{
|
|
708
848
|
id: "metallic-roughness",
|
|
709
849
|
label: "Metallic/roughness",
|
|
710
|
-
rootSafeApi: "
|
|
850
|
+
rootSafeApi: "supported",
|
|
711
851
|
productionRuntime: "supported",
|
|
712
|
-
evidence: "Root
|
|
852
|
+
evidence: "Root textured-PBR upgrade composites roughnessMap/metalnessMap to the glTF metallic-roughness convention with scalar fallbacks and controlled browser pixel proof (tests/browser/root-textured-c1.spec.ts).",
|
|
713
853
|
claimRule: "Do not claim full PBR parity without browser evidence."
|
|
714
854
|
},
|
|
715
855
|
{
|
|
716
856
|
id: "normal-map",
|
|
717
857
|
label: "Normal maps",
|
|
718
|
-
rootSafeApi: "
|
|
858
|
+
rootSafeApi: "supported",
|
|
859
|
+
productionRuntime: "supported",
|
|
860
|
+
evidence: "Root textured-PBR upgrade binds linear normal maps with tangent-space sampling over authored TBN and controlled browser pixel proof (tests/browser/root-textured-c1.spec.ts).",
|
|
861
|
+
claimRule: "Claim only with material-specific screenshot evidence."
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
id: "occlusion-map",
|
|
865
|
+
label: "Occlusion maps",
|
|
866
|
+
rootSafeApi: "supported",
|
|
719
867
|
productionRuntime: "supported",
|
|
720
|
-
evidence: "Root
|
|
868
|
+
evidence: "Root textured-PBR upgrade binds linear occlusionMap textures (R channel) with occlusionStrength and controlled browser pixel proof (tests/browser/root-textured-c1.spec.ts).",
|
|
721
869
|
claimRule: "Claim only with material-specific screenshot evidence."
|
|
722
870
|
},
|
|
723
871
|
{
|
|
724
872
|
id: "emissive",
|
|
725
873
|
label: "Emissive",
|
|
726
|
-
rootSafeApi: "
|
|
874
|
+
rootSafeApi: "supported",
|
|
727
875
|
productionRuntime: "supported",
|
|
728
|
-
evidence: "Root
|
|
876
|
+
evidence: "Root textured-PBR upgrade binds sRGB emissiveMap textures multiplied by the emissive factor with controlled browser pixel proof (tests/browser/root-textured-c1.spec.ts); pixel-backed bloom is reported separately.",
|
|
729
877
|
claimRule: "Distinguish emissive material from postprocess bloom."
|
|
730
878
|
},
|
|
731
879
|
{
|
|
@@ -779,9 +927,9 @@ const materialCapabilityCatalog = [
|
|
|
779
927
|
{
|
|
780
928
|
id: "hdr-ibl",
|
|
781
929
|
label: "HDR/IBL",
|
|
782
|
-
rootSafeApi: "
|
|
930
|
+
rootSafeApi: "supported",
|
|
783
931
|
productionRuntime: "supported",
|
|
784
|
-
evidence: "Root
|
|
932
|
+
evidence: "Root environments.hdri resolves authored Radiance HDR through the HDR-to-cubemap-to-GGX-to-BRDF-LUT chain with iblPixelBacked diagnostics and controlled browser pixel deltas (tests/browser/root-ibl-b3.spec.ts, tests/reports/root-ibl-b3/b3-probe.json).",
|
|
785
933
|
claimRule: "Root HDR/IBL claims require diagnostics and browser pixels."
|
|
786
934
|
},
|
|
787
935
|
{
|
|
@@ -805,7 +953,9 @@ function createMaterialCapabilityDiagnostics(input) {
|
|
|
805
953
|
requested.add("metallic-roughness");
|
|
806
954
|
if (spec.normal)
|
|
807
955
|
requested.add("normal-map");
|
|
808
|
-
if (spec.
|
|
956
|
+
if (spec.occlusionMap || spec.occlusionStrength !== undefined)
|
|
957
|
+
requested.add("occlusion-map");
|
|
958
|
+
if (spec.emissive || spec.emissiveIntensity !== undefined || spec.emissiveMap)
|
|
809
959
|
requested.add("emissive");
|
|
810
960
|
if (spec.opacity !== undefined && spec.opacity < 1)
|
|
811
961
|
requested.add("alpha");
|
|
@@ -829,6 +979,11 @@ function createMaterialCapabilityDiagnostics(input) {
|
|
|
829
979
|
if (partialRequestedFeatures.length > 0) {
|
|
830
980
|
warnings.push(`Partial root material features requested: ${partialRequestedFeatures.join(", ")}. Capture route pixels before claiming production material quality.`);
|
|
831
981
|
}
|
|
982
|
+
for (const spec of specs) {
|
|
983
|
+
for (const physicalWarning of spec.physicalWarnings ?? []) {
|
|
984
|
+
warnings.push(`Physical material extension bounded: ${physicalWarning}`);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
832
987
|
if (unsupportedRequestedFeatures.length > 0) {
|
|
833
988
|
warnings.push(`Metadata/internal material features requested: ${unsupportedRequestedFeatures.join(", ")}. Do not claim rendered support from root createAuraApp alone.`);
|
|
834
989
|
}
|
|
@@ -1048,6 +1203,20 @@ export const lights = {
|
|
|
1048
1203
|
intensity: options.intensity ?? 2,
|
|
1049
1204
|
color: options.color ?? "#ffffff"
|
|
1050
1205
|
}),
|
|
1206
|
+
spot: (options = {}) => new AuraNodeBuilder({
|
|
1207
|
+
kind: "light",
|
|
1208
|
+
light: "spot",
|
|
1209
|
+
name: options.name ?? "spot light",
|
|
1210
|
+
position: options.position ?? [0, 4, 0],
|
|
1211
|
+
target: options.target,
|
|
1212
|
+
angle: options.angle ?? Math.PI / 6,
|
|
1213
|
+
penumbra: options.penumbra ?? 0.4,
|
|
1214
|
+
distance: options.distance ?? 12,
|
|
1215
|
+
decay: options.decay,
|
|
1216
|
+
intensity: options.intensity ?? 8,
|
|
1217
|
+
color: options.color ?? "#ffffff",
|
|
1218
|
+
shadow: options.shadow
|
|
1219
|
+
}),
|
|
1051
1220
|
studio: (options = {}) => new AuraNodeBuilder({
|
|
1052
1221
|
kind: "light",
|
|
1053
1222
|
light: "studio",
|
|
@@ -1257,8 +1426,10 @@ export const camera = {
|
|
|
1257
1426
|
solar: () => camera.orbit({ target: [0, 0, 0], distance: 7.2, fov: 46 }),
|
|
1258
1427
|
humanoid: () => camera.perspective({ position: [1.2, 1.12, 3.45], target: [0, 0.78, -0.55], fov: 36 }),
|
|
1259
1428
|
miniGolf: () => camera.follow({ targetNode: "white physics golf ball", distance: 4.2, fov: 48 }),
|
|
1260
|
-
neon: () => camera.flythrough({ from: [0, 0.36, 1.6], to: [0, 0.36, -5.8], target: [0, 0.26, -6.8], fov: 54, captureTime: 0.16 })
|
|
1429
|
+
neon: () => camera.flythrough({ from: [0, 0.36, 1.6], to: [0, 0.36, -5.8], target: [0, 0.26, -6.8], fov: 54, captureTime: 0.16 }),
|
|
1430
|
+
...gameCameraRigs
|
|
1261
1431
|
};
|
|
1432
|
+
export const gameFeel = { create: gameFeelBuilders.create, hitStopDefaults: gameFeelBuilders.hitStopDefaults };
|
|
1262
1433
|
function resolveFrameAssetRenderScale(bounds, options) {
|
|
1263
1434
|
const size = boundsSize(bounds);
|
|
1264
1435
|
const height = Math.max(0.001, boundsHeight(bounds));
|
|
@@ -1314,7 +1485,10 @@ export const effects = {
|
|
|
1314
1485
|
radius: options.radius ?? 0.38,
|
|
1315
1486
|
threshold: options.threshold ?? 0.7,
|
|
1316
1487
|
antiBlowout,
|
|
1317
|
-
maxIntensity
|
|
1488
|
+
maxIntensity,
|
|
1489
|
+
...(options.quality !== undefined ? { quality: options.quality } : {}),
|
|
1490
|
+
...(options.softKnee !== undefined ? { softKnee: options.softKnee } : {}),
|
|
1491
|
+
...(options.shoulder !== undefined ? { shoulder: options.shoulder } : {})
|
|
1318
1492
|
});
|
|
1319
1493
|
},
|
|
1320
1494
|
cinematicBloom: (options = {}) => effects.bloom({
|
|
@@ -1333,10 +1507,22 @@ export const effects = {
|
|
|
1333
1507
|
antiBlowout: options.antiBlowout ?? true,
|
|
1334
1508
|
maxIntensity: options.maxIntensity ?? 0.92
|
|
1335
1509
|
}),
|
|
1336
|
-
|
|
1510
|
+
/**
|
|
1511
|
+
* A5 volumetric fog (muse3jsparity-PRD): builds a DISTINCT "volumetric-fog"
|
|
1512
|
+
* node (never plain fog) that submits the depth-aware inscatter pass plus
|
|
1513
|
+
* forward GPU height-fog terms. quality "off" keeps forward exp2 fog only.
|
|
1514
|
+
*/
|
|
1515
|
+
volumetricFog: (options = {}) => new AuraNodeBuilder({
|
|
1516
|
+
kind: "effect",
|
|
1517
|
+
effect: "volumetric-fog",
|
|
1518
|
+
name: options.name ?? "volumetric fog inscatter",
|
|
1337
1519
|
density: options.density ?? 0.18,
|
|
1338
1520
|
color: options.color ?? "#6f84b9",
|
|
1339
|
-
intensity: options.intensity ?? 0.7
|
|
1521
|
+
intensity: options.intensity ?? 0.7,
|
|
1522
|
+
...(options.volumetricQuality ? { volumetricQuality: options.volumetricQuality } : {}),
|
|
1523
|
+
...(options.lightPosition ? { lightPosition: options.lightPosition } : {}),
|
|
1524
|
+
...(options.heightFalloff !== undefined ? { heightFalloff: options.heightFalloff } : {}),
|
|
1525
|
+
...(options.heightReference !== undefined ? { heightReference: options.heightReference } : {})
|
|
1340
1526
|
}),
|
|
1341
1527
|
depthFog: (options = {}) => effects.fog({
|
|
1342
1528
|
density: options.density ?? 0.14,
|
|
@@ -1361,6 +1547,82 @@ export const effects = {
|
|
|
1361
1547
|
density: options.density ?? 0.7,
|
|
1362
1548
|
color: options.color ?? "#020617"
|
|
1363
1549
|
}),
|
|
1550
|
+
/**
|
|
1551
|
+
* Root color-grade node (muse3jsparity-PRD A3). contrast/saturation execute
|
|
1552
|
+
* natively; exposure/shadows/highlights/lut are recorded on the node and
|
|
1553
|
+
* warned (no native grade target yet — never silently accepted).
|
|
1554
|
+
*/
|
|
1555
|
+
colorGrade: (options = {}) => new AuraNodeBuilder({
|
|
1556
|
+
kind: "effect",
|
|
1557
|
+
effect: "color-grade",
|
|
1558
|
+
name: options.name ?? "color grade",
|
|
1559
|
+
intensity: options.intensity ?? 1,
|
|
1560
|
+
exposure: options.exposure ?? 1,
|
|
1561
|
+
contrast: options.contrast ?? 1,
|
|
1562
|
+
saturation: options.saturation ?? 1,
|
|
1563
|
+
...(options.shadows !== undefined ? { shadows: options.shadows } : {}),
|
|
1564
|
+
...(options.highlights !== undefined ? { highlights: options.highlights } : {}),
|
|
1565
|
+
...(options.lut !== undefined ? { lut: options.lut } : {})
|
|
1566
|
+
}),
|
|
1567
|
+
/**
|
|
1568
|
+
* Root anti-alias node (muse3jsparity-PRD A3). `fxaa` executes natively;
|
|
1569
|
+
* `off` submits nothing; `taa` is recorded but withheld (no history binding
|
|
1570
|
+
* at root) with an explicit diagnostic warning.
|
|
1571
|
+
*/
|
|
1572
|
+
antiAlias: (options = {}) => new AuraNodeBuilder({
|
|
1573
|
+
kind: "effect",
|
|
1574
|
+
effect: "anti-alias",
|
|
1575
|
+
name: options.name ?? "anti alias",
|
|
1576
|
+
mode: options.mode ?? "fxaa",
|
|
1577
|
+
intensity: options.intensity ?? 1
|
|
1578
|
+
}),
|
|
1579
|
+
/**
|
|
1580
|
+
* Root outline node (muse3jsparity-PRD A3): native outline pass, width in
|
|
1581
|
+
* pixels clamped to the device range 1-6 by the bridge.
|
|
1582
|
+
*/
|
|
1583
|
+
outline: (options = {}) => new AuraNodeBuilder({
|
|
1584
|
+
kind: "effect",
|
|
1585
|
+
effect: "outline",
|
|
1586
|
+
name: options.name ?? "outline",
|
|
1587
|
+
color: options.color ?? "#ff9822",
|
|
1588
|
+
width: options.width ?? 3,
|
|
1589
|
+
threshold: options.threshold ?? 0.12,
|
|
1590
|
+
intensity: options.intensity ?? 0.9
|
|
1591
|
+
}),
|
|
1592
|
+
/**
|
|
1593
|
+
* Root screen-space reflections node (muse3jsparity-PRD A3): submits the
|
|
1594
|
+
* native SSR pass against renderer-owned depth where available.
|
|
1595
|
+
*/
|
|
1596
|
+
screenSpaceReflections: (options = {}) => new AuraNodeBuilder({
|
|
1597
|
+
kind: "effect",
|
|
1598
|
+
effect: "screen-space-reflections",
|
|
1599
|
+
name: options.name ?? "screen space reflections",
|
|
1600
|
+
intensity: options.intensity ?? 0.9
|
|
1601
|
+
}),
|
|
1602
|
+
/**
|
|
1603
|
+
* Root depth-of-field node (muse3jsparity-PRD A3). focus is a
|
|
1604
|
+
* linear-distance fraction (0 = near plane, 1 = far plane); aperture widens
|
|
1605
|
+
* the focus transition band (game-tuned control, not a physical f-stop).
|
|
1606
|
+
*/
|
|
1607
|
+
depthOfField: (options = {}) => new AuraNodeBuilder({
|
|
1608
|
+
kind: "effect",
|
|
1609
|
+
effect: "depth-of-field",
|
|
1610
|
+
name: options.name ?? "depth of field",
|
|
1611
|
+
focus: options.focus ?? 0.02,
|
|
1612
|
+
aperture: options.aperture ?? 0.35,
|
|
1613
|
+
maxBlur: options.maxBlur ?? 4,
|
|
1614
|
+
intensity: options.intensity ?? 1
|
|
1615
|
+
}),
|
|
1616
|
+
/**
|
|
1617
|
+
* Root motion-blur node (muse3jsparity-PRD A3): recorded but withheld (no
|
|
1618
|
+
* velocity binding at root) with an explicit diagnostic warning.
|
|
1619
|
+
*/
|
|
1620
|
+
motionBlur: (options = {}) => new AuraNodeBuilder({
|
|
1621
|
+
kind: "effect",
|
|
1622
|
+
effect: "motion-blur",
|
|
1623
|
+
name: options.name ?? "motion blur",
|
|
1624
|
+
intensity: options.intensity ?? 0.5
|
|
1625
|
+
}),
|
|
1364
1626
|
rain: (options = {}) => new AuraNodeBuilder({
|
|
1365
1627
|
kind: "effect",
|
|
1366
1628
|
effect: "rain",
|
|
@@ -1373,6 +1635,62 @@ export const effects = {
|
|
|
1373
1635
|
splashes: options.splashes ?? true,
|
|
1374
1636
|
mist: options.mist ?? true
|
|
1375
1637
|
}),
|
|
1638
|
+
snow: (options = {}) => new AuraNodeBuilder({
|
|
1639
|
+
kind: "effect",
|
|
1640
|
+
effect: "snow",
|
|
1641
|
+
intensity: options.intensity ?? 0.4,
|
|
1642
|
+
density: options.density ?? 0.68,
|
|
1643
|
+
color: options.color ?? "#e8f1ff",
|
|
1644
|
+
speed: options.speed ?? 1,
|
|
1645
|
+
wind: options.wind ?? [-0.85, -1.1, -0.22],
|
|
1646
|
+
particleCount: options.particleCount,
|
|
1647
|
+
splashes: options.splashes ?? false,
|
|
1648
|
+
mist: options.mist ?? true
|
|
1649
|
+
}),
|
|
1650
|
+
/**
|
|
1651
|
+
* Flipbook explosion/muzzle-flash sprite sheet (muse3jsparity-PRD D4):
|
|
1652
|
+
* recorded with validated sheet geometry, but withheld — root has no
|
|
1653
|
+
* native sprite-sheet sampler yet, so no pass is submitted.
|
|
1654
|
+
*/
|
|
1655
|
+
flipbook: (options = {}) => {
|
|
1656
|
+
const columns = options.spriteColumns ?? 4;
|
|
1657
|
+
const rows = options.spriteRows ?? 4;
|
|
1658
|
+
resolveFlipbookUv(0, columns, rows);
|
|
1659
|
+
return new AuraNodeBuilder({
|
|
1660
|
+
kind: "effect",
|
|
1661
|
+
effect: "flipbook-sprite",
|
|
1662
|
+
name: options.name ?? "flipbook explosion sprite",
|
|
1663
|
+
intensity: options.intensity ?? 1,
|
|
1664
|
+
color: options.color ?? "#ffb347",
|
|
1665
|
+
spriteColumns: columns,
|
|
1666
|
+
spriteRows: rows,
|
|
1667
|
+
frameRate: options.frameRate ?? 24
|
|
1668
|
+
});
|
|
1669
|
+
},
|
|
1670
|
+
/**
|
|
1671
|
+
* Additive thick light beam / fence strip (muse3jsparity-PRD D4): recorded
|
|
1672
|
+
* with a validated quad-strip descriptor, but withheld — root has no native
|
|
1673
|
+
* beam target yet, so no pass is submitted.
|
|
1674
|
+
*/
|
|
1675
|
+
beam: (options = {}) => {
|
|
1676
|
+
const descriptor = createBeamDescriptor({
|
|
1677
|
+
from: options.from ?? [0, 1, 0],
|
|
1678
|
+
to: options.to ?? [0, 1, -4],
|
|
1679
|
+
...(options.widthWorld !== undefined ? { widthWorld: options.widthWorld } : {}),
|
|
1680
|
+
...(options.segmentCount !== undefined ? { segmentCount: options.segmentCount } : {})
|
|
1681
|
+
});
|
|
1682
|
+
return new AuraNodeBuilder({
|
|
1683
|
+
kind: "effect",
|
|
1684
|
+
effect: "light-beam",
|
|
1685
|
+
name: options.name ?? "additive light beam",
|
|
1686
|
+
intensity: options.intensity ?? 0.9,
|
|
1687
|
+
color: options.color ?? "#9fd8ff",
|
|
1688
|
+
from: [...descriptor.from],
|
|
1689
|
+
to: [...descriptor.to],
|
|
1690
|
+
widthWorld: descriptor.widthWorld,
|
|
1691
|
+
segmentCount: descriptor.segmentCount
|
|
1692
|
+
});
|
|
1693
|
+
},
|
|
1376
1694
|
particles: (options = {}) => new AuraNodeBuilder({
|
|
1377
1695
|
kind: "effect",
|
|
1378
1696
|
effect: "particles",
|
|
@@ -1398,6 +1716,161 @@ export const effects = {
|
|
|
1398
1716
|
noise: options.noise ?? 0.22
|
|
1399
1717
|
})
|
|
1400
1718
|
};
|
|
1719
|
+
/**
|
|
1720
|
+
* D3 atmosphere builders (PRD D3 boxes 1-2). ADDITIVE root surface over the
|
|
1721
|
+
* pure descriptors in `@aura3d/rendering` (`DayNightSky.ts`,
|
|
1722
|
+
* `AtmosphereWetness.ts`, `Weather.ts`).
|
|
1723
|
+
*
|
|
1724
|
+
* Every builder below composes pre-existing node kinds only (primitive,
|
|
1725
|
+
* light, effect); no existing builder is modified. Precipitation pixels in
|
|
1726
|
+
* the default production path come from weather-state-driven primitive
|
|
1727
|
+
* streaks/flakes, because the production bridge does not pixel-back `rain`,
|
|
1728
|
+
* `snow`, or `particles` effect passes (they render only in the safe-basic
|
|
1729
|
+
* fallback and the Canvas2D diagnostic path). The matching effect node is
|
|
1730
|
+
* still declared so diagnostics report the request.
|
|
1731
|
+
*/
|
|
1732
|
+
export const sky = {
|
|
1733
|
+
/** Time-of-day sky: background key, sun/moon discs, stars, 2D-noise clouds, key light. */
|
|
1734
|
+
dayNight: (options = {}) => {
|
|
1735
|
+
const state = createDayNightSky(options);
|
|
1736
|
+
const nodes = [];
|
|
1737
|
+
const sunUp = state.sun.elevationRadians > -0.05;
|
|
1738
|
+
const moonUp = state.moon.elevationRadians > -0.05;
|
|
1739
|
+
if (sunUp) {
|
|
1740
|
+
nodes.push(primitives.sphere({ name: "d3 sun disc", material: material.emissive({ color: state.sun.color, emissive: state.sun.color, emissiveIntensity: 2.2, roughness: 0.8 }) })
|
|
1741
|
+
.position(-6 * Math.cos(state.sun.azimuthRadians), 1 + 5 * Math.sin(state.sun.azimuthRadians), -7).scale(0.85).toJSON());
|
|
1742
|
+
}
|
|
1743
|
+
if (moonUp) {
|
|
1744
|
+
nodes.push(primitives.sphere({ name: "d3 moon disc", material: material.emissive({ color: state.moon.color, emissive: state.moon.color, emissiveIntensity: 1.4, roughness: 0.8 }) })
|
|
1745
|
+
.position(-6 * Math.cos(state.moon.azimuthRadians), 1 + 5 * Math.sin(state.moon.azimuthRadians), -7).scale(0.6).toJSON());
|
|
1746
|
+
}
|
|
1747
|
+
const starLimit = Math.max(0, Math.min(120, options.starLimit ?? 48));
|
|
1748
|
+
const visibleStars = state.stars.filter((star) => star.brightness > 0.05 && star.y > 0).slice(0, starLimit);
|
|
1749
|
+
for (const [index, star] of visibleStars.entries()) {
|
|
1750
|
+
nodes.push(primitives.sphere({ name: `d3 night star ${index}`, material: material.emissive({ color: "#dbeafe", emissive: "#bfdbfe", emissiveIntensity: 1.8, roughness: 0.9 }) })
|
|
1751
|
+
.position(star.x * 7, star.y * 4 + 0.6, -6.5).scale(0.028 + star.size * 2).toJSON());
|
|
1752
|
+
}
|
|
1753
|
+
const cloudLimit = Math.max(0, Math.min(48, options.cloudLimit ?? 12));
|
|
1754
|
+
const cloudTint = state.dayFactor > 0.5 ? "#f1f5f9" : state.dayFactor > 0.2 ? "#b6a6a6" : "#1e293b";
|
|
1755
|
+
for (const [index, cell] of state.clouds.slice(0, cloudLimit).entries()) {
|
|
1756
|
+
nodes.push(primitives.sphere({ name: `d3 noise cloud ${index}`, material: material.pbr({ color: cloudTint, roughness: 1, metallic: 0 }) })
|
|
1757
|
+
.position(cell.x * 6, cell.y * 3 + 1.4, -5.5).scale([cell.radius * 1.7, cell.radius * 0.5, cell.radius * 0.8]).toJSON());
|
|
1758
|
+
}
|
|
1759
|
+
nodes.push(lights.directional({
|
|
1760
|
+
name: sunUp ? "d3 sun key light" : "d3 moon key light",
|
|
1761
|
+
position: sunUp ? [-4, 5, 2] : [3, 4, -1],
|
|
1762
|
+
color: sunUp ? state.sun.color : state.moon.color,
|
|
1763
|
+
intensity: sunUp ? 0.6 + state.dayFactor * 1.2 : 0.35
|
|
1764
|
+
}).toJSON());
|
|
1765
|
+
return { nodes, background: state.dayFactor > 0.5 ? state.horizonColor : state.nightFactor > 0.6 ? "#020617" : state.horizonColor, dayFactor: state.dayFactor, visibleStarCount: visibleStars.length };
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1768
|
+
export const weather = {
|
|
1769
|
+
/** Rain/snow declaration + weather-state-driven primitive streaks/flakes for production-path pixels. */
|
|
1770
|
+
precipitation: (options) => {
|
|
1771
|
+
const state = createWeatherState({
|
|
1772
|
+
type: options.type,
|
|
1773
|
+
seed: options.seed ?? 0xd3e7,
|
|
1774
|
+
elapsedSeconds: options.elapsedSeconds ?? 1.2,
|
|
1775
|
+
maxVisualDrops: 400
|
|
1776
|
+
});
|
|
1777
|
+
const snowing = options.type === "snow";
|
|
1778
|
+
const intensity = snowing ? Math.max(0.2, state.snowIntensity) : Math.max(0.2, state.rainIntensity);
|
|
1779
|
+
const nodes = [
|
|
1780
|
+
(snowing ? effects.snow({ intensity, color: "#e8f1ff" }) : effects.rain({ intensity, color: "#bcd7ff" })).toJSON()
|
|
1781
|
+
];
|
|
1782
|
+
const limit = Math.max(0, Math.min(160, options.streakLimit ?? (snowing ? 70 : 90)));
|
|
1783
|
+
const drops = state.visualDrops.slice(0, limit);
|
|
1784
|
+
drops.forEach((drop, index) => {
|
|
1785
|
+
const x = Math.max(-3.4, Math.min(3.4, drop.x * 2.4));
|
|
1786
|
+
const y = Math.max(0.15, Math.min(3.4, drop.y + 1.6));
|
|
1787
|
+
const z = Math.max(-4.5, Math.min(2.5, drop.z * 2.4));
|
|
1788
|
+
if (snowing) {
|
|
1789
|
+
nodes.push(primitives.sphere({ name: `d3 snow flake ${index}`, material: material.pbr({ color: "#eef4ff", roughness: 0.85, metallic: 0 }) })
|
|
1790
|
+
.position(x, y, z).scale(0.035).toJSON());
|
|
1791
|
+
}
|
|
1792
|
+
else {
|
|
1793
|
+
nodes.push(primitives.box({ name: `d3 rain streak ${index}`, material: material.pbr({ color: "#bcd7ff", roughness: 0.35, metallic: 0 }) })
|
|
1794
|
+
.position(x, y, z).scale([0.014, Math.max(0.08, drop.length * 2.4), 0.014]).toJSON());
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
return { nodes, dropCount: drops.length, wetness: state.wetness };
|
|
1798
|
+
},
|
|
1799
|
+
/** Ground slab with wetness darkening + puddle discs mapped from WeatherPuddlePatch. */
|
|
1800
|
+
wetGround: (options = {}) => {
|
|
1801
|
+
const probe = describeWetMaterial({
|
|
1802
|
+
type: options.type ?? "rain",
|
|
1803
|
+
dryColor: options.dryColor ?? "#5b6b4f",
|
|
1804
|
+
dryRoughness: options.dryRoughness ?? 0.9,
|
|
1805
|
+
seed: options.seed ?? 0xd3e7
|
|
1806
|
+
});
|
|
1807
|
+
const size = Math.max(2, Math.min(14, options.size ?? 8));
|
|
1808
|
+
const nodes = [
|
|
1809
|
+
primitives.box({ name: "d3 weather ground slab", material: material.pbr({ color: probe.response.albedoColor, roughness: probe.response.roughness, metallic: 0 }) })
|
|
1810
|
+
.position(0, -0.02, 0).scale([size, 0.04, size]).toJSON()
|
|
1811
|
+
];
|
|
1812
|
+
probe.weather.puddlePatches.forEach((patch, index) => {
|
|
1813
|
+
nodes.push(primitives.cylinder({ name: `d3 puddle disc ${index}`, material: material.pbr({ color: "#16283a", roughness: 0.05, metallic: 0.1 }) })
|
|
1814
|
+
.position(patch.x * 2.4, 0.005, patch.z * 2.4).scale([Math.max(0.06, patch.radius * 3), 0.006, Math.max(0.06, patch.radius * 3)]).toJSON());
|
|
1815
|
+
});
|
|
1816
|
+
return {
|
|
1817
|
+
nodes,
|
|
1818
|
+
wetness: probe.response.wetness,
|
|
1819
|
+
albedoColor: probe.response.albedoColor,
|
|
1820
|
+
roughness: probe.response.roughness,
|
|
1821
|
+
puddleCount: probe.weather.puddlePatches.length
|
|
1822
|
+
};
|
|
1823
|
+
},
|
|
1824
|
+
/** Lightning-flash light hook: intensity is 0 unless type is thunderstorm. */
|
|
1825
|
+
lightning: (options = { type: "thunderstorm" }) => {
|
|
1826
|
+
const probe = describeWetMaterial({ type: options.type, elapsedSeconds: options.elapsedSeconds ?? 0.4, seed: options.seed ?? 0xd3e7 });
|
|
1827
|
+
return {
|
|
1828
|
+
nodes: probe.flash.intensity > 0
|
|
1829
|
+
? [lights.directional({ name: "d3 lightning flash", color: "#dbeafe", intensity: 1 + probe.flash.intensity * 4, position: [2, 6, -3] }).toJSON()]
|
|
1830
|
+
: [],
|
|
1831
|
+
intensity: probe.flash.intensity
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
};
|
|
1835
|
+
/**
|
|
1836
|
+
* D3 water surface builder (PRD D3 box 3). ADDITIVE root surface over
|
|
1837
|
+
* `createWaterSurface` in `@aura3d/rendering`.
|
|
1838
|
+
*
|
|
1839
|
+
* Rendered material = layered opaque depth-tinted bands (bounded refraction
|
|
1840
|
+
* look) + fresnel-baked sky tint + white shore-foam discs mapped from
|
|
1841
|
+
* `OceanFoamPatch` + boat + fading wake trail. Buoyancy queries stay on the
|
|
1842
|
+
* fixture (`sampleOceanFixture().buoyancy`); this builder creates no planar
|
|
1843
|
+
* reflection/refraction targets (B4 dependency, see
|
|
1844
|
+
* WATER_SURFACE_PLANAR_DEPENDENCY).
|
|
1845
|
+
*/
|
|
1846
|
+
export const water = {
|
|
1847
|
+
surface: (options = {}) => {
|
|
1848
|
+
const state = createWaterSurface({ preset: options.preset, seed: options.seed, boat: options.boat });
|
|
1849
|
+
const nodes = [];
|
|
1850
|
+
const bandSpan = 4 / state.bands.length;
|
|
1851
|
+
state.bands.forEach((band, index) => {
|
|
1852
|
+
nodes.push(primitives.box({ name: `d3 water depth band ${index}`, material: material.pbr({ color: band.color, roughness: 0.12, metallic: 0.05 }) })
|
|
1853
|
+
.position(0, 0.02 + index * 0.0012, -4.5 + bandSpan * (index + 0.5)).scale([9, 0.024, bandSpan + 0.01]).toJSON());
|
|
1854
|
+
});
|
|
1855
|
+
nodes.push(primitives.box({ name: "d3 shoreline sand", material: material.pbr({ color: "#cbb37f", roughness: 0.95, metallic: 0 }) })
|
|
1856
|
+
.position(0, 0, 0.9).scale([9, 0.03, 2.6]).toJSON());
|
|
1857
|
+
for (const [index, patch] of state.foam.entries()) {
|
|
1858
|
+
nodes.push(primitives.sphere({ name: `d3 shore foam ${index}`, material: material.pbr({ color: "#f4fafd", roughness: 0.55, metallic: 0 }) })
|
|
1859
|
+
.position(Math.max(-4.2, Math.min(4.2, patch.x * 3)), 0.045, -0.42 + (index % 3) * 0.12).scale([Math.max(0.08, patch.radius * 6), 0.02, Math.max(0.05, patch.radius * 3)]).toJSON());
|
|
1860
|
+
}
|
|
1861
|
+
if (options.withBoatHull !== false && options.boat) {
|
|
1862
|
+
nodes.push(primitives.box({ name: "d3 wake boat hull", material: material.pbr({ color: "#7c3f21", roughness: 0.7, metallic: 0 }) })
|
|
1863
|
+
.position(options.boat.x ?? 0, 0.12, options.boat.z ?? -2).scale([0.34, 0.16, 0.9]).toJSON());
|
|
1864
|
+
}
|
|
1865
|
+
state.wake.forEach((segment, index) => {
|
|
1866
|
+
nodes.push(primitives.box({ name: `d3 boat wake ${index}`, material: material.pbr({ color: "#cfeaf7", roughness: 0.3, metallic: 0 }) })
|
|
1867
|
+
.position(segment.x * 2, 0.038, segment.z - 1.2).scale([segment.width, 0.012, 0.3]).toJSON());
|
|
1868
|
+
});
|
|
1869
|
+
return { nodes, bandCount: state.bands.length, foamCount: state.foam.length, wakeActive: state.wakeActive, wakeSegmentCount: state.wake.length };
|
|
1870
|
+
},
|
|
1871
|
+
/** Fixture-side buoyancy telemetry passthrough (no physics implemented here). */
|
|
1872
|
+
buoyancy: (options = {}) => sampleOceanFixture({ preset: options.preset ?? "moderate", seed: options.seed ?? 0xaa7e5 }).buoyancy
|
|
1873
|
+
};
|
|
1401
1874
|
export const interactions = {
|
|
1402
1875
|
orbit: (options = {}) => new AuraNodeBuilder({
|
|
1403
1876
|
kind: "interaction",
|
|
@@ -1648,7 +2121,24 @@ export const environments = {
|
|
|
1648
2121
|
if (materialClass === "product")
|
|
1649
2122
|
return environments.productHero();
|
|
1650
2123
|
return environments.studio();
|
|
1651
|
-
}
|
|
2124
|
+
},
|
|
2125
|
+
/**
|
|
2126
|
+
* B3 root HDRI environment (muse3jsparity-PRD). The `.hdr` asset resolves
|
|
2127
|
+
* post-mount: first frames render the honest studio procedural fallback,
|
|
2128
|
+
* then the HDR→cubemap→GGX-prefilter→BRDF-LUT chain swaps in and
|
|
2129
|
+
* `iblPixelBacked` flips true in diagnostics. Fetch/parse failures keep the
|
|
2130
|
+
* fallback and warn — never a black scene, never a silent swap.
|
|
2131
|
+
*/
|
|
2132
|
+
hdri: (options) => new AuraNodeBuilder({
|
|
2133
|
+
kind: "environment",
|
|
2134
|
+
environment: "hdri",
|
|
2135
|
+
name: options.name ?? "hdri ibl environment",
|
|
2136
|
+
intensity: options.intensity ?? 1,
|
|
2137
|
+
texture: options.texture,
|
|
2138
|
+
...(options.reflectionTexture ? { reflectionTexture: options.reflectionTexture } : {}),
|
|
2139
|
+
...(options.color ? { color: options.color } : {}),
|
|
2140
|
+
...(options.rotation !== undefined ? { rotation: options.rotation } : {})
|
|
2141
|
+
})
|
|
1652
2142
|
};
|
|
1653
2143
|
const rendererColorManagementPreset = {
|
|
1654
2144
|
kind: "aura-renderer-color-management",
|
|
@@ -1774,6 +2264,7 @@ function normalizeCreateAppRendererOptions(options) {
|
|
|
1774
2264
|
mode,
|
|
1775
2265
|
fallback: options?.fallback ?? "safe-basic",
|
|
1776
2266
|
qualityProfile: profile.id,
|
|
2267
|
+
textureBudgetBytes: normalizeTextureBudgetBytes(options?.textureBudgetBytes),
|
|
1777
2268
|
profile
|
|
1778
2269
|
};
|
|
1779
2270
|
}
|
|
@@ -1817,17 +2308,42 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1817
2308
|
const sceneCategory = resolveRendererSceneCategory(snapshot, names);
|
|
1818
2309
|
const exposure = sceneExposurePresets[sceneCategory];
|
|
1819
2310
|
const bloom = flattened.find((node) => node.kind === "effect" && node.effect === "bloom");
|
|
1820
|
-
const fog = flattened.find((node) => node.kind === "effect" && node.effect === "fog");
|
|
2311
|
+
const fog = flattened.find((node) => node.kind === "effect" && (node.effect === "fog" || node.effect === "volumetric-fog"));
|
|
1821
2312
|
const environment = flattened.find((node) => node.kind === "environment");
|
|
1822
2313
|
const contactShadows = names.filter((name) => name.includes("contact shadow") || name.includes("footprint") || name.includes("glow pool")).length;
|
|
1823
2314
|
const ambientOcclusion = flattened.some((node) => node.kind === "effect" && node.effect === "ambient-occlusion");
|
|
1824
2315
|
const contactOcclusion = flattened.some((node) => node.kind === "effect" && node.effect === "contact-occlusion") || contactShadows > 0;
|
|
2316
|
+
// muse3jsparity-PRD A3: newly requestable root passes. motion-blur and
|
|
2317
|
+
// taa-mode anti-alias are recorded but withheld (no velocity/history binding
|
|
2318
|
+
// at root) — requestedPasses names the intent, actualPasses the execution.
|
|
2319
|
+
const colorGradeNode = flattened.find((node) => node.kind === "effect" && node.effect === "color-grade");
|
|
2320
|
+
const antiAliasNode = flattened.find((node) => node.kind === "effect" && node.effect === "anti-alias");
|
|
2321
|
+
const outlineNode = flattened.find((node) => node.kind === "effect" && node.effect === "outline");
|
|
2322
|
+
const ssrNode = flattened.find((node) => node.kind === "effect" && node.effect === "screen-space-reflections");
|
|
2323
|
+
const dofNode = flattened.find((node) => node.kind === "effect" && node.effect === "depth-of-field");
|
|
2324
|
+
const motionBlurNode = flattened.find((node) => node.kind === "effect" && node.effect === "motion-blur");
|
|
2325
|
+
const flipbookNode = flattened.find((node) => node.kind === "effect" && node.effect === "flipbook-sprite");
|
|
2326
|
+
const beamNode = flattened.find((node) => node.kind === "effect" && node.effect === "light-beam");
|
|
2327
|
+
const sdfTextRequested = flattened.some((node) => node.kind === "primitive" && node.text3D?.backend === "sdf");
|
|
2328
|
+
const fxaaRequested = (antiAliasNode?.mode ?? "fxaa") === "fxaa" && Boolean(antiAliasNode);
|
|
2329
|
+
const taaWithheld = (antiAliasNode?.mode ?? "fxaa") === "taa";
|
|
2330
|
+
const motionBlurWithheld = Boolean(motionBlurNode);
|
|
1825
2331
|
const runtimePostprocess = runtime?.postprocess;
|
|
1826
2332
|
const runtimePasses = runtimePostprocess?.actualPasses ?? [];
|
|
1827
|
-
const postprocessRequested = Boolean(bloom) || ambientOcclusion || contactOcclusion
|
|
2333
|
+
const postprocessRequested = Boolean(bloom) || ambientOcclusion || contactOcclusion
|
|
2334
|
+
|| Boolean(colorGradeNode) || Boolean(antiAliasNode) || Boolean(outlineNode)
|
|
2335
|
+
|| Boolean(ssrNode) || Boolean(dofNode) || motionBlurWithheld || runtimePasses.length > 0;
|
|
1828
2336
|
const requestedPasses = runtimePasses.length > 0
|
|
1829
2337
|
? runtimePasses
|
|
1830
|
-
: requestedRendererPostProcessPasses(Boolean(bloom), ambientOcclusion, contactOcclusion
|
|
2338
|
+
: requestedRendererPostProcessPasses(Boolean(bloom), ambientOcclusion, contactOcclusion, {
|
|
2339
|
+
colorGrade: Boolean(colorGradeNode),
|
|
2340
|
+
fxaa: fxaaRequested,
|
|
2341
|
+
outline: Boolean(outlineNode),
|
|
2342
|
+
ssr: Boolean(ssrNode),
|
|
2343
|
+
depthOfField: Boolean(dofNode),
|
|
2344
|
+
motionBlurWithheld,
|
|
2345
|
+
taaWithheld
|
|
2346
|
+
});
|
|
1831
2347
|
const runtimeStatus = !postprocessRequested
|
|
1832
2348
|
? "disabled"
|
|
1833
2349
|
: runtime?.mounted
|
|
@@ -1842,6 +2358,28 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1842
2358
|
warnings.push("scene has no contact shadow or contact-occlusion grounding cue");
|
|
1843
2359
|
if (bloom && (bloom.intensity ?? 0) > 0.95 && bloom.antiBlowout !== true)
|
|
1844
2360
|
warnings.push("bloom is high without anti-blowout safeguards");
|
|
2361
|
+
if (motionBlurWithheld)
|
|
2362
|
+
warnings.push("motion-blur is recorded but withheld: root has no velocity binding, so no motion-blur pass is submitted");
|
|
2363
|
+
if (flipbookNode)
|
|
2364
|
+
warnings.push("flipbook-sprite is recorded but withheld: root has no native sprite-sheet sampler yet, so no flipbook pass is submitted");
|
|
2365
|
+
if (beamNode)
|
|
2366
|
+
warnings.push("light-beam is recorded but withheld: root has no native beam target yet, so no beam pass is submitted");
|
|
2367
|
+
// G1: the baked-SDF sampler mounts synchronously on the production bridge.
|
|
2368
|
+
// Warn only when the SDF request cannot be pixel-backed (non-production
|
|
2369
|
+
// mount, or the sampler fell back to the extruded mesh at runtime).
|
|
2370
|
+
if (sdfTextRequested && (!runtime?.mounted || !(runtime.text?.textPixelBacked ?? false))) {
|
|
2371
|
+
warnings.push(runtime?.mounted
|
|
2372
|
+
? `text3D sdf backend fell back to the extruded mesh (${runtime.text?.reason ?? "sampler failed"}); textPixelBacked stays false`
|
|
2373
|
+
: "text3D sdf backend is recorded but unmounted: the production bridge samples the SDF atlas at mount, so textPixelBacked stays false until render");
|
|
2374
|
+
}
|
|
2375
|
+
if (taaWithheld)
|
|
2376
|
+
warnings.push("anti-alias mode \"taa\" is recorded but withheld: root has no history binding, so no taa pass is submitted");
|
|
2377
|
+
if (colorGradeNode && (colorGradeNode.exposure ?? 1) !== 1)
|
|
2378
|
+
warnings.push("color-grade exposure is recorded but has no native grade target yet; contrast/saturation execute");
|
|
2379
|
+
if (colorGradeNode && (colorGradeNode.shadows !== undefined || colorGradeNode.highlights !== undefined))
|
|
2380
|
+
warnings.push("color-grade shadows/highlights are recorded but have no native grade target yet; contrast/saturation execute");
|
|
2381
|
+
if (colorGradeNode?.lut !== undefined)
|
|
2382
|
+
warnings.push("color-grade lut is recorded but LUT samplers are not bound yet; the lut is ignored");
|
|
1845
2383
|
if (postprocessRequested && !runtime?.mounted)
|
|
1846
2384
|
warnings.push("renderer diagnostics are a scene plan only; call createAuraApp(...).diagnostics() after the first render for pixel-backed pass status");
|
|
1847
2385
|
if (postprocessRequested && runtime?.mounted && !runtimePostprocess?.pixelBacked)
|
|
@@ -1891,7 +2429,8 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1891
2429
|
intensity: bloom?.intensity ?? 0,
|
|
1892
2430
|
threshold: bloom?.threshold ?? 0,
|
|
1893
2431
|
radius: bloom?.radius ?? 0,
|
|
1894
|
-
antiBlowout: bloom?.antiBlowout ?? true
|
|
2432
|
+
antiBlowout: bloom?.antiBlowout ?? true,
|
|
2433
|
+
...(bloom?.quality !== undefined ? { quality: bloom.quality } : {})
|
|
1895
2434
|
},
|
|
1896
2435
|
shadows: {
|
|
1897
2436
|
// Never a source-authored `true`. When the runtime is mounted this is the
|
|
@@ -1906,7 +2445,8 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1906
2445
|
nativeShadowMapBindings: runtime?.shadow?.nativeShadowMapBindings ?? 0,
|
|
1907
2446
|
shadowRenderTargetsAllocated: runtime?.shadow?.shadowRenderTargetsAllocated ?? 0,
|
|
1908
2447
|
contactShadows,
|
|
1909
|
-
mapType: "pcf-soft"
|
|
2448
|
+
mapType: "pcf-soft",
|
|
2449
|
+
...(runtime?.shadow?.spot === undefined ? {} : { spot: runtime.shadow.spot })
|
|
1910
2450
|
},
|
|
1911
2451
|
occlusion: {
|
|
1912
2452
|
enabled: ambientOcclusion || contactOcclusion,
|
|
@@ -1921,7 +2461,9 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1921
2461
|
fog: {
|
|
1922
2462
|
enabled: Boolean(fog),
|
|
1923
2463
|
density: fog?.density ?? 0,
|
|
1924
|
-
|
|
2464
|
+
// A5: a volumetric-fog node labels "volumetric" because it submits the
|
|
2465
|
+
// distinct inscatter pass; plain fog keeps the legacy intensity label.
|
|
2466
|
+
preset: fog ? (fog.effect === "volumetric-fog" || (fog.intensity ?? 0) > 0.65 ? "volumetric" : "depth") : "none"
|
|
1925
2467
|
},
|
|
1926
2468
|
postprocess: {
|
|
1927
2469
|
enabled: runtimePostprocess?.pixelBacked ?? false,
|
|
@@ -1937,6 +2479,7 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1937
2479
|
actualPasses: runtimePostprocess?.actualPasses ?? [],
|
|
1938
2480
|
fallbackPasses: runtimePostprocess?.fallbackPasses ?? [],
|
|
1939
2481
|
targetFormat: runtimePostprocess?.targetFormat,
|
|
2482
|
+
executionMode: runtimePostprocess?.executionMode ?? "unknown",
|
|
1940
2483
|
evidence: postprocessEvidence
|
|
1941
2484
|
},
|
|
1942
2485
|
runtime: {
|
|
@@ -1950,13 +2493,39 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1950
2493
|
visibleObjects: runtime?.deviceDiagnostics?.visibleObjects ?? 0,
|
|
1951
2494
|
culledObjects: runtime?.deviceDiagnostics?.culledObjects ?? 0,
|
|
1952
2495
|
frustumTestedObjects: runtime?.deviceDiagnostics?.frustumTestedObjects ?? 0,
|
|
1953
|
-
|
|
2496
|
+
bloom: runtime?.deviceDiagnostics?.bloom ?? null,
|
|
2497
|
+
samplerAnisotropyUploads: runtime?.deviceDiagnostics?.samplerAnisotropyUploads ?? 0,
|
|
2498
|
+
maxTextureAnisotropy: runtime?.deviceDiagnostics?.maxTextureAnisotropy ?? 1,
|
|
2499
|
+
lodSelections: runtime?.lodSelections ?? [],
|
|
2500
|
+
texturedMaterials: runtime?.texturedMaterials ?? []
|
|
1954
2501
|
},
|
|
1955
2502
|
environment: {
|
|
1956
2503
|
enabled: environmentStatus.enabled,
|
|
1957
2504
|
preset: environmentStatus.preset,
|
|
1958
2505
|
intensity: environmentStatus.intensity,
|
|
1959
|
-
evidence: environmentStatus.evidence
|
|
2506
|
+
evidence: environmentStatus.evidence,
|
|
2507
|
+
// B3: pass through the HDRI upgrade observation; the unmounted default
|
|
2508
|
+
// reports not-pixel-backed so static diagnostics never claim HDRI proof.
|
|
2509
|
+
iblPixelBacked: environmentStatus.iblPixelBacked ?? false,
|
|
2510
|
+
hdriStatus: environmentStatus.hdriStatus ?? "none",
|
|
2511
|
+
...(environmentStatus.dualProbe === undefined ? {} : { dualProbe: environmentStatus.dualProbe }),
|
|
2512
|
+
...(environmentStatus.hdriRotation === undefined ? {} : { hdriRotation: environmentStatus.hdriRotation })
|
|
2513
|
+
},
|
|
2514
|
+
text: runtime?.text ?? {
|
|
2515
|
+
sdfTexts: flattened.filter((node) => node.kind === "primitive" && node.text3D?.backend === "sdf").length,
|
|
2516
|
+
textPixelBacked: false,
|
|
2517
|
+
quadCount: 0,
|
|
2518
|
+
lastOpacity: 1,
|
|
2519
|
+
reason: "SDF text requested in the scene graph but no runtime has mounted yet"
|
|
2520
|
+
},
|
|
2521
|
+
textures: runtime?.textures ?? {
|
|
2522
|
+
budgetBytes: normalizeTextureBudgetBytes(rendererOptions?.textureBudgetBytes),
|
|
2523
|
+
usedBytes: 0,
|
|
2524
|
+
requestedBytes: 0,
|
|
2525
|
+
overBudget: false,
|
|
2526
|
+
overBudgetBytes: 0,
|
|
2527
|
+
residentEntries: 0,
|
|
2528
|
+
evictedEntries: []
|
|
1960
2529
|
},
|
|
1961
2530
|
antialiasing: rendererQualityPresets.screenshot.antialiasing,
|
|
1962
2531
|
screenshotQuality: rendererQualityPresets.screenshot,
|
|
@@ -1964,14 +2533,31 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1964
2533
|
warnings
|
|
1965
2534
|
};
|
|
1966
2535
|
}
|
|
1967
|
-
function requestedRendererPostProcessPasses(hasBloom, ambientOcclusion, contactOcclusion) {
|
|
1968
|
-
if (!hasBloom && !ambientOcclusion && !contactOcclusion
|
|
2536
|
+
function requestedRendererPostProcessPasses(hasBloom, ambientOcclusion, contactOcclusion, extra = {}) {
|
|
2537
|
+
if (!hasBloom && !ambientOcclusion && !contactOcclusion && !extra.colorGrade && !extra.fxaa
|
|
2538
|
+
&& !extra.outline && !extra.ssr && !extra.depthOfField && !extra.motionBlurWithheld && !extra.taaWithheld)
|
|
1969
2539
|
return [];
|
|
1970
2540
|
const passes = ["render"];
|
|
1971
2541
|
if (ambientOcclusion || contactOcclusion)
|
|
1972
2542
|
passes.push("ssao");
|
|
1973
2543
|
if (hasBloom)
|
|
1974
2544
|
passes.push("bloom");
|
|
2545
|
+
if (extra.colorGrade)
|
|
2546
|
+
passes.push("color-grade");
|
|
2547
|
+
if (extra.ssr)
|
|
2548
|
+
passes.push("ssr");
|
|
2549
|
+
if (extra.depthOfField)
|
|
2550
|
+
passes.push("depth-of-field");
|
|
2551
|
+
if (extra.outline)
|
|
2552
|
+
passes.push("outline");
|
|
2553
|
+
if (extra.fxaa)
|
|
2554
|
+
passes.push("fxaa");
|
|
2555
|
+
// Withheld intents stay visible in the request list with an explicit suffix
|
|
2556
|
+
// so requested-vs-actual never silently agrees on an unexecuted pass.
|
|
2557
|
+
if (extra.motionBlurWithheld)
|
|
2558
|
+
passes.push("motion-blur (withheld: no velocity binding)");
|
|
2559
|
+
if (extra.taaWithheld)
|
|
2560
|
+
passes.push("taa (withheld: no history binding)");
|
|
1975
2561
|
passes.push("output");
|
|
1976
2562
|
return passes;
|
|
1977
2563
|
}
|
|
@@ -6384,6 +6970,13 @@ export function createRuntimeNodeImportedAssetEvidence(input) {
|
|
|
6384
6970
|
renderItemCount: input.renderItemCount ?? 0,
|
|
6385
6971
|
skinnedRenderItemCount: input.skinnedRenderItemCount ?? 0,
|
|
6386
6972
|
morphRenderItemCount: input.morphRenderItemCount ?? 0,
|
|
6973
|
+
...(input.lastMaterialTracksApplied !== undefined ? { lastMaterialTracksApplied: input.lastMaterialTracksApplied } : {}),
|
|
6974
|
+
...(input.lastLightTracksApplied !== undefined ? { lastLightTracksApplied: input.lastLightTracksApplied } : {}),
|
|
6975
|
+
...(input.lastFootPlantingGroundedFeet !== undefined ? { lastFootPlantingGroundedFeet: input.lastFootPlantingGroundedFeet } : {}),
|
|
6976
|
+
...(input.lastFootPlantingTargetError !== undefined ? { lastFootPlantingTargetError: input.lastFootPlantingTargetError } : {}),
|
|
6977
|
+
...(input.lastFootPlantingHipOffset !== undefined ? { lastFootPlantingHipOffset: input.lastFootPlantingHipOffset } : {}),
|
|
6978
|
+
...(input.lastFootPlantingMissingLegs !== undefined ? { lastFootPlantingMissingLegs: [...input.lastFootPlantingMissingLegs] } : {}),
|
|
6979
|
+
...(input.footPlantingConfigured !== undefined ? { footPlantingConfigured: input.footPlantingConfigured } : {}),
|
|
6387
6980
|
diagnostics
|
|
6388
6981
|
};
|
|
6389
6982
|
}
|
|
@@ -6749,7 +7342,9 @@ export function createAuraApp(target, options) {
|
|
|
6749
7342
|
appPhysicsWorldInstance ??= new PhysicsWorld({
|
|
6750
7343
|
gravity: options.physics?.gravity ? [...options.physics.gravity] : [0, -9.81, 0],
|
|
6751
7344
|
fixedDelta: 1 / 60,
|
|
6752
|
-
enableSleeping: true
|
|
7345
|
+
enableSleeping: true,
|
|
7346
|
+
...(options.physics?.seed === undefined ? {} : { seed: options.physics.seed }),
|
|
7347
|
+
...(options.physics?.continuousCollision ? { continuousCollision: options.physics.continuousCollision } : {})
|
|
6753
7348
|
});
|
|
6754
7349
|
return appPhysicsWorldInstance;
|
|
6755
7350
|
};
|
|
@@ -7717,6 +8312,13 @@ pausedRenderTime = () => 0) {
|
|
|
7717
8312
|
labelLayer.setOcclusionTest(createSceneLabelOcclusionTest(snapshot, resolveCameraFrame(snapshot, snapshot.camera, renderTime, runtimeNodes).eye, runtimeNodes));
|
|
7718
8313
|
labelLayer.update(renderer.viewProjection(renderTime));
|
|
7719
8314
|
diagnosticsState.labels = labelLayer.snapshot();
|
|
8315
|
+
const labelNodes = groups.flatten(snapshot.nodes).filter((node) => node.kind === "label");
|
|
8316
|
+
diagnosticsState.labelTelemetry = collectLabelTelemetry(labelNodes, diagnosticsState.labels);
|
|
8317
|
+
diagnosticsState.textBuckets = summarizeTextBuckets({
|
|
8318
|
+
accessibleDom: labelNodes.filter((node) => node.label === "hud").length,
|
|
8319
|
+
worldAnchoredPlaced: diagnosticsState.labels.filter((label) => label.visible).length,
|
|
8320
|
+
sdfTexts: groups.flatten(snapshot.nodes).filter((node) => node.kind === "primitive" && node.text3D?.backend === "sdf").length
|
|
8321
|
+
});
|
|
7720
8322
|
}
|
|
7721
8323
|
overlay?.update();
|
|
7722
8324
|
if ((sceneWantsFrames || requiresFrames()) && options.autoStart !== false && typeof requestAnimationFrame !== "undefined") {
|
|
@@ -7814,16 +8416,37 @@ function createProductionRuntimeEnvironment(snapshot) {
|
|
|
7814
8416
|
"product-hero": "softbox",
|
|
7815
8417
|
"night-cinematic": "evening",
|
|
7816
8418
|
"metal-studio": "exhibit",
|
|
7817
|
-
"glass-studio": "softbox"
|
|
8419
|
+
"glass-studio": "softbox",
|
|
8420
|
+
// B3: HDRI first frames render the honest studio procedural fallback;
|
|
8421
|
+
// the post-mount upgrade swaps in the HDR chain (hdriUrl below).
|
|
8422
|
+
"hdri": "studio"
|
|
7818
8423
|
};
|
|
7819
8424
|
const preset = presetByEnvironment[authoredEnvironment.environment];
|
|
7820
8425
|
const bundle = createExternalParityEnvironmentLighting(preset);
|
|
7821
8426
|
const intensity = nonNegativeFinite(authoredEnvironment.intensity);
|
|
7822
8427
|
const proceduralMap = bundle.lighting.proceduralMap;
|
|
8428
|
+
const hdriUrl = authoredEnvironment.environment === "hdri"
|
|
8429
|
+
&& typeof authoredEnvironment.texture === "object"
|
|
8430
|
+
&& authoredEnvironment.texture.kind === "aura-asset-ref"
|
|
8431
|
+
? authoredEnvironment.texture.url ?? undefined
|
|
8432
|
+
: undefined;
|
|
8433
|
+
const hdriReflectionUrl = authoredEnvironment.environment === "hdri"
|
|
8434
|
+
&& typeof authoredEnvironment.reflectionTexture === "object"
|
|
8435
|
+
&& authoredEnvironment.reflectionTexture.kind === "aura-asset-ref"
|
|
8436
|
+
? authoredEnvironment.reflectionTexture.url ?? undefined
|
|
8437
|
+
: undefined;
|
|
8438
|
+
const hdriRotation = authoredEnvironment.environment === "hdri" && authoredEnvironment.rotation !== undefined
|
|
8439
|
+
? authoredEnvironment.rotation
|
|
8440
|
+
: undefined;
|
|
7823
8441
|
return {
|
|
7824
8442
|
preset: authoredEnvironment.environment,
|
|
7825
8443
|
intensity,
|
|
7826
|
-
evidence:
|
|
8444
|
+
evidence: hdriUrl
|
|
8445
|
+
? `production bridge renders the studio procedural fallback until the authored HDRI ${hdriUrl}${hdriReflectionUrl ? ` (reflection ${hdriReflectionUrl})` : ""} resolves through the HDR chain`
|
|
8446
|
+
: `production bridge submitted the authored ${authoredEnvironment.environment} environment through generated HDR ${preset} lighting`,
|
|
8447
|
+
...(hdriUrl ? { hdriUrl } : {}),
|
|
8448
|
+
...(hdriReflectionUrl ? { hdriReflectionUrl } : {}),
|
|
8449
|
+
...(hdriRotation !== undefined ? { hdriRotation } : {}),
|
|
7827
8450
|
lighting: {
|
|
7828
8451
|
...bundle.lighting,
|
|
7829
8452
|
color: authoredEnvironment.color ? colorToLinearRgb(authoredEnvironment.color) : bundle.lighting.color,
|
|
@@ -7878,14 +8501,16 @@ function createProductionRuntimeEnvironment(snapshot) {
|
|
|
7878
8501
|
* changed exactly zero pixels through `createAuraApp`. Returning `false` when no
|
|
7879
8502
|
* fog node is authored keeps the unfogged path unchanged.
|
|
7880
8503
|
*/
|
|
7881
|
-
function createProductionRuntimeEnvironmentFog(snapshot) {
|
|
7882
|
-
const
|
|
8504
|
+
function createProductionRuntimeEnvironmentFog(snapshot, lights = [], renderWidth = 1280, renderHeight = 720) {
|
|
8505
|
+
const nodes = groups.flatten(snapshot.nodes);
|
|
8506
|
+
const volumetric = nodes.find((node) => node.kind === "effect" && node.effect === "volumetric-fog");
|
|
8507
|
+
const fog = volumetric ?? nodes.find((node) => node.kind === "effect" && node.effect === "fog");
|
|
7883
8508
|
if (!fog)
|
|
7884
8509
|
return false;
|
|
7885
8510
|
const density = clampNumber(fog.density ?? 0.12, 0, 1);
|
|
7886
8511
|
const intensity = clampNumber(fog.intensity ?? 0.5, 0, 1);
|
|
7887
8512
|
const color = colorToLinearRgb(fog.color ?? "#9fb7d9");
|
|
7888
|
-
|
|
8513
|
+
const base = {
|
|
7889
8514
|
// Exponential-squared reads as depth haze rather than a hard linear band, which
|
|
7890
8515
|
// matches what the public helper documents.
|
|
7891
8516
|
mode: "exponential-squared",
|
|
@@ -7895,10 +8520,46 @@ function createProductionRuntimeEnvironmentFog(snapshot) {
|
|
|
7895
8520
|
density,
|
|
7896
8521
|
maxOpacity: clampNumber(0.25 + intensity * 0.55, 0, 0.92)
|
|
7897
8522
|
};
|
|
8523
|
+
if (!volumetric)
|
|
8524
|
+
return base;
|
|
8525
|
+
// A5: the volumetric-fog node drives the GPU forward inscatter terms from
|
|
8526
|
+
// the dominant collected light; quality "off" zeroes the forward term and
|
|
8527
|
+
// keeps this exp2 base exactly.
|
|
8528
|
+
const resolved = resolveVolumetricFog({
|
|
8529
|
+
density: volumetric.density,
|
|
8530
|
+
intensity: volumetric.intensity,
|
|
8531
|
+
...(volumetric.lightPosition ? { lightPosition: volumetric.lightPosition } : {}),
|
|
8532
|
+
...(volumetric.heightFalloff !== undefined ? { heightFalloff: volumetric.heightFalloff } : {}),
|
|
8533
|
+
...(volumetric.heightReference !== undefined ? { heightReference: volumetric.heightReference } : {}),
|
|
8534
|
+
...(volumetric.volumetricQuality ? { quality: volumetric.volumetricQuality } : {})
|
|
8535
|
+
}, lights, renderWidth, renderHeight);
|
|
8536
|
+
return {
|
|
8537
|
+
...base,
|
|
8538
|
+
density: clampNumber(volumetric.density ?? 0.18, 0, 1),
|
|
8539
|
+
volumetricIntensity: resolved.forward.volumetricIntensity,
|
|
8540
|
+
...(resolved.forward.volumetricLightDirection ? { volumetricLightDirection: resolved.forward.volumetricLightDirection } : {}),
|
|
8541
|
+
volumetricLightColor: resolved.forward.volumetricLightColor,
|
|
8542
|
+
...(resolved.forward.heightFalloff !== undefined ? { heightFalloff: resolved.forward.heightFalloff } : {}),
|
|
8543
|
+
...(resolved.forward.heightReference !== undefined ? { heightReference: resolved.forward.heightReference } : {})
|
|
8544
|
+
};
|
|
7898
8545
|
}
|
|
7899
|
-
function createProductionRuntimePostprocess(snapshot) {
|
|
8546
|
+
function createProductionRuntimePostprocess(snapshot, lights = [], renderWidth = 1280, renderHeight = 720) {
|
|
7900
8547
|
const nodes = groups.flatten(snapshot.nodes);
|
|
7901
8548
|
const authoredBloom = nodes.find((node) => node.kind === "effect" && node.effect === "bloom");
|
|
8549
|
+
// A5: the DISTINCT volumetric-fog node submits the depth-aware inscatter
|
|
8550
|
+
// pass (renderer-owned depth attaches automatically); quality "off" (or no
|
|
8551
|
+
// node) submits nothing and the scene keeps forward exp2 fog only.
|
|
8552
|
+
const authoredVolumetricFog = nodes.find((node) => node.kind === "effect" && node.effect === "volumetric-fog");
|
|
8553
|
+
const volumetricPass = authoredVolumetricFog
|
|
8554
|
+
? resolveVolumetricFog({
|
|
8555
|
+
density: authoredVolumetricFog.density,
|
|
8556
|
+
intensity: authoredVolumetricFog.intensity,
|
|
8557
|
+
...(authoredVolumetricFog.lightPosition ? { lightPosition: authoredVolumetricFog.lightPosition } : {}),
|
|
8558
|
+
...(authoredVolumetricFog.heightFalloff !== undefined ? { heightFalloff: authoredVolumetricFog.heightFalloff } : {}),
|
|
8559
|
+
...(authoredVolumetricFog.heightReference !== undefined ? { heightReference: authoredVolumetricFog.heightReference } : {}),
|
|
8560
|
+
...(authoredVolumetricFog.volumetricQuality ? { quality: authoredVolumetricFog.volumetricQuality } : {})
|
|
8561
|
+
}, lights, renderWidth, renderHeight).pass
|
|
8562
|
+
: null;
|
|
7902
8563
|
const bloomRequested = Boolean(authoredBloom);
|
|
7903
8564
|
// Root previously advertised `ssao` in `requestedPasses` whenever a scene added
|
|
7904
8565
|
// effects.ambientOcclusion() or effects.contactOcclusion(), but never submitted
|
|
@@ -7908,6 +8569,17 @@ function createProductionRuntimePostprocess(snapshot) {
|
|
|
7908
8569
|
const authoredAmbientOcclusion = nodes.find((node) => node.kind === "effect" && node.effect === "ambient-occlusion");
|
|
7909
8570
|
const authoredContactOcclusion = nodes.find((node) => node.kind === "effect" && node.effect === "contact-occlusion");
|
|
7910
8571
|
const authoredOcclusion = authoredAmbientOcclusion ?? authoredContactOcclusion;
|
|
8572
|
+
// muse3jsparity-PRD A3: color-grade / outline / fxaa / ssr / dof submit real
|
|
8573
|
+
// native options. motion-blur and taa are withheld (velocity/history have no
|
|
8574
|
+
// root binding): the status layer warns instead of submitting doomed options
|
|
8575
|
+
// that would fail the route with zero draw calls.
|
|
8576
|
+
const authoredColorGrade = nodes.find((node) => node.kind === "effect" && node.effect === "color-grade");
|
|
8577
|
+
const authoredAntiAlias = nodes.find((node) => node.kind === "effect" && node.effect === "anti-alias");
|
|
8578
|
+
const authoredOutline = nodes.find((node) => node.kind === "effect" && node.effect === "outline");
|
|
8579
|
+
const authoredSsr = nodes.find((node) => node.kind === "effect" && node.effect === "screen-space-reflections");
|
|
8580
|
+
const authoredDof = nodes.find((node) => node.kind === "effect" && node.effect === "depth-of-field");
|
|
8581
|
+
const fxaaRequested = (authoredAntiAlias?.mode ?? "fxaa") === "fxaa";
|
|
8582
|
+
const outlineChannels = colorToRgba(authoredOutline?.color ?? "#ff9822");
|
|
7911
8583
|
return {
|
|
7912
8584
|
// Tone mapping requires unclamped linear input. RGBA8 quantized dark clear
|
|
7913
8585
|
// colors and clipped highlights before ACES, which produced washed-out output.
|
|
@@ -7916,7 +8588,10 @@ function createProductionRuntimePostprocess(snapshot) {
|
|
|
7916
8588
|
bloom: {
|
|
7917
8589
|
threshold: clampNumber(authoredBloom?.threshold ?? 0.78, 0, 1),
|
|
7918
8590
|
intensity: clampNumber(authoredBloom?.intensity ?? 0.3, 0, 2),
|
|
7919
|
-
radius: Math.max(1, Math.min(4, Math.round(authoredBloom?.radius ?? 2)))
|
|
8591
|
+
radius: Math.max(1, Math.min(4, Math.round(authoredBloom?.radius ?? 2))),
|
|
8592
|
+
...(authoredBloom?.quality !== undefined ? { quality: authoredBloom.quality } : {}),
|
|
8593
|
+
...(authoredBloom?.softKnee !== undefined ? { softKnee: authoredBloom.softKnee } : {}),
|
|
8594
|
+
...(authoredBloom?.shoulder !== undefined ? { shoulder: authoredBloom.shoulder } : {})
|
|
7920
8595
|
}
|
|
7921
8596
|
} : {}),
|
|
7922
8597
|
...(authoredOcclusion ? {
|
|
@@ -7937,7 +8612,41 @@ function createProductionRuntimePostprocess(snapshot) {
|
|
|
7937
8612
|
operator: "aces",
|
|
7938
8613
|
inputColorSpace: "linear",
|
|
7939
8614
|
outputColorSpace: "srgb"
|
|
7940
|
-
}
|
|
8615
|
+
},
|
|
8616
|
+
...(authoredColorGrade ? {
|
|
8617
|
+
colorGrade: {
|
|
8618
|
+
contrast: clampNumber(authoredColorGrade.contrast ?? 1, 0, 3),
|
|
8619
|
+
saturation: clampNumber(authoredColorGrade.saturation ?? 1, 0, 3)
|
|
8620
|
+
}
|
|
8621
|
+
} : {}),
|
|
8622
|
+
...(authoredOutline ? {
|
|
8623
|
+
outline: {
|
|
8624
|
+
color: [
|
|
8625
|
+
Math.round(outlineChannels[0] * 255),
|
|
8626
|
+
Math.round(outlineChannels[1] * 255),
|
|
8627
|
+
Math.round(outlineChannels[2] * 255),
|
|
8628
|
+
255
|
|
8629
|
+
],
|
|
8630
|
+
width: Math.max(1, Math.min(6, Math.round(authoredOutline.width ?? 3))),
|
|
8631
|
+
threshold: clampNumber(authoredOutline.threshold ?? 0.12, 0, 4),
|
|
8632
|
+
opacity: clampNumber(authoredOutline.intensity ?? 0.9, 0, 1)
|
|
8633
|
+
}
|
|
8634
|
+
} : {}),
|
|
8635
|
+
...(fxaaRequested && authoredAntiAlias ? { fxaa: {} } : {}),
|
|
8636
|
+
...(authoredSsr ? {
|
|
8637
|
+
ssr: {
|
|
8638
|
+
intensity: clampNumber(authoredSsr.intensity ?? 0.9, 0, 2),
|
|
8639
|
+
maxDistance: 18
|
|
8640
|
+
}
|
|
8641
|
+
} : {}),
|
|
8642
|
+
...(authoredDof ? {
|
|
8643
|
+
depthOfField: {
|
|
8644
|
+
focusDepth: clampNumber(authoredDof.focus ?? 0.02, 0, 1),
|
|
8645
|
+
focusRange: clampNumber(0.02 + clampNumber(authoredDof.aperture ?? 0.35, 0, 1) * 0.3, 0.001, 1),
|
|
8646
|
+
maxRadius: Math.max(0, Math.min(8, Math.round(authoredDof.maxBlur ?? 4)))
|
|
8647
|
+
}
|
|
8648
|
+
} : {}),
|
|
8649
|
+
...(volumetricPass ? { volumetricLight: volumetricPass } : {})
|
|
7941
8650
|
};
|
|
7942
8651
|
}
|
|
7943
8652
|
function createProductionRuntimeShadowOptions(snapshot, collectedLights) {
|
|
@@ -7990,6 +8699,79 @@ function createProductionRuntimeShadowObservation(shadowOptions, diagnostics) {
|
|
|
7990
8699
|
shadowRenderTargetsAllocated: diagnostics.shadowRenderTargetsAllocated ?? 0
|
|
7991
8700
|
};
|
|
7992
8701
|
}
|
|
8702
|
+
/**
|
|
8703
|
+
* G1 SDF text observation (muse3jsparity-PRD): per-resource fail-closed gate
|
|
8704
|
+
* (`describeSdfTextPixelBacking`) ANDed across mounted SDF resources. Layout
|
|
8705
|
+
* alone never backs pixels; lastOpacity is the live LOD x occlusion proof.
|
|
8706
|
+
*/
|
|
8707
|
+
function createProductionTextObservation(entries) {
|
|
8708
|
+
const sdf = entries.flatMap((entry) => entry.resources.filter((resource) => resource.sdfText !== null));
|
|
8709
|
+
if (sdf.length === 0) {
|
|
8710
|
+
return {
|
|
8711
|
+
sdfTexts: 0,
|
|
8712
|
+
textPixelBacked: false,
|
|
8713
|
+
quadCount: 0,
|
|
8714
|
+
lastOpacity: 1,
|
|
8715
|
+
reason: "no SDF text nodes mounted"
|
|
8716
|
+
};
|
|
8717
|
+
}
|
|
8718
|
+
const gates = sdf.map((resource) => describeSdfTextPixelBacking({
|
|
8719
|
+
atlasUploaded: resource.textureStatus === "textured" && resource.texturedMaterial !== null,
|
|
8720
|
+
quadsSubmitted: resource.sdfText.lastSubmitted,
|
|
8721
|
+
quadCount: resource.sdfText.quadCount
|
|
8722
|
+
}));
|
|
8723
|
+
const backed = gates.filter((gate) => gate.textPixelBacked).length;
|
|
8724
|
+
const quadCount = sdf.reduce((total, resource) => total + (resource.sdfText?.quadCount ?? 0), 0);
|
|
8725
|
+
const lastOpacity = Math.min(...sdf.map((resource) => resource.sdfText?.lastOpacity ?? 1));
|
|
8726
|
+
return {
|
|
8727
|
+
sdfTexts: sdf.length,
|
|
8728
|
+
textPixelBacked: backed === sdf.length,
|
|
8729
|
+
quadCount,
|
|
8730
|
+
lastOpacity,
|
|
8731
|
+
reason: backed === sdf.length
|
|
8732
|
+
? `${backed}/${sdf.length} SDF quad sets sampled from uploaded label textures (${quadCount} quads)`
|
|
8733
|
+
: `${backed}/${sdf.length} SDF quad sets backed: ${gates.filter((gate) => !gate.textPixelBacked).map((gate) => gate.reason).join("; ")}`
|
|
8734
|
+
};
|
|
8735
|
+
}
|
|
8736
|
+
/**
|
|
8737
|
+
* M2 texture streaming observation (muse3jsparity-PRD): funds the
|
|
8738
|
+
* post-upgrade texture table (SDF label images + C1 upgrades) against the
|
|
8739
|
+
* budget with distance-prioritized residency. Empty table = nothing to fund.
|
|
8740
|
+
*/
|
|
8741
|
+
function createProductionTexturesObservation(entries, cameraEye, budgetBytes) {
|
|
8742
|
+
const budget = normalizeTextureBudgetBytes(budgetBytes);
|
|
8743
|
+
const table = entries.flatMap((entry, entryIndex) => entry.resources
|
|
8744
|
+
.filter((resource) => resource.textureMipBytes.length > 0)
|
|
8745
|
+
.map((resource, resourceIndex) => {
|
|
8746
|
+
const position = entry.node.position ?? [0, 0, 0];
|
|
8747
|
+
return {
|
|
8748
|
+
id: `${entry.node.name ?? `primitive-${entryIndex}`}#${resourceIndex}`,
|
|
8749
|
+
mipBytesCoarseToFine: resource.textureMipBytes,
|
|
8750
|
+
distanceMeters: Math.hypot(cameraEye[0] - position[0], cameraEye[1] - position[1], cameraEye[2] - position[2])
|
|
8751
|
+
};
|
|
8752
|
+
}));
|
|
8753
|
+
if (table.length === 0) {
|
|
8754
|
+
return {
|
|
8755
|
+
budgetBytes: budget,
|
|
8756
|
+
usedBytes: 0,
|
|
8757
|
+
requestedBytes: 0,
|
|
8758
|
+
overBudget: false,
|
|
8759
|
+
overBudgetBytes: 0,
|
|
8760
|
+
residentEntries: 0,
|
|
8761
|
+
evictedEntries: []
|
|
8762
|
+
};
|
|
8763
|
+
}
|
|
8764
|
+
const residency = describeTextureStreamingResidency(table, budget);
|
|
8765
|
+
return {
|
|
8766
|
+
budgetBytes: budget,
|
|
8767
|
+
usedBytes: residency.usedBytes,
|
|
8768
|
+
requestedBytes: residency.requestedBytes,
|
|
8769
|
+
overBudget: residency.overBudget,
|
|
8770
|
+
overBudgetBytes: residency.overBudgetBytes,
|
|
8771
|
+
residentEntries: residency.residents.filter((resident) => resident.residentLevels > 0).length,
|
|
8772
|
+
evictedEntries: [...residency.evicted]
|
|
8773
|
+
};
|
|
8774
|
+
}
|
|
7993
8775
|
function createProductionRuntimePostprocessObservation(diagnostics) {
|
|
7994
8776
|
const actualPasses = diagnostics.postprocessPassNames ?? [];
|
|
7995
8777
|
const pixelBacked = actualPasses.length > 0 && diagnostics.lastError === null;
|
|
@@ -8002,7 +8784,8 @@ function createProductionRuntimePostprocessObservation(diagnostics) {
|
|
|
8002
8784
|
pixelBacked,
|
|
8003
8785
|
actualPasses,
|
|
8004
8786
|
fallbackPasses: pixelBacked ? [] : ["direct-render"],
|
|
8005
|
-
targetFormat: diagnostics.postprocessTargetFormat
|
|
8787
|
+
targetFormat: diagnostics.postprocessTargetFormat,
|
|
8788
|
+
executionMode: diagnostics.postprocessPlan?.executionMode ?? "unknown"
|
|
8006
8789
|
};
|
|
8007
8790
|
}
|
|
8008
8791
|
function createProductionRuntimeCollectedLights(snapshot) {
|
|
@@ -8010,16 +8793,26 @@ function createProductionRuntimeCollectedLights(snapshot) {
|
|
|
8010
8793
|
const descriptors = authoredLights.flatMap((node, index) => createProductionRuntimeLightDescriptors(node, index));
|
|
8011
8794
|
if (descriptors.length === 0)
|
|
8012
8795
|
return createProductionRuntimeFallbackLights();
|
|
8013
|
-
const shadowCasterIndex = descriptors
|
|
8796
|
+
const shadowCasterIndex = resolveProductionShadowCasterIndex(descriptors);
|
|
8797
|
+
return descriptors.map((descriptor, index) => createProductionRuntimeCollectedLight(descriptor, index === shadowCasterIndex));
|
|
8798
|
+
}
|
|
8799
|
+
/**
|
|
8800
|
+
* N1 caster selection (pure, unit-tested): an explicit `shadow: true`
|
|
8801
|
+
* request wins the single caster slot over unrequested lights; ties and
|
|
8802
|
+
* all-unrequested scenes keep the legacy priority-then-intensity order.
|
|
8803
|
+
*/
|
|
8804
|
+
export function resolveProductionShadowCasterIndex(descriptors) {
|
|
8805
|
+
return descriptors.reduce((selected, descriptor, index) => {
|
|
8014
8806
|
if (selected < 0)
|
|
8015
8807
|
return index;
|
|
8016
8808
|
const current = descriptors[selected];
|
|
8017
|
-
|
|
8018
|
-
|
|
8809
|
+
const priority = descriptor.shadowPriority + (descriptor.shadowRequested ? 10 : 0);
|
|
8810
|
+
const currentPriority = current.shadowPriority + (current.shadowRequested ? 10 : 0);
|
|
8811
|
+
if (priority !== currentPriority) {
|
|
8812
|
+
return priority > currentPriority ? index : selected;
|
|
8019
8813
|
}
|
|
8020
8814
|
return descriptor.intensity > current.intensity ? index : selected;
|
|
8021
8815
|
}, -1);
|
|
8022
|
-
return descriptors.map((descriptor, index) => createProductionRuntimeCollectedLight(descriptor, index === shadowCasterIndex));
|
|
8023
8816
|
}
|
|
8024
8817
|
function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
8025
8818
|
const name = node.name?.trim() || `aura-authored-${node.light}-${authoredIndex + 1}`;
|
|
@@ -8044,6 +8837,7 @@ function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
|
8044
8837
|
spotAngle: 0,
|
|
8045
8838
|
penumbra: 0,
|
|
8046
8839
|
shadowPriority: 3,
|
|
8840
|
+
shadowRequested: false,
|
|
8047
8841
|
authoredLight: node.light
|
|
8048
8842
|
}];
|
|
8049
8843
|
}
|
|
@@ -8059,12 +8853,33 @@ function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
|
8059
8853
|
spotAngle: 0,
|
|
8060
8854
|
penumbra: 0,
|
|
8061
8855
|
shadowPriority: 1,
|
|
8856
|
+
shadowRequested: false,
|
|
8062
8857
|
authoredLight: node.light
|
|
8063
8858
|
}];
|
|
8064
8859
|
}
|
|
8065
8860
|
if (node.light === "studio") {
|
|
8066
8861
|
return createProductionRuntimeStudioLightDescriptors(node, name, color, intensity, position);
|
|
8067
8862
|
}
|
|
8863
|
+
if (node.light === "spot") {
|
|
8864
|
+
// N1 (muse3jsparity-PRD): authored spot with an explicit cone. The aim
|
|
8865
|
+
// target wins over lookAt; the direction feeds the B1 spot shadow path
|
|
8866
|
+
// and the A5 volumetric dominant-light selection.
|
|
8867
|
+
const spotTarget = node.target ?? node.lookAt ?? [0, 0.75, 0];
|
|
8868
|
+
return [{
|
|
8869
|
+
kind: "spot",
|
|
8870
|
+
name,
|
|
8871
|
+
color,
|
|
8872
|
+
intensity,
|
|
8873
|
+
position,
|
|
8874
|
+
direction: normalize3([spotTarget[0] - position[0], spotTarget[1] - position[1], spotTarget[2] - position[2]]),
|
|
8875
|
+
range: Math.max(1, node.distance ?? 12),
|
|
8876
|
+
spotAngle: clampNumber(node.angle ?? Math.PI / 6, 0.08, Math.PI / 2 - 0.01),
|
|
8877
|
+
penumbra: clampNumber(node.penumbra ?? 0.4, 0, 1),
|
|
8878
|
+
shadowPriority: 2,
|
|
8879
|
+
shadowRequested: node.shadow ?? false,
|
|
8880
|
+
authoredLight: node.light
|
|
8881
|
+
}];
|
|
8882
|
+
}
|
|
8068
8883
|
const width = positiveFinite(node.width, node.light === "softbox" ? 2.4 : 2.2)
|
|
8069
8884
|
* productionRuntimeLightAxisScale(node.scale, 0);
|
|
8070
8885
|
const height = positiveFinite(node.height, node.light === "softbox" ? 1.6 : 1.2)
|
|
@@ -8083,6 +8898,7 @@ function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
|
8083
8898
|
spotAngle: clampNumber(Math.atan2(halfDiagonal, targetDistance), 0.08, Math.PI / 2 - 0.01),
|
|
8084
8899
|
penumbra: node.light === "softbox" ? 0.78 : 0.42,
|
|
8085
8900
|
shadowPriority: 2,
|
|
8901
|
+
shadowRequested: false,
|
|
8086
8902
|
authoredLight: node.light,
|
|
8087
8903
|
authoredWidth: width,
|
|
8088
8904
|
authoredHeight: height
|
|
@@ -8112,6 +8928,7 @@ function createProductionRuntimeStudioLightDescriptors(node, name, color, intens
|
|
|
8112
8928
|
spotAngle: 0,
|
|
8113
8929
|
penumbra: 0,
|
|
8114
8930
|
shadowPriority: 3,
|
|
8931
|
+
shadowRequested: false,
|
|
8115
8932
|
authoredLight: node.light
|
|
8116
8933
|
},
|
|
8117
8934
|
{
|
|
@@ -8125,6 +8942,7 @@ function createProductionRuntimeStudioLightDescriptors(node, name, color, intens
|
|
|
8125
8942
|
spotAngle: 0,
|
|
8126
8943
|
penumbra: 0,
|
|
8127
8944
|
shadowPriority: 0,
|
|
8945
|
+
shadowRequested: false,
|
|
8128
8946
|
authoredLight: node.light
|
|
8129
8947
|
},
|
|
8130
8948
|
{
|
|
@@ -8138,6 +8956,7 @@ function createProductionRuntimeStudioLightDescriptors(node, name, color, intens
|
|
|
8138
8956
|
spotAngle: 0,
|
|
8139
8957
|
penumbra: 0,
|
|
8140
8958
|
shadowPriority: 0,
|
|
8959
|
+
shadowRequested: false,
|
|
8141
8960
|
authoredLight: node.light
|
|
8142
8961
|
}
|
|
8143
8962
|
];
|
|
@@ -8157,6 +8976,7 @@ function createProductionRuntimeFallbackLights() {
|
|
|
8157
8976
|
spotAngle: 0,
|
|
8158
8977
|
penumbra: 0,
|
|
8159
8978
|
shadowPriority: 3,
|
|
8979
|
+
shadowRequested: false,
|
|
8160
8980
|
authoredLight: "fallback"
|
|
8161
8981
|
},
|
|
8162
8982
|
{
|
|
@@ -8170,6 +8990,7 @@ function createProductionRuntimeFallbackLights() {
|
|
|
8170
8990
|
spotAngle: 0,
|
|
8171
8991
|
penumbra: 0,
|
|
8172
8992
|
shadowPriority: 0,
|
|
8993
|
+
shadowRequested: false,
|
|
8173
8994
|
authoredLight: "fallback"
|
|
8174
8995
|
},
|
|
8175
8996
|
{
|
|
@@ -8183,6 +9004,7 @@ function createProductionRuntimeFallbackLights() {
|
|
|
8183
9004
|
spotAngle: 0,
|
|
8184
9005
|
penumbra: 0,
|
|
8185
9006
|
shadowPriority: 0,
|
|
9007
|
+
shadowRequested: false,
|
|
8186
9008
|
authoredLight: "fallback"
|
|
8187
9009
|
}
|
|
8188
9010
|
];
|
|
@@ -8349,53 +9171,142 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
8349
9171
|
});
|
|
8350
9172
|
let latestDeviceDiagnostics = productionRenderer.getDiagnostics();
|
|
8351
9173
|
let latestFeatures = productionRenderer.getFeatures();
|
|
9174
|
+
// M2 streaming distances measure against the live camera eye; refreshed
|
|
9175
|
+
// every render so residency follows the camera instead of mount intent.
|
|
9176
|
+
let latestCameraEye = resolveCameraFrame(snapshot, snapshot.camera, 0, runtimeNodes).eye;
|
|
8352
9177
|
const runtimeWarnings = new Set();
|
|
9178
|
+
// C1 textured upgrade: fire-and-forget after mount. Scalar first frames stay
|
|
9179
|
+
// fast; outcomes land in textureUpgradeWarnings, which render() never clears
|
|
9180
|
+
// (per-frame runtimeWarnings are rebuilt every frame).
|
|
9181
|
+
const textureUpgradeWarnings = new Set();
|
|
9182
|
+
void upgradeProductionPrimitiveTextures(primitiveEntries, (message) => {
|
|
9183
|
+
textureUpgradeWarnings.add(message);
|
|
9184
|
+
}).catch((error) => {
|
|
9185
|
+
textureUpgradeWarnings.add(`textured upgrade pass failed (${error instanceof Error ? error.message : String(error)}); scalar materials retained`);
|
|
9186
|
+
});
|
|
8353
9187
|
const productionEnvironment = createProductionRuntimeEnvironment(snapshot);
|
|
9188
|
+
// B3 HDRI upgrade: fire-and-forget after mount. First frames render the
|
|
9189
|
+
// studio procedural fallback; the HDR chain swaps the lighting object the
|
|
9190
|
+
// render closure reads every frame. Outcomes land in hdriWarnings, which
|
|
9191
|
+
// render() never clears (per-frame runtimeWarnings are rebuilt every frame).
|
|
9192
|
+
let currentEnvironmentLighting = productionEnvironment.lighting;
|
|
9193
|
+
const hdriWarnings = new Set();
|
|
9194
|
+
const hdriState = { status: productionEnvironment.hdriUrl ? "pending" : "none" };
|
|
9195
|
+
let disposeHdriEnvironment = null;
|
|
9196
|
+
if (flattened.some((node) => node.kind === "environment" && node.environment === "hdri" && node.texture?.kind !== "aura-asset-ref")) {
|
|
9197
|
+
hdriWarnings.add("hdri environment has no texture asset ref; studio procedural fallback retained");
|
|
9198
|
+
}
|
|
9199
|
+
if (productionEnvironment.hdriUrl) {
|
|
9200
|
+
const hdriUrl = productionEnvironment.hdriUrl;
|
|
9201
|
+
const hdriReflectionUrl = productionEnvironment.hdriReflectionUrl;
|
|
9202
|
+
const hdriIntensity = productionEnvironment.intensity;
|
|
9203
|
+
const hdriRotation = productionEnvironment.hdriRotation;
|
|
9204
|
+
void upgradeProductionEnvironmentHdri(hdriUrl, hdriIntensity, hdriReflectionUrl, hdriRotation)
|
|
9205
|
+
.then((result) => {
|
|
9206
|
+
disposeHdriEnvironment?.();
|
|
9207
|
+
disposeHdriEnvironment = result.dispose;
|
|
9208
|
+
currentEnvironmentLighting = result.lighting;
|
|
9209
|
+
hdriState.status = "ready";
|
|
9210
|
+
hdriState.maxLinearValue = result.maxLinearValue;
|
|
9211
|
+
hdriState.specularMipCount = result.specularMipCount;
|
|
9212
|
+
hdriState.dualProbe = result.dualProbe;
|
|
9213
|
+
})
|
|
9214
|
+
.catch((error) => {
|
|
9215
|
+
hdriState.status = "fallback";
|
|
9216
|
+
hdriWarnings.add(`HDRI upgrade failed for ${hdriUrl} (${error instanceof Error ? error.message : String(error)}); studio procedural fallback retained`);
|
|
9217
|
+
});
|
|
9218
|
+
}
|
|
8354
9219
|
const productionRuntimeLights = createProductionRuntimeCollectedLights(snapshot);
|
|
8355
9220
|
const authoredLightNodes = flattened.filter((node) => node.kind === "light");
|
|
8356
9221
|
const authoredDirectLightNodes = authoredLightNodes.filter((node) => node.light !== "ambient");
|
|
8357
9222
|
const authoredAmbientLightCount = authoredLightNodes.length - authoredDirectLightNodes.length;
|
|
8358
9223
|
const authoredAreaProxyCount = authoredDirectLightNodes.filter((node) => node.light === "rect" || node.light === "softbox").length;
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
9224
|
+
// N1 + G1 + M2 observations refresh on every diagnostics read from the
|
|
9225
|
+
// live entries/device state — never cached intent.
|
|
9226
|
+
const buildDiagnostics = () => {
|
|
9227
|
+
const shadowObservation = createProductionRuntimeShadowObservation(createProductionRuntimeShadowOptions(snapshot, productionRuntimeLights), latestDeviceDiagnostics);
|
|
9228
|
+
const spotCaster = productionRuntimeLights.find((light) => light.castsShadow);
|
|
9229
|
+
const spotAngle = spotCaster?.kind === "spot" ? spotCaster.spotAngle : undefined;
|
|
9230
|
+
const texturesObservation = createProductionTexturesObservation(primitiveEntries, latestCameraEye, rendererOptions?.textureBudgetBytes ?? normalizeTextureBudgetBytes(undefined));
|
|
9231
|
+
const streamingWarnings = texturesObservation.overBudget
|
|
9232
|
+
? [`texture streaming over budget by ${texturesObservation.overBudgetBytes} bytes `
|
|
9233
|
+
+ `(${texturesObservation.residentEntries} resident, `
|
|
9234
|
+
+ `${texturesObservation.evictedEntries.length} evicted: ${texturesObservation.evictedEntries.join(", ") || "none"}); `
|
|
9235
|
+
+ `raise renderer.textureBudgetBytes above ${texturesObservation.requestedBytes} bytes or move textures closer`]
|
|
9236
|
+
: [];
|
|
9237
|
+
return createRendererDiagnosticReport(snapshot, {
|
|
9238
|
+
mounted: true,
|
|
9239
|
+
backend: "production-runtime",
|
|
9240
|
+
postprocess: createProductionRuntimePostprocessObservation(latestDeviceDiagnostics),
|
|
9241
|
+
shadow: {
|
|
9242
|
+
...shadowObservation,
|
|
9243
|
+
spot: describeProductionSpotShadow({
|
|
9244
|
+
requested: flattened.some((node) => node.kind === "light" && node.light === "spot" && node.shadow === true),
|
|
9245
|
+
casterIsSpot: spotCaster?.kind === "spot",
|
|
9246
|
+
...(spotCaster === undefined ? {} : { casterName: spotCaster.source.name }),
|
|
9247
|
+
...(spotAngle === undefined ? {} : { angle: spotAngle }),
|
|
9248
|
+
...(spotCaster?.kind === "spot" ? { penumbra: spotCaster.penumbra, range: spotCaster.range } : {}),
|
|
9249
|
+
mapRendered: shadowObservation.mapRendered,
|
|
9250
|
+
mapSampled: shadowObservation.mapSampled
|
|
9251
|
+
})
|
|
9252
|
+
},
|
|
9253
|
+
text: createProductionTextObservation(primitiveEntries),
|
|
9254
|
+
textures: texturesObservation,
|
|
9255
|
+
environment: {
|
|
9256
|
+
enabled: true,
|
|
9257
|
+
preset: productionEnvironment.preset,
|
|
9258
|
+
intensity: productionEnvironment.intensity,
|
|
9259
|
+
evidence: hdriState.status === "ready"
|
|
9260
|
+
? `${productionEnvironment.evidence} — HDRI chain ready${hdriState.dualProbe ? " (dual-probe: illumination diffuse + reflection specular)" : " (single probe)"} (maxLinear ${hdriState.maxLinearValue}, ${hdriState.specularMipCount} specular mips)`
|
|
9261
|
+
: productionEnvironment.evidence,
|
|
9262
|
+
// B3 iblPixelBacked: true only after the HDR chain swaps the live
|
|
9263
|
+
// lighting object. Procedural first frames and fallbacks never claim it.
|
|
9264
|
+
iblPixelBacked: hdriState.status === "ready",
|
|
9265
|
+
hdriStatus: hdriState.status,
|
|
9266
|
+
...(hdriState.dualProbe === undefined ? {} : { dualProbe: hdriState.dualProbe }),
|
|
9267
|
+
...(productionEnvironment.hdriRotation === undefined ? {} : { hdriRotation: productionEnvironment.hdriRotation })
|
|
9268
|
+
},
|
|
9269
|
+
warnings: [
|
|
9270
|
+
`Production runtime bridge active with ${actorEntries.length} typed GLB actor${actorEntries.length === 1 ? "" : "s"} and ${primitiveEntries.length} Aura primitive${primitiveEntries.length === 1 ? "" : "s"} on ${productionRenderer.backend}.`,
|
|
9271
|
+
...(authoredDirectLightNodes.length === 0
|
|
9272
|
+
? ["Production runtime direct-light fallback active: the scene has no authored directional, point, studio, rect, or softbox light."]
|
|
9273
|
+
: [`Production runtime derived ${productionRuntimeLights.length} collected direct light${productionRuntimeLights.length === 1 ? "" : "s"} from ${authoredDirectLightNodes.length} authored scene light${authoredDirectLightNodes.length === 1 ? "" : "s"}.`]),
|
|
9274
|
+
...(authoredAmbientLightCount > 0
|
|
9275
|
+
? [`${authoredAmbientLightCount} authored ambient light${authoredAmbientLightCount === 1 ? "" : "s"} remain environment-lighting intent and are not mislabeled as direct CollectedLight entries.`]
|
|
9276
|
+
: []),
|
|
9277
|
+
...(authoredAreaProxyCount > 0
|
|
9278
|
+
? [`${authoredAreaProxyCount} authored rect/softbox light${authoredAreaProxyCount === 1 ? "" : "s"} use bounded spot-light proxies whose cone and range derive from authored width and height; this does not claim physical area-light shading.`]
|
|
9279
|
+
: []),
|
|
9280
|
+
...(flattened.some((node) => node.kind === "effect")
|
|
9281
|
+
? ["Effect nodes are requested in the scene graph; production bridge diagnostics report them, but unsupported postprocess/effect passes remain non-pixel-backed until the runtime feature reports support."]
|
|
9282
|
+
: []),
|
|
9283
|
+
...(productionRenderer.backendSelection.fallback ? [`Production runtime backend fallback: ${productionRenderer.backendSelection.reason}`] : []),
|
|
9284
|
+
...latestFeatures
|
|
9285
|
+
.filter((feature) => feature.state !== "supported")
|
|
9286
|
+
.map((feature) => `Production runtime feature ${feature.id} is ${feature.state}: ${feature.detail}`),
|
|
9287
|
+
...runtimeWarnings,
|
|
9288
|
+
...textureUpgradeWarnings,
|
|
9289
|
+
...streamingWarnings,
|
|
9290
|
+
...hdriWarnings
|
|
9291
|
+
],
|
|
9292
|
+
deviceDiagnostics: latestDeviceDiagnostics,
|
|
9293
|
+
lodSelections: primitiveEntries
|
|
9294
|
+
.filter((entry) => Boolean(entry.node.lod?.levels.length))
|
|
9295
|
+
.map((entry) => ({
|
|
9296
|
+
nodeName: entry.node.name ?? "unnamed distance LOD",
|
|
9297
|
+
levelIndex: entry.currentLodIndex,
|
|
9298
|
+
levelName: entry.resources[entry.currentLodIndex]?.name ?? `level-${entry.currentLodIndex}`
|
|
9299
|
+
})),
|
|
9300
|
+
texturedMaterials: primitiveEntries.flatMap((entry) => entry.resources.map((resource) => ({
|
|
9301
|
+
nodeName: entry.node.name ?? `aura-primitive-${entry.node.primitive}`,
|
|
9302
|
+
levelName: resource.name,
|
|
9303
|
+
status: resource.textureStatus,
|
|
9304
|
+
slots: resource.textureSlots,
|
|
9305
|
+
pixelBacked: resource.textureStatus === "textured" && resource.texturedMaterial !== null,
|
|
9306
|
+
warnings: resource.textureWarnings
|
|
9307
|
+
})))
|
|
9308
|
+
}, rendererOptions);
|
|
9309
|
+
};
|
|
8399
9310
|
return {
|
|
8400
9311
|
get backend() {
|
|
8401
9312
|
return productionRenderer.backend;
|
|
@@ -8405,7 +9316,8 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
8405
9316
|
},
|
|
8406
9317
|
render(time) {
|
|
8407
9318
|
runtimeWarnings.clear();
|
|
8408
|
-
|
|
9319
|
+
latestCameraEye = resolveCameraFrame(snapshot, snapshot.camera, time, runtimeNodes).eye;
|
|
9320
|
+
const input = createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, primitiveEntries, time, runtimeNodes, runtimeWarnings, currentEnvironmentLighting, productionRuntimeLights);
|
|
8409
9321
|
const result = productionRenderer.renderInteractiveFrame(input);
|
|
8410
9322
|
latestDeviceDiagnostics = result.diagnostics;
|
|
8411
9323
|
latestFeatures = result.features;
|
|
@@ -8427,13 +9339,15 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
8427
9339
|
return productionRenderer.deviceLost();
|
|
8428
9340
|
},
|
|
8429
9341
|
dispose() {
|
|
9342
|
+
disposeHdriEnvironment?.();
|
|
8430
9343
|
productionRenderer.dispose();
|
|
8431
9344
|
for (const { actor } of actorEntries)
|
|
8432
9345
|
actor.dispose();
|
|
8433
9346
|
for (const { resources } of primitiveEntries) {
|
|
8434
|
-
for (const { geometry, material } of resources) {
|
|
9347
|
+
for (const { geometry, material, texturedMaterial } of resources) {
|
|
8435
9348
|
geometry.dispose();
|
|
8436
9349
|
material.dispose();
|
|
9350
|
+
texturedMaterial?.dispose();
|
|
8437
9351
|
}
|
|
8438
9352
|
}
|
|
8439
9353
|
}
|
|
@@ -8448,21 +9362,88 @@ function createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, pr
|
|
|
8448
9362
|
const currentNode = currentState.node;
|
|
8449
9363
|
if (currentNode.visible === false)
|
|
8450
9364
|
continue;
|
|
9365
|
+
const modelMatrix = [...createModelMatrix(currentNode, productionActorModelBounds(currentNode.asset, entry.actor), shouldNormalizeModelNode(currentNode), time)];
|
|
9366
|
+
// The foot-planting post-pass solves in the same world space this matrix draws into;
|
|
9367
|
+
// refresh its matrix before the clip plays so the solve uses this frame, not the last.
|
|
9368
|
+
applyProductionActorFootPlanting(entry, currentState.animationBinding, modelMatrix, runtimeWarnings);
|
|
8451
9369
|
applyProductionActorAnimation(entry, currentNode, currentState.animationBinding, time, runtimeWarnings);
|
|
8452
9370
|
applyProductionActorMorphTargets(entry, currentState.morphTargets, runtimeWarnings);
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
9371
|
+
// Wrinkle detail (E1 face-rig demo): resolve morph weights through the model's hook.
|
|
9372
|
+
// Absent hook (or empty weights) resolves to 0 = today's rendering exactly.
|
|
9373
|
+
const wrinkleStrength = currentNode.wrinkle
|
|
9374
|
+
? resolveWrinkleMapStrength(currentState.morphTargets ?? {}, currentNode.wrinkle)
|
|
9375
|
+
: undefined;
|
|
9376
|
+
const actorItems = entry.actor.collectRenderItems({
|
|
9377
|
+
modelMatrix,
|
|
9378
|
+
...(wrinkleStrength === undefined ? {} : { wrinkleStrength })
|
|
9379
|
+
});
|
|
9380
|
+
// P2 (muse3jsparity-PRD): model instances attach exactly like primitive
|
|
9381
|
+
// nodes. Skinned actors cannot instance (D1 matrix) — warn once and draw
|
|
9382
|
+
// single instead of silently dropping copies.
|
|
9383
|
+
const modelInstances = currentNode.instances;
|
|
9384
|
+
let modelInstanceAttach = {};
|
|
9385
|
+
if (modelInstances && modelInstances.length > 0) {
|
|
9386
|
+
if (actorItems.some((item) => item.skinning)) {
|
|
9387
|
+
warnOnInstancingFallback({
|
|
9388
|
+
material: currentNode.name ?? currentNode.asset.id,
|
|
9389
|
+
requestedInstances: modelInstances.length,
|
|
9390
|
+
drawnBatches: Math.max(1, actorItems.length),
|
|
9391
|
+
reason: "skinned-palette-overflow-cpu-fallback",
|
|
9392
|
+
onWarning: (message) => runtimeWarnings.add(message)
|
|
9393
|
+
});
|
|
9394
|
+
}
|
|
9395
|
+
else {
|
|
9396
|
+
modelInstanceAttach = {
|
|
9397
|
+
instanceTransforms: createProductionModelInstanceTransforms(modelInstances, currentNode, productionActorModelBounds(currentNode.asset, entry.actor), time),
|
|
9398
|
+
...(currentNode.instanceColors
|
|
9399
|
+
? { instanceColors: createProductionInstanceColors(currentNode.instanceColors, modelInstances.length) }
|
|
9400
|
+
: {})
|
|
9401
|
+
};
|
|
9402
|
+
}
|
|
9403
|
+
}
|
|
9404
|
+
if (currentNode.instancedModelWarning)
|
|
9405
|
+
runtimeWarnings.add(currentNode.instancedModelWarning);
|
|
9406
|
+
items.push(...actorItems.map((item) => ({ ...item, castShadow: currentNode.castShadow, ...modelInstanceAttach })));
|
|
8456
9407
|
attachProductionActorEvidence(currentNode, entry.actor, actorItems, runtimeNodes);
|
|
8457
9408
|
}
|
|
9409
|
+
// G1 SDF occlusion test, built once per frame only when SDF quads exist
|
|
9410
|
+
// (same cost argument as WS-2.7: one bbox per node, negligible beside render).
|
|
9411
|
+
const sdfEntries = primitiveEntries.filter((entry) => entry.resources.some((resource) => resource.sdfText !== null));
|
|
9412
|
+
const sdfOcclusionTest = sdfEntries.length > 0
|
|
9413
|
+
? createSceneLabelOcclusionTest(snapshot, cameraPosition, runtimeNodes)
|
|
9414
|
+
: undefined;
|
|
8458
9415
|
for (const entry of primitiveEntries) {
|
|
8459
9416
|
const currentState = resolveProductionPrimitiveRuntimeState(entry, runtimeNodes);
|
|
8460
9417
|
if (!currentState.visible)
|
|
8461
9418
|
continue;
|
|
8462
9419
|
const resource = selectProductionPrimitiveResource(entry, currentState.node, cameraPosition);
|
|
9420
|
+
if (resource.sdfText && resource.texturedMaterial) {
|
|
9421
|
+
// G1 per-frame SDF opacity: LOD fade from the live camera distance
|
|
9422
|
+
// times the scene occlusion policy, written to the quad material.
|
|
9423
|
+
// Occlusion "hide" skips submission (no pixels, no backing claim).
|
|
9424
|
+
const position = currentState.node.position ?? [0, 0, 0];
|
|
9425
|
+
const distance = Math.hypot(cameraPosition[0] - position[0], cameraPosition[1] - position[1], cameraPosition[2] - position[2]);
|
|
9426
|
+
const frame = resolveSdfTextFrameOpacity({
|
|
9427
|
+
distance,
|
|
9428
|
+
...(resource.sdfText.lodFadeNear === undefined ? {} : { lodFadeNear: resource.sdfText.lodFadeNear }),
|
|
9429
|
+
...(resource.sdfText.lodFadeFar === undefined ? {} : { lodFadeFar: resource.sdfText.lodFadeFar }),
|
|
9430
|
+
occluded: sdfOcclusionTest?.(position) ?? false,
|
|
9431
|
+
occlusionPolicy: resource.sdfText.occlusionPolicy
|
|
9432
|
+
});
|
|
9433
|
+
resource.sdfText.lastOpacity = frame.opacity;
|
|
9434
|
+
resource.sdfText.lastVisible = frame.visible;
|
|
9435
|
+
resource.sdfText.lastSubmitted = frame.visible;
|
|
9436
|
+
if (!frame.visible)
|
|
9437
|
+
continue;
|
|
9438
|
+
const base = resource.texturedMaterial.getParameter("baseColor");
|
|
9439
|
+
const rgb = Array.isArray(base) && base.length >= 3
|
|
9440
|
+
? [base[0] ?? 1, base[1] ?? 1, base[2] ?? 1]
|
|
9441
|
+
: [1, 1, 1];
|
|
9442
|
+
resource.texturedMaterial.setParameter("baseColor", [rgb[0], rgb[1], rgb[2], frame.opacity]);
|
|
9443
|
+
}
|
|
8463
9444
|
items.push({
|
|
8464
9445
|
geometry: resource.geometry,
|
|
8465
|
-
material: resource.material,
|
|
9446
|
+
material: resource.texturedMaterial ?? resource.material,
|
|
8466
9447
|
modelMatrix: createModelMatrix(currentState.node, resource.bounds, false, time),
|
|
8467
9448
|
label: currentState.node.name ?? `aura-primitive-${currentState.node.primitive}`,
|
|
8468
9449
|
castShadow: currentState.node.castShadow,
|
|
@@ -8480,9 +9461,9 @@ function createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, pr
|
|
|
8480
9461
|
// The production runtime owns the pixel-backed HDR target and pass chain for
|
|
8481
9462
|
// routes that request effects. The diagnostics are device-observed, so a
|
|
8482
9463
|
// compositor failure is reported as fallback rather than claimed as a pass.
|
|
8483
|
-
postprocess: createProductionRuntimePostprocess(snapshot),
|
|
9464
|
+
postprocess: createProductionRuntimePostprocess(snapshot, collectedLights, canvas.width, canvas.height),
|
|
8484
9465
|
shadow: createProductionRuntimeShadowOptions(snapshot, collectedLights),
|
|
8485
|
-
environmentFog: createProductionRuntimeEnvironmentFog(snapshot),
|
|
9466
|
+
environmentFog: createProductionRuntimeEnvironmentFog(snapshot, collectedLights, canvas.width, canvas.height),
|
|
8486
9467
|
cameraPosition
|
|
8487
9468
|
};
|
|
8488
9469
|
const cameraLike = { viewProjectionMatrix };
|
|
@@ -8497,10 +9478,505 @@ function createProductionRuntimePrimitiveEntries(nodes) {
|
|
|
8497
9478
|
.filter((node) => node.kind === "primitive")
|
|
8498
9479
|
.map((node) => ({ node, resources: createProductionPrimitiveResources(node), currentLodIndex: 0 }));
|
|
8499
9480
|
}
|
|
9481
|
+
function blankProductionPrimitiveTextureState() {
|
|
9482
|
+
return { texturedMaterial: null, textureStatus: "none", textureSlots: [], textureWarnings: [], sdfText: null, textureBytes: 0, textureMipBytes: [] };
|
|
9483
|
+
}
|
|
9484
|
+
/**
|
|
9485
|
+
* C1 texture intent classification (pure, unit-tested). Asset refs resolve to
|
|
9486
|
+
* fetchable urls; procedural inputs have no rasterizer and are reported so
|
|
9487
|
+
* the caller can warn instead of silently dropping them.
|
|
9488
|
+
*/
|
|
9489
|
+
export function createProductionPrimitiveTextureIntent(materialSpec) {
|
|
9490
|
+
const refUrl = (input) => input && typeof input === "object" && input.kind === "aura-asset-ref"
|
|
9491
|
+
? input.url ?? undefined
|
|
9492
|
+
: undefined;
|
|
9493
|
+
const procedurals = [];
|
|
9494
|
+
const noteProcedural = (slot, input) => {
|
|
9495
|
+
if (input && typeof input === "object" && input.kind === "aura-procedural-texture") {
|
|
9496
|
+
procedurals.push(`${slot}:${input.texture}`);
|
|
9497
|
+
}
|
|
9498
|
+
};
|
|
9499
|
+
noteProcedural("normal", materialSpec?.normal);
|
|
9500
|
+
noteProcedural("roughnessMap", materialSpec?.roughnessMap);
|
|
9501
|
+
noteProcedural("metalnessMap", materialSpec?.metalnessMap);
|
|
9502
|
+
noteProcedural("occlusionMap", materialSpec?.occlusionMap);
|
|
9503
|
+
noteProcedural("emissiveMap", materialSpec?.emissiveMap);
|
|
9504
|
+
const baseColorUrl = refUrl(materialSpec?.texture);
|
|
9505
|
+
const normalUrl = refUrl(materialSpec?.normal);
|
|
9506
|
+
const roughnessUrl = refUrl(materialSpec?.roughnessMap);
|
|
9507
|
+
const metalnessUrl = refUrl(materialSpec?.metalnessMap);
|
|
9508
|
+
const occlusionUrl = refUrl(materialSpec?.occlusionMap);
|
|
9509
|
+
const emissiveUrl = refUrl(materialSpec?.emissiveMap);
|
|
9510
|
+
return {
|
|
9511
|
+
...(baseColorUrl ? { baseColorUrl } : {}),
|
|
9512
|
+
...(normalUrl ? { normalUrl } : {}),
|
|
9513
|
+
...(roughnessUrl ? { roughnessUrl } : {}),
|
|
9514
|
+
...(metalnessUrl ? { metalnessUrl } : {}),
|
|
9515
|
+
...(occlusionUrl ? { occlusionUrl } : {}),
|
|
9516
|
+
...(emissiveUrl ? { emissiveUrl } : {}),
|
|
9517
|
+
proceduralInputs: procedurals
|
|
9518
|
+
};
|
|
9519
|
+
}
|
|
9520
|
+
/**
|
|
9521
|
+
* C1 metallic-roughness compositing (pure, unit-tested). glTF convention:
|
|
9522
|
+
* R = occlusion (unused here, forced to 255), G = roughness, B = metallic.
|
|
9523
|
+
* Missing channels fall back to the scalar spec values.
|
|
9524
|
+
*/
|
|
9525
|
+
async function loadProductionPrimitiveBitmap(url) {
|
|
9526
|
+
if (typeof fetch !== "function" || typeof createImageBitmap !== "function") {
|
|
9527
|
+
throw new Error("texture fetch requires fetch + createImageBitmap (browser production mount)");
|
|
9528
|
+
}
|
|
9529
|
+
const response = await fetch(url);
|
|
9530
|
+
if (!response.ok)
|
|
9531
|
+
throw new Error(`texture fetch failed with status ${response.status} for ${url}`);
|
|
9532
|
+
return await createImageBitmap(await response.blob());
|
|
9533
|
+
}
|
|
9534
|
+
function bitmapRgbaPixels(bitmap) {
|
|
9535
|
+
if (typeof document === "undefined")
|
|
9536
|
+
throw new Error("texture compositing requires a DOM canvas");
|
|
9537
|
+
const canvas = document.createElement("canvas");
|
|
9538
|
+
canvas.width = bitmap.width;
|
|
9539
|
+
canvas.height = bitmap.height;
|
|
9540
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
9541
|
+
if (!context)
|
|
9542
|
+
throw new Error("2d canvas unavailable for texture compositing");
|
|
9543
|
+
context.drawImage(bitmap, 0, 0);
|
|
9544
|
+
const image = context.getImageData(0, 0, bitmap.width, bitmap.height);
|
|
9545
|
+
return { width: bitmap.width, height: bitmap.height, data: new Uint8Array(image.data) };
|
|
9546
|
+
}
|
|
9547
|
+
/**
|
|
9548
|
+
* C1 post-mount textured upgrade (muse3jsparity-PRD). Runs fire-and-forget
|
|
9549
|
+
* after mount: scalar first frames stay fast and honest, and every outcome —
|
|
9550
|
+
* textured, fallback, or skipped — is recorded on the resource with warnings.
|
|
9551
|
+
* Procedural inputs have no rasterizer: recorded + warned, never faked.
|
|
9552
|
+
*/
|
|
9553
|
+
export async function upgradeProductionPrimitiveTextures(entries, warn) {
|
|
9554
|
+
for (const entry of entries) {
|
|
9555
|
+
for (const resource of entry.resources) {
|
|
9556
|
+
const spec = resource.materialSpec;
|
|
9557
|
+
const intent = createProductionPrimitiveTextureIntent(spec);
|
|
9558
|
+
for (const procedural of intent.proceduralInputs) {
|
|
9559
|
+
// Static channel (collectGeneratedCodeWarnings) already warns; record
|
|
9560
|
+
// per-resource without duplicating into runtime warnings.
|
|
9561
|
+
resource.textureWarnings.push(`procedural texture ${procedural} on "${resource.name}" has no rasterizer; recorded only, scalar material retained`);
|
|
9562
|
+
}
|
|
9563
|
+
const urls = [intent.baseColorUrl, intent.normalUrl, intent.roughnessUrl, intent.metalnessUrl, intent.occlusionUrl, intent.emissiveUrl].filter((url) => typeof url === "string" && url.length > 0);
|
|
9564
|
+
if (urls.length === 0)
|
|
9565
|
+
continue;
|
|
9566
|
+
if (resource.textureStatus !== "none")
|
|
9567
|
+
continue;
|
|
9568
|
+
resource.textureStatus = "pending";
|
|
9569
|
+
try {
|
|
9570
|
+
await upgradeProductionPrimitiveResource(resource, resource.sourceNode, spec, intent);
|
|
9571
|
+
}
|
|
9572
|
+
catch (error) {
|
|
9573
|
+
resource.textureStatus = "fallback";
|
|
9574
|
+
const message = `textured upgrade failed for "${resource.name}" (${error instanceof Error ? error.message : String(error)}); scalar material retained`;
|
|
9575
|
+
resource.textureWarnings.push(message);
|
|
9576
|
+
warn(message);
|
|
9577
|
+
}
|
|
9578
|
+
}
|
|
9579
|
+
}
|
|
9580
|
+
}
|
|
9581
|
+
/**
|
|
9582
|
+
* B3 post-mount HDRI upgrade (muse3jsparity-PRD). Fetches a Radiance `.hdr`
|
|
9583
|
+
* asset, runs the HDR→cubemap→GGX-prefilter→BRDF-LUT chain, and returns the
|
|
9584
|
+
* live lighting object plus its disposal. Throws on fetch/parse failure so
|
|
9585
|
+
* the caller keeps the honest procedural fallback and warns.
|
|
9586
|
+
*/
|
|
9587
|
+
export async function upgradeProductionEnvironmentHdri(url, intensity, reflectionUrl, rotation) {
|
|
9588
|
+
if (typeof fetch !== "function") {
|
|
9589
|
+
throw new Error("HDRI upgrade requires fetch (browser production mount)");
|
|
9590
|
+
}
|
|
9591
|
+
const fetchRadiance = async (assetUrl) => {
|
|
9592
|
+
const response = await fetch(assetUrl);
|
|
9593
|
+
if (!response.ok)
|
|
9594
|
+
throw new Error(`HDRI fetch failed with status ${response.status} for ${assetUrl}`);
|
|
9595
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
9596
|
+
if (bytes.length === 0)
|
|
9597
|
+
throw new Error(`HDRI asset is empty for ${assetUrl}`);
|
|
9598
|
+
return bytes;
|
|
9599
|
+
};
|
|
9600
|
+
const clampIntensity = Math.max(0, intensity);
|
|
9601
|
+
const illumination = createProductionPbrHdrPipelineFromRadiance(await fetchRadiance(url), {
|
|
9602
|
+
id: "root-hdri-environment",
|
|
9603
|
+
label: "Root HDRI environment",
|
|
9604
|
+
intensity: clampIntensity,
|
|
9605
|
+
...(rotation !== undefined ? { rotation } : {})
|
|
9606
|
+
});
|
|
9607
|
+
if (!illumination.diagnostics.realRadianceHdr)
|
|
9608
|
+
throw new Error(`HDRI asset did not parse as Radiance HDR for ${url}`);
|
|
9609
|
+
if (!reflectionUrl) {
|
|
9610
|
+
const resources = createProductionEnvironmentLightingResources(illumination);
|
|
9611
|
+
return {
|
|
9612
|
+
lighting: resources.lighting,
|
|
9613
|
+
dispose: resources.dispose,
|
|
9614
|
+
maxLinearValue: illumination.diagnostics.maxLinearValue,
|
|
9615
|
+
specularMipCount: illumination.diagnostics.specularMipCount,
|
|
9616
|
+
dualProbe: false
|
|
9617
|
+
};
|
|
9618
|
+
}
|
|
9619
|
+
const reflection = createProductionPbrHdrPipelineFromRadiance(await fetchRadiance(reflectionUrl), {
|
|
9620
|
+
id: "root-hdri-reflection-environment",
|
|
9621
|
+
label: "Root HDRI reflection environment",
|
|
9622
|
+
intensity: clampIntensity,
|
|
9623
|
+
...(rotation !== undefined ? { rotation } : {})
|
|
9624
|
+
});
|
|
9625
|
+
if (!reflection.diagnostics.realRadianceHdr)
|
|
9626
|
+
throw new Error(`HDRI reflection asset did not parse as Radiance HDR for ${reflectionUrl}`);
|
|
9627
|
+
const dual = createDualProbeEnvironmentLightingResources({ illumination, reflection });
|
|
9628
|
+
return {
|
|
9629
|
+
lighting: dual.lighting,
|
|
9630
|
+
dispose: dual.dispose,
|
|
9631
|
+
maxLinearValue: Math.max(illumination.diagnostics.maxLinearValue, reflection.diagnostics.maxLinearValue),
|
|
9632
|
+
specularMipCount: reflection.diagnostics.specularMipCount,
|
|
9633
|
+
dualProbe: true
|
|
9634
|
+
};
|
|
9635
|
+
}
|
|
9636
|
+
async function upgradeProductionPrimitiveResource(resource, node, spec, intent) {
|
|
9637
|
+
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
|
+
if (!resource.geometry.vertexBuffer.format.hasAttribute("uv")) {
|
|
9642
|
+
throw fail(`"${resource.name}" geometry carries no uv set; textured upgrade needs generated uvs`);
|
|
9643
|
+
}
|
|
9644
|
+
const scalars = resolveProductionPrimitiveScalars(node);
|
|
9645
|
+
const [baseColorSource, normalSource, roughnessSource, metalnessSource, occlusionSource, emissiveSource] = await Promise.all([
|
|
9646
|
+
intent.baseColorUrl ? loadProductionPrimitiveBitmap(intent.baseColorUrl) : Promise.resolve(undefined),
|
|
9647
|
+
intent.normalUrl ? loadProductionPrimitiveBitmap(intent.normalUrl) : Promise.resolve(undefined),
|
|
9648
|
+
intent.roughnessUrl ? loadProductionPrimitiveBitmap(intent.roughnessUrl) : Promise.resolve(undefined),
|
|
9649
|
+
intent.metalnessUrl ? loadProductionPrimitiveBitmap(intent.metalnessUrl) : Promise.resolve(undefined),
|
|
9650
|
+
intent.occlusionUrl ? loadProductionPrimitiveBitmap(intent.occlusionUrl) : Promise.resolve(undefined),
|
|
9651
|
+
intent.emissiveUrl ? loadProductionPrimitiveBitmap(intent.emissiveUrl) : Promise.resolve(undefined)
|
|
9652
|
+
]);
|
|
9653
|
+
const slots = [];
|
|
9654
|
+
const baseColorTexture = baseColorSource
|
|
9655
|
+
? new Texture({ width: baseColorSource.width, height: baseColorSource.height, source: baseColorSource, colorSpace: "srgb", label: `${resource.name}-basecolor` })
|
|
9656
|
+
: undefined;
|
|
9657
|
+
if (baseColorTexture)
|
|
9658
|
+
slots.push("baseColor");
|
|
9659
|
+
const normalTexture = normalSource
|
|
9660
|
+
? new Texture({ width: normalSource.width, height: normalSource.height, source: normalSource, colorSpace: "linear", label: `${resource.name}-normal` })
|
|
9661
|
+
: undefined;
|
|
9662
|
+
if (normalTexture)
|
|
9663
|
+
slots.push("normal");
|
|
9664
|
+
let metallicRoughnessTexture;
|
|
9665
|
+
if (roughnessSource ?? metalnessSource) {
|
|
9666
|
+
const base = roughnessSource ?? metalnessSource;
|
|
9667
|
+
const size = { width: base.width, height: base.height };
|
|
9668
|
+
const readPixels = (bitmap) => {
|
|
9669
|
+
if (!bitmap)
|
|
9670
|
+
return undefined;
|
|
9671
|
+
const decoded = bitmapRgbaPixels(bitmap);
|
|
9672
|
+
if (decoded.width !== size.width || decoded.height !== size.height) {
|
|
9673
|
+
const canvas = document.createElement("canvas");
|
|
9674
|
+
canvas.width = size.width;
|
|
9675
|
+
canvas.height = size.height;
|
|
9676
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
9677
|
+
if (!context)
|
|
9678
|
+
throw fail("2d canvas unavailable for texture compositing");
|
|
9679
|
+
context.drawImage(bitmap, 0, 0, size.width, size.height);
|
|
9680
|
+
return new Uint8Array(context.getImageData(0, 0, size.width, size.height).data);
|
|
9681
|
+
}
|
|
9682
|
+
return decoded.data;
|
|
9683
|
+
};
|
|
9684
|
+
const composited = compositeMetallicRoughnessPixels(readPixels(roughnessSource), readPixels(metalnessSource), size.width * size.height, spec?.roughness ?? 0.58, spec?.metallic ?? spec?.metalness ?? 0);
|
|
9685
|
+
metallicRoughnessTexture = new Texture({ width: size.width, height: size.height, data: composited, colorSpace: "linear", label: `${resource.name}-metallicroughness` });
|
|
9686
|
+
slots.push("metallicRoughness");
|
|
9687
|
+
}
|
|
9688
|
+
const occlusionTexture = occlusionSource
|
|
9689
|
+
? new Texture({ width: occlusionSource.width, height: occlusionSource.height, source: occlusionSource, colorSpace: "linear", label: `${resource.name}-occlusion` })
|
|
9690
|
+
: undefined;
|
|
9691
|
+
if (occlusionTexture)
|
|
9692
|
+
slots.push("occlusion");
|
|
9693
|
+
const emissiveTexture = emissiveSource
|
|
9694
|
+
? new Texture({ width: emissiveSource.width, height: emissiveSource.height, source: emissiveSource, colorSpace: "srgb", label: `${resource.name}-emissive` })
|
|
9695
|
+
: undefined;
|
|
9696
|
+
if (emissiveTexture)
|
|
9697
|
+
slots.push("emissive");
|
|
9698
|
+
if (slots.length === 0)
|
|
9699
|
+
throw fail(`no texture resolved for "${resource.name}"`);
|
|
9700
|
+
// M2 streaming table: resident bytes from the decoded sources (base +
|
|
9701
|
+
// full mip-chain estimate) with the chain keyed off the largest source.
|
|
9702
|
+
const loadedDims = [baseColorSource, normalSource, roughnessSource, metalnessSource, occlusionSource, emissiveSource]
|
|
9703
|
+
.filter((source) => source !== undefined)
|
|
9704
|
+
.map((source) => ({ width: source.width, height: source.height }));
|
|
9705
|
+
const loadedBaseBytes = loadedDims.reduce((total, dims) => total + dims.width * dims.height * 4, 0);
|
|
9706
|
+
const largestDims = loadedDims.reduce((largest, dims) => (dims.width * dims.height > largest.width * largest.height ? dims : largest), { width: 1, height: 1 });
|
|
9707
|
+
resource.textureBytes = Math.round(loadedBaseBytes * (4 / 3));
|
|
9708
|
+
resource.textureMipBytes = mipChainBytesCoarseToFine(largestDims.width, largestDims.height);
|
|
9709
|
+
const texCoords = spec?.texCoords;
|
|
9710
|
+
// C3: every root textured slot shares one capability-gated sampler request
|
|
9711
|
+
// (default 8x where supported; the renderer clamps to the device maximum).
|
|
9712
|
+
const textureSampler = new Sampler({
|
|
9713
|
+
maxAnisotropy: resolveSamplerAnisotropy({ desired: spec?.textureAnisotropy }).applied
|
|
9714
|
+
});
|
|
9715
|
+
resource.texturedMaterial = new TexturedPBRMaterial({
|
|
9716
|
+
name: `a3d-production-textured-primitive-${resource.name}`,
|
|
9717
|
+
baseColor: [scalars.baseColor[0], scalars.baseColor[1], scalars.baseColor[2], scalars.opacity],
|
|
9718
|
+
metallic: clamp01(spec?.metallic ?? spec?.metalness ?? 0),
|
|
9719
|
+
roughness: clamp01(spec?.roughness ?? 0.58),
|
|
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 } : {})
|
|
9764
|
+
}
|
|
9765
|
+
}
|
|
9766
|
+
: {})
|
|
9767
|
+
});
|
|
9768
|
+
resource.textureStatus = "textured";
|
|
9769
|
+
resource.textureSlots = slots;
|
|
9770
|
+
}
|
|
9771
|
+
export function compositeMetallicRoughnessPixels(rough, metal, pixelCount, roughnessScalar, metalnessScalar) {
|
|
9772
|
+
const out = new Uint8Array(pixelCount * 4);
|
|
9773
|
+
const fallbackG = Math.round(clamp01(roughnessScalar) * 255);
|
|
9774
|
+
const fallbackB = Math.round(clamp01(metalnessScalar) * 255);
|
|
9775
|
+
for (let pixel = 0; pixel < pixelCount; pixel += 1) {
|
|
9776
|
+
const base = pixel * 4;
|
|
9777
|
+
out[base] = 255;
|
|
9778
|
+
out[base + 1] = rough ? rough[base + 1] ?? fallbackG : fallbackG;
|
|
9779
|
+
out[base + 2] = metal ? metal[base + 2] ?? fallbackB : fallbackB;
|
|
9780
|
+
out[base + 3] = 255;
|
|
9781
|
+
}
|
|
9782
|
+
return out;
|
|
9783
|
+
}
|
|
9784
|
+
/**
|
|
9785
|
+
* G1 SDF text resource (muse3jsparity-PRD): replays the recorded descriptor
|
|
9786
|
+
* through the atlas sampler at mount, uploads the label image as a native
|
|
9787
|
+
* texture, and submits atlas-derived quads. Returns null (extruded fallback)
|
|
9788
|
+
* with a warning when the sampler cannot run — never a silent mesh swap.
|
|
9789
|
+
*/
|
|
9790
|
+
function createSdfTextPrimitiveResource(node, warn) {
|
|
9791
|
+
const text = node.text3D;
|
|
9792
|
+
if (!text || text.backend !== "sdf")
|
|
9793
|
+
return null;
|
|
9794
|
+
try {
|
|
9795
|
+
const layout = layoutSdfText(text.text, rootSdfFontAtlas(), {
|
|
9796
|
+
size: text.sdfSize ?? 1,
|
|
9797
|
+
...(text.sdfLetterSpacing === undefined ? {} : { letterSpacing: text.sdfLetterSpacing }),
|
|
9798
|
+
...(text.sdfStyle === undefined ? {} : { style: text.sdfStyle })
|
|
9799
|
+
});
|
|
9800
|
+
const style = layout.style;
|
|
9801
|
+
const styled = text.sdfStyle !== undefined && (style.outlineWidthEm > 0 || style.glowRadiusEm > 0 || style.shadowOffsetEm !== undefined);
|
|
9802
|
+
const image = rasterizeSdfTextLabelImage(layout, rootSdfFontAtlas(), {
|
|
9803
|
+
texelsPerWorldUnit: 64,
|
|
9804
|
+
...(styled
|
|
9805
|
+
? {
|
|
9806
|
+
outline: [1, 0.85, 0.6, 1],
|
|
9807
|
+
glow: [0.5, 0.8, 1, 1],
|
|
9808
|
+
shadow: [0, 0, 0, 1]
|
|
9809
|
+
}
|
|
9810
|
+
: {})
|
|
9811
|
+
});
|
|
9812
|
+
const mesh = createSdfTextQuadMesh(layout, image);
|
|
9813
|
+
const vertexCount = mesh.vertexCount;
|
|
9814
|
+
const vertices = new VertexBuffer(VertexFormat.P3N3T4T2T2, vertexCount);
|
|
9815
|
+
for (let index = 0; index < vertexCount; index += 1) {
|
|
9816
|
+
vertices.setAttribute(index, "position", [mesh.positions[index * 3] ?? 0, mesh.positions[index * 3 + 1] ?? 0, 0]);
|
|
9817
|
+
vertices.setAttribute(index, "normal", [0, 0, 1]);
|
|
9818
|
+
vertices.setAttribute(index, "tangent", [1, 0, 0, 1]);
|
|
9819
|
+
vertices.setAttribute(index, "uv", [mesh.uvs[index * 2] ?? 0, mesh.uvs[index * 2 + 1] ?? 0]);
|
|
9820
|
+
vertices.setAttribute(index, "uv1", [mesh.uvs[index * 2] ?? 0, mesh.uvs[index * 2 + 1] ?? 0]);
|
|
9821
|
+
}
|
|
9822
|
+
const geometry = new Geometry(vertices, new IndexBuffer(mesh.indices, vertexCount), "triangles", { min: [mesh.min[0], mesh.min[1], mesh.min[2]], max: [mesh.max[0], mesh.max[1], mesh.max[2]] });
|
|
9823
|
+
const opacity = clamp01(node.material?.opacity ?? 1);
|
|
9824
|
+
const labelTexture = new Texture({
|
|
9825
|
+
width: image.width,
|
|
9826
|
+
height: image.height,
|
|
9827
|
+
data: image.data,
|
|
9828
|
+
colorSpace: "srgb",
|
|
9829
|
+
label: `${node.name ?? "sdf-text"}-sdf-label`
|
|
9830
|
+
});
|
|
9831
|
+
const sampler = new Sampler({ maxAnisotropy: resolveSamplerAnisotropy({ desired: node.material?.textureAnisotropy }).applied });
|
|
9832
|
+
const texturedMaterial = new TexturedPBRMaterial({
|
|
9833
|
+
name: `a3d-production-sdf-text-${node.name ?? "label"}`,
|
|
9834
|
+
baseColor: [1, 1, 1, opacity],
|
|
9835
|
+
metallic: 0,
|
|
9836
|
+
roughness: 0.9,
|
|
9837
|
+
emissiveColor: [0.92, 0.92, 0.92],
|
|
9838
|
+
emissiveStrength: 0.9,
|
|
9839
|
+
occlusionStrength: 0,
|
|
9840
|
+
environmentIntensity: 0,
|
|
9841
|
+
renderState: { blend: true, depthWrite: false, cullMode: "none" },
|
|
9842
|
+
baseColorTexture: labelTexture,
|
|
9843
|
+
baseColorSampler: sampler,
|
|
9844
|
+
emissiveTexture: labelTexture,
|
|
9845
|
+
emissiveSampler: sampler
|
|
9846
|
+
});
|
|
9847
|
+
const mipBytes = mipChainBytesCoarseToFine(image.width, image.height);
|
|
9848
|
+
return {
|
|
9849
|
+
geometry,
|
|
9850
|
+
material: createProductionPrimitiveMaterial(node),
|
|
9851
|
+
bounds: { min: [mesh.min[0], mesh.min[1], mesh.min[2]], max: [mesh.max[0], mesh.max[1], mesh.max[2]] },
|
|
9852
|
+
name: node.name ?? "sdf-text",
|
|
9853
|
+
materialSpec: node.material,
|
|
9854
|
+
sourceNode: node,
|
|
9855
|
+
...blankProductionPrimitiveTextureState(),
|
|
9856
|
+
texturedMaterial,
|
|
9857
|
+
textureStatus: "textured",
|
|
9858
|
+
textureSlots: ["baseColor", "emissive"],
|
|
9859
|
+
textureWarnings: [],
|
|
9860
|
+
sdfText: {
|
|
9861
|
+
quadCount: mesh.quadCount,
|
|
9862
|
+
imageBytes: image.width * image.height * 4,
|
|
9863
|
+
...(style.lodFadeNear === undefined ? {} : { lodFadeNear: style.lodFadeNear }),
|
|
9864
|
+
...(style.lodFadeFar === undefined ? {} : { lodFadeFar: style.lodFadeFar }),
|
|
9865
|
+
occlusionPolicy: (text.sdfOcclusion ?? "dim"),
|
|
9866
|
+
lastOpacity: opacity,
|
|
9867
|
+
lastVisible: true,
|
|
9868
|
+
lastSubmitted: false
|
|
9869
|
+
},
|
|
9870
|
+
textureBytes: Math.round(image.width * image.height * 4 * (4 / 3)),
|
|
9871
|
+
textureMipBytes: mipBytes
|
|
9872
|
+
};
|
|
9873
|
+
}
|
|
9874
|
+
catch (error) {
|
|
9875
|
+
warn(`SDF text sampler failed for "${node.name ?? "sdf-text"}" (${error instanceof Error ? error.message : String(error)}); extruded mesh fallback retained`);
|
|
9876
|
+
return null;
|
|
9877
|
+
}
|
|
9878
|
+
}
|
|
9879
|
+
/**
|
|
9880
|
+
* M2 mip-chain byte estimate (pure, unit-tested): full chain from the base
|
|
9881
|
+
* level, coarse-to-fine, RGBA8. Matches the GPU residency the bridge funds.
|
|
9882
|
+
*/
|
|
9883
|
+
export function mipChainBytesCoarseToFine(width, height) {
|
|
9884
|
+
if (!Number.isInteger(width) || width <= 0 || !Number.isInteger(height) || height <= 0) {
|
|
9885
|
+
throw new Error("Mip-chain bytes require positive integer dimensions.");
|
|
9886
|
+
}
|
|
9887
|
+
const levels = [];
|
|
9888
|
+
let w = width;
|
|
9889
|
+
let h = height;
|
|
9890
|
+
while (true) {
|
|
9891
|
+
levels.unshift(w * h * 4);
|
|
9892
|
+
if (w === 1 && h === 1)
|
|
9893
|
+
break;
|
|
9894
|
+
w = Math.max(1, Math.floor(w / 2));
|
|
9895
|
+
h = Math.max(1, Math.floor(h / 2));
|
|
9896
|
+
}
|
|
9897
|
+
return levels;
|
|
9898
|
+
}
|
|
9899
|
+
/** M2 streaming budget normalization (pure, unit-tested): default 256 MiB, fail-closed. */
|
|
9900
|
+
export function normalizeTextureBudgetBytes(value) {
|
|
9901
|
+
if (value === undefined)
|
|
9902
|
+
return 256 * 1024 * 1024;
|
|
9903
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
9904
|
+
throw new Error("Aura3D textureBudgetBytes must be a positive byte count.");
|
|
9905
|
+
}
|
|
9906
|
+
return Math.floor(value);
|
|
9907
|
+
}
|
|
9908
|
+
/**
|
|
9909
|
+
* M2 streaming residency from the post-upgrade texture table (pure,
|
|
9910
|
+
* unit-tested): distance-prioritized mip funding against the budget with
|
|
9911
|
+
* over-budget telemetry for the unfunded tail.
|
|
9912
|
+
*/
|
|
9913
|
+
export function describeTextureStreamingResidency(table, budgetBytes) {
|
|
9914
|
+
const candidates = table.map((entry) => ({
|
|
9915
|
+
id: entry.id,
|
|
9916
|
+
mipBytesCoarseToFine: entry.mipBytesCoarseToFine,
|
|
9917
|
+
distanceMeters: entry.distanceMeters
|
|
9918
|
+
}));
|
|
9919
|
+
return evaluateDistancePrioritizedMipResidency(candidates, normalizeTextureBudgetBytes(budgetBytes));
|
|
9920
|
+
}
|
|
9921
|
+
/**
|
|
9922
|
+
* N1 spot shadow observation (pure, unit-tested): cone + atlas tier from the
|
|
9923
|
+
* authored spot, pixel-backing gated on the device-observed map signals with
|
|
9924
|
+
* the spot as caster. No signal, no claim.
|
|
9925
|
+
*/
|
|
9926
|
+
export function describeProductionSpotShadow(input) {
|
|
9927
|
+
if (!input.requested || !input.casterIsSpot || input.angle === undefined) {
|
|
9928
|
+
return {
|
|
9929
|
+
requested: input.requested,
|
|
9930
|
+
casterIsSpot: input.casterIsSpot,
|
|
9931
|
+
...(input.casterName === undefined ? {} : { casterName: input.casterName }),
|
|
9932
|
+
spotPixelBacked: false,
|
|
9933
|
+
reason: !input.requested
|
|
9934
|
+
? "no authored spot requested a shadow map"
|
|
9935
|
+
: !input.casterIsSpot
|
|
9936
|
+
? "shadow requested but the caster slot went to a non-spot light"
|
|
9937
|
+
: "spot caster has no cone description"
|
|
9938
|
+
};
|
|
9939
|
+
}
|
|
9940
|
+
// Tier selection validates the cone fail-closed (RangeError on bad angle);
|
|
9941
|
+
// the full projection matrix composes in-device from this cone + range.
|
|
9942
|
+
const tier = selectSpotShadowAtlasTier(input.angle);
|
|
9943
|
+
createSpotShadowProjection(input.angle, Math.max(1, input.range ?? 12));
|
|
9944
|
+
const backed = input.mapRendered && input.mapSampled;
|
|
9945
|
+
return {
|
|
9946
|
+
requested: true,
|
|
9947
|
+
casterIsSpot: true,
|
|
9948
|
+
...(input.casterName === undefined ? {} : { casterName: input.casterName }),
|
|
9949
|
+
angle: input.angle,
|
|
9950
|
+
...(input.penumbra === undefined ? {} : { penumbra: input.penumbra }),
|
|
9951
|
+
range: Math.max(1, input.range ?? 12),
|
|
9952
|
+
atlasResolution: tier.resolution,
|
|
9953
|
+
atlasReason: tier.reason,
|
|
9954
|
+
spotPixelBacked: backed,
|
|
9955
|
+
reason: backed
|
|
9956
|
+
? `spot shadow map rendered and sampled (${tier.resolution}px ${tier.reason})`
|
|
9957
|
+
: "spot caster selected but the device shows no rendered+sampled shadow map yet"
|
|
9958
|
+
};
|
|
9959
|
+
}
|
|
8500
9960
|
function createProductionPrimitiveResources(node) {
|
|
9961
|
+
const sdfWarnings = [];
|
|
9962
|
+
const sdfResource = createSdfTextPrimitiveResource(node, (message) => { sdfWarnings.push(message); });
|
|
8501
9963
|
const levels = node.lod?.levels;
|
|
8502
9964
|
if (!levels?.length) {
|
|
8503
|
-
|
|
9965
|
+
if (sdfResource)
|
|
9966
|
+
return [sdfResource];
|
|
9967
|
+
const fallback = {
|
|
9968
|
+
geometry: createProductionPrimitiveGeometry(node),
|
|
9969
|
+
material: createProductionPrimitiveMaterial(node),
|
|
9970
|
+
bounds: primitiveGeometryBounds(node),
|
|
9971
|
+
name: node.name ?? node.primitive,
|
|
9972
|
+
materialSpec: node.material,
|
|
9973
|
+
sourceNode: node,
|
|
9974
|
+
...blankProductionPrimitiveTextureState()
|
|
9975
|
+
};
|
|
9976
|
+
// Sampler failures stay visible on the fallback resource (surfaced via
|
|
9977
|
+
// texturedMaterials diagnostics), never swallowed by the mesh swap.
|
|
9978
|
+
fallback.textureWarnings.push(...sdfWarnings);
|
|
9979
|
+
return [fallback];
|
|
8504
9980
|
}
|
|
8505
9981
|
return levels.map((level, index) => {
|
|
8506
9982
|
if (!level.primitive && !level.geometry)
|
|
@@ -8512,7 +9988,21 @@ function createProductionPrimitiveResources(node) {
|
|
|
8512
9988
|
material: level.material ?? node.material,
|
|
8513
9989
|
lod: undefined
|
|
8514
9990
|
};
|
|
8515
|
-
|
|
9991
|
+
const levelWarnings = [];
|
|
9992
|
+
const levelSdf = createSdfTextPrimitiveResource(levelNode, (message) => { levelWarnings.push(message); });
|
|
9993
|
+
if (levelSdf)
|
|
9994
|
+
return { ...levelSdf, name: level.name };
|
|
9995
|
+
const levelFallback = {
|
|
9996
|
+
geometry: createProductionPrimitiveGeometry(levelNode),
|
|
9997
|
+
material: createProductionPrimitiveMaterial(levelNode),
|
|
9998
|
+
bounds: primitiveGeometryBounds(levelNode),
|
|
9999
|
+
name: level.name,
|
|
10000
|
+
materialSpec: levelNode.material,
|
|
10001
|
+
sourceNode: levelNode,
|
|
10002
|
+
...blankProductionPrimitiveTextureState()
|
|
10003
|
+
};
|
|
10004
|
+
levelFallback.textureWarnings.push(...levelWarnings);
|
|
10005
|
+
return levelFallback;
|
|
8516
10006
|
});
|
|
8517
10007
|
}
|
|
8518
10008
|
function selectProductionPrimitiveResource(entry, node, cameraPosition) {
|
|
@@ -8524,6 +10014,14 @@ function selectProductionPrimitiveResource(entry, node, cameraPosition) {
|
|
|
8524
10014
|
entry.currentLodIndex = selection.levelIndex;
|
|
8525
10015
|
return entry.resources[selection.levelIndex] ?? entry.resources[entry.resources.length - 1];
|
|
8526
10016
|
}
|
|
10017
|
+
function createProductionModelInstanceTransforms(transforms, node, bounds, time) {
|
|
10018
|
+
const matrices = new Float32Array(transforms.length * 16);
|
|
10019
|
+
transforms.forEach((transform, index) => {
|
|
10020
|
+
const localNode = { ...node, ...transform };
|
|
10021
|
+
matrices.set(createModelMatrix(localNode, bounds, shouldNormalizeModelNode(node), time), index * 16);
|
|
10022
|
+
});
|
|
10023
|
+
return matrices;
|
|
10024
|
+
}
|
|
8527
10025
|
function createProductionInstanceTransforms(transforms, node) {
|
|
8528
10026
|
const matrices = new Float32Array(transforms.length * 16);
|
|
8529
10027
|
transforms.forEach((transform, index) => {
|
|
@@ -8559,37 +10057,7 @@ function resolveProductionPrimitiveRuntimeState(entry, runtimeNodes) {
|
|
|
8559
10057
|
visible: runtimeSnapshot.visible
|
|
8560
10058
|
};
|
|
8561
10059
|
}
|
|
8562
|
-
|
|
8563
|
-
* Build the production `PBRMaterial` for a primitive node.
|
|
8564
|
-
*
|
|
8565
|
-
* ## WS-2.1a — the parameter drop this function used to be
|
|
8566
|
-
*
|
|
8567
|
-
* Until 1.6 this forwarded `clearcoat` and **nothing else** from the extended material surface.
|
|
8568
|
-
* `AuraMaterialSpec` accepts `sheen`, `sheenRoughness`, `sheenColor`, `iridescence`,
|
|
8569
|
-
* `iridescenceIOR`, `iridescenceThicknessRange`, `anisotropy`, `anisotropyRotation`, `transmission`,
|
|
8570
|
-
* `thickness`, `ior`, `attenuationColor` and `attenuationDistance`; `PBRMaterial` accepts a uniform
|
|
8571
|
-
* for every one of them and binds them all. The bridge between the two dropped them on the floor.
|
|
8572
|
-
*
|
|
8573
|
-
* How that presented, measured by `tools/material-structural-parity` before the fix:
|
|
8574
|
-
*
|
|
8575
|
-
* ```
|
|
8576
|
-
* material.pbr({ ..., sheen: 1 }) -> byte-identical frame to sheen: 0
|
|
8577
|
-
* material.pbr({ ..., iridescence: 1 }) -> byte-identical frame to iridescence: 0
|
|
8578
|
-
* material.pbr({ ..., clearcoat: 1 }) -> byte-identical frame to clearcoat: 0
|
|
8579
|
-
* ```
|
|
8580
|
-
*
|
|
8581
|
-
* `clearcoat` was *forwarded* and still produced an identical frame, which pins the second, separate
|
|
8582
|
-
* defect: `a3dPbrExtensionEnvironmentLight` in `ShaderChunks.ts` adds its lobes to
|
|
8583
|
-
* `specularRadiance`, a term that is zero without an environment map. Forwarding alone is necessary
|
|
8584
|
-
* and not sufficient, so `environmentIntensity` now also has a floor when an extension factor is
|
|
8585
|
-
* present — otherwise a developer setting `clearcoat: 1` on a scene with no environment sees nothing
|
|
8586
|
-
* and has no way to find out why.
|
|
8587
|
-
*
|
|
8588
|
-
* The reason this survived so long is worth stating: three of the four lobes are scalar
|
|
8589
|
-
* approximations rather than real BRDFs, and **nobody could tell**, because the parameters never
|
|
8590
|
-
* arrived. The plumbing defect concealed the shading defect.
|
|
8591
|
-
*/
|
|
8592
|
-
function createProductionPrimitiveMaterial(node) {
|
|
10060
|
+
function resolveProductionPrimitiveScalars(node) {
|
|
8593
10061
|
const materialSpec = node.material;
|
|
8594
10062
|
const baseColor = colorToLinearRgba(materialSpec?.color ?? materialSpec?.emissive ?? "#d7dee8");
|
|
8595
10063
|
const opacity = clamp01(materialSpec?.opacity ?? baseColor[3] ?? 1);
|
|
@@ -8620,6 +10088,17 @@ function createProductionPrimitiveMaterial(node) {
|
|
|
8620
10088
|
const environmentIntensity = declaresExtension
|
|
8621
10089
|
? Math.max(0.35, requestedEnvironmentIntensity)
|
|
8622
10090
|
: requestedEnvironmentIntensity;
|
|
10091
|
+
// B3: the sampled env-map scale defaults to 1 (no look change) and honors
|
|
10092
|
+
// an explicitly authored envMapIntensity, including 0 (killed response).
|
|
10093
|
+
const envMapIntensity = materialSpec?.envMapIntensity === undefined ? 1 : Math.max(0, materialSpec.envMapIntensity);
|
|
10094
|
+
return {
|
|
10095
|
+
materialSpec, baseColor, opacity, emissiveColor, clearcoat, sheen, iridescence,
|
|
10096
|
+
anisotropy, transmission, thicknessRange, sheenColor, declaresExtension, environmentIntensity, envMapIntensity
|
|
10097
|
+
};
|
|
10098
|
+
}
|
|
10099
|
+
function createProductionPrimitiveMaterial(node) {
|
|
10100
|
+
const materialSpec = node.material;
|
|
10101
|
+
const { baseColor, opacity, emissiveColor, clearcoat, sheenColor, declaresExtension, transmission, thicknessRange, sheen, iridescence, anisotropy, environmentIntensity, envMapIntensity } = resolveProductionPrimitiveScalars(node);
|
|
8623
10102
|
/*
|
|
8624
10103
|
* Native instance submission requires the renderer's instanced shader contract. The ordinary
|
|
8625
10104
|
* PBR shader intentionally has no instance matrix inputs and ForwardPass therefore expands it.
|
|
@@ -8637,6 +10116,7 @@ function createProductionPrimitiveMaterial(node) {
|
|
|
8637
10116
|
emissiveColor,
|
|
8638
10117
|
emissiveStrength: Math.max(0, materialSpec?.emissiveIntensity ?? (materialSpec?.emissive ? 1.35 : 0)),
|
|
8639
10118
|
environmentIntensity,
|
|
10119
|
+
envMapIntensity,
|
|
8640
10120
|
renderState: {
|
|
8641
10121
|
blend: opacity < 0.999,
|
|
8642
10122
|
depthWrite: opacity >= 0.999,
|
|
@@ -8667,6 +10147,7 @@ function createProductionPrimitiveMaterial(node) {
|
|
|
8667
10147
|
...(materialSpec?.attenuationColor ? { volumeAttenuationColor: colorToLinearRgb(materialSpec.attenuationColor) } : {}),
|
|
8668
10148
|
...(materialSpec?.attenuationDistance === undefined ? {} : { volumeAttenuationDistance: Math.max(0, materialSpec.attenuationDistance) }),
|
|
8669
10149
|
environmentIntensity,
|
|
10150
|
+
envMapIntensity,
|
|
8670
10151
|
renderState: {
|
|
8671
10152
|
blend: opacity < 0.999,
|
|
8672
10153
|
depthWrite: opacity >= 0.999,
|
|
@@ -8689,6 +10170,12 @@ function primitiveGeometryBounds(nodeOrPrimitive) {
|
|
|
8689
10170
|
throw new Error("Aura3D custom primitive requires geometry.");
|
|
8690
10171
|
return createProductionPrimitiveMesh(primitive).bounds;
|
|
8691
10172
|
}
|
|
10173
|
+
function tilingSecondUnwrap(uvs) {
|
|
10174
|
+
const uv1s = new Float32Array(uvs.length);
|
|
10175
|
+
for (let i = 0; i < uvs.length; i += 1)
|
|
10176
|
+
uv1s[i] = (uvs[i] ?? 0) * 2;
|
|
10177
|
+
return uv1s;
|
|
10178
|
+
}
|
|
8692
10179
|
function createProductionPrimitiveMesh(primitive) {
|
|
8693
10180
|
if (primitive === "sphere")
|
|
8694
10181
|
return createSphereGeometry();
|
|
@@ -8740,11 +10227,24 @@ function calculateCustomGeometryNormals(spec) {
|
|
|
8740
10227
|
}
|
|
8741
10228
|
function createProductionGeometryFromPrimitiveMesh(mesh) {
|
|
8742
10229
|
const vertexCount = mesh.positions.length / 3;
|
|
8743
|
-
const
|
|
10230
|
+
const textured = Boolean(mesh.uvs && mesh.uvs.length === vertexCount * 2
|
|
10231
|
+
&& mesh.tangents && mesh.tangents.length === vertexCount * 4);
|
|
10232
|
+
const vertices = new VertexBuffer(textured ? VertexFormat.P3N3T4T2T2 : VertexFormat.P3N3, vertexCount);
|
|
8744
10233
|
for (let index = 0; index < vertexCount; index += 1) {
|
|
8745
10234
|
const base = index * 3;
|
|
8746
10235
|
vertices.setAttribute(index, "position", [mesh.positions[base] ?? 0, mesh.positions[base + 1] ?? 0, mesh.positions[base + 2] ?? 0]);
|
|
8747
10236
|
vertices.setAttribute(index, "normal", [mesh.normals[base] ?? 0, mesh.normals[base + 1] ?? 1, mesh.normals[base + 2] ?? 0]);
|
|
10237
|
+
if (textured) {
|
|
10238
|
+
const uvBase = index * 2;
|
|
10239
|
+
const tangentBase = index * 4;
|
|
10240
|
+
vertices.setAttribute(index, "tangent", [
|
|
10241
|
+
mesh.tangents[tangentBase] ?? 1, mesh.tangents[tangentBase + 1] ?? 0,
|
|
10242
|
+
mesh.tangents[tangentBase + 2] ?? 0, mesh.tangents[tangentBase + 3] ?? 1
|
|
10243
|
+
]);
|
|
10244
|
+
vertices.setAttribute(index, "uv", [mesh.uvs[uvBase] ?? 0, mesh.uvs[uvBase + 1] ?? 0]);
|
|
10245
|
+
const uv1 = mesh.uv1s && mesh.uv1s.length === vertexCount * 2 ? mesh.uv1s : mesh.uvs;
|
|
10246
|
+
vertices.setAttribute(index, "uv1", [uv1[uvBase] ?? 0, uv1[uvBase + 1] ?? 0]);
|
|
10247
|
+
}
|
|
8748
10248
|
}
|
|
8749
10249
|
return new Geometry(vertices, new IndexBuffer(Array.from(mesh.indices), vertexCount), "triangles", mesh.bounds);
|
|
8750
10250
|
}
|
|
@@ -8785,6 +10285,39 @@ function applyProductionActorAnimation(entry, node, animationBinding, time, runt
|
|
|
8785
10285
|
runtimeWarnings.add(`Typed GLB actor "${entry.actor.id}" failed to apply clip "${clipName}": ${productionRenderErrorMessage(error)}`);
|
|
8786
10286
|
}
|
|
8787
10287
|
}
|
|
10288
|
+
/**
|
|
10289
|
+
* Applies controller-bound foot planting (E2) to the typed GLB actor. The resolved config
|
|
10290
|
+
* is refreshed every frame with the actor's live model matrix: time-animated model nodes
|
|
10291
|
+
* (float/orbit/turntable) move, and the post-pass solves in world space, so a stale matrix
|
|
10292
|
+
* would plant feet in yesterday's frame. Foot-lock state survives the refresh — the runtime
|
|
10293
|
+
* keeps its rig across matrix-only updates and resets it only when the leg set, ground, or
|
|
10294
|
+
* solve parameters change.
|
|
10295
|
+
*/
|
|
10296
|
+
function applyProductionActorFootPlanting(entry, animationBinding, modelMatrix, runtimeWarnings) {
|
|
10297
|
+
const footPlanting = animationBinding?.footPlanting;
|
|
10298
|
+
try {
|
|
10299
|
+
entry.actor.animation.setFootPlanting((footPlanting === undefined
|
|
10300
|
+
? undefined
|
|
10301
|
+
: { ...footPlanting, ...spreadFootPlantingWorldMatrix(modelMatrix) }));
|
|
10302
|
+
}
|
|
10303
|
+
catch (error) {
|
|
10304
|
+
runtimeWarnings.add(`Typed GLB actor "${entry.actor.id}" failed to apply foot planting: ${productionRenderErrorMessage(error)}`);
|
|
10305
|
+
}
|
|
10306
|
+
}
|
|
10307
|
+
/**
|
|
10308
|
+
* Packages the actor model matrix as the post-pass `worldFromLocal`. The matrix the
|
|
10309
|
+
* renderer draws with (column-major 16, model-local → world) is exactly the space the
|
|
10310
|
+
* foot-planting ground speaks, so the same array threads both paths. Returns {} when the
|
|
10311
|
+
* matrix is missing or malformed so the pass falls back to actor-local solving.
|
|
10312
|
+
*/
|
|
10313
|
+
function spreadFootPlantingWorldMatrix(modelMatrix) {
|
|
10314
|
+
if (!modelMatrix || modelMatrix.length !== 16)
|
|
10315
|
+
return {};
|
|
10316
|
+
const worldFromLocal = Array.from(modelMatrix, (component) => Number(component));
|
|
10317
|
+
if (worldFromLocal.some((component) => !Number.isFinite(component)))
|
|
10318
|
+
return {};
|
|
10319
|
+
return { worldFromLocal };
|
|
10320
|
+
}
|
|
8788
10321
|
function applyProductionActorMorphTargets(entry, morphTargets, runtimeWarnings) {
|
|
8789
10322
|
if (!morphTargets || Object.keys(morphTargets).length === 0)
|
|
8790
10323
|
return;
|
|
@@ -8832,7 +10365,14 @@ function createRuntimeEvidenceFromTypedGLBActor(node, actor, renderItems) {
|
|
|
8832
10365
|
},
|
|
8833
10366
|
renderItemCount: renderItems.length,
|
|
8834
10367
|
skinnedRenderItemCount: renderItems.filter((item) => item.skinning).length,
|
|
8835
|
-
morphRenderItemCount: renderItems.filter((item) => item.morphTargets && item.morphTargets.length > 0).length
|
|
10368
|
+
morphRenderItemCount: renderItems.filter((item) => item.morphTargets && item.morphTargets.length > 0).length,
|
|
10369
|
+
lastMaterialTracksApplied: actorEvidence.lastMaterialTracksApplied,
|
|
10370
|
+
lastLightTracksApplied: actorEvidence.lastLightTracksApplied,
|
|
10371
|
+
lastFootPlantingGroundedFeet: actorEvidence.lastFootPlantingGroundedFeet,
|
|
10372
|
+
lastFootPlantingTargetError: actorEvidence.lastFootPlantingTargetError,
|
|
10373
|
+
lastFootPlantingHipOffset: actorEvidence.lastFootPlantingHipOffset,
|
|
10374
|
+
lastFootPlantingMissingLegs: [...actorEvidence.lastFootPlantingMissingLegs],
|
|
10375
|
+
footPlantingConfigured: actorEvidence.footPlantingConfigured
|
|
8836
10376
|
});
|
|
8837
10377
|
}
|
|
8838
10378
|
function createProductionRuntimeMetadata(actorEntries, primitiveEntries) {
|
|
@@ -9277,7 +10817,9 @@ function collectRuntimeEffectNodes(snapshot) {
|
|
|
9277
10817
|
return groups.flatten(snapshot.nodes).filter((node) => node.kind === "effect");
|
|
9278
10818
|
}
|
|
9279
10819
|
function hasRuntimePostProcessEffects(effectNodes) {
|
|
9280
|
-
return effectNodes.some((node) => node.effect === "bloom" || node.effect === "ambient-occlusion" || node.effect === "contact-occlusion"
|
|
10820
|
+
return effectNodes.some((node) => node.effect === "bloom" || node.effect === "ambient-occlusion" || node.effect === "contact-occlusion"
|
|
10821
|
+
|| node.effect === "color-grade" || node.effect === "anti-alias" || node.effect === "outline"
|
|
10822
|
+
|| node.effect === "screen-space-reflections" || node.effect === "depth-of-field" || node.effect === "motion-blur");
|
|
9281
10823
|
}
|
|
9282
10824
|
async function createWebGLSceneRenderer(canvas, snapshot, rendererOptions, runtimeWarnings = [], runtimeNodes) {
|
|
9283
10825
|
const gl = canvas.getContext("webgl2", { antialias: true, preserveDrawingBuffer: true });
|
|
@@ -9313,7 +10855,8 @@ async function createWebGLSceneRenderer(canvas, snapshot, rendererOptions, runti
|
|
|
9313
10855
|
contactOcclusionReceiver: false,
|
|
9314
10856
|
pixelBacked: false,
|
|
9315
10857
|
actualPasses: [],
|
|
9316
|
-
fallbackPasses: hasRuntimePostProcessEffects(requestedEffectNodes) ? ["webgl2-direct-render"] : []
|
|
10858
|
+
fallbackPasses: hasRuntimePostProcessEffects(requestedEffectNodes) ? ["webgl2-direct-render"] : [],
|
|
10859
|
+
executionMode: "none"
|
|
9317
10860
|
},
|
|
9318
10861
|
warnings: [
|
|
9319
10862
|
"Aura3D WebGL2 agent runtime is an explicit safe-basic fallback; advanced postprocess, environment prefiltering, shadow maps, and GLB animation mixers are reported as unsupported unless the production runtime proves them.",
|
|
@@ -10549,6 +12092,8 @@ function textureRef(texture, uvs, key) {
|
|
|
10549
12092
|
return { [key]: texture.index };
|
|
10550
12093
|
}
|
|
10551
12094
|
function createPlaneGeometry() {
|
|
12095
|
+
const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
12096
|
+
const tangents = new Float32Array([1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1]);
|
|
10552
12097
|
return {
|
|
10553
12098
|
positions: new Float32Array([
|
|
10554
12099
|
-0.5, 0, -0.5,
|
|
@@ -10569,10 +12114,21 @@ function createPlaneGeometry() {
|
|
|
10569
12114
|
// up-normal downward. The plane then faced away from every overhead light and
|
|
10570
12115
|
// received no direct lighting or visible cast shadows.
|
|
10571
12116
|
indices: new Uint16Array([0, 2, 1, 0, 3, 2]),
|
|
10572
|
-
bounds: { min: [-0.5, 0, -0.5], max: [0.5, 0, 0.5] }
|
|
12117
|
+
bounds: { min: [-0.5, 0, -0.5], max: [0.5, 0, 0.5] },
|
|
12118
|
+
uvs,
|
|
12119
|
+
uv1s: tilingSecondUnwrap(uvs),
|
|
12120
|
+
tangents
|
|
10573
12121
|
};
|
|
10574
12122
|
}
|
|
10575
12123
|
function createBoxGeometry() {
|
|
12124
|
+
// One full 0-1 unwrap per face, matching the 4-vertex face order below.
|
|
12125
|
+
const faceUvs = [0, 0, 1, 0, 1, 1, 0, 1];
|
|
12126
|
+
const uvs = new Float32Array([...faceUvs, ...faceUvs, ...faceUvs, ...faceUvs, ...faceUvs, ...faceUvs]);
|
|
12127
|
+
// Analytic tangents along each face's +u direction (face order +z,-z,+y,-y,+x,-x).
|
|
12128
|
+
const faceTangents = [
|
|
12129
|
+
[1, 0, 0, 1], [-1, 0, 0, 1], [1, 0, 0, 1], [1, 0, 0, 1], [0, 0, -1, 1], [0, 0, 1, 1]
|
|
12130
|
+
];
|
|
12131
|
+
const tangents = new Float32Array(faceTangents.flatMap((tangent) => [...tangent, ...tangent, ...tangent, ...tangent]));
|
|
10576
12132
|
const positions = new Float32Array([
|
|
10577
12133
|
-0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5,
|
|
10578
12134
|
0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5,
|
|
@@ -10600,7 +12156,10 @@ function createBoxGeometry() {
|
|
|
10600
12156
|
16, 17, 18, 16, 18, 19,
|
|
10601
12157
|
20, 21, 22, 20, 22, 23
|
|
10602
12158
|
]),
|
|
10603
|
-
bounds: { min: [-0.5, -0.5, -0.5], max: [0.5, 0.5, 0.5] }
|
|
12159
|
+
bounds: { min: [-0.5, -0.5, -0.5], max: [0.5, 0.5, 0.5] },
|
|
12160
|
+
uvs,
|
|
12161
|
+
uv1s: tilingSecondUnwrap(uvs),
|
|
12162
|
+
tangents
|
|
10604
12163
|
};
|
|
10605
12164
|
}
|
|
10606
12165
|
function createSphereGeometry() {
|
|
@@ -10608,6 +12167,8 @@ function createSphereGeometry() {
|
|
|
10608
12167
|
const columns = 16;
|
|
10609
12168
|
const positions = [];
|
|
10610
12169
|
const normals = [];
|
|
12170
|
+
const uvs = [];
|
|
12171
|
+
const tangents = [];
|
|
10611
12172
|
const indices = [];
|
|
10612
12173
|
for (let row = 0; row <= rows; row += 1) {
|
|
10613
12174
|
const v = row / rows;
|
|
@@ -10620,6 +12181,8 @@ function createSphereGeometry() {
|
|
|
10620
12181
|
const z = Math.sin(theta) * Math.sin(phi);
|
|
10621
12182
|
positions.push(x * 0.5, y * 0.5, z * 0.5);
|
|
10622
12183
|
normals.push(x, y, z);
|
|
12184
|
+
uvs.push(u, 1 - v);
|
|
12185
|
+
tangents.push(-Math.sin(phi), 0, Math.cos(phi), 1);
|
|
10623
12186
|
}
|
|
10624
12187
|
}
|
|
10625
12188
|
for (let row = 0; row < rows; row += 1) {
|
|
@@ -10632,11 +12195,15 @@ function createSphereGeometry() {
|
|
|
10632
12195
|
indices.push(a, a + 1, b, b, a + 1, b + 1);
|
|
10633
12196
|
}
|
|
10634
12197
|
}
|
|
12198
|
+
const sphereUvs = new Float32Array(uvs);
|
|
10635
12199
|
return {
|
|
10636
12200
|
positions: new Float32Array(positions),
|
|
10637
12201
|
normals: new Float32Array(normals),
|
|
10638
12202
|
indices: new Uint16Array(indices),
|
|
10639
|
-
bounds: { min: [-0.5, -0.5, -0.5], max: [0.5, 0.5, 0.5] }
|
|
12203
|
+
bounds: { min: [-0.5, -0.5, -0.5], max: [0.5, 0.5, 0.5] },
|
|
12204
|
+
uvs: sphereUvs,
|
|
12205
|
+
uv1s: tilingSecondUnwrap(sphereUvs),
|
|
12206
|
+
tangents: new Float32Array(tangents)
|
|
10640
12207
|
};
|
|
10641
12208
|
}
|
|
10642
12209
|
function createCylinderGeometry() {
|
|
@@ -11370,7 +12937,9 @@ function snapshotDiagnostics(value) {
|
|
|
11370
12937
|
renderer: value.renderer,
|
|
11371
12938
|
warnings: [...value.warnings],
|
|
11372
12939
|
errors: [...value.errors],
|
|
11373
|
-
...(value.labels ? { labels: value.labels } : {})
|
|
12940
|
+
...(value.labels ? { labels: value.labels } : {}),
|
|
12941
|
+
...(value.labelTelemetry ? { labelTelemetry: value.labelTelemetry } : {}),
|
|
12942
|
+
...(value.textBuckets ? { textBuckets: value.textBuckets } : {})
|
|
11374
12943
|
};
|
|
11375
12944
|
}
|
|
11376
12945
|
function collectGeneratedCodeWarnings(snapshot) {
|
|
@@ -11381,6 +12950,27 @@ function collectGeneratedCodeWarnings(snapshot) {
|
|
|
11381
12950
|
if (!snapshot.nodes.some((node) => node.kind === "interaction")) {
|
|
11382
12951
|
warnings.push("Scene has no interactions. Suggested fix: add interactions.orbit() for product/viewer scenes.");
|
|
11383
12952
|
}
|
|
12953
|
+
// muse3jsparity-PRD A3: withheld postprocess intents must warn on the
|
|
12954
|
+
// mounted channel too (plan-level warnings never reach app.diagnostics()).
|
|
12955
|
+
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
|
+
// muse3jsparity-PRD C1: procedural texture inputs have no rasterizer. The
|
|
12965
|
+
// intent is compile-time known, so warn here; fetch outcomes stay dynamic.
|
|
12966
|
+
for (const node of flatNodes) {
|
|
12967
|
+
if (node.kind !== "primitive" || !node.material)
|
|
12968
|
+
continue;
|
|
12969
|
+
const label = node.name ?? `aura-primitive-${node.primitive}`;
|
|
12970
|
+
for (const procedural of createProductionPrimitiveTextureIntent(node.material).proceduralInputs) {
|
|
12971
|
+
warnings.push(`procedural texture ${procedural} on "${label}" has no rasterizer; recorded only, scalar material retained`);
|
|
12972
|
+
}
|
|
12973
|
+
}
|
|
11384
12974
|
for (const node of snapshot.nodes) {
|
|
11385
12975
|
if (node.kind === "model" && createAssetProvenance(node.asset).source === "unsafe-url") {
|
|
11386
12976
|
warnings.push(`Model uses unsafeModelUrl("${node.asset.url}"). Suggested fix: run assets add and use typed assets.`);
|
|
@@ -11544,6 +13134,16 @@ function drawEffect(context, width, height, node, time) {
|
|
|
11544
13134
|
context.fillStyle = toAlphaColor(node.color ?? "#9fb7d9", node.density ?? 0.12);
|
|
11545
13135
|
context.fillRect(0, height * 0.2, width, height * 0.8);
|
|
11546
13136
|
}
|
|
13137
|
+
if (node.effect === "volumetric-fog") {
|
|
13138
|
+
context.fillStyle = toAlphaColor(node.color ?? "#6f84b9", node.density ?? 0.18);
|
|
13139
|
+
context.fillRect(0, height * 0.2, width, height * 0.8);
|
|
13140
|
+
const anchor = node.lightPosition ?? [0.5, 0.18];
|
|
13141
|
+
const glow = context.createRadialGradient(width * anchor[0], height * anchor[1], 8, width * anchor[0], height * anchor[1], width * 0.3);
|
|
13142
|
+
glow.addColorStop(0, toAlphaColor(node.color ?? "#fff3d6", (node.intensity ?? 0.7) * 0.35));
|
|
13143
|
+
glow.addColorStop(1, "rgba(255,255,255,0)");
|
|
13144
|
+
context.fillStyle = glow;
|
|
13145
|
+
context.fillRect(0, 0, width, height);
|
|
13146
|
+
}
|
|
11547
13147
|
if (node.effect === "bloom") {
|
|
11548
13148
|
const gradient = context.createRadialGradient(width * 0.5, height * 0.45, 20, width * 0.5, height * 0.45, width * 0.46);
|
|
11549
13149
|
gradient.addColorStop(0, toAlphaColor(node.color ?? "#ffffff", (node.intensity ?? 0.35) * 0.3));
|
|
@@ -11596,6 +13196,29 @@ function drawEffect(context, width, height, node, time) {
|
|
|
11596
13196
|
}
|
|
11597
13197
|
}
|
|
11598
13198
|
}
|
|
13199
|
+
if (node.effect === "snow") {
|
|
13200
|
+
const density = Math.max(0.2, Math.min(1.6, node.density ?? node.intensity ?? 0.68));
|
|
13201
|
+
const intensity = Math.max(0.1, Math.min(1.4, node.intensity ?? 0.4));
|
|
13202
|
+
const color = node.color ?? "#e8f1ff";
|
|
13203
|
+
if (node.mist !== false) {
|
|
13204
|
+
context.fillStyle = toAlphaColor(color, Math.min(0.1, 0.03 + intensity * 0.04));
|
|
13205
|
+
context.fillRect(0, height * 0.2, width, height * 0.72);
|
|
13206
|
+
}
|
|
13207
|
+
const drift = (node.wind?.[0] ?? -0.85) * 0.02;
|
|
13208
|
+
for (let layer = 0; layer < 3; layer += 1) {
|
|
13209
|
+
const count = Math.round((layer === 2 ? 30 : 52) * density);
|
|
13210
|
+
const size = layer === 2 ? 3.1 : layer === 1 ? 2.2 : 1.4;
|
|
13211
|
+
context.fillStyle = toAlphaColor(color, Math.min(0.85, 0.34 + intensity * (0.3 - layer * 0.06)));
|
|
13212
|
+
for (let i = 0; i < count; i += 1) {
|
|
13213
|
+
const sway = Math.sin(time * 0.0011 * (node.speed ?? 1) + i * 1.7 + layer) * (6 + layer * 5);
|
|
13214
|
+
const x = (((i * 61 + layer * 197 + time * 0.008 * (node.speed ?? 1) * drift * 60) % width) + width) % width + sway * 0.14;
|
|
13215
|
+
const y = ((i * 97 + layer * 131 + time * 0.028 * (node.speed ?? 1) * (1 + layer * 0.3)) % (height * 0.86)) + height * 0.06;
|
|
13216
|
+
context.beginPath();
|
|
13217
|
+
context.arc(x, y, size, 0, Math.PI * 2);
|
|
13218
|
+
context.fill();
|
|
13219
|
+
}
|
|
13220
|
+
}
|
|
13221
|
+
}
|
|
11599
13222
|
if (node.effect === "particles") {
|
|
11600
13223
|
const isFountain = node.emitter === "fountain";
|
|
11601
13224
|
// Fountain cap matches prefabs.particleFountain's 2400 maximum so rendered counts never undercut the scene JSON.
|