@3driseai/3drise-viewer 0.1.27 → 0.1.29
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/dist/animationsV2/ClipPlayer.d.ts.map +1 -1
- package/dist/animationsV2/ClipPlayer.js +13 -9
- package/dist/animationsV2/ClipsRuntime.d.ts +44 -3
- package/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
- package/dist/animationsV2/ClipsRuntime.js +173 -55
- package/dist/animationsV2/appliers.d.ts.map +1 -1
- package/dist/animationsV2/appliers.js +21 -5
- package/dist/animationsV2/index.d.ts +1 -1
- package/dist/animationsV2/index.d.ts.map +1 -1
- package/dist/animationsV2/normalize.d.ts.map +1 -1
- package/dist/animationsV2/normalize.js +7 -3
- package/dist/animationsV2/sequenceUtils.d.ts +44 -6
- package/dist/animationsV2/sequenceUtils.d.ts.map +1 -1
- package/dist/animationsV2/sequenceUtils.js +58 -12
- package/dist/animationsV2/types.d.ts +22 -12
- package/dist/animationsV2/types.d.ts.map +1 -1
- package/dist/api/sceneHandle.d.ts +35 -1
- package/dist/api/sceneHandle.d.ts.map +1 -1
- package/dist/api/sceneHandle.js +88 -17
- package/dist/components/AdvancedGrid.d.ts +1 -5
- package/dist/components/AdvancedGrid.d.ts.map +1 -1
- package/dist/components/AdvancedGrid.js +6 -35
- package/dist/components/AnimatedObject.d.ts.map +1 -1
- package/dist/components/AnimatedObject.js +9 -4
- package/dist/components/ApollonianGrid.d.ts +25 -0
- package/dist/components/ApollonianGrid.d.ts.map +1 -0
- package/dist/components/ApollonianGrid.js +55 -0
- package/dist/components/ChladniGrid.d.ts +1 -1
- package/dist/components/ChladniGrid.d.ts.map +1 -1
- package/dist/components/ChladniGrid.js +6 -35
- package/dist/components/CircuitGrid.d.ts +1 -1
- package/dist/components/CircuitGrid.d.ts.map +1 -1
- package/dist/components/CircuitGrid.js +6 -34
- package/dist/components/ConformalGrid.d.ts +25 -0
- package/dist/components/ConformalGrid.d.ts.map +1 -0
- package/dist/components/ConformalGrid.js +66 -0
- package/dist/components/ContourGrid.d.ts +1 -1
- package/dist/components/ContourGrid.d.ts.map +1 -1
- package/dist/components/ContourGrid.js +6 -34
- package/dist/components/CustomPrimitive.js +1 -1
- package/dist/components/EnvironmentObject.d.ts.map +1 -1
- package/dist/components/EnvironmentObject.js +9 -7
- package/dist/components/FlowNetGrid.d.ts +1 -1
- package/dist/components/FlowNetGrid.d.ts.map +1 -1
- package/dist/components/FlowNetGrid.js +6 -34
- package/dist/components/FractalGrid.d.ts +1 -1
- package/dist/components/FractalGrid.d.ts.map +1 -1
- package/dist/components/FractalGrid.js +6 -37
- package/dist/components/Galaxy.d.ts.map +1 -1
- package/dist/components/Galaxy.js +9 -23
- package/dist/components/GirihGrid.d.ts +1 -1
- package/dist/components/GirihGrid.d.ts.map +1 -1
- package/dist/components/GirihGrid.js +6 -35
- package/dist/components/GravWaveGrid.d.ts +1 -1
- package/dist/components/GravWaveGrid.d.ts.map +1 -1
- package/dist/components/GravWaveGrid.js +6 -35
- package/dist/components/GridsGenerator.d.ts +2 -2
- package/dist/components/GridsGenerator.d.ts.map +1 -1
- package/dist/components/GridsGenerator.js +21 -0
- package/dist/components/HexGrid.d.ts +1 -5
- package/dist/components/HexGrid.d.ts.map +1 -1
- package/dist/components/HexGrid.js +6 -36
- package/dist/components/HilbertGrid.d.ts +25 -0
- package/dist/components/HilbertGrid.d.ts.map +1 -0
- package/dist/components/HilbertGrid.js +66 -0
- package/dist/components/HyperbolicGrid.d.ts +1 -1
- package/dist/components/HyperbolicGrid.d.ts.map +1 -1
- package/dist/components/HyperbolicGrid.js +6 -34
- package/dist/components/LandObject.d.ts +9 -0
- package/dist/components/LandObject.d.ts.map +1 -0
- package/dist/components/LandObject.js +15 -0
- package/dist/components/LensingGrid.d.ts +25 -0
- package/dist/components/LensingGrid.d.ts.map +1 -0
- package/dist/components/LensingGrid.js +55 -0
- package/dist/components/LightsManager.d.ts.map +1 -1
- package/dist/components/LightsManager.js +8 -0
- package/dist/components/ModelBoundary.d.ts +2 -0
- package/dist/components/ModelBoundary.d.ts.map +1 -1
- package/dist/components/ModelBoundary.js +3 -0
- package/dist/components/ModelPrimitive.d.ts.map +1 -1
- package/dist/components/ModelPrimitive.js +6 -0
- package/dist/components/MoireGrid.d.ts +1 -1
- package/dist/components/MoireGrid.d.ts.map +1 -1
- package/dist/components/MoireGrid.js +6 -35
- package/dist/components/MoonDisc.d.ts +11 -0
- package/dist/components/MoonDisc.d.ts.map +1 -0
- package/dist/components/MoonDisc.js +128 -0
- package/dist/components/ObjectNode.d.ts.map +1 -1
- package/dist/components/ObjectNode.js +15 -4
- package/dist/components/ObjectsRenderer.d.ts.map +1 -1
- package/dist/components/ObjectsRenderer.js +7 -5
- package/dist/components/OceanBuilder.d.ts.map +1 -1
- package/dist/components/OceanBuilder.js +2 -0
- package/dist/components/ParastichyGrid.d.ts +25 -0
- package/dist/components/ParastichyGrid.d.ts.map +1 -0
- package/dist/components/ParastichyGrid.js +55 -0
- package/dist/components/PhyllotaxisGrid.d.ts +25 -0
- package/dist/components/PhyllotaxisGrid.d.ts.map +1 -0
- package/dist/components/PhyllotaxisGrid.js +66 -0
- package/dist/components/ProjectLoader.d.ts +5 -3
- package/dist/components/ProjectLoader.d.ts.map +1 -1
- package/dist/components/ProjectLoader.js +6 -3
- package/dist/components/QuasiGrid.d.ts +1 -1
- package/dist/components/QuasiGrid.d.ts.map +1 -1
- package/dist/components/QuasiGrid.js +6 -33
- package/dist/components/RadarGrid.d.ts +1 -1
- package/dist/components/RadarGrid.d.ts.map +1 -1
- package/dist/components/RadarGrid.js +6 -34
- package/dist/components/ReactionDiffusionGrid.d.ts +1 -1
- package/dist/components/ReactionDiffusionGrid.d.ts.map +1 -1
- package/dist/components/ReactionDiffusionGrid.js +6 -35
- package/dist/components/SceneBuilder.d.ts +25 -3
- package/dist/components/SceneBuilder.d.ts.map +1 -1
- package/dist/components/SceneBuilder.js +68 -38
- package/dist/components/SkyController.d.ts +25 -1
- package/dist/components/SkyController.d.ts.map +1 -1
- package/dist/components/SkyController.js +189 -130
- package/dist/components/SkyMesh.d.ts.map +1 -1
- package/dist/components/SkyMesh.js +26 -1
- package/dist/components/SkyStars.d.ts +11 -0
- package/dist/components/SkyStars.d.ts.map +1 -0
- package/dist/components/SkyStars.js +187 -0
- package/dist/components/SkySystem.d.ts +3 -3
- package/dist/components/SkySystem.d.ts.map +1 -1
- package/dist/components/SkySystem.js +8 -2
- package/dist/components/Sun.d.ts +15 -1
- package/dist/components/Sun.d.ts.map +1 -1
- package/dist/components/Sun.js +64 -3
- package/dist/components/SunLight.d.ts +6 -1
- package/dist/components/SunLight.d.ts.map +1 -1
- package/dist/components/SunLight.js +96 -44
- package/dist/components/TerrainBuilder.d.ts.map +1 -1
- package/dist/components/TerrainBuilder.js +2 -0
- package/dist/components/TextGenerator.d.ts +9 -0
- package/dist/components/TextGenerator.d.ts.map +1 -0
- package/dist/components/TextGenerator.js +29 -0
- package/dist/components/TruchetGrid.d.ts +25 -0
- package/dist/components/TruchetGrid.d.ts.map +1 -0
- package/dist/components/TruchetGrid.js +55 -0
- package/dist/components/VoronoiGrid.d.ts +1 -1
- package/dist/components/VoronoiGrid.d.ts.map +1 -1
- package/dist/components/VoronoiGrid.js +6 -33
- package/dist/components/WallpaperGrid.d.ts +1 -1
- package/dist/components/WallpaperGrid.d.ts.map +1 -1
- package/dist/components/WallpaperGrid.js +6 -35
- package/dist/components/environment/Rain.d.ts +15 -0
- package/dist/components/environment/Rain.d.ts.map +1 -0
- package/dist/components/environment/Rain.js +110 -0
- package/dist/components/environment/Rainbow.d.ts +20 -0
- package/dist/components/environment/Rainbow.d.ts.map +1 -0
- package/dist/components/environment/Rainbow.js +974 -0
- package/dist/components/environment/Sandstorm.d.ts +20 -0
- package/dist/components/environment/Sandstorm.d.ts.map +1 -0
- package/dist/components/environment/Sandstorm.js +776 -0
- package/dist/components/environment/Snow.d.ts +20 -0
- package/dist/components/environment/Snow.d.ts.map +1 -0
- package/dist/components/environment/Snow.js +609 -0
- package/dist/components/environment/Thunderstorm.d.ts +20 -0
- package/dist/components/environment/Thunderstorm.d.ts.map +1 -0
- package/dist/components/environment/Thunderstorm.js +731 -0
- package/dist/components/environment/Tornado.d.ts +20 -0
- package/dist/components/environment/Tornado.d.ts.map +1 -0
- package/dist/components/environment/Tornado.js +762 -0
- package/dist/components/environment/index.d.ts +13 -0
- package/dist/components/environment/index.d.ts.map +1 -0
- package/dist/components/environment/index.js +7 -0
- package/dist/components/index.d.ts +24 -10
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +15 -6
- package/dist/components/land/LandPatch.d.ts +15 -0
- package/dist/components/land/LandPatch.d.ts.map +1 -0
- package/dist/components/land/LandPatch.js +170 -0
- package/dist/components/land/index.d.ts +3 -0
- package/dist/components/land/index.d.ts.map +1 -0
- package/dist/components/land/index.js +1 -0
- package/dist/components/space/Earth.d.ts +16 -0
- package/dist/components/space/Earth.d.ts.map +1 -0
- package/dist/components/space/Earth.js +75 -0
- package/dist/components/space/ShootingStar.d.ts +21 -0
- package/dist/components/space/ShootingStar.d.ts.map +1 -0
- package/dist/components/space/ShootingStar.js +101 -0
- package/dist/components/space/ShootingStars.d.ts +16 -0
- package/dist/components/space/ShootingStars.d.ts.map +1 -0
- package/dist/components/space/ShootingStars.js +71 -0
- package/dist/components/space/SolarSystem.d.ts +16 -0
- package/dist/components/space/SolarSystem.d.ts.map +1 -0
- package/dist/components/space/SolarSystem.js +141 -0
- package/dist/components/space/Stars.d.ts +20 -0
- package/dist/components/space/Stars.d.ts.map +1 -0
- package/dist/components/space/Stars.js +160 -0
- package/dist/components/space/index.d.ts +11 -0
- package/dist/components/space/index.d.ts.map +1 -0
- package/dist/components/space/index.js +8 -0
- package/dist/components/terrain/TerrainSurface.d.ts.map +1 -1
- package/dist/components/terrain/TerrainSurface.js +27 -3
- package/dist/components/terrain/types.d.ts +5 -1
- package/dist/components/terrain/types.d.ts.map +1 -1
- package/dist/components/text/BurnAwayText.d.ts +15 -0
- package/dist/components/text/BurnAwayText.d.ts.map +1 -0
- package/dist/components/text/BurnAwayText.js +4 -0
- package/dist/components/text/ChalkboardText.d.ts +15 -0
- package/dist/components/text/ChalkboardText.d.ts.map +1 -0
- package/dist/components/text/ChalkboardText.js +4 -0
- package/dist/components/text/ConstellationText.d.ts +15 -0
- package/dist/components/text/ConstellationText.d.ts.map +1 -0
- package/dist/components/text/ConstellationText.js +4 -0
- package/dist/components/text/EmbroideryText.d.ts +15 -0
- package/dist/components/text/EmbroideryText.d.ts.map +1 -0
- package/dist/components/text/EmbroideryText.js +4 -0
- package/dist/components/text/FourierSketchText.d.ts +15 -0
- package/dist/components/text/FourierSketchText.d.ts.map +1 -0
- package/dist/components/text/FourierSketchText.js +4 -0
- package/dist/components/text/FrostGrowthText.d.ts +15 -0
- package/dist/components/text/FrostGrowthText.d.ts.map +1 -0
- package/dist/components/text/FrostGrowthText.js +4 -0
- package/dist/components/text/HandwritingText.d.ts +15 -0
- package/dist/components/text/HandwritingText.d.ts.map +1 -0
- package/dist/components/text/HandwritingText.js +4 -0
- package/dist/components/text/InkBleedText.d.ts +15 -0
- package/dist/components/text/InkBleedText.d.ts.map +1 -0
- package/dist/components/text/InkBleedText.js +4 -0
- package/dist/components/text/LightPaintingText.d.ts +15 -0
- package/dist/components/text/LightPaintingText.d.ts.map +1 -0
- package/dist/components/text/LightPaintingText.js +4 -0
- package/dist/components/text/LiquidMetalText.d.ts +15 -0
- package/dist/components/text/LiquidMetalText.d.ts.map +1 -0
- package/dist/components/text/LiquidMetalText.js +4 -0
- package/dist/components/text/NeonTubeText.d.ts +15 -0
- package/dist/components/text/NeonTubeText.d.ts.map +1 -0
- package/dist/components/text/NeonTubeText.js +4 -0
- package/dist/components/text/PlasmaDischargeText.d.ts +15 -0
- package/dist/components/text/PlasmaDischargeText.d.ts.map +1 -0
- package/dist/components/text/PlasmaDischargeText.js +4 -0
- package/dist/components/text/TextObjectView.d.ts +12 -0
- package/dist/components/text/TextObjectView.d.ts.map +1 -0
- package/dist/components/text/TextObjectView.js +11 -0
- package/dist/components/text/engine/fieldkit.d.ts +24 -0
- package/dist/components/text/engine/fieldkit.d.ts.map +1 -0
- package/dist/components/text/engine/fieldkit.js +201 -0
- package/dist/components/text/engine/glyphs.d.ts +19 -0
- package/dist/components/text/engine/glyphs.d.ts.map +1 -0
- package/dist/components/text/engine/glyphs.js +269 -0
- package/dist/components/text/engine/looks/burnAway.d.ts +144 -0
- package/dist/components/text/engine/looks/burnAway.d.ts.map +1 -0
- package/dist/components/text/engine/looks/burnAway.js +593 -0
- package/dist/components/text/engine/looks/chalkboard.d.ts +135 -0
- package/dist/components/text/engine/looks/chalkboard.d.ts.map +1 -0
- package/dist/components/text/engine/looks/chalkboard.js +426 -0
- package/dist/components/text/engine/looks/constellation.d.ts +111 -0
- package/dist/components/text/engine/looks/constellation.d.ts.map +1 -0
- package/dist/components/text/engine/looks/constellation.js +496 -0
- package/dist/components/text/engine/looks/embroidery.d.ts +165 -0
- package/dist/components/text/engine/looks/embroidery.d.ts.map +1 -0
- package/dist/components/text/engine/looks/embroidery.js +645 -0
- package/dist/components/text/engine/looks/frostGrowth.d.ts +109 -0
- package/dist/components/text/engine/looks/frostGrowth.d.ts.map +1 -0
- package/dist/components/text/engine/looks/frostGrowth.js +727 -0
- package/dist/components/text/engine/looks/index.d.ts +2 -0
- package/dist/components/text/engine/looks/index.d.ts.map +1 -0
- package/dist/components/text/engine/looks/index.js +48 -0
- package/dist/components/text/engine/looks/inkBleed.d.ts +103 -0
- package/dist/components/text/engine/looks/inkBleed.d.ts.map +1 -0
- package/dist/components/text/engine/looks/inkBleed.js +590 -0
- package/dist/components/text/engine/looks/lightPainting.d.ts +132 -0
- package/dist/components/text/engine/looks/lightPainting.d.ts.map +1 -0
- package/dist/components/text/engine/looks/lightPainting.js +567 -0
- package/dist/components/text/engine/looks/liquidMetal.d.ts +135 -0
- package/dist/components/text/engine/looks/liquidMetal.d.ts.map +1 -0
- package/dist/components/text/engine/looks/liquidMetal.js +655 -0
- package/dist/components/text/engine/looks/plasmaDischarge.d.ts +126 -0
- package/dist/components/text/engine/looks/plasmaDischarge.d.ts.map +1 -0
- package/dist/components/text/engine/looks/plasmaDischarge.js +763 -0
- package/dist/components/text/engine/methods.d.ts +19 -0
- package/dist/components/text/engine/methods.d.ts.map +1 -0
- package/dist/components/text/engine/methods.js +126 -0
- package/dist/components/text/engine/playback.d.ts +40 -0
- package/dist/components/text/engine/playback.d.ts.map +1 -0
- package/dist/components/text/engine/playback.js +114 -0
- package/dist/components/text/engine/ribbon.d.ts +58 -0
- package/dist/components/text/engine/ribbon.d.ts.map +1 -0
- package/dist/components/text/engine/ribbon.js +250 -0
- package/dist/components/text/engine/variants.d.ts +159 -0
- package/dist/components/text/engine/variants.d.ts.map +1 -0
- package/dist/components/text/engine/variants.js +378 -0
- package/dist/components/text/fonts/architects-daughter.outline.d.ts +3 -0
- package/dist/components/text/fonts/architects-daughter.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/architects-daughter.outline.js +4 -0
- package/dist/components/text/fonts/architects-daughter.strokes.d.ts +3 -0
- package/dist/components/text/fonts/architects-daughter.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/architects-daughter.strokes.js +4 -0
- package/dist/components/text/fonts/caveat.outline.d.ts +3 -0
- package/dist/components/text/fonts/caveat.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/caveat.outline.js +4 -0
- package/dist/components/text/fonts/caveat.strokes.d.ts +3 -0
- package/dist/components/text/fonts/caveat.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/caveat.strokes.js +4 -0
- package/dist/components/text/fonts/courgette.outline.d.ts +3 -0
- package/dist/components/text/fonts/courgette.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/courgette.outline.js +4 -0
- package/dist/components/text/fonts/courgette.strokes.d.ts +3 -0
- package/dist/components/text/fonts/courgette.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/courgette.strokes.js +4 -0
- package/dist/components/text/fonts/dancing-script.outline.d.ts +3 -0
- package/dist/components/text/fonts/dancing-script.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/dancing-script.outline.js +4 -0
- package/dist/components/text/fonts/dancing-script.strokes.d.ts +3 -0
- package/dist/components/text/fonts/dancing-script.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/dancing-script.strokes.js +4 -0
- package/dist/components/text/fonts/gochi-hand.outline.d.ts +3 -0
- package/dist/components/text/fonts/gochi-hand.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/gochi-hand.outline.js +4 -0
- package/dist/components/text/fonts/gochi-hand.strokes.d.ts +3 -0
- package/dist/components/text/fonts/gochi-hand.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/gochi-hand.strokes.js +4 -0
- package/dist/components/text/fonts/handlee.outline.d.ts +3 -0
- package/dist/components/text/fonts/handlee.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/handlee.outline.js +4 -0
- package/dist/components/text/fonts/handlee.strokes.d.ts +3 -0
- package/dist/components/text/fonts/handlee.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/handlee.strokes.js +4 -0
- package/dist/components/text/fonts/index.d.ts +5 -0
- package/dist/components/text/fonts/index.d.ts.map +1 -0
- package/dist/components/text/fonts/index.js +44 -0
- package/dist/components/text/fonts/indie-flower.outline.d.ts +3 -0
- package/dist/components/text/fonts/indie-flower.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/indie-flower.outline.js +4 -0
- package/dist/components/text/fonts/indie-flower.strokes.d.ts +3 -0
- package/dist/components/text/fonts/indie-flower.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/indie-flower.strokes.js +4 -0
- package/dist/components/text/fonts/kalam.outline.d.ts +3 -0
- package/dist/components/text/fonts/kalam.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/kalam.outline.js +4 -0
- package/dist/components/text/fonts/kalam.strokes.d.ts +3 -0
- package/dist/components/text/fonts/kalam.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/kalam.strokes.js +4 -0
- package/dist/components/text/fonts/kaushan-script.outline.d.ts +3 -0
- package/dist/components/text/fonts/kaushan-script.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/kaushan-script.outline.js +4 -0
- package/dist/components/text/fonts/kaushan-script.strokes.d.ts +3 -0
- package/dist/components/text/fonts/kaushan-script.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/kaushan-script.strokes.js +4 -0
- package/dist/components/text/fonts/mynerve.outline.d.ts +3 -0
- package/dist/components/text/fonts/mynerve.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/mynerve.outline.js +4 -0
- package/dist/components/text/fonts/mynerve.strokes.d.ts +3 -0
- package/dist/components/text/fonts/mynerve.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/mynerve.strokes.js +4 -0
- package/dist/components/text/fonts/nanum-pen-script.outline.d.ts +3 -0
- package/dist/components/text/fonts/nanum-pen-script.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/nanum-pen-script.outline.js +4 -0
- package/dist/components/text/fonts/nanum-pen-script.strokes.d.ts +3 -0
- package/dist/components/text/fonts/nanum-pen-script.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/nanum-pen-script.strokes.js +4 -0
- package/dist/components/text/fonts/pacifico.outline.d.ts +3 -0
- package/dist/components/text/fonts/pacifico.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/pacifico.outline.js +4 -0
- package/dist/components/text/fonts/pacifico.strokes.d.ts +3 -0
- package/dist/components/text/fonts/pacifico.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/pacifico.strokes.js +4 -0
- package/dist/components/text/fonts/patrick-hand.outline.d.ts +3 -0
- package/dist/components/text/fonts/patrick-hand.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/patrick-hand.outline.js +4 -0
- package/dist/components/text/fonts/patrick-hand.strokes.d.ts +3 -0
- package/dist/components/text/fonts/patrick-hand.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/patrick-hand.strokes.js +4 -0
- package/dist/components/text/fonts/poppins.outline.d.ts +3 -0
- package/dist/components/text/fonts/poppins.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/poppins.outline.js +4 -0
- package/dist/components/text/fonts/poppins.strokes.d.ts +3 -0
- package/dist/components/text/fonts/poppins.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/poppins.strokes.js +4 -0
- package/dist/components/text/fonts/satisfy.outline.d.ts +3 -0
- package/dist/components/text/fonts/satisfy.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/satisfy.outline.js +4 -0
- package/dist/components/text/fonts/satisfy.strokes.d.ts +3 -0
- package/dist/components/text/fonts/satisfy.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/satisfy.strokes.js +4 -0
- package/dist/components/text/fonts/sriracha.outline.d.ts +3 -0
- package/dist/components/text/fonts/sriracha.outline.d.ts.map +1 -0
- package/dist/components/text/fonts/sriracha.outline.js +4 -0
- package/dist/components/text/fonts/sriracha.strokes.d.ts +3 -0
- package/dist/components/text/fonts/sriracha.strokes.d.ts.map +1 -0
- package/dist/components/text/fonts/sriracha.strokes.js +4 -0
- package/dist/components/text/index.d.ts +27 -0
- package/dist/components/text/index.d.ts.map +1 -0
- package/dist/components/text/index.js +13 -0
- package/dist/components/text/useTextObject.d.ts +21 -0
- package/dist/components/text/useTextObject.d.ts.map +1 -0
- package/dist/components/text/useTextObject.js +118 -0
- package/dist/components/water/OceanSurface.d.ts.map +1 -1
- package/dist/components/water/OceanSurface.js +3 -1
- package/dist/components/water/WaterBody.d.ts.map +1 -1
- package/dist/components/water/WaterBody.js +3 -1
- package/dist/components/water/types.d.ts +5 -1
- package/dist/components/water/types.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +5 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useCloudSun.d.ts +9 -4
- package/dist/hooks/useCloudSun.d.ts.map +1 -1
- package/dist/hooks/useCloudSun.js +17 -7
- package/dist/hooks/useConfigLive.d.ts +3 -2
- package/dist/hooks/useConfigLive.d.ts.map +1 -1
- package/dist/hooks/useContinuousEffects.d.ts.map +1 -1
- package/dist/hooks/useContinuousEffects.js +4 -10
- package/dist/hooks/useEnvironmentProbe.d.ts +3 -22
- package/dist/hooks/useEnvironmentProbe.d.ts.map +1 -1
- package/dist/hooks/useEnvironmentProbe.js +47 -8
- package/dist/hooks/useGridSurface.d.ts +3 -10
- package/dist/hooks/useGridSurface.d.ts.map +1 -1
- package/dist/hooks/useGridSurface.js +5 -31
- package/dist/hooks/useInteraction.d.ts.map +1 -1
- package/dist/hooks/useInteraction.js +19 -72
- package/dist/hooks/useLightConfig.d.ts +2 -1
- package/dist/hooks/useLightConfig.d.ts.map +1 -1
- package/dist/hooks/useOceanMaterial.d.ts +6 -2
- package/dist/hooks/useOceanMaterial.d.ts.map +1 -1
- package/dist/hooks/useOceanMaterial.js +37 -2
- package/dist/hooks/useSkyLive.d.ts +23 -0
- package/dist/hooks/useSkyLive.d.ts.map +1 -0
- package/dist/hooks/useSkyLive.js +30 -0
- package/dist/hooks/useSkyUniforms.d.ts.map +1 -1
- package/dist/hooks/useSkyUniforms.js +2 -10
- package/dist/hooks/useTerrainLive.d.ts +3 -1
- package/dist/hooks/useTerrainLive.d.ts.map +1 -1
- package/dist/hooks/useTerrainTextures.d.ts +38 -0
- package/dist/hooks/useTerrainTextures.d.ts.map +1 -0
- package/dist/hooks/useTerrainTextures.js +149 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/operations/catalogue/blastOff.d.ts +3 -0
- package/dist/operations/catalogue/blastOff.d.ts.map +1 -0
- package/dist/operations/catalogue/blastOff.js +118 -0
- package/dist/operations/catalogue/bounce.d.ts +3 -0
- package/dist/operations/catalogue/bounce.d.ts.map +1 -0
- package/dist/operations/catalogue/bounce.js +34 -0
- package/dist/operations/catalogue/curlSmoke.d.ts.map +1 -1
- package/dist/operations/catalogue/curlSmoke.js +3 -1
- package/dist/operations/catalogue/dissolve.d.ts.map +1 -1
- package/dist/operations/catalogue/dissolve.js +2 -1
- package/dist/operations/catalogue/driftUp.d.ts +3 -0
- package/dist/operations/catalogue/driftUp.d.ts.map +1 -0
- package/dist/operations/catalogue/driftUp.js +57 -0
- package/dist/operations/catalogue/emberAsh.d.ts.map +1 -1
- package/dist/operations/catalogue/emberAsh.js +2 -1
- package/dist/operations/catalogue/flip.d.ts.map +1 -1
- package/dist/operations/catalogue/flip.js +19 -9
- package/dist/operations/catalogue/float.d.ts +3 -0
- package/dist/operations/catalogue/float.d.ts.map +1 -0
- package/dist/operations/catalogue/float.js +39 -0
- package/dist/operations/catalogue/flyBy.d.ts +3 -0
- package/dist/operations/catalogue/flyBy.d.ts.map +1 -0
- package/dist/operations/catalogue/flyBy.js +79 -0
- package/dist/operations/catalogue/frost.d.ts.map +1 -1
- package/dist/operations/catalogue/frost.js +10 -3
- package/dist/operations/catalogue/grow.d.ts +3 -0
- package/dist/operations/catalogue/grow.d.ts.map +1 -0
- package/dist/operations/catalogue/grow.js +19 -0
- package/dist/operations/catalogue/hologram.d.ts.map +1 -1
- package/dist/operations/catalogue/hologram.js +11 -5
- package/dist/operations/catalogue/jelly.d.ts.map +1 -1
- package/dist/operations/catalogue/jelly.js +18 -8
- package/dist/operations/catalogue/knockAway.d.ts +3 -0
- package/dist/operations/catalogue/knockAway.d.ts.map +1 -0
- package/dist/operations/catalogue/knockAway.js +74 -0
- package/dist/operations/catalogue/leafFall.d.ts +3 -0
- package/dist/operations/catalogue/leafFall.d.ts.map +1 -0
- package/dist/operations/catalogue/leafFall.js +74 -0
- package/dist/operations/catalogue/lidarScan.d.ts.map +1 -1
- package/dist/operations/catalogue/lidarScan.js +4 -1
- package/dist/operations/catalogue/metalize.d.ts.map +1 -1
- package/dist/operations/catalogue/metalize.js +8 -2
- package/dist/operations/catalogue/motionTrail.d.ts +6 -0
- package/dist/operations/catalogue/motionTrail.d.ts.map +1 -0
- package/dist/operations/catalogue/motionTrail.js +221 -0
- package/dist/operations/catalogue/petrify.d.ts.map +1 -1
- package/dist/operations/catalogue/petrify.js +9 -3
- package/dist/operations/catalogue/physicalExit.d.ts +55 -0
- package/dist/operations/catalogue/physicalExit.d.ts.map +1 -0
- package/dist/operations/catalogue/physicalExit.js +165 -0
- package/dist/operations/catalogue/physicalHeld.d.ts +41 -0
- package/dist/operations/catalogue/physicalHeld.d.ts.map +1 -0
- package/dist/operations/catalogue/physicalHeld.js +78 -0
- package/dist/operations/catalogue/physicalMix.d.ts +45 -0
- package/dist/operations/catalogue/physicalMix.d.ts.map +1 -0
- package/dist/operations/catalogue/physicalMix.js +144 -0
- package/dist/operations/catalogue/pop.d.ts +3 -0
- package/dist/operations/catalogue/pop.d.ts.map +1 -0
- package/dist/operations/catalogue/pop.js +44 -0
- package/dist/operations/catalogue/print3d.d.ts.map +1 -1
- package/dist/operations/catalogue/print3d.js +5 -1
- package/dist/operations/catalogue/pulse.d.ts +3 -0
- package/dist/operations/catalogue/pulse.d.ts.map +1 -0
- package/dist/operations/catalogue/pulse.js +29 -0
- package/dist/operations/catalogue/restPose.d.ts +5 -7
- package/dist/operations/catalogue/restPose.d.ts.map +1 -1
- package/dist/operations/catalogue/restPose.js +18 -48
- package/dist/operations/catalogue/rotate.d.ts +3 -0
- package/dist/operations/catalogue/rotate.d.ts.map +1 -0
- package/dist/operations/catalogue/rotate.js +49 -0
- package/dist/operations/catalogue/shake.d.ts +3 -0
- package/dist/operations/catalogue/shake.d.ts.map +1 -0
- package/dist/operations/catalogue/shake.js +31 -0
- package/dist/operations/catalogue/shatter.d.ts.map +1 -1
- package/dist/operations/catalogue/shatter.js +5 -1
- package/dist/operations/catalogue/slam.d.ts +3 -0
- package/dist/operations/catalogue/slam.d.ts.map +1 -0
- package/dist/operations/catalogue/slam.js +89 -0
- package/dist/operations/catalogue/slingshot.d.ts +3 -0
- package/dist/operations/catalogue/slingshot.d.ts.map +1 -0
- package/dist/operations/catalogue/slingshot.js +88 -0
- package/dist/operations/catalogue/spin.d.ts.map +1 -1
- package/dist/operations/catalogue/spin.js +24 -13
- package/dist/operations/catalogue/sway.d.ts +3 -0
- package/dist/operations/catalogue/sway.d.ts.map +1 -0
- package/dist/operations/catalogue/sway.js +23 -0
- package/dist/operations/catalogue/swirl.d.ts.map +1 -1
- package/dist/operations/catalogue/swirl.js +6 -5
- package/dist/operations/catalogue/trailCore.d.ts +89 -0
- package/dist/operations/catalogue/trailCore.d.ts.map +1 -0
- package/dist/operations/catalogue/trailCore.js +190 -0
- package/dist/operations/catalogue/trailGhosts.d.ts +5 -0
- package/dist/operations/catalogue/trailGhosts.d.ts.map +1 -0
- package/dist/operations/catalogue/trailGhosts.js +179 -0
- package/dist/operations/catalogue/trailStampStyles.d.ts +11 -0
- package/dist/operations/catalogue/trailStampStyles.d.ts.map +1 -0
- package/dist/operations/catalogue/trailStampStyles.js +479 -0
- package/dist/operations/catalogue/trailStamps.d.ts +57 -0
- package/dist/operations/catalogue/trailStamps.d.ts.map +1 -0
- package/dist/operations/catalogue/trailStamps.js +349 -0
- package/dist/operations/catalogue/trailStreaks.d.ts +3 -0
- package/dist/operations/catalogue/trailStreaks.d.ts.map +1 -0
- package/dist/operations/catalogue/trailStreaks.js +147 -0
- package/dist/operations/catalogue/transitionField.d.ts +30 -0
- package/dist/operations/catalogue/transitionField.d.ts.map +1 -1
- package/dist/operations/catalogue/transitionField.js +21 -0
- package/dist/operations/catalogue/turnAway.d.ts +3 -0
- package/dist/operations/catalogue/turnAway.d.ts.map +1 -0
- package/dist/operations/catalogue/turnAway.js +54 -0
- package/dist/operations/catalogue/vortexDrain.d.ts +3 -0
- package/dist/operations/catalogue/vortexDrain.d.ts.map +1 -0
- package/dist/operations/catalogue/vortexDrain.js +68 -0
- package/dist/operations/catalogue/warp.d.ts +3 -0
- package/dist/operations/catalogue/warp.d.ts.map +1 -0
- package/dist/operations/catalogue/warp.js +73 -0
- package/dist/operations/catalogue/whoosh.d.ts +3 -0
- package/dist/operations/catalogue/whoosh.d.ts.map +1 -0
- package/dist/operations/catalogue/whoosh.js +84 -0
- package/dist/operations/catalogue/wobble.d.ts +3 -0
- package/dist/operations/catalogue/wobble.d.ts.map +1 -0
- package/dist/operations/catalogue/wobble.js +23 -0
- package/dist/operations/clipPreview.d.ts +3 -0
- package/dist/operations/clipPreview.d.ts.map +1 -0
- package/dist/operations/clipPreview.js +20 -0
- package/dist/operations/groupFanout.d.ts +96 -0
- package/dist/operations/groupFanout.d.ts.map +1 -0
- package/dist/operations/groupFanout.js +365 -0
- package/dist/operations/groups.d.ts +20 -0
- package/dist/operations/groups.d.ts.map +1 -0
- package/dist/operations/groups.js +66 -0
- package/dist/operations/index.d.ts +5 -2
- package/dist/operations/index.d.ts.map +1 -1
- package/dist/operations/index.js +5 -2
- package/dist/operations/objectEffects.d.ts +161 -6
- package/dist/operations/objectEffects.d.ts.map +1 -1
- package/dist/operations/objectEffects.js +1091 -17
- package/dist/operations/registry.d.ts.map +1 -1
- package/dist/operations/registry.js +24 -2
- package/dist/operations/runtime.d.ts +57 -2
- package/dist/operations/runtime.d.ts.map +1 -1
- package/dist/operations/runtime.js +161 -18
- package/dist/operations/types.d.ts +60 -3
- package/dist/operations/types.d.ts.map +1 -1
- package/dist/operations/types.js +5 -1
- package/dist/operations/useObjectEffectLayers.d.ts +2 -2
- package/dist/operations/useObjectEffectLayers.d.ts.map +1 -1
- package/dist/operations/useObjectEffectLayers.js +31 -10
- package/dist/operations/usePresentedVisibility.d.ts +10 -0
- package/dist/operations/usePresentedVisibility.d.ts.map +1 -0
- package/dist/operations/usePresentedVisibility.js +53 -0
- package/dist/presets/terrainBiomes.d.ts +67 -0
- package/dist/presets/terrainBiomes.d.ts.map +1 -0
- package/dist/presets/terrainBiomes.js +294 -0
- package/dist/presets/terrainPresets.d.ts +6 -0
- package/dist/presets/terrainPresets.d.ts.map +1 -1
- package/dist/presets/terrainPresets.js +26 -38
- package/dist/shaders/glsl/oceanCore.glsl.d.ts +2 -2
- package/dist/shaders/glsl/oceanCore.glsl.d.ts.map +1 -1
- package/dist/shaders/glsl/oceanCore.glsl.js +17 -0
- package/dist/shaders/glsl/surfaceCore.glsl.d.ts +21 -0
- package/dist/shaders/glsl/surfaceCore.glsl.d.ts.map +1 -0
- package/dist/shaders/glsl/surfaceCore.glsl.js +120 -0
- package/dist/shaders/glsl/terrainCore.glsl.d.ts +16 -3
- package/dist/shaders/glsl/terrainCore.glsl.d.ts.map +1 -1
- package/dist/shaders/glsl/terrainCore.glsl.js +266 -20
- package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts +2 -0
- package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts.map +1 -1
- package/dist/shaders/ocean/OceanSurfaceMaterial.js +3 -0
- package/dist/shaders/terrain/TerrainMaterial.d.ts +17 -0
- package/dist/shaders/terrain/TerrainMaterial.d.ts.map +1 -1
- package/dist/shaders/terrain/TerrainMaterial.js +107 -8
- package/dist/triggers/engine.d.ts +44 -0
- package/dist/triggers/engine.d.ts.map +1 -0
- package/dist/triggers/engine.js +165 -0
- package/dist/triggers/hosts/actions.d.ts +8 -0
- package/dist/triggers/hosts/actions.d.ts.map +1 -0
- package/dist/triggers/hosts/actions.js +22 -0
- package/dist/triggers/hosts/interactions.d.ts +6 -0
- package/dist/triggers/hosts/interactions.d.ts.map +1 -0
- package/dist/triggers/hosts/interactions.js +4 -0
- package/dist/triggers/hosts/timelineCues.d.ts +2 -0
- package/dist/triggers/hosts/timelineCues.d.ts.map +1 -0
- package/dist/triggers/hosts/timelineCues.js +22 -0
- package/dist/triggers/index.d.ts +13 -0
- package/dist/triggers/index.d.ts.map +1 -0
- package/dist/triggers/index.js +9 -0
- package/dist/triggers/kinds/action.d.ts +2 -0
- package/dist/triggers/kinds/action.d.ts.map +1 -0
- package/dist/triggers/kinds/action.js +31 -0
- package/dist/triggers/kinds/animations.d.ts +2 -0
- package/dist/triggers/kinds/animations.d.ts.map +1 -0
- package/dist/triggers/kinds/animations.js +75 -0
- package/dist/triggers/kinds/effects.d.ts +2 -0
- package/dist/triggers/kinds/effects.d.ts.map +1 -0
- package/dist/triggers/kinds/effects.js +27 -0
- package/dist/triggers/kinds/index.d.ts +4 -0
- package/dist/triggers/kinds/index.d.ts.map +1 -0
- package/dist/triggers/kinds/index.js +4 -0
- package/dist/triggers/log.d.ts +21 -0
- package/dist/triggers/log.d.ts.map +1 -0
- package/dist/triggers/log.js +43 -0
- package/dist/triggers/readiness.d.ts +40 -0
- package/dist/triggers/readiness.d.ts.map +1 -0
- package/dist/triggers/readiness.js +158 -0
- package/dist/triggers/registry.d.ts +41 -0
- package/dist/triggers/registry.d.ts.map +1 -0
- package/dist/triggers/registry.js +7 -0
- package/dist/types/gridConfigs.d.ts +117 -19
- package/dist/types/gridConfigs.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/ui/UiAnchorUpdater.d.ts +10 -0
- package/dist/ui/UiAnchorUpdater.d.ts.map +1 -0
- package/dist/ui/UiAnchorUpdater.js +111 -0
- package/dist/ui/UiLayers.d.ts +20 -0
- package/dist/ui/UiLayers.d.ts.map +1 -0
- package/dist/ui/UiLayers.js +57 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/uiAnchors.d.ts +21 -0
- package/dist/ui/uiAnchors.d.ts.map +1 -0
- package/dist/ui/uiAnchors.js +22 -0
- package/dist/ui/uiBaseStyles.d.ts +2 -0
- package/dist/ui/uiBaseStyles.d.ts.map +1 -0
- package/dist/ui/uiBaseStyles.js +100 -0
- package/dist/ui/uiBinder.d.ts +30 -0
- package/dist/ui/uiBinder.d.ts.map +1 -0
- package/dist/ui/uiBinder.js +344 -0
- package/dist/utils/configAnimations.d.ts.map +1 -1
- package/dist/utils/configAnimations.js +5 -1
- package/dist/utils/gridConfig.d.ts +1 -1
- package/dist/utils/gridConfig.d.ts.map +1 -1
- package/dist/utils/gridConfig.js +1 -1
- package/dist/utils/landPatch.d.ts +33 -0
- package/dist/utils/landPatch.d.ts.map +1 -0
- package/dist/utils/landPatch.js +114 -0
- package/dist/utils/renderedSky.d.ts +32 -0
- package/dist/utils/renderedSky.d.ts.map +1 -0
- package/dist/utils/renderedSky.js +57 -0
- package/dist/utils/skyMoon.d.ts +35 -0
- package/dist/utils/skyMoon.d.ts.map +1 -0
- package/dist/utils/skyMoon.js +41 -0
- package/dist/utils/terrainHeight.d.ts +4 -0
- package/dist/utils/terrainHeight.d.ts.map +1 -0
- package/dist/utils/terrainHeight.js +150 -0
- package/dist/utils/terrainTextureResolver.d.ts +18 -0
- package/dist/utils/terrainTextureResolver.d.ts.map +1 -0
- package/dist/utils/terrainTextureResolver.js +17 -0
- package/dist/utils/terrainUniforms.d.ts +17 -0
- package/dist/utils/terrainUniforms.d.ts.map +1 -1
- package/dist/utils/terrainUniforms.js +84 -0
- package/dist/utils/textureArrays.d.ts +29 -0
- package/dist/utils/textureArrays.d.ts.map +1 -0
- package/dist/utils/textureArrays.js +140 -0
- package/package.json +2 -2
|
@@ -0,0 +1,974 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
|
3
|
+
import * as THREE from 'three';
|
|
4
|
+
import { useFrame } from '@react-three/fiber';
|
|
5
|
+
import { DEFAULT_ENVIRONMENT_OBJECTS, ENVIRONMENT_OBJECTS_ANIMATABLE } from '@3driseai/3drise-core';
|
|
6
|
+
import { withConfigDefaults } from '../../utils/configAnimations.js';
|
|
7
|
+
import { useObjectReady } from '../../hooks/useObjectReady.js';
|
|
8
|
+
import { useConfigDriver } from '../../hooks/useConfigDriver.js';
|
|
9
|
+
import { useCloudSun } from '../../hooks/useCloudSun.js';
|
|
10
|
+
import { stepPointerField, usePointerField } from '../../utils/pointerField.js';
|
|
11
|
+
// Generated from Claude outputs/environment-lab (common.mjs + rainbow.mjs, the approved lab modules)
|
|
12
|
+
// by gen_family.mjs — edit there and regenerate.
|
|
13
|
+
// ---------------------------------------------------------------------------- shared core
|
|
14
|
+
// Shared core of the environment objects (lab). Inlined verbatim into every generated component.
|
|
15
|
+
//
|
|
16
|
+
// Slice volume: continuous media (dust, rain curtains, funnels) are rendered as N view-aligned
|
|
17
|
+
// slices between the camera and the far side of the volume, drawn back to front in one instanced
|
|
18
|
+
// call. Each slice is a full-screen quad at a fixed view depth, depth-tested against the scene,
|
|
19
|
+
// so objects inside the medium are hidden by exactly the medium in front of them — volume
|
|
20
|
+
// rendering without a depth texture. Slices are spaced geometrically (dense near the eye) and
|
|
21
|
+
// every fragment samples at a per-pixel dithered depth inside its slab, which turns slice
|
|
22
|
+
// banding into fine grain.
|
|
23
|
+
//
|
|
24
|
+
// What a slice volume costs is pixels x slices x the work per sample, so the core keeps all three down:
|
|
25
|
+
// noise vnoise3 is one trilinear fetch from a small 3-D texture holding the very lattice values
|
|
26
|
+
// the hash gave (the smoothstep goes into the coordinate), instead of 8 hashes and 7 mixes.
|
|
27
|
+
// bounds slices cover the volume's box on screen, not the whole screen.
|
|
28
|
+
// density on a dense display (device pixel ratio above 1) neighbouring pixels take turns over the
|
|
29
|
+
// slices (2 or 4 interleaved sets, each sample standing for the slabs it skips), so the
|
|
30
|
+
// samples per CSS pixel — what the eye resolves — stay what they are at ratio 1.
|
|
31
|
+
// reach near layers (streaks, grains, splashes) are not submitted when the camera is too far
|
|
32
|
+
// from the volume to see any of them (layerInReach).
|
|
33
|
+
const WIND_MS_COMMON = 8; // m/s per unit of windStrength (Rain's 0..1 range)
|
|
34
|
+
const DEG_C = Math.PI / 180;
|
|
35
|
+
function mulberry32(a) {
|
|
36
|
+
return function () {
|
|
37
|
+
a |= 0;
|
|
38
|
+
a = (a + 0x6d2b79f5) | 0;
|
|
39
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
40
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
41
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------- noise texture
|
|
45
|
+
// The lattice of vnoise3 as a texture: NOISE_N^3 values of the same hash the shader used to evaluate,
|
|
46
|
+
// for lattice points -NOISE_N/2 .. NOISE_N/2 - 1 on each axis (beyond that it repeats). One per page,
|
|
47
|
+
// shared by every environment object.
|
|
48
|
+
const NOISE_N = 64;
|
|
49
|
+
let _noiseTex = null;
|
|
50
|
+
function noiseTexture() {
|
|
51
|
+
if (_noiseTex)
|
|
52
|
+
return _noiseTex;
|
|
53
|
+
const N = NOISE_N, H = N / 2, f = Math.fround;
|
|
54
|
+
const fr = (x) => f(x - Math.floor(x));
|
|
55
|
+
const data = new Uint8Array(N * N * N);
|
|
56
|
+
let o = 0;
|
|
57
|
+
for (let z = 0; z < N; z++)
|
|
58
|
+
for (let y = 0; y < N; y++)
|
|
59
|
+
for (let x = 0; x < N; x++) {
|
|
60
|
+
// hash13: p = fract(p * 0.1031); p += dot(p, p.zyx + 31.32); return fract((p.x + p.y) * p.z);
|
|
61
|
+
let px = fr(f((x - H) * f(0.1031))), py = fr(f((y - H) * f(0.1031))), pz = fr(f((z - H) * f(0.1031)));
|
|
62
|
+
const d = f(f(f(px * f(pz + f(31.32))) + f(py * f(py + f(31.32)))) + f(pz * f(px + f(31.32))));
|
|
63
|
+
px = f(px + d);
|
|
64
|
+
py = f(py + d);
|
|
65
|
+
pz = f(pz + d);
|
|
66
|
+
data[o++] = Math.min(255, Math.round(fr(f(f(px + py) * pz)) * 255));
|
|
67
|
+
}
|
|
68
|
+
const tex = new THREE.Data3DTexture(data, N, N, N);
|
|
69
|
+
tex.format = THREE.RedFormat;
|
|
70
|
+
tex.type = THREE.UnsignedByteType;
|
|
71
|
+
tex.minFilter = THREE.LinearFilter;
|
|
72
|
+
tex.magFilter = THREE.LinearFilter;
|
|
73
|
+
tex.wrapS = THREE.RepeatWrapping;
|
|
74
|
+
tex.wrapT = THREE.RepeatWrapping;
|
|
75
|
+
tex.wrapR = THREE.RepeatWrapping;
|
|
76
|
+
tex.unpackAlignment = 1;
|
|
77
|
+
tex.generateMipmaps = false;
|
|
78
|
+
tex.needsUpdate = true;
|
|
79
|
+
_noiseTex = tex;
|
|
80
|
+
return tex;
|
|
81
|
+
}
|
|
82
|
+
function setColor(uniform, v) {
|
|
83
|
+
if (v === undefined || v === null)
|
|
84
|
+
return;
|
|
85
|
+
if (v.isColor)
|
|
86
|
+
uniform.value.copy(v);
|
|
87
|
+
else
|
|
88
|
+
uniform.value.set(v);
|
|
89
|
+
}
|
|
90
|
+
/** Uniforms every environment object has. */
|
|
91
|
+
function createCommonUniforms() {
|
|
92
|
+
return {
|
|
93
|
+
uTime: { value: 0 },
|
|
94
|
+
uWind: { value: new THREE.Vector2() },
|
|
95
|
+
uWindDir: { value: new THREE.Vector2(0, 1) },
|
|
96
|
+
uWindStrength: { value: 0.2 },
|
|
97
|
+
uTurb: { value: 0.3 },
|
|
98
|
+
uTurbPhase: { value: 0 },
|
|
99
|
+
uSpeed: { value: 1 },
|
|
100
|
+
uVolume: { value: new THREE.Vector3(100, 40, 100) },
|
|
101
|
+
uCam: { value: new THREE.Vector3() },
|
|
102
|
+
uCamToLocal: { value: new THREE.Matrix4() },
|
|
103
|
+
uSliceNear: { value: 0.3 },
|
|
104
|
+
uSliceFar: { value: 200 },
|
|
105
|
+
uSliceCount: { value: 32 },
|
|
106
|
+
uSliceRect: { value: new THREE.Vector4(-1.04, -1.04, 1.04, 1.04) },
|
|
107
|
+
uSkip: { value: 1 },
|
|
108
|
+
uSkipMax: { value: 4 },
|
|
109
|
+
uNoise: { value: noiseTexture() },
|
|
110
|
+
uFollow: { value: 0 },
|
|
111
|
+
uViewH: { value: 800 },
|
|
112
|
+
uIntensity: { value: 1 },
|
|
113
|
+
uOpacity: { value: 1 },
|
|
114
|
+
uColor: { value: new THREE.Color('#ffffff') },
|
|
115
|
+
uShadeColor: { value: new THREE.Color('#808080') },
|
|
116
|
+
uSunDir: { value: new THREE.Vector3(0.35, 0.42, 0.84).normalize() },
|
|
117
|
+
uSunColor: { value: new THREE.Color(1.0, 0.93, 0.82) },
|
|
118
|
+
uSkyAmbient: { value: new THREE.Color(0.55, 0.65, 0.85) },
|
|
119
|
+
uPointer: { value: new THREE.Vector3() },
|
|
120
|
+
uPointerVel: { value: new THREE.Vector3() },
|
|
121
|
+
uPointerActive: { value: 0 },
|
|
122
|
+
uPointerMode: { value: -1 },
|
|
123
|
+
uPointerStrength: { value: 1 },
|
|
124
|
+
uPointerRadius: { value: 1 },
|
|
125
|
+
uPointerColor: { value: new THREE.Color('#ffffff') },
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** The base knobs every environment object shares (push point, part 1). */
|
|
129
|
+
function applyCommonConfig(u, c, pointerModes) {
|
|
130
|
+
if (c.intensity !== undefined)
|
|
131
|
+
u.uIntensity.value = c.intensity;
|
|
132
|
+
if (c.speed !== undefined)
|
|
133
|
+
u.uSpeed.value = c.speed;
|
|
134
|
+
if (c.opacity !== undefined)
|
|
135
|
+
u.uOpacity.value = c.opacity;
|
|
136
|
+
setColor(u.uColor, c.color);
|
|
137
|
+
setColor(u.uShadeColor, c.shadeColor);
|
|
138
|
+
setColor(u.uPointerColor, c.pointerColor);
|
|
139
|
+
if (c.windStrength !== undefined)
|
|
140
|
+
u.uWindStrength.value = c.windStrength;
|
|
141
|
+
if (c.windDirection !== undefined)
|
|
142
|
+
u.uWindDir.value.set(Math.sin(c.windDirection * DEG_C), Math.cos(c.windDirection * DEG_C));
|
|
143
|
+
if (c.turbulence !== undefined)
|
|
144
|
+
u.uTurb.value = c.turbulence;
|
|
145
|
+
if (c.pointerStrength !== undefined)
|
|
146
|
+
u.uPointerStrength.value = c.pointerStrength;
|
|
147
|
+
if (c.pointerRadius !== undefined)
|
|
148
|
+
u.uPointerRadius.value = c.pointerRadius;
|
|
149
|
+
if (c.pointerMode !== undefined)
|
|
150
|
+
u.uPointerMode.value = pointerModes.indexOf(c.pointerMode);
|
|
151
|
+
if (c.followCamera !== undefined)
|
|
152
|
+
u.uFollow.value = c.followCamera ? 1 : 0;
|
|
153
|
+
}
|
|
154
|
+
const _cs = new THREE.Vector3(), _cc = new THREE.Vector3(), _cq = new THREE.Quaternion(), _csun = new THREE.Vector3();
|
|
155
|
+
const _cinv = new THREE.Matrix4();
|
|
156
|
+
const _corner = new THREE.Vector3();
|
|
157
|
+
const _cview = new THREE.Matrix4();
|
|
158
|
+
/** Per-frame bookkeeping shared by every object: volume, camera, slice range and bounds, sun, phases. */
|
|
159
|
+
function updateCommon(u, object, camera, dt, viewH, sunWorld, dpr) {
|
|
160
|
+
// the camera may have been moved this frame (controls) without its matrices following yet
|
|
161
|
+
camera.updateMatrixWorld();
|
|
162
|
+
object.updateWorldMatrix(true, false);
|
|
163
|
+
object.getWorldScale(_cs);
|
|
164
|
+
u.uVolume.value.set(Math.max(_cs.x, 0.01), Math.max(_cs.y, 0.01), Math.max(_cs.z, 0.01));
|
|
165
|
+
const V = u.uVolume.value;
|
|
166
|
+
object.worldToLocal(_cc.copy(camera.position));
|
|
167
|
+
u.uCam.value.set(_cc.x * V.x, _cc.y * V.y, _cc.z * V.z);
|
|
168
|
+
_cinv.copy(object.matrixWorld).invert();
|
|
169
|
+
u.uCamToLocal.value.multiplyMatrices(_cinv, camera.matrixWorld);
|
|
170
|
+
if (viewH)
|
|
171
|
+
u.uViewH.value = viewH;
|
|
172
|
+
// slice range: from where the volume starts in front of the eye to its far corner
|
|
173
|
+
const C = u.uCam.value;
|
|
174
|
+
const follow = u.uFollow.value > 0.5;
|
|
175
|
+
const hx = follow ? 1e6 : V.x / 2, hz = follow ? 1e6 : V.z / 2;
|
|
176
|
+
const dx = Math.max(Math.abs(C.x) - hx, 0), dz = Math.max(Math.abs(C.z) - hz, 0);
|
|
177
|
+
const dy = Math.max(-C.y, C.y - V.y, 0);
|
|
178
|
+
const dNear = Math.hypot(dx, dy, dz);
|
|
179
|
+
let dFar = 0;
|
|
180
|
+
for (let i = 0; i < 8; i++) {
|
|
181
|
+
_corner.set((i & 1 ? 1 : -1) * Math.min(hx, 3e3), i & 2 ? V.y : 0, (i & 4 ? 1 : -1) * Math.min(hz, 3e3));
|
|
182
|
+
dFar = Math.max(dFar, _corner.distanceTo(C));
|
|
183
|
+
}
|
|
184
|
+
const far = camera.far !== undefined ? camera.far : 2000;
|
|
185
|
+
u.uSliceNear.value = Math.max(0.3, dNear * 0.6);
|
|
186
|
+
u.uSliceFar.value = Math.max(u.uSliceNear.value * 1.5, Math.min(dFar, 600, far * 0.98));
|
|
187
|
+
// Slice bounds: the volume's box on screen (NDC). Whole screen when the box reaches behind the
|
|
188
|
+
// eye (the camera is inside or right next to it) or follows the camera.
|
|
189
|
+
const R = u.uSliceRect.value.set(-1.04, -1.04, 1.04, 1.04);
|
|
190
|
+
if (!follow && camera.projectionMatrix) {
|
|
191
|
+
_cview.copy(camera.matrixWorld).invert().multiply(object.matrixWorld);
|
|
192
|
+
const P = camera.projectionMatrix.elements, nearZ = camera.near !== undefined ? camera.near : 0.1;
|
|
193
|
+
let x0 = 1e9, y0 = 1e9, x1 = -1e9, y1 = -1e9, ok = true;
|
|
194
|
+
for (let i = 0; i < 8 && ok; i++) {
|
|
195
|
+
_corner.set(i & 1 ? 0.5 : -0.5, i & 2 ? 1 : 0, i & 4 ? 0.5 : -0.5).applyMatrix4(_cview);
|
|
196
|
+
if (_corner.z > -nearZ) {
|
|
197
|
+
ok = false;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
const w = -_corner.z, x = (P[0] * _corner.x + P[8] * _corner.z) / w, y = (P[5] * _corner.y + P[9] * _corner.z) / w;
|
|
201
|
+
if (x < x0)
|
|
202
|
+
x0 = x;
|
|
203
|
+
if (x > x1)
|
|
204
|
+
x1 = x;
|
|
205
|
+
if (y < y0)
|
|
206
|
+
y0 = y;
|
|
207
|
+
if (y > y1)
|
|
208
|
+
y1 = y;
|
|
209
|
+
}
|
|
210
|
+
// the margin covers the depth dither, which slides a sample along its ray but never off its pixel
|
|
211
|
+
if (ok)
|
|
212
|
+
R.set(Math.max(x0 - 0.03, -1.04), Math.max(y0 - 0.03, -1.04), Math.min(x1 + 0.03, 1.04), Math.min(y1 + 0.03, 1.04));
|
|
213
|
+
if (R.z <= R.x || R.w <= R.y)
|
|
214
|
+
R.set(0, 0, 0, 0); // off screen: nothing to draw
|
|
215
|
+
}
|
|
216
|
+
// Interleaved slices on dense displays: pixels take turns over 2 (ratio >= 1.25) or 4 (>= 1.75) sets.
|
|
217
|
+
// An object caps it (buildSlices' interleave): fine, fast-changing structure like the tornado's funnel
|
|
218
|
+
// loses its crispness when neighbours see different slabs, so it takes every slice.
|
|
219
|
+
u.uSkip.value = Math.min(dpr >= 1.75 ? 4 : dpr >= 1.25 ? 2 : 1, u.uSkipMax.value);
|
|
220
|
+
// phases (rates integrated on the CPU, so animating them never jumps)
|
|
221
|
+
const sp = u.uSpeed.value;
|
|
222
|
+
u.uTime.value += dt * sp;
|
|
223
|
+
u.uWind.value.addScaledVector(u.uWindDir.value, dt * sp * u.uWindStrength.value * WIND_MS_COMMON);
|
|
224
|
+
u.uTurbPhase.value += dt * sp * 0.25;
|
|
225
|
+
if (sunWorld) {
|
|
226
|
+
object.getWorldQuaternion(_cq).invert();
|
|
227
|
+
u.uSunDir.value.copy(_csun.copy(sunWorld).normalize().applyQuaternion(_cq));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* True when a layer that only exists inside the volume and within `reach` metres of the eye can show
|
|
232
|
+
* anything: used to skip submitting near layers (thousands of instances, each fully evaluated per
|
|
233
|
+
* vertex) while the camera is away from the volume.
|
|
234
|
+
*/
|
|
235
|
+
function layerInReach(u, reach) {
|
|
236
|
+
const C = u.uCam.value, V = u.uVolume.value, follow = u.uFollow.value > 0.5;
|
|
237
|
+
const ox = follow ? 0 : Math.max(Math.abs(C.x) - 0.5 * V.x, 0), oz = follow ? 0 : Math.max(Math.abs(C.z) - 0.5 * V.z, 0);
|
|
238
|
+
const oy = Math.max(-C.y, C.y - V.y, 0);
|
|
239
|
+
return ox * ox + oy * oy + oz * oz < reach * reach;
|
|
240
|
+
}
|
|
241
|
+
// Where the pointer plane passes (object-local unit coords; the plane faces the camera):
|
|
242
|
+
// a few metres ahead of the eye inside the volume, else through the volume centre's depth.
|
|
243
|
+
const _pa = new THREE.Vector3(), _pd = new THREE.Vector3(), _pc = new THREE.Vector3();
|
|
244
|
+
function pointerAnchor(object, camera, u, out, ahead) {
|
|
245
|
+
const V = u.uVolume.value, C = u.uCam.value;
|
|
246
|
+
const inside = Math.abs(C.x) < 0.5 * V.x && Math.abs(C.z) < 0.5 * V.z && C.y > 0 && C.y < V.y;
|
|
247
|
+
camera.getWorldDirection(_pd);
|
|
248
|
+
let dist = ahead || 4.5;
|
|
249
|
+
if (!inside) {
|
|
250
|
+
object.updateWorldMatrix(true, false);
|
|
251
|
+
object.localToWorld(_pc.set(0, 0.5, 0));
|
|
252
|
+
dist = Math.max(dist, _pc.sub(camera.position).dot(_pd));
|
|
253
|
+
}
|
|
254
|
+
_pa.copy(camera.position).addScaledVector(_pd, dist);
|
|
255
|
+
return object.worldToLocal(out.copy(_pa));
|
|
256
|
+
}
|
|
257
|
+
const NOISE_GLSL = /* glsl */ `
|
|
258
|
+
uniform highp sampler3D uNoise;
|
|
259
|
+
float hash13(vec3 p) { p = fract(p * 0.1031); p += dot(p, p.zyx + 31.32); return fract((p.x + p.y) * p.z); }
|
|
260
|
+
float hash12(vec2 p) { vec3 q = fract(vec3(p.xyx) * 0.1031); q += dot(q, q.yzx + 33.33); return fract((q.x + q.y) * q.z); }
|
|
261
|
+
// value noise from the lattice texture: the smoothstep is put into the coordinate, the trilinear
|
|
262
|
+
// filter does the eight-corner mix (same lattice values as hash13 around the origin)
|
|
263
|
+
float vnoise3(vec3 p) {
|
|
264
|
+
vec3 i = floor(p); vec3 f = fract(p); f = f * f * (3.0 - 2.0 * f);
|
|
265
|
+
return texture(uNoise, (i + f + ${(0.5 + NOISE_N / 2).toFixed(1)}) / ${NOISE_N.toFixed(1)}).r;
|
|
266
|
+
}
|
|
267
|
+
float vnoise2(vec2 p) {
|
|
268
|
+
vec2 i = floor(p); vec2 f = fract(p); f = f * f * (3.0 - 2.0 * f);
|
|
269
|
+
return mix(mix(hash12(i), hash12(i + vec2(1, 0)), f.x), mix(hash12(i + vec2(0, 1)), hash12(i + vec2(1, 1)), f.x), f.y);
|
|
270
|
+
}
|
|
271
|
+
// three octaves, rotated between octaves so the lattice never lines up
|
|
272
|
+
float fbm3(vec3 p) {
|
|
273
|
+
float s = 0.5 * vnoise3(p);
|
|
274
|
+
p = mat3(0.00, 0.80, 0.60, -0.80, 0.36, -0.48, -0.60, -0.48, 0.64) * p * 2.03;
|
|
275
|
+
s += 0.25 * vnoise3(p);
|
|
276
|
+
p = mat3(0.00, 0.80, 0.60, -0.80, 0.36, -0.48, -0.60, -0.48, 0.64) * p * 2.01;
|
|
277
|
+
s += 0.125 * vnoise3(p);
|
|
278
|
+
return s / 0.875;
|
|
279
|
+
}
|
|
280
|
+
float bump(float x) { float t = clamp(1.0 - x * x, 0.0, 1.0); return t * t; }
|
|
281
|
+
float hgPhase(float mu, float g) { return 0.0795775 * (1.0 - g * g) / pow(1.0 + g * g - 2.0 * g * mu, 1.5); }
|
|
282
|
+
float ign(vec2 px) { return fract(52.9829189 * fract(dot(px, vec2(0.06711056, 0.00583715)))); }
|
|
283
|
+
`;
|
|
284
|
+
const COMMON_UNIFORMS_GLSL = /* glsl */ `
|
|
285
|
+
uniform float uTime, uWindStrength, uTurb, uTurbPhase, uSpeed, uFollow, uViewH;
|
|
286
|
+
uniform vec2 uWind, uWindDir;
|
|
287
|
+
uniform vec3 uVolume, uCam;
|
|
288
|
+
uniform vec3 uSunDir, uSunColor, uSkyAmbient, uColor, uShadeColor, uPointerColor;
|
|
289
|
+
uniform float uIntensity, uOpacity;
|
|
290
|
+
uniform vec3 uPointer, uPointerVel;
|
|
291
|
+
uniform float uPointerActive, uPointerMode, uPointerStrength, uPointerRadius;
|
|
292
|
+
`;
|
|
293
|
+
const FOG_V = /* glsl */ `
|
|
294
|
+
#ifdef USE_FOG
|
|
295
|
+
varying float vFogDepth;
|
|
296
|
+
#endif
|
|
297
|
+
`;
|
|
298
|
+
const FOG_F = /* glsl */ `
|
|
299
|
+
#ifdef USE_FOG
|
|
300
|
+
uniform vec3 fogColor;
|
|
301
|
+
varying float vFogDepth;
|
|
302
|
+
#ifdef FOG_EXP2
|
|
303
|
+
uniform float fogDensity;
|
|
304
|
+
#else
|
|
305
|
+
uniform float fogNear;
|
|
306
|
+
uniform float fogFar;
|
|
307
|
+
#endif
|
|
308
|
+
#endif
|
|
309
|
+
`;
|
|
310
|
+
const FOG_APPLY = /* glsl */ `
|
|
311
|
+
#ifdef USE_FOG
|
|
312
|
+
#ifdef FOG_EXP2
|
|
313
|
+
float fogFactor = 1.0 - exp(-fogDensity * fogDensity * vFogDepth * vFogDepth);
|
|
314
|
+
#else
|
|
315
|
+
float fogFactor = smoothstep(fogNear, fogFar, vFogDepth);
|
|
316
|
+
#endif
|
|
317
|
+
gl_FragColor.rgb = mix(gl_FragColor.rgb, fogColor * gl_FragColor.a, fogFactor);
|
|
318
|
+
#endif
|
|
319
|
+
`;
|
|
320
|
+
/** Soft walls of the volume (metres); the ground is hard. */
|
|
321
|
+
const VOLUME_GLSL = /* glsl */ `
|
|
322
|
+
float volumeFade(vec3 p, float side, float top) {
|
|
323
|
+
float a = step(0.0, p.y) * (1.0 - smoothstep(uVolume.y - top, uVolume.y, p.y));
|
|
324
|
+
if (uFollow < 0.5) {
|
|
325
|
+
vec2 e = 0.5 * uVolume.xz - abs(p.xz);
|
|
326
|
+
a *= smoothstep(0.0, side, e.x) * smoothstep(0.0, side, e.y);
|
|
327
|
+
}
|
|
328
|
+
return a;
|
|
329
|
+
}
|
|
330
|
+
`;
|
|
331
|
+
const SLICE_VERT = /* glsl */ `
|
|
332
|
+
${COMMON_UNIFORMS_GLSL}
|
|
333
|
+
uniform float uSliceNear, uSliceFar, uSliceCount;
|
|
334
|
+
uniform vec4 uSliceRect;
|
|
335
|
+
uniform mat4 uCamToLocal;
|
|
336
|
+
attribute vec2 aCorner;
|
|
337
|
+
attribute float aSlice;
|
|
338
|
+
varying vec3 vLocal;
|
|
339
|
+
varying vec3 vRay;
|
|
340
|
+
varying float vDz, vSlice;
|
|
341
|
+
${FOG_V}
|
|
342
|
+
void main() {
|
|
343
|
+
// slice 0 is the farthest: instances draw back to front
|
|
344
|
+
float k = log(uSliceFar / uSliceNear);
|
|
345
|
+
float t0 = 1.0 - (aSlice + 1.0) / uSliceCount;
|
|
346
|
+
float t1 = 1.0 - aSlice / uSliceCount;
|
|
347
|
+
float z0 = uSliceNear * exp(k * t0);
|
|
348
|
+
float z1 = uSliceNear * exp(k * t1);
|
|
349
|
+
float z = 0.5 * (z0 + z1);
|
|
350
|
+
vDz = z1 - z0;
|
|
351
|
+
// the quad spans the volume's bounds on screen (NDC), not the screen
|
|
352
|
+
vec2 ndc = mix(uSliceRect.xy, uSliceRect.zw, aCorner * 0.5 + 0.5);
|
|
353
|
+
vec3 view = vec3((ndc.x + projectionMatrix[2][0]) * z / projectionMatrix[0][0], (ndc.y + projectionMatrix[2][1]) * z / projectionMatrix[1][1], -z);
|
|
354
|
+
vSlice = aSlice;
|
|
355
|
+
gl_Position = projectionMatrix * vec4(view, 1.0);
|
|
356
|
+
vLocal = (uCamToLocal * vec4(view, 1.0)).xyz * uVolume;
|
|
357
|
+
vRay = vLocal - uCam;
|
|
358
|
+
#ifdef USE_FOG
|
|
359
|
+
vFogDepth = z;
|
|
360
|
+
#endif
|
|
361
|
+
}
|
|
362
|
+
`;
|
|
363
|
+
/**
|
|
364
|
+
* Wraps an object's `vec4 sliceSample(vec3 p, vec3 rd, float dz)` (premultiplied rgb, alpha).
|
|
365
|
+
*
|
|
366
|
+
* The canvas stores display (non-linear) values. By default the premultiplied colour goes through
|
|
367
|
+
* the output transform as it is, which makes a thin layer brighter than its share: the look then
|
|
368
|
+
* depends on how thin the slabs are (the first objects were tuned by eye under it — leave them).
|
|
369
|
+
* `straight` converts the colour first and weights it by alpha after, so a medium looks the same
|
|
370
|
+
* at every slice count, density and quality; new objects use it.
|
|
371
|
+
*/
|
|
372
|
+
function sliceFrag(body, straight) {
|
|
373
|
+
return /* glsl */ `
|
|
374
|
+
${COMMON_UNIFORMS_GLSL}
|
|
375
|
+
uniform float uSkip;
|
|
376
|
+
varying vec3 vLocal;
|
|
377
|
+
varying vec3 vRay;
|
|
378
|
+
varying float vDz, vSlice;
|
|
379
|
+
${FOG_F}
|
|
380
|
+
${NOISE_GLSL}
|
|
381
|
+
${VOLUME_GLSL}
|
|
382
|
+
${body}
|
|
383
|
+
void main() {
|
|
384
|
+
// dense displays: neighbouring pixels take turns over the slices (see updateCommon)
|
|
385
|
+
if (uSkip > 1.5) {
|
|
386
|
+
vec2 px = mod(floor(gl_FragCoord.xy), 2.0);
|
|
387
|
+
float turn = uSkip > 3.0 ? mod(px.x * 2.0 + px.y * 3.0, 4.0) : mod(px.x + px.y, 2.0);
|
|
388
|
+
if (mod(vSlice + turn, uSkip) > 0.5) discard;
|
|
389
|
+
}
|
|
390
|
+
vec3 rd = normalize(vRay);
|
|
391
|
+
// dithered depth inside the slab: banding becomes grain
|
|
392
|
+
vec3 p = vLocal + rd * (ign(gl_FragCoord.xy) - 0.5) * 0.6 * vDz;
|
|
393
|
+
if (p.y < 0.0 || p.y > uVolume.y) discard;
|
|
394
|
+
if (uFollow < 0.5 && (abs(p.x) > 0.5 * uVolume.x || abs(p.z) > 0.5 * uVolume.z)) discard;
|
|
395
|
+
vec4 s = sliceSample(p, rd, vDz);
|
|
396
|
+
s *= uOpacity;
|
|
397
|
+
if (s.a < 0.0015 && dot(s.rgb, vec3(1.0)) < 0.002) discard;
|
|
398
|
+
gl_FragColor = ${straight ? 'vec4(s.rgb / max(s.a, 1e-5) * uIntensity, 1.0)' : 'vec4(s.rgb * uIntensity, s.a)'};
|
|
399
|
+
#include <tonemapping_fragment>
|
|
400
|
+
#include <colorspace_fragment>${straight ? '\n gl_FragColor = vec4(gl_FragColor.rgb * s.a, s.a);' : ''}
|
|
401
|
+
${FOG_APPLY}
|
|
402
|
+
// A pixel that skipped slabs stands for them: it lays down what 2 or 4 copies of its own thin
|
|
403
|
+
// layer would have composited to (colour c (1 + t + ...), transmittance t^n), so the volume
|
|
404
|
+
// keeps exactly the brightness and opacity it has when every pixel takes every slice.
|
|
405
|
+
if (uSkip > 1.5) {
|
|
406
|
+
float t = 1.0 - gl_FragColor.a;
|
|
407
|
+
float t2 = t * t;
|
|
408
|
+
gl_FragColor = uSkip > 3.0 ? vec4(gl_FragColor.rgb * (1.0 + t) * (1.0 + t2), 1.0 - t2 * t2) : vec4(gl_FragColor.rgb * (1.0 + t), 1.0 - t2);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
`;
|
|
412
|
+
}
|
|
413
|
+
function premulMaterial(u, vertexShader, fragmentShader, additive) {
|
|
414
|
+
return new THREE.ShaderMaterial({
|
|
415
|
+
uniforms: Object.assign({}, THREE.UniformsUtils.clone(THREE.UniformsLib.fog), u),
|
|
416
|
+
vertexShader, fragmentShader,
|
|
417
|
+
transparent: true, depthWrite: false, fog: true,
|
|
418
|
+
blending: THREE.CustomBlending,
|
|
419
|
+
blendSrc: THREE.OneFactor, blendDst: additive ? THREE.OneFactor : THREE.OneMinusSrcAlphaFactor,
|
|
420
|
+
blendSrcAlpha: THREE.OneFactor, blendDstAlpha: THREE.OneMinusSrcAlphaFactor,
|
|
421
|
+
premultipliedAlpha: true,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
/** Bounds-only `position`: the unit volume box, so Box3.expandByObject centres the gizmo on it. */
|
|
425
|
+
function boxPositions(geo) {
|
|
426
|
+
geo.setAttribute('position', new THREE.Float32BufferAttribute([-0.5, 0, -0.5, 0.5, 0, 0.5, 0.5, 1, -0.5, -0.5, 1, 0.5], 3));
|
|
427
|
+
geo.boundingSphere = new THREE.Sphere(new THREE.Vector3(0, 0.5, 0), 0.87);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* One instanced draw of `count` view-aligned slices. `interleave` (1, 2 or 4, default 4) is the most
|
|
431
|
+
* sets of slices neighbouring pixels may take turns over on a dense display; 1 = every pixel, every slice.
|
|
432
|
+
*/
|
|
433
|
+
function buildSlices(u, fragmentShader, count, interleave) {
|
|
434
|
+
const geo = new THREE.InstancedBufferGeometry();
|
|
435
|
+
boxPositions(geo);
|
|
436
|
+
geo.setAttribute('aCorner', new THREE.Float32BufferAttribute([-1, -1, 1, -1, 1, 1, -1, 1], 2));
|
|
437
|
+
geo.setIndex([0, 1, 2, 0, 2, 3]);
|
|
438
|
+
const idx = new Float32Array(count);
|
|
439
|
+
for (let i = 0; i < count; i++)
|
|
440
|
+
idx[i] = i;
|
|
441
|
+
geo.setAttribute('aSlice', new THREE.InstancedBufferAttribute(idx, 1));
|
|
442
|
+
geo.instanceCount = count;
|
|
443
|
+
u.uSliceCount.value = count;
|
|
444
|
+
u.uSkipMax.value = interleave || 4;
|
|
445
|
+
const mat = premulMaterial(u, SLICE_VERT, fragmentShader, false);
|
|
446
|
+
const mesh = new THREE.Mesh(geo, mat);
|
|
447
|
+
mesh.frustumCulled = false;
|
|
448
|
+
mesh.raycast = function () { };
|
|
449
|
+
return mesh;
|
|
450
|
+
}
|
|
451
|
+
/** Instanced quad geometry with per-instance seeds (two vec4s). */
|
|
452
|
+
function seededQuads(count, rnd) {
|
|
453
|
+
const geo = new THREE.InstancedBufferGeometry();
|
|
454
|
+
boxPositions(geo);
|
|
455
|
+
geo.setAttribute('aCorner', new THREE.Float32BufferAttribute([-1, -1, 1, -1, 1, 1, -1, 1], 2));
|
|
456
|
+
geo.setIndex([0, 1, 2, 0, 2, 3]);
|
|
457
|
+
const a = new Float32Array(count * 4), b = new Float32Array(count * 4);
|
|
458
|
+
for (let i = 0; i < count * 4; i++) {
|
|
459
|
+
a[i] = rnd();
|
|
460
|
+
b[i] = rnd();
|
|
461
|
+
}
|
|
462
|
+
geo.setAttribute('aSeedA', new THREE.InstancedBufferAttribute(a, 4));
|
|
463
|
+
geo.setAttribute('aSeedB', new THREE.InstancedBufferAttribute(b, 4));
|
|
464
|
+
geo.instanceCount = count;
|
|
465
|
+
return geo;
|
|
466
|
+
}
|
|
467
|
+
/** A streak quad between two view-space points, at least 1 px wide (energy kept by alpha). */
|
|
468
|
+
const STREAK_GLSL = /* glsl */ `
|
|
469
|
+
// returns the clip position of this corner; writes the coverage factor into cover
|
|
470
|
+
vec4 streakCorner(vec4 headV, vec4 tailV, vec2 corner, float widthM, out float cover, out float lenPx) {
|
|
471
|
+
vec4 h = projectionMatrix * headV;
|
|
472
|
+
vec4 t = projectionMatrix * tailV;
|
|
473
|
+
vec2 hs = h.xy / h.w, ts = t.xy / t.w;
|
|
474
|
+
vec2 scr = vec2(uViewH * projectionMatrix[1][1] / projectionMatrix[0][0], uViewH) * 0.5;
|
|
475
|
+
vec2 d = (hs - ts) * scr;
|
|
476
|
+
lenPx = length(d);
|
|
477
|
+
vec2 dir = lenPx > 1e-4 ? d / lenPx : vec2(0.0, 1.0);
|
|
478
|
+
vec2 nrm = vec2(dir.y, -dir.x); // (nrm, dir) right-handed: the quad stays front-facing
|
|
479
|
+
float wPx = widthM * uViewH * projectionMatrix[1][1] * 0.5 / max(-headV.z, 1e-3);
|
|
480
|
+
float w = max(wPx, 1.0);
|
|
481
|
+
cover = wPx / w;
|
|
482
|
+
vec4 base = corner.y > 0.0 ? h : t;
|
|
483
|
+
vec2 off = nrm * corner.x * 0.5 * w / scr;
|
|
484
|
+
// extend half a pixel past both ends so short streaks stay round
|
|
485
|
+
off += dir * corner.y * 0.5 / scr;
|
|
486
|
+
return vec4(base.xy + off * base.w, base.zw);
|
|
487
|
+
}
|
|
488
|
+
`;
|
|
489
|
+
// ---------------------------------------------------------------------------- rainbow
|
|
490
|
+
// Rainbow — environment object (lab module). Shared core: ./common.mjs.
|
|
491
|
+
// One rainbow and nothing else: no rain curtain, no shafts, no mist (the shower is the `mist` object).
|
|
492
|
+
// Construction:
|
|
493
|
+
// optics the scattering angle between the view ray and the antisolar point is compared with the
|
|
494
|
+
// Descartes minimum-deviation angle of light refracted into a water drop and reflected k
|
|
495
|
+
// times: cos i = sqrt((n² − 1)/(k² + 2k)), D = 2(i − r) + k(π − 2r). k = 1 gives the
|
|
496
|
+
// primary bow (~42°), k = 2 the optional secondary (~51°, colours reversed).
|
|
497
|
+
// n(λ) = 1.3240 + 3046/λ² (water, λ in nm) — dispersion spreads the colours.
|
|
498
|
+
// profile around each bow the intensity follows the Airy function: Ai(−x)² on the bright side
|
|
499
|
+
// with its supernumerary fringes, a fast exponential fall on the dark side. The fringe
|
|
500
|
+
// scale is (λ / d)^(2/3), so small drops give wide, pale bows and at fog-drop sizes the
|
|
501
|
+
// colours overlap into a white fogbow; big drops wash the fringes out. The sun's disc
|
|
502
|
+
// (0.5°) smears every profile.
|
|
503
|
+
// colour 24 wavelengths 400–700 nm through a CIE 1931 fit, XYZ → linear sRGB.
|
|
504
|
+
// table all of the above depends on the angle only, so it is evaluated on the CPU into a
|
|
505
|
+
// 1-D texture (angle → colour) whenever dropSize or secondary change. A pixel costs one
|
|
506
|
+
// acos and one texture fetch instead of the whole spectral sum.
|
|
507
|
+
// where the bow is seen through the object's volume as if it were filled with invisible fine
|
|
508
|
+
// drops: a half-ellipsoid standing on the ground, density 1 − s² (s = scaled radius), so
|
|
509
|
+
// its strength 1 − exp(−τ) builds with the depth looked through and falls smoothly to
|
|
510
|
+
// nothing at the silhouette. The integral along the view ray is a cubic — no samples.
|
|
511
|
+
// depth the light is drawn on 12 shells around the eye, only inside the band of angles where
|
|
512
|
+
// the table is not black; each shell carries its slab's share e^(−τ before) − e^(−τ after).
|
|
513
|
+
// Every pixel is depth-tested at a dithered distance inside its slab, so a scene object
|
|
514
|
+
// hides exactly the share of the drops behind it and the slab edges turn into fine grain.
|
|
515
|
+
// blend the bow is light, but added to a bright sky its colours would wash out to white, so
|
|
516
|
+
// it also covers what is behind it in proportion to its own brightness.
|
|
517
|
+
// follows the sun read-only; below 0° or above ~42° elevation the bow is gone, as in nature.
|
|
518
|
+
const catalog = {
|
|
519
|
+
key: 'rainbow',
|
|
520
|
+
name: 'Rainbow',
|
|
521
|
+
description: 'A single physical rainbow on its own: water dispersion and an Airy profile with supernumerary fringes, centred opposite the sun and seen through the object’s volume. The cursor sprays, clears or polarises it. Use for after-rain and hopeful scenes.',
|
|
522
|
+
tags: ['rainbow', 'optics', 'sky', 'weather', 'dispersion', 'interactive'],
|
|
523
|
+
structural: [],
|
|
524
|
+
pointerModes: ['spray', 'clear', 'polarizer'],
|
|
525
|
+
pointerLabels: ['Mist spray', 'Clearing', 'Polarising filter'],
|
|
526
|
+
volume: [240, 90, 240],
|
|
527
|
+
staging: { sky: '#6f8fb4', ground: '#556b4c', fog: 0.0012, sunEl: 18, sunAz: 15, inside: [[0, 1.7, 60], [0, 14, -40]], outside: [[0, 4, 210], [0, 25, 0]] },
|
|
528
|
+
pointerAhead: 30,
|
|
529
|
+
};
|
|
530
|
+
const SHELLS = 12; // depth slabs the bow's light is split over
|
|
531
|
+
const SEGMENTS = 96; // sides of the band around the antisolar point
|
|
532
|
+
const LUT_N = 1024; // texels of the angle → colour table
|
|
533
|
+
const LUT_A = 0.05, LUT_B = 1.25; // its range, radians from the antisolar point (2.9°..71.6°)
|
|
534
|
+
const DEPTH = 3.0; // optical depth scale: a ray through the middle of the volume has τ = 4
|
|
535
|
+
function createUniforms() {
|
|
536
|
+
return Object.assign(createCommonUniforms(), {
|
|
537
|
+
uDrop: { value: 1 },
|
|
538
|
+
uSecondary: { value: 0 },
|
|
539
|
+
uLut: { value: null },
|
|
540
|
+
uLutRange: { value: new THREE.Vector2(LUT_A, LUT_B) },
|
|
541
|
+
uTheta: { value: new THREE.Vector4(0.6, 0.8, 0.8, 0.8) },
|
|
542
|
+
uShell: { value: new THREE.Vector3(1, 300, SHELLS) },
|
|
543
|
+
uAntiW: { value: new THREE.Vector3(0, 0, -1) },
|
|
544
|
+
uE1W: { value: new THREE.Vector3(1, 0, 0) },
|
|
545
|
+
uE2W: { value: new THREE.Vector3(0, 1, 0) },
|
|
546
|
+
uToLocal: { value: new THREE.Matrix3() },
|
|
547
|
+
uFilter: { value: new THREE.Vector3(1, 0, 0) },
|
|
548
|
+
uProj: { value: new THREE.Vector2(-1, -0.2) },
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
function applyConfig(u, c) {
|
|
552
|
+
applyCommonConfig(u, c, catalog.pointerModes);
|
|
553
|
+
if (c.dropSize !== undefined)
|
|
554
|
+
u.uDrop.value = c.dropSize;
|
|
555
|
+
if (c.secondary !== undefined)
|
|
556
|
+
u.uSecondary.value = c.secondary;
|
|
557
|
+
}
|
|
558
|
+
// ---------------------------------------------------------------- the bow's colour by angle (CPU)
|
|
559
|
+
// Descartes angle (radians from the antisolar point) of the k-th order bow
|
|
560
|
+
function bowAngle(n, k) {
|
|
561
|
+
const ci = Math.sqrt((n * n - 1) / (k * k + 2 * k));
|
|
562
|
+
const i = Math.acos(ci);
|
|
563
|
+
const r = Math.asin(Math.sin(i) / n);
|
|
564
|
+
const D = 2 * (i - r) + k * (Math.PI - 2 * r);
|
|
565
|
+
return Math.abs(k > 1.5 ? D - Math.PI : Math.PI - D);
|
|
566
|
+
}
|
|
567
|
+
// Airy profile, x > 0 on the bright side; damp = how far the fringes survive
|
|
568
|
+
function airy(x, damp) {
|
|
569
|
+
if (x < 0)
|
|
570
|
+
return 0.126 * Math.exp(-1.33 * Math.pow(-x, 1.5));
|
|
571
|
+
const s = Math.sin(0.6667 * Math.pow(x, 1.5) + 0.785398);
|
|
572
|
+
const osc = 0.9 * s * s / (Math.PI * Math.sqrt(Math.max(x, 0.3)));
|
|
573
|
+
if (x < 1.02) {
|
|
574
|
+
const t = x / 1.02, h = t * t * (3 - 2 * t);
|
|
575
|
+
return 0.126 + (0.287 - 0.126) * h;
|
|
576
|
+
}
|
|
577
|
+
// past the main peak only the supernumerary fringes are kept, dying out with damp: the faint
|
|
578
|
+
// even glow that real drops return across the whole inside of the bow belongs to the shower
|
|
579
|
+
// (the mist object), not to a rainbow on its own
|
|
580
|
+
return osc * 0.92 * Math.exp(-(x - 1.02) * damp);
|
|
581
|
+
}
|
|
582
|
+
// CIE 1931 colour-matching fit (Wyman, Sloan, Shirley 2013)
|
|
583
|
+
function cie(l, out) {
|
|
584
|
+
const g = (t) => Math.exp(-0.5 * t * t);
|
|
585
|
+
out[0] = 0.362 * g((l - 442.0) * (l < 442.0 ? 0.0624 : 0.0374)) + 1.056 * g((l - 599.8) * (l < 599.8 ? 0.0264 : 0.0323)) - 0.065 * g((l - 501.1) * (l < 501.1 ? 0.0490 : 0.0382));
|
|
586
|
+
out[1] = 0.821 * g((l - 568.8) * (l < 568.8 ? 0.0213 : 0.0247)) + 0.286 * g((l - 530.9) * (l < 530.9 ? 0.0613 : 0.0322));
|
|
587
|
+
out[2] = 1.217 * g((l - 437.0) * (l < 437.0 ? 0.0845 : 0.0278)) + 0.681 * g((l - 459.0) * (l < 459.0 ? 0.0385 : 0.0725));
|
|
588
|
+
}
|
|
589
|
+
const WAVES = 24;
|
|
590
|
+
const SPECTRUM = (function () {
|
|
591
|
+
const rows = [], w = [0, 0, 0];
|
|
592
|
+
let whiteY = 0;
|
|
593
|
+
for (let j = 0; j < WAVES; j++) {
|
|
594
|
+
const l = 400 + 300 * (j + 0.5) / WAVES;
|
|
595
|
+
cie(l, w);
|
|
596
|
+
const n = 1.3240 + 3046.0 / (l * l);
|
|
597
|
+
rows.push({ l: l, x: w[0], y: w[1], z: w[2], a1: bowAngle(n, 1), a2: bowAngle(n, 2) });
|
|
598
|
+
whiteY += w[1];
|
|
599
|
+
}
|
|
600
|
+
return { rows: rows, whiteY: whiteY };
|
|
601
|
+
})();
|
|
602
|
+
/** Linear-sRGB colour of the bows at every angle of the table; returns the band of angles that is not black. */
|
|
603
|
+
function bowTable(drop, secondary, rgb) {
|
|
604
|
+
const dmm = Math.max(drop, 0.03);
|
|
605
|
+
// how fast the supernumerary fringes die out: big drops flatten and wash them out; fog and drizzle
|
|
606
|
+
// come in mixed sizes, which leaves one or two
|
|
607
|
+
const damp = 0.55 + 1.6 * dmm + 0.75 * Math.exp(-dmm / 0.12);
|
|
608
|
+
const rows = SPECTRUM.rows;
|
|
609
|
+
const sc = new Float32Array(WAVES), en = new Float32Array(WAVES);
|
|
610
|
+
for (let j = 0; j < WAVES; j++) {
|
|
611
|
+
// Airy fringe scale (radians): (λ / d)^(2/3), smeared by the sun's disc
|
|
612
|
+
let s = Math.pow(rows[j].l * 1e-6 / dmm, 0.6667) * 1.05;
|
|
613
|
+
s = Math.sqrt(s * s + 0.0044 * 0.0044);
|
|
614
|
+
sc[j] = s;
|
|
615
|
+
// energy is conserved: a bow spread over a wider angle is proportionally fainter
|
|
616
|
+
en[j] = Math.min(1, 0.0075 / s);
|
|
617
|
+
}
|
|
618
|
+
let peak = 0;
|
|
619
|
+
for (let i = 0; i < LUT_N; i++) {
|
|
620
|
+
const theta = LUT_A + (LUT_B - LUT_A) * (i + 0.5) / LUT_N;
|
|
621
|
+
let X = 0, Y = 0, Z = 0;
|
|
622
|
+
for (let j = 0; j < WAVES; j++) {
|
|
623
|
+
const r = rows[j];
|
|
624
|
+
let p = airy((r.a1 - theta) / sc[j], damp);
|
|
625
|
+
if (secondary > 0)
|
|
626
|
+
p += airy((theta - r.a2) / sc[j], damp) * 0.43 * secondary;
|
|
627
|
+
p *= en[j];
|
|
628
|
+
X += r.x * p;
|
|
629
|
+
Y += r.y * p;
|
|
630
|
+
Z += r.z * p;
|
|
631
|
+
}
|
|
632
|
+
X /= SPECTRUM.whiteY;
|
|
633
|
+
Y /= SPECTRUM.whiteY;
|
|
634
|
+
Z /= SPECTRUM.whiteY;
|
|
635
|
+
const R = Math.max(3.2406 * X - 1.5372 * Y - 0.4986 * Z, 0);
|
|
636
|
+
const G = Math.max(-0.9689 * X + 1.8758 * Y + 0.0415 * Z, 0);
|
|
637
|
+
const B = Math.max(0.0557 * X - 0.2040 * Y + 1.0570 * Z, 0);
|
|
638
|
+
rgb[i * 3] = R;
|
|
639
|
+
rgb[i * 3 + 1] = G;
|
|
640
|
+
rgb[i * 3 + 2] = B;
|
|
641
|
+
peak = Math.max(peak, R, G, B);
|
|
642
|
+
}
|
|
643
|
+
// the band worth drawing: everything above 1.2 % of the brightest colour
|
|
644
|
+
let lo = LUT_N - 1, hi = 0;
|
|
645
|
+
const cut = 0.012 * peak;
|
|
646
|
+
for (let i = 0; i < LUT_N; i++) {
|
|
647
|
+
if (Math.max(rgb[i * 3], rgb[i * 3 + 1], rgb[i * 3 + 2]) > cut) {
|
|
648
|
+
if (i < lo)
|
|
649
|
+
lo = i;
|
|
650
|
+
if (i > hi)
|
|
651
|
+
hi = i;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
if (hi < lo) {
|
|
655
|
+
lo = 0;
|
|
656
|
+
hi = 1;
|
|
657
|
+
}
|
|
658
|
+
// with a secondary bow there is a dark gap between the two (Alexander's band): two bands, not one
|
|
659
|
+
let gapA = -1, gapB = -1, run = 0;
|
|
660
|
+
for (let i = lo; i <= hi; i++) {
|
|
661
|
+
if (Math.max(rgb[i * 3], rgb[i * 3 + 1], rgb[i * 3 + 2]) > cut)
|
|
662
|
+
run = 0;
|
|
663
|
+
else {
|
|
664
|
+
run++;
|
|
665
|
+
if (run > gapB - gapA) {
|
|
666
|
+
gapB = i;
|
|
667
|
+
gapA = i - run;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
const at = (i) => LUT_A + (LUT_B - LUT_A) * (i + 0.5) / LUT_N;
|
|
672
|
+
const a0 = at(Math.max(lo - 2, 0)), a3 = at(Math.min(hi + 2, LUT_N - 1));
|
|
673
|
+
const split = gapB - gapA > 16; // wider than about 1°
|
|
674
|
+
return { peak: peak, theta: split ? [a0, at(gapA + 3), at(gapB - 2), a3] : [a0, a3, a3, a3] };
|
|
675
|
+
}
|
|
676
|
+
// brightness is set so the default bow (1 mm drops) peaks at 1
|
|
677
|
+
let GAIN = 0;
|
|
678
|
+
const BOW_VERT = /* glsl */ `
|
|
679
|
+
${COMMON_UNIFORMS_GLSL}
|
|
680
|
+
uniform vec3 uAntiW, uE1W, uE2W;
|
|
681
|
+
uniform vec4 uTheta; // inner / outer edge of the primary band, then of the secondary's
|
|
682
|
+
uniform vec3 uShell; // nearest radius, farthest radius, count
|
|
683
|
+
attribute vec2 aBand; // angle around the antisolar axis, which of the four edges
|
|
684
|
+
attribute float aShell;
|
|
685
|
+
varying vec3 vDirW, vDirV;
|
|
686
|
+
varying vec2 vSlab;
|
|
687
|
+
${FOG_V}
|
|
688
|
+
float shellR(float k) { return uShell.x + (uShell.y - uShell.x) * pow(k / uShell.z, 1.5); }
|
|
689
|
+
void main() {
|
|
690
|
+
// a band of directions around the antisolar point, on a shell around the eye
|
|
691
|
+
float th = aBand.y < 0.5 ? uTheta.x : aBand.y < 1.5 ? uTheta.y : aBand.y < 2.5 ? uTheta.z : uTheta.w;
|
|
692
|
+
vec3 dW = uAntiW * cos(th) + (uE1W * cos(aBand.x) + uE2W * sin(aBand.x)) * sin(th);
|
|
693
|
+
// instance 0 is the farthest shell: they draw back to front
|
|
694
|
+
float k = uShell.z - 1.0 - aShell;
|
|
695
|
+
float r0 = shellR(k), r1 = shellR(k + 1.0);
|
|
696
|
+
float rm = 0.5 * (r0 + r1);
|
|
697
|
+
vDirV = mat3(viewMatrix) * dW;
|
|
698
|
+
gl_Position = projectionMatrix * vec4(vDirV * rm, 1.0);
|
|
699
|
+
vDirW = dW;
|
|
700
|
+
vSlab = vec2(r0, r1);
|
|
701
|
+
#ifdef USE_FOG
|
|
702
|
+
vFogDepth = rm;
|
|
703
|
+
#endif
|
|
704
|
+
}
|
|
705
|
+
`;
|
|
706
|
+
const BOW_FRAG = /* glsl */ `
|
|
707
|
+
${COMMON_UNIFORMS_GLSL}
|
|
708
|
+
uniform sampler2D uLut;
|
|
709
|
+
uniform vec2 uLutRange;
|
|
710
|
+
uniform mat3 uToLocal;
|
|
711
|
+
uniform vec3 uFilter;
|
|
712
|
+
uniform vec2 uProj; // projectionMatrix[2][2], projectionMatrix[3][2]
|
|
713
|
+
varying vec3 vDirW, vDirV;
|
|
714
|
+
varying vec2 vSlab;
|
|
715
|
+
${FOG_F}
|
|
716
|
+
float bump(float x) { float t = clamp(1.0 - x * x, 0.0, 1.0); return t * t; }
|
|
717
|
+
float ign(vec2 px) { return fract(52.9829189 * fract(dot(px, vec2(0.06711056, 0.00583715)))); }
|
|
718
|
+
// linear light -> what the canvas stores (the renderer's tone mapping and output colour space)
|
|
719
|
+
vec3 shown(vec3 lin) {
|
|
720
|
+
#if defined( TONE_MAPPING )
|
|
721
|
+
lin = toneMapping(lin);
|
|
722
|
+
#endif
|
|
723
|
+
return linearToOutputTexel(vec4(lin, 1.0)).rgb;
|
|
724
|
+
}
|
|
725
|
+
void main() {
|
|
726
|
+
vec3 d = normalize(uToLocal * vDirW); // the view ray, in the volume's own axes (metres)
|
|
727
|
+
vec3 anti = -uSunDir;
|
|
728
|
+
float th = acos(clamp(dot(d, anti), -1.0, 1.0));
|
|
729
|
+
vec3 bow = texture2D(uLut, vec2((th - uLutRange.x) / (uLutRange.y - uLutRange.x), 0.5)).rgb;
|
|
730
|
+
float top = max(bow.r, max(bow.g, bow.b));
|
|
731
|
+
if (top < 0.004) discard;
|
|
732
|
+
|
|
733
|
+
// Drops fill a half-ellipsoid standing on the ground, density 1 - s^2 (s = scaled radius).
|
|
734
|
+
// Along the ray that is disc/a - a x^2 around the chord's middle x = 0: its integral is a cubic.
|
|
735
|
+
vec3 A = vec3(0.5 * uVolume.x, uVolume.y, 0.5 * uVolume.z);
|
|
736
|
+
vec3 o1 = uCam / A, d1 = d / A;
|
|
737
|
+
float a = dot(d1, d1), b = dot(o1, d1), c = dot(o1, o1) - 1.0;
|
|
738
|
+
float disc = b * b - a * c;
|
|
739
|
+
float tauF = 0.0, dT = 0.0;
|
|
740
|
+
float ra = vSlab.x, rb = vSlab.y; // where this slab's drops are, along the ray
|
|
741
|
+
if (disc > 0.0) {
|
|
742
|
+
float sq = sqrt(disc), tm = -b / a;
|
|
743
|
+
float t0 = max(tm - sq / a, 0.0), t1 = tm + sq / a;
|
|
744
|
+
// the ground cuts the ellipsoid in half
|
|
745
|
+
if (d.y < -1e-5) t1 = min(t1, -uCam.y / d.y);
|
|
746
|
+
else if (uCam.y < 0.0) t0 = d.y > 1e-5 ? max(t0, -uCam.y / d.y) : t1;
|
|
747
|
+
if (t1 > t0) {
|
|
748
|
+
float x0 = t0 - tm;
|
|
749
|
+
float xa = clamp(vSlab.x, t0, t1) - tm, xb = clamp(vSlab.y, t0, t1) - tm;
|
|
750
|
+
float h = disc / a, k = ${DEPTH.toFixed(1)} * sqrt(a);
|
|
751
|
+
float F0 = x0 * h - a * x0 * x0 * x0 / 3.0;
|
|
752
|
+
float Fa = xa * h - a * xa * xa * xa / 3.0;
|
|
753
|
+
float Fb = xb * h - a * xb * xb * xb / 3.0;
|
|
754
|
+
tauF = k * (Fa - F0); // drops in front of this slab
|
|
755
|
+
dT = k * (Fb - Fa); // drops inside it
|
|
756
|
+
ra = xa + tm; rb = xb + tm;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
float pol = 1.0, boost = 1.0;
|
|
761
|
+
vec3 tint = vec3(1.0);
|
|
762
|
+
if (uPointerMode > -0.5 && uPointerActive > 0.001) {
|
|
763
|
+
float S = uPointerStrength * uPointerActive;
|
|
764
|
+
if (uPointerMode > 1.5) {
|
|
765
|
+
// the cursor's polariser: bow light is ~96 % polarised along the bow's tangent
|
|
766
|
+
vec3 er = normalize(d - dot(d, anti) * anti);
|
|
767
|
+
vec3 et = cross(anti, er);
|
|
768
|
+
float cf = dot(et, uFilter);
|
|
769
|
+
pol = mix(1.0, 0.04 + 1.92 * cf * cf, clamp(S, 0.0, 1.0));
|
|
770
|
+
} else {
|
|
771
|
+
// a column around the line of sight to the cursor
|
|
772
|
+
vec3 P = uPointer * uVolume - uCam;
|
|
773
|
+
float tc = dot(P, d);
|
|
774
|
+
float q = sqrt(max(dot(P, P) - tc * tc, 0.0));
|
|
775
|
+
float bq = bump(q / max(uPointerRadius * 6.0, 0.5)) * step(0.0, tc);
|
|
776
|
+
if (uPointerMode < 0.5) {
|
|
777
|
+
// mist spray: fresh drops where the cursor is — a patch of bow even outside the volume,
|
|
778
|
+
// and a brighter, tinted stretch of the bow inside it
|
|
779
|
+
float sp = 2.5 * S * bq;
|
|
780
|
+
if (tc < vSlab.x) tauF += sp;
|
|
781
|
+
else if (tc < vSlab.y) { dT += sp; tint = mix(vec3(1.0), uPointerColor, sp / max(dT, 1e-4)); }
|
|
782
|
+
boost = 1.0 + 0.9 * min(S, 2.0) * bq;
|
|
783
|
+
} else {
|
|
784
|
+
// clearing: the drops are swept out of the column
|
|
785
|
+
float keep = 1.0 - clamp(S * bq, 0.0, 1.0);
|
|
786
|
+
tauF *= keep; dT *= keep;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// This slab's share of the bow: what the drops in it return, dimmed by the drops in front.
|
|
792
|
+
float g0 = 1.0 - exp(-tauF), g1 = 1.0 - exp(-tauF - dT); // the bow built up before / after this slab
|
|
793
|
+
float w = g1 - g0;
|
|
794
|
+
float sunUp = smoothstep(-0.02, 0.06, uSunDir.y);
|
|
795
|
+
float gain = pol * sunUp * uOpacity;
|
|
796
|
+
if (w * gain * top < 0.0004) discard;
|
|
797
|
+
// The canvas blends in its own (non-linear) colour space, where twelve small lights add up to far
|
|
798
|
+
// more than one light of their sum. So each slab adds the step, in canvas values, from the bow
|
|
799
|
+
// built up in front of it to the bow including it: the steps sum to the right colour, and an
|
|
800
|
+
// object standing in the volume cuts the sum exactly where it stands.
|
|
801
|
+
vec3 light = bow * tint * uSunColor * uColor * (gain * uIntensity * boost);
|
|
802
|
+
vec3 col = shown(light * g1) - shown(light * g0);
|
|
803
|
+
// Added to a bright sky the colours would wash out to white, so the bow also covers what is behind
|
|
804
|
+
// it in proportion to its own brightness. Nearer slabs cover farther ones as well: each slab's
|
|
805
|
+
// step is raised by what the slabs in front of it take away.
|
|
806
|
+
float solid = 0.85 * pow(min(top, 1.0), 0.45) * gain;
|
|
807
|
+
col *= exp(solid * g0);
|
|
808
|
+
gl_FragColor = vec4(col, w * solid);
|
|
809
|
+
#ifndef USE_LOGDEPTHBUF
|
|
810
|
+
// depth-test at a dithered distance inside the slab: an object standing in it hides its share
|
|
811
|
+
float zd = mix(ra, rb, ign(gl_FragCoord.xy)) * max(-normalize(vDirV).z, 1e-4);
|
|
812
|
+
gl_FragDepth = clamp(0.5 * (uProj.y - uProj.x * zd) / zd + 0.5, 0.0, 1.0);
|
|
813
|
+
#endif
|
|
814
|
+
${FOG_APPLY}
|
|
815
|
+
}
|
|
816
|
+
`;
|
|
817
|
+
function build(u, c) {
|
|
818
|
+
// two bands (primary, secondary) of SEGMENTS quads around the antisolar axis, one instance per shell
|
|
819
|
+
const geo = new THREE.InstancedBufferGeometry();
|
|
820
|
+
const nv = (SEGMENTS + 1) * 4;
|
|
821
|
+
const band = new Float32Array(nv * 2), pos = new Float32Array(nv * 3);
|
|
822
|
+
// `position` is bounds only: the unit volume box, so Box3.expandByObject centres the gizmo on it
|
|
823
|
+
const box = [-0.5, 0, -0.5, 0.5, 0, 0.5, 0.5, 1, -0.5, -0.5, 1, 0.5];
|
|
824
|
+
for (let i = 0; i <= SEGMENTS; i++) {
|
|
825
|
+
for (let r = 0; r < 4; r++) {
|
|
826
|
+
const v = i * 4 + r;
|
|
827
|
+
band[v * 2] = (i / SEGMENTS) * Math.PI * 2;
|
|
828
|
+
band[v * 2 + 1] = r;
|
|
829
|
+
pos[v * 3] = box[(v % 4) * 3];
|
|
830
|
+
pos[v * 3 + 1] = box[(v % 4) * 3 + 1];
|
|
831
|
+
pos[v * 3 + 2] = box[(v % 4) * 3 + 2];
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
const idx = [];
|
|
835
|
+
for (let i = 0; i < SEGMENTS; i++)
|
|
836
|
+
for (let b = 0; b < 4; b += 2) {
|
|
837
|
+
const p = i * 4 + b;
|
|
838
|
+
idx.push(p, p + 1, p + 4, p + 1, p + 5, p + 4);
|
|
839
|
+
}
|
|
840
|
+
geo.setAttribute('position', new THREE.BufferAttribute(pos, 3));
|
|
841
|
+
geo.setAttribute('aBand', new THREE.BufferAttribute(band, 2));
|
|
842
|
+
geo.setIndex(idx);
|
|
843
|
+
const shells = new Float32Array(SHELLS);
|
|
844
|
+
for (let i = 0; i < SHELLS; i++)
|
|
845
|
+
shells[i] = i;
|
|
846
|
+
geo.setAttribute('aShell', new THREE.InstancedBufferAttribute(shells, 1));
|
|
847
|
+
geo.instanceCount = SHELLS;
|
|
848
|
+
geo.boundingSphere = new THREE.Sphere(new THREE.Vector3(0, 0.5, 0), 0.87);
|
|
849
|
+
// the angle -> colour table
|
|
850
|
+
const rgb = new Float32Array(LUT_N * 3), half = new Uint16Array(LUT_N * 4);
|
|
851
|
+
const lut = new THREE.DataTexture(half, LUT_N, 1, THREE.RGBAFormat, THREE.HalfFloatType);
|
|
852
|
+
lut.minFilter = THREE.LinearFilter;
|
|
853
|
+
lut.magFilter = THREE.LinearFilter;
|
|
854
|
+
lut.wrapS = THREE.ClampToEdgeWrapping;
|
|
855
|
+
lut.wrapT = THREE.ClampToEdgeWrapping;
|
|
856
|
+
lut.generateMipmaps = false;
|
|
857
|
+
u.uLut.value = lut;
|
|
858
|
+
if (!GAIN)
|
|
859
|
+
GAIN = 1 / bowTable(1, 0, rgb).peak;
|
|
860
|
+
let lutDrop = -1, lutSec = -1;
|
|
861
|
+
function refreshTable() {
|
|
862
|
+
lutDrop = u.uDrop.value;
|
|
863
|
+
lutSec = u.uSecondary.value;
|
|
864
|
+
const t = bowTable(lutDrop, lutSec, rgb);
|
|
865
|
+
for (let i = 0; i < LUT_N; i++) {
|
|
866
|
+
half[i * 4] = THREE.DataUtils.toHalfFloat(Math.min(rgb[i * 3] * GAIN, 60000));
|
|
867
|
+
half[i * 4 + 1] = THREE.DataUtils.toHalfFloat(Math.min(rgb[i * 3 + 1] * GAIN, 60000));
|
|
868
|
+
half[i * 4 + 2] = THREE.DataUtils.toHalfFloat(Math.min(rgb[i * 3 + 2] * GAIN, 60000));
|
|
869
|
+
half[i * 4 + 3] = 15360; // 1.0
|
|
870
|
+
}
|
|
871
|
+
lut.needsUpdate = true;
|
|
872
|
+
// the outer vertices of a band sit a little farther out, so its straight sides still enclose it
|
|
873
|
+
const out = (a) => Math.atan(Math.tan(a) / Math.cos(Math.PI / SEGMENTS));
|
|
874
|
+
u.uTheta.value.set(t.theta[0], out(t.theta[1]), t.theta[2], out(t.theta[3]));
|
|
875
|
+
}
|
|
876
|
+
const mat = premulMaterial(u, BOW_VERT, BOW_FRAG, false);
|
|
877
|
+
mat.side = THREE.DoubleSide; // the band is seen from inside its cone, whichever way it winds
|
|
878
|
+
const mesh = new THREE.Mesh(geo, mat);
|
|
879
|
+
mesh.frustumCulled = false;
|
|
880
|
+
mesh.raycast = function () { };
|
|
881
|
+
const object = new THREE.Group();
|
|
882
|
+
object.name = 'rainbow';
|
|
883
|
+
object.add(mesh);
|
|
884
|
+
const _x = new THREE.Vector3(), _y = new THREE.Vector3(), _q = new THREE.Quaternion(), _m = new THREE.Matrix4();
|
|
885
|
+
const _up = new THREE.Vector3(0, 1, 0), _k = new THREE.Vector3();
|
|
886
|
+
return {
|
|
887
|
+
object,
|
|
888
|
+
materials: [mat],
|
|
889
|
+
apply: function (cfg) { applyConfig(u, cfg); },
|
|
890
|
+
update: function (dt, camera, viewH, sunWorld, dpr) {
|
|
891
|
+
updateCommon(u, object, camera, dt, viewH, sunWorld, dpr);
|
|
892
|
+
// the uniforms are shared by every build of this object (React builds twice in StrictMode):
|
|
893
|
+
// make sure the table bound is this build's, and filled
|
|
894
|
+
if (u.uLut.value !== lut) {
|
|
895
|
+
u.uLut.value = lut;
|
|
896
|
+
lutDrop = -1;
|
|
897
|
+
}
|
|
898
|
+
if (u.uDrop.value !== lutDrop || u.uSecondary.value !== lutSec)
|
|
899
|
+
refreshTable();
|
|
900
|
+
// the bow's frame in the world: the antisolar axis and two directions across it
|
|
901
|
+
object.getWorldQuaternion(_q);
|
|
902
|
+
const anti = u.uAntiW.value.copy(u.uSunDir.value).applyQuaternion(_q).negate().normalize();
|
|
903
|
+
const e1 = u.uE1W.value.crossVectors(anti, _up);
|
|
904
|
+
if (e1.lengthSq() < 1e-8)
|
|
905
|
+
e1.set(1, 0, 0);
|
|
906
|
+
e1.normalize();
|
|
907
|
+
u.uE2W.value.crossVectors(anti, e1);
|
|
908
|
+
u.uToLocal.value.setFromMatrix4(_m.makeRotationFromQuaternion(_q.invert()));
|
|
909
|
+
// shells from where the volume starts in front of the eye to its far corner
|
|
910
|
+
const C = u.uCam.value, V = u.uVolume.value;
|
|
911
|
+
const hx = V.x / 2, hz = V.z / 2;
|
|
912
|
+
const near = Math.hypot(Math.max(Math.abs(C.x) - hx, 0), Math.max(-C.y, C.y - V.y, 0), Math.max(Math.abs(C.z) - hz, 0));
|
|
913
|
+
let far = 0;
|
|
914
|
+
for (let i = 0; i < 8; i++)
|
|
915
|
+
far = Math.max(far, _k.set(i & 1 ? hx : -hx, i & 2 ? V.y : 0, i & 4 ? hz : -hz).distanceTo(C));
|
|
916
|
+
const camFar = camera.far !== undefined ? camera.far : 2000;
|
|
917
|
+
const n = Math.max(camera.near !== undefined ? camera.near * 2 : 0.2, 0.3, near);
|
|
918
|
+
u.uShell.value.set(n, Math.max(n * 1.5, Math.min(far, camFar * 0.98)), SHELLS);
|
|
919
|
+
u.uProj.value.set(camera.projectionMatrix.elements[10], camera.projectionMatrix.elements[14]);
|
|
920
|
+
// polariser axis: the cursor's angle around the screen centre, in object space (_q is inverted)
|
|
921
|
+
const P = u.uPointer.value;
|
|
922
|
+
_x.setFromMatrixColumn(camera.matrixWorld, 0).applyQuaternion(_q);
|
|
923
|
+
_y.setFromMatrixColumn(camera.matrixWorld, 1).applyQuaternion(_q);
|
|
924
|
+
const dx = P.x * V.x - C.x, dy = P.y * V.y - C.y, dzz = P.z * V.z - C.z;
|
|
925
|
+
const sx = dx * _x.x + dy * _x.y + dzz * _x.z, sy = dx * _y.x + dy * _y.y + dzz * _y.z;
|
|
926
|
+
const ang = Math.atan2(sy, sx);
|
|
927
|
+
u.uFilter.value.copy(_x).multiplyScalar(Math.cos(ang)).addScaledVector(_y, Math.sin(ang)).normalize();
|
|
928
|
+
},
|
|
929
|
+
dispose: function () { geo.dispose(); mat.dispose(); lut.dispose(); },
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
// =============================================================================
|
|
933
|
+
// Component
|
|
934
|
+
// =============================================================================
|
|
935
|
+
const DEFAULTS = DEFAULT_ENVIRONMENT_OBJECTS.rainbow;
|
|
936
|
+
/** Knobs that rebuild the object: never live, they change once, on commit. */
|
|
937
|
+
const STRUCTURAL = [];
|
|
938
|
+
/** Knobs a keyframe or pointer binding may drive per frame — exactly what applyConfig pushes. */
|
|
939
|
+
const ANIMATABLE = new Set(ENVIRONMENT_OBJECTS_ANIMATABLE.rainbow.map((p) => p.value));
|
|
940
|
+
/** The scene's sky fill is brighter than the medium's own ambient term expects. */
|
|
941
|
+
const SKY_FILL = 0.5;
|
|
942
|
+
/**
|
|
943
|
+
* A single physical rainbow on its own: water dispersion and an Airy profile with supernumerary fringes, centred opposite the sun and seen through the object’s volume. The cursor sprays, clears or polarises it. Use for after-rain and hopeful scenes.
|
|
944
|
+
*
|
|
945
|
+
* The volume is the parent's transform: x / z span its scale, y rises from its origin to scale.y.
|
|
946
|
+
*/
|
|
947
|
+
const Rainbow = ({ config, id, keyframes, mouseMove, onObjectReady, onObjectDispose }) => {
|
|
948
|
+
const rootRef = useRef(null);
|
|
949
|
+
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
950
|
+
const cfg = useMemo(() => withConfigDefaults(DEFAULTS, config), [config]);
|
|
951
|
+
// uniforms outlive rebuilds, so phases and keyframes survive a structural change
|
|
952
|
+
const uniforms = useMemo(() => createUniforms(), []);
|
|
953
|
+
const instance = useMemo(() => build(uniforms, cfg), [uniforms]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
954
|
+
useEffect(() => () => instance.dispose(), [instance]);
|
|
955
|
+
useLayoutEffect(() => { applyConfig(uniforms, cfg); }, [uniforms, cfg]);
|
|
956
|
+
const push = useCallback((merged) => { applyConfig(uniforms, merged); }, [uniforms]);
|
|
957
|
+
useConfigDriver({ rootRef, id, family: 'environment', base: cfg, structural: STRUCTURAL, allowed: ANIMATABLE, keyframes, mouseMove, push });
|
|
958
|
+
const pointer = usePointerField();
|
|
959
|
+
// the scene's sun, read-only: the same one the clouds, the water and the key light use
|
|
960
|
+
const sun = useCloudSun();
|
|
961
|
+
const anchor = useMemo(() => new THREE.Vector3(), []);
|
|
962
|
+
const buffer = useMemo(() => new THREE.Vector2(), []);
|
|
963
|
+
useFrame((state, delta) => {
|
|
964
|
+
const dt = Math.min(delta, 0.1);
|
|
965
|
+
const s = sun.update();
|
|
966
|
+
uniforms.uSunColor.value.copy(s.color);
|
|
967
|
+
uniforms.uSkyAmbient.value.copy(s.skyAmbient).multiplyScalar(SKY_FILL);
|
|
968
|
+
state.gl.getDrawingBufferSize(buffer);
|
|
969
|
+
instance.update(dt, state.camera, buffer.y, s.direction, state.viewport.dpr);
|
|
970
|
+
stepPointerField(uniforms, pointer, state.camera, instance.object, 1, dt, pointerAnchor(instance.object, state.camera, uniforms, anchor, 30));
|
|
971
|
+
});
|
|
972
|
+
return (_jsx("group", { ref: rootRef, children: _jsx("primitive", { object: instance.object }) }));
|
|
973
|
+
};
|
|
974
|
+
export default Rainbow;
|