@crystaldesign/real-time-viewer-next 26.11.0-beta.18
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/build/esm/DeckCutoutSubsystem-76dfe828.js +2536 -0
- package/build/esm/SunLensflare-e4ab3f81.js +483 -0
- package/build/esm/WaterSurface-93383fdf.js +2041 -0
- package/build/esm/index-920dd901.js +23273 -0
- package/build/esm/index.js +32 -0
- package/build/types/real-time-viewer-next/src/RealTimeRendererNext.d.ts +58 -0
- package/build/types/real-time-viewer-next/src/RealTimeRendererNext.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/analyzerComponents.d.ts +23 -0
- package/build/types/real-time-viewer-next/src/analyzerComponents.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/analyzerStageContext.d.ts +9 -0
- package/build/types/real-time-viewer-next/src/analyzerStageContext.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/capture/applyShot.d.ts +8 -0
- package/build/types/real-time-viewer-next/src/capture/applyShot.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/capture/blob.d.ts +11 -0
- package/build/types/real-time-viewer-next/src/capture/blob.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/capture/materialMute.d.ts +16 -0
- package/build/types/real-time-viewer-next/src/capture/materialMute.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/capture/shotState.d.ts +24 -0
- package/build/types/real-time-viewer-next/src/capture/shotState.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/capture/types.d.ts +80 -0
- package/build/types/real-time-viewer-next/src/capture/types.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/capture/useCaptureEngine.d.ts +7 -0
- package/build/types/real-time-viewer-next/src/capture/useCaptureEngine.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/config/descriptor.d.ts +156 -0
- package/build/types/real-time-viewer-next/src/config/descriptor.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/config/descriptor.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/config/descriptor.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/config/environment.config.d.ts +511 -0
- package/build/types/real-time-viewer-next/src/config/environment.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/config/environment.config.test.d.ts +5 -0
- package/build/types/real-time-viewer-next/src/config/environment.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/BBoxPreloader.d.ts +15 -0
- package/build/types/real-time-viewer-next/src/core/BBoxPreloader.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/FloorGrid.d.ts +3 -0
- package/build/types/real-time-viewer-next/src/core/FloorGrid.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/GeoBBoxReporter.d.ts +10 -0
- package/build/types/real-time-viewer-next/src/core/GeoBBoxReporter.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/GeoInstance.d.ts +13 -0
- package/build/types/real-time-viewer-next/src/core/GeoInstance.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/PaletteDragPreview.d.ts +19 -0
- package/build/types/real-time-viewer-next/src/core/PaletteDragPreview.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/PerfLog.d.ts +38 -0
- package/build/types/real-time-viewer-next/src/core/PerfLog.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/PlannerGrid.d.ts +83 -0
- package/build/types/real-time-viewer-next/src/core/PlannerGrid.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/R3FAssetBoundary.d.ts +31 -0
- package/build/types/real-time-viewer-next/src/core/R3FAssetBoundary.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/RTVErrorBoundary.d.ts +16 -0
- package/build/types/real-time-viewer-next/src/core/RTVErrorBoundary.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/SceneRenderer.d.ts +3 -0
- package/build/types/real-time-viewer-next/src/core/SceneRenderer.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/SceneTree.d.ts +16 -0
- package/build/types/real-time-viewer-next/src/core/SceneTree.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/VariantMeshOverride.d.ts +62 -0
- package/build/types/real-time-viewer-next/src/core/VariantMeshOverride.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/CameraController.d.ts +3 -0
- package/build/types/real-time-viewer-next/src/core/camera/CameraController.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/cameraAnimationEngine.d.ts +69 -0
- package/build/types/real-time-viewer-next/src/core/camera/cameraAnimationEngine.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/cameraPoseBook.d.ts +24 -0
- package/build/types/real-time-viewer-next/src/core/camera/cameraPoseBook.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/controlsLock.d.ts +25 -0
- package/build/types/real-time-viewer-next/src/core/camera/controlsLock.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/sectorLimits.d.ts +38 -0
- package/build/types/real-time-viewer-next/src/core/camera/sectorLimits.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/sectorLimits.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/camera/sectorLimits.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/useAutoFrame.d.ts +29 -0
- package/build/types/real-time-viewer-next/src/core/camera/useAutoFrame.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/useAutoRotate.d.ts +8 -0
- package/build/types/real-time-viewer-next/src/core/camera/useAutoRotate.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/useCameraActions.d.ts +13 -0
- package/build/types/real-time-viewer-next/src/core/camera/useCameraActions.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/useCameraAnimation.d.ts +15 -0
- package/build/types/real-time-viewer-next/src/core/camera/useCameraAnimation.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/useCameraConstraints.d.ts +25 -0
- package/build/types/real-time-viewer-next/src/core/camera/useCameraConstraints.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/useCameraSmoothing.d.ts +57 -0
- package/build/types/real-time-viewer-next/src/core/camera/useCameraSmoothing.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/useMaterialFade.d.ts +5 -0
- package/build/types/real-time-viewer-next/src/core/camera/useMaterialFade.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/camera/useViewModeTransition.d.ts +19 -0
- package/build/types/real-time-viewer-next/src/core/camera/useViewModeTransition.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/DimensionLine2D.d.ts +28 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/DimensionLine2D.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/DimensionLines2D.d.ts +3 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/DimensionLines2D.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/DimensionLines3D.d.ts +3 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/DimensionLines3D.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionGeometry.d.ts +34 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionGeometry.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLineBuilders.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLineBuilders.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLineBuilders2D.d.ts +82 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLineBuilders2D.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLineBuilders3D.d.ts +76 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLineBuilders3D.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLines.config.d.ts +25 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLines.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLines.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/dimensionLines.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/index.d.ts +3 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/index.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/useDimensionFade.d.ts +12 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/useDimensionFade.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/useLabelScale.d.ts +11 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/useLabelScale.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/utils.d.ts +26 -0
- package/build/types/real-time-viewer-next/src/core/dimension-lines/utils.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/EnvironmentDome.d.ts +14 -0
- package/build/types/real-time-viewer-next/src/core/environment/EnvironmentDome.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/EnvironmentSceneLoader.d.ts +14 -0
- package/build/types/real-time-viewer-next/src/core/environment/EnvironmentSceneLoader.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/EnvironmentSources.d.ts +33 -0
- package/build/types/real-time-viewer-next/src/core/environment/EnvironmentSources.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/EnvironmentSunLensflare.d.ts +3 -0
- package/build/types/real-time-viewer-next/src/core/environment/EnvironmentSunLensflare.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/LightingRig.d.ts +28 -0
- package/build/types/real-time-viewer-next/src/core/environment/LightingRig.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/PlainGround.d.ts +21 -0
- package/build/types/real-time-viewer-next/src/core/environment/PlainGround.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/RuntimePerfAdjuster.d.ts +15 -0
- package/build/types/real-time-viewer-next/src/core/environment/RuntimePerfAdjuster.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/SceneEnvironment.d.ts +19 -0
- package/build/types/real-time-viewer-next/src/core/environment/SceneEnvironment.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/SkydomeBackground.d.ts +11 -0
- package/build/types/real-time-viewer-next/src/core/environment/SkydomeBackground.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/SunLensflare.d.ts +29 -0
- package/build/types/real-time-viewer-next/src/core/environment/SunLensflare.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/SunLensflareLazy.d.ts +11 -0
- package/build/types/real-time-viewer-next/src/core/environment/SunLensflareLazy.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/adaptive.config.d.ts +24 -0
- package/build/types/real-time-viewer-next/src/core/environment/adaptive.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/adaptive.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/adaptive.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/DeckCutoutArticleSync.d.ts +22 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/DeckCutoutArticleSync.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/DeckCutoutCollar.d.ts +15 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/DeckCutoutCollar.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/DeckCutoutLazy.d.ts +10 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/DeckCutoutLazy.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/DeckCutoutSubsystem.d.ts +9 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/DeckCutoutSubsystem.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/anchorElements.d.ts +40 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/anchorElements.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/anchorElements.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/anchorElements.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/articleFootprint.d.ts +62 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/articleFootprint.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/articleFootprint.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/articleFootprint.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/buildCollarGeometry.d.ts +25 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/buildCollarGeometry.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/buildCollarGeometry.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/buildCollarGeometry.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/collectDeckTriangles.d.ts +86 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/collectDeckTriangles.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/deriveWaterVolume.d.ts +28 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/deriveWaterVolume.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/deriveWaterVolume.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/deriveWaterVolume.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/measureArticle.d.ts +49 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/measureArticle.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/measureArticle.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/measureArticle.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/measureDiagnostics.d.ts +20 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/measureDiagnostics.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/useCollarTextures.d.ts +16 -0
- package/build/types/real-time-viewer-next/src/core/environment/deckCutout/useCollarTextures.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/dome.config.d.ts +191 -0
- package/build/types/real-time-viewer-next/src/core/environment/dome.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/dome.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/dome.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/ground.config.d.ts +130 -0
- package/build/types/real-time-viewer-next/src/core/environment/ground.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/ground.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/ground.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/lighting.config.d.ts +297 -0
- package/build/types/real-time-viewer-next/src/core/environment/lighting.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/lighting.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/lighting.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/scene.config.d.ts +440 -0
- package/build/types/real-time-viewer-next/src/core/environment/scene.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/scene.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/environment/scene.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/useEnvironmentMap.d.ts +19 -0
- package/build/types/real-time-viewer-next/src/core/environment/useEnvironmentMap.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/environment/useStudioLightEffects.d.ts +28 -0
- package/build/types/real-time-viewer-next/src/core/environment/useStudioLightEffects.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/frameOrder.d.ts +91 -0
- package/build/types/real-time-viewer-next/src/core/frameOrder.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/handles.d.ts +35 -0
- package/build/types/real-time-viewer-next/src/core/handles.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/AccessoryDropController.d.ts +9 -0
- package/build/types/real-time-viewer-next/src/core/interaction/AccessoryDropController.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/AccessoryFollowController.d.ts +15 -0
- package/build/types/real-time-viewer-next/src/core/interaction/AccessoryFollowController.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/DragController.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/interaction/DragController.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/ElementOverlay.d.ts +16 -0
- package/build/types/real-time-viewer-next/src/core/interaction/ElementOverlay.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/ElementOverlayIcons.d.ts +18 -0
- package/build/types/real-time-viewer-next/src/core/interaction/ElementOverlayIcons.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/FreeGridLayer.d.ts +10 -0
- package/build/types/real-time-viewer-next/src/core/interaction/FreeGridLayer.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/InteractionGuides.d.ts +3 -0
- package/build/types/real-time-viewer-next/src/core/interaction/InteractionGuides.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/accessoryCellTargeting.d.ts +31 -0
- package/build/types/real-time-viewer-next/src/core/interaction/accessoryCellTargeting.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/selection.config.d.ts +65 -0
- package/build/types/real-time-viewer-next/src/core/interaction/selection.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/selection.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/interaction/selection.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/useAccessoryMoveDrag.d.ts +21 -0
- package/build/types/real-time-viewer-next/src/core/interaction/useAccessoryMoveDrag.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/interaction/useElementInteraction.d.ts +35 -0
- package/build/types/real-time-viewer-next/src/core/interaction/useElementInteraction.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/lazySubsystems.d.ts +16 -0
- package/build/types/real-time-viewer-next/src/core/lazySubsystems.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/materials/MaterialContext.d.ts +23 -0
- package/build/types/real-time-viewer-next/src/core/materials/MaterialContext.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/materials/MaterialManager.d.ts +4 -0
- package/build/types/real-time-viewer-next/src/core/materials/MaterialManager.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/materials/apply.d.ts +17 -0
- package/build/types/real-time-viewer-next/src/core/materials/apply.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/materials/caches.d.ts +44 -0
- package/build/types/real-time-viewer-next/src/core/materials/caches.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/materials/factory.d.ts +37 -0
- package/build/types/real-time-viewer-next/src/core/materials/factory.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/materials/materials.config.d.ts +13 -0
- package/build/types/real-time-viewer-next/src/core/materials/materials.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/materials/materials.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/materials/materials.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/materials/textureCache.d.ts +26 -0
- package/build/types/real-time-viewer-next/src/core/materials/textureCache.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/materials/types.d.ts +5 -0
- package/build/types/real-time-viewer-next/src/core/materials/types.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/meshBVH.d.ts +22 -0
- package/build/types/real-time-viewer-next/src/core/meshBVH.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/meshBVH.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/meshBVH.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/PostProcessingDynamicEffects.d.ts +11 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/PostProcessingDynamicEffects.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/PostProcessingStack.d.ts +28 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/PostProcessingStack.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/applyRendererToneMapping.d.ts +10 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/applyRendererToneMapping.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/index.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/index.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/postProcessing.config.d.ts +453 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/postProcessing.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/postProcessing.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/postprocessing/postProcessing.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/renderer.config.d.ts +55 -0
- package/build/types/real-time-viewer-next/src/core/renderer.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/renderer.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/renderer.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/SceneShadowSystem.d.ts +18 -0
- package/build/types/real-time-viewer-next/src/core/shadows/SceneShadowSystem.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/SunShadow.d.ts +29 -0
- package/build/types/real-time-viewer-next/src/core/shadows/SunShadow.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/index.d.ts +4 -0
- package/build/types/real-time-viewer-next/src/core/shadows/index.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadowMapSize.d.ts +20 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadowMapSize.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadowMapSize.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadowMapSize.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadowRegistry.d.ts +24 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadowRegistry.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadows.config.d.ts +75 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadows.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadows.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/shadows/shadows.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/useShadowDirty.d.ts +14 -0
- package/build/types/real-time-viewer-next/src/core/shadows/useShadowDirty.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/useShadowFlags.d.ts +28 -0
- package/build/types/real-time-viewer-next/src/core/shadows/useShadowFlags.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/shadows/useShadowRebake.d.ts +14 -0
- package/build/types/real-time-viewer-next/src/core/shadows/useShadowRebake.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/useFrameloopVisibility.d.ts +37 -0
- package/build/types/real-time-viewer-next/src/core/useFrameloopVisibility.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/view2d/DockingPoints2D.d.ts +17 -0
- package/build/types/real-time-viewer-next/src/core/view2d/DockingPoints2D.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/view2d/Element2DPlane.d.ts +18 -0
- package/build/types/real-time-viewer-next/src/core/view2d/Element2DPlane.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/view2d/ElementAnnotation2D.d.ts +16 -0
- package/build/types/real-time-viewer-next/src/core/view2d/ElementAnnotation2D.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/view2d/Scene2DOverlay.d.ts +10 -0
- package/build/types/real-time-viewer-next/src/core/view2d/Scene2DOverlay.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/ViewModeRuntimeContext.d.ts +20 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/ViewModeRuntimeContext.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/index.d.ts +4 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/index.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/selectViewModeRuntime.d.ts +9 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/selectViewModeRuntime.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/selectViewModeRuntime.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/selectViewModeRuntime.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/types.d.ts +67 -0
- package/build/types/real-time-viewer-next/src/core/viewMode/types.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/CausticReceiver.d.ts +49 -0
- package/build/types/real-time-viewer-next/src/core/water/CausticReceiver.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/CausticsTexture.d.ts +48 -0
- package/build/types/real-time-viewer-next/src/core/water/CausticsTexture.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/Heightfield.d.ts +88 -0
- package/build/types/real-time-viewer-next/src/core/water/Heightfield.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/PoolHeightMap.d.ts +36 -0
- package/build/types/real-time-viewer-next/src/core/water/PoolHeightMap.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/RefractionCapture.d.ts +73 -0
- package/build/types/real-time-viewer-next/src/core/water/RefractionCapture.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/RefractionCapture.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/water/RefractionCapture.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/SceneCapture.d.ts +66 -0
- package/build/types/real-time-viewer-next/src/core/water/SceneCapture.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/WaterSurface.d.ts +8 -0
- package/build/types/real-time-viewer-next/src/core/water/WaterSurface.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/WaterSurfaceLazy.d.ts +10 -0
- package/build/types/real-time-viewer-next/src/core/water/WaterSurfaceLazy.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/WaterSurfaceRenderer.d.ts +71 -0
- package/build/types/real-time-viewer-next/src/core/water/WaterSurfaceRenderer.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/useCausticReceivers.d.ts +11 -0
- package/build/types/real-time-viewer-next/src/core/water/useCausticReceivers.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/useWaterPointer.d.ts +13 -0
- package/build/types/real-time-viewer-next/src/core/water/useWaterPointer.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/useWaveSimulation.d.ts +44 -0
- package/build/types/real-time-viewer-next/src/core/water/useWaveSimulation.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/useWaveSimulation.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/water/useWaveSimulation.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/water.config.d.ts +90 -0
- package/build/types/real-time-viewer-next/src/core/water/water.config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/water.config.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/core/water/water.config.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/waterDebug.d.ts +14 -0
- package/build/types/real-time-viewer-next/src/core/water/waterDebug.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/waterShaders.d.ts +9 -0
- package/build/types/real-time-viewer-next/src/core/water/waterShaders.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/waterSurfaceMaterial.d.ts +21 -0
- package/build/types/real-time-viewer-next/src/core/water/waterSurfaceMaterial.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/core/water/waterUniforms.d.ts +51 -0
- package/build/types/real-time-viewer-next/src/core/water/waterUniforms.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/hooks/useAssembledScene.d.ts +17 -0
- package/build/types/real-time-viewer-next/src/hooks/useAssembledScene.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/hooks/useHostPropsSync.d.ts +20 -0
- package/build/types/real-time-viewer-next/src/hooks/useHostPropsSync.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/index.d.ts +17 -0
- package/build/types/real-time-viewer-next/src/index.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/internals.d.ts +61 -0
- package/build/types/real-time-viewer-next/src/internals.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/loadingGates.d.ts +34 -0
- package/build/types/real-time-viewer-next/src/loadingGates.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/parser/parseRenderScript.d.ts +51 -0
- package/build/types/real-time-viewer-next/src/parser/parseRenderScript.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/parser/types.d.ts +58 -0
- package/build/types/real-time-viewer-next/src/parser/types.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/presets/mergePresetBundle.d.ts +33 -0
- package/build/types/real-time-viewer-next/src/presets/mergePresetBundle.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/presets/polyfaser-planner.d.ts +4 -0
- package/build/types/real-time-viewer-next/src/presets/polyfaser-planner.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/presets/polyfaser-pool.d.ts +4 -0
- package/build/types/real-time-viewer-next/src/presets/polyfaser-pool.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/presets/polyfaserPlannerOverride.d.ts +9 -0
- package/build/types/real-time-viewer-next/src/presets/polyfaserPlannerOverride.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/presets/villa-cliff.d.ts +4 -0
- package/build/types/real-time-viewer-next/src/presets/villa-cliff.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/publicApi.d.ts +35 -0
- package/build/types/real-time-viewer-next/src/publicApi.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/publicPath.d.ts +1 -0
- package/build/types/real-time-viewer-next/src/publicPath.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/index.d.ts +18 -0
- package/build/types/real-time-viewer-next/src/store/index.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/shared.d.ts +25 -0
- package/build/types/real-time-viewer-next/src/store/shared.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/accessory.d.ts +147 -0
- package/build/types/real-time-viewer-next/src/store/slices/accessory.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/analyzerGate.d.ts +15 -0
- package/build/types/real-time-viewer-next/src/store/slices/analyzerGate.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/camera.d.ts +66 -0
- package/build/types/real-time-viewer-next/src/store/slices/camera.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/capture.d.ts +60 -0
- package/build/types/real-time-viewer-next/src/store/slices/capture.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/config.d.ts +52 -0
- package/build/types/real-time-viewer-next/src/store/slices/config.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/deckCutout.d.ts +29 -0
- package/build/types/real-time-viewer-next/src/store/slices/deckCutout.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/environment.d.ts +47 -0
- package/build/types/real-time-viewer-next/src/store/slices/environment.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/glGeneration.d.ts +18 -0
- package/build/types/real-time-viewer-next/src/store/slices/glGeneration.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/hostSelection.test.d.ts +5 -0
- package/build/types/real-time-viewer-next/src/store/slices/hostSelection.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/interaction.d.ts +53 -0
- package/build/types/real-time-viewer-next/src/store/slices/interaction.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/performanceProfile.d.ts +71 -0
- package/build/types/real-time-viewer-next/src/store/slices/performanceProfile.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/plannerUi.d.ts +46 -0
- package/build/types/real-time-viewer-next/src/store/slices/plannerUi.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/scene.d.ts +26 -0
- package/build/types/real-time-viewer-next/src/store/slices/scene.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/sceneLoad.d.ts +75 -0
- package/build/types/real-time-viewer-next/src/store/slices/sceneLoad.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/sceneLoad.test.d.ts +5 -0
- package/build/types/real-time-viewer-next/src/store/slices/sceneLoad.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/selection.d.ts +66 -0
- package/build/types/real-time-viewer-next/src/store/slices/selection.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/versions.d.ts +34 -0
- package/build/types/real-time-viewer-next/src/store/slices/versions.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/slices/viewMode.d.ts +16 -0
- package/build/types/real-time-viewer-next/src/store/slices/viewMode.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/store/types.d.ts +96 -0
- package/build/types/real-time-viewer-next/src/store/types.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/testUtils/deepDiff.d.ts +26 -0
- package/build/types/real-time-viewer-next/src/testUtils/deepDiff.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/testUtils/deepDiff.test.d.ts +11 -0
- package/build/types/real-time-viewer-next/src/testUtils/deepDiff.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/testUtils/divaCoreMock.d.ts +47 -0
- package/build/types/real-time-viewer-next/src/testUtils/divaCoreMock.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/testUtils/resolvedConfigSnapshots.d.ts +36 -0
- package/build/types/real-time-viewer-next/src/testUtils/resolvedConfigSnapshots.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/testUtils/structuredClonePolyfill.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/testUtils/structuredClonePolyfill.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/types/accessoryGrids.d.ts +134 -0
- package/build/types/real-time-viewer-next/src/types/accessoryGrids.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/types/camera.d.ts +288 -0
- package/build/types/real-time-viewer-next/src/types/camera.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/types/environment.d.ts +146 -0
- package/build/types/real-time-viewer-next/src/types/environment.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/types/plannerUi.d.ts +68 -0
- package/build/types/real-time-viewer-next/src/types/plannerUi.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/types/scene2d.d.ts +168 -0
- package/build/types/real-time-viewer-next/src/types/scene2d.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/bootPhase.d.ts +75 -0
- package/build/types/real-time-viewer-next/src/utils/bootPhase.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/bootPhase.test.d.ts +5 -0
- package/build/types/real-time-viewer-next/src/utils/bootPhase.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/camera.d.ts +8 -0
- package/build/types/real-time-viewer-next/src/utils/camera.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/cameraFraming.d.ts +47 -0
- package/build/types/real-time-viewer-next/src/utils/cameraFraming.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/collisionTargets.d.ts +34 -0
- package/build/types/real-time-viewer-next/src/utils/collisionTargets.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/collisionTargets.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/utils/collisionTargets.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/cursorStack.d.ts +21 -0
- package/build/types/real-time-viewer-next/src/utils/cursorStack.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/detectDeviceTier.d.ts +24 -0
- package/build/types/real-time-viewer-next/src/utils/detectDeviceTier.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/detectDeviceTier.test.d.ts +5 -0
- package/build/types/real-time-viewer-next/src/utils/detectDeviceTier.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/dockingGroups.d.ts +7 -0
- package/build/types/real-time-viewer-next/src/utils/dockingGroups.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/elementIds.d.ts +27 -0
- package/build/types/real-time-viewer-next/src/utils/elementIds.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/equirectCache.d.ts +45 -0
- package/build/types/real-time-viewer-next/src/utils/equirectCache.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/equirectUrl.d.ts +22 -0
- package/build/types/real-time-viewer-next/src/utils/equirectUrl.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/equirectUrl.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/utils/equirectUrl.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/footprint.d.ts +15 -0
- package/build/types/real-time-viewer-next/src/utils/footprint.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/footprint.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/utils/footprint.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/footprintTrace.d.ts +55 -0
- package/build/types/real-time-viewer-next/src/utils/footprintTrace.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/footprintTrace.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/utils/footprintTrace.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/geoViewAngle.d.ts +17 -0
- package/build/types/real-time-viewer-next/src/utils/geoViewAngle.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/gltfLoader.d.ts +34 -0
- package/build/types/real-time-viewer-next/src/utils/gltfLoader.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/gridGeometryApi.d.ts +17 -0
- package/build/types/real-time-viewer-next/src/utils/gridGeometryApi.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/localUVSampler.d.ts +89 -0
- package/build/types/real-time-viewer-next/src/utils/localUVSampler.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/localUVSampler.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/utils/localUVSampler.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/overlayGeometry.d.ts +28 -0
- package/build/types/real-time-viewer-next/src/utils/overlayGeometry.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/overlayGeometry.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/utils/overlayGeometry.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/planarTransform.d.ts +20 -0
- package/build/types/real-time-viewer-next/src/utils/planarTransform.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/planarTransform.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/utils/planarTransform.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/polygon2d.d.ts +25 -0
- package/build/types/real-time-viewer-next/src/utils/polygon2d.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/preloadEnvironment.d.ts +15 -0
- package/build/types/real-time-viewer-next/src/utils/preloadEnvironment.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/profiles.d.ts +113 -0
- package/build/types/real-time-viewer-next/src/utils/profiles.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/profiles.test.d.ts +17 -0
- package/build/types/real-time-viewer-next/src/utils/profiles.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/rasterMask.d.ts +31 -0
- package/build/types/real-time-viewer-next/src/utils/rasterMask.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/rasterMask.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/utils/rasterMask.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/resolveEnvironment.d.ts +16 -0
- package/build/types/real-time-viewer-next/src/utils/resolveEnvironment.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/resolveGridCells.d.ts +35 -0
- package/build/types/real-time-viewer-next/src/utils/resolveGridCells.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/scene.d.ts +3 -0
- package/build/types/real-time-viewer-next/src/utils/scene.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/screenProjection.d.ts +42 -0
- package/build/types/real-time-viewer-next/src/utils/screenProjection.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/screenProjection.test.d.ts +2 -0
- package/build/types/real-time-viewer-next/src/utils/screenProjection.test.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/selectionScopeNames.d.ts +6 -0
- package/build/types/real-time-viewer-next/src/utils/selectionScopeNames.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/selectionScopeScene.d.ts +8 -0
- package/build/types/real-time-viewer-next/src/utils/selectionScopeScene.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/smoothDamp.d.ts +19 -0
- package/build/types/real-time-viewer-next/src/utils/smoothDamp.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/sunAngles.d.ts +29 -0
- package/build/types/real-time-viewer-next/src/utils/sunAngles.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/suspense.d.ts +11 -0
- package/build/types/real-time-viewer-next/src/utils/suspense.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/whiteRoomPreset.d.ts +48 -0
- package/build/types/real-time-viewer-next/src/utils/whiteRoomPreset.d.ts.map +1 -0
- package/build/types/real-time-viewer-next/src/utils/withRenderTarget.d.ts +48 -0
- package/build/types/real-time-viewer-next/src/utils/withRenderTarget.d.ts.map +1 -0
- package/build/umd/114.real-time-viewer-next.umd.min.js +2 -0
- package/build/umd/114.real-time-viewer-next.umd.min.js.LICENSE.txt +1 -0
- package/build/umd/699.real-time-viewer-next.umd.min.js +2 -0
- package/build/umd/699.real-time-viewer-next.umd.min.js.LICENSE.txt +1 -0
- package/build/umd/964.real-time-viewer-next.umd.min.js +2 -0
- package/build/umd/964.real-time-viewer-next.umd.min.js.LICENSE.txt +1 -0
- package/build/umd/real-time-viewer-next.umd.min.js +2 -0
- package/build/umd/real-time-viewer-next.umd.min.js.LICENSE.txt +117 -0
- package/build/umd/report.html +38 -0
- package/package.json +31 -0
|
@@ -0,0 +1,2041 @@
|
|
|
1
|
+
import { w as withRenderTarget, s as setCausticReceiversSuspended, a as waterUniforms, d as disableWaterUniforms, u as useRTVStore, F as FRAME_ORDER, c as sunAnglesToDirection, l as lightingSunTowardDirection } from './index-920dd901.js';
|
|
2
|
+
import { useRef, useMemo, useEffect, useCallback } from 'react';
|
|
3
|
+
import { useThree, useFrame } from '@react-three/fiber';
|
|
4
|
+
import { Vector3, WebGLRenderTarget, HalfFloatType, RGBAFormat, LinearFilter, OrthographicCamera, ShaderMaterial, DoubleSide, MeshStandardMaterial, Box3, NoToneMapping, Frustum, Matrix4, Vector4, Vector2, Plane, Scene, Mesh, PlaneGeometry, ClampToEdgeWrapping, UnsignedByteType, MeshBasicMaterial, Color } from 'three';
|
|
5
|
+
import _classCallCheck from '@babel/runtime/helpers/classCallCheck';
|
|
6
|
+
import _createClass from '@babel/runtime/helpers/createClass';
|
|
7
|
+
import _defineProperty from '@babel/runtime/helpers/defineProperty';
|
|
8
|
+
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
|
|
9
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
10
|
+
import '@crystaldesign/diva-core';
|
|
11
|
+
import '@babel/runtime/helpers/slicedToArray';
|
|
12
|
+
import '@babel/runtime/helpers/asyncToGenerator';
|
|
13
|
+
import '@babel/runtime/regenerator';
|
|
14
|
+
import 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
15
|
+
import 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
16
|
+
import 'zustand';
|
|
17
|
+
import 'zustand/middleware';
|
|
18
|
+
import '@babel/runtime/helpers/typeof';
|
|
19
|
+
import 'postprocessing';
|
|
20
|
+
import '@react-three/drei';
|
|
21
|
+
import '@babel/runtime/helpers/possibleConstructorReturn';
|
|
22
|
+
import '@babel/runtime/helpers/getPrototypeOf';
|
|
23
|
+
import '@babel/runtime/helpers/inherits';
|
|
24
|
+
import 'classnames';
|
|
25
|
+
import 'three-mesh-bvh';
|
|
26
|
+
import '@babel/runtime/helpers/objectWithoutProperties';
|
|
27
|
+
import 'three/examples/jsm/environments/RoomEnvironment.js';
|
|
28
|
+
import 'three/examples/jsm/loaders/RGBELoader.js';
|
|
29
|
+
import 'three/examples/jsm/loaders/EXRLoader.js';
|
|
30
|
+
import '@react-three/postprocessing';
|
|
31
|
+
import '@babel/runtime/helpers/construct';
|
|
32
|
+
import 'detect-gpu';
|
|
33
|
+
|
|
34
|
+
function _createForOfIteratorHelper$2(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray$2(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
|
35
|
+
function _unsupportedIterableToArray$2(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray$2(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray$2(r, a) : void 0; } }
|
|
36
|
+
function _arrayLikeToArray$2(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
37
|
+
|
|
38
|
+
/** Output resolution per side — ~2-4 cm/texel for typical pool footprints. */
|
|
39
|
+
var HEIGHT_MAP_SIZE = 256;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Backstop cadence (frames) for content changes that bump no version —
|
|
43
|
+
* HQRS geos stream in asynchronously after assembly. One tiny pass every
|
|
44
|
+
* ~2 s at 60 fps is negligible and guarantees eventual convergence.
|
|
45
|
+
*/
|
|
46
|
+
var REFRESH_INTERVAL_FRAMES = 120;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Capture range above the waterline: rim tops slightly above the water
|
|
50
|
+
* still register (their overhang shadows the geo beneath) while roofs and
|
|
51
|
+
* terrace slabs higher up fall outside the near plane.
|
|
52
|
+
*/
|
|
53
|
+
var ABOVE_WATER_MARGIN = 0.3;
|
|
54
|
+
|
|
55
|
+
/** Capture range below the volume floor. */
|
|
56
|
+
var BELOW_FLOOR_MARGIN = 0.5;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Override-pass vertex shader. MUST use real camera matrices — the
|
|
60
|
+
* renderer's CPU frustum culling tests mesh bounds against the camera, so
|
|
61
|
+
* a hand-written gl_Position would cull an arbitrary subset of meshes.
|
|
62
|
+
*/
|
|
63
|
+
var HEIGHT_VERTEX = /* glsl */"\n uniform vec3 uVolumeCenter;\n uniform vec3 uPoolHalf;\n varying float vHeightEncoded;\n void main() {\n vec4 worldPosition = vec4(position, 1.0);\n #ifdef USE_INSTANCING\n worldPosition = instanceMatrix * worldPosition;\n #endif\n worldPosition = modelMatrix * worldPosition;\n vHeightEncoded = (worldPosition.y - uVolumeCenter.y + uPoolHalf.y) / (3.0 * uPoolHalf.y);\n gl_Position = projectionMatrix * viewMatrix * worldPosition;\n }\n";
|
|
64
|
+
var HEIGHT_FRAGMENT = /* glsl */"\n varying float vHeightEncoded;\n void main() {\n gl_FragColor = vec4(vHeightEncoded, 0.0, 0.0, 1.0);\n }\n";
|
|
65
|
+
|
|
66
|
+
/** Duck-type guards for renderables that must not draw into the pass. */
|
|
67
|
+
|
|
68
|
+
var PoolHeightMap = /*#__PURE__*/function () {
|
|
69
|
+
function PoolHeightMap() {
|
|
70
|
+
_classCallCheck(this, PoolHeightMap);
|
|
71
|
+
_defineProperty(this, "rt", void 0);
|
|
72
|
+
_defineProperty(this, "overrideMaterial", void 0);
|
|
73
|
+
_defineProperty(this, "camera", void 0);
|
|
74
|
+
_defineProperty(this, "lastCenter", new Vector3(Infinity, Infinity, Infinity));
|
|
75
|
+
_defineProperty(this, "lastHalf", new Vector3(0, 0, 0));
|
|
76
|
+
_defineProperty(this, "lastGeometryVersion", -1);
|
|
77
|
+
_defineProperty(this, "age", REFRESH_INTERVAL_FRAMES);
|
|
78
|
+
this.rt = new WebGLRenderTarget(HEIGHT_MAP_SIZE, HEIGHT_MAP_SIZE, {
|
|
79
|
+
type: HalfFloatType,
|
|
80
|
+
format: RGBAFormat,
|
|
81
|
+
depthBuffer: true,
|
|
82
|
+
stencilBuffer: false,
|
|
83
|
+
minFilter: LinearFilter,
|
|
84
|
+
magFilter: LinearFilter
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Re-fitted to the volume in `updateCamera` per render; its frustum
|
|
88
|
+
// culls out-of-volume meshes, roofs (near plane) and terrain (far
|
|
89
|
+
// plane) for free.
|
|
90
|
+
this.camera = new OrthographicCamera(-1, 1, 1, -1, 0.01, 1);
|
|
91
|
+
this.overrideMaterial = new ShaderMaterial({
|
|
92
|
+
vertexShader: HEIGHT_VERTEX,
|
|
93
|
+
fragmentShader: HEIGHT_FRAGMENT,
|
|
94
|
+
uniforms: {
|
|
95
|
+
uVolumeCenter: {
|
|
96
|
+
value: new Vector3()
|
|
97
|
+
},
|
|
98
|
+
uPoolHalf: {
|
|
99
|
+
value: new Vector3(1, 1, 1)
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
// CAD-exported pool GLBs can carry inconsistent winding; the depth
|
|
103
|
+
// test keeps the topmost surface regardless, so double-sided is the
|
|
104
|
+
// safe choice.
|
|
105
|
+
side: DoubleSide
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Output texture handle — stable reference across re-renders. */
|
|
110
|
+
return _createClass(PoolHeightMap, [{
|
|
111
|
+
key: "texture",
|
|
112
|
+
get: function get() {
|
|
113
|
+
return this.rt.texture;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Fit the top-down camera to the volume. The axis mapping must reproduce
|
|
118
|
+
* the read formula `uv = local.xz / (2 * poolHalf.xz) + 0.5`: with
|
|
119
|
+
* `up = (0, 0, -1)` camera-right is world +X, and the swapped
|
|
120
|
+
* `top`/`bottom` flip NDC y back to world +Z. Depth increases downward,
|
|
121
|
+
* so the topmost surface wins the depth test.
|
|
122
|
+
*/
|
|
123
|
+
}, {
|
|
124
|
+
key: "updateCamera",
|
|
125
|
+
value: function updateCamera(volumeCenter, poolHalf) {
|
|
126
|
+
var cam = this.camera;
|
|
127
|
+
cam.up.set(0, 0, -1);
|
|
128
|
+
cam.position.set(volumeCenter.x, volumeCenter.y + ABOVE_WATER_MARGIN, volumeCenter.z);
|
|
129
|
+
cam.lookAt(volumeCenter.x, volumeCenter.y - poolHalf.y, volumeCenter.z);
|
|
130
|
+
cam.left = -poolHalf.x;
|
|
131
|
+
cam.right = poolHalf.x;
|
|
132
|
+
cam.top = -poolHalf.z;
|
|
133
|
+
cam.bottom = poolHalf.z;
|
|
134
|
+
cam.near = 0.01;
|
|
135
|
+
cam.far = ABOVE_WATER_MARGIN + poolHalf.y + BELOW_FLOOR_MARGIN;
|
|
136
|
+
cam.updateProjectionMatrix();
|
|
137
|
+
cam.updateMatrixWorld(true);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Re-render the height map when it is stale, otherwise do nothing.
|
|
142
|
+
* Call once per frame BEFORE `CausticsTexture.render` so the caustics
|
|
143
|
+
* pass never samples a blank first-frame target.
|
|
144
|
+
*
|
|
145
|
+
* @param volumeCenter World-space water-volume center (surface Y).
|
|
146
|
+
* @param poolHalf (halfX, depth, halfZ) in world meters.
|
|
147
|
+
* @param geometryVersion Article + decor version sum — a change forces a render.
|
|
148
|
+
* @param excluded Subtrees hidden for the pass (the water group).
|
|
149
|
+
* @returns true when a render actually happened.
|
|
150
|
+
*/
|
|
151
|
+
}, {
|
|
152
|
+
key: "renderIfNeeded",
|
|
153
|
+
value: function renderIfNeeded(renderer, scene, volumeCenter, poolHalf, geometryVersion, excluded) {
|
|
154
|
+
var _this = this;
|
|
155
|
+
this.age += 1;
|
|
156
|
+
var stale = geometryVersion !== this.lastGeometryVersion || !volumeCenter.equals(this.lastCenter) || !poolHalf.equals(this.lastHalf) || this.age >= REFRESH_INTERVAL_FRAMES;
|
|
157
|
+
if (!stale) return false;
|
|
158
|
+
this.overrideMaterial.uniforms.uVolumeCenter.value.copy(volumeCenter);
|
|
159
|
+
this.overrideMaterial.uniforms.uPoolHalf.value.copy(poolHalf);
|
|
160
|
+
this.updateCamera(volumeCenter, poolHalf);
|
|
161
|
+
|
|
162
|
+
// Hide everything that is not receiving geometry: explicit exclusions,
|
|
163
|
+
// non-mesh renderables (the override material would rasterize lines /
|
|
164
|
+
// sprites / points into the map), and non-opaque materials. Same
|
|
165
|
+
// hide/restore mechanism as SceneCapture.
|
|
166
|
+
var hidden = [];
|
|
167
|
+
var _iterator = _createForOfIteratorHelper$2(excluded),
|
|
168
|
+
_step;
|
|
169
|
+
try {
|
|
170
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
171
|
+
var _obj = _step.value;
|
|
172
|
+
if (_obj && _obj.visible) {
|
|
173
|
+
_obj.visible = false;
|
|
174
|
+
hidden.push(_obj);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch (err) {
|
|
178
|
+
_iterator.e(err);
|
|
179
|
+
} finally {
|
|
180
|
+
_iterator.f();
|
|
181
|
+
}
|
|
182
|
+
scene.traverse(function (node) {
|
|
183
|
+
var flags = node;
|
|
184
|
+
if (!node.visible) return;
|
|
185
|
+
if (flags.isLine || flags.isPoints || flags.isSprite) {
|
|
186
|
+
node.visible = false;
|
|
187
|
+
hidden.push(node);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!flags.isMesh) return;
|
|
191
|
+
var mesh = node;
|
|
192
|
+
var material = mesh.material;
|
|
193
|
+
// Receiving geometry = opaque standard material. `transparent` AND
|
|
194
|
+
// `transmission` must both be checked — glass/water materials carry
|
|
195
|
+
// transmission > 0 with transparent still false, and light passes
|
|
196
|
+
// through them (e.g. the pool article's own water plane, which would
|
|
197
|
+
// otherwise cap every landing at the waterline).
|
|
198
|
+
var receives = function receives(m) {
|
|
199
|
+
if (!(m instanceof MeshStandardMaterial)) return false;
|
|
200
|
+
if (m.transparent || m.opacity < 1) return false;
|
|
201
|
+
var transmission = m.transmission;
|
|
202
|
+
return !(typeof transmission === 'number' && transmission > 0);
|
|
203
|
+
};
|
|
204
|
+
var isReceiver = Array.isArray(material) ? material.some(receives) : receives(material);
|
|
205
|
+
if (!isReceiver) {
|
|
206
|
+
mesh.visible = false;
|
|
207
|
+
hidden.push(mesh);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
var prevOverride = scene.overrideMaterial;
|
|
211
|
+
// A scene background draws its OWN pass: a colour background forces a clear in its own colour,
|
|
212
|
+
// a texture/cube background draws a mesh flagged `allowOverride: false`. Either paints over the
|
|
213
|
+
// black clear this encoding depends on, so no-geometry texels stop decoding to the floor plane.
|
|
214
|
+
var prevBackground = scene.background;
|
|
215
|
+
try {
|
|
216
|
+
// Clear color 0 decodes to the floor plane — the no-geometry fallback.
|
|
217
|
+
withRenderTarget(renderer, {
|
|
218
|
+
target: this.rt,
|
|
219
|
+
clearColor: 0x000000,
|
|
220
|
+
clearAlpha: 1
|
|
221
|
+
}, function () {
|
|
222
|
+
renderer.clear(true, true, false);
|
|
223
|
+
scene.background = null;
|
|
224
|
+
scene.overrideMaterial = _this.overrideMaterial;
|
|
225
|
+
renderer.render(scene, _this.camera);
|
|
226
|
+
});
|
|
227
|
+
} finally {
|
|
228
|
+
// Restored here rather than after `renderer.render`: a throw inside the draw would otherwise
|
|
229
|
+
// leave the height shader as the scene's override material — and the receiving geometry
|
|
230
|
+
// hidden — for every subsequent frame, since R3F schedules the next frame before running us.
|
|
231
|
+
scene.overrideMaterial = prevOverride;
|
|
232
|
+
scene.background = prevBackground;
|
|
233
|
+
for (var _i = 0, _hidden = hidden; _i < _hidden.length; _i++) {
|
|
234
|
+
var obj = _hidden[_i];
|
|
235
|
+
obj.visible = true;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
this.lastCenter.copy(volumeCenter);
|
|
239
|
+
this.lastHalf.copy(poolHalf);
|
|
240
|
+
this.lastGeometryVersion = geometryVersion;
|
|
241
|
+
this.age = 0;
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
}, {
|
|
245
|
+
key: "dispose",
|
|
246
|
+
value: function dispose() {
|
|
247
|
+
this.rt.dispose();
|
|
248
|
+
this.overrideMaterial.dispose();
|
|
249
|
+
}
|
|
250
|
+
}]);
|
|
251
|
+
}();
|
|
252
|
+
|
|
253
|
+
function _createForOfIteratorHelper$1(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray$1(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
|
254
|
+
function _unsupportedIterableToArray$1(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray$1(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray$1(r, a) : void 0; } }
|
|
255
|
+
function _arrayLikeToArray$1(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
256
|
+
|
|
257
|
+
/** Scratch for the per-mesh world-space bounds test in the cull pass. */
|
|
258
|
+
var _meshWorldBox = new Box3();
|
|
259
|
+
var SceneCapture = /*#__PURE__*/function () {
|
|
260
|
+
function SceneCapture() {
|
|
261
|
+
var _opts$format;
|
|
262
|
+
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
263
|
+
_classCallCheck(this, SceneCapture);
|
|
264
|
+
_defineProperty(this, "rt", void 0);
|
|
265
|
+
/** Drawing-buffer pixel width currently allocated for the RT. */
|
|
266
|
+
_defineProperty(this, "width", 0);
|
|
267
|
+
/** Drawing-buffer pixel height currently allocated for the RT. */
|
|
268
|
+
_defineProperty(this, "height", 0);
|
|
269
|
+
// Start at 1x1; the host will resize on the first frame via
|
|
270
|
+
// `resizeIfNeeded` so we never allocate a needlessly-large RT before
|
|
271
|
+
// the canvas mounts.
|
|
272
|
+
//
|
|
273
|
+
// Depth buffer is REQUIRED for opaque depth testing. Without it the
|
|
274
|
+
// renderer paints meshes in scene-graph / sort order with no Z
|
|
275
|
+
// comparison, so far geometry overwrites near geometry — e.g. the
|
|
276
|
+
// far pool wall painted after a nearer underwater mesh ends up
|
|
277
|
+
// covering it, and the SSR sample shows wall where that mesh
|
|
278
|
+
// should be. Keep it a PLAIN depth buffer — attaching a sampleable
|
|
279
|
+
// DepthTexture instead dims the captured colour.
|
|
280
|
+
this.rt = new WebGLRenderTarget(1, 1, {
|
|
281
|
+
type: HalfFloatType,
|
|
282
|
+
format: (_opts$format = opts.format) !== null && _opts$format !== void 0 ? _opts$format : RGBAFormat,
|
|
283
|
+
depthBuffer: true,
|
|
284
|
+
stencilBuffer: false,
|
|
285
|
+
// LinearFilter — the screen UV in the water shader is offset by
|
|
286
|
+
// sub-pixel amounts to simulate refraction, so we want smooth
|
|
287
|
+
// interpolation rather than nearest-neighbor blockiness.
|
|
288
|
+
minFilter: LinearFilter,
|
|
289
|
+
magFilter: LinearFilter
|
|
290
|
+
});
|
|
291
|
+
this.width = 1;
|
|
292
|
+
this.height = 1;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Output texture handle. Stable reference across frames — the
|
|
297
|
+
* WebGLRenderTarget keeps the same `.texture` even after we render into
|
|
298
|
+
* it, so consumers can assign this once and stop worrying about
|
|
299
|
+
* per-frame refreshes (unlike the heightfield ping-pong).
|
|
300
|
+
*/
|
|
301
|
+
return _createClass(SceneCapture, [{
|
|
302
|
+
key: "texture",
|
|
303
|
+
get: function get() {
|
|
304
|
+
return this.rt.texture;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Resize the render target if `(w, h, pr, scale)` differs from the
|
|
309
|
+
* currently allocated extent. Pass DRAWING-BUFFER pixels (CSS pixels *
|
|
310
|
+
* pixelRatio). The host typically gets this from `useThree(s => s.size)`
|
|
311
|
+
* and `renderer.getPixelRatio()`.
|
|
312
|
+
*
|
|
313
|
+
* `resolutionScale` renders the capture BELOW the canvas resolution.
|
|
314
|
+
* Consumers sample it with a normalized UV, so a smaller target costs
|
|
315
|
+
* nothing but sharpness — and refraction is a blur, so half-res is
|
|
316
|
+
* effectively free while cutting fill and bandwidth by 4x.
|
|
317
|
+
*
|
|
318
|
+
* Returns true when the target was actually reallocated. Its contents
|
|
319
|
+
* are gone at that point, so a consumer that skips captures on idle
|
|
320
|
+
* frames must treat this as "must re-capture now".
|
|
321
|
+
*/
|
|
322
|
+
}, {
|
|
323
|
+
key: "resizeIfNeeded",
|
|
324
|
+
value: function resizeIfNeeded(cssWidth, cssHeight, pixelRatio) {
|
|
325
|
+
var resolutionScale = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
|
|
326
|
+
var w = Math.max(1, Math.round(cssWidth * pixelRatio * resolutionScale));
|
|
327
|
+
var h = Math.max(1, Math.round(cssHeight * pixelRatio * resolutionScale));
|
|
328
|
+
if (w === this.width && h === this.height) return false;
|
|
329
|
+
this.rt.setSize(w, h);
|
|
330
|
+
this.width = w;
|
|
331
|
+
this.height = h;
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Render `scene` to the offscreen target with `excluded` meshes
|
|
337
|
+
* temporarily hidden. Renderer state is taken over and put back via
|
|
338
|
+
* {@link withRenderTarget} so the main R3F render is undisturbed.
|
|
339
|
+
*
|
|
340
|
+
* `excluded` is typically `[surfaceMeshRef.current]` so the water
|
|
341
|
+
* surface excludes itself from the captured scene (otherwise we'd
|
|
342
|
+
* refract through the water and see... the same water).
|
|
343
|
+
*
|
|
344
|
+
* `scissor` (in RENDER-TARGET pixels, GL bottom-left origin) restricts
|
|
345
|
+
* the pass to the screen region the consumer can actually sample. The
|
|
346
|
+
* full target is still cleared first, so everything outside the rect
|
|
347
|
+
* stays at alpha 0 and reads as a miss — exactly like a clipped pixel.
|
|
348
|
+
*
|
|
349
|
+
* `cullBox` (world space) hides every Mesh whose world bounds miss the
|
|
350
|
+
* box for the duration of the pass. The clip planes already discard
|
|
351
|
+
* out-of-volume FRAGMENTS, but each mesh still costs a draw call and its
|
|
352
|
+
* full vertex work — for a pool inside a large scene this skips almost
|
|
353
|
+
* all of it. Caveat: an InstancedMesh is tested by its BASE geometry
|
|
354
|
+
* bounds, so instanced content meant to appear underwater would be
|
|
355
|
+
* over-culled (none exists today).
|
|
356
|
+
*/
|
|
357
|
+
}, {
|
|
358
|
+
key: "render",
|
|
359
|
+
value: function render(renderer, scene, camera, excluded, clippingPlanes, scissor, cullBox) {
|
|
360
|
+
var _this = this;
|
|
361
|
+
// Hide the requested meshes for the capture pass. Tracked so we can
|
|
362
|
+
// restore visibility even if the underlying mesh is null on this
|
|
363
|
+
// frame (defensive — R3F refs can be transiently null around mount).
|
|
364
|
+
var hidden = [];
|
|
365
|
+
var _iterator = _createForOfIteratorHelper$1(excluded),
|
|
366
|
+
_step;
|
|
367
|
+
try {
|
|
368
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
369
|
+
var _obj = _step.value;
|
|
370
|
+
if (_obj && _obj.visible) {
|
|
371
|
+
_obj.visible = false;
|
|
372
|
+
hidden.push(_obj);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Volume cull — same hide/restore mechanism as `excluded`. Only meshes
|
|
377
|
+
// that are currently visible are touched so the restore loop can't
|
|
378
|
+
// accidentally reveal something else's hidden mesh. World matrices are
|
|
379
|
+
// at worst one frame stale here (the capture runs before the main
|
|
380
|
+
// render updates them), which the caller's box margin absorbs.
|
|
381
|
+
} catch (err) {
|
|
382
|
+
_iterator.e(err);
|
|
383
|
+
} finally {
|
|
384
|
+
_iterator.f();
|
|
385
|
+
}
|
|
386
|
+
if (cullBox) {
|
|
387
|
+
scene.traverse(function (node) {
|
|
388
|
+
var mesh = node;
|
|
389
|
+
if (!mesh.isMesh || !mesh.visible) return;
|
|
390
|
+
var geometry = mesh.geometry;
|
|
391
|
+
if (!geometry) return;
|
|
392
|
+
if (geometry.boundingBox === null) geometry.computeBoundingBox();
|
|
393
|
+
var bounds = geometry.boundingBox;
|
|
394
|
+
if (!bounds) return;
|
|
395
|
+
_meshWorldBox.copy(bounds).applyMatrix4(mesh.matrixWorld);
|
|
396
|
+
if (!_meshWorldBox.intersectsBox(cullBox)) {
|
|
397
|
+
mesh.visible = false;
|
|
398
|
+
hidden.push(mesh);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Scissor state lives on the RENDER TARGET, not the renderer: with a
|
|
404
|
+
// target bound, `setRenderTarget` copies `renderTarget.scissor` /
|
|
405
|
+
// `.scissorTest` into the GL state and ignores `renderer.setScissor`
|
|
406
|
+
// entirely (WebGLRenderer.js, the `_currentScissor.copy( renderTarget
|
|
407
|
+
// .scissor )` branch). So we bind twice — once scissor-free to clear
|
|
408
|
+
// the whole target, once with the rect to restrict the draw. The
|
|
409
|
+
// scissor-free state has to be set before the pass binds the target.
|
|
410
|
+
this.rt.scissorTest = false;
|
|
411
|
+
|
|
412
|
+
// The scene background draws its own pass — a colour background forces an opaque clear in its
|
|
413
|
+
// own colour, a texture/cube background draws a mesh flagged `allowOverride: false`. Either one
|
|
414
|
+
// lands on top of the `clearAlpha: 0` this capture depends on, so the consumer's "alpha 0 (or
|
|
415
|
+
// near-black) = capture miss" test could never fire and the refraction fallback colour was
|
|
416
|
+
// unreachable. Null it for the pass; restored in the `finally` below.
|
|
417
|
+
var prevBackground = scene.background;
|
|
418
|
+
scene.background = null;
|
|
419
|
+
try {
|
|
420
|
+
withRenderTarget(renderer, {
|
|
421
|
+
target: this.rt,
|
|
422
|
+
// The main R3F render typically uses ACESFilmic or another
|
|
423
|
+
// tone-mapping curve. For the SSR sample we want the LINEAR colour
|
|
424
|
+
// of the scene so the water shader's downstream math (Fresnel mix,
|
|
425
|
+
// waterColor tint) operates in the same space as the rest of the
|
|
426
|
+
// water uniforms. Tone mapping is re-applied to the FINAL
|
|
427
|
+
// R3F render — not this intermediate.
|
|
428
|
+
toneMapping: NoToneMapping,
|
|
429
|
+
// Clear with transparent black so any pixel the scene doesn't write
|
|
430
|
+
// (above-horizon sky, off-canvas regions, AND pixels removed by the
|
|
431
|
+
// water-Y clip plane below) ends up as 0,0,0,0. The shader's alpha
|
|
432
|
+
// check turns those zero-alpha samples into "capture miss, use the
|
|
433
|
+
// fallback colour" so refraction never blends in the clipped geo.
|
|
434
|
+
// Alpha only — the clear colour's RGB is irrelevant at alpha 0 and
|
|
435
|
+
// leaving it alone keeps the restore to a single property.
|
|
436
|
+
clearAlpha: 0,
|
|
437
|
+
// Used by WaterSurfaceRenderer to install a horizontal plane at the
|
|
438
|
+
// water surface Y: every fragment above water is discarded, leaving
|
|
439
|
+
// the captured framebuffer alpha=0 in those screen regions. The
|
|
440
|
+
// surface shader then samples the alpha — if 0 there is no underwater
|
|
441
|
+
// geo behind this fragment (e.g. a wave crest peeking above the water
|
|
442
|
+
// line) and we use the refraction fallback colour. This is the only
|
|
443
|
+
// way to detect screen-space-adjacent-but-3D-far bleed (which a pure
|
|
444
|
+
// depth ordering test can't catch).
|
|
445
|
+
clippingPlanes: clippingPlanes
|
|
446
|
+
}, function () {
|
|
447
|
+
// Clear BOTH color and depth. Color so any pixel the scene doesn't
|
|
448
|
+
// write stays alpha=0 for the shader's miss-detect; depth so the
|
|
449
|
+
// capture pass starts from a fresh Z-buffer each frame (otherwise
|
|
450
|
+
// stale depth values from the previous capture would block writes
|
|
451
|
+
// and produce flickering / missing geometry).
|
|
452
|
+
//
|
|
453
|
+
// This has to cover the WHOLE target even when a scissor follows —
|
|
454
|
+
// otherwise the region outside the rect keeps last frame's pixels and
|
|
455
|
+
// the consumer samples stale geometry instead of detecting a miss.
|
|
456
|
+
renderer.clear(true, true, false);
|
|
457
|
+
if (scissor) {
|
|
458
|
+
_this.rt.scissor.copy(scissor);
|
|
459
|
+
_this.rt.scissorTest = true;
|
|
460
|
+
renderer.setRenderTarget(_this.rt);
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
renderer.render(scene, camera);
|
|
464
|
+
} finally {
|
|
465
|
+
// Leave the scissor off so the next frame's clear covers everything even if that frame
|
|
466
|
+
// passes no rect — or if this draw threw, which would otherwise scissor the target
|
|
467
|
+
// permanently and defeat the alpha-0 miss detection.
|
|
468
|
+
_this.rt.scissorTest = false;
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
} finally {
|
|
472
|
+
// Restore AFTER the renderer restore so the main R3F render — which runs right after this
|
|
473
|
+
// useFrame returns — sees the proper scene graph, and in a `finally` so a throw inside the
|
|
474
|
+
// pass cannot leave the scene half-hidden or without its background.
|
|
475
|
+
scene.background = prevBackground;
|
|
476
|
+
for (var _i = 0, _hidden = hidden; _i < _hidden.length; _i++) {
|
|
477
|
+
var obj = _hidden[_i];
|
|
478
|
+
obj.visible = true;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}, {
|
|
483
|
+
key: "dispose",
|
|
484
|
+
value: function dispose() {
|
|
485
|
+
this.rt.dispose();
|
|
486
|
+
}
|
|
487
|
+
}]);
|
|
488
|
+
}();
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Capture resolution as a fraction of the canvas drawing buffer.
|
|
492
|
+
* Refraction is a blur and the shader samples with a normalized UV, so
|
|
493
|
+
* half-res costs no visible sharpness and quarters fill + bandwidth.
|
|
494
|
+
*/
|
|
495
|
+
var CAPTURE_RESOLUTION_SCALE = 0.5;
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Minimum frames between captures while something that affects the capture is
|
|
499
|
+
* actually changing. MUST stay 1 while the capture is sampled at the live
|
|
500
|
+
* camera pose: the surface is redrawn every frame, so a capture from an older
|
|
501
|
+
* pose makes the pool interior visibly lag behind the rim during orbits.
|
|
502
|
+
* Affordable because the pass is half-res, scissored to the pool's screen rect
|
|
503
|
+
* and volume-culled — raise it only if pose-lag is an acceptable trade.
|
|
504
|
+
*/
|
|
505
|
+
var CAPTURE_FRAME_INTERVAL = 1;
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Frames between captures when nothing we track has changed. The texture
|
|
509
|
+
* EXCLUDES the water surface (wave motion only moves the sampling UV) and is
|
|
510
|
+
* rendered with the caustic receivers SUSPENDED, so on a still camera over a
|
|
511
|
+
* still scene it is identical frame after frame.
|
|
512
|
+
*
|
|
513
|
+
* A self-heal interval, not the correctness mechanism — the dirty checks in
|
|
514
|
+
* `update` are. It bounds how long a mutation we DON'T track (a material
|
|
515
|
+
* tweak, a lighting slider) can leave the refraction stale.
|
|
516
|
+
*/
|
|
517
|
+
var CAPTURE_IDLE_FRAME_INTERVAL = 15;
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Extra world-metre margin above/below the water plane when fitting the
|
|
521
|
+
* capture's screen rect, so a wave crest (or trough) can't produce fragments
|
|
522
|
+
* outside the scissored region.
|
|
523
|
+
*/
|
|
524
|
+
var CAPTURE_WAVE_MARGIN = 0.4;
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Extra world-metre margin on the capture's SIDE clip planes. Pool geo at the
|
|
528
|
+
* basin edge (stair blocks, benches) pokes slightly outside the water
|
|
529
|
+
* footprint; clipping it makes those pixels capture misses that render as
|
|
530
|
+
* dark fallback-color patches.
|
|
531
|
+
*/
|
|
532
|
+
var CAPTURE_XZ_MARGIN = 0.25;
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Screen-UV padding on the scissor rect. Covers the camera motion on the
|
|
536
|
+
* frames BETWEEN two captures: the surface is redrawn every frame, so without
|
|
537
|
+
* this its leading edge would drift outside the stale rect during an orbit and
|
|
538
|
+
* sample the cleared (miss) region for a frame.
|
|
539
|
+
*/
|
|
540
|
+
var CAPTURE_SCISSOR_MOTION_PAD_UV = 0.03;
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Tolerance for "the camera did not move". Damped orbit controls approach
|
|
544
|
+
* their target asymptotically, so an exact matrix compare could stay
|
|
545
|
+
* marginally dirty forever; 1e-6 on view-projection elements is far below a
|
|
546
|
+
* sub-pixel change at any sane scene scale.
|
|
547
|
+
*/
|
|
548
|
+
var CAPTURE_VIEW_EPSILON = 1e-6;
|
|
549
|
+
|
|
550
|
+
/** Scratch state for the per-frame capture bookkeeping. */
|
|
551
|
+
var CAPTURE_BOX = new Box3();
|
|
552
|
+
var CAPTURE_FRUSTUM = new Frustum();
|
|
553
|
+
var CAPTURE_VIEW_PROJ = new Matrix4();
|
|
554
|
+
var CAPTURE_SCISSOR = new Vector4();
|
|
555
|
+
var CAPTURE_CORNER = new Vector4();
|
|
556
|
+
var CAPTURE_DRAWING_BUFFER = new Vector2();
|
|
557
|
+
/**
|
|
558
|
+
* Capture-throttle decision. A reallocated target is blank, so it can't wait
|
|
559
|
+
* out an interval — the surface would sample the cleared texture and flash
|
|
560
|
+
* the refraction fallback colour.
|
|
561
|
+
*/
|
|
562
|
+
function isCaptureDue(framesSinceCapture, dirty, targetResized) {
|
|
563
|
+
return targetResized || framesSinceCapture >= (dirty ? CAPTURE_FRAME_INTERVAL : CAPTURE_IDLE_FRAME_INTERVAL);
|
|
564
|
+
}
|
|
565
|
+
var RefractionCapture = /*#__PURE__*/function () {
|
|
566
|
+
function RefractionCapture() {
|
|
567
|
+
_classCallCheck(this, RefractionCapture);
|
|
568
|
+
_defineProperty(this, "capture", new SceneCapture());
|
|
569
|
+
/**
|
|
570
|
+
* Six planes installed on the renderer ONLY during the capture pass, so
|
|
571
|
+
* EVERY fragment outside the water volume — above water, below the pool
|
|
572
|
+
* floor, beside the pool — is discarded and the surface shader can detect
|
|
573
|
+
* the resulting alpha=0 pixels as capture misses.
|
|
574
|
+
*
|
|
575
|
+
* Three.js keeps a point P iff `dot(normal, P) + constant >= 0`, so every
|
|
576
|
+
* plane here points INWARD. Allocated once and mutated per frame;
|
|
577
|
+
* SceneCapture saves and restores `renderer.clippingPlanes` so they never
|
|
578
|
+
* leak into the main R3F render.
|
|
579
|
+
*/
|
|
580
|
+
_defineProperty(this, "clipPlanes", [new Plane(new Vector3(-1, 0, 0), 0),
|
|
581
|
+
// right face (normal points -X)
|
|
582
|
+
new Plane(new Vector3(1, 0, 0), 0),
|
|
583
|
+
// left face (normal points +X)
|
|
584
|
+
new Plane(new Vector3(0, -1, 0), 0),
|
|
585
|
+
// top face (normal points -Y)
|
|
586
|
+
new Plane(new Vector3(0, 1, 0), 0),
|
|
587
|
+
// bottom face (normal points +Y)
|
|
588
|
+
new Plane(new Vector3(0, 0, -1), 0),
|
|
589
|
+
// front face (normal points -Z)
|
|
590
|
+
new Plane(new Vector3(0, 0, 1), 0) // back face (normal points +Z)
|
|
591
|
+
]);
|
|
592
|
+
/**
|
|
593
|
+
* Frames since the last capture, and the state that capture was taken with.
|
|
594
|
+
* Together they answer "is the texture we already have still a correct
|
|
595
|
+
* picture of the scene?". Starts due so the first frame captures.
|
|
596
|
+
*/
|
|
597
|
+
_defineProperty(this, "framesSinceCapture", CAPTURE_IDLE_FRAME_INTERVAL);
|
|
598
|
+
_defineProperty(this, "capturedViewProj", new Matrix4());
|
|
599
|
+
_defineProperty(this, "capturedBox", new Box3());
|
|
600
|
+
_defineProperty(this, "capturedGeometryVersion", -1);
|
|
601
|
+
}
|
|
602
|
+
return _createClass(RefractionCapture, [{
|
|
603
|
+
key: "texture",
|
|
604
|
+
get: function get() {
|
|
605
|
+
return this.capture.texture;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Publish the refraction uniforms and re-render the capture if it is stale
|
|
610
|
+
* and the water is on screen. Call once per frame from the water frame
|
|
611
|
+
* stage, after the heightfield/caustics passes and before the composite.
|
|
612
|
+
*/
|
|
613
|
+
}, {
|
|
614
|
+
key: "update",
|
|
615
|
+
value: function update(frame) {
|
|
616
|
+
var renderer = frame.renderer,
|
|
617
|
+
camera = frame.camera,
|
|
618
|
+
material = frame.material,
|
|
619
|
+
volumeCenter = frame.volumeCenter,
|
|
620
|
+
poolHalf = frame.poolHalf;
|
|
621
|
+
|
|
622
|
+
// The renderer's drawing-buffer size = CSS size × pixelRatio; SceneCapture
|
|
623
|
+
// does that multiplication so it can decide whether a real resize is needed.
|
|
624
|
+
var targetResized = this.capture.resizeIfNeeded(frame.cssWidth, frame.cssHeight, renderer.getPixelRatio(), CAPTURE_RESOLUTION_SCALE);
|
|
625
|
+
material.uniforms.uSceneColor.value = this.capture.texture;
|
|
626
|
+
|
|
627
|
+
// uResolution is the INVERSE of the MAIN drawing-buffer extent — NOT the
|
|
628
|
+
// capture's. The shader builds its UV from `gl_FragCoord`, which is in
|
|
629
|
+
// canvas pixels when the surface is drawn; the UV is normalized, so a
|
|
630
|
+
// lower-resolution capture samples correctly and only costs sharpness.
|
|
631
|
+
renderer.getDrawingBufferSize(CAPTURE_DRAWING_BUFFER);
|
|
632
|
+
material.uniforms.uResolution.value.set(1 / Math.max(1, CAPTURE_DRAWING_BUFFER.x), 1 / Math.max(1, CAPTURE_DRAWING_BUFFER.y));
|
|
633
|
+
|
|
634
|
+
// Clip-plane constants for the current volume. With inward-pointing
|
|
635
|
+
// normals the constant is the box bound projected onto the negated normal:
|
|
636
|
+
// right face at x = cx + hX, normal (-1,0,0): constant = cx + hX
|
|
637
|
+
// left face at x = cx - hX, normal (+1,0,0): constant = hX - cx
|
|
638
|
+
// top face at y = cy, normal (0,-1,0): constant = cy
|
|
639
|
+
// bottom face at y = cy - hY, normal (0,+1,0): constant = hY - cy
|
|
640
|
+
// front face at z = cz + hZ, normal (0,0,-1): constant = cz + hZ
|
|
641
|
+
// back face at z = cz - hZ, normal (0,0,+1): constant = hZ - cz
|
|
642
|
+
var cx = volumeCenter.x;
|
|
643
|
+
var cy = volumeCenter.y;
|
|
644
|
+
var cz = volumeCenter.z;
|
|
645
|
+
var hX = poolHalf.x + CAPTURE_XZ_MARGIN;
|
|
646
|
+
var hY = poolHalf.y;
|
|
647
|
+
var hZ = poolHalf.z + CAPTURE_XZ_MARGIN;
|
|
648
|
+
this.clipPlanes[0].constant = cx + hX;
|
|
649
|
+
this.clipPlanes[1].constant = hX - cx;
|
|
650
|
+
this.clipPlanes[2].constant = cy;
|
|
651
|
+
this.clipPlanes[3].constant = hY - cy;
|
|
652
|
+
this.clipPlanes[4].constant = cz + hZ;
|
|
653
|
+
this.clipPlanes[5].constant = hZ - cz;
|
|
654
|
+
|
|
655
|
+
// Water volume in world space, with a margin for wave displacement.
|
|
656
|
+
// Doubles as the frustum-cull test and the source of the scissor rect.
|
|
657
|
+
CAPTURE_BOX.min.set(cx - hX, cy - hY - CAPTURE_WAVE_MARGIN, cz - hZ);
|
|
658
|
+
CAPTURE_BOX.max.set(cx + hX, cy + CAPTURE_WAVE_MARGIN, cz + hZ);
|
|
659
|
+
|
|
660
|
+
// The camera's world matrix is still last frame's at this point in the
|
|
661
|
+
// frameloop (`useCameraSmoothing` moved it at priority 1 and only
|
|
662
|
+
// `renderer.render` refreshes it), so bring it current before deriving the
|
|
663
|
+
// frustum — otherwise the scissor lags a frame behind the orbit.
|
|
664
|
+
camera.updateMatrixWorld();
|
|
665
|
+
CAPTURE_VIEW_PROJ.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
666
|
+
CAPTURE_FRUSTUM.setFromProjectionMatrix(CAPTURE_VIEW_PROJ);
|
|
667
|
+
|
|
668
|
+
// Does the texture we already hold still depict the scene correctly? It
|
|
669
|
+
// excludes the water and suspends the receiver caustics, so neither waves
|
|
670
|
+
// nor the dancing light invalidate it — only a moved camera, a moved
|
|
671
|
+
// volume, or changed scene content does.
|
|
672
|
+
var dirty = frame.sceneMutating || frame.geometryVersion !== this.capturedGeometryVersion || !CAPTURE_BOX.equals(this.capturedBox) || !matricesApproxEqual(CAPTURE_VIEW_PROJ, this.capturedViewProj, CAPTURE_VIEW_EPSILON);
|
|
673
|
+
this.framesSinceCapture += 1;
|
|
674
|
+
if (!isCaptureDue(this.framesSinceCapture, dirty, targetResized)) return;
|
|
675
|
+
// Off-frustum deliberately does NOT reset the age, so the capture
|
|
676
|
+
// refreshes on the first frame the water comes back into view.
|
|
677
|
+
if (!CAPTURE_FRUSTUM.intersectsBox(CAPTURE_BOX)) return;
|
|
678
|
+
|
|
679
|
+
// Restrict the pass to the water's screen footprint plus the camera-motion
|
|
680
|
+
// pad. Refraction is flat, so the shader samples the surface's own screen
|
|
681
|
+
// UV and needs no displacement pad on top of that.
|
|
682
|
+
var fitted = fitScissorToBox(CAPTURE_BOX, CAPTURE_VIEW_PROJ, this.capture.width, this.capture.height, CAPTURE_SCISSOR_MOTION_PAD_UV, CAPTURE_SCISSOR);
|
|
683
|
+
|
|
684
|
+
// Receivers are suspended for the capture: their caustic term animates
|
|
685
|
+
// every frame, which would (a) get double-applied once the surface shader
|
|
686
|
+
// composites the live pattern onto the sample and (b) make a still scene's
|
|
687
|
+
// captures differ frame to frame, defeating the idle throttle. CAPTURE_BOX
|
|
688
|
+
// doubles as the volume cull so only pool-intersecting meshes are drawn.
|
|
689
|
+
setCausticReceiversSuspended(true);
|
|
690
|
+
try {
|
|
691
|
+
this.capture.render(renderer, frame.scene, camera, [frame.surfaceMesh], this.clipPlanes, fitted ? CAPTURE_SCISSOR : null, CAPTURE_BOX);
|
|
692
|
+
} finally {
|
|
693
|
+
// A module singleton shared by every receiver program: a throw inside the capture would gate
|
|
694
|
+
// their caustics off for the rest of the session.
|
|
695
|
+
setCausticReceiversSuspended(false);
|
|
696
|
+
}
|
|
697
|
+
this.framesSinceCapture = 0;
|
|
698
|
+
this.capturedViewProj.copy(CAPTURE_VIEW_PROJ);
|
|
699
|
+
this.capturedBox.copy(CAPTURE_BOX);
|
|
700
|
+
this.capturedGeometryVersion = frame.geometryVersion;
|
|
701
|
+
}
|
|
702
|
+
}, {
|
|
703
|
+
key: "dispose",
|
|
704
|
+
value: function dispose() {
|
|
705
|
+
this.capture.dispose();
|
|
706
|
+
}
|
|
707
|
+
}]);
|
|
708
|
+
}();
|
|
709
|
+
|
|
710
|
+
/** Element-wise matrix compare with a tolerance. */
|
|
711
|
+
function matricesApproxEqual(a, b, epsilon) {
|
|
712
|
+
var ae = a.elements;
|
|
713
|
+
var be = b.elements;
|
|
714
|
+
for (var i = 0; i < 16; i++) {
|
|
715
|
+
if (Math.abs(ae[i] - be[i]) > epsilon) return false;
|
|
716
|
+
}
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Fit `box`'s screen-space extent into `out` (render-target pixels, GL
|
|
722
|
+
* bottom-left origin), padded by `marginUV` in each axis to cover the camera
|
|
723
|
+
* motion between two captures.
|
|
724
|
+
*
|
|
725
|
+
* Returns false when the box straddles the camera plane — a corner with
|
|
726
|
+
* w <= 0 projects to garbage, so the caller must fall back to capturing the
|
|
727
|
+
* full target rather than trusting a bogus rect.
|
|
728
|
+
*/
|
|
729
|
+
function fitScissorToBox(box, viewProj, width, height, marginUV, out) {
|
|
730
|
+
var minX = Infinity;
|
|
731
|
+
var minY = Infinity;
|
|
732
|
+
var maxX = -Infinity;
|
|
733
|
+
var maxY = -Infinity;
|
|
734
|
+
for (var i = 0; i < 8; i++) {
|
|
735
|
+
CAPTURE_CORNER.set(i & 1 ? box.max.x : box.min.x, i & 2 ? box.max.y : box.min.y, i & 4 ? box.max.z : box.min.z, 1).applyMatrix4(viewProj);
|
|
736
|
+
if (CAPTURE_CORNER.w <= 1e-6) return false;
|
|
737
|
+
var ndcX = CAPTURE_CORNER.x / CAPTURE_CORNER.w;
|
|
738
|
+
var ndcY = CAPTURE_CORNER.y / CAPTURE_CORNER.w;
|
|
739
|
+
if (ndcX < minX) minX = ndcX;
|
|
740
|
+
if (ndcX > maxX) maxX = ndcX;
|
|
741
|
+
if (ndcY < minY) minY = ndcY;
|
|
742
|
+
if (ndcY > maxY) maxY = ndcY;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// NDC [-1, 1] → pixels. The margin is in UV units (the shader offsets a
|
|
746
|
+
// normalized UV), so it scales by the target extent. +1 px covers the
|
|
747
|
+
// LinearFilter tap at the edge.
|
|
748
|
+
var padX = marginUV * width + 1;
|
|
749
|
+
var padY = marginUV * height + 1;
|
|
750
|
+
var x0 = Math.max(0, Math.floor((minX * 0.5 + 0.5) * width - padX));
|
|
751
|
+
var y0 = Math.max(0, Math.floor((minY * 0.5 + 0.5) * height - padY));
|
|
752
|
+
var x1 = Math.min(width, Math.ceil((maxX * 0.5 + 0.5) * width + padX));
|
|
753
|
+
var y1 = Math.min(height, Math.ceil((maxY * 0.5 + 0.5) * height + padY));
|
|
754
|
+
if (x1 <= x0 || y1 <= y0) return false;
|
|
755
|
+
out.set(x0, y0, x1 - x0, y1 - y0);
|
|
756
|
+
return true;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/*
|
|
760
|
+
* Water-surface shaders — port of evanw/webgl-water `renderer.js` (the
|
|
761
|
+
* realistic-water demo), refactored to operate ENTIRELY in WORLD METERS (no
|
|
762
|
+
* normalized-pool space inside the shaders).
|
|
763
|
+
*
|
|
764
|
+
* https://github.com/evanw/webgl-water/blob/master/renderer.js
|
|
765
|
+
*
|
|
766
|
+
* --- Coordinate system ---
|
|
767
|
+
*
|
|
768
|
+
* All `vWaterPos`, `point`, `eye`, `light`, etc. inside the
|
|
769
|
+
* shaders are in WORLD METERS, in the pool's local frame (origin at the
|
|
770
|
+
* water-surface centre, +Y up). The mapping to the demo's normalized
|
|
771
|
+
* `[-1, +1]³` cube is:
|
|
772
|
+
*
|
|
773
|
+
* world.xz = normalized.xz * uPoolHalf.xz
|
|
774
|
+
* world.y = normalized.y * uPoolHalf.y (negative = below surface)
|
|
775
|
+
*
|
|
776
|
+
* Hardcoded `±1` / `poolHeight = 1` literals from the demo have all been
|
|
777
|
+
* replaced with `uPoolHalf.{x,y,z}` (the pool's world half-extents — note
|
|
778
|
+
* `.y` is the FULL depth, since Y is asymmetric: surface at 0, floor at
|
|
779
|
+
* `-uPoolHalf.y`).
|
|
780
|
+
*
|
|
781
|
+
* The "rim height" literal `2.0/12.0` (= `poolHeight / 6.0` in the demo)
|
|
782
|
+
* becomes `uPoolHalf.y / 6.0`.
|
|
783
|
+
*
|
|
784
|
+
* --- Substitutions from the upstream GLSL ---
|
|
785
|
+
*
|
|
786
|
+
* 1. `texture2D(tiles, ...)` → `uWallColor` flat-color uniform (the pool
|
|
787
|
+
* carries no tile texture). Everything else in `getWallColor` (caustics
|
|
788
|
+
* multiplier, AO falloff, rim shadow) is preserved.
|
|
789
|
+
*
|
|
790
|
+
* 2. `textureCube(sky, ray).rgb` → `sampleProceduralSky(ray)`.
|
|
791
|
+
* We don't have a scene cubemap. `sampleProceduralSky` is a 2-color
|
|
792
|
+
* vertical gradient so the surface still has a sky to reflect into. The
|
|
793
|
+
* demo's sun-specular highlight is appended after it, identical to the
|
|
794
|
+
* demo.
|
|
795
|
+
*
|
|
796
|
+
* 3. `gl_Vertex` / `gl_ModelViewProjectionMatrix` → Three's `position`
|
|
797
|
+
* attribute and pre-bound `projectionMatrix * modelViewMatrix`. Two-line
|
|
798
|
+
* mechanical rename — no math changed.
|
|
799
|
+
*
|
|
800
|
+
* 4. `info.ba *= 0.5` (hard-coded caustic dampening in CAUSTICS_VERTEX)
|
|
801
|
+
* → `info.ba *= uCausticIntensity` uniform so the look is tunable per
|
|
802
|
+
* scene.
|
|
803
|
+
*
|
|
804
|
+
* 6. (new in this refactor) `uPoolHalf : vec3` everywhere; geometry in
|
|
805
|
+
* world meters; caustic projection scales by `uPoolHalf.xz` so the
|
|
806
|
+
* caustic texture's [-1,+1] clip range maps to the full pool floor.
|
|
807
|
+
*
|
|
808
|
+
* 7. Geometry-aware caustics: CAUSTICS_VERTEX `project()` lands rays on
|
|
809
|
+
* the real receiving geometry via a top-down height map
|
|
810
|
+
* (PoolHeightMap.ts), CAUSTICS_FRAGMENT stores the landing height in
|
|
811
|
+
* the blue channel, and consumers (receivers + SSR composite) occlude
|
|
812
|
+
* fragments below the surface that caught the light.
|
|
813
|
+
*/
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Core block — pasted into EVERY shader here (surface and caustics): the IOR
|
|
817
|
+
* constants, the uniforms both programs read and `intersectCube`.
|
|
818
|
+
*
|
|
819
|
+
* `uPoolHalf` semantics:
|
|
820
|
+
* .x — pool half-width in world meters (X axis half-extent)
|
|
821
|
+
* .y — pool full depth in world meters (distance from water surface to
|
|
822
|
+
* floor; water surface lives at local Y = 0, floor at Y = -uPoolHalf.y)
|
|
823
|
+
* .z — pool half-length in world meters (Z axis half-extent)
|
|
824
|
+
*/
|
|
825
|
+
var SHADER_CORE = /* glsl */"\n const float IOR_AIR = 1.0;\n const float IOR_WATER = 1.333;\n uniform vec3 light;\n uniform vec3 uPoolHalf; // (halfX, depth, halfZ) \u2014 see file header\n uniform sampler2D water;\n\n vec2 intersectCube(vec3 origin, vec3 ray, vec3 cubeMin, vec3 cubeMax) {\n vec3 tMin = (cubeMin - origin) / ray;\n vec3 tMax = (cubeMax - origin) / ray;\n vec3 t1 = min(tMin, tMax);\n vec3 t2 = max(tMin, tMax);\n float tNear = max(max(t1.x, t1.y), t1.z);\n float tFar = min(min(t2.x, t2.y), t2.z);\n return vec2(tNear, tFar);\n }\n";
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* GLSL for the decoded receiving-geometry height at a footprint position
|
|
829
|
+
* (volume-local Y): a `float <name>(vec2 xz)` reading `<sampler>`.
|
|
830
|
+
*
|
|
831
|
+
* The clamp keeps landings inside the water column — rim geo above the surface
|
|
832
|
+
* reads as a waterline hit, and sentinel texels (0 = cleared / no geo) decode to
|
|
833
|
+
* the analytic volume floor, so an empty map reproduces the flat-floor behaviour.
|
|
834
|
+
*/
|
|
835
|
+
function heightSamplerGLSL(name, sampler) {
|
|
836
|
+
return /* glsl */"\n float ".concat(name, "(vec2 xz) {\n float encoded = texture2D(").concat(sampler, ", xz / (2.0 * uPoolHalf.xz) + 0.5).r;\n return clamp(encoded * 3.0 * uPoolHalf.y - uPoolHalf.y, -uPoolHalf.y, 0.0);\n }\n");
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* GLSL for landing a straight ray on the pool's receiving geometry by marching
|
|
840
|
+
* it against the height map (the analytic AABB's faces do not coincide with the
|
|
841
|
+
* real wall/floor: square box vs rounded pool, stairs, benches).
|
|
842
|
+
*
|
|
843
|
+
* `coarseSteps` samples bound the first crossing and 4 bisection steps refine
|
|
844
|
+
* it; samples past a wall read the high rim value, so the coarse pass cannot
|
|
845
|
+
* tunnel through. Glancing rays can straddle EVERY coarse sample (stair
|
|
846
|
+
* envelopes especially) — those fall back to 3 fixed-point iterations onto the
|
|
847
|
+
* field, which land on the geometry instead of tunneling to the analytic cube
|
|
848
|
+
* exit (which would store the `b == 0` no-data sentinel).
|
|
849
|
+
*/
|
|
850
|
+
function heightFieldMarchGLSL(_ref) {
|
|
851
|
+
var name = _ref.name,
|
|
852
|
+
sampler = _ref.sampler,
|
|
853
|
+
coarseSteps = _ref.coarseSteps,
|
|
854
|
+
guardShallowRays = _ref.guardShallowRays;
|
|
855
|
+
var fixedPoint = ['for (int k = 0; k < 3; k++) {', " p += ray * ((".concat(sampler, "(p.xz) - p.y) / ray.y);"), '}'];
|
|
856
|
+
var fallback = guardShallowRays ? ['if (ray.y < -0.001) {'].concat(_toConsumableArray(fixedPoint.map(function (line) {
|
|
857
|
+
return " ".concat(line);
|
|
858
|
+
})), ['}']) : fixedPoint;
|
|
859
|
+
return /* glsl */"\n vec3 ".concat(name, "(vec3 origin, vec3 ray) {\n vec2 t = intersectCube(origin, ray, vec3(-uPoolHalf.x, -uPoolHalf.y, -uPoolHalf.z), vec3(uPoolHalf.x, 2.0 * uPoolHalf.y, uPoolHalf.z));\n float tEnd = max(t.y, 0.0);\n float lo = 0.0;\n float hi = tEnd;\n bool crossed = false;\n for (int i = 1; i <= ").concat(coarseSteps, "; i++) {\n float ti = tEnd * float(i) / ").concat(coarseSteps.toFixed(1), ";\n vec3 p = origin + ray * ti;\n if (p.y <= ").concat(sampler, "(p.xz)) {\n hi = ti;\n crossed = true;\n break;\n }\n lo = ti;\n }\n if (!crossed) {\n vec3 p = origin + ray * tEnd;\n").concat(fallback.map(function (line) {
|
|
860
|
+
return " ".concat(line);
|
|
861
|
+
}).join('\n'), "\n return p;\n }\n for (int j = 0; j < 4; j++) {\n float mid = 0.5 * (lo + hi);\n vec3 pm = origin + ray * mid;\n if (pm.y <= ").concat(sampler, "(pm.xz)) {\n hi = mid;\n } else {\n lo = mid;\n }\n }\n return origin + ray * (0.5 * (lo + hi));\n }\n");
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Surface-only block — the demo's remaining `helperFunctions` with all
|
|
866
|
+
* substitutions inline. The caustics programs never call any of it, so they
|
|
867
|
+
* get `SHADER_CORE` alone and none of these uniforms.
|
|
868
|
+
*/
|
|
869
|
+
var SURFACE_HELPERS = /* glsl */"\n uniform float uWaterClarity;\n uniform vec3 uWallColor; // MOD #1: replaces sampler2D tiles\n uniform vec3 uSkyHorizonColor; // MOD #2: drives sampleProceduralSky\n uniform vec3 uSkyZenithColor; // MOD #2: drives sampleProceduralSky\n uniform sampler2D causticTex;\n\n // MOD #2: replaces textureCube(sky, ray). Two-color vertical gradient.\n vec3 sampleProceduralSky(vec3 ray) {\n float t = clamp(ray.y * 0.5 + 0.5, 0.0, 1.0);\n return mix(uSkyHorizonColor, uSkyZenithColor, pow(t, 1.5));\n }\n\n // Sample the heightfield at a WORLD-METER xz position. The heightfield\n // texture's UV [0, 1]\xB2 spans the full pool footprint [-halfX, +halfX] \xD7\n // [-halfZ, +halfZ].\n vec4 sampleWaterAt(vec2 worldXZ) {\n return texture2D(water, worldXZ / (2.0 * uPoolHalf.xz) + 0.5);\n }\n\n // Sample the caustic texture for a point at WORLD-METER position. The\n // caustic texture indexes by \"where would the light from this point have\n // come from at the water surface\", projected back along refractedLight.\n // Mapping mirrors the caustics-vertex gl_Position write:\n // clip_xz = 0.75 * (point.xz - point.y * slope) / uPoolHalf.xz\n // Both vertex and fragment use this identical formula so reading and\n // writing into the texture are consistent across the full pool.\n vec4 sampleCausticAt(vec3 point, vec3 refractedLight) {\n vec2 lookup = (point.xz - point.y * refractedLight.xz / refractedLight.y) / uPoolHalf.xz;\n return texture2D(causticTex, 0.75 * lookup * 0.5 + 0.5);\n }\n\n vec3 getWallColor(vec3 point) {\n float scale = 0.5;\n\n vec3 wallColor;\n vec3 normal;\n // Wall detection: a tiny relative epsilon (0.1% of pool half-extent) so\n // we still recognise points sitting exactly on the wall geometry while\n // being tolerant of floating-point noise in the ray-march hit positions.\n if (abs(point.x) > uPoolHalf.x * 0.999) {\n wallColor = uWallColor; // MOD #1\n normal = vec3(-point.x, 0.0, 0.0);\n } else if (abs(point.z) > uPoolHalf.z * 0.999) {\n wallColor = uWallColor; // MOD #1\n normal = vec3(0.0, 0.0, -point.z);\n } else {\n wallColor = uWallColor; // MOD #1\n normal = vec3(0.0, 1.0, 0.0);\n }\n\n scale /= mix(length(point), 1.0, uWaterClarity); /* pool ambient occlusion */\n\n /* caustics */\n vec3 refractedLight = -refract(-light, vec3(0.0, 1.0, 0.0), IOR_AIR / IOR_WATER);\n float diffuse = max(0.0, dot(refractedLight, normal));\n vec4 info = sampleWaterAt(point.xz);\n if (point.y < info.r) {\n vec4 caustic = sampleCausticAt(point, refractedLight);\n scale += diffuse * caustic.r * 2.0 * caustic.g;\n } else {\n /* shadow for the rim of the pool */\n vec2 t = intersectCube(point, refractedLight, vec3(-uPoolHalf.x, -uPoolHalf.y, -uPoolHalf.z), vec3(uPoolHalf.x, 2.0 * uPoolHalf.y, uPoolHalf.z));\n diffuse *= 1.0 / (1.0 + exp(-200.0 / (1.0 + 10.0 * (t.y - t.x)) * (point.y + refractedLight.y * t.y - uPoolHalf.y / 6.0)));\n\n scale += diffuse * 0.5;\n }\n\n return wallColor * scale;\n }\n";
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Water surface vertex shader.
|
|
873
|
+
*
|
|
874
|
+
* Geometry is pre-rotated into XZ (`rotateX(PI/2)` at construction) so AO
|
|
875
|
+
* override passes see a horizontal plane. position.xz = pool XZ in world
|
|
876
|
+
* meters; position.y = 0. Wave height displaces Y.
|
|
877
|
+
*/
|
|
878
|
+
var WATER_SURFACE_VERTEX = /* glsl */"\n uniform sampler2D water;\n uniform vec3 uPoolHalf;\n varying vec3 vWaterPos;\n void main() {\n vec4 info = texture2D(water, position.xz / (2.0 * uPoolHalf.xz) + 0.5);\n vWaterPos = vec3(position.x, info.r, position.z);\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position.x, info.r, position.z, 1.0);\n }\n";
|
|
879
|
+
|
|
880
|
+
/** Water surface fragment shader — above-water branch. */
|
|
881
|
+
var WATER_SURFACE_FRAGMENT_ABOVE = /* glsl */"\n ".concat(SHADER_CORE, "\n ").concat(SURFACE_HELPERS, "\n uniform vec3 eye;\n // Screen-space refraction inputs. The host renders the scene minus the\n // water surface itself to uSceneColor once per frame (SceneCapture.ts);\n // the surface then samples that texture at its own screen UV. This\n // replaces the demo's in-shader intersectCube + getWallColor AABB\n // raytrace so the underwater view shows the ACTUAL pool geo in the scene.\n uniform sampler2D uSceneColor;\n uniform vec2 uResolution;\n uniform float uUseScreenSpaceRefraction;\n uniform vec3 uRefractionFallbackColor;\n // Grazing-angle ceiling of the Schlick fresnel, in [fresnelBase, 1.0]. The\n // standard formula reaches 1.0 at cosI = 0, which on steep wave fronts reads\n // as harsh bright streaks (env.water.fresnelMax).\n uniform float uFresnelMax;\n // MOD #7 debug views (0 in production):\n // 4 = LANDING \u2014 ramp of the landing height the caustic composite\n // anchors to at this pixel (caustic texture .b at the marched\n // view-ray hit). Downstream of the height map AND caustics pass.\n // 5 = HEIGHTMAP \u2014 the raw PoolHeightMap capture painted over the pool\n // footprint. Upstream: exactly what the top-down pass recorded.\n uniform float uCausticDebugMode;\n // Pool receiving-geometry height map (PoolHeightMap.ts) \u2014 PRODUCTION\n // input: the SSR composite marches the view ray against it to find the\n // displayed wall/floor point. Also drives debug view 5.\n uniform sampler2D uPoolHeightMap;\n varying vec3 vWaterPos;\n\n // Landing-height debug ramp. Magenta = no data (encoded 0: cleared height\n // map texel, or a caustic ray that landed at the analytic volume floor);\n // blue = volume floor, green = mid-depth, red = waterline.\n vec3 rtvLandingRamp(float encoded) {\n if (encoded < 0.002) return vec3(1.0, 0.0, 1.0);\n float t = clamp(encoded * 3.0, 0.0, 1.0);\n return t < 0.5 ? mix(vec3(0.0, 0.2, 1.0), vec3(0.0, 1.0, 0.2), t * 2.0) : mix(vec3(0.0, 1.0, 0.2), vec3(1.0, 0.1, 0.0), t * 2.0 - 1.0);\n }\n\n ").concat(heightSamplerGLSL('rtvSampleHeight', 'uPoolHeightMap'), "\n\n // Height-field normal from central differences. Steep wall bands in the\n // map tilt it toward horizontal, so walls get a proper light-incidence\n // term without any analytic-AABB face detection.\n vec3 rtvHeightFieldNormal(vec2 xz) {\n float eps = uPoolHalf.x / 128.0;\n float hxp = rtvSampleHeight(xz + vec2(eps, 0.0));\n float hxm = rtvSampleHeight(xz - vec2(eps, 0.0));\n float hzp = rtvSampleHeight(xz + vec2(0.0, eps));\n float hzm = rtvSampleHeight(xz - vec2(0.0, eps));\n return normalize(vec3(hxm - hxp, 2.0 * eps, hzm - hzp));\n }\n\n /* MOD #7 \u2014 find the displayed geometry point along the straight view ray by\n marching it against the pool height map. Runs per FRAGMENT, hence the\n coarser 12-step pass; see heightFieldMarchGLSL for the algorithm. */\n ").concat(heightFieldMarchGLSL({
|
|
882
|
+
name: 'rtvMarchViewRay',
|
|
883
|
+
sampler: 'rtvSampleHeight',
|
|
884
|
+
coarseSteps: 12,
|
|
885
|
+
guardShallowRays: true
|
|
886
|
+
}), "\n\n /*\n * Screen-space refraction sampler.\n *\n * gl_FragCoord.xy * uResolution (with uResolution = 1/drawingBuffer)\n * gives the current fragment's screen UV \u2014 the same UV the SceneCapture\n * target was written with, and the UV we sample at. Refraction is flat:\n * the sample is never displaced by the wave normal.\n *\n * The SceneCapture pass clips everything outside the water bbox via\n * six clipping planes installed on the renderer, so any fragment of\n * geometry that sits outside the water volume (above, below, beside\n * the pool) is discarded \u2014 those screen pixels stay at the cleared\n * alpha = 0 in the capture.\n *\n * When the sample is alpha=0 (or near-black, since a cleared capture\n * pixel reads as opaque-but-zero on some drivers) \u2014 a tall wave crest\n * peeking above the original waterline, so behind it on screen there's\n * no captured geo \u2014 return uRefractionFallbackColor, a host-configurable\n * deep-water tint surfaced as a Leva colour picker.\n */\n // Treat cleared / clipped capture pixels as a miss even if alpha is wrong.\n bool rtvSceneCaptureMissed(vec4 s) {\n return s.a < 0.5 || (s.r + s.g + s.b) < 0.02;\n }\n\n vec3 sampleScreenSpaceRefraction(vec3 viewRay, vec3 waterColor) {\n vec2 sampleUV = gl_FragCoord.xy * uResolution;\n vec4 sceneSample = texture2D(uSceneColor, sampleUV);\n\n // Live caustic composite. The capture is rendered with the caustic\n // receivers SUSPENDED (see setCausticReceiversSuspended) so still-scene\n // captures stay byte-identical for the throttle; the animated caustic\n // term is applied here every frame instead.\n //\n // MOD #7 \u2014 the hit marches the STRAIGHT view ray, not the refracted\n // one: the capture is an unrefracted straight-view render sampled at\n // the fragment's own screen UV, so the geometry displayed at this\n // fragment lies along the camera ray.\n float causticBoost = 1.0;\n vec3 refractedLight = -refract(-light, vec3(0.0, 1.0, 0.0), IOR_AIR / IOR_WATER);\n vec3 hit = rtvMarchViewRay(vWaterPos, viewRay);\n vec3 hitNormal = rtvHeightFieldNormal(hit.xz);\n float diffuseTerm = max(0.0, dot(refractedLight, hitNormal));\n vec4 info = sampleWaterAt(hit.xz);\n if (hit.y < info.r) {\n vec4 caustic = sampleCausticAt(hit, refractedLight);\n // Landing-height shadow, same rule as the receivers: a hit clearly\n // below the ray's landing is behind the surface that caught the\n // light. b == 0 is the no-data sentinel \u2192 no occlusion.\n float landingY = caustic.b * 3.0 * uPoolHalf.y - uPoolHalf.y;\n float occlusion = 1.0 - step(0.002, caustic.b) * smoothstep(0.05, 0.2, landingY - hit.y);\n // Hard ceiling on the contribution: at concave edges the tilted\n // height-field normal and the fold-compressed caustic can multiply\n // into an outline far brighter than the open-floor pattern.\n causticBoost += min(diffuseTerm * caustic.r * 2.0 * caustic.g * occlusion, 0.8);\n }\n\n // The fallback gets the boost too so a caustic band doesn't cut off\n // abruptly at the valid-sample/fallback boundary.\n if (rtvSceneCaptureMissed(sceneSample)) {\n return uRefractionFallbackColor * causticBoost;\n }\n return sceneSample.rgb * causticBoost * waterColor;\n }\n\n vec3 getSurfaceRayColor(vec3 origin, vec3 ray, vec3 waterColor) {\n vec3 color;\n if (ray.y < 0.0) {\n vec2 t = intersectCube(origin, ray, vec3(-uPoolHalf.x, -uPoolHalf.y, -uPoolHalf.z), vec3(uPoolHalf.x, 2.0 * uPoolHalf.y, uPoolHalf.z));\n color = getWallColor(origin + ray * t.y);\n } else {\n vec2 t = intersectCube(origin, ray, vec3(-uPoolHalf.x, -uPoolHalf.y, -uPoolHalf.z), vec3(uPoolHalf.x, 2.0 * uPoolHalf.y, uPoolHalf.z));\n vec3 hit = origin + ray * t.y;\n if (hit.y < uPoolHalf.y / 6.0) {\n color = getWallColor(hit);\n } else {\n color = sampleProceduralSky(ray); // MOD #2\n color += vec3(pow(max(0.0, dot(light, ray)), 5000.0)) * vec3(10.0, 8.0, 6.0);\n }\n }\n if (ray.y < 0.0) color *= waterColor;\n return color;\n }\n\n void main() {\n vec2 coord = vWaterPos.xz / (2.0 * uPoolHalf.xz) + 0.5;\n vec4 info = texture2D(water, coord);\n\n // HEIGHTMAP debug view \u2014 the raw PoolHeightMap capture over the pool\n // footprint (same UV the caustics pass samples with).\n if (uCausticDebugMode > 4.5) {\n gl_FragColor = vec4(rtvLandingRamp(texture2D(uPoolHeightMap, coord).r), 1.0);\n return;\n }\n\n /* make water look more \"peaked\" */\n for (int i = 0; i < 5; i++) {\n coord += info.ba * 0.005;\n info = texture2D(water, coord);\n }\n\n vec3 normal = vec3(info.b, sqrt(max(0.0, 1.0 - dot(info.ba, info.ba))), info.a);\n vec3 incomingRay = normalize(vWaterPos - eye);\n\n /* above water */\n vec3 reflectedRay = reflect(incomingRay, normal);\n vec3 refractedRay = refract(incomingRay, normal, IOR_AIR / IOR_WATER);\n\n // LANDING debug view \u2014 the landing height the composite uses at this\n // pixel, computed with the same view-ray march as production.\n if (uCausticDebugMode > 3.5) {\n vec3 dbgRefractedLight = -refract(-light, vec3(0.0, 1.0, 0.0), IOR_AIR / IOR_WATER);\n vec3 dbgHit = rtvMarchViewRay(vWaterPos, incomingRay);\n gl_FragColor = vec4(rtvLandingRamp(sampleCausticAt(dbgHit, dbgRefractedLight).b), 1.0);\n return;\n }\n\n float fresnelBase = mix(0.25, 0.05, uWaterClarity);\n // Schlick fresnel. CRITICAL: clamp the cos-incidence term to [0, 1]\n // BEFORE the pow(). On steep wave fronts dot(normal, -incomingRay)\n // can slip slightly above 1.0 (fp error) \u2014 then 1.0 - dot becomes\n // negative and pow(negative, 3.0) is undefined in GLSL \u2192 NaN.\n // The NaN propagates into fresnel and into mix(refracted, reflected,\n // fresnel), and the framebuffer write clamps NaN to 0, producing\n // pure-black wave-front streaks. The clamp keeps fresnel a well-\n // defined float in [fresnelBase, uFresnelMax] everywhere.\n float cosI = clamp(dot(normal, -incomingRay), 0.0, 1.0);\n float fresnel = mix(fresnelBase, uFresnelMax, pow(1.0 - cosI, 3.0));\n\n vec3 abovewaterColor = mix(vec3(0.25, 1.0, 1.25), vec3(1.0), uWaterClarity);\n vec3 reflectedColor = getSurfaceRayColor(vWaterPos, reflectedRay, abovewaterColor);\n // Refraction path: screen-space sample of the captured scene texture when\n // uUseScreenSpaceRefraction is on, falling back to the demo's in-shader\n // AABB raytrace otherwise. Both paths share the same abovewaterColor tint\n // so toggling between them doesn't shift the overall hue.\n vec3 refractedColor;\n if (uUseScreenSpaceRefraction > 0.5) {\n refractedColor = sampleScreenSpaceRefraction(incomingRay, abovewaterColor);\n } else {\n refractedColor = getSurfaceRayColor(vWaterPos, refractedRay, abovewaterColor);\n }\n\n gl_FragColor = vec4(mix(refractedColor, reflectedColor, fresnel), 1.0);\n }\n");
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Caustics vertex/fragment.
|
|
890
|
+
*
|
|
891
|
+
* The caustic-mesh plane is sized `2*halfX × 2*halfZ` in world meters so
|
|
892
|
+
* `position.xy` here is the WORLD XZ of a water-surface point. The
|
|
893
|
+
* projection logic refracts the (world-space) light direction through the
|
|
894
|
+
* wave normal and lands on the pool floor at world Y = -uPoolHalf.y.
|
|
895
|
+
*
|
|
896
|
+
* `gl_Position.xy` is written in caustic-clip space (range [-1, +1]) using
|
|
897
|
+
* the SAME `(p.xz - p.y * slope) / uPoolHalf.xz` formula the fragment
|
|
898
|
+
* lookup uses, so write and read are consistent at any pool aspect.
|
|
899
|
+
*
|
|
900
|
+
* MOD #4: `info.ba *= 0.5` → `info.ba *= uCausticIntensity` so the strength
|
|
901
|
+
* of the wave-normal contribution is tunable.
|
|
902
|
+
*/
|
|
903
|
+
var CAUSTICS_VERTEX = /* glsl */"\n ".concat(SHADER_CORE, "\n uniform float uCausticIntensity; // MOD #4\n // MOD #7 \u2014 receiving-geometry height map (PoolHeightMap.ts). R channel:\n // volume-local height as (y + poolHalf.y) / (3 * poolHalf.y); the cleared\n // texture (0) decodes to the floor plane.\n uniform sampler2D uHeightMap;\n uniform float uHeightMapEnabled;\n varying vec3 oldPos;\n varying vec3 newPos;\n varying vec3 vRay;\n varying float vEdgeFade;\n\n ").concat(heightSamplerGLSL('causticSampleHeight', 'uHeightMap'), "\n\n /* Runs per VERTEX of the caustic mesh, so it affords a 24-step coarse pass;\n see heightFieldMarchGLSL for the algorithm. */\n ").concat(heightFieldMarchGLSL({
|
|
904
|
+
name: 'causticMarchLightRay',
|
|
905
|
+
sampler: 'causticSampleHeight',
|
|
906
|
+
coarseSteps: 24,
|
|
907
|
+
guardShallowRays: false
|
|
908
|
+
}), "\n\n /* Land the ray on the receiving geometry. Without a height map: the\n demo's AABB clip + flat-floor projection. With one: march the ray\n against the height map \u2014 walls, ROUNDED CORNERS and stairs all\n terminate rays at their real position. (The analytic AABB attributes\n corner rays to the wrong wall face: square box vs rounded pool.)\n The caustic-clip lookup is invariant to where along the ray we stop. */\n vec3 project(vec3 origin, vec3 ray, vec3 refractedLight) {\n if (uHeightMapEnabled < 0.5) {\n vec2 tcube = intersectCube(origin, ray, vec3(-uPoolHalf.x, -uPoolHalf.y, -uPoolHalf.z), vec3(uPoolHalf.x, 2.0 * uPoolHalf.y, uPoolHalf.z));\n origin += ray * tcube.y;\n float tplane = (-origin.y - uPoolHalf.y) / refractedLight.y;\n return origin + refractedLight * tplane;\n }\n return causticMarchLightRay(origin, ray);\n }\n\n void main() {\n vec4 info = texture2D(water, position.xy / (2.0 * uPoolHalf.xz) + 0.5);\n info.ba *= uCausticIntensity; // MOD #4: was *= 0.5\n vec3 normal = vec3(info.b, sqrt(max(0.0, 1.0 - dot(info.ba, info.ba))), info.a);\n\n /* project the vertices along the refracted vertex ray */\n vec3 refractedLight = refract(-light, vec3(0.0, 1.0, 0.0), IOR_AIR / IOR_WATER);\n vRay = refract(-light, normal, IOR_AIR / IOR_WATER);\n oldPos = project(position.xzy, refractedLight, refractedLight);\n newPos = project(position.xzy + vec3(0.0, info.r, 0.0), vRay, refractedLight);\n\n // Concave-edge fade. Grazing light makes the landing mapping\n // near-singular at riser bases and wall coves \u2014 wave-normal jitter\n // funnels rays onto the exact corner line, which the area-ratio math\n // rewards with harsh max-brightness dashes. Fade landings that are\n // FLAT at their own position (small-radius gradient) but have a STEEP\n // feature nearby (large-radius gradient) \u2014 the signature of a concave\n // base line. Wall-FACE landings are steep at both radii and therefore\n // never faded; open floor is flat at both and also untouched.\n vEdgeFade = 1.0;\n if (uHeightMapEnabled > 0.5) {\n float epsNear = uPoolHalf.x / 128.0;\n float epsFar = uPoolHalf.x / 24.0;\n float gxN = causticSampleHeight(newPos.xz + vec2(epsNear, 0.0)) - causticSampleHeight(newPos.xz - vec2(epsNear, 0.0));\n float gzN = causticSampleHeight(newPos.xz + vec2(0.0, epsNear)) - causticSampleHeight(newPos.xz - vec2(0.0, epsNear));\n float gxF = causticSampleHeight(newPos.xz + vec2(epsFar, 0.0)) - causticSampleHeight(newPos.xz - vec2(epsFar, 0.0));\n float gzF = causticSampleHeight(newPos.xz + vec2(0.0, epsFar)) - causticSampleHeight(newPos.xz - vec2(0.0, epsFar));\n float flatHere = 1.0 - smoothstep(0.2, 0.6, length(vec2(gxN, gzN)) / (2.0 * epsNear));\n float steepNearby = smoothstep(0.25, 0.6, length(vec2(gxF, gzF)) / (2.0 * epsFar));\n vEdgeFade = 1.0 - steepNearby * flatHere;\n }\n\n // Caustic-clip output mirrors sampleCausticAt exactly: same projection,\n // same uPoolHalf.xz divide, same 0.75 margin. With slope = 0 (vertical\n // sun) this lands the pool floor [-uPoolHalf.xz, +uPoolHalf.xz] in\n // caustic-clip [-0.75, +0.75], leaving a 25% border for slanted-sun\n // projection drift.\n vec2 lookup = (newPos.xz - newPos.y * refractedLight.xz / refractedLight.y) / uPoolHalf.xz;\n gl_Position = vec4(0.75 * lookup, 0.0, 1.0);\n }\n");
|
|
909
|
+
|
|
910
|
+
// Three's ShaderMaterial prepends the GL_OES_standard_derivatives extension
|
|
911
|
+
// directive when `material.extensions.derivatives = true`, so we omit it here.
|
|
912
|
+
var CAUSTICS_FRAGMENT = /* glsl */"\n ".concat(SHADER_CORE, "\n varying vec3 oldPos;\n varying vec3 newPos;\n varying vec3 vRay;\n varying float vEdgeFade;\n\n void main() {\n /* if the triangle gets smaller, it gets brighter, and vice versa */\n float oldArea = length(dFdx(oldPos)) * length(dFdy(oldPos));\n float newArea = length(dFdx(newPos)) * length(dFdy(newPos));\n // vEdgeFade suppresses the fold-caustic line at concave height-field\n // edges (riser bases, wall coves) \u2014 see CAUSTICS_VERTEX.\n gl_FragColor = vec4(oldArea / newArea * 0.2 * vEdgeFade, 1.0, 0.0, 0.0);\n\n vec3 refractedLight = refract(-light, vec3(0.0, 1.0, 0.0), IOR_AIR / IOR_WATER);\n\n /* shadow for the rim of the pool */\n vec2 t = intersectCube(newPos, -refractedLight, vec3(-uPoolHalf.x, -uPoolHalf.y, -uPoolHalf.z), vec3(uPoolHalf.x, 2.0 * uPoolHalf.y, uPoolHalf.z));\n gl_FragColor.r *= 1.0 / (1.0 + exp(-200.0 / (1.0 + 10.0 * (t.y - t.x)) * (newPos.y - refractedLight.y * t.y - uPoolHalf.y / 6.0)));\n\n // MOD #7 \u2014 landing height into the blue channel (same normalization as\n // the height map). Consumers occlude fragments clearly below it.\n gl_FragColor.b = clamp((newPos.y + uPoolHalf.y) / (3.0 * uPoolHalf.y), 0.0, 1.0);\n }\n");
|
|
913
|
+
var WATER_SURFACE_SHADER_ABOVE = {
|
|
914
|
+
vertex: WATER_SURFACE_VERTEX,
|
|
915
|
+
fragment: WATER_SURFACE_FRAGMENT_ABOVE
|
|
916
|
+
};
|
|
917
|
+
var CAUSTICS_SHADER = {
|
|
918
|
+
vertex: CAUSTICS_VERTEX,
|
|
919
|
+
fragment: CAUSTICS_FRAGMENT
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
923
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
924
|
+
function buildSharedUniformValues(initial) {
|
|
925
|
+
var _initial$light, _initial$water, _initial$causticTex, _initial$uPoolHalf, _initial$uWallColor, _initial$uSkyHorizonC, _initial$uSkyZenithCo, _initial$uWaterClarit;
|
|
926
|
+
return {
|
|
927
|
+
light: {
|
|
928
|
+
value: ((_initial$light = initial.light) !== null && _initial$light !== void 0 ? _initial$light : new Vector3(0.7071, 0.7071, 0.0)).clone()
|
|
929
|
+
},
|
|
930
|
+
water: {
|
|
931
|
+
value: (_initial$water = initial.water) !== null && _initial$water !== void 0 ? _initial$water : null
|
|
932
|
+
},
|
|
933
|
+
causticTex: {
|
|
934
|
+
value: (_initial$causticTex = initial.causticTex) !== null && _initial$causticTex !== void 0 ? _initial$causticTex : null
|
|
935
|
+
},
|
|
936
|
+
// (halfX, depth, halfZ) — pool half-extents in WORLD METERS.
|
|
937
|
+
// Default reproduces the demo's unit-cube behaviour.
|
|
938
|
+
uPoolHalf: {
|
|
939
|
+
value: ((_initial$uPoolHalf = initial.uPoolHalf) !== null && _initial$uPoolHalf !== void 0 ? _initial$uPoolHalf : new Vector3(1, 1, 1)).clone()
|
|
940
|
+
},
|
|
941
|
+
uWallColor: {
|
|
942
|
+
value: ((_initial$uWallColor = initial.uWallColor) !== null && _initial$uWallColor !== void 0 ? _initial$uWallColor : new Vector3(0.7, 0.78, 0.85)).clone()
|
|
943
|
+
},
|
|
944
|
+
uSkyHorizonColor: {
|
|
945
|
+
value: ((_initial$uSkyHorizonC = initial.uSkyHorizonColor) !== null && _initial$uSkyHorizonC !== void 0 ? _initial$uSkyHorizonC : new Vector3(0.78, 0.85, 0.95)).clone()
|
|
946
|
+
},
|
|
947
|
+
uSkyZenithColor: {
|
|
948
|
+
value: ((_initial$uSkyZenithCo = initial.uSkyZenithColor) !== null && _initial$uSkyZenithCo !== void 0 ? _initial$uSkyZenithCo : new Vector3(0.18, 0.36, 0.78)).clone()
|
|
949
|
+
},
|
|
950
|
+
uWaterClarity: {
|
|
951
|
+
value: (_initial$uWaterClarit = initial.uWaterClarity) !== null && _initial$uWaterClarit !== void 0 ? _initial$uWaterClarit : 0
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
function createWaterSurfaceAboveMaterial() {
|
|
956
|
+
var _initial$eye;
|
|
957
|
+
var initial = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
958
|
+
return new ShaderMaterial({
|
|
959
|
+
vertexShader: WATER_SURFACE_SHADER_ABOVE.vertex,
|
|
960
|
+
fragmentShader: WATER_SURFACE_SHADER_ABOVE.fragment,
|
|
961
|
+
uniforms: _objectSpread(_objectSpread({}, buildSharedUniformValues(initial)), {}, {
|
|
962
|
+
eye: {
|
|
963
|
+
value: ((_initial$eye = initial.eye) !== null && _initial$eye !== void 0 ? _initial$eye : new Vector3(0, 5, 5)).clone()
|
|
964
|
+
},
|
|
965
|
+
// Screen-space refraction source: the scene minus the water surface,
|
|
966
|
+
// captured each frame by SceneCapture.ts. Sampled at the fragment's screen
|
|
967
|
+
// UV, so the underwater view shows the ACTUAL pool geo instead of the
|
|
968
|
+
// demo's synthetic AABB walls.
|
|
969
|
+
uSceneColor: {
|
|
970
|
+
value: null
|
|
971
|
+
},
|
|
972
|
+
// Drawing-buffer pixel size — used to convert `gl_FragCoord.xy` into a
|
|
973
|
+
// [0, 1] UV for the scene-color sample. WaterSurfaceRenderer syncs
|
|
974
|
+
// this against the live canvas size on every resize.
|
|
975
|
+
uResolution: {
|
|
976
|
+
value: new Vector2(1, 1)
|
|
977
|
+
},
|
|
978
|
+
// 1.0 → screen-space refraction (SceneCapture); 0.0 → AABB raytrace.
|
|
979
|
+
uUseScreenSpaceRefraction: {
|
|
980
|
+
value: 0.0
|
|
981
|
+
},
|
|
982
|
+
// Returned by sampleScreenSpaceRefraction when the sampled UV lands on a
|
|
983
|
+
// clipped / empty capture pixel (tall wave crests). Live value comes from
|
|
984
|
+
// env.water.refractionFallbackColor.
|
|
985
|
+
uRefractionFallbackColor: {
|
|
986
|
+
value: new Vector3(0.039, 0.145, 0.251)
|
|
987
|
+
},
|
|
988
|
+
// Grazing-angle ceiling of the Schlick fresnel (env.water.fresnelMax).
|
|
989
|
+
uFresnelMax: {
|
|
990
|
+
value: 1.0
|
|
991
|
+
},
|
|
992
|
+
// MOD #7 debug views (4 = landing-height ramp, 5 = raw height map);
|
|
993
|
+
// 0 in production. These live in the surface shader because receiver
|
|
994
|
+
// debug modes never reach the screen underwater in SSR mode.
|
|
995
|
+
uCausticDebugMode: {
|
|
996
|
+
value: 0.0
|
|
997
|
+
},
|
|
998
|
+
// Pool receiving-geometry height map — production input for the SSR
|
|
999
|
+
// composite's view-ray march; bound every frame by the host.
|
|
1000
|
+
uPoolHeightMap: {
|
|
1001
|
+
value: null
|
|
1002
|
+
}
|
|
1003
|
+
}),
|
|
1004
|
+
transparent: false,
|
|
1005
|
+
// DoubleSide so the surface is visible from both above and below the water
|
|
1006
|
+
// plane. Single-sided FrontSide can cause one side of the surface to vanish
|
|
1007
|
+
// depending on camera angle (visible as a missing-quadrant artifact).
|
|
1008
|
+
side: DoubleSide
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/**
|
|
1013
|
+
* Update the shared uniforms in one go. Pass whichever subset changed this
|
|
1014
|
+
* frame; everything else is left alone.
|
|
1015
|
+
*/
|
|
1016
|
+
function updateSharedUniforms(mat, patch) {
|
|
1017
|
+
var u = mat.uniforms;
|
|
1018
|
+
if (patch.light) u.light.value.copy(patch.light);
|
|
1019
|
+
if (patch.water !== undefined) u.water.value = patch.water;
|
|
1020
|
+
if (patch.causticTex !== undefined) u.causticTex.value = patch.causticTex;
|
|
1021
|
+
if (patch.uPoolHalf) u.uPoolHalf.value.copy(patch.uPoolHalf);
|
|
1022
|
+
if (patch.uWallColor) u.uWallColor.value.copy(patch.uWallColor);
|
|
1023
|
+
if (patch.uSkyHorizonColor) u.uSkyHorizonColor.value.copy(patch.uSkyHorizonColor);
|
|
1024
|
+
if (patch.uSkyZenithColor) u.uSkyZenithColor.value.copy(patch.uSkyZenithColor);
|
|
1025
|
+
if (patch.uWaterClarity !== undefined) u.uWaterClarity.value = patch.uWaterClarity;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
var VERTEX_SHADER = /* glsl */"\n varying vec2 coord;\n void main() {\n coord = position.xy * 0.5 + 0.5;\n gl_Position = vec4(position.xyz, 1.0);\n }\n";
|
|
1029
|
+
|
|
1030
|
+
// NOTE on the `tSrc` uniform name:
|
|
1031
|
+
// The demo names this uniform `texture`. That works in GLSL ES 1.00 (WebGL1)
|
|
1032
|
+
// but `texture` is a reserved keyword in GLSL ES 3.00, which is what Three.js
|
|
1033
|
+
// v0.176 emits by default for ShaderMaterial in WebGL2. With the demo's name
|
|
1034
|
+
// the kernel fragments fail to compile silently and the heightfield never
|
|
1035
|
+
// updates. Renaming to `tSrc` is the minimal change that keeps the rest of
|
|
1036
|
+
// the GLSL character-for-character verbatim.
|
|
1037
|
+
|
|
1038
|
+
// MOD #A1: world-meter distance in the drop falloff.
|
|
1039
|
+
//
|
|
1040
|
+
// The demo's pool is a unit cube so a UV-space drop circle is also a circle
|
|
1041
|
+
// in world space. Our pools are rectangular boxes (e.g. 2×10) and the
|
|
1042
|
+
// heightfield's UV [0,1]² spans a rectangle in world meters, so a
|
|
1043
|
+
// UV-distance circle is an ellipse in world. To keep a "circular drop in
|
|
1044
|
+
// world space" property, we convert both the texel position and the drop
|
|
1045
|
+
// center into world meters (via the `poolHalf` uniform = pool's
|
|
1046
|
+
// half-extents in world meters) before measuring distance. `radius` is
|
|
1047
|
+
// then naturally interpreted as a world-meter falloff radius and stays
|
|
1048
|
+
// isotropic across any pool aspect.
|
|
1049
|
+
var DROP_FRAGMENT = /* glsl */"\n const float PI = 3.141592653589793;\n uniform sampler2D tSrc;\n uniform vec2 center; // pool-normalized [-1, 1]\n uniform float radius; // WORLD METERS\n uniform float strength;\n uniform vec2 poolHalf; // (vx, vz) world half-extents (meters)\n varying vec2 coord;\n void main() {\n vec4 info = texture2D(tSrc, coord);\n\n vec2 texelWorld = (coord * 2.0 - 1.0) * poolHalf;\n vec2 centerWorld = center * poolHalf;\n float drop = max(0.0, 1.0 - distance(texelWorld, centerWorld) / radius);\n drop = 0.5 - cos(drop * PI) * 0.5;\n info.r += drop * strength;\n\n gl_FragColor = info;\n }\n";
|
|
1050
|
+
|
|
1051
|
+
// Derived from water.js updateShader fragment. One change vs. the demo:
|
|
1052
|
+
// the demo's hard-coded `info.g *= 0.995` becomes `info.g *= damping`.
|
|
1053
|
+
//
|
|
1054
|
+
// Rationale: the demo runs 2 substeps per frame at 60 fps for a fixed 256²
|
|
1055
|
+
// heightfield → 120 substeps/sec, with 0.995^120 ≈ 0.547 per-second damping.
|
|
1056
|
+
// In our component the substep count auto-scales with the heightfield
|
|
1057
|
+
// resolution (so waves propagate the same world meters per frame at any
|
|
1058
|
+
// resolution); if we kept `* 0.995` per substep, doubling the resolution
|
|
1059
|
+
// would more than double the substep rate and waves would die before they
|
|
1060
|
+
// finished spreading across the pool. JS picks a per-substep damping each
|
|
1061
|
+
// frame such that the per-SECOND damping stays at the demo's 0.547.
|
|
1062
|
+
//
|
|
1063
|
+
// The acceleration coefficient stays at the demo's `* 2.0` literal — it
|
|
1064
|
+
// sets the wave equation's `c² · dt² / Δx²` ratio at exactly 0.5, well
|
|
1065
|
+
// inside the CFL stability bound. Tweaking it without re-deriving the
|
|
1066
|
+
// stability regime breaks the simulation.
|
|
1067
|
+
var UPDATE_FRAGMENT = /* glsl */"\n uniform sampler2D tSrc;\n uniform vec2 delta;\n uniform float damping;\n varying vec2 coord;\n void main() {\n /* get vertex info */\n vec4 info = texture2D(tSrc, coord);\n\n /* calculate average neighbor height */\n vec2 dx = vec2(delta.x, 0.0);\n vec2 dy = vec2(0.0, delta.y);\n float average = (\n texture2D(tSrc, coord - dx).r +\n texture2D(tSrc, coord - dy).r +\n texture2D(tSrc, coord + dx).r +\n texture2D(tSrc, coord + dy).r\n ) * 0.25;\n\n /* change the velocity to move toward the average */\n info.g += (average - info.r) * 2.0;\n\n /* attenuate the velocity a little so waves do not last forever */\n info.g *= damping;\n\n /* move the vertex along the velocity */\n info.r += info.g;\n\n gl_FragColor = info;\n }\n";
|
|
1068
|
+
|
|
1069
|
+
// VERBATIM from water.js normalShader fragment (uniform `texture` → `tSrc`).
|
|
1070
|
+
var NORMAL_FRAGMENT = /* glsl */"\n uniform sampler2D tSrc;\n uniform vec2 delta;\n varying vec2 coord;\n void main() {\n /* get vertex info */\n vec4 info = texture2D(tSrc, coord);\n\n /* update the normal */\n vec3 dx = vec3(delta.x, texture2D(tSrc, vec2(coord.x + delta.x, coord.y)).r - info.r, 0.0);\n vec3 dy = vec3(0.0, texture2D(tSrc, vec2(coord.x, coord.y + delta.y)).r - info.r, delta.y);\n info.ba = normalize(cross(dy, dx)).xz;\n\n gl_FragColor = info;\n }\n";
|
|
1071
|
+
/**
|
|
1072
|
+
* GPU heightfield with a ping-pong pair of float render targets. Constructed
|
|
1073
|
+
* without a renderer; the per-frame calls take one.
|
|
1074
|
+
*
|
|
1075
|
+
* Rectangular pools need no kernel change: all three shaders sample through
|
|
1076
|
+
* the `coord` varying (UV ∈ [0,1]²) and `delta` (per-axis texel size), both of
|
|
1077
|
+
* which work unchanged on non-square textures. The only physically-relevant
|
|
1078
|
+
* difference is the drop falloff math — see MOD #A1 in this file.
|
|
1079
|
+
*/
|
|
1080
|
+
var Heightfield = /*#__PURE__*/function () {
|
|
1081
|
+
function Heightfield() {
|
|
1082
|
+
var _opts$sizeX, _opts$sizeY;
|
|
1083
|
+
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
1084
|
+
_classCallCheck(this, Heightfield);
|
|
1085
|
+
_defineProperty(this, "sizeX", void 0);
|
|
1086
|
+
_defineProperty(this, "sizeY", void 0);
|
|
1087
|
+
_defineProperty(this, "delta", void 0);
|
|
1088
|
+
_defineProperty(this, "poolHalf", void 0);
|
|
1089
|
+
_defineProperty(this, "rtA", void 0);
|
|
1090
|
+
_defineProperty(this, "rtB", void 0);
|
|
1091
|
+
_defineProperty(this, "scene", void 0);
|
|
1092
|
+
_defineProperty(this, "camera", void 0);
|
|
1093
|
+
_defineProperty(this, "quad", void 0);
|
|
1094
|
+
_defineProperty(this, "dropMat", void 0);
|
|
1095
|
+
_defineProperty(this, "updateMat", void 0);
|
|
1096
|
+
_defineProperty(this, "normalMat", void 0);
|
|
1097
|
+
_defineProperty(this, "clearedOnce", false);
|
|
1098
|
+
this.sizeX = (_opts$sizeX = opts.sizeX) !== null && _opts$sizeX !== void 0 ? _opts$sizeX : 256;
|
|
1099
|
+
this.sizeY = (_opts$sizeY = opts.sizeY) !== null && _opts$sizeY !== void 0 ? _opts$sizeY : this.sizeX;
|
|
1100
|
+
this.delta = new Vector2(1 / this.sizeX, 1 / this.sizeY);
|
|
1101
|
+
this.poolHalf = new Vector2(1, 1);
|
|
1102
|
+
if (opts.poolHalf) this.poolHalf.copy(opts.poolHalf);
|
|
1103
|
+
|
|
1104
|
+
// ClampToEdge prevents the kernel from sampling the opposite side of the
|
|
1105
|
+
// heightfield (which would manifest as ghost ripples wrapping around the
|
|
1106
|
+
// pool). LinearFilter is the visual win: the surface vertex shader
|
|
1107
|
+
// samples this texture at 200×200 plane vertices (and the fragment
|
|
1108
|
+
// shader peaks-loop adds another 5 taps per pixel), so the difference
|
|
1109
|
+
// between Nearest and Linear shows up as visible chunky pixelation when
|
|
1110
|
+
// Nearest is used. WebGL2 + half-float linear is universally supported
|
|
1111
|
+
// via OES_texture_half_float_linear (auto-enabled on WebGL2 contexts).
|
|
1112
|
+
var rtParams = {
|
|
1113
|
+
type: HalfFloatType,
|
|
1114
|
+
format: RGBAFormat,
|
|
1115
|
+
wrapS: ClampToEdgeWrapping,
|
|
1116
|
+
wrapT: ClampToEdgeWrapping,
|
|
1117
|
+
magFilter: LinearFilter,
|
|
1118
|
+
minFilter: LinearFilter,
|
|
1119
|
+
depthBuffer: false,
|
|
1120
|
+
stencilBuffer: false
|
|
1121
|
+
};
|
|
1122
|
+
this.rtA = new WebGLRenderTarget(this.sizeX, this.sizeY, rtParams);
|
|
1123
|
+
this.rtB = new WebGLRenderTarget(this.sizeX, this.sizeY, rtParams);
|
|
1124
|
+
this.scene = new Scene();
|
|
1125
|
+
this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
|
1126
|
+
this.quad = new Mesh(new PlaneGeometry(2, 2));
|
|
1127
|
+
this.scene.add(this.quad);
|
|
1128
|
+
this.dropMat = new ShaderMaterial({
|
|
1129
|
+
vertexShader: VERTEX_SHADER,
|
|
1130
|
+
fragmentShader: DROP_FRAGMENT,
|
|
1131
|
+
uniforms: {
|
|
1132
|
+
tSrc: {
|
|
1133
|
+
value: null
|
|
1134
|
+
},
|
|
1135
|
+
center: {
|
|
1136
|
+
value: new Vector2(0, 0)
|
|
1137
|
+
},
|
|
1138
|
+
radius: {
|
|
1139
|
+
value: 0.1
|
|
1140
|
+
},
|
|
1141
|
+
strength: {
|
|
1142
|
+
value: 0.01
|
|
1143
|
+
},
|
|
1144
|
+
poolHalf: {
|
|
1145
|
+
value: this.poolHalf
|
|
1146
|
+
}
|
|
1147
|
+
},
|
|
1148
|
+
depthTest: false,
|
|
1149
|
+
depthWrite: false
|
|
1150
|
+
});
|
|
1151
|
+
this.updateMat = new ShaderMaterial({
|
|
1152
|
+
vertexShader: VERTEX_SHADER,
|
|
1153
|
+
fragmentShader: UPDATE_FRAGMENT,
|
|
1154
|
+
uniforms: {
|
|
1155
|
+
tSrc: {
|
|
1156
|
+
value: null
|
|
1157
|
+
},
|
|
1158
|
+
delta: {
|
|
1159
|
+
value: this.delta
|
|
1160
|
+
},
|
|
1161
|
+
// Per-substep damping. Caller is expected to set this each frame
|
|
1162
|
+
// via `setDamping(value)` so it matches the actual substep rate;
|
|
1163
|
+
// 0.995 is the demo baseline (2 substeps/frame on a 256² grid).
|
|
1164
|
+
damping: {
|
|
1165
|
+
value: 0.995
|
|
1166
|
+
}
|
|
1167
|
+
},
|
|
1168
|
+
depthTest: false,
|
|
1169
|
+
depthWrite: false
|
|
1170
|
+
});
|
|
1171
|
+
this.normalMat = new ShaderMaterial({
|
|
1172
|
+
vertexShader: VERTEX_SHADER,
|
|
1173
|
+
fragmentShader: NORMAL_FRAGMENT,
|
|
1174
|
+
uniforms: {
|
|
1175
|
+
tSrc: {
|
|
1176
|
+
value: null
|
|
1177
|
+
},
|
|
1178
|
+
delta: {
|
|
1179
|
+
value: this.delta
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1182
|
+
depthTest: false,
|
|
1183
|
+
depthWrite: false
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* Read-only handle of the front-buffer (the one that holds the latest simulation
|
|
1189
|
+
* state). Sample this texture from the surface and caustics shaders.
|
|
1190
|
+
*/
|
|
1191
|
+
return _createClass(Heightfield, [{
|
|
1192
|
+
key: "currentTexture",
|
|
1193
|
+
get: function get() {
|
|
1194
|
+
return this.rtA.texture;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
/**
|
|
1198
|
+
* Clear both ping-pong RTs to (0,0,0,0) on the first frame so neither buffer
|
|
1199
|
+
* starts with garbage. Stability bug guard: skipping this gives the kernel
|
|
1200
|
+
* uninitialized data and the wave amplitude blows up.
|
|
1201
|
+
*/
|
|
1202
|
+
}, {
|
|
1203
|
+
key: "ensureCleared",
|
|
1204
|
+
value: function ensureCleared(renderer) {
|
|
1205
|
+
var _this = this;
|
|
1206
|
+
if (this.clearedOnce) return;
|
|
1207
|
+
withRenderTarget(renderer, {
|
|
1208
|
+
target: this.rtA,
|
|
1209
|
+
clearColor: 0x000000,
|
|
1210
|
+
clearAlpha: 0
|
|
1211
|
+
}, function () {
|
|
1212
|
+
renderer.clear(true, true, true);
|
|
1213
|
+
// Second buffer of the ping-pong. Re-binding inside the pass is safe —
|
|
1214
|
+
// the restore uses the target captured before it.
|
|
1215
|
+
renderer.setRenderTarget(_this.rtB);
|
|
1216
|
+
renderer.clear(true, true, true);
|
|
1217
|
+
});
|
|
1218
|
+
this.clearedOnce = true;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* One ping-pong step: draw the quad into the back buffer, then swap.
|
|
1223
|
+
*
|
|
1224
|
+
* Deliberately not a `withRenderTarget` pass — it touches no clear/tone-mapping
|
|
1225
|
+
* state and unbinds to the default framebuffer rather than restoring whatever
|
|
1226
|
+
* was bound. Callers run it from the water frame stage, where nothing is bound.
|
|
1227
|
+
*/
|
|
1228
|
+
}, {
|
|
1229
|
+
key: "renderPass",
|
|
1230
|
+
value: function renderPass(renderer, mat) {
|
|
1231
|
+
this.quad.material = mat;
|
|
1232
|
+
renderer.setRenderTarget(this.rtB);
|
|
1233
|
+
renderer.render(this.scene, this.camera);
|
|
1234
|
+
renderer.setRenderTarget(null);
|
|
1235
|
+
var _ref = [this.rtB, this.rtA];
|
|
1236
|
+
this.rtA = _ref[0];
|
|
1237
|
+
this.rtB = _ref[1];
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Add a drop. Center is in pool-normalized [-1, 1] coordinates so the same
|
|
1242
|
+
* call site works across pool sizes. `radius` is in WORLD METERS — see the
|
|
1243
|
+
* MOD #A1 comment above DROP_FRAGMENT — so a 0.2 m drop has the same
|
|
1244
|
+
* physical size on a 4×4 pool and a 2×10 pool. Strength is unitless
|
|
1245
|
+
* (height delta applied at the drop centre).
|
|
1246
|
+
*/
|
|
1247
|
+
}, {
|
|
1248
|
+
key: "addDrop",
|
|
1249
|
+
value: function addDrop(renderer, center) {
|
|
1250
|
+
var radius = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.1;
|
|
1251
|
+
var strength = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0.01;
|
|
1252
|
+
this.ensureCleared(renderer);
|
|
1253
|
+
this.dropMat.uniforms.tSrc.value = this.rtA.texture;
|
|
1254
|
+
this.dropMat.uniforms.center.value.copy(center);
|
|
1255
|
+
this.dropMat.uniforms.radius.value = radius;
|
|
1256
|
+
this.dropMat.uniforms.strength.value = strength;
|
|
1257
|
+
this.renderPass(renderer, this.dropMat);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
/** Advance the wave simulation one substep. */
|
|
1261
|
+
}, {
|
|
1262
|
+
key: "step",
|
|
1263
|
+
value: function step(renderer) {
|
|
1264
|
+
this.ensureCleared(renderer);
|
|
1265
|
+
this.updateMat.uniforms.tSrc.value = this.rtA.texture;
|
|
1266
|
+
this.renderPass(renderer, this.updateMat);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* Per-substep damping coefficient applied to the velocity channel after
|
|
1271
|
+
* each `step()`. With more substeps per second (e.g. when the host
|
|
1272
|
+
* component auto-scales substep count for higher-resolution
|
|
1273
|
+
* heightfields) this value must be closer to 1.0 to keep amplitude
|
|
1274
|
+
* decay constant in real time. The host component computes this as
|
|
1275
|
+
* `damping = (perSecondDamping) ^ (1 / substepsPerSecond)`
|
|
1276
|
+
* and pushes it every frame.
|
|
1277
|
+
*/
|
|
1278
|
+
}, {
|
|
1279
|
+
key: "setDamping",
|
|
1280
|
+
value: function setDamping(value) {
|
|
1281
|
+
this.updateMat.uniforms.damping.value = value;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
/** Recompute surface normals into channels (b, a). */
|
|
1285
|
+
}, {
|
|
1286
|
+
key: "updateNormals",
|
|
1287
|
+
value: function updateNormals(renderer) {
|
|
1288
|
+
this.ensureCleared(renderer);
|
|
1289
|
+
this.normalMat.uniforms.tSrc.value = this.rtA.texture;
|
|
1290
|
+
this.renderPass(renderer, this.normalMat);
|
|
1291
|
+
}
|
|
1292
|
+
}, {
|
|
1293
|
+
key: "dispose",
|
|
1294
|
+
value: function dispose() {
|
|
1295
|
+
this.rtA.dispose();
|
|
1296
|
+
this.rtB.dispose();
|
|
1297
|
+
this.dropMat.dispose();
|
|
1298
|
+
this.updateMat.dispose();
|
|
1299
|
+
this.normalMat.dispose();
|
|
1300
|
+
this.quad.geometry.dispose();
|
|
1301
|
+
}
|
|
1302
|
+
}]);
|
|
1303
|
+
}();
|
|
1304
|
+
|
|
1305
|
+
var CausticsTexture = /*#__PURE__*/function () {
|
|
1306
|
+
function CausticsTexture() {
|
|
1307
|
+
var _opts$size, _opts$detail, _opts$poolHalf;
|
|
1308
|
+
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
1309
|
+
_classCallCheck(this, CausticsTexture);
|
|
1310
|
+
_defineProperty(this, "size", void 0);
|
|
1311
|
+
_defineProperty(this, "poolHalf", void 0);
|
|
1312
|
+
_defineProperty(this, "rt", void 0);
|
|
1313
|
+
_defineProperty(this, "scene", void 0);
|
|
1314
|
+
_defineProperty(this, "camera", void 0);
|
|
1315
|
+
_defineProperty(this, "quad", void 0);
|
|
1316
|
+
_defineProperty(this, "material", void 0);
|
|
1317
|
+
this.size = (_opts$size = opts.size) !== null && _opts$size !== void 0 ? _opts$size : 1024;
|
|
1318
|
+
var detail = (_opts$detail = opts.detail) !== null && _opts$detail !== void 0 ? _opts$detail : 200;
|
|
1319
|
+
this.poolHalf = ((_opts$poolHalf = opts.poolHalf) !== null && _opts$poolHalf !== void 0 ? _opts$poolHalf : new Vector3(1, 1, 1)).clone();
|
|
1320
|
+
this.rt = new WebGLRenderTarget(this.size, this.size, {
|
|
1321
|
+
type: UnsignedByteType,
|
|
1322
|
+
format: RGBAFormat,
|
|
1323
|
+
depthBuffer: false,
|
|
1324
|
+
stencilBuffer: false
|
|
1325
|
+
});
|
|
1326
|
+
this.scene = new Scene();
|
|
1327
|
+
// The caustics vertex shader writes gl_Position directly, ignoring the
|
|
1328
|
+
// camera's projection / view matrices. Any camera works — this one is
|
|
1329
|
+
// chosen to match the [-1, 1] clip-space range of the shader output.
|
|
1330
|
+
this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
|
1331
|
+
this.material = new ShaderMaterial({
|
|
1332
|
+
vertexShader: CAUSTICS_SHADER.vertex,
|
|
1333
|
+
fragmentShader: CAUSTICS_SHADER.fragment,
|
|
1334
|
+
uniforms: {
|
|
1335
|
+
light: {
|
|
1336
|
+
value: new Vector3(0.7071, 0.7071, 0.0)
|
|
1337
|
+
},
|
|
1338
|
+
water: {
|
|
1339
|
+
value: null
|
|
1340
|
+
},
|
|
1341
|
+
uPoolHalf: {
|
|
1342
|
+
value: this.poolHalf.clone()
|
|
1343
|
+
},
|
|
1344
|
+
// Demo's hard-coded `info.ba *= 0.5` lifted to a uniform — see MOD #4
|
|
1345
|
+
// in waterShaders.ts. Lower values mute small-amplitude ripple
|
|
1346
|
+
// caustics that look like static noise on the pool floor at higher
|
|
1347
|
+
// simulation resolutions.
|
|
1348
|
+
uCausticIntensity: {
|
|
1349
|
+
value: 0.5
|
|
1350
|
+
},
|
|
1351
|
+
// MOD #7 — receiving-geometry height map; while unset the vertex
|
|
1352
|
+
// shader keeps the flat-floor projection.
|
|
1353
|
+
uHeightMap: {
|
|
1354
|
+
value: null
|
|
1355
|
+
},
|
|
1356
|
+
uHeightMapEnabled: {
|
|
1357
|
+
value: 0
|
|
1358
|
+
}
|
|
1359
|
+
},
|
|
1360
|
+
depthTest: false,
|
|
1361
|
+
depthWrite: false
|
|
1362
|
+
// dFdx / dFdy in the caustics fragment are part of GLSL ES 3.00 (WebGL2);
|
|
1363
|
+
// Three v0.176 emits GLSL ES 3.00 by default, so no `extensions` flag is
|
|
1364
|
+
// needed. The shader stays portable to WebGL1 as long as Three injects
|
|
1365
|
+
// the GL_OES_standard_derivatives directive itself when it falls back.
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
// Plane mesh: sized in world meters so `position.xy` in the vertex
|
|
1369
|
+
// shader is the WORLD XZ of a water-surface point. Segment count scales
|
|
1370
|
+
// with each axis independently from a longest-axis "200 / 2m" budget
|
|
1371
|
+
// (≈ the demo density), floored at 32 so we never degenerate to a
|
|
1372
|
+
// 1-segment strip on extreme aspect ratios.
|
|
1373
|
+
var vx = this.poolHalf.x;
|
|
1374
|
+
var vz = this.poolHalf.z;
|
|
1375
|
+
var longer = Math.max(vx, vz);
|
|
1376
|
+
var segsPerMeter = detail / Math.max(0.001, 2 * longer);
|
|
1377
|
+
var segsX = Math.max(32, Math.round(2 * vx * segsPerMeter));
|
|
1378
|
+
var segsZ = Math.max(32, Math.round(2 * vz * segsPerMeter));
|
|
1379
|
+
this.quad = new Mesh(new PlaneGeometry(2 * vx, 2 * vz, segsX, segsZ), this.material);
|
|
1380
|
+
this.scene.add(this.quad);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
/** Output texture handle. Bind this where the demo bound `causticTex`. */
|
|
1384
|
+
return _createClass(CausticsTexture, [{
|
|
1385
|
+
key: "texture",
|
|
1386
|
+
get: function get() {
|
|
1387
|
+
return this.rt.texture;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
/**
|
|
1391
|
+
* Render the caustics for the current heightfield + light. Should
|
|
1392
|
+
* be called once per frame, after `Heightfield.updateNormals()` so the
|
|
1393
|
+
* normals sampled here are in sync with the displaced surface.
|
|
1394
|
+
*
|
|
1395
|
+
* Inputs are read from the uniform setters below — pass updates BEFORE
|
|
1396
|
+
* calling render().
|
|
1397
|
+
*/
|
|
1398
|
+
}, {
|
|
1399
|
+
key: "render",
|
|
1400
|
+
value: function render(renderer) {
|
|
1401
|
+
var _this = this;
|
|
1402
|
+
withRenderTarget(renderer, {
|
|
1403
|
+
target: this.rt,
|
|
1404
|
+
clearColor: 0x000000,
|
|
1405
|
+
clearAlpha: 0
|
|
1406
|
+
}, function () {
|
|
1407
|
+
renderer.clear(true, false, false);
|
|
1408
|
+
renderer.render(_this.scene, _this.camera);
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
}, {
|
|
1412
|
+
key: "setHeightfield",
|
|
1413
|
+
value: function setHeightfield(tex) {
|
|
1414
|
+
this.material.uniforms.water.value = tex;
|
|
1415
|
+
}
|
|
1416
|
+
}, {
|
|
1417
|
+
key: "setLight",
|
|
1418
|
+
value: function setLight(x, y, z) {
|
|
1419
|
+
this.material.uniforms.light.value.set(x, y, z);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
/** MOD #4 — caustic intensity multiplier (replaces demo's `info.ba *= 0.5`). */
|
|
1423
|
+
}, {
|
|
1424
|
+
key: "setIntensity",
|
|
1425
|
+
value: function setIntensity(intensity) {
|
|
1426
|
+
this.material.uniforms.uCausticIntensity.value = intensity;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/** MOD #7 — bind (or clear) the receiving-geometry height map. */
|
|
1430
|
+
}, {
|
|
1431
|
+
key: "setHeightMap",
|
|
1432
|
+
value: function setHeightMap(tex) {
|
|
1433
|
+
this.material.uniforms.uHeightMap.value = tex;
|
|
1434
|
+
this.material.uniforms.uHeightMapEnabled.value = tex ? 1 : 0;
|
|
1435
|
+
}
|
|
1436
|
+
}, {
|
|
1437
|
+
key: "dispose",
|
|
1438
|
+
value: function dispose() {
|
|
1439
|
+
this.rt.dispose();
|
|
1440
|
+
this.material.dispose();
|
|
1441
|
+
this.quad.geometry.dispose();
|
|
1442
|
+
}
|
|
1443
|
+
}]);
|
|
1444
|
+
}();
|
|
1445
|
+
|
|
1446
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
|
1447
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
1448
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
1449
|
+
|
|
1450
|
+
/** Per-second amplitude decay: the demo's `0.995` per substep at its 120 substeps/s. */
|
|
1451
|
+
var PER_SECOND_DAMPING = 0.547;
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* Longest frame the accumulator integrates. A paused tab, a JIT pause or an
|
|
1455
|
+
* fps-cap resume hands us a huge delta; unclamped it becomes a burst of
|
|
1456
|
+
* hundreds of substeps that visually snaps the wave field. 1/15 s is up to
|
|
1457
|
+
* 4 substeps per resumption at the default `substeps = 2`.
|
|
1458
|
+
*/
|
|
1459
|
+
var MAX_STEP_SECONDS = 1 / 15;
|
|
1460
|
+
|
|
1461
|
+
/**
|
|
1462
|
+
* First-frame seed: a handful of large, slow drops so the surface starts
|
|
1463
|
+
* alive without high-frequency content (which would project into many tiny
|
|
1464
|
+
* caustic stripes on the pool walls). ~0.3 m matches the demo's normalized
|
|
1465
|
+
* 0.15 on the default 4×2×4 pool.
|
|
1466
|
+
*/
|
|
1467
|
+
var SEED_DROP_COUNT = 10;
|
|
1468
|
+
var SEED_DROP_RADIUS = 0.3;
|
|
1469
|
+
var SEED_DROP_STRENGTH = 0.04;
|
|
1470
|
+
var AMBIENT_DROP_RADIUS = 0.24;
|
|
1471
|
+
var AMBIENT_DROP_STRENGTH = 0.04;
|
|
1472
|
+
|
|
1473
|
+
/** Smallest heightfield extent per axis, so a thin pool still simulates. */
|
|
1474
|
+
var MIN_HEIGHTFIELD_TEXELS = 32;
|
|
1475
|
+
|
|
1476
|
+
/**
|
|
1477
|
+
* Fractional fixed-step accumulator. `take` turns a frame delta into a whole
|
|
1478
|
+
* number of substeps at the requested per-second rate and carries the
|
|
1479
|
+
* remainder, so the rate is exact on average and wave speed is INDEPENDENT
|
|
1480
|
+
* of rendered fps — a 30 fps frame runs twice the substeps of a 60 fps one.
|
|
1481
|
+
*/
|
|
1482
|
+
var SubstepAccumulator = /*#__PURE__*/function () {
|
|
1483
|
+
function SubstepAccumulator() {
|
|
1484
|
+
_classCallCheck(this, SubstepAccumulator);
|
|
1485
|
+
_defineProperty(this, "leftover", 0);
|
|
1486
|
+
}
|
|
1487
|
+
return _createClass(SubstepAccumulator, [{
|
|
1488
|
+
key: "take",
|
|
1489
|
+
value: function take(substepsPerSecond, delta) {
|
|
1490
|
+
this.leftover += substepsPerSecond * Math.min(delta, MAX_STEP_SECONDS);
|
|
1491
|
+
var steps = Math.floor(this.leftover);
|
|
1492
|
+
this.leftover -= steps;
|
|
1493
|
+
return steps;
|
|
1494
|
+
}
|
|
1495
|
+
}, {
|
|
1496
|
+
key: "reset",
|
|
1497
|
+
value: function reset() {
|
|
1498
|
+
this.leftover = 0;
|
|
1499
|
+
}
|
|
1500
|
+
}]);
|
|
1501
|
+
}();
|
|
1502
|
+
function useWaveSimulation(_ref) {
|
|
1503
|
+
var poolHalf = _ref.poolHalf,
|
|
1504
|
+
heightfieldSize = _ref.heightfieldSize,
|
|
1505
|
+
causticTextureSize = _ref.causticTextureSize,
|
|
1506
|
+
substeps = _ref.substeps,
|
|
1507
|
+
ambientDropPeriod = _ref.ambientDropPeriod;
|
|
1508
|
+
var renderer = useThree(function (s) {
|
|
1509
|
+
return s.gl;
|
|
1510
|
+
});
|
|
1511
|
+
var heightfieldRef = useRef(null);
|
|
1512
|
+
var causticsRef = useRef(null);
|
|
1513
|
+
var pendingDrops = useRef([]);
|
|
1514
|
+
var seededRef = useRef(false);
|
|
1515
|
+
var ambientTimerRef = useRef(0);
|
|
1516
|
+
var accumulatorRef = useRef(new SubstepAccumulator());
|
|
1517
|
+
|
|
1518
|
+
// Rectangular heightfield with the texel-per-meter resolution matched on
|
|
1519
|
+
// both axes, so wave propagation is isotropic in world space.
|
|
1520
|
+
var heightfieldShape = useMemo(function () {
|
|
1521
|
+
var vx = poolHalf.x;
|
|
1522
|
+
var vz = poolHalf.z;
|
|
1523
|
+
var texelsPerMeter = heightfieldSize / (2 * Math.max(vx, vz));
|
|
1524
|
+
return {
|
|
1525
|
+
sizeX: Math.max(MIN_HEIGHTFIELD_TEXELS, Math.round(2 * vx * texelsPerMeter)),
|
|
1526
|
+
sizeY: Math.max(MIN_HEIGHTFIELD_TEXELS, Math.round(2 * vz * texelsPerMeter)),
|
|
1527
|
+
poolHalfX: vx,
|
|
1528
|
+
poolHalfZ: vz,
|
|
1529
|
+
poolDepth: poolHalf.y
|
|
1530
|
+
};
|
|
1531
|
+
}, [poolHalf.x, poolHalf.y, poolHalf.z, heightfieldSize]);
|
|
1532
|
+
|
|
1533
|
+
// Both get rebuilt when the pool aspect changes — losing wave state is
|
|
1534
|
+
// acceptable, the seed drops restock the surface within one frame.
|
|
1535
|
+
useEffect(function () {
|
|
1536
|
+
var hf = new Heightfield({
|
|
1537
|
+
sizeX: heightfieldShape.sizeX,
|
|
1538
|
+
sizeY: heightfieldShape.sizeY,
|
|
1539
|
+
poolHalf: new Vector2(heightfieldShape.poolHalfX, heightfieldShape.poolHalfZ)
|
|
1540
|
+
});
|
|
1541
|
+
var cx = new CausticsTexture({
|
|
1542
|
+
poolHalf: new Vector3(heightfieldShape.poolHalfX, heightfieldShape.poolDepth, heightfieldShape.poolHalfZ),
|
|
1543
|
+
size: causticTextureSize
|
|
1544
|
+
});
|
|
1545
|
+
heightfieldRef.current = hf;
|
|
1546
|
+
causticsRef.current = cx;
|
|
1547
|
+
seededRef.current = false;
|
|
1548
|
+
// A fresh sim must not inherit the previous resolution's fractional
|
|
1549
|
+
// leftover, or the first frame runs an accumulated burst of steps.
|
|
1550
|
+
accumulatorRef.current.reset();
|
|
1551
|
+
return function () {
|
|
1552
|
+
hf.dispose();
|
|
1553
|
+
cx.dispose();
|
|
1554
|
+
heightfieldRef.current = null;
|
|
1555
|
+
causticsRef.current = null;
|
|
1556
|
+
};
|
|
1557
|
+
}, [heightfieldShape, causticTextureSize]);
|
|
1558
|
+
var queueDrop = useCallback(function (center, radius, strength) {
|
|
1559
|
+
pendingDrops.current.push({
|
|
1560
|
+
center: center,
|
|
1561
|
+
radius: radius,
|
|
1562
|
+
strength: strength
|
|
1563
|
+
});
|
|
1564
|
+
}, []);
|
|
1565
|
+
var advance = function advance(delta) {
|
|
1566
|
+
var hf = heightfieldRef.current;
|
|
1567
|
+
var cx = causticsRef.current;
|
|
1568
|
+
if (!hf || !cx) return false;
|
|
1569
|
+
if (!seededRef.current) {
|
|
1570
|
+
for (var i = 0; i < SEED_DROP_COUNT; i++) {
|
|
1571
|
+
var dx = Math.random() * 2 - 1;
|
|
1572
|
+
var dy = Math.random() * 2 - 1;
|
|
1573
|
+
hf.addDrop(renderer, new Vector2(dx, dy), SEED_DROP_RADIUS, i & 1 ? SEED_DROP_STRENGTH : -SEED_DROP_STRENGTH);
|
|
1574
|
+
}
|
|
1575
|
+
seededRef.current = true;
|
|
1576
|
+
}
|
|
1577
|
+
if (ambientDropPeriod > 0) {
|
|
1578
|
+
ambientTimerRef.current += delta;
|
|
1579
|
+
if (ambientTimerRef.current >= ambientDropPeriod) {
|
|
1580
|
+
ambientTimerRef.current = 0;
|
|
1581
|
+
var _dx = Math.random() * 2 - 1;
|
|
1582
|
+
var _dy = Math.random() * 2 - 1;
|
|
1583
|
+
hf.addDrop(renderer, new Vector2(_dx, _dy), AMBIENT_DROP_RADIUS, AMBIENT_DROP_STRENGTH);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
if (pendingDrops.current.length > 0) {
|
|
1587
|
+
var _iterator = _createForOfIteratorHelper(pendingDrops.current),
|
|
1588
|
+
_step;
|
|
1589
|
+
try {
|
|
1590
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1591
|
+
var drop = _step.value;
|
|
1592
|
+
hf.addDrop(renderer, drop.center, drop.radius, drop.strength);
|
|
1593
|
+
}
|
|
1594
|
+
} catch (err) {
|
|
1595
|
+
_iterator.e(err);
|
|
1596
|
+
} finally {
|
|
1597
|
+
_iterator.f();
|
|
1598
|
+
}
|
|
1599
|
+
pendingDrops.current.length = 0;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// The wave kernel propagates exactly one cell per substep, so wave speed
|
|
1603
|
+
// in world meters per second is
|
|
1604
|
+
// c_world = substepsPerSecond × (2 × poolHalf.x / sizeX).
|
|
1605
|
+
var substepsPerSecond = substeps * 60;
|
|
1606
|
+
if (substepsPerSecond > 0) {
|
|
1607
|
+
// Damping is a per-SECOND target, so the per-substep factor has to be
|
|
1608
|
+
// derived from the actual substep rate.
|
|
1609
|
+
hf.setDamping(Math.pow(PER_SECOND_DAMPING, 1 / substepsPerSecond));
|
|
1610
|
+
var stepsThisFrame = accumulatorRef.current.take(substepsPerSecond, delta);
|
|
1611
|
+
for (var _i = 0; _i < stepsThisFrame; _i++) {
|
|
1612
|
+
hf.step(renderer);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
hf.updateNormals(renderer);
|
|
1616
|
+
return true;
|
|
1617
|
+
};
|
|
1618
|
+
var setHeightMap = function setHeightMap(texture) {
|
|
1619
|
+
var _causticsRef$current;
|
|
1620
|
+
(_causticsRef$current = causticsRef.current) === null || _causticsRef$current === void 0 || _causticsRef$current.setHeightMap(texture);
|
|
1621
|
+
};
|
|
1622
|
+
var renderCaustics = function renderCaustics(light, intensity) {
|
|
1623
|
+
var hf = heightfieldRef.current;
|
|
1624
|
+
var cx = causticsRef.current;
|
|
1625
|
+
if (!hf || !cx) return;
|
|
1626
|
+
cx.setHeightfield(hf.currentTexture);
|
|
1627
|
+
cx.setLight(light.x, light.y, light.z);
|
|
1628
|
+
cx.setIntensity(intensity);
|
|
1629
|
+
cx.render(renderer);
|
|
1630
|
+
};
|
|
1631
|
+
return {
|
|
1632
|
+
queueDrop: queueDrop,
|
|
1633
|
+
advance: advance,
|
|
1634
|
+
setHeightMap: setHeightMap,
|
|
1635
|
+
renderCaustics: renderCaustics,
|
|
1636
|
+
get heightTexture() {
|
|
1637
|
+
var _heightfieldRef$curre, _heightfieldRef$curre2;
|
|
1638
|
+
return (_heightfieldRef$curre = (_heightfieldRef$curre2 = heightfieldRef.current) === null || _heightfieldRef$curre2 === void 0 ? void 0 : _heightfieldRef$curre2.currentTexture) !== null && _heightfieldRef$curre !== void 0 ? _heightfieldRef$curre : null;
|
|
1639
|
+
},
|
|
1640
|
+
get causticTexture() {
|
|
1641
|
+
var _causticsRef$current$, _causticsRef$current2;
|
|
1642
|
+
return (_causticsRef$current$ = (_causticsRef$current2 = causticsRef.current) === null || _causticsRef$current2 === void 0 ? void 0 : _causticsRef$current2.texture) !== null && _causticsRef$current$ !== void 0 ? _causticsRef$current$ : null;
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* Pointer drop radius in WORLD METERS — the demo's normalized 0.05 on the
|
|
1649
|
+
* default 4×2×4 pool, now the same physical size on any pool aspect.
|
|
1650
|
+
*/
|
|
1651
|
+
var POINTER_DROP_RADIUS = 0.1;
|
|
1652
|
+
|
|
1653
|
+
/**
|
|
1654
|
+
* Base drop strengths, on the gentle side of the demo (~0.01 hover / ~0.03
|
|
1655
|
+
* click): `pointermove` fires per pointer sample, so a single drag across the
|
|
1656
|
+
* pool stacks 50+ drops and anything above ~0.02 looks like a tsunami after a
|
|
1657
|
+
* few hundred ms of motion.
|
|
1658
|
+
*/
|
|
1659
|
+
var HOVER_DROP_STRENGTH = 0.01;
|
|
1660
|
+
var PRESS_DROP_STRENGTH = 0.04;
|
|
1661
|
+
/**
|
|
1662
|
+
* `queueDrop` receives pool-normalized `[-1, +1]` centers. The raycast plane
|
|
1663
|
+
* is rotated -π/2 about X, so its local +Y aligns with world -Z: the v
|
|
1664
|
+
* coordinate is flipped to put the drop under the cursor.
|
|
1665
|
+
*/
|
|
1666
|
+
function useWaterPointer(queueDrop, pointerStrength) {
|
|
1667
|
+
var queueAt = useCallback(function (event, strength) {
|
|
1668
|
+
if (!event.uv) return;
|
|
1669
|
+
queueDrop(new Vector2(event.uv.x * 2 - 1, 1 - event.uv.y * 2), POINTER_DROP_RADIUS, strength);
|
|
1670
|
+
}, [queueDrop]);
|
|
1671
|
+
var onPointerMove = useCallback(function (event) {
|
|
1672
|
+
return queueAt(event, HOVER_DROP_STRENGTH * pointerStrength);
|
|
1673
|
+
}, [queueAt, pointerStrength]);
|
|
1674
|
+
var onPointerDown = useCallback(function (event) {
|
|
1675
|
+
return queueAt(event, PRESS_DROP_STRENGTH * pointerStrength);
|
|
1676
|
+
}, [queueAt, pointerStrength]);
|
|
1677
|
+
return {
|
|
1678
|
+
onPointerMove: onPointerMove,
|
|
1679
|
+
onPointerDown: onPointerDown
|
|
1680
|
+
};
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* Debug visualization mode. `'overlay' | 'isolate' | 'mask'` are RECEIVER
|
|
1685
|
+
* views; `'landing' | 'heightmap'` are SURFACE-shader views, because receiver
|
|
1686
|
+
* views never reach the screen underwater in screen-space-refraction mode.
|
|
1687
|
+
*/
|
|
1688
|
+
|
|
1689
|
+
/** Mode → the float code both shader families compare against. */
|
|
1690
|
+
var CAUSTIC_DEBUG_CODE = {
|
|
1691
|
+
off: 0,
|
|
1692
|
+
overlay: 1,
|
|
1693
|
+
isolate: 2,
|
|
1694
|
+
mask: 3,
|
|
1695
|
+
landing: 4,
|
|
1696
|
+
heightmap: 5
|
|
1697
|
+
};
|
|
1698
|
+
|
|
1699
|
+
/** Highest code the receiver shader understands; above it are surface views. */
|
|
1700
|
+
var HIGHEST_RECEIVER_CODE = 3;
|
|
1701
|
+
|
|
1702
|
+
/**
|
|
1703
|
+
* Publish `mode` to the surface material and to the receiver singleton.
|
|
1704
|
+
* Surface-only modes hand the receivers 0 so they keep their production look
|
|
1705
|
+
* while a surface view is up.
|
|
1706
|
+
*/
|
|
1707
|
+
function publishCausticDebugMode(material, mode) {
|
|
1708
|
+
var code = CAUSTIC_DEBUG_CODE[mode];
|
|
1709
|
+
waterUniforms.causticDebugMode = code <= HIGHEST_RECEIVER_CODE ? code : 0;
|
|
1710
|
+
material.uniforms.uCausticDebugMode.value = code;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
/**
|
|
1714
|
+
* Drop the demo's self-contained pool world into the scene at the configured
|
|
1715
|
+
* AABB. EVERY internal coordinate is in WORLD METERS in the pool's local
|
|
1716
|
+
* frame (origin at the water-surface centre, +Y up):
|
|
1717
|
+
*
|
|
1718
|
+
* xz ∈ [-poolHalf.x, +poolHalf.x] × [-poolHalf.z, +poolHalf.z] (pool footprint)
|
|
1719
|
+
* y ∈ [-poolHalf.y, +2 * poolHalf.y] (floor → sky ceiling)
|
|
1720
|
+
*
|
|
1721
|
+
* The parent `<group>` carries ONLY a `position` (no scale) so non-cubic pools
|
|
1722
|
+
* don't introduce anisotropic stretching to the inside-the-pool view (wall
|
|
1723
|
+
* caustics, etc.); the shaders get a `uPoolHalf` uniform so their internal
|
|
1724
|
+
* math (intersectCube, caustic projection, wall detection, rim shadow) runs in
|
|
1725
|
+
* the same world-meter frame.
|
|
1726
|
+
*
|
|
1727
|
+
* The above-water surface is rendered; the pool interior is ray-traced inside
|
|
1728
|
+
* the surface shader. The underwater pass is intentionally skipped (the camera
|
|
1729
|
+
* never goes below the water in the host scene).
|
|
1730
|
+
*/
|
|
1731
|
+
function WaterSurfaceRenderer(_ref) {
|
|
1732
|
+
var position = _ref.position,
|
|
1733
|
+
volumeSize = _ref.volumeSize,
|
|
1734
|
+
sunDirection = _ref.sunDirection,
|
|
1735
|
+
wallColor = _ref.wallColor,
|
|
1736
|
+
skyHorizonColor = _ref.skyHorizonColor,
|
|
1737
|
+
skyZenithColor = _ref.skyZenithColor,
|
|
1738
|
+
heightfieldSize = _ref.heightfieldSize,
|
|
1739
|
+
causticTextureSize = _ref.causticTextureSize,
|
|
1740
|
+
substeps = _ref.substeps,
|
|
1741
|
+
ambientDropPeriod = _ref.ambientDropPeriod,
|
|
1742
|
+
pointerStrength = _ref.pointerStrength,
|
|
1743
|
+
causticIntensity = _ref.causticIntensity,
|
|
1744
|
+
waterClarity = _ref.waterClarity,
|
|
1745
|
+
useScreenSpaceRefraction = _ref.useScreenSpaceRefraction,
|
|
1746
|
+
refractionFallbackColor = _ref.refractionFallbackColor,
|
|
1747
|
+
fresnelMax = _ref.fresnelMax,
|
|
1748
|
+
causticDebugMode = _ref.causticDebugMode;
|
|
1749
|
+
var groupRef = useRef(null);
|
|
1750
|
+
var surfaceMeshRef = useRef(null);
|
|
1751
|
+
var renderer = useThree(function (s) {
|
|
1752
|
+
return s.gl;
|
|
1753
|
+
});
|
|
1754
|
+
var camera = useThree(function (s) {
|
|
1755
|
+
return s.camera;
|
|
1756
|
+
});
|
|
1757
|
+
var sceneR3F = useThree(function (s) {
|
|
1758
|
+
return s.scene;
|
|
1759
|
+
});
|
|
1760
|
+
// CSS pixels; the capture applies the pixel ratio itself each frame, so its
|
|
1761
|
+
// texture tracks the live canvas without a React re-render.
|
|
1762
|
+
var r3fSize = useThree(function (s) {
|
|
1763
|
+
return s.size;
|
|
1764
|
+
});
|
|
1765
|
+
|
|
1766
|
+
/** Screen-space-refraction capture; resizes itself with the canvas. */
|
|
1767
|
+
var refractionRef = useRef(null);
|
|
1768
|
+
if (refractionRef.current === null) {
|
|
1769
|
+
refractionRef.current = new RefractionCapture();
|
|
1770
|
+
}
|
|
1771
|
+
/**
|
|
1772
|
+
* Top-down height map of the caustic-receiving geometry, feeding the caustics
|
|
1773
|
+
* pass and the SSR composite (see PoolHeightMap.ts for its refresh policy).
|
|
1774
|
+
*/
|
|
1775
|
+
var poolHeightMapRef = useRef(null);
|
|
1776
|
+
if (poolHeightMapRef.current === null) {
|
|
1777
|
+
poolHeightMapRef.current = new PoolHeightMap();
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
// The `uPoolHalf : vec3` uniform every shader gets, in WORLD METERS:
|
|
1781
|
+
// .x — half-width (X axis)
|
|
1782
|
+
// .y — full depth (Y axis, asymmetric: surface at 0, floor at -.y)
|
|
1783
|
+
// .z — half-length (Z axis)
|
|
1784
|
+
var poolHalf = useMemo(function () {
|
|
1785
|
+
return new Vector3(volumeSize[0] / 2, volumeSize[1], volumeSize[2] / 2);
|
|
1786
|
+
}, [volumeSize[0], volumeSize[1], volumeSize[2]]);
|
|
1787
|
+
var sim = useWaveSimulation({
|
|
1788
|
+
poolHalf: poolHalf,
|
|
1789
|
+
heightfieldSize: heightfieldSize,
|
|
1790
|
+
causticTextureSize: causticTextureSize,
|
|
1791
|
+
substeps: substeps,
|
|
1792
|
+
ambientDropPeriod: ambientDropPeriod
|
|
1793
|
+
});
|
|
1794
|
+
var pointer = useWaterPointer(sim.queueDrop, pointerStrength);
|
|
1795
|
+
|
|
1796
|
+
// Water plane: flat in XZ (baked rotateX so AO override passes see a
|
|
1797
|
+
// horizontal surface in the depth buffer).
|
|
1798
|
+
var waterGeom = useMemo(function () {
|
|
1799
|
+
var segs = function segs(extent) {
|
|
1800
|
+
return Math.max(32, Math.min(256, Math.round(50 * extent)));
|
|
1801
|
+
};
|
|
1802
|
+
var geom = new PlaneGeometry(2 * poolHalf.x, 2 * poolHalf.z, segs(2 * poolHalf.x), segs(2 * poolHalf.z));
|
|
1803
|
+
geom.rotateX(Math.PI / 2);
|
|
1804
|
+
var nArr = geom.attributes.normal.array;
|
|
1805
|
+
for (var i = 1; i < nArr.length; i += 3) nArr[i] = 1;
|
|
1806
|
+
return geom;
|
|
1807
|
+
}, [poolHalf.x, poolHalf.z]);
|
|
1808
|
+
var raycastGeom = useMemo(function () {
|
|
1809
|
+
return new PlaneGeometry(2 * poolHalf.x, 2 * poolHalf.z);
|
|
1810
|
+
}, [poolHalf.x, poolHalf.z]);
|
|
1811
|
+
var raycastMat = useMemo(function () {
|
|
1812
|
+
return new MeshBasicMaterial({
|
|
1813
|
+
colorWrite: false,
|
|
1814
|
+
depthWrite: false,
|
|
1815
|
+
side: DoubleSide
|
|
1816
|
+
});
|
|
1817
|
+
}, []);
|
|
1818
|
+
|
|
1819
|
+
// Surface material. Shares the wall/sky/light uniform schema plus the
|
|
1820
|
+
// `uPoolHalf` uniform; per-material uniforms (eye) are bound separately.
|
|
1821
|
+
var surfaceMaterial = useMemo(function () {
|
|
1822
|
+
return createWaterSurfaceAboveMaterial({
|
|
1823
|
+
uPoolHalf: poolHalf.clone(),
|
|
1824
|
+
uWallColor: colorToVec3(wallColor),
|
|
1825
|
+
uSkyHorizonColor: colorToVec3(skyHorizonColor),
|
|
1826
|
+
uSkyZenithColor: colorToVec3(skyZenithColor)
|
|
1827
|
+
});
|
|
1828
|
+
},
|
|
1829
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1830
|
+
[]);
|
|
1831
|
+
useEffect(function () {
|
|
1832
|
+
return function () {
|
|
1833
|
+
waterGeom.dispose();
|
|
1834
|
+
raycastGeom.dispose();
|
|
1835
|
+
};
|
|
1836
|
+
}, [waterGeom, raycastGeom]);
|
|
1837
|
+
|
|
1838
|
+
// Both materials are memoized for the component's lifetime, so their disposal has to be too:
|
|
1839
|
+
// sharing the geometries' effect freed the LIVE material on every pool resize, forcing three to
|
|
1840
|
+
// re-link the water shader on the very frame the pool changes size.
|
|
1841
|
+
useEffect(function () {
|
|
1842
|
+
return function () {
|
|
1843
|
+
raycastMat.dispose();
|
|
1844
|
+
surfaceMaterial.dispose();
|
|
1845
|
+
};
|
|
1846
|
+
}, [raycastMat, surfaceMaterial]);
|
|
1847
|
+
|
|
1848
|
+
// The material outlives pool-aspect changes, and out-of-tree caustic
|
|
1849
|
+
// receivers must sample the right AABB on the very first frame after a
|
|
1850
|
+
// resize — so both copies of uPoolHalf are refreshed here.
|
|
1851
|
+
useEffect(function () {
|
|
1852
|
+
updateSharedUniforms(surfaceMaterial, {
|
|
1853
|
+
uPoolHalf: poolHalf
|
|
1854
|
+
});
|
|
1855
|
+
waterUniforms.poolHalf.copy(poolHalf);
|
|
1856
|
+
}, [poolHalf, surfaceMaterial]);
|
|
1857
|
+
|
|
1858
|
+
// Flip the receiver singleton off when this water instance unmounts so
|
|
1859
|
+
// receivers stop reading stale (disposed) texture refs. The publish path
|
|
1860
|
+
// below sets `enabled = true` once the first frame's heightfield is ready.
|
|
1861
|
+
useEffect(function () {
|
|
1862
|
+
return function () {
|
|
1863
|
+
disableWaterUniforms();
|
|
1864
|
+
};
|
|
1865
|
+
}, []);
|
|
1866
|
+
|
|
1867
|
+
// Dispose the capture and height-map render targets on unmount. Both are
|
|
1868
|
+
// recreated on the next mount via the lazy `null` inits above.
|
|
1869
|
+
useEffect(function () {
|
|
1870
|
+
return function () {
|
|
1871
|
+
var _refractionRef$curren, _poolHeightMapRef$cur;
|
|
1872
|
+
(_refractionRef$curren = refractionRef.current) === null || _refractionRef$curren === void 0 || _refractionRef$curren.dispose();
|
|
1873
|
+
refractionRef.current = null;
|
|
1874
|
+
(_poolHeightMapRef$cur = poolHeightMapRef.current) === null || _poolHeightMapRef$cur === void 0 || _poolHeightMapRef$cur.dispose();
|
|
1875
|
+
poolHeightMapRef.current = null;
|
|
1876
|
+
};
|
|
1877
|
+
}, []);
|
|
1878
|
+
useEffect(function () {
|
|
1879
|
+
var wc = colorToVec3(wallColor);
|
|
1880
|
+
var hc = colorToVec3(skyHorizonColor);
|
|
1881
|
+
var zc = colorToVec3(skyZenithColor);
|
|
1882
|
+
updateSharedUniforms(surfaceMaterial, {
|
|
1883
|
+
uWallColor: wc,
|
|
1884
|
+
uSkyHorizonColor: hc,
|
|
1885
|
+
uSkyZenithColor: zc,
|
|
1886
|
+
uWaterClarity: waterClarity
|
|
1887
|
+
});
|
|
1888
|
+
}, [wallColor, skyHorizonColor, skyZenithColor, waterClarity, surfaceMaterial]);
|
|
1889
|
+
useEffect(function () {
|
|
1890
|
+
var c = colorToVec3(refractionFallbackColor);
|
|
1891
|
+
surfaceMaterial.uniforms.uRefractionFallbackColor.value.copy(c);
|
|
1892
|
+
}, [refractionFallbackColor, surfaceMaterial]);
|
|
1893
|
+
|
|
1894
|
+
// Scratch vectors for per-frame conversions — kept outside useFrame to avoid
|
|
1895
|
+
// GC pressure at 60+ fps.
|
|
1896
|
+
var eyeLocal = useRef(new Vector3());
|
|
1897
|
+
var lightLocal = useRef(new Vector3());
|
|
1898
|
+
useFrame(function (_state, delta) {
|
|
1899
|
+
if (!sim.advance(delta)) return;
|
|
1900
|
+
|
|
1901
|
+
// World-space camera + sun direction → pool's local frame.
|
|
1902
|
+
// eye: subtract group world position (identity scale, no division).
|
|
1903
|
+
// light: direction vector — no offset, no scaling, just normalize.
|
|
1904
|
+
var group = groupRef.current;
|
|
1905
|
+
if (group) {
|
|
1906
|
+
var gp = group.position;
|
|
1907
|
+
eyeLocal.current.set(camera.position.x - gp.x, camera.position.y - gp.y, camera.position.z - gp.z);
|
|
1908
|
+
} else {
|
|
1909
|
+
eyeLocal.current.copy(camera.position);
|
|
1910
|
+
}
|
|
1911
|
+
lightLocal.current.set(sunDirection[0], sunDirection[1], sunDirection[2]).normalize();
|
|
1912
|
+
|
|
1913
|
+
// Refreshed EARLY: the height map and caustics passes below need it, and
|
|
1914
|
+
// the capture and the singleton consumers read it later this same frame.
|
|
1915
|
+
if (group) {
|
|
1916
|
+
group.getWorldPosition(waterUniforms.volumeCenter);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
// Receiving geometry is article AND deck, so either version invalidates
|
|
1920
|
+
// the height map and the refraction capture.
|
|
1921
|
+
var store = useRTVStore.getState();
|
|
1922
|
+
var geometryVersion = store.articleVersion + store.decorVersion;
|
|
1923
|
+
|
|
1924
|
+
// Keep the height map current and bind it to the caustics pass. The water
|
|
1925
|
+
// group is excluded — none of its meshes are receiving geometry.
|
|
1926
|
+
var heightMap = poolHeightMapRef.current;
|
|
1927
|
+
if (heightMap) {
|
|
1928
|
+
heightMap.renderIfNeeded(renderer, sceneR3F, waterUniforms.volumeCenter, poolHalf, geometryVersion, [group]);
|
|
1929
|
+
sim.setHeightMap(heightMap.texture);
|
|
1930
|
+
}
|
|
1931
|
+
sim.renderCaustics(lightLocal.current, causticIntensity);
|
|
1932
|
+
updateSharedUniforms(surfaceMaterial, {
|
|
1933
|
+
water: sim.heightTexture,
|
|
1934
|
+
causticTex: sim.causticTexture,
|
|
1935
|
+
light: lightLocal.current
|
|
1936
|
+
});
|
|
1937
|
+
surfaceMaterial.uniforms.eye.value.copy(eyeLocal.current);
|
|
1938
|
+
|
|
1939
|
+
// Scene capture for screen-space refraction: after the heightfield and
|
|
1940
|
+
// caustics passes above, before the composite.
|
|
1941
|
+
var refraction = refractionRef.current;
|
|
1942
|
+
if (refraction && useScreenSpaceRefraction) {
|
|
1943
|
+
refraction.update({
|
|
1944
|
+
renderer: renderer,
|
|
1945
|
+
scene: sceneR3F,
|
|
1946
|
+
camera: camera,
|
|
1947
|
+
material: surfaceMaterial,
|
|
1948
|
+
surfaceMesh: surfaceMeshRef.current,
|
|
1949
|
+
volumeCenter: waterUniforms.volumeCenter,
|
|
1950
|
+
poolHalf: poolHalf,
|
|
1951
|
+
cssWidth: r3fSize.width,
|
|
1952
|
+
cssHeight: r3fSize.height,
|
|
1953
|
+
geometryVersion: geometryVersion,
|
|
1954
|
+
// Per-frame transform mutations that never bump a version.
|
|
1955
|
+
sceneMutating: store.isDragging || store.accessoryMove !== null
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
// Pushed every frame: both are live config knobs, and a direct uniform
|
|
1959
|
+
// write keeps them in sync without depending on a re-render.
|
|
1960
|
+
surfaceMaterial.uniforms.uUseScreenSpaceRefraction.value = useScreenSpaceRefraction ? 1.0 : 0.0;
|
|
1961
|
+
surfaceMaterial.uniforms.uFresnelMax.value = fresnelMax;
|
|
1962
|
+
|
|
1963
|
+
// Publish into the singleton so out-of-tree caustic receivers sample the
|
|
1964
|
+
// same heightfield + caustic texture this frame.
|
|
1965
|
+
waterUniforms.waterTex = sim.heightTexture;
|
|
1966
|
+
waterUniforms.causticTex = sim.causticTexture;
|
|
1967
|
+
waterUniforms.light.copy(lightLocal.current);
|
|
1968
|
+
publishCausticDebugMode(surfaceMaterial, causticDebugMode);
|
|
1969
|
+
surfaceMaterial.uniforms.uPoolHeightMap.value = heightMap ? heightMap.texture : null;
|
|
1970
|
+
waterUniforms.enabled = true;
|
|
1971
|
+
}, FRAME_ORDER.WATER_CAPTURE);
|
|
1972
|
+
return /*#__PURE__*/jsxs("group", {
|
|
1973
|
+
ref: groupRef,
|
|
1974
|
+
position: position,
|
|
1975
|
+
children: [/*#__PURE__*/jsx("mesh", {
|
|
1976
|
+
ref: surfaceMeshRef,
|
|
1977
|
+
geometry: waterGeom,
|
|
1978
|
+
material: surfaceMaterial,
|
|
1979
|
+
raycast: NOOP_RAYCAST
|
|
1980
|
+
}), /*#__PURE__*/jsx("mesh", {
|
|
1981
|
+
geometry: raycastGeom,
|
|
1982
|
+
material: raycastMat,
|
|
1983
|
+
rotation: [-Math.PI / 2, 0, 0],
|
|
1984
|
+
onPointerMove: pointer.onPointerMove,
|
|
1985
|
+
onPointerDown: pointer.onPointerDown
|
|
1986
|
+
})]
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
var NOOP_RAYCAST = function NOOP_RAYCAST() {};
|
|
1990
|
+
|
|
1991
|
+
/** `'#rrggbb'` → linear-RGB Vector3 in [0, 1]. */
|
|
1992
|
+
function colorToVec3(hex) {
|
|
1993
|
+
var c = new Color(hex);
|
|
1994
|
+
return new Vector3(c.r, c.g, c.b);
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
function WaterSurface() {
|
|
1998
|
+
var _waterVolumeOverride$, _waterVolumeOverride$2;
|
|
1999
|
+
var water = useRTVStore(function (s) {
|
|
2000
|
+
return s.resolvedEnvironment.water;
|
|
2001
|
+
});
|
|
2002
|
+
var lightingSun = useRTVStore(function (s) {
|
|
2003
|
+
return s.resolvedEnvironment.lighting.sun;
|
|
2004
|
+
});
|
|
2005
|
+
// Article-derived volume from `DeckCutoutArticleSync` (deckCutout.waterSync);
|
|
2006
|
+
// wins over the configured volume while set.
|
|
2007
|
+
var waterVolumeOverride = useRTVStore(function (s) {
|
|
2008
|
+
return s.waterVolumeOverride;
|
|
2009
|
+
});
|
|
2010
|
+
|
|
2011
|
+
// Caustic light follows the scene sun unless the preset overrides it
|
|
2012
|
+
// (`water.causticSun`) — e.g. villa-cliff's 2.2° sunset disc is too
|
|
2013
|
+
// grazing to steer the underwater rim shadow with.
|
|
2014
|
+
var causticSun = water.causticSun;
|
|
2015
|
+
var causticSunDirection = useMemo(function () {
|
|
2016
|
+
return causticSun ? sunAnglesToDirection(causticSun.azimuth, causticSun.elevation) : lightingSunTowardDirection(lightingSun);
|
|
2017
|
+
}, [causticSun, lightingSun]);
|
|
2018
|
+
|
|
2019
|
+
// The renderer owns its own world transform via volume.center / .size.
|
|
2020
|
+
return /*#__PURE__*/jsx(WaterSurfaceRenderer, {
|
|
2021
|
+
position: (_waterVolumeOverride$ = waterVolumeOverride === null || waterVolumeOverride === void 0 ? void 0 : waterVolumeOverride.center) !== null && _waterVolumeOverride$ !== void 0 ? _waterVolumeOverride$ : water.volume.center,
|
|
2022
|
+
volumeSize: (_waterVolumeOverride$2 = waterVolumeOverride === null || waterVolumeOverride === void 0 ? void 0 : waterVolumeOverride.size) !== null && _waterVolumeOverride$2 !== void 0 ? _waterVolumeOverride$2 : water.volume.size,
|
|
2023
|
+
sunDirection: causticSunDirection,
|
|
2024
|
+
wallColor: water.wallColor,
|
|
2025
|
+
skyHorizonColor: water.skyHorizonColor,
|
|
2026
|
+
skyZenithColor: water.skyZenithColor,
|
|
2027
|
+
waterClarity: water.waterClarity,
|
|
2028
|
+
ambientDropPeriod: water.ambientDropPeriod,
|
|
2029
|
+
pointerStrength: water.pointerStrength,
|
|
2030
|
+
heightfieldSize: water.heightfieldSize,
|
|
2031
|
+
causticTextureSize: water.causticTextureSize,
|
|
2032
|
+
causticIntensity: water.causticIntensity,
|
|
2033
|
+
useScreenSpaceRefraction: water.useScreenSpaceRefraction,
|
|
2034
|
+
refractionFallbackColor: water.refractionFallbackColor,
|
|
2035
|
+
fresnelMax: water.fresnelMax,
|
|
2036
|
+
causticDebugMode: water.causticDebugMode,
|
|
2037
|
+
substeps: water.waveSpeed
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
export { WaterSurface };
|