@aura3d/engine 2.0.3 → 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 +37 -46
- 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/asset-core-types.d.ts +19 -0
- package/dist/aura3d-cli/asset-core-types.d.ts.map +1 -1
- package/dist/aura3d-cli/asset-manifest.d.ts.map +1 -1
- package/dist/aura3d-cli/asset-manifest.js +2 -1
- package/dist/aura3d-cli/asset-manifest.js.map +1 -1
- package/dist/aura3d-cli/cli-help.d.ts.map +1 -1
- package/dist/aura3d-cli/cli-help.js +2 -0
- package/dist/aura3d-cli/cli-help.js.map +1 -1
- package/dist/aura3d-cli/cli.js +24 -1
- package/dist/aura3d-cli/cli.js.map +1 -1
- package/dist/aura3d-cli/index.d.ts +5 -1
- package/dist/aura3d-cli/index.d.ts.map +1 -1
- package/dist/aura3d-cli/index.js +5 -1
- package/dist/aura3d-cli/index.js.map +1 -1
- package/dist/aura3d-cli/meshy/admission.d.ts +40 -0
- package/dist/aura3d-cli/meshy/admission.d.ts.map +1 -0
- package/dist/aura3d-cli/meshy/admission.js +126 -0
- package/dist/aura3d-cli/meshy/admission.js.map +1 -0
- package/dist/aura3d-cli/meshy/evidence.d.ts +12 -0
- package/dist/aura3d-cli/meshy/evidence.d.ts.map +1 -0
- package/dist/aura3d-cli/meshy/evidence.js +37 -0
- package/dist/aura3d-cli/meshy/evidence.js.map +1 -0
- package/dist/aura3d-cli/meshy/import.d.ts +25 -0
- package/dist/aura3d-cli/meshy/import.d.ts.map +1 -0
- package/dist/aura3d-cli/meshy/import.js +104 -0
- package/dist/aura3d-cli/meshy/import.js.map +1 -0
- package/dist/aura3d-cli/meshy/index.d.ts +10 -0
- package/dist/aura3d-cli/meshy/index.d.ts.map +1 -0
- package/dist/aura3d-cli/meshy/index.js +6 -0
- package/dist/aura3d-cli/meshy/index.js.map +1 -0
- package/dist/aura3d-cli/meshy/metadata.d.ts +22 -0
- package/dist/aura3d-cli/meshy/metadata.d.ts.map +1 -0
- package/dist/aura3d-cli/meshy/metadata.js +176 -0
- package/dist/aura3d-cli/meshy/metadata.js.map +1 -0
- package/dist/aura3d-cli/meshy/provenance.d.ts +4 -0
- package/dist/aura3d-cli/meshy/provenance.d.ts.map +1 -0
- package/dist/aura3d-cli/meshy/provenance.js +21 -0
- package/dist/aura3d-cli/meshy/provenance.js.map +1 -0
- package/dist/aura3d-cli/meshy/thumbnail.d.ts +19 -0
- package/dist/aura3d-cli/meshy/thumbnail.d.ts.map +1 -0
- package/dist/aura3d-cli/meshy/thumbnail.js +59 -0
- package/dist/aura3d-cli/meshy/thumbnail.js.map +1 -0
- package/dist/aura3d-cli/meshy/validation.d.ts +5 -0
- package/dist/aura3d-cli/meshy/validation.d.ts.map +1 -0
- package/dist/aura3d-cli/meshy/validation.js +64 -0
- package/dist/aura3d-cli/meshy/validation.js.map +1 -0
- 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/create-aura3d/showcase-spec-game-geometry-extractor.d.ts.map +1 -1
- package/dist/create-aura3d/showcase-spec-game-geometry-extractor.js +86 -4
- package/dist/create-aura3d/showcase-spec-game-geometry-extractor.js.map +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/GameGenreKits.d.ts.map +1 -1
- package/dist/engine/agent-api/GameGenreKits.js +26 -3
- package/dist/engine/agent-api/GameGenreKits.js.map +1 -1
- 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 +723 -6
- package/dist/engine/agent-api/index.d.ts.map +1 -1
- package/dist/engine/agent-api/index.js +1805 -137
- 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 +36 -2
- package/dist/engine/production-runtime/TypedGLBActor.d.ts.map +1 -1
- package/dist/engine/production-runtime/TypedGLBActor.js +92 -5
- 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 +67 -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 +8 -5
- 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/SceneOptimization.d.ts +1 -0
- package/dist/rendering/SceneOptimization.d.ts.map +1 -1
- package/dist/rendering/SceneOptimization.js +4 -2
- package/dist/rendering/SceneOptimization.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 +30 -23
- package/templates/cinematic-scene/package.json +1 -1
- package/templates/mini-game/package.json +1 -1
- package/templates/product-viewer/package.json +1 -1
|
@@ -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,10 +50,27 @@ 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";
|
|
67
|
+
/**
|
|
68
|
+
* Deliberate public surface for the shared arcade vehicle core. The racing kit
|
|
69
|
+
* owns the certified circuit path; exporting the bare motion helper lets routes
|
|
70
|
+
* such as showcase-courier-rush prove a different driving personality on their
|
|
71
|
+
* own topology without touching kit internals. Pinned by unit test.
|
|
72
|
+
*/
|
|
73
|
+
export { createGameArcadeVehicle } from "./GameRuntime.js";
|
|
56
74
|
export * from "./PlatformerMotion.js";
|
|
57
75
|
export * from "./CombatFrameData.js";
|
|
58
76
|
export * from "./SceneQueries.js";
|
|
@@ -76,7 +94,16 @@ export * from "./WebCodecsFrameEncoder.js";
|
|
|
76
94
|
*/
|
|
77
95
|
export { GameInspector, createGameInspector } from "./GameInspector.js";
|
|
78
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";
|
|
79
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";
|
|
80
107
|
export * from "./RenderProgressTracker.js";
|
|
81
108
|
export * from "./AudioVisemeAnalyzer.js";
|
|
82
109
|
export * from "./ExternalPhonemeAnalyzer.js";
|
|
@@ -107,6 +134,7 @@ export * from "./EpisodeTemplates.js";
|
|
|
107
134
|
export * from "./AnimationMotionQuality.js";
|
|
108
135
|
export * from "./AnimationRouteProof.js";
|
|
109
136
|
export * from "./AnimationEpisodePackage.js";
|
|
137
|
+
import { evaluateDistancePrioritizedMipResidency } from "../../assets/browser-index.js";
|
|
110
138
|
import { createFrameLoop } from "./FrameLoop.js";
|
|
111
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";
|
|
112
140
|
import { collectGameRuntimeEvidence as collectGameRuntimeEvidenceV105 } from "./GameEvidence.js";
|
|
@@ -146,6 +174,8 @@ export * from "./GameAssetValidation.js";
|
|
|
146
174
|
export * from "./CharacterAssembly.js";
|
|
147
175
|
export * from "./AssetEvidence.js";
|
|
148
176
|
export * from "./AnimationController.js";
|
|
177
|
+
export * from "./AnimationMixerBuilders.js";
|
|
178
|
+
export * from "./AnimationDebugOverlay.js";
|
|
149
179
|
export { gameAssetValidation, quaterniusGameReadyFighterValidationContract, validateQuaterniusGameReadyFighterAsset } from "./GameAssetValidation.js";
|
|
150
180
|
export { createAnimationController } from "./AnimationController.js";
|
|
151
181
|
export * from "./PromptAnimationContract.js";
|
|
@@ -237,7 +267,8 @@ export function model(asset, options = {}) {
|
|
|
237
267
|
targetMaxDimension: options.targetMaxDimension,
|
|
238
268
|
targetLength: options.targetLength,
|
|
239
269
|
physics: options.physics,
|
|
240
|
-
hiddenNodeNames: options.hiddenNodeNames
|
|
270
|
+
hiddenNodeNames: options.hiddenNodeNames,
|
|
271
|
+
wrinkle: options.wrinkle
|
|
241
272
|
});
|
|
242
273
|
}
|
|
243
274
|
export function unsafeModelUrl(url, options = {}) {
|
|
@@ -329,15 +360,103 @@ export const instances = {
|
|
|
329
360
|
plane: (options) => instancedPrimitive("plane", options),
|
|
330
361
|
cylinder: (options) => instancedPrimitive("cylinder", options),
|
|
331
362
|
capsule: (options) => instancedPrimitive("capsule", options),
|
|
332
|
-
torus: (options) => instancedPrimitive("torus", options)
|
|
363
|
+
torus: (options) => instancedPrimitive("torus", options),
|
|
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"
|
|
333
421
|
};
|
|
334
422
|
export const geometry = {
|
|
335
423
|
define: defineAuraCustomGeometry,
|
|
336
424
|
custom: (spec, options = {}) => primitive("custom", { ...options, geometry: defineAuraCustomGeometry(spec) })
|
|
337
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
|
+
}
|
|
338
432
|
export function text3D(text, options = {}) {
|
|
339
433
|
const built = createAuraText3DGeometry(text, options);
|
|
340
|
-
|
|
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
|
+
});
|
|
341
460
|
}
|
|
342
461
|
export function distanceLod(options) {
|
|
343
462
|
if (options.levels.length === 0)
|
|
@@ -389,6 +508,13 @@ function proceduralTexture(texture, options = {}) {
|
|
|
389
508
|
colorB: options.colorB
|
|
390
509
|
};
|
|
391
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
|
+
];
|
|
392
518
|
export const material = {
|
|
393
519
|
pbr: (options = {}) => ({
|
|
394
520
|
color: "#d7dee8",
|
|
@@ -397,7 +523,29 @@ export const material = {
|
|
|
397
523
|
metalness: options.metalness ?? options.metallic ?? 0,
|
|
398
524
|
...options
|
|
399
525
|
}),
|
|
400
|
-
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
|
+
},
|
|
401
549
|
emissive: (options = {}) => ({
|
|
402
550
|
color: options.color ?? "#111827",
|
|
403
551
|
emissive: options.emissive ?? options.color ?? "#38d6ff",
|
|
@@ -691,33 +839,41 @@ const materialCapabilityCatalog = [
|
|
|
691
839
|
{
|
|
692
840
|
id: "base-color-texture",
|
|
693
841
|
label: "Base-color texture",
|
|
694
|
-
rootSafeApi: "
|
|
842
|
+
rootSafeApi: "supported",
|
|
695
843
|
productionRuntime: "supported",
|
|
696
|
-
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).",
|
|
697
845
|
claimRule: "Can describe typed texture metadata and rendered textured assets; do not claim full texture-material parity without controlled root pixels."
|
|
698
846
|
},
|
|
699
847
|
{
|
|
700
848
|
id: "metallic-roughness",
|
|
701
849
|
label: "Metallic/roughness",
|
|
702
|
-
rootSafeApi: "
|
|
850
|
+
rootSafeApi: "supported",
|
|
703
851
|
productionRuntime: "supported",
|
|
704
|
-
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).",
|
|
705
853
|
claimRule: "Do not claim full PBR parity without browser evidence."
|
|
706
854
|
},
|
|
707
855
|
{
|
|
708
856
|
id: "normal-map",
|
|
709
857
|
label: "Normal maps",
|
|
710
|
-
rootSafeApi: "
|
|
858
|
+
rootSafeApi: "supported",
|
|
711
859
|
productionRuntime: "supported",
|
|
712
|
-
evidence: "Root
|
|
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",
|
|
867
|
+
productionRuntime: "supported",
|
|
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).",
|
|
713
869
|
claimRule: "Claim only with material-specific screenshot evidence."
|
|
714
870
|
},
|
|
715
871
|
{
|
|
716
872
|
id: "emissive",
|
|
717
873
|
label: "Emissive",
|
|
718
|
-
rootSafeApi: "
|
|
874
|
+
rootSafeApi: "supported",
|
|
719
875
|
productionRuntime: "supported",
|
|
720
|
-
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.",
|
|
721
877
|
claimRule: "Distinguish emissive material from postprocess bloom."
|
|
722
878
|
},
|
|
723
879
|
{
|
|
@@ -771,9 +927,9 @@ const materialCapabilityCatalog = [
|
|
|
771
927
|
{
|
|
772
928
|
id: "hdr-ibl",
|
|
773
929
|
label: "HDR/IBL",
|
|
774
|
-
rootSafeApi: "
|
|
930
|
+
rootSafeApi: "supported",
|
|
775
931
|
productionRuntime: "supported",
|
|
776
|
-
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).",
|
|
777
933
|
claimRule: "Root HDR/IBL claims require diagnostics and browser pixels."
|
|
778
934
|
},
|
|
779
935
|
{
|
|
@@ -797,7 +953,9 @@ function createMaterialCapabilityDiagnostics(input) {
|
|
|
797
953
|
requested.add("metallic-roughness");
|
|
798
954
|
if (spec.normal)
|
|
799
955
|
requested.add("normal-map");
|
|
800
|
-
if (spec.
|
|
956
|
+
if (spec.occlusionMap || spec.occlusionStrength !== undefined)
|
|
957
|
+
requested.add("occlusion-map");
|
|
958
|
+
if (spec.emissive || spec.emissiveIntensity !== undefined || spec.emissiveMap)
|
|
801
959
|
requested.add("emissive");
|
|
802
960
|
if (spec.opacity !== undefined && spec.opacity < 1)
|
|
803
961
|
requested.add("alpha");
|
|
@@ -821,6 +979,11 @@ function createMaterialCapabilityDiagnostics(input) {
|
|
|
821
979
|
if (partialRequestedFeatures.length > 0) {
|
|
822
980
|
warnings.push(`Partial root material features requested: ${partialRequestedFeatures.join(", ")}. Capture route pixels before claiming production material quality.`);
|
|
823
981
|
}
|
|
982
|
+
for (const spec of specs) {
|
|
983
|
+
for (const physicalWarning of spec.physicalWarnings ?? []) {
|
|
984
|
+
warnings.push(`Physical material extension bounded: ${physicalWarning}`);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
824
987
|
if (unsupportedRequestedFeatures.length > 0) {
|
|
825
988
|
warnings.push(`Metadata/internal material features requested: ${unsupportedRequestedFeatures.join(", ")}. Do not claim rendered support from root createAuraApp alone.`);
|
|
826
989
|
}
|
|
@@ -1040,6 +1203,20 @@ export const lights = {
|
|
|
1040
1203
|
intensity: options.intensity ?? 2,
|
|
1041
1204
|
color: options.color ?? "#ffffff"
|
|
1042
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
|
+
}),
|
|
1043
1220
|
studio: (options = {}) => new AuraNodeBuilder({
|
|
1044
1221
|
kind: "light",
|
|
1045
1222
|
light: "studio",
|
|
@@ -1127,6 +1304,8 @@ export const camera = {
|
|
|
1127
1304
|
position: options.position,
|
|
1128
1305
|
target: options.target ?? [0, 1, 0],
|
|
1129
1306
|
offset: options.offset,
|
|
1307
|
+
targetOffset: options.targetOffset,
|
|
1308
|
+
offsetMode: options.offsetMode,
|
|
1130
1309
|
fov: options.fov ?? 50,
|
|
1131
1310
|
easing: options.easing,
|
|
1132
1311
|
captureTime: options.captureTime,
|
|
@@ -1247,8 +1426,10 @@ export const camera = {
|
|
|
1247
1426
|
solar: () => camera.orbit({ target: [0, 0, 0], distance: 7.2, fov: 46 }),
|
|
1248
1427
|
humanoid: () => camera.perspective({ position: [1.2, 1.12, 3.45], target: [0, 0.78, -0.55], fov: 36 }),
|
|
1249
1428
|
miniGolf: () => camera.follow({ targetNode: "white physics golf ball", distance: 4.2, fov: 48 }),
|
|
1250
|
-
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
|
|
1251
1431
|
};
|
|
1432
|
+
export const gameFeel = { create: gameFeelBuilders.create, hitStopDefaults: gameFeelBuilders.hitStopDefaults };
|
|
1252
1433
|
function resolveFrameAssetRenderScale(bounds, options) {
|
|
1253
1434
|
const size = boundsSize(bounds);
|
|
1254
1435
|
const height = Math.max(0.001, boundsHeight(bounds));
|
|
@@ -1304,7 +1485,10 @@ export const effects = {
|
|
|
1304
1485
|
radius: options.radius ?? 0.38,
|
|
1305
1486
|
threshold: options.threshold ?? 0.7,
|
|
1306
1487
|
antiBlowout,
|
|
1307
|
-
maxIntensity
|
|
1488
|
+
maxIntensity,
|
|
1489
|
+
...(options.quality !== undefined ? { quality: options.quality } : {}),
|
|
1490
|
+
...(options.softKnee !== undefined ? { softKnee: options.softKnee } : {}),
|
|
1491
|
+
...(options.shoulder !== undefined ? { shoulder: options.shoulder } : {})
|
|
1308
1492
|
});
|
|
1309
1493
|
},
|
|
1310
1494
|
cinematicBloom: (options = {}) => effects.bloom({
|
|
@@ -1323,10 +1507,22 @@ export const effects = {
|
|
|
1323
1507
|
antiBlowout: options.antiBlowout ?? true,
|
|
1324
1508
|
maxIntensity: options.maxIntensity ?? 0.92
|
|
1325
1509
|
}),
|
|
1326
|
-
|
|
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",
|
|
1327
1519
|
density: options.density ?? 0.18,
|
|
1328
1520
|
color: options.color ?? "#6f84b9",
|
|
1329
|
-
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 } : {})
|
|
1330
1526
|
}),
|
|
1331
1527
|
depthFog: (options = {}) => effects.fog({
|
|
1332
1528
|
density: options.density ?? 0.14,
|
|
@@ -1351,6 +1547,82 @@ export const effects = {
|
|
|
1351
1547
|
density: options.density ?? 0.7,
|
|
1352
1548
|
color: options.color ?? "#020617"
|
|
1353
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
|
+
}),
|
|
1354
1626
|
rain: (options = {}) => new AuraNodeBuilder({
|
|
1355
1627
|
kind: "effect",
|
|
1356
1628
|
effect: "rain",
|
|
@@ -1363,6 +1635,62 @@ export const effects = {
|
|
|
1363
1635
|
splashes: options.splashes ?? true,
|
|
1364
1636
|
mist: options.mist ?? true
|
|
1365
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
|
+
},
|
|
1366
1694
|
particles: (options = {}) => new AuraNodeBuilder({
|
|
1367
1695
|
kind: "effect",
|
|
1368
1696
|
effect: "particles",
|
|
@@ -1388,6 +1716,161 @@ export const effects = {
|
|
|
1388
1716
|
noise: options.noise ?? 0.22
|
|
1389
1717
|
})
|
|
1390
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
|
+
};
|
|
1391
1874
|
export const interactions = {
|
|
1392
1875
|
orbit: (options = {}) => new AuraNodeBuilder({
|
|
1393
1876
|
kind: "interaction",
|
|
@@ -1638,7 +2121,24 @@ export const environments = {
|
|
|
1638
2121
|
if (materialClass === "product")
|
|
1639
2122
|
return environments.productHero();
|
|
1640
2123
|
return environments.studio();
|
|
1641
|
-
}
|
|
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
|
+
})
|
|
1642
2142
|
};
|
|
1643
2143
|
const rendererColorManagementPreset = {
|
|
1644
2144
|
kind: "aura-renderer-color-management",
|
|
@@ -1764,6 +2264,7 @@ function normalizeCreateAppRendererOptions(options) {
|
|
|
1764
2264
|
mode,
|
|
1765
2265
|
fallback: options?.fallback ?? "safe-basic",
|
|
1766
2266
|
qualityProfile: profile.id,
|
|
2267
|
+
textureBudgetBytes: normalizeTextureBudgetBytes(options?.textureBudgetBytes),
|
|
1767
2268
|
profile
|
|
1768
2269
|
};
|
|
1769
2270
|
}
|
|
@@ -1807,17 +2308,42 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1807
2308
|
const sceneCategory = resolveRendererSceneCategory(snapshot, names);
|
|
1808
2309
|
const exposure = sceneExposurePresets[sceneCategory];
|
|
1809
2310
|
const bloom = flattened.find((node) => node.kind === "effect" && node.effect === "bloom");
|
|
1810
|
-
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"));
|
|
1811
2312
|
const environment = flattened.find((node) => node.kind === "environment");
|
|
1812
2313
|
const contactShadows = names.filter((name) => name.includes("contact shadow") || name.includes("footprint") || name.includes("glow pool")).length;
|
|
1813
2314
|
const ambientOcclusion = flattened.some((node) => node.kind === "effect" && node.effect === "ambient-occlusion");
|
|
1814
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);
|
|
1815
2331
|
const runtimePostprocess = runtime?.postprocess;
|
|
1816
2332
|
const runtimePasses = runtimePostprocess?.actualPasses ?? [];
|
|
1817
|
-
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;
|
|
1818
2336
|
const requestedPasses = runtimePasses.length > 0
|
|
1819
2337
|
? runtimePasses
|
|
1820
|
-
: 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
|
+
});
|
|
1821
2347
|
const runtimeStatus = !postprocessRequested
|
|
1822
2348
|
? "disabled"
|
|
1823
2349
|
: runtime?.mounted
|
|
@@ -1832,6 +2358,28 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1832
2358
|
warnings.push("scene has no contact shadow or contact-occlusion grounding cue");
|
|
1833
2359
|
if (bloom && (bloom.intensity ?? 0) > 0.95 && bloom.antiBlowout !== true)
|
|
1834
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");
|
|
1835
2383
|
if (postprocessRequested && !runtime?.mounted)
|
|
1836
2384
|
warnings.push("renderer diagnostics are a scene plan only; call createAuraApp(...).diagnostics() after the first render for pixel-backed pass status");
|
|
1837
2385
|
if (postprocessRequested && runtime?.mounted && !runtimePostprocess?.pixelBacked)
|
|
@@ -1881,7 +2429,8 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1881
2429
|
intensity: bloom?.intensity ?? 0,
|
|
1882
2430
|
threshold: bloom?.threshold ?? 0,
|
|
1883
2431
|
radius: bloom?.radius ?? 0,
|
|
1884
|
-
antiBlowout: bloom?.antiBlowout ?? true
|
|
2432
|
+
antiBlowout: bloom?.antiBlowout ?? true,
|
|
2433
|
+
...(bloom?.quality !== undefined ? { quality: bloom.quality } : {})
|
|
1885
2434
|
},
|
|
1886
2435
|
shadows: {
|
|
1887
2436
|
// Never a source-authored `true`. When the runtime is mounted this is the
|
|
@@ -1896,7 +2445,8 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1896
2445
|
nativeShadowMapBindings: runtime?.shadow?.nativeShadowMapBindings ?? 0,
|
|
1897
2446
|
shadowRenderTargetsAllocated: runtime?.shadow?.shadowRenderTargetsAllocated ?? 0,
|
|
1898
2447
|
contactShadows,
|
|
1899
|
-
mapType: "pcf-soft"
|
|
2448
|
+
mapType: "pcf-soft",
|
|
2449
|
+
...(runtime?.shadow?.spot === undefined ? {} : { spot: runtime.shadow.spot })
|
|
1900
2450
|
},
|
|
1901
2451
|
occlusion: {
|
|
1902
2452
|
enabled: ambientOcclusion || contactOcclusion,
|
|
@@ -1911,7 +2461,9 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1911
2461
|
fog: {
|
|
1912
2462
|
enabled: Boolean(fog),
|
|
1913
2463
|
density: fog?.density ?? 0,
|
|
1914
|
-
|
|
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"
|
|
1915
2467
|
},
|
|
1916
2468
|
postprocess: {
|
|
1917
2469
|
enabled: runtimePostprocess?.pixelBacked ?? false,
|
|
@@ -1927,6 +2479,7 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1927
2479
|
actualPasses: runtimePostprocess?.actualPasses ?? [],
|
|
1928
2480
|
fallbackPasses: runtimePostprocess?.fallbackPasses ?? [],
|
|
1929
2481
|
targetFormat: runtimePostprocess?.targetFormat,
|
|
2482
|
+
executionMode: runtimePostprocess?.executionMode ?? "unknown",
|
|
1930
2483
|
evidence: postprocessEvidence
|
|
1931
2484
|
},
|
|
1932
2485
|
runtime: {
|
|
@@ -1940,13 +2493,39 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1940
2493
|
visibleObjects: runtime?.deviceDiagnostics?.visibleObjects ?? 0,
|
|
1941
2494
|
culledObjects: runtime?.deviceDiagnostics?.culledObjects ?? 0,
|
|
1942
2495
|
frustumTestedObjects: runtime?.deviceDiagnostics?.frustumTestedObjects ?? 0,
|
|
1943
|
-
|
|
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 ?? []
|
|
1944
2501
|
},
|
|
1945
2502
|
environment: {
|
|
1946
2503
|
enabled: environmentStatus.enabled,
|
|
1947
2504
|
preset: environmentStatus.preset,
|
|
1948
2505
|
intensity: environmentStatus.intensity,
|
|
1949
|
-
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: []
|
|
1950
2529
|
},
|
|
1951
2530
|
antialiasing: rendererQualityPresets.screenshot.antialiasing,
|
|
1952
2531
|
screenshotQuality: rendererQualityPresets.screenshot,
|
|
@@ -1954,14 +2533,31 @@ function createRendererDiagnosticReport(snapshot, runtime, rendererOptions) {
|
|
|
1954
2533
|
warnings
|
|
1955
2534
|
};
|
|
1956
2535
|
}
|
|
1957
|
-
function requestedRendererPostProcessPasses(hasBloom, ambientOcclusion, contactOcclusion) {
|
|
1958
|
-
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)
|
|
1959
2539
|
return [];
|
|
1960
2540
|
const passes = ["render"];
|
|
1961
2541
|
if (ambientOcclusion || contactOcclusion)
|
|
1962
2542
|
passes.push("ssao");
|
|
1963
2543
|
if (hasBloom)
|
|
1964
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)");
|
|
1965
2561
|
passes.push("output");
|
|
1966
2562
|
return passes;
|
|
1967
2563
|
}
|
|
@@ -6374,6 +6970,13 @@ export function createRuntimeNodeImportedAssetEvidence(input) {
|
|
|
6374
6970
|
renderItemCount: input.renderItemCount ?? 0,
|
|
6375
6971
|
skinnedRenderItemCount: input.skinnedRenderItemCount ?? 0,
|
|
6376
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 } : {}),
|
|
6377
6980
|
diagnostics
|
|
6378
6981
|
};
|
|
6379
6982
|
}
|
|
@@ -6739,7 +7342,9 @@ export function createAuraApp(target, options) {
|
|
|
6739
7342
|
appPhysicsWorldInstance ??= new PhysicsWorld({
|
|
6740
7343
|
gravity: options.physics?.gravity ? [...options.physics.gravity] : [0, -9.81, 0],
|
|
6741
7344
|
fixedDelta: 1 / 60,
|
|
6742
|
-
enableSleeping: true
|
|
7345
|
+
enableSleeping: true,
|
|
7346
|
+
...(options.physics?.seed === undefined ? {} : { seed: options.physics.seed }),
|
|
7347
|
+
...(options.physics?.continuousCollision ? { continuousCollision: options.physics.continuousCollision } : {})
|
|
6743
7348
|
});
|
|
6744
7349
|
return appPhysicsWorldInstance;
|
|
6745
7350
|
};
|
|
@@ -7707,6 +8312,13 @@ pausedRenderTime = () => 0) {
|
|
|
7707
8312
|
labelLayer.setOcclusionTest(createSceneLabelOcclusionTest(snapshot, resolveCameraFrame(snapshot, snapshot.camera, renderTime, runtimeNodes).eye, runtimeNodes));
|
|
7708
8313
|
labelLayer.update(renderer.viewProjection(renderTime));
|
|
7709
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
|
+
});
|
|
7710
8322
|
}
|
|
7711
8323
|
overlay?.update();
|
|
7712
8324
|
if ((sceneWantsFrames || requiresFrames()) && options.autoStart !== false && typeof requestAnimationFrame !== "undefined") {
|
|
@@ -7804,16 +8416,37 @@ function createProductionRuntimeEnvironment(snapshot) {
|
|
|
7804
8416
|
"product-hero": "softbox",
|
|
7805
8417
|
"night-cinematic": "evening",
|
|
7806
8418
|
"metal-studio": "exhibit",
|
|
7807
|
-
"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"
|
|
7808
8423
|
};
|
|
7809
8424
|
const preset = presetByEnvironment[authoredEnvironment.environment];
|
|
7810
8425
|
const bundle = createExternalParityEnvironmentLighting(preset);
|
|
7811
8426
|
const intensity = nonNegativeFinite(authoredEnvironment.intensity);
|
|
7812
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;
|
|
7813
8441
|
return {
|
|
7814
8442
|
preset: authoredEnvironment.environment,
|
|
7815
8443
|
intensity,
|
|
7816
|
-
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 } : {}),
|
|
7817
8450
|
lighting: {
|
|
7818
8451
|
...bundle.lighting,
|
|
7819
8452
|
color: authoredEnvironment.color ? colorToLinearRgb(authoredEnvironment.color) : bundle.lighting.color,
|
|
@@ -7868,14 +8501,16 @@ function createProductionRuntimeEnvironment(snapshot) {
|
|
|
7868
8501
|
* changed exactly zero pixels through `createAuraApp`. Returning `false` when no
|
|
7869
8502
|
* fog node is authored keeps the unfogged path unchanged.
|
|
7870
8503
|
*/
|
|
7871
|
-
function createProductionRuntimeEnvironmentFog(snapshot) {
|
|
7872
|
-
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");
|
|
7873
8508
|
if (!fog)
|
|
7874
8509
|
return false;
|
|
7875
8510
|
const density = clampNumber(fog.density ?? 0.12, 0, 1);
|
|
7876
8511
|
const intensity = clampNumber(fog.intensity ?? 0.5, 0, 1);
|
|
7877
8512
|
const color = colorToLinearRgb(fog.color ?? "#9fb7d9");
|
|
7878
|
-
|
|
8513
|
+
const base = {
|
|
7879
8514
|
// Exponential-squared reads as depth haze rather than a hard linear band, which
|
|
7880
8515
|
// matches what the public helper documents.
|
|
7881
8516
|
mode: "exponential-squared",
|
|
@@ -7885,10 +8520,46 @@ function createProductionRuntimeEnvironmentFog(snapshot) {
|
|
|
7885
8520
|
density,
|
|
7886
8521
|
maxOpacity: clampNumber(0.25 + intensity * 0.55, 0, 0.92)
|
|
7887
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
|
+
};
|
|
7888
8545
|
}
|
|
7889
|
-
function createProductionRuntimePostprocess(snapshot) {
|
|
8546
|
+
function createProductionRuntimePostprocess(snapshot, lights = [], renderWidth = 1280, renderHeight = 720) {
|
|
7890
8547
|
const nodes = groups.flatten(snapshot.nodes);
|
|
7891
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;
|
|
7892
8563
|
const bloomRequested = Boolean(authoredBloom);
|
|
7893
8564
|
// Root previously advertised `ssao` in `requestedPasses` whenever a scene added
|
|
7894
8565
|
// effects.ambientOcclusion() or effects.contactOcclusion(), but never submitted
|
|
@@ -7898,6 +8569,17 @@ function createProductionRuntimePostprocess(snapshot) {
|
|
|
7898
8569
|
const authoredAmbientOcclusion = nodes.find((node) => node.kind === "effect" && node.effect === "ambient-occlusion");
|
|
7899
8570
|
const authoredContactOcclusion = nodes.find((node) => node.kind === "effect" && node.effect === "contact-occlusion");
|
|
7900
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");
|
|
7901
8583
|
return {
|
|
7902
8584
|
// Tone mapping requires unclamped linear input. RGBA8 quantized dark clear
|
|
7903
8585
|
// colors and clipped highlights before ACES, which produced washed-out output.
|
|
@@ -7906,7 +8588,10 @@ function createProductionRuntimePostprocess(snapshot) {
|
|
|
7906
8588
|
bloom: {
|
|
7907
8589
|
threshold: clampNumber(authoredBloom?.threshold ?? 0.78, 0, 1),
|
|
7908
8590
|
intensity: clampNumber(authoredBloom?.intensity ?? 0.3, 0, 2),
|
|
7909
|
-
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 } : {})
|
|
7910
8595
|
}
|
|
7911
8596
|
} : {}),
|
|
7912
8597
|
...(authoredOcclusion ? {
|
|
@@ -7927,7 +8612,41 @@ function createProductionRuntimePostprocess(snapshot) {
|
|
|
7927
8612
|
operator: "aces",
|
|
7928
8613
|
inputColorSpace: "linear",
|
|
7929
8614
|
outputColorSpace: "srgb"
|
|
7930
|
-
}
|
|
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 } : {})
|
|
7931
8650
|
};
|
|
7932
8651
|
}
|
|
7933
8652
|
function createProductionRuntimeShadowOptions(snapshot, collectedLights) {
|
|
@@ -7980,6 +8699,79 @@ function createProductionRuntimeShadowObservation(shadowOptions, diagnostics) {
|
|
|
7980
8699
|
shadowRenderTargetsAllocated: diagnostics.shadowRenderTargetsAllocated ?? 0
|
|
7981
8700
|
};
|
|
7982
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
|
+
}
|
|
7983
8775
|
function createProductionRuntimePostprocessObservation(diagnostics) {
|
|
7984
8776
|
const actualPasses = diagnostics.postprocessPassNames ?? [];
|
|
7985
8777
|
const pixelBacked = actualPasses.length > 0 && diagnostics.lastError === null;
|
|
@@ -7992,7 +8784,8 @@ function createProductionRuntimePostprocessObservation(diagnostics) {
|
|
|
7992
8784
|
pixelBacked,
|
|
7993
8785
|
actualPasses,
|
|
7994
8786
|
fallbackPasses: pixelBacked ? [] : ["direct-render"],
|
|
7995
|
-
targetFormat: diagnostics.postprocessTargetFormat
|
|
8787
|
+
targetFormat: diagnostics.postprocessTargetFormat,
|
|
8788
|
+
executionMode: diagnostics.postprocessPlan?.executionMode ?? "unknown"
|
|
7996
8789
|
};
|
|
7997
8790
|
}
|
|
7998
8791
|
function createProductionRuntimeCollectedLights(snapshot) {
|
|
@@ -8000,16 +8793,26 @@ function createProductionRuntimeCollectedLights(snapshot) {
|
|
|
8000
8793
|
const descriptors = authoredLights.flatMap((node, index) => createProductionRuntimeLightDescriptors(node, index));
|
|
8001
8794
|
if (descriptors.length === 0)
|
|
8002
8795
|
return createProductionRuntimeFallbackLights();
|
|
8003
|
-
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) => {
|
|
8004
8806
|
if (selected < 0)
|
|
8005
8807
|
return index;
|
|
8006
8808
|
const current = descriptors[selected];
|
|
8007
|
-
|
|
8008
|
-
|
|
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;
|
|
8009
8813
|
}
|
|
8010
8814
|
return descriptor.intensity > current.intensity ? index : selected;
|
|
8011
8815
|
}, -1);
|
|
8012
|
-
return descriptors.map((descriptor, index) => createProductionRuntimeCollectedLight(descriptor, index === shadowCasterIndex));
|
|
8013
8816
|
}
|
|
8014
8817
|
function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
8015
8818
|
const name = node.name?.trim() || `aura-authored-${node.light}-${authoredIndex + 1}`;
|
|
@@ -8034,6 +8837,7 @@ function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
|
8034
8837
|
spotAngle: 0,
|
|
8035
8838
|
penumbra: 0,
|
|
8036
8839
|
shadowPriority: 3,
|
|
8840
|
+
shadowRequested: false,
|
|
8037
8841
|
authoredLight: node.light
|
|
8038
8842
|
}];
|
|
8039
8843
|
}
|
|
@@ -8049,12 +8853,33 @@ function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
|
8049
8853
|
spotAngle: 0,
|
|
8050
8854
|
penumbra: 0,
|
|
8051
8855
|
shadowPriority: 1,
|
|
8856
|
+
shadowRequested: false,
|
|
8052
8857
|
authoredLight: node.light
|
|
8053
8858
|
}];
|
|
8054
8859
|
}
|
|
8055
8860
|
if (node.light === "studio") {
|
|
8056
8861
|
return createProductionRuntimeStudioLightDescriptors(node, name, color, intensity, position);
|
|
8057
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
|
+
}
|
|
8058
8883
|
const width = positiveFinite(node.width, node.light === "softbox" ? 2.4 : 2.2)
|
|
8059
8884
|
* productionRuntimeLightAxisScale(node.scale, 0);
|
|
8060
8885
|
const height = positiveFinite(node.height, node.light === "softbox" ? 1.6 : 1.2)
|
|
@@ -8073,6 +8898,7 @@ function createProductionRuntimeLightDescriptors(node, authoredIndex) {
|
|
|
8073
8898
|
spotAngle: clampNumber(Math.atan2(halfDiagonal, targetDistance), 0.08, Math.PI / 2 - 0.01),
|
|
8074
8899
|
penumbra: node.light === "softbox" ? 0.78 : 0.42,
|
|
8075
8900
|
shadowPriority: 2,
|
|
8901
|
+
shadowRequested: false,
|
|
8076
8902
|
authoredLight: node.light,
|
|
8077
8903
|
authoredWidth: width,
|
|
8078
8904
|
authoredHeight: height
|
|
@@ -8102,6 +8928,7 @@ function createProductionRuntimeStudioLightDescriptors(node, name, color, intens
|
|
|
8102
8928
|
spotAngle: 0,
|
|
8103
8929
|
penumbra: 0,
|
|
8104
8930
|
shadowPriority: 3,
|
|
8931
|
+
shadowRequested: false,
|
|
8105
8932
|
authoredLight: node.light
|
|
8106
8933
|
},
|
|
8107
8934
|
{
|
|
@@ -8115,6 +8942,7 @@ function createProductionRuntimeStudioLightDescriptors(node, name, color, intens
|
|
|
8115
8942
|
spotAngle: 0,
|
|
8116
8943
|
penumbra: 0,
|
|
8117
8944
|
shadowPriority: 0,
|
|
8945
|
+
shadowRequested: false,
|
|
8118
8946
|
authoredLight: node.light
|
|
8119
8947
|
},
|
|
8120
8948
|
{
|
|
@@ -8128,6 +8956,7 @@ function createProductionRuntimeStudioLightDescriptors(node, name, color, intens
|
|
|
8128
8956
|
spotAngle: 0,
|
|
8129
8957
|
penumbra: 0,
|
|
8130
8958
|
shadowPriority: 0,
|
|
8959
|
+
shadowRequested: false,
|
|
8131
8960
|
authoredLight: node.light
|
|
8132
8961
|
}
|
|
8133
8962
|
];
|
|
@@ -8147,6 +8976,7 @@ function createProductionRuntimeFallbackLights() {
|
|
|
8147
8976
|
spotAngle: 0,
|
|
8148
8977
|
penumbra: 0,
|
|
8149
8978
|
shadowPriority: 3,
|
|
8979
|
+
shadowRequested: false,
|
|
8150
8980
|
authoredLight: "fallback"
|
|
8151
8981
|
},
|
|
8152
8982
|
{
|
|
@@ -8160,6 +8990,7 @@ function createProductionRuntimeFallbackLights() {
|
|
|
8160
8990
|
spotAngle: 0,
|
|
8161
8991
|
penumbra: 0,
|
|
8162
8992
|
shadowPriority: 0,
|
|
8993
|
+
shadowRequested: false,
|
|
8163
8994
|
authoredLight: "fallback"
|
|
8164
8995
|
},
|
|
8165
8996
|
{
|
|
@@ -8173,6 +9004,7 @@ function createProductionRuntimeFallbackLights() {
|
|
|
8173
9004
|
spotAngle: 0,
|
|
8174
9005
|
penumbra: 0,
|
|
8175
9006
|
shadowPriority: 0,
|
|
9007
|
+
shadowRequested: false,
|
|
8176
9008
|
authoredLight: "fallback"
|
|
8177
9009
|
}
|
|
8178
9010
|
];
|
|
@@ -8306,6 +9138,7 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
8306
9138
|
width: canvas.width,
|
|
8307
9139
|
height: canvas.height,
|
|
8308
9140
|
...(node.hiddenNodeNames ? { hiddenNodeNames: node.hiddenNodeNames } : {}),
|
|
9141
|
+
...(node.role === "primaryWorld" ? { consolidateStaticMeshes: true } : {}),
|
|
8309
9142
|
...(node.material?.color ? {
|
|
8310
9143
|
tint: {
|
|
8311
9144
|
baseColor: colorToLinearRgba(node.material.color),
|
|
@@ -8338,53 +9171,142 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
8338
9171
|
});
|
|
8339
9172
|
let latestDeviceDiagnostics = productionRenderer.getDiagnostics();
|
|
8340
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;
|
|
8341
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
|
+
});
|
|
8342
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
|
+
}
|
|
8343
9219
|
const productionRuntimeLights = createProductionRuntimeCollectedLights(snapshot);
|
|
8344
9220
|
const authoredLightNodes = flattened.filter((node) => node.kind === "light");
|
|
8345
9221
|
const authoredDirectLightNodes = authoredLightNodes.filter((node) => node.light !== "ambient");
|
|
8346
9222
|
const authoredAmbientLightCount = authoredLightNodes.length - authoredDirectLightNodes.length;
|
|
8347
9223
|
const authoredAreaProxyCount = authoredDirectLightNodes.filter((node) => node.light === "rect" || node.light === "softbox").length;
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
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
|
-
|
|
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
|
+
};
|
|
8388
9310
|
return {
|
|
8389
9311
|
get backend() {
|
|
8390
9312
|
return productionRenderer.backend;
|
|
@@ -8394,7 +9316,8 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
8394
9316
|
},
|
|
8395
9317
|
render(time) {
|
|
8396
9318
|
runtimeWarnings.clear();
|
|
8397
|
-
|
|
9319
|
+
latestCameraEye = resolveCameraFrame(snapshot, snapshot.camera, time, runtimeNodes).eye;
|
|
9320
|
+
const input = createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, primitiveEntries, time, runtimeNodes, runtimeWarnings, currentEnvironmentLighting, productionRuntimeLights);
|
|
8398
9321
|
const result = productionRenderer.renderInteractiveFrame(input);
|
|
8399
9322
|
latestDeviceDiagnostics = result.diagnostics;
|
|
8400
9323
|
latestFeatures = result.features;
|
|
@@ -8416,13 +9339,15 @@ async function createProductionRuntimeSceneRenderer(canvas, snapshot, rendererOp
|
|
|
8416
9339
|
return productionRenderer.deviceLost();
|
|
8417
9340
|
},
|
|
8418
9341
|
dispose() {
|
|
9342
|
+
disposeHdriEnvironment?.();
|
|
8419
9343
|
productionRenderer.dispose();
|
|
8420
9344
|
for (const { actor } of actorEntries)
|
|
8421
|
-
actor.
|
|
9345
|
+
actor.dispose();
|
|
8422
9346
|
for (const { resources } of primitiveEntries) {
|
|
8423
|
-
for (const { geometry, material } of resources) {
|
|
9347
|
+
for (const { geometry, material, texturedMaterial } of resources) {
|
|
8424
9348
|
geometry.dispose();
|
|
8425
9349
|
material.dispose();
|
|
9350
|
+
texturedMaterial?.dispose();
|
|
8426
9351
|
}
|
|
8427
9352
|
}
|
|
8428
9353
|
}
|
|
@@ -8437,23 +9362,91 @@ function createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, pr
|
|
|
8437
9362
|
const currentNode = currentState.node;
|
|
8438
9363
|
if (currentNode.visible === false)
|
|
8439
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);
|
|
8440
9369
|
applyProductionActorAnimation(entry, currentNode, currentState.animationBinding, time, runtimeWarnings);
|
|
8441
9370
|
applyProductionActorMorphTargets(entry, currentState.morphTargets, runtimeWarnings);
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
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 })));
|
|
8445
9407
|
attachProductionActorEvidence(currentNode, entry.actor, actorItems, runtimeNodes);
|
|
8446
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;
|
|
8447
9415
|
for (const entry of primitiveEntries) {
|
|
8448
9416
|
const currentState = resolveProductionPrimitiveRuntimeState(entry, runtimeNodes);
|
|
8449
9417
|
if (!currentState.visible)
|
|
8450
9418
|
continue;
|
|
8451
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
|
+
}
|
|
8452
9444
|
items.push({
|
|
8453
9445
|
geometry: resource.geometry,
|
|
8454
|
-
material: resource.material,
|
|
9446
|
+
material: resource.texturedMaterial ?? resource.material,
|
|
8455
9447
|
modelMatrix: createModelMatrix(currentState.node, resource.bounds, false, time),
|
|
8456
9448
|
label: currentState.node.name ?? `aura-primitive-${currentState.node.primitive}`,
|
|
9449
|
+
castShadow: currentState.node.castShadow,
|
|
8457
9450
|
includeInAutoFrame: false,
|
|
8458
9451
|
...(currentState.node.instances ? { instanceTransforms: createProductionInstanceTransforms(currentState.node.instances, currentState.node), instanceColors: createProductionInstanceColors(currentState.node.instanceColors, currentState.node.instances.length) } : {})
|
|
8459
9452
|
});
|
|
@@ -8465,9 +9458,12 @@ function createProductionRuntimeRendererInput(snapshot, canvas, actorEntries, pr
|
|
|
8465
9458
|
frustumCulling: true,
|
|
8466
9459
|
collectedLights,
|
|
8467
9460
|
environmentLighting,
|
|
8468
|
-
|
|
9461
|
+
// The production runtime owns the pixel-backed HDR target and pass chain for
|
|
9462
|
+
// routes that request effects. The diagnostics are device-observed, so a
|
|
9463
|
+
// compositor failure is reported as fallback rather than claimed as a pass.
|
|
9464
|
+
postprocess: createProductionRuntimePostprocess(snapshot, collectedLights, canvas.width, canvas.height),
|
|
8469
9465
|
shadow: createProductionRuntimeShadowOptions(snapshot, collectedLights),
|
|
8470
|
-
environmentFog: createProductionRuntimeEnvironmentFog(snapshot),
|
|
9466
|
+
environmentFog: createProductionRuntimeEnvironmentFog(snapshot, collectedLights, canvas.width, canvas.height),
|
|
8471
9467
|
cameraPosition
|
|
8472
9468
|
};
|
|
8473
9469
|
const cameraLike = { viewProjectionMatrix };
|
|
@@ -8482,10 +9478,505 @@ function createProductionRuntimePrimitiveEntries(nodes) {
|
|
|
8482
9478
|
.filter((node) => node.kind === "primitive")
|
|
8483
9479
|
.map((node) => ({ node, resources: createProductionPrimitiveResources(node), currentLodIndex: 0 }));
|
|
8484
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
|
+
}
|
|
8485
9960
|
function createProductionPrimitiveResources(node) {
|
|
9961
|
+
const sdfWarnings = [];
|
|
9962
|
+
const sdfResource = createSdfTextPrimitiveResource(node, (message) => { sdfWarnings.push(message); });
|
|
8486
9963
|
const levels = node.lod?.levels;
|
|
8487
9964
|
if (!levels?.length) {
|
|
8488
|
-
|
|
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];
|
|
8489
9980
|
}
|
|
8490
9981
|
return levels.map((level, index) => {
|
|
8491
9982
|
if (!level.primitive && !level.geometry)
|
|
@@ -8497,7 +9988,21 @@ function createProductionPrimitiveResources(node) {
|
|
|
8497
9988
|
material: level.material ?? node.material,
|
|
8498
9989
|
lod: undefined
|
|
8499
9990
|
};
|
|
8500
|
-
|
|
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;
|
|
8501
10006
|
});
|
|
8502
10007
|
}
|
|
8503
10008
|
function selectProductionPrimitiveResource(entry, node, cameraPosition) {
|
|
@@ -8509,6 +10014,14 @@ function selectProductionPrimitiveResource(entry, node, cameraPosition) {
|
|
|
8509
10014
|
entry.currentLodIndex = selection.levelIndex;
|
|
8510
10015
|
return entry.resources[selection.levelIndex] ?? entry.resources[entry.resources.length - 1];
|
|
8511
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
|
+
}
|
|
8512
10025
|
function createProductionInstanceTransforms(transforms, node) {
|
|
8513
10026
|
const matrices = new Float32Array(transforms.length * 16);
|
|
8514
10027
|
transforms.forEach((transform, index) => {
|
|
@@ -8544,37 +10057,7 @@ function resolveProductionPrimitiveRuntimeState(entry, runtimeNodes) {
|
|
|
8544
10057
|
visible: runtimeSnapshot.visible
|
|
8545
10058
|
};
|
|
8546
10059
|
}
|
|
8547
|
-
|
|
8548
|
-
* Build the production `PBRMaterial` for a primitive node.
|
|
8549
|
-
*
|
|
8550
|
-
* ## WS-2.1a — the parameter drop this function used to be
|
|
8551
|
-
*
|
|
8552
|
-
* Until 1.6 this forwarded `clearcoat` and **nothing else** from the extended material surface.
|
|
8553
|
-
* `AuraMaterialSpec` accepts `sheen`, `sheenRoughness`, `sheenColor`, `iridescence`,
|
|
8554
|
-
* `iridescenceIOR`, `iridescenceThicknessRange`, `anisotropy`, `anisotropyRotation`, `transmission`,
|
|
8555
|
-
* `thickness`, `ior`, `attenuationColor` and `attenuationDistance`; `PBRMaterial` accepts a uniform
|
|
8556
|
-
* for every one of them and binds them all. The bridge between the two dropped them on the floor.
|
|
8557
|
-
*
|
|
8558
|
-
* How that presented, measured by `tools/material-structural-parity` before the fix:
|
|
8559
|
-
*
|
|
8560
|
-
* ```
|
|
8561
|
-
* material.pbr({ ..., sheen: 1 }) -> byte-identical frame to sheen: 0
|
|
8562
|
-
* material.pbr({ ..., iridescence: 1 }) -> byte-identical frame to iridescence: 0
|
|
8563
|
-
* material.pbr({ ..., clearcoat: 1 }) -> byte-identical frame to clearcoat: 0
|
|
8564
|
-
* ```
|
|
8565
|
-
*
|
|
8566
|
-
* `clearcoat` was *forwarded* and still produced an identical frame, which pins the second, separate
|
|
8567
|
-
* defect: `a3dPbrExtensionEnvironmentLight` in `ShaderChunks.ts` adds its lobes to
|
|
8568
|
-
* `specularRadiance`, a term that is zero without an environment map. Forwarding alone is necessary
|
|
8569
|
-
* and not sufficient, so `environmentIntensity` now also has a floor when an extension factor is
|
|
8570
|
-
* present — otherwise a developer setting `clearcoat: 1` on a scene with no environment sees nothing
|
|
8571
|
-
* and has no way to find out why.
|
|
8572
|
-
*
|
|
8573
|
-
* The reason this survived so long is worth stating: three of the four lobes are scalar
|
|
8574
|
-
* approximations rather than real BRDFs, and **nobody could tell**, because the parameters never
|
|
8575
|
-
* arrived. The plumbing defect concealed the shading defect.
|
|
8576
|
-
*/
|
|
8577
|
-
function createProductionPrimitiveMaterial(node) {
|
|
10060
|
+
function resolveProductionPrimitiveScalars(node) {
|
|
8578
10061
|
const materialSpec = node.material;
|
|
8579
10062
|
const baseColor = colorToLinearRgba(materialSpec?.color ?? materialSpec?.emissive ?? "#d7dee8");
|
|
8580
10063
|
const opacity = clamp01(materialSpec?.opacity ?? baseColor[3] ?? 1);
|
|
@@ -8605,6 +10088,17 @@ function createProductionPrimitiveMaterial(node) {
|
|
|
8605
10088
|
const environmentIntensity = declaresExtension
|
|
8606
10089
|
? Math.max(0.35, requestedEnvironmentIntensity)
|
|
8607
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);
|
|
8608
10102
|
/*
|
|
8609
10103
|
* Native instance submission requires the renderer's instanced shader contract. The ordinary
|
|
8610
10104
|
* PBR shader intentionally has no instance matrix inputs and ForwardPass therefore expands it.
|
|
@@ -8622,6 +10116,7 @@ function createProductionPrimitiveMaterial(node) {
|
|
|
8622
10116
|
emissiveColor,
|
|
8623
10117
|
emissiveStrength: Math.max(0, materialSpec?.emissiveIntensity ?? (materialSpec?.emissive ? 1.35 : 0)),
|
|
8624
10118
|
environmentIntensity,
|
|
10119
|
+
envMapIntensity,
|
|
8625
10120
|
renderState: {
|
|
8626
10121
|
blend: opacity < 0.999,
|
|
8627
10122
|
depthWrite: opacity >= 0.999,
|
|
@@ -8652,6 +10147,7 @@ function createProductionPrimitiveMaterial(node) {
|
|
|
8652
10147
|
...(materialSpec?.attenuationColor ? { volumeAttenuationColor: colorToLinearRgb(materialSpec.attenuationColor) } : {}),
|
|
8653
10148
|
...(materialSpec?.attenuationDistance === undefined ? {} : { volumeAttenuationDistance: Math.max(0, materialSpec.attenuationDistance) }),
|
|
8654
10149
|
environmentIntensity,
|
|
10150
|
+
envMapIntensity,
|
|
8655
10151
|
renderState: {
|
|
8656
10152
|
blend: opacity < 0.999,
|
|
8657
10153
|
depthWrite: opacity >= 0.999,
|
|
@@ -8674,6 +10170,12 @@ function primitiveGeometryBounds(nodeOrPrimitive) {
|
|
|
8674
10170
|
throw new Error("Aura3D custom primitive requires geometry.");
|
|
8675
10171
|
return createProductionPrimitiveMesh(primitive).bounds;
|
|
8676
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
|
+
}
|
|
8677
10179
|
function createProductionPrimitiveMesh(primitive) {
|
|
8678
10180
|
if (primitive === "sphere")
|
|
8679
10181
|
return createSphereGeometry();
|
|
@@ -8725,11 +10227,24 @@ function calculateCustomGeometryNormals(spec) {
|
|
|
8725
10227
|
}
|
|
8726
10228
|
function createProductionGeometryFromPrimitiveMesh(mesh) {
|
|
8727
10229
|
const vertexCount = mesh.positions.length / 3;
|
|
8728
|
-
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);
|
|
8729
10233
|
for (let index = 0; index < vertexCount; index += 1) {
|
|
8730
10234
|
const base = index * 3;
|
|
8731
10235
|
vertices.setAttribute(index, "position", [mesh.positions[base] ?? 0, mesh.positions[base + 1] ?? 0, mesh.positions[base + 2] ?? 0]);
|
|
8732
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
|
+
}
|
|
8733
10248
|
}
|
|
8734
10249
|
return new Geometry(vertices, new IndexBuffer(Array.from(mesh.indices), vertexCount), "triangles", mesh.bounds);
|
|
8735
10250
|
}
|
|
@@ -8770,6 +10285,39 @@ function applyProductionActorAnimation(entry, node, animationBinding, time, runt
|
|
|
8770
10285
|
runtimeWarnings.add(`Typed GLB actor "${entry.actor.id}" failed to apply clip "${clipName}": ${productionRenderErrorMessage(error)}`);
|
|
8771
10286
|
}
|
|
8772
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
|
+
}
|
|
8773
10321
|
function applyProductionActorMorphTargets(entry, morphTargets, runtimeWarnings) {
|
|
8774
10322
|
if (!morphTargets || Object.keys(morphTargets).length === 0)
|
|
8775
10323
|
return;
|
|
@@ -8817,7 +10365,14 @@ function createRuntimeEvidenceFromTypedGLBActor(node, actor, renderItems) {
|
|
|
8817
10365
|
},
|
|
8818
10366
|
renderItemCount: renderItems.length,
|
|
8819
10367
|
skinnedRenderItemCount: renderItems.filter((item) => item.skinning).length,
|
|
8820
|
-
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
|
|
8821
10376
|
});
|
|
8822
10377
|
}
|
|
8823
10378
|
function createProductionRuntimeMetadata(actorEntries, primitiveEntries) {
|
|
@@ -9130,7 +10685,12 @@ function findRuntimeCameraTarget(cameraSpec, runtimeNodes) {
|
|
|
9130
10685
|
return undefined;
|
|
9131
10686
|
const targetNode = cameraSpec.targetNode;
|
|
9132
10687
|
const directHandle = runtimeNodes.get(targetNode);
|
|
9133
|
-
|
|
10688
|
+
// An explicitly named target remains a valid camera anchor while hidden.
|
|
10689
|
+
// Composition probes temporarily suppress the hero node to isolate its
|
|
10690
|
+
// pixels; dropping that target here would also drop target-yaw rotation and
|
|
10691
|
+
// move the camera between the visible and suppressed captures. Visibility
|
|
10692
|
+
// controls drawing, not whether the camera can follow the requested node.
|
|
10693
|
+
if (directHandle)
|
|
9134
10694
|
return { position: directHandle.position, rotation: directHandle.rotation };
|
|
9135
10695
|
const namedHandle = runtimeNodes.all().find((handle) => handle.visible !== false && (handle.name === targetNode || handle.tags.includes(targetNode)));
|
|
9136
10696
|
return namedHandle ? { position: namedHandle.position, rotation: namedHandle.rotation } : undefined;
|
|
@@ -9257,7 +10817,9 @@ function collectRuntimeEffectNodes(snapshot) {
|
|
|
9257
10817
|
return groups.flatten(snapshot.nodes).filter((node) => node.kind === "effect");
|
|
9258
10818
|
}
|
|
9259
10819
|
function hasRuntimePostProcessEffects(effectNodes) {
|
|
9260
|
-
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");
|
|
9261
10823
|
}
|
|
9262
10824
|
async function createWebGLSceneRenderer(canvas, snapshot, rendererOptions, runtimeWarnings = [], runtimeNodes) {
|
|
9263
10825
|
const gl = canvas.getContext("webgl2", { antialias: true, preserveDrawingBuffer: true });
|
|
@@ -9293,7 +10855,8 @@ async function createWebGLSceneRenderer(canvas, snapshot, rendererOptions, runti
|
|
|
9293
10855
|
contactOcclusionReceiver: false,
|
|
9294
10856
|
pixelBacked: false,
|
|
9295
10857
|
actualPasses: [],
|
|
9296
|
-
fallbackPasses: hasRuntimePostProcessEffects(requestedEffectNodes) ? ["webgl2-direct-render"] : []
|
|
10858
|
+
fallbackPasses: hasRuntimePostProcessEffects(requestedEffectNodes) ? ["webgl2-direct-render"] : [],
|
|
10859
|
+
executionMode: "none"
|
|
9297
10860
|
},
|
|
9298
10861
|
warnings: [
|
|
9299
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.",
|
|
@@ -10529,6 +12092,8 @@ function textureRef(texture, uvs, key) {
|
|
|
10529
12092
|
return { [key]: texture.index };
|
|
10530
12093
|
}
|
|
10531
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]);
|
|
10532
12097
|
return {
|
|
10533
12098
|
positions: new Float32Array([
|
|
10534
12099
|
-0.5, 0, -0.5,
|
|
@@ -10549,10 +12114,21 @@ function createPlaneGeometry() {
|
|
|
10549
12114
|
// up-normal downward. The plane then faced away from every overhead light and
|
|
10550
12115
|
// received no direct lighting or visible cast shadows.
|
|
10551
12116
|
indices: new Uint16Array([0, 2, 1, 0, 3, 2]),
|
|
10552
|
-
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
|
|
10553
12121
|
};
|
|
10554
12122
|
}
|
|
10555
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]));
|
|
10556
12132
|
const positions = new Float32Array([
|
|
10557
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,
|
|
10558
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,
|
|
@@ -10580,7 +12156,10 @@ function createBoxGeometry() {
|
|
|
10580
12156
|
16, 17, 18, 16, 18, 19,
|
|
10581
12157
|
20, 21, 22, 20, 22, 23
|
|
10582
12158
|
]),
|
|
10583
|
-
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
|
|
10584
12163
|
};
|
|
10585
12164
|
}
|
|
10586
12165
|
function createSphereGeometry() {
|
|
@@ -10588,6 +12167,8 @@ function createSphereGeometry() {
|
|
|
10588
12167
|
const columns = 16;
|
|
10589
12168
|
const positions = [];
|
|
10590
12169
|
const normals = [];
|
|
12170
|
+
const uvs = [];
|
|
12171
|
+
const tangents = [];
|
|
10591
12172
|
const indices = [];
|
|
10592
12173
|
for (let row = 0; row <= rows; row += 1) {
|
|
10593
12174
|
const v = row / rows;
|
|
@@ -10600,6 +12181,8 @@ function createSphereGeometry() {
|
|
|
10600
12181
|
const z = Math.sin(theta) * Math.sin(phi);
|
|
10601
12182
|
positions.push(x * 0.5, y * 0.5, z * 0.5);
|
|
10602
12183
|
normals.push(x, y, z);
|
|
12184
|
+
uvs.push(u, 1 - v);
|
|
12185
|
+
tangents.push(-Math.sin(phi), 0, Math.cos(phi), 1);
|
|
10603
12186
|
}
|
|
10604
12187
|
}
|
|
10605
12188
|
for (let row = 0; row < rows; row += 1) {
|
|
@@ -10612,11 +12195,15 @@ function createSphereGeometry() {
|
|
|
10612
12195
|
indices.push(a, a + 1, b, b, a + 1, b + 1);
|
|
10613
12196
|
}
|
|
10614
12197
|
}
|
|
12198
|
+
const sphereUvs = new Float32Array(uvs);
|
|
10615
12199
|
return {
|
|
10616
12200
|
positions: new Float32Array(positions),
|
|
10617
12201
|
normals: new Float32Array(normals),
|
|
10618
12202
|
indices: new Uint16Array(indices),
|
|
10619
|
-
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)
|
|
10620
12207
|
};
|
|
10621
12208
|
}
|
|
10622
12209
|
function createCylinderGeometry() {
|
|
@@ -11285,8 +12872,33 @@ function validateSceneAssets(snapshot, assets) {
|
|
|
11285
12872
|
}
|
|
11286
12873
|
function createAssetProvenance(asset) {
|
|
11287
12874
|
const remote = /^https?:\/\//i.test(asset.url);
|
|
12875
|
+
/*
|
|
12876
|
+
* A deployment may rewrite the URL of a generated manifest asset to an
|
|
12877
|
+
* immutable external asset host (for example, GitHub media or an object
|
|
12878
|
+
* store) so the site bundle does not need to carry every GLB. URL location
|
|
12879
|
+
* is therefore not a sufficient trust signal: the manifest identity is the
|
|
12880
|
+
* typed ref's kind/id/hash plus durable source-path provenance. Keep
|
|
12881
|
+
* `unsafeModelUrl(...)` blocked by its sentinel id, and do not bless an
|
|
12882
|
+
* arbitrary remote object that merely happens to carry a hash.
|
|
12883
|
+
*/
|
|
12884
|
+
const manifestPaths = [
|
|
12885
|
+
asset.metadata?.provenance?.sourcePath,
|
|
12886
|
+
asset.metadata?.sourcePath,
|
|
12887
|
+
asset.metadata?.outputPath,
|
|
12888
|
+
asset.metadata?.provenance?.outputPath
|
|
12889
|
+
];
|
|
12890
|
+
const hasDurableManifestProvenance = asset.kind === "aura-asset-ref"
|
|
12891
|
+
&& asset.id !== "unsafe"
|
|
12892
|
+
&& typeof asset.hash === "string"
|
|
12893
|
+
&& asset.hash.length > 0
|
|
12894
|
+
&& manifestPaths.some((value) => typeof value === "string" && value.replaceAll("\\", "/").startsWith("public/aura-assets/"));
|
|
12895
|
+
const trustedManifestAsset = !remote || hasDurableManifestProvenance;
|
|
11288
12896
|
return {
|
|
11289
|
-
source: asset.id === "unsafe" ||
|
|
12897
|
+
source: asset.id === "unsafe" || !trustedManifestAsset
|
|
12898
|
+
? "unsafe-url"
|
|
12899
|
+
: asset.hash
|
|
12900
|
+
? "typed-aura-assets-manifest"
|
|
12901
|
+
: "inline-definition",
|
|
11290
12902
|
id: asset.id,
|
|
11291
12903
|
url: asset.url,
|
|
11292
12904
|
...(asset.hash ? { hash: asset.hash } : {}),
|
|
@@ -11325,7 +12937,9 @@ function snapshotDiagnostics(value) {
|
|
|
11325
12937
|
renderer: value.renderer,
|
|
11326
12938
|
warnings: [...value.warnings],
|
|
11327
12939
|
errors: [...value.errors],
|
|
11328
|
-
...(value.labels ? { labels: value.labels } : {})
|
|
12940
|
+
...(value.labels ? { labels: value.labels } : {}),
|
|
12941
|
+
...(value.labelTelemetry ? { labelTelemetry: value.labelTelemetry } : {}),
|
|
12942
|
+
...(value.textBuckets ? { textBuckets: value.textBuckets } : {})
|
|
11329
12943
|
};
|
|
11330
12944
|
}
|
|
11331
12945
|
function collectGeneratedCodeWarnings(snapshot) {
|
|
@@ -11336,6 +12950,27 @@ function collectGeneratedCodeWarnings(snapshot) {
|
|
|
11336
12950
|
if (!snapshot.nodes.some((node) => node.kind === "interaction")) {
|
|
11337
12951
|
warnings.push("Scene has no interactions. Suggested fix: add interactions.orbit() for product/viewer scenes.");
|
|
11338
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
|
+
}
|
|
11339
12974
|
for (const node of snapshot.nodes) {
|
|
11340
12975
|
if (node.kind === "model" && createAssetProvenance(node.asset).source === "unsafe-url") {
|
|
11341
12976
|
warnings.push(`Model uses unsafeModelUrl("${node.asset.url}"). Suggested fix: run assets add and use typed assets.`);
|
|
@@ -11499,6 +13134,16 @@ function drawEffect(context, width, height, node, time) {
|
|
|
11499
13134
|
context.fillStyle = toAlphaColor(node.color ?? "#9fb7d9", node.density ?? 0.12);
|
|
11500
13135
|
context.fillRect(0, height * 0.2, width, height * 0.8);
|
|
11501
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
|
+
}
|
|
11502
13147
|
if (node.effect === "bloom") {
|
|
11503
13148
|
const gradient = context.createRadialGradient(width * 0.5, height * 0.45, 20, width * 0.5, height * 0.45, width * 0.46);
|
|
11504
13149
|
gradient.addColorStop(0, toAlphaColor(node.color ?? "#ffffff", (node.intensity ?? 0.35) * 0.3));
|
|
@@ -11551,6 +13196,29 @@ function drawEffect(context, width, height, node, time) {
|
|
|
11551
13196
|
}
|
|
11552
13197
|
}
|
|
11553
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
|
+
}
|
|
11554
13222
|
if (node.effect === "particles") {
|
|
11555
13223
|
const isFountain = node.emitter === "fountain";
|
|
11556
13224
|
// Fountain cap matches prefabs.particleFountain's 2400 maximum so rendered counts never undercut the scene JSON.
|