@3driseai/3drise-viewer 0.1.41 → 0.1.43
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/ClipsRuntime.d.ts +16 -1
- package/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
- package/dist/animationsV2/ClipsRuntime.js +144 -25
- package/dist/animationsV2/appliers.d.ts +9 -0
- package/dist/animationsV2/appliers.d.ts.map +1 -1
- package/dist/animationsV2/appliers.js +62 -2
- package/dist/animationsV2/clipUtils.d.ts +30 -0
- package/dist/animationsV2/clipUtils.d.ts.map +1 -1
- package/dist/animationsV2/clipUtils.js +163 -17
- package/dist/animationsV2/elementSettings.d.ts +5 -0
- package/dist/animationsV2/elementSettings.d.ts.map +1 -1
- package/dist/animationsV2/elementSettings.js +6 -3
- package/dist/animationsV2/sequenceUtils.js +4 -4
- package/dist/animationsV2/types.d.ts +23 -0
- package/dist/animationsV2/types.d.ts.map +1 -1
- package/dist/api/sceneHandle.js +2 -2
- package/dist/components/CreatedObject.d.ts +3 -0
- package/dist/components/CreatedObject.d.ts.map +1 -1
- package/dist/components/CreatedObject.js +6 -2
- package/dist/components/CustomPrimitive.d.ts.map +1 -1
- package/dist/components/CustomPrimitive.js +1 -10
- package/dist/components/EnvironmentManager.d.ts.map +1 -1
- package/dist/components/EnvironmentManager.js +11 -5
- package/dist/components/GeometryShape.d.ts +5 -2
- package/dist/components/GeometryShape.d.ts.map +1 -1
- package/dist/components/GeometryShape.js +71 -10
- package/dist/components/LandObject.d.ts.map +1 -1
- package/dist/components/LandObject.js +1 -1
- package/dist/components/LayoutInstances.d.ts +2 -1
- package/dist/components/LayoutInstances.d.ts.map +1 -1
- package/dist/components/LayoutInstances.js +39 -6
- package/dist/components/LightObject.d.ts.map +1 -1
- package/dist/components/LightObject.js +3 -0
- package/dist/components/ObjectNode.d.ts.map +1 -1
- package/dist/components/ObjectNode.js +62 -13
- package/dist/components/OceanBuilder.d.ts +2 -0
- package/dist/components/OceanBuilder.d.ts.map +1 -1
- package/dist/components/OceanBuilder.js +2 -1
- package/dist/components/RenderEffects.d.ts +8 -0
- package/dist/components/RenderEffects.d.ts.map +1 -0
- package/dist/components/RenderEffects.js +120 -0
- package/dist/components/SceneBuilder.d.ts.map +1 -1
- package/dist/components/SceneBuilder.js +18 -9
- package/dist/components/SportsObject.d.ts +9 -0
- package/dist/components/SportsObject.d.ts.map +1 -0
- package/dist/components/SportsObject.js +20 -0
- package/dist/components/UrbanObject.d.ts +9 -0
- package/dist/components/UrbanObject.d.ts.map +1 -0
- package/dist/components/UrbanObject.js +28 -0
- package/dist/components/VegetationObject.d.ts +9 -0
- package/dist/components/VegetationObject.d.ts.map +1 -0
- package/dist/components/VegetationObject.js +26 -0
- package/dist/components/WaterObject.d.ts +15 -0
- package/dist/components/WaterObject.d.ts.map +1 -0
- package/dist/components/WaterObject.js +29 -0
- package/dist/components/index.d.ts +7 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +6 -1
- package/dist/components/land/LandPatch.d.ts +3 -1
- package/dist/components/land/LandPatch.d.ts.map +1 -1
- package/dist/components/land/LandPatch.js +80 -7
- package/dist/components/layout/InstancedLayout.d.ts +19 -0
- package/dist/components/layout/InstancedLayout.d.ts.map +1 -0
- package/dist/components/layout/InstancedLayout.js +154 -0
- package/dist/components/layout/LayoutGrounds.d.ts +19 -0
- package/dist/components/layout/LayoutGrounds.d.ts.map +1 -0
- package/dist/components/layout/LayoutGrounds.js +104 -0
- package/dist/components/layout/LinkProxies.d.ts +5 -0
- package/dist/components/layout/LinkProxies.d.ts.map +1 -0
- package/dist/components/layout/LinkProxies.js +51 -0
- package/dist/components/layout/LinkedSource.d.ts +14 -0
- package/dist/components/layout/LinkedSource.d.ts.map +1 -0
- package/dist/components/layout/LinkedSource.js +129 -0
- package/dist/components/projector/Projector.d.ts +14 -0
- package/dist/components/projector/Projector.d.ts.map +1 -0
- package/dist/components/projector/Projector.js +44 -0
- package/dist/components/projector/index.d.ts +4 -0
- package/dist/components/projector/index.d.ts.map +1 -0
- package/dist/components/projector/index.js +2 -0
- package/dist/components/projector/projectorCore.d.ts +19 -0
- package/dist/components/projector/projectorCore.d.ts.map +1 -0
- package/dist/components/projector/projectorCore.js +545 -0
- package/dist/components/sports/SportsBall.d.ts +9 -0
- package/dist/components/sports/SportsBall.d.ts.map +1 -0
- package/dist/components/sports/SportsBall.js +23 -0
- package/dist/components/sports/SportsCourt.d.ts +9 -0
- package/dist/components/sports/SportsCourt.d.ts.map +1 -0
- package/dist/components/sports/SportsCourt.js +23 -0
- package/dist/components/sports/SportsEquipment.d.ts +9 -0
- package/dist/components/sports/SportsEquipment.d.ts.map +1 -0
- package/dist/components/sports/SportsEquipment.js +23 -0
- package/dist/components/sports/SportsGoal.d.ts +9 -0
- package/dist/components/sports/SportsGoal.d.ts.map +1 -0
- package/dist/components/sports/SportsGoal.js +23 -0
- package/dist/components/sports/core/ball.d.ts +166 -0
- package/dist/components/sports/core/ball.d.ts.map +1 -0
- package/dist/components/sports/core/ball.js +402 -0
- package/dist/components/sports/core/court.d.ts +442 -0
- package/dist/components/sports/core/court.d.ts.map +1 -0
- package/dist/components/sports/core/court.js +658 -0
- package/dist/components/sports/core/equipment.d.ts +103 -0
- package/dist/components/sports/core/equipment.d.ts.map +1 -0
- package/dist/components/sports/core/equipment.js +403 -0
- package/dist/components/sports/core/goal.d.ts +98 -0
- package/dist/components/sports/core/goal.d.ts.map +1 -0
- package/dist/components/sports/core/goal.js +44 -0
- package/dist/components/sports/index.d.ts +11 -0
- package/dist/components/sports/index.d.ts.map +1 -0
- package/dist/components/sports/index.js +8 -0
- package/dist/components/sports/sportsCore.d.ts +195 -0
- package/dist/components/sports/sportsCore.d.ts.map +1 -0
- package/dist/components/sports/sportsCore.js +758 -0
- package/dist/components/sports/useSportsInstance.d.ts +35 -0
- package/dist/components/sports/useSportsInstance.d.ts.map +1 -0
- package/dist/components/sports/useSportsInstance.js +47 -0
- package/dist/components/terrain/TerrainSurface.d.ts.map +1 -1
- package/dist/components/terrain/TerrainSurface.js +14 -0
- package/dist/components/urban/UrbanBlock.d.ts +9 -0
- package/dist/components/urban/UrbanBlock.d.ts.map +1 -0
- package/dist/components/urban/UrbanBlock.js +23 -0
- package/dist/components/urban/UrbanBuilding.d.ts +9 -0
- package/dist/components/urban/UrbanBuilding.d.ts.map +1 -0
- package/dist/components/urban/UrbanBuilding.js +23 -0
- package/dist/components/urban/UrbanJunction.d.ts +9 -0
- package/dist/components/urban/UrbanJunction.d.ts.map +1 -0
- package/dist/components/urban/UrbanJunction.js +23 -0
- package/dist/components/urban/UrbanPark.d.ts +9 -0
- package/dist/components/urban/UrbanPark.d.ts.map +1 -0
- package/dist/components/urban/UrbanPark.js +23 -0
- package/dist/components/urban/UrbanParkingLot.d.ts +9 -0
- package/dist/components/urban/UrbanParkingLot.d.ts.map +1 -0
- package/dist/components/urban/UrbanParkingLot.js +23 -0
- package/dist/components/urban/UrbanPlaza.d.ts +9 -0
- package/dist/components/urban/UrbanPlaza.d.ts.map +1 -0
- package/dist/components/urban/UrbanPlaza.js +23 -0
- package/dist/components/urban/UrbanRoad.d.ts +9 -0
- package/dist/components/urban/UrbanRoad.d.ts.map +1 -0
- package/dist/components/urban/UrbanRoad.js +23 -0
- package/dist/components/urban/UrbanSignage.d.ts +9 -0
- package/dist/components/urban/UrbanSignage.d.ts.map +1 -0
- package/dist/components/urban/UrbanSignage.js +23 -0
- package/dist/components/urban/UrbanSkyline.d.ts +9 -0
- package/dist/components/urban/UrbanSkyline.d.ts.map +1 -0
- package/dist/components/urban/UrbanSkyline.js +23 -0
- package/dist/components/urban/UrbanStreetFurniture.d.ts +9 -0
- package/dist/components/urban/UrbanStreetFurniture.d.ts.map +1 -0
- package/dist/components/urban/UrbanStreetFurniture.js +23 -0
- package/dist/components/urban/UrbanStreetGrid.d.ts +9 -0
- package/dist/components/urban/UrbanStreetGrid.d.ts.map +1 -0
- package/dist/components/urban/UrbanStreetGrid.js +34 -0
- package/dist/components/urban/UrbanTraffic.d.ts +9 -0
- package/dist/components/urban/UrbanTraffic.d.ts.map +1 -0
- package/dist/components/urban/UrbanTraffic.js +23 -0
- package/dist/components/urban/core/block.d.ts +119 -0
- package/dist/components/urban/core/block.d.ts.map +1 -0
- package/dist/components/urban/core/block.js +266 -0
- package/dist/components/urban/core/building.d.ts +128 -0
- package/dist/components/urban/core/building.d.ts.map +1 -0
- package/dist/components/urban/core/building.js +741 -0
- package/dist/components/urban/core/junction.d.ts +152 -0
- package/dist/components/urban/core/junction.d.ts.map +1 -0
- package/dist/components/urban/core/junction.js +617 -0
- package/dist/components/urban/core/park.d.ts +67 -0
- package/dist/components/urban/core/park.d.ts.map +1 -0
- package/dist/components/urban/core/park.js +176 -0
- package/dist/components/urban/core/parkingLot.d.ts +82 -0
- package/dist/components/urban/core/parkingLot.d.ts.map +1 -0
- package/dist/components/urban/core/parkingLot.js +284 -0
- package/dist/components/urban/core/plaza.d.ts +24 -0
- package/dist/components/urban/core/plaza.d.ts.map +1 -0
- package/dist/components/urban/core/plaza.js +243 -0
- package/dist/components/urban/core/road.d.ts +113 -0
- package/dist/components/urban/core/road.d.ts.map +1 -0
- package/dist/components/urban/core/road.js +347 -0
- package/dist/components/urban/core/signage.d.ts +83 -0
- package/dist/components/urban/core/signage.d.ts.map +1 -0
- package/dist/components/urban/core/signage.js +209 -0
- package/dist/components/urban/core/skyline.d.ts +100 -0
- package/dist/components/urban/core/skyline.d.ts.map +1 -0
- package/dist/components/urban/core/skyline.js +89 -0
- package/dist/components/urban/core/streetFurniture.d.ts +75 -0
- package/dist/components/urban/core/streetFurniture.d.ts.map +1 -0
- package/dist/components/urban/core/streetFurniture.js +193 -0
- package/dist/components/urban/core/streetGrid.d.ts +134 -0
- package/dist/components/urban/core/streetGrid.d.ts.map +1 -0
- package/dist/components/urban/core/streetGrid.js +422 -0
- package/dist/components/urban/core/traffic.d.ts +125 -0
- package/dist/components/urban/core/traffic.d.ts.map +1 -0
- package/dist/components/urban/core/traffic.js +305 -0
- package/dist/components/urban/index.d.ts +29 -0
- package/dist/components/urban/index.d.ts.map +1 -0
- package/dist/components/urban/index.js +16 -0
- package/dist/components/urban/textureSetResolver.d.ts +8 -0
- package/dist/components/urban/textureSetResolver.d.ts.map +1 -0
- package/dist/components/urban/textureSetResolver.js +16 -0
- package/dist/components/urban/urbanCore.d.ts +262 -0
- package/dist/components/urban/urbanCore.d.ts.map +1 -0
- package/dist/components/urban/urbanCore.js +928 -0
- package/dist/components/urban/useUrbanInstance.d.ts +53 -0
- package/dist/components/urban/useUrbanInstance.d.ts.map +1 -0
- package/dist/components/urban/useUrbanInstance.js +100 -0
- package/dist/components/vegetation/VegetationBush.d.ts +9 -0
- package/dist/components/vegetation/VegetationBush.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationBush.js +23 -0
- package/dist/components/vegetation/VegetationCrops.d.ts +9 -0
- package/dist/components/vegetation/VegetationCrops.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationCrops.js +23 -0
- package/dist/components/vegetation/VegetationFlowers.d.ts +9 -0
- package/dist/components/vegetation/VegetationFlowers.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationFlowers.js +23 -0
- package/dist/components/vegetation/VegetationForest.d.ts +9 -0
- package/dist/components/vegetation/VegetationForest.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationForest.js +23 -0
- package/dist/components/vegetation/VegetationFruit.d.ts +9 -0
- package/dist/components/vegetation/VegetationFruit.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationFruit.js +23 -0
- package/dist/components/vegetation/VegetationGrass.d.ts +9 -0
- package/dist/components/vegetation/VegetationGrass.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationGrass.js +23 -0
- package/dist/components/vegetation/VegetationHedge.d.ts +9 -0
- package/dist/components/vegetation/VegetationHedge.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationHedge.js +23 -0
- package/dist/components/vegetation/VegetationIvy.d.ts +9 -0
- package/dist/components/vegetation/VegetationIvy.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationIvy.js +23 -0
- package/dist/components/vegetation/VegetationTree.d.ts +9 -0
- package/dist/components/vegetation/VegetationTree.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationTree.js +23 -0
- package/dist/components/vegetation/VegetationVegetable.d.ts +9 -0
- package/dist/components/vegetation/VegetationVegetable.d.ts.map +1 -0
- package/dist/components/vegetation/VegetationVegetable.js +23 -0
- package/dist/components/vegetation/core/bush.d.ts +1256 -0
- package/dist/components/vegetation/core/bush.d.ts.map +1 -0
- package/dist/components/vegetation/core/bush.js +376 -0
- package/dist/components/vegetation/core/crops.d.ts +1033 -0
- package/dist/components/vegetation/core/crops.d.ts.map +1 -0
- package/dist/components/vegetation/core/crops.js +378 -0
- package/dist/components/vegetation/core/flowers.d.ts +648 -0
- package/dist/components/vegetation/core/flowers.d.ts.map +1 -0
- package/dist/components/vegetation/core/flowers.js +225 -0
- package/dist/components/vegetation/core/forest.d.ts +138 -0
- package/dist/components/vegetation/core/forest.d.ts.map +1 -0
- package/dist/components/vegetation/core/forest.js +310 -0
- package/dist/components/vegetation/core/fruit.d.ts +99 -0
- package/dist/components/vegetation/core/fruit.d.ts.map +1 -0
- package/dist/components/vegetation/core/fruit.js +77 -0
- package/dist/components/vegetation/core/grass.d.ts +103 -0
- package/dist/components/vegetation/core/grass.d.ts.map +1 -0
- package/dist/components/vegetation/core/grass.js +102 -0
- package/dist/components/vegetation/core/hedge.d.ts +193 -0
- package/dist/components/vegetation/core/hedge.d.ts.map +1 -0
- package/dist/components/vegetation/core/hedge.js +243 -0
- package/dist/components/vegetation/core/ivy.d.ts +213 -0
- package/dist/components/vegetation/core/ivy.d.ts.map +1 -0
- package/dist/components/vegetation/core/ivy.js +173 -0
- package/dist/components/vegetation/core/tree.d.ts +1117 -0
- package/dist/components/vegetation/core/tree.d.ts.map +1 -0
- package/dist/components/vegetation/core/tree.js +408 -0
- package/dist/components/vegetation/core/vegetable.d.ts +99 -0
- package/dist/components/vegetation/core/vegetable.d.ts.map +1 -0
- package/dist/components/vegetation/core/vegetable.js +76 -0
- package/dist/components/vegetation/index.d.ts +23 -0
- package/dist/components/vegetation/index.d.ts.map +1 -0
- package/dist/components/vegetation/index.js +14 -0
- package/dist/components/vegetation/produceCore.d.ts +929 -0
- package/dist/components/vegetation/produceCore.d.ts.map +1 -0
- package/dist/components/vegetation/produceCore.js +723 -0
- package/dist/components/vegetation/useVegetationInstance.d.ts +39 -0
- package/dist/components/vegetation/useVegetationInstance.d.ts.map +1 -0
- package/dist/components/vegetation/useVegetationInstance.js +45 -0
- package/dist/components/vegetation/vegetationCore.d.ts +412 -0
- package/dist/components/vegetation/vegetationCore.d.ts.map +1 -0
- package/dist/components/vegetation/vegetationCore.js +1728 -0
- package/dist/components/volumetricClouds/VolumetricCloudsPass.d.ts +5 -0
- package/dist/components/volumetricClouds/VolumetricCloudsPass.d.ts.map +1 -1
- package/dist/components/volumetricClouds/VolumetricCloudsPass.js +39 -13
- package/dist/components/water/OceanSurface.d.ts.map +1 -1
- package/dist/components/water/OceanSurface.js +3 -2
- package/dist/components/water/WaterBody.d.ts.map +1 -1
- package/dist/components/water/WaterBody.js +31 -10
- package/dist/components/water/WaterFountain.d.ts +9 -0
- package/dist/components/water/WaterFountain.d.ts.map +1 -0
- package/dist/components/water/WaterFountain.js +24 -0
- package/dist/components/water/WaterStream.d.ts +9 -0
- package/dist/components/water/WaterStream.d.ts.map +1 -0
- package/dist/components/water/WaterStream.js +24 -0
- package/dist/components/water/WaterWaterfall.d.ts +9 -0
- package/dist/components/water/WaterWaterfall.d.ts.map +1 -0
- package/dist/components/water/WaterWaterfall.js +24 -0
- package/dist/components/water/core/fountain.d.ts +70 -0
- package/dist/components/water/core/fountain.d.ts.map +1 -0
- package/dist/components/water/core/fountain.js +619 -0
- package/dist/components/water/core/stream.d.ts +72 -0
- package/dist/components/water/core/stream.d.ts.map +1 -0
- package/dist/components/water/core/stream.js +167 -0
- package/dist/components/water/core/waterfall.d.ts +78 -0
- package/dist/components/water/core/waterfall.d.ts.map +1 -0
- package/dist/components/water/core/waterfall.js +325 -0
- package/dist/components/water/index.d.ts +8 -0
- package/dist/components/water/index.d.ts.map +1 -1
- package/dist/components/water/index.js +7 -0
- package/dist/components/water/types.d.ts +3 -0
- package/dist/components/water/types.d.ts.map +1 -1
- package/dist/components/water/useWaterInstance.d.ts +43 -0
- package/dist/components/water/useWaterInstance.d.ts.map +1 -0
- package/dist/components/water/useWaterInstance.js +89 -0
- package/dist/components/water/waterCore.d.ts +179 -0
- package/dist/components/water/waterCore.d.ts.map +1 -0
- package/dist/components/water/waterCore.js +516 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +9 -1
- package/dist/hooks/index.d.ts +7 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +5 -2
- package/dist/hooks/useActionPatch.d.ts +23 -0
- package/dist/hooks/useActionPatch.d.ts.map +1 -0
- package/dist/hooks/useActionPatch.js +38 -0
- package/dist/hooks/useCloudSun.d.ts.map +1 -1
- package/dist/hooks/useCloudSun.js +3 -2
- package/dist/hooks/useCommittedInstance.d.ts +6 -0
- package/dist/hooks/useCommittedInstance.d.ts.map +1 -0
- package/dist/hooks/useCommittedInstance.js +42 -0
- package/dist/hooks/useConfigLive.d.ts +1 -1
- package/dist/hooks/useConfigLive.d.ts.map +1 -1
- package/dist/hooks/useContinuousEffects.d.ts.map +1 -1
- package/dist/hooks/useContinuousEffects.js +36 -20
- package/dist/hooks/useGridLayout.d.ts +7 -0
- package/dist/hooks/useGridLayout.d.ts.map +1 -0
- package/dist/hooks/useGridLayout.js +10 -0
- package/dist/hooks/useInteractionEffects.d.ts.map +1 -1
- package/dist/hooks/useInteractionEffects.js +48 -9
- package/dist/hooks/useLightLive.d.ts.map +1 -1
- package/dist/hooks/useLightLive.js +2 -0
- package/dist/hooks/useLinkStatus.d.ts +7 -0
- package/dist/hooks/useLinkStatus.d.ts.map +1 -0
- package/dist/hooks/useLinkStatus.js +20 -0
- package/dist/hooks/useMaterialApplication.d.ts +6 -1
- package/dist/hooks/useMaterialApplication.d.ts.map +1 -1
- package/dist/hooks/useMaterialApplication.js +7 -2
- package/dist/hooks/useOceanMaterial.d.ts +11 -1
- package/dist/hooks/useOceanMaterial.d.ts.map +1 -1
- package/dist/hooks/useOceanMaterial.js +185 -27
- package/dist/hooks/usePathAnimation.d.ts +8 -0
- package/dist/hooks/usePathAnimation.d.ts.map +1 -1
- package/dist/hooks/usePathAnimation.js +140 -59
- package/dist/hooks/useResolvedLayout.d.ts +10 -0
- package/dist/hooks/useResolvedLayout.d.ts.map +1 -0
- package/dist/hooks/useResolvedLayout.js +17 -0
- package/dist/hooks/useTimeLighting.d.ts +36 -0
- package/dist/hooks/useTimeLighting.d.ts.map +1 -0
- package/dist/hooks/useTimeLighting.js +67 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/operations/catalogue/shaderLayers.d.ts.map +1 -1
- package/dist/operations/catalogue/shaderLayers.js +26 -3
- package/dist/render/renderLive.d.ts +11 -0
- package/dist/render/renderLive.d.ts.map +1 -0
- package/dist/render/renderLive.js +57 -0
- 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 +35 -5
- package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts +12 -0
- package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts.map +1 -1
- package/dist/shaders/ocean/OceanSurfaceMaterial.js +55 -7
- package/dist/shaders/terrain/TerrainMaterial.d.ts.map +1 -1
- package/dist/shaders/terrain/TerrainMaterial.js +28 -0
- package/dist/triggers/kinds/change.js +1 -1
- package/dist/ui/UiLayers.d.ts +17 -1
- package/dist/ui/UiLayers.d.ts.map +1 -1
- package/dist/ui/UiLayers.js +76 -3
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/utils/composerHostedPass.d.ts +74 -0
- package/dist/utils/composerHostedPass.d.ts.map +1 -0
- package/dist/utils/composerHostedPass.js +63 -0
- package/dist/utils/geometryScript.d.ts +17 -0
- package/dist/utils/geometryScript.d.ts.map +1 -0
- package/dist/utils/geometryScript.js +92 -0
- package/dist/utils/geometryScript.worker.d.ts +2 -0
- package/dist/utils/geometryScript.worker.d.ts.map +1 -0
- package/dist/utils/geometryScript.worker.js +42 -0
- package/dist/utils/groundRegistry.d.ts +11 -0
- package/dist/utils/groundRegistry.d.ts.map +1 -0
- package/dist/utils/groundRegistry.js +39 -0
- package/dist/utils/instancingSupport.d.ts +9 -0
- package/dist/utils/instancingSupport.d.ts.map +1 -0
- package/dist/utils/instancingSupport.js +13 -0
- package/dist/utils/landPatch.d.ts +26 -5
- package/dist/utils/landPatch.d.ts.map +1 -1
- package/dist/utils/landPatch.js +124 -9
- package/dist/utils/layoutPlacement.d.ts +30 -0
- package/dist/utils/layoutPlacement.d.ts.map +1 -0
- package/dist/utils/layoutPlacement.js +113 -0
- package/dist/utils/linkRegistry.d.ts +15 -0
- package/dist/utils/linkRegistry.d.ts.map +1 -0
- package/dist/utils/linkRegistry.js +11 -0
- package/dist/utils/shapes.d.ts +28 -2
- package/dist/utils/shapes.d.ts.map +1 -1
- package/dist/utils/shapes.js +32 -3
- package/dist/utils/sharedParts.d.ts +61 -0
- package/dist/utils/sharedParts.d.ts.map +1 -0
- package/dist/utils/sharedParts.js +268 -0
- package/dist/utils/svgAsset.d.ts +6 -0
- package/dist/utils/svgAsset.d.ts.map +1 -0
- package/dist/utils/svgAsset.js +46 -0
- package/dist/utils/waterLight.d.ts +29 -0
- package/dist/utils/waterLight.d.ts.map +1 -0
- package/dist/utils/waterLight.js +67 -0
- package/dist/utils/waterReflection.d.ts +185 -0
- package/dist/utils/waterReflection.d.ts.map +1 -0
- package/dist/utils/waterReflection.js +616 -0
- package/package.json +4 -3
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
// Emitted from Claude outputs/sports-lab/common.mjs by gen_family.mjs — edit the lab module and re-emit.
|
|
2
|
+
// Shared core of the sports objects (lab). Emitted as packages/viewer/src/components/sports/sportsCore.ts.
|
|
3
|
+
//
|
|
4
|
+
// A sports object is a solid prop standing in the scene — a court, a ball, a piece of equipment, a
|
|
5
|
+
// goal — built at REAL size in metres (a tennis court is 23.77 × 10.97 m, a football 22 cm across;
|
|
6
|
+
// the transform scale starts at 1 and only places / resizes it). Everything is lit by the scene's
|
|
7
|
+
// lights like a mesh and casts and receives shadows: every material is a three.js lit material
|
|
8
|
+
// (MeshStandardMaterial) with the prop's own code injected through onBeforeCompile —
|
|
9
|
+
// fragment procedural surfaces (acrylic, clay, wood planks, grass, ice, leather, felt, rubber,
|
|
10
|
+
// brushed metal, carbon weave), court markings and ball seams as anti-aliased signed
|
|
11
|
+
// distances (fwidth), wear as fbm scuffs where the play is, seams and dimples as bumps
|
|
12
|
+
// from screen-space derivatives (no tangent frames);
|
|
13
|
+
// vertex nets sag by a catenary (uniform-driven), ball panels puff out between the seams.
|
|
14
|
+
// Colour slots every variant shares: color (the main surface), secondaryColor (lines / second
|
|
15
|
+
// panel / grip / net), accentColor (seams, paint, bands). Knobs describe the prop, never move it.
|
|
16
|
+
import * as THREE from 'three';
|
|
17
|
+
import { MAX_LAMPS, lampRegistry, registerLamp, unregisterLamp, touchLamps, setLamps, placeLamps, createLampUniforms, gatherLamps, createSunUniforms, nightFromSun, applySun, GLSL_COMMON, } from '../urban/urbanCore.js';
|
|
18
|
+
// Night lighting is the urban family's (one lamp list, one night factor): re-exported for the variants.
|
|
19
|
+
export { MAX_LAMPS, lampRegistry, registerLamp, unregisterLamp, touchLamps, setLamps, placeLamps, createLampUniforms, gatherLamps, createSunUniforms, nightFromSun, applySun, GLSL_COMMON, };
|
|
20
|
+
export const DEG_S = Math.PI / 180;
|
|
21
|
+
export const QUALITY_S = { low: 0.5, medium: 1, high: 2 };
|
|
22
|
+
export function mulberry32(a) {
|
|
23
|
+
return function () {
|
|
24
|
+
a |= 0;
|
|
25
|
+
a = (a + 0x6d2b79f5) | 0;
|
|
26
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
27
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
28
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function setColor(uniform, v) {
|
|
32
|
+
if (v === undefined || v === null)
|
|
33
|
+
return;
|
|
34
|
+
if (v.isColor)
|
|
35
|
+
uniform.value.copy(v);
|
|
36
|
+
else
|
|
37
|
+
uniform.value.set(v);
|
|
38
|
+
}
|
|
39
|
+
export function injectUniforms(shader, u) { for (const k in u)
|
|
40
|
+
if (u[k] && typeof u[k] === 'object' && 'value' in u[k])
|
|
41
|
+
shader.uniforms[k] = u[k]; }
|
|
42
|
+
// ---------------------------------------------------------------------------- uniforms / config
|
|
43
|
+
/** Uniforms every sports object has (shared by all of its materials). */
|
|
44
|
+
export function createCommonUniforms() {
|
|
45
|
+
return {
|
|
46
|
+
uTime: { value: 0 },
|
|
47
|
+
uColor: { value: new THREE.Color('#ffffff') },
|
|
48
|
+
uSecondaryColor: { value: new THREE.Color('#ffffff') },
|
|
49
|
+
uAccentColor: { value: new THREE.Color('#ffffff') },
|
|
50
|
+
uWear: { value: 0.2 },
|
|
51
|
+
uGloss: { value: 1 },
|
|
52
|
+
uOpacity: { value: 1 },
|
|
53
|
+
uSeed: { value: 0 },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** The base knobs every sports object shares (push point, part 1). `mats` = every lit material of the object. */
|
|
57
|
+
export function applyCommonConfig(u, c, mats = undefined) {
|
|
58
|
+
setColor(u.uColor, c.color);
|
|
59
|
+
setColor(u.uSecondaryColor, c.secondaryColor);
|
|
60
|
+
setColor(u.uAccentColor, c.accentColor);
|
|
61
|
+
if (c.wear !== undefined)
|
|
62
|
+
u.uWear.value = c.wear;
|
|
63
|
+
if (c.gloss !== undefined)
|
|
64
|
+
u.uGloss.value = c.gloss;
|
|
65
|
+
if (c.seed !== undefined)
|
|
66
|
+
u.uSeed.value = (c.seed % 1000) * 0.731;
|
|
67
|
+
if (c.opacity !== undefined) {
|
|
68
|
+
u.uOpacity.value = c.opacity;
|
|
69
|
+
if (mats)
|
|
70
|
+
for (const m of mats) {
|
|
71
|
+
m.opacity = c.opacity;
|
|
72
|
+
m.transparent = c.opacity < 0.999 || !!m.userData.alwaysTransparent;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Per-frame bookkeeping shared by every object. */
|
|
77
|
+
export function updateCommon(u, dt) { u.uTime.value += dt; }
|
|
78
|
+
// ---------------------------------------------------------------------------- GLSL: shared pieces
|
|
79
|
+
export const SPORT_UNIFORMS_GLSL = /* glsl */ `
|
|
80
|
+
uniform float uTime, uWear, uGloss, uOpacity, uSeed;
|
|
81
|
+
uniform vec3 uColor, uSecondaryColor, uAccentColor;
|
|
82
|
+
varying vec3 vObj;
|
|
83
|
+
varying vec3 vWorldPos;
|
|
84
|
+
`;
|
|
85
|
+
export const NOISE_GLSL_S = /* glsl */ `
|
|
86
|
+
float shash12(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); }
|
|
87
|
+
float shash13(vec3 p) { p = fract(p * 0.1031); p += dot(p, p.zyx + 31.32); return fract((p.x + p.y) * p.z); }
|
|
88
|
+
float snoise2(vec2 p) {
|
|
89
|
+
vec2 i = floor(p); vec2 f = fract(p); f = f * f * (3.0 - 2.0 * f);
|
|
90
|
+
return mix(mix(shash12(i), shash12(i + vec2(1, 0)), f.x), mix(shash12(i + vec2(0, 1)), shash12(i + vec2(1, 1)), f.x), f.y);
|
|
91
|
+
}
|
|
92
|
+
float snoise3(vec3 p) {
|
|
93
|
+
vec3 i = floor(p); vec3 f = fract(p); f = f * f * (3.0 - 2.0 * f);
|
|
94
|
+
return mix(mix(mix(shash13(i), shash13(i + vec3(1, 0, 0)), f.x), mix(shash13(i + vec3(0, 1, 0)), shash13(i + vec3(1, 1, 0)), f.x), f.y),
|
|
95
|
+
mix(mix(shash13(i + vec3(0, 0, 1)), shash13(i + vec3(1, 0, 1)), f.x), mix(shash13(i + vec3(0, 1, 1)), shash13(i + vec3(1, 1, 1)), f.x), f.y), f.z);
|
|
96
|
+
}
|
|
97
|
+
float sfbm2(vec2 p) { return (0.5 * snoise2(p) + 0.25 * snoise2(p * 2.03 + 7.1) + 0.125 * snoise2(p * 4.07 + 3.3)) / 0.875; }
|
|
98
|
+
float sfbm3(vec3 p) { return (0.5 * snoise3(p) + 0.25 * snoise3(p * 2.03 + 7.1) + 0.125 * snoise3(p * 4.07 + 3.3)) / 0.875; }
|
|
99
|
+
// anti-aliased line coverage of a signed distance d (metres) for a line of width w
|
|
100
|
+
float aaLine(float d, float w) { float fw = fwidth(d) * 0.75 + 1e-5; return 1.0 - smoothstep(w * 0.5 - fw, w * 0.5 + fw, abs(d)); }
|
|
101
|
+
float aaStep(float d) { float fw = fwidth(d) * 0.75 + 1e-5; return smoothstep(-fw, fw, d); }
|
|
102
|
+
float sdSeg(vec2 p, vec2 a, vec2 b) { vec2 pa = p - a, ba = b - a; float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); return length(pa - ba * h); }
|
|
103
|
+
float sdBox2(vec2 p, vec2 b) { vec2 d = abs(p) - b; return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); }
|
|
104
|
+
// distance to the outline of a box / circle / arc (for line marking)
|
|
105
|
+
float lnBox(vec2 p, vec2 c, vec2 hb) { return abs(sdBox2(p - c, hb)); }
|
|
106
|
+
float lnCircle(vec2 p, vec2 c, float r) { return abs(length(p - c) - r); }
|
|
107
|
+
// arc of a circle between angles a0..a1 (radians, counter-clockwise from +x); outside the span, distance to the end points
|
|
108
|
+
float lnArc(vec2 p, vec2 c, float r, float a0, float a1) {
|
|
109
|
+
vec2 q = p - c; float a = atan(q.y, q.x);
|
|
110
|
+
float mid = 0.5 * (a0 + a1), hw = 0.5 * (a1 - a0);
|
|
111
|
+
float da = mod(a - mid + 3.14159265, 6.2831853) - 3.14159265;
|
|
112
|
+
if (abs(da) <= hw) return abs(length(q) - r);
|
|
113
|
+
vec2 e0 = c + r * vec2(cos(a0), sin(a0)), e1 = c + r * vec2(cos(a1), sin(a1));
|
|
114
|
+
return min(length(p - e0), length(p - e1));
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
117
|
+
export const WEAR_GLSL = /* glsl */ `
|
|
118
|
+
// Scuffs: two fbm layers thresholded by uWear, 0 pristine .. 1 heavily worn; focus (0..1) raises the wear
|
|
119
|
+
// where the play is (a key, a baseline, the sweet spot) so it never looks like uniform noise.
|
|
120
|
+
float scuffs(vec3 p, float scale, float focus) {
|
|
121
|
+
float n = sfbm3(p * scale + uSeed * 7.0) * 0.65 + sfbm3(p * scale * 4.3 + 13.0) * 0.35;
|
|
122
|
+
float t = 1.0 - clamp(uWear * (0.55 + 0.9 * focus), 0.0, 1.0);
|
|
123
|
+
return smoothstep(t - 0.05, t + 0.3, n) * clamp(uWear * 2.0, 0.0, 1.0);
|
|
124
|
+
}
|
|
125
|
+
// a height field as a bump, from screen-space derivatives (no tangent frame needed)
|
|
126
|
+
vec3 bumpNormal(vec3 normal, float h) {
|
|
127
|
+
vec3 dpdx = dFdx(-vViewPosition), dpdy = dFdy(-vViewPosition);
|
|
128
|
+
float dhx = dFdx(h), dhy = dFdy(h);
|
|
129
|
+
vec3 r1 = cross(dpdy, normal), r2 = cross(normal, dpdx);
|
|
130
|
+
float det = dot(dpdx, r1);
|
|
131
|
+
vec3 grad = (r1 * dhx + r2 * dhy) / max(abs(det), 1e-7) * sign(det);
|
|
132
|
+
return normalize(normal - grad);
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
const COMMON_VERT_PARS = /* glsl */ `
|
|
136
|
+
varying vec3 vObj;
|
|
137
|
+
varying vec3 vWorldPos;
|
|
138
|
+
`;
|
|
139
|
+
/**
|
|
140
|
+
* A lit material (MeshStandardMaterial) with the prop's code injected, plus its depth twin for
|
|
141
|
+
* shadows (same vertex displacement, same alpha cut). Hooks (GLSL strings):
|
|
142
|
+
* pars fragment declarations (functions, uniforms of `own`)
|
|
143
|
+
* color sets `diffuseColor.rgb` (and `.a` for cut-outs); runs at map_fragment
|
|
144
|
+
* rough expression for roughnessFactor (default o.roughness)
|
|
145
|
+
* metal expression for metalnessFactor
|
|
146
|
+
* normal code that may reassign `normal` (after normal_fragment_maps)
|
|
147
|
+
* emissive code adding to totalEmissiveRadiance
|
|
148
|
+
* vPars / vCode vertex declarations / code after begin_vertex (may edit `transformed`, `objectNormal` is already read)
|
|
149
|
+
* depthBias [factor, units] polygon offset for a surface laid on other ground surfaces
|
|
150
|
+
* Every material gets vObj (object-space position) and vWorldPos.
|
|
151
|
+
*/
|
|
152
|
+
export function litMaterial(u, o) {
|
|
153
|
+
const own = o.own || {};
|
|
154
|
+
const mat = new THREE.MeshStandardMaterial({
|
|
155
|
+
color: 0xffffff, roughness: o.roughness ?? 0.7, metalness: o.metalness ?? 0, envMapIntensity: o.envMapIntensity ?? 0.6,
|
|
156
|
+
side: o.side ?? THREE.FrontSide, transparent: !!o.transparent, alphaTest: o.alphaTest ?? 0, depthWrite: o.depthWrite ?? true,
|
|
157
|
+
});
|
|
158
|
+
if (o.transparent)
|
|
159
|
+
mat.userData.alwaysTransparent = true;
|
|
160
|
+
// A ground surface laid on other ground surfaces (a court on a plaza, a park lawn, a road) sits only
|
|
161
|
+
// centimetres above them; urban paving already pulls itself toward the camera in depth (polygon offset
|
|
162
|
+
// -2 / -4) to beat the terrain, so at a distance it would beat the court too. `depthBias` [factor, units]
|
|
163
|
+
// pulls this surface further forward — depth only, nothing moves on screen.
|
|
164
|
+
if (o.depthBias) {
|
|
165
|
+
mat.polygonOffset = true;
|
|
166
|
+
mat.polygonOffsetFactor = o.depthBias[0];
|
|
167
|
+
mat.polygonOffsetUnits = o.depthBias[1];
|
|
168
|
+
}
|
|
169
|
+
mat.defines = { USE_UV: '' };
|
|
170
|
+
mat.userData.uniforms = own;
|
|
171
|
+
const vert = (shader) => {
|
|
172
|
+
injectUniforms(shader, u);
|
|
173
|
+
injectUniforms(shader, own);
|
|
174
|
+
shader.vertexShader = shader.vertexShader
|
|
175
|
+
.replace('#include <common>', `#include <common>\n${COMMON_VERT_PARS}\n${SPORT_UNIFORMS_GLSL.replace(/varying[^\n]*\n/g, '')}\n${o.vPars || ''}`)
|
|
176
|
+
.replace('#include <begin_vertex>', `#include <begin_vertex>\n vObj = position;\n ${o.vCode || ''}\n vWorldPos = (modelMatrix * vec4(transformed, 1.0)).xyz;`);
|
|
177
|
+
};
|
|
178
|
+
mat.onBeforeCompile = (shader) => {
|
|
179
|
+
vert(shader);
|
|
180
|
+
shader.fragmentShader = shader.fragmentShader
|
|
181
|
+
.replace('#include <common>', `#include <common>\n${SPORT_UNIFORMS_GLSL}\n${NOISE_GLSL_S}\n${WEAR_GLSL}\n${o.pars || ''}`)
|
|
182
|
+
.replace('#include <map_fragment>', `${o.color || ''}\n float propRough = ${o.rough ? '(' + o.rough + ')' : 'roughness'};\n float propMetal = ${o.metal ? '(' + o.metal + ')' : 'metalness'};`)
|
|
183
|
+
.replace('#include <roughnessmap_fragment>', '#include <roughnessmap_fragment>\n roughnessFactor = clamp(propRough, 0.03, 1.0);')
|
|
184
|
+
.replace('#include <metalnessmap_fragment>', '#include <metalnessmap_fragment>\n metalnessFactor = propMetal;')
|
|
185
|
+
.replace('#include <normal_fragment_maps>', `#include <normal_fragment_maps>\n ${o.normal || ''}`)
|
|
186
|
+
.replace('#include <emissivemap_fragment>', `#include <emissivemap_fragment>\n ${o.emissive || ''}`);
|
|
187
|
+
};
|
|
188
|
+
mat.customProgramCacheKey = () => 'sport-' + (o.name || 'lit');
|
|
189
|
+
const depth = new THREE.MeshDepthMaterial({ depthPacking: THREE.RGBADepthPacking, side: o.side ?? THREE.FrontSide });
|
|
190
|
+
depth.defines = { USE_UV: '' };
|
|
191
|
+
if (o.alphaTest || o.transparent)
|
|
192
|
+
depth.alphaTest = o.depthAlphaTest ?? 0.5;
|
|
193
|
+
depth.onBeforeCompile = (shader) => {
|
|
194
|
+
vert(shader);
|
|
195
|
+
if (o.depthAlpha) {
|
|
196
|
+
shader.fragmentShader = shader.fragmentShader
|
|
197
|
+
.replace('#include <common>', `#include <common>\n${SPORT_UNIFORMS_GLSL}\n${NOISE_GLSL_S}\n${o.pars || ''}`)
|
|
198
|
+
.replace('#include <map_fragment>', `${o.depthAlpha}`);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
depth.customProgramCacheKey = () => 'sport-' + (o.name || 'lit') + '-depth';
|
|
202
|
+
return { material: mat, depth };
|
|
203
|
+
}
|
|
204
|
+
/** A prop mesh: casts and receives shadows, picks normally (props are clickable scene objects). */
|
|
205
|
+
export function propMesh(geo, pair, name = '', opts = undefined) {
|
|
206
|
+
const o = Object.assign({ cast: true, receive: true, cull: true }, opts);
|
|
207
|
+
const mesh = new THREE.Mesh(geo, pair.material);
|
|
208
|
+
mesh.customDepthMaterial = pair.depth;
|
|
209
|
+
mesh.castShadow = o.cast;
|
|
210
|
+
mesh.receiveShadow = o.receive;
|
|
211
|
+
mesh.frustumCulled = o.cull;
|
|
212
|
+
mesh.name = name || '';
|
|
213
|
+
return mesh;
|
|
214
|
+
}
|
|
215
|
+
export function disposePair(pair) { pair.material.dispose(); pair.depth.dispose(); }
|
|
216
|
+
// ---------------------------------------------------------------------------- geometry helpers
|
|
217
|
+
/** Concatenate geometries (made non-indexed) that share a material. */
|
|
218
|
+
export function mergeGeos(list) {
|
|
219
|
+
const parts = list.map((g) => (g.index ? g.toNonIndexed() : g));
|
|
220
|
+
let n = 0;
|
|
221
|
+
for (const g of parts)
|
|
222
|
+
n += g.attributes.position.count;
|
|
223
|
+
const pos = new Float32Array(n * 3), nrm = new Float32Array(n * 3), uv = new Float32Array(n * 2);
|
|
224
|
+
let off = 0;
|
|
225
|
+
for (const g of parts) {
|
|
226
|
+
const c = g.attributes.position.count;
|
|
227
|
+
pos.set(g.attributes.position.array, off * 3);
|
|
228
|
+
if (g.attributes.normal)
|
|
229
|
+
nrm.set(g.attributes.normal.array, off * 3);
|
|
230
|
+
if (g.attributes.uv)
|
|
231
|
+
uv.set(g.attributes.uv.array, off * 2);
|
|
232
|
+
off += c;
|
|
233
|
+
}
|
|
234
|
+
const out = new THREE.BufferGeometry();
|
|
235
|
+
out.setAttribute('position', new THREE.BufferAttribute(pos, 3));
|
|
236
|
+
out.setAttribute('normal', new THREE.BufferAttribute(nrm, 3));
|
|
237
|
+
out.setAttribute('uv', new THREE.BufferAttribute(uv, 2));
|
|
238
|
+
for (const g of parts)
|
|
239
|
+
if (!list.includes(g))
|
|
240
|
+
g.dispose();
|
|
241
|
+
out.computeBoundingSphere();
|
|
242
|
+
out.computeBoundingBox();
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
/** Apply a matrix (position / rotation / scale) to a geometry in place and return it. */
|
|
246
|
+
export function placed(geo, pos = undefined, rotEuler = undefined, scale = undefined) {
|
|
247
|
+
const m = new THREE.Matrix4();
|
|
248
|
+
const q = new THREE.Quaternion();
|
|
249
|
+
if (rotEuler)
|
|
250
|
+
q.setFromEuler(new THREE.Euler(rotEuler[0] || 0, rotEuler[1] || 0, rotEuler[2] || 0));
|
|
251
|
+
m.compose(new THREE.Vector3(...(pos || [0, 0, 0])), q, new THREE.Vector3(...(scale || [1, 1, 1])));
|
|
252
|
+
geo.applyMatrix4(m);
|
|
253
|
+
return geo;
|
|
254
|
+
}
|
|
255
|
+
/** Lathe from a profile of [radius, y] pairs (bottom → top). */
|
|
256
|
+
export function lathe(profile, segs = 32) {
|
|
257
|
+
const pts = profile.map(([r, y]) => new THREE.Vector2(r, y));
|
|
258
|
+
const g = new THREE.LatheGeometry(pts, segs || 32);
|
|
259
|
+
return g;
|
|
260
|
+
}
|
|
261
|
+
/** A tube along points (metres), round section. */
|
|
262
|
+
export function tube(points, radius, segs = 32, radial = 10, closed = false) {
|
|
263
|
+
const curve = new THREE.CatmullRomCurve3(points.map((p) => new THREE.Vector3(...p)), !!closed, 'catmullrom', 0.5);
|
|
264
|
+
return new THREE.TubeGeometry(curve, segs || 32, radius, radial || 10, !!closed);
|
|
265
|
+
}
|
|
266
|
+
/** A straight cylinder between two points. */
|
|
267
|
+
export function bar(a, b, r, radial = 12) {
|
|
268
|
+
const A = new THREE.Vector3(...a), B = new THREE.Vector3(...b);
|
|
269
|
+
const len = A.distanceTo(B);
|
|
270
|
+
const g = new THREE.CylinderGeometry(r, r, len, radial || 12, 1);
|
|
271
|
+
const mid = A.clone().add(B).multiplyScalar(0.5);
|
|
272
|
+
const dir = B.clone().sub(A).normalize();
|
|
273
|
+
const q = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir);
|
|
274
|
+
g.applyQuaternion(q);
|
|
275
|
+
g.translate(mid.x, mid.y, mid.z);
|
|
276
|
+
return g;
|
|
277
|
+
}
|
|
278
|
+
export function box(w, h, d, pos = undefined, rot = undefined) { return placed(new THREE.BoxGeometry(w, h, d), pos, rot); }
|
|
279
|
+
// ---------------------------------------------------------------------------- the material library
|
|
280
|
+
/**
|
|
281
|
+
* The surfaces equipment, goals and fixtures are made of, one shader with a per-part kind:
|
|
282
|
+
* 0 paint smooth coat with a faint orange peel, chips where worn
|
|
283
|
+
* 1 wood grain along the part's own long axis (uGrainAxis), ash / maple tones, scuffed where worn
|
|
284
|
+
* 2 metal brushed (anisotropic streaks along the axis), fingerprints where worn, metallic
|
|
285
|
+
* 3 rubber matte with a fine stipple, lighter where worn
|
|
286
|
+
* 4 leather pebble grain bump, darker creases, polished where handled
|
|
287
|
+
* 5 carbon twill weave, glossy clear coat
|
|
288
|
+
* 6 plastic glossy, slight flow lines
|
|
289
|
+
* 7 fabric felt / cloth, soft nap (no specular)
|
|
290
|
+
* Colour: `slot` 0 color · 1 secondaryColor · 2 accentColor · 3 the part's own uTint (natural material colour).
|
|
291
|
+
* A multiplier uTint always applies (white = as is).
|
|
292
|
+
*/
|
|
293
|
+
export const PART_KINDS = { paint: 0, wood: 1, metal: 2, rubber: 3, leather: 4, carbon: 5, plastic: 6, fabric: 7 };
|
|
294
|
+
export function partMaterial(u, o = undefined) {
|
|
295
|
+
const opt = Object.assign({ kind: 'paint', slot: 3, tint: '#cccccc', roughness: 0.5, metalness: 0, axis: [1, 0, 0], scale: 1 }, o);
|
|
296
|
+
const own = {
|
|
297
|
+
uKind: { value: PART_KINDS[opt.kind] ?? 0 },
|
|
298
|
+
uSlot: { value: opt.slot },
|
|
299
|
+
uTint: { value: new THREE.Color(opt.tint) },
|
|
300
|
+
uGrainAxis: { value: new THREE.Vector3(...opt.axis).normalize() },
|
|
301
|
+
uPartScale: { value: opt.scale },
|
|
302
|
+
};
|
|
303
|
+
const pair = litMaterial(u, {
|
|
304
|
+
name: 'part-' + opt.kind,
|
|
305
|
+
own, roughness: opt.roughness, metalness: opt.metalness, envMapIntensity: opt.kind === 'metal' ? 1.0 : 0.6,
|
|
306
|
+
pars: /* glsl */ `
|
|
307
|
+
uniform float uKind, uSlot, uPartScale;
|
|
308
|
+
uniform vec3 uTint, uGrainAxis;
|
|
309
|
+
vec3 slotColor() {
|
|
310
|
+
vec3 c = uSlot < 0.5 ? uColor : uSlot < 1.5 ? uSecondaryColor : uSlot < 2.5 ? uAccentColor : uTint;
|
|
311
|
+
return uSlot < 2.5 ? c * uTint : c;
|
|
312
|
+
}
|
|
313
|
+
// coordinates along / across the part's grain axis, in metres
|
|
314
|
+
vec2 grainUV(vec3 p) {
|
|
315
|
+
vec3 a = uGrainAxis; vec3 b = normalize(abs(a.y) < 0.9 ? cross(a, vec3(0, 1, 0)) : cross(a, vec3(1, 0, 0)));
|
|
316
|
+
vec3 c = cross(a, b);
|
|
317
|
+
return vec2(dot(p, a), dot(p, b) + dot(p, c) * 0.37);
|
|
318
|
+
}
|
|
319
|
+
float partHeight; float partRough; float partMetalF; vec3 partCol;
|
|
320
|
+
void partSurface(vec3 p) {
|
|
321
|
+
vec3 base = slotColor();
|
|
322
|
+
vec2 g = grainUV(p) / uPartScale;
|
|
323
|
+
float worn = scuffs(p, 28.0 / uPartScale, 0.6);
|
|
324
|
+
partHeight = 0.0; partRough = roughness; partMetalF = metalness; partCol = base;
|
|
325
|
+
if (uKind < 0.5) { // paint: orange peel, chips show primer
|
|
326
|
+
float peel = snoise3(p * 900.0) * 0.5 + 0.5;
|
|
327
|
+
partHeight = peel * 0.00004;
|
|
328
|
+
partCol = mix(base, base * 0.55 + 0.2, worn * 0.8);
|
|
329
|
+
partRough = mix(0.25, 0.6, worn) + peel * 0.05;
|
|
330
|
+
} else if (uKind < 1.5) { // wood: rings as long streaks along the axis, pores, darker where oiled by hands
|
|
331
|
+
float ring = sfbm2(vec2(g.x * 6.0, g.y * 90.0));
|
|
332
|
+
float pores = snoise2(vec2(g.x * 400.0, g.y * 1400.0));
|
|
333
|
+
float grain = smoothstep(0.35, 0.75, ring) * 0.7 + pores * 0.15;
|
|
334
|
+
partCol = base * mix(vec3(1.08, 1.03, 0.95), vec3(0.72, 0.6, 0.48), grain);
|
|
335
|
+
partCol = mix(partCol, partCol * 0.6, worn * 0.7);
|
|
336
|
+
partHeight = grain * 0.00006;
|
|
337
|
+
partRough = mix(0.55, 0.85, grain) + worn * 0.1;
|
|
338
|
+
} else if (uKind < 2.5) { // metal: brushed streaks, dull where handled
|
|
339
|
+
float streak = snoise2(vec2(g.x * 60.0, g.y * 2500.0));
|
|
340
|
+
partCol = base * (0.92 + 0.16 * streak);
|
|
341
|
+
partRough = mix(0.28, 0.5, streak * 0.5 + worn * 0.5);
|
|
342
|
+
partMetalF = 1.0 - worn * 0.3;
|
|
343
|
+
partHeight = streak * 0.00002;
|
|
344
|
+
} else if (uKind < 3.5) { // rubber: stipple, polished where worn
|
|
345
|
+
float st = snoise3(p * 1500.0);
|
|
346
|
+
partCol = mix(base, base * 1.6 + 0.08, worn * 0.5);
|
|
347
|
+
partHeight = st * 0.00005;
|
|
348
|
+
partRough = mix(0.9, 0.55, worn);
|
|
349
|
+
} else if (uKind < 4.5) { // leather: pebble grain, creases
|
|
350
|
+
float peb = snoise3(p * 700.0) * 0.6 + snoise3(p * 2100.0) * 0.4;
|
|
351
|
+
float crease = smoothstep(0.55, 0.75, sfbm3(p * 90.0 + 3.0));
|
|
352
|
+
partCol = base * (0.85 + 0.3 * peb) * (1.0 - 0.25 * crease);
|
|
353
|
+
partCol = mix(partCol, partCol * 1.25, worn * 0.5);
|
|
354
|
+
partHeight = peb * 0.00012;
|
|
355
|
+
partRough = mix(0.65, 0.35, worn) - 0.1 * crease;
|
|
356
|
+
} else if (uKind < 5.5) { // carbon: 2x2 twill weave under a clear coat
|
|
357
|
+
vec2 w = g * 700.0;
|
|
358
|
+
float tw = step(0.5, fract((floor(w.x) + floor(w.y)) * 0.5));
|
|
359
|
+
float fib = mix(abs(fract(w.x) - 0.5), abs(fract(w.y) - 0.5), tw) * 2.0;
|
|
360
|
+
partCol = base * mix(0.55, 1.15, 1.0 - fib * fib) ;
|
|
361
|
+
partRough = 0.18 + worn * 0.25;
|
|
362
|
+
partHeight = (1.0 - fib) * 0.00001;
|
|
363
|
+
} else if (uKind < 6.5) { // plastic: glossy with flow lines
|
|
364
|
+
float fl = snoise2(g * vec2(30.0, 400.0));
|
|
365
|
+
partCol = base * (0.96 + 0.08 * fl);
|
|
366
|
+
partCol = mix(partCol, partCol * 0.8 + 0.1, worn * 0.6);
|
|
367
|
+
partRough = 0.22 + worn * 0.3;
|
|
368
|
+
} else { // fabric / felt: soft nap, no shine
|
|
369
|
+
float nap = snoise3(p * 2500.0);
|
|
370
|
+
partCol = base * (0.9 + 0.2 * nap);
|
|
371
|
+
partCol = mix(partCol, partCol * 0.7, worn * 0.5);
|
|
372
|
+
partRough = 1.0;
|
|
373
|
+
partHeight = nap * 0.00008;
|
|
374
|
+
}
|
|
375
|
+
partRough = clamp(partRough / max(uGloss, 0.05), 0.03, 1.0);
|
|
376
|
+
}`,
|
|
377
|
+
color: /* glsl */ `partSurface(vObj);\n diffuseColor.rgb *= partCol;`,
|
|
378
|
+
rough: 'partRough', metal: 'partMetalF',
|
|
379
|
+
normal: /* glsl */ `normal = bumpNormal(normal, partHeight);`,
|
|
380
|
+
});
|
|
381
|
+
return pair;
|
|
382
|
+
}
|
|
383
|
+
// ---------------------------------------------------------------------------- nets
|
|
384
|
+
/**
|
|
385
|
+
* A net: knotted square mesh drawn as anti-aliased threads (alpha-cut, with a translucent far field so a
|
|
386
|
+
* distant net reads as a veil instead of shimmering), on a ruled surface spanned between two frame
|
|
387
|
+
* curves (netRuled). The sag is a uniform (so netTension animates): every vertex moves along its own
|
|
388
|
+
* aSag (direction × metres, set at build: out of the goal, down for a roof) by
|
|
389
|
+
* uSag × 4s(1−s) × profile(t) profile: hanging (bottom free) 1−t · held on both edges 4t(1−t)
|
|
390
|
+
* where s runs along the frame curves and t from the first curve to the second.
|
|
391
|
+
*/
|
|
392
|
+
export function netMaterial(u, o = undefined) {
|
|
393
|
+
const opt = Object.assign({ mesh: 0.12, thread: 0.004, sag: 0.1, sagMode: 1, slot: 1 }, o);
|
|
394
|
+
const own = {
|
|
395
|
+
uMesh: { value: opt.mesh }, uThread: { value: opt.thread }, uSag: { value: opt.sag }, uSagMode: { value: opt.sagMode },
|
|
396
|
+
uNetSlot: { value: opt.slot },
|
|
397
|
+
};
|
|
398
|
+
const pars = /* glsl */ `
|
|
399
|
+
uniform float uMesh, uThread, uNetSlot;
|
|
400
|
+
float netCover(vec2 m) {
|
|
401
|
+
// square mesh with knots at the crossings; m in metres
|
|
402
|
+
vec2 c = m / uMesh;
|
|
403
|
+
vec2 d = abs(fract(c) - 0.5) * uMesh; // distance to the nearest thread in each direction
|
|
404
|
+
float w = uThread;
|
|
405
|
+
float fw = max(fwidth(m.x), fwidth(m.y)) * 0.7 + 1e-5;
|
|
406
|
+
float tx = 1.0 - smoothstep(w * 0.5 - fw, w * 0.5 + fw, d.x);
|
|
407
|
+
float ty = 1.0 - smoothstep(w * 0.5 - fw, w * 0.5 + fw, d.y);
|
|
408
|
+
float knot = 1.0 - smoothstep(w * 0.9 - fw, w * 0.9 + fw, length(d));
|
|
409
|
+
float cover = max(max(tx, ty), knot);
|
|
410
|
+
// far field: when a cell is a few pixels the threads can't be resolved — fade to the mean coverage
|
|
411
|
+
float far = smoothstep(0.08, 0.5, fw / uMesh);
|
|
412
|
+
// two thread directions plus knots, seen a little obliquely: reads denser than the bare 2w/mesh
|
|
413
|
+
float mean = clamp(2.6 * w / uMesh, 0.0, 0.85);
|
|
414
|
+
return mix(cover, mean, far);
|
|
415
|
+
}`;
|
|
416
|
+
const pair = litMaterial(u, {
|
|
417
|
+
name: 'net', own, roughness: 0.85, metalness: 0, side: THREE.DoubleSide, transparent: true, alphaTest: 0.02, depthWrite: true,
|
|
418
|
+
vPars: /* glsl */ `uniform float uSag, uSagMode; attribute vec2 aT; attribute vec3 aSag;`,
|
|
419
|
+
vCode: /* glsl */ `
|
|
420
|
+
{
|
|
421
|
+
float across = 4.0 * aT.x * (1.0 - aT.x);
|
|
422
|
+
float prof = uSagMode < 0.5 ? (1.0 - aT.y) : 4.0 * aT.y * (1.0 - aT.y);
|
|
423
|
+
transformed += aSag * (uSag * across * prof);
|
|
424
|
+
}`,
|
|
425
|
+
pars,
|
|
426
|
+
color: /* glsl */ `
|
|
427
|
+
float cover = netCover(vUv);
|
|
428
|
+
vec3 nc = uNetSlot < 0.5 ? uColor : uNetSlot < 1.5 ? uSecondaryColor : uAccentColor;
|
|
429
|
+
float dirt = scuffs(vWorldPos, 3.0, 0.3);
|
|
430
|
+
diffuseColor.rgb *= mix(nc, nc * 0.6, dirt * 0.6);
|
|
431
|
+
diffuseColor.a *= cover;`,
|
|
432
|
+
depthAlpha: /* glsl */ `diffuseColor.a *= netCover(vUv);`,
|
|
433
|
+
rough: '0.9',
|
|
434
|
+
});
|
|
435
|
+
pair.material.userData.net = true;
|
|
436
|
+
return pair;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* A net surface spanned between two curves A(s) and B(s), s in 0..1 (both [x, y, z] in metres): the
|
|
440
|
+
* vertex at (s, t) is A(s)·(1−t) + B(s)·t. uv in metres (arc length along the mid curve, distance
|
|
441
|
+
* across) so the mesh cells stay square on any trapezoid or curved strip. sag(s) → [x, y, z]: the
|
|
442
|
+
* direction × metres this vertex bellies at netTension 0.
|
|
443
|
+
*/
|
|
444
|
+
export function netRuled(A, B, nS, nT, sag) {
|
|
445
|
+
const pos = [], uv = [], at = [], sg = [], idx = [];
|
|
446
|
+
// arc length of the mid curve for u
|
|
447
|
+
const mids = [], acc = [0];
|
|
448
|
+
for (let i = 0; i <= nS; i++) {
|
|
449
|
+
const s = i / nS, a = A(s), b = B(s);
|
|
450
|
+
mids.push([(a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2]);
|
|
451
|
+
}
|
|
452
|
+
for (let i = 1; i <= nS; i++)
|
|
453
|
+
acc.push(acc[i - 1] + Math.hypot(mids[i][0] - mids[i - 1][0], mids[i][1] - mids[i - 1][1], mids[i][2] - mids[i - 1][2]));
|
|
454
|
+
for (let i = 0; i <= nS; i++) {
|
|
455
|
+
const s = i / nS, a = A(s), b = B(s), g = sag(s);
|
|
456
|
+
const span = Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
|
|
457
|
+
for (let j = 0; j <= nT; j++) {
|
|
458
|
+
const t = j / nT;
|
|
459
|
+
pos.push(a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t);
|
|
460
|
+
uv.push(acc[i], span * t);
|
|
461
|
+
at.push(s, t);
|
|
462
|
+
sg.push(g[0], g[1], g[2]);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
for (let i = 0; i < nS; i++)
|
|
466
|
+
for (let j = 0; j < nT; j++) {
|
|
467
|
+
const k = i * (nT + 1) + j;
|
|
468
|
+
idx.push(k, k + nT + 1, k + 1, k + 1, k + nT + 1, k + nT + 2);
|
|
469
|
+
}
|
|
470
|
+
const g = new THREE.BufferGeometry();
|
|
471
|
+
g.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3));
|
|
472
|
+
g.setAttribute('uv', new THREE.Float32BufferAttribute(uv, 2));
|
|
473
|
+
g.setAttribute('aT', new THREE.Float32BufferAttribute(at, 2));
|
|
474
|
+
g.setAttribute('aSag', new THREE.Float32BufferAttribute(sg, 3));
|
|
475
|
+
g.setIndex(idx);
|
|
476
|
+
g.computeVertexNormals();
|
|
477
|
+
g.computeBoundingSphere();
|
|
478
|
+
g.computeBoundingBox();
|
|
479
|
+
return g;
|
|
480
|
+
}
|
|
481
|
+
const line3 = (p, q) => (s) => [p[0] + (q[0] - p[0]) * s, p[1] + (q[1] - p[1]) * s, p[2] + (q[2] - p[2]) * s];
|
|
482
|
+
/** One net mesh = one net material (each net has its own sag share and mode). */
|
|
483
|
+
export function netMesh(geo, pair) {
|
|
484
|
+
const m = new THREE.Mesh(geo, pair.material);
|
|
485
|
+
m.customDepthMaterial = pair.depth;
|
|
486
|
+
m.castShadow = true;
|
|
487
|
+
m.receiveShadow = false;
|
|
488
|
+
return m;
|
|
489
|
+
}
|
|
490
|
+
// ---------------------------------------------------------------------------- goals (shared by `goal` and a court's fixtures)
|
|
491
|
+
export const GOAL_KINDS = ['footballGoal', 'futsalGoal', 'handballGoal', 'hockeyNet', 'basketballHoop', 'volleyballNet', 'tennisNet', 'badmintonNet', 'rugbyPosts', 'americanUprights'];
|
|
492
|
+
/** Natural colours of every goal / net kind, [posts / frame, mesh, tapes / pads / board frame], and its default mesh size (m). */
|
|
493
|
+
export const GOAL_COLORS = {
|
|
494
|
+
footballGoal: { colors: ['#f4f4f2', '#f2f2f2', '#f4f4f2'], mesh: 0.12 },
|
|
495
|
+
futsalGoal: { colors: ['#f4f4f2', '#f2f2f2', '#f4f4f2'], mesh: 0.1 },
|
|
496
|
+
handballGoal: { colors: ['#f4f4f2', '#f2f2f2', '#d13b2a'], mesh: 0.1 },
|
|
497
|
+
hockeyNet: { colors: ['#d12f2f', '#f2f2f2', '#f2f2f2'], mesh: 0.045 },
|
|
498
|
+
basketballHoop: { colors: ['#b9bcc0', '#f2f2f2', '#f2f2f2'], mesh: 0.06 },
|
|
499
|
+
volleyballNet: { colors: ['#b9bcc0', '#1a1a1a', '#f2f2f2'], mesh: 0.1 },
|
|
500
|
+
tennisNet: { colors: ['#2a2a2a', '#1a1a1a', '#f2f2f2'], mesh: 0.045 },
|
|
501
|
+
badmintonNet: { colors: ['#2a2a2a', '#4a2a1e', '#f2f2f2'], mesh: 0.019 },
|
|
502
|
+
pickleballNet: { colors: ['#2a2a2a', '#1a1a1a', '#f2f2f2'], mesh: 0.04 },
|
|
503
|
+
padelNet: { colors: ['#2a2a2a', '#1a1a1a', '#f2f2f2'], mesh: 0.045 },
|
|
504
|
+
tableTennisNet: { colors: ['#2a2a2a', '#1f3f86', '#f2f2f2'], mesh: 0.012 },
|
|
505
|
+
rugbyPosts: { colors: ['#f4f4f2', '#f2f2f2', '#1d4f9c'], mesh: 0.1 },
|
|
506
|
+
americanUprights: { colors: ['#f2c230', '#f2f2f2', '#1d4f9c'], mesh: 0.1 },
|
|
507
|
+
};
|
|
508
|
+
/**
|
|
509
|
+
* Builds a goal / net / hoop at the origin (the goal line, the net's centre, the hoop's base on the
|
|
510
|
+
* ground), facing +z (the play side). Returns { object, mats, nets, apply(cfg), dispose }.
|
|
511
|
+
* Dimensions are the rulebook ones. Materials: posts = slot 0 (color), nets = slot 1, pads / boards /
|
|
512
|
+
* bands = slot 2.
|
|
513
|
+
*/
|
|
514
|
+
export function buildGoal(kind, u, o = undefined) {
|
|
515
|
+
const opt = Object.assign({ mesh: undefined, net: true, quality: 1, sag: 0.3 }, o);
|
|
516
|
+
const object = new THREE.Group();
|
|
517
|
+
const mats = [], nets = [], geos = [];
|
|
518
|
+
const paint = (slot, kindName = undefined, extra = undefined) => { const p = partMaterial(u, Object.assign({ kind: kindName || 'paint', slot, tint: '#ffffff', roughness: 0.35 }, extra)); mats.push(p); return p; };
|
|
519
|
+
const addMesh = (geo, pair, name = '') => { geos.push(geo); const m = propMesh(geo, pair, name); object.add(m); return m; };
|
|
520
|
+
// np: { mesh, thread, k (this net's share of the sag at netTension 0) }; mode 0 hanging (bottom free), 1 held on all edges
|
|
521
|
+
const addNet = (geo, np, mode = undefined) => {
|
|
522
|
+
if (!opt.net) {
|
|
523
|
+
geo.dispose();
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
const k = np.k ?? 1;
|
|
527
|
+
const pair = netMaterial(u, { mesh: np.mesh, thread: np.thread, sag: opt.sag * k, sagMode: mode ?? 1, slot: 1 });
|
|
528
|
+
mats.push(pair);
|
|
529
|
+
geos.push(geo);
|
|
530
|
+
const m = netMesh(geo, pair);
|
|
531
|
+
m.userData.sagK = k;
|
|
532
|
+
object.add(m);
|
|
533
|
+
nets.push(m);
|
|
534
|
+
return m;
|
|
535
|
+
};
|
|
536
|
+
const R = 10 + Math.round(6 * opt.quality);
|
|
537
|
+
const nq = (len) => Math.max(4, Math.round(len * 4 * opt.quality)); // net segments for a length
|
|
538
|
+
/**
|
|
539
|
+
* The nets of a box goal (football, futsal, handball), spanned between the frame's own bars:
|
|
540
|
+
* posts (±W/2, 0..H, 0), crossbar at H, ground bars to the back bar (x, 0, −D), top-rear bar at
|
|
541
|
+
* (x, Hb, −D), stanchions from the crossbar corners down to it. Back and roof belly out / down,
|
|
542
|
+
* the sides belly outward; every edge sits on a bar.
|
|
543
|
+
*/
|
|
544
|
+
const boxGoalNets = (W, H, D, Hb, np) => {
|
|
545
|
+
const x0 = -W / 2, x1 = W / 2;
|
|
546
|
+
addNet(netRuled(line3([x0, 0, -D], [x1, 0, -D]), line3([x0, Hb, -D], [x1, Hb, -D]), nq(W), nq(Hb), () => [0, 0, -0.35]), np, 1); // back
|
|
547
|
+
addNet(netRuled(line3([x0, H, 0], [x1, H, 0]), line3([x0, Hb, -D], [x1, Hb, -D]), nq(W), nq(D), () => [0, -0.25, 0]), np, 1); // roof
|
|
548
|
+
for (const x of [x0, x1]) {
|
|
549
|
+
const out = Math.sign(x) * 0.2;
|
|
550
|
+
addNet(netRuled(line3([x, 0, 0], [x, 0, -D]), line3([x, H, 0], [x, Hb, -D]), nq(D), nq(H), () => [out, 0, 0]), np, 1); // side
|
|
551
|
+
}
|
|
552
|
+
};
|
|
553
|
+
const boxGoalFrame = (W, H, D, Hb, r) => {
|
|
554
|
+
const rb = r * 0.5;
|
|
555
|
+
return [
|
|
556
|
+
bar([-W / 2, rb, 0], [-W / 2, rb, -D], rb, 8), bar([W / 2, rb, 0], [W / 2, rb, -D], rb, 8), bar([-W / 2, rb, -D], [W / 2, rb, -D], rb, 8),
|
|
557
|
+
bar([-W / 2, Hb, -D], [W / 2, Hb, -D], rb, 8), bar([-W / 2, rb, -D], [-W / 2, Hb, -D], rb, 8), bar([W / 2, rb, -D], [W / 2, Hb, -D], rb, 8),
|
|
558
|
+
bar([-W / 2, H, 0], [-W / 2, Hb, -D], rb, 8), bar([W / 2, H, 0], [W / 2, Hb, -D], rb, 8),
|
|
559
|
+
];
|
|
560
|
+
};
|
|
561
|
+
const frameGoal = (W, H, D, r, postPair, meshSize) => {
|
|
562
|
+
const Hb = H * 0.8;
|
|
563
|
+
const parts = [bar([-W / 2, 0, 0], [-W / 2, H + r, 0], r, R), bar([W / 2, 0, 0], [W / 2, H + r, 0], r, R), bar([-W / 2 - r, H + r, 0], [W / 2 + r, H + r, 0], r, R)];
|
|
564
|
+
addMesh(mergeGeos(parts.concat(boxGoalFrame(W, H, D, Hb, r))), postPair, 'frame');
|
|
565
|
+
boxGoalNets(W, H, D, Hb, { mesh: meshSize, thread: Math.min(0.005, meshSize * 0.045) });
|
|
566
|
+
};
|
|
567
|
+
/**
|
|
568
|
+
* A net across a court, along x at z = 0 (the court builder turns it across the court). n:
|
|
569
|
+
* posts distance between the posts (m) netW net length (≤ posts; cables run on to the posts)
|
|
570
|
+
* top height of the top of the net at the posts dip how much lower it is at the centre (tennis 1.07 → 0.914)
|
|
571
|
+
* depth net depth (top to bottom edge) grounded the bottom edge rests on the ground (tennis, pickleball, padel)
|
|
572
|
+
* band top tape height strap centre strap (tennis, pickleball, padel)
|
|
573
|
+
* antennae volleyball antennae + side bands at ±antennae
|
|
574
|
+
* The top edge, its tape and the mesh follow the dip as a parabola between the posts.
|
|
575
|
+
*/
|
|
576
|
+
const netOnPosts = (n, meshSize, postPair, bandPair) => {
|
|
577
|
+
const L = n.posts, W = n.netW ?? n.posts, Htop = n.top, dip = n.dip ?? 0, D = n.depth ?? n.top, bandH = n.band, postR = n.postR;
|
|
578
|
+
const topAt = (x) => Htop - dip * (1 - (2 * x / W) ** 2); // x in [-W/2, W/2]
|
|
579
|
+
const bottomAt = (x) => (n.grounded ? 0 : topAt(x) - D);
|
|
580
|
+
addMesh(mergeGeos([bar([-L / 2, 0, 0], [-L / 2, Htop + 0.03, 0], postR, R), bar([L / 2, 0, 0], [L / 2, Htop + 0.03, 0], postR, R)]), postPair, 'posts');
|
|
581
|
+
// cables from the net's ends to the posts (only when the net is shorter than the post span)
|
|
582
|
+
const cables = [];
|
|
583
|
+
if (L - W > 0.05)
|
|
584
|
+
for (const sx of [-1, 1]) {
|
|
585
|
+
cables.push(bar([sx * W / 2, Htop - bandH / 2, 0], [sx * L / 2, Htop - bandH / 2, 0], 0.003, 6));
|
|
586
|
+
if (!n.grounded)
|
|
587
|
+
cables.push(bar([sx * W / 2, Htop - D + 0.02, 0], [sx * L / 2, Htop - D + 0.02, 0], 0.003, 6));
|
|
588
|
+
}
|
|
589
|
+
if (cables.length)
|
|
590
|
+
addMesh(mergeGeos(cables), postPair, 'cables');
|
|
591
|
+
// tapes: the top band follows the dip (short boxes along the parabola), a bottom band on hanging nets, the centre strap
|
|
592
|
+
const tape = (yAt, h, thick) => {
|
|
593
|
+
const seg = 32, parts = [];
|
|
594
|
+
for (let i = 0; i < seg; i++) {
|
|
595
|
+
const x0 = -W / 2 + W * i / seg, x1 = -W / 2 + W * (i + 1) / seg;
|
|
596
|
+
const y0 = yAt(x0), y1 = yAt(x1), len = Math.hypot(x1 - x0, y1 - y0);
|
|
597
|
+
parts.push(box(len + 0.002, h, thick, [(x0 + x1) / 2, (y0 + y1) / 2 - h / 2, 0], [0, 0, Math.atan2(y1 - y0, x1 - x0)]));
|
|
598
|
+
}
|
|
599
|
+
return mergeGeos(parts);
|
|
600
|
+
};
|
|
601
|
+
const bands = [tape(topAt, bandH, 0.012)];
|
|
602
|
+
if (!n.grounded)
|
|
603
|
+
bands.push(tape((x) => bottomAt(x) + bandH * 0.6, bandH * 0.6, 0.01));
|
|
604
|
+
if (n.strap)
|
|
605
|
+
bands.push(box(0.05, topAt(0), 0.014, [0, topAt(0) / 2, 0]));
|
|
606
|
+
if (n.antennae)
|
|
607
|
+
for (const sx of [-1, 1])
|
|
608
|
+
bands.push(box(0.05, D, 0.013, [sx * n.antennae, Htop - D / 2, 0]));
|
|
609
|
+
addMesh(mergeGeos(bands), bandPair, 'bands');
|
|
610
|
+
if (n.antennae) {
|
|
611
|
+
// 1.8 m fibreglass rods, 0.8 m above the net, 10 cm red / white stripes
|
|
612
|
+
const red = [], white = [];
|
|
613
|
+
for (const sx of [-1, 1])
|
|
614
|
+
for (let i = 0; i < 18; i++) {
|
|
615
|
+
const y0 = Htop - D + i * 0.1, y1 = y0 + 0.1;
|
|
616
|
+
(i % 2 ? white : red).push(bar([sx * (n.antennae + 0.03), y0, 0], [sx * (n.antennae + 0.03), y1, 0], 0.005, 8));
|
|
617
|
+
}
|
|
618
|
+
addMesh(mergeGeos(red), paint(3, 'plastic', { tint: '#d13b2a', roughness: 0.4 }), 'antennaRed');
|
|
619
|
+
addMesh(mergeGeos(white), paint(3, 'plastic', { tint: '#f2f2f2', roughness: 0.4 }), 'antennaWhite');
|
|
620
|
+
}
|
|
621
|
+
if (opt.net) {
|
|
622
|
+
// the mesh: from the bottom edge to just under the top tape, along x at z = 0; the top follows
|
|
623
|
+
// the dip (grounded: stretched, the bottom stays on the ground; hanging: shifted down)
|
|
624
|
+
const h0 = (n.grounded ? Htop : D) - bandH * 0.5;
|
|
625
|
+
const y0 = n.grounded ? 0 : Htop - D;
|
|
626
|
+
const drop = (x) => dip * (1 - (2 * x / W) ** 2);
|
|
627
|
+
const A = (s) => { const x = -W / 2 + W * s; return [x, n.grounded ? 0 : y0 - drop(x), 0]; };
|
|
628
|
+
const B = (s) => { const x = -W / 2 + W * s; return [x, (n.grounded ? h0 : y0 + h0) - drop(x), 0]; };
|
|
629
|
+
addNet(netRuled(A, B, nq(W), nq(h0), () => [0, 0, -0.08 * Math.min(h0, 1)]), { mesh: meshSize, thread: n.thread ?? Math.min(0.0035, meshSize * 0.07), k: 0.6 }, 1);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
if (kind === 'footballGoal')
|
|
633
|
+
frameGoal(7.32, 2.44, 2.0, 0.06, paint(0, 'paint', { tint: '#ffffff', roughness: 0.3 }), opt.mesh ?? 0.12);
|
|
634
|
+
else if (kind === 'futsalGoal')
|
|
635
|
+
frameGoal(3.0, 2.0, 1.0, 0.04, paint(0, 'paint', { tint: '#ffffff', roughness: 0.3 }), opt.mesh ?? 0.1);
|
|
636
|
+
else if (kind === 'handballGoal') {
|
|
637
|
+
// 3 × 2 m, posts and crossbar painted in 0.2 m bands of two colours (two meshes)
|
|
638
|
+
const W = 3, H = 2, D = 1.0, r = 0.04;
|
|
639
|
+
const p0 = paint(0, 'paint', { tint: '#ffffff' }), p1 = paint(2, 'paint', { tint: '#ffffff' });
|
|
640
|
+
const segsA = [], segsB = [];
|
|
641
|
+
const stripe = (a, b) => { const len = Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]); const n = Math.max(1, Math.round(len / 0.2)); for (let i = 0; i < n; i++) {
|
|
642
|
+
const t0 = i / n, t1 = (i + 1) / n;
|
|
643
|
+
const pa = a.map((v, k) => v + (b[k] - v) * t0), pb = a.map((v, k) => v + (b[k] - v) * t1);
|
|
644
|
+
(i % 2 ? segsB : segsA).push(bar(pa, pb, r, R));
|
|
645
|
+
} };
|
|
646
|
+
stripe([-W / 2, 0, 0], [-W / 2, H + r, 0]);
|
|
647
|
+
stripe([W / 2, 0, 0], [W / 2, H + r, 0]);
|
|
648
|
+
stripe([-W / 2 - r, H + r, 0], [W / 2 + r, H + r, 0]);
|
|
649
|
+
addMesh(mergeGeos(segsA), p0, 'frameA');
|
|
650
|
+
addMesh(mergeGeos(segsB), p1, 'frameB');
|
|
651
|
+
addMesh(mergeGeos(boxGoalFrame(W, H, D, H * 0.8, r)), p0, 'back');
|
|
652
|
+
boxGoalNets(W, H, D, H * 0.8, { mesh: opt.mesh ?? 0.1, thread: 0.0045 });
|
|
653
|
+
}
|
|
654
|
+
else if (kind === 'hockeyNet') {
|
|
655
|
+
// NHL: 1.83 wide, 1.22 high, 1.12 deep. Posts and crossbar at the goal line; a base frame on the ice
|
|
656
|
+
// curving back to its deepest point; an upper frame at 0.55 H over the same curve; the net spans
|
|
657
|
+
// crossbar → upper frame (roof), upper frame → base (back) and post → frame (the two sides).
|
|
658
|
+
const W = 1.83, H = 1.22, D = 1.12, r = 0.025, Hu = H * 0.55;
|
|
659
|
+
const zb = (s) => -D * (0.55 + 0.45 * Math.sin(Math.PI * s)); // depth of the curved back at s
|
|
660
|
+
const base = (s) => [-W / 2 + W * s, r, zb(s)], upper = (s) => [-W / 2 + W * s, Hu, zb(s)];
|
|
661
|
+
const curvePts = (f) => { const p = []; for (let i = 0; i <= 16; i++)
|
|
662
|
+
p.push(f(i / 16)); return p; };
|
|
663
|
+
const red = paint(0, 'paint', { tint: '#ffffff', roughness: 0.3 });
|
|
664
|
+
const fr = [tube(curvePts(base), r, 32, 8), tube(curvePts(upper), r * 0.8, 32, 8),
|
|
665
|
+
bar([-W / 2, 0, 0], [-W / 2, H, 0], r, R), bar([W / 2, 0, 0], [W / 2, H, 0], r, R), bar([-W / 2 - r, H, 0], [W / 2 + r, H, 0], r, R)];
|
|
666
|
+
for (const sx of [-1, 1]) {
|
|
667
|
+
fr.push(bar([sx * W / 2, r, 0], [sx * W / 2, r, -0.55 * D], r, 8)); // base side bar on the ice
|
|
668
|
+
fr.push(bar([sx * W / 2, r, -0.55 * D], [sx * W / 2, Hu, -0.55 * D], r * 0.8, 8)); // upright at the back corner
|
|
669
|
+
fr.push(bar([sx * W / 2, H, 0], [sx * W / 2, Hu, -0.55 * D], r * 0.8, 8)); // top side bar
|
|
670
|
+
}
|
|
671
|
+
fr.push(bar([0, H, 0], [0, Hu, -D], r * 0.6, 8));
|
|
672
|
+
addMesh(mergeGeos(fr), red, 'frame');
|
|
673
|
+
const np = { mesh: opt.mesh ?? 0.045, thread: 0.0025, k: 0.5 };
|
|
674
|
+
addNet(netRuled(base, upper, nq(W * 1.3), nq(Hu), (s) => { const x = -W / 2 + W * s, z = zb(s) + 0.3 * D, l = Math.hypot(x, z) || 1; return [0.15 * x / l, 0, 0.15 * z / l]; }), np, 1); // back, out of the goal
|
|
675
|
+
addNet(netRuled(line3([-W / 2, H, 0], [W / 2, H, 0]), upper, nq(W), nq(D * 0.8), () => [0, -0.12, 0]), np, 1); // roof
|
|
676
|
+
for (const sx of [-1, 1])
|
|
677
|
+
addNet(netRuled(line3([sx * W / 2, 0, 0], [sx * W / 2, 0, -0.55 * D]), line3([sx * W / 2, H, 0], [sx * W / 2, Hu, -0.55 * D]), nq(0.6), nq(H), () => [sx * 0.1, 0, 0]), np, 1); // sides
|
|
678
|
+
}
|
|
679
|
+
else if (kind === 'basketballHoop') {
|
|
680
|
+
// backboard 1.8 × 1.05, rim r 0.2286 at 3.05 m, 0.15 m in front of the board; pole 1.2 m behind the baseline with a padded base
|
|
681
|
+
const rimR = 0.2286, rimY = 3.05, bw = 1.8, bh = 1.05, bz = -0.15 - rimR;
|
|
682
|
+
const pole = paint(0, 'metal', { tint: '#dddddd', roughness: 0.35, metalness: 1 });
|
|
683
|
+
const pad = paint(2, 'rubber', { tint: '#ffffff', roughness: 0.9 });
|
|
684
|
+
const glass = paint(3, 'plastic', { tint: '#dfe8ee', roughness: 0.15 });
|
|
685
|
+
const rimP = paint(3, 'paint', { tint: '#e8521f', roughness: 0.4 });
|
|
686
|
+
const pz = bz - 1.2;
|
|
687
|
+
addMesh(mergeGeos([bar([0, 0, pz], [0, rimY + 0.6, pz], 0.08, R), bar([0, rimY + 0.5, pz], [0, rimY + 0.35, bz - 0.03], 0.04, 10), bar([0, rimY - 0.25, pz], [0, rimY - 0.2, bz - 0.03], 0.04, 10)]), pole, 'pole');
|
|
688
|
+
addMesh(box(0.5, 1.8, 0.5, [0, 0.9, pz]), pad, 'pad');
|
|
689
|
+
addMesh(mergeGeos([box(bw, bh, 0.02, [0, rimY + bh / 2 - 0.3, bz - 0.02])]), glass, 'board');
|
|
690
|
+
// board frame + inner rectangle (accent)
|
|
691
|
+
const frameP = paint(2, 'paint', { tint: '#ffffff', roughness: 0.4 });
|
|
692
|
+
addMesh(mergeGeos([box(bw, 0.05, 0.03, [0, rimY + bh - 0.3, bz - 0.01]), box(bw, 0.05, 0.03, [0, rimY - 0.3, bz - 0.01]), box(0.05, bh, 0.03, [-bw / 2, rimY + bh / 2 - 0.3, bz - 0.01]), box(0.05, bh, 0.03, [bw / 2, rimY + bh / 2 - 0.3, bz - 0.01]),
|
|
693
|
+
box(0.59, 0.03, 0.03, [0, rimY + 0.45, bz]), box(0.03, 0.45, 0.03, [-0.295, rimY + 0.225, bz]), box(0.03, 0.45, 0.03, [0.295, rimY + 0.225, bz])]), frameP, 'boardFrame');
|
|
694
|
+
const ring = new THREE.TorusGeometry(rimR, 0.01, 10, 48);
|
|
695
|
+
ring.rotateX(Math.PI / 2);
|
|
696
|
+
ring.translate(0, rimY, 0);
|
|
697
|
+
addMesh(mergeGeos([ring, box(0.12, 0.08, 0.15, [0, rimY - 0.03, bz + 0.07])]), rimP, 'rim');
|
|
698
|
+
if (opt.net) {
|
|
699
|
+
// the net: a cone of 12 cords, drawn as a lathe with the net shader in polar coordinates
|
|
700
|
+
const np = netMaterial(u, { mesh: opt.mesh ?? 0.06, thread: 0.004, sag: 0, slot: 1 });
|
|
701
|
+
mats.push(np);
|
|
702
|
+
const g = lathe([[rimR * 0.62, rimY - 0.42], [rimR * 0.78, rimY - 0.3], [rimR * 0.95, rimY - 0.12], [rimR, rimY - 0.01]], 36);
|
|
703
|
+
const uv = g.attributes.uv;
|
|
704
|
+
for (let i = 0; i < uv.count; i++)
|
|
705
|
+
uv.setXY(i, uv.getX(i) * rimR * 6.2832, uv.getY(i) * 0.42);
|
|
706
|
+
g.setAttribute('aT', new THREE.Float32BufferAttribute(new Float32Array(uv.count * 2), 2));
|
|
707
|
+
g.setAttribute('aSag', new THREE.Float32BufferAttribute(new Float32Array(uv.count * 3), 3));
|
|
708
|
+
geos.push(g);
|
|
709
|
+
const m = netMesh(g, np);
|
|
710
|
+
m.userData.sagK = 0;
|
|
711
|
+
object.add(m);
|
|
712
|
+
nets.push(m);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
// posts: slot 0, mesh: slot 1, tapes: slot 2 — natural colours in GOAL_COLORS
|
|
716
|
+
else if (kind === 'volleyballNet')
|
|
717
|
+
netOnPosts({ posts: 11.0, netW: 10.0, top: 2.43, depth: 1.0, band: 0.07, postR: 0.05, antennae: 4.5 }, opt.mesh ?? 0.1, paint(0, 'metal', { tint: '#ffffff', roughness: 0.4, metalness: 1 }), paint(2, 'fabric', { tint: '#ffffff' }));
|
|
718
|
+
else if (kind === 'tennisNet')
|
|
719
|
+
netOnPosts({ posts: 12.8, top: 1.07, dip: 0.156, band: 0.06, postR: 0.04, grounded: true, strap: true, thread: 0.003 }, opt.mesh ?? 0.045, paint(0, 'metal', { tint: '#ffffff', roughness: 0.5, metalness: 0.6 }), paint(2, 'fabric', { tint: '#ffffff' }));
|
|
720
|
+
else if (kind === 'badmintonNet')
|
|
721
|
+
netOnPosts({ posts: 6.1, top: 1.55, dip: 0.026, depth: 0.76, band: 0.075, postR: 0.02 }, opt.mesh ?? 0.019, paint(0, 'metal', { tint: '#ffffff', roughness: 0.5, metalness: 0.6 }), paint(2, 'fabric', { tint: '#ffffff' }));
|
|
722
|
+
else if (kind === 'pickleballNet')
|
|
723
|
+
netOnPosts({ posts: 6.71, top: 0.914, dip: 0.05, band: 0.05, postR: 0.03, grounded: true, strap: true }, opt.mesh ?? 0.04, paint(0, 'metal', { tint: '#ffffff', roughness: 0.5, metalness: 0.6 }), paint(2, 'fabric', { tint: '#ffffff' }));
|
|
724
|
+
else if (kind === 'padelNet')
|
|
725
|
+
netOnPosts({ posts: 10.0, top: 0.92, dip: 0.04, band: 0.06, postR: 0.04, grounded: true, strap: true }, opt.mesh ?? 0.045, paint(0, 'metal', { tint: '#ffffff', roughness: 0.5, metalness: 0.6 }), paint(2, 'fabric', { tint: '#ffffff' }));
|
|
726
|
+
else if (kind === 'tableTennisNet')
|
|
727
|
+
netOnPosts({ posts: 1.83, top: 0.1525, band: 0.015, postR: 0.008, grounded: true, thread: 0.0012 }, opt.mesh ?? 0.012, paint(0, 'metal', { tint: '#ffffff', roughness: 0.5, metalness: 0.6 }), paint(2, 'fabric', { tint: '#ffffff' }));
|
|
728
|
+
else if (kind === 'rugbyPosts' || kind === 'americanUprights') {
|
|
729
|
+
const american = kind === 'americanUprights';
|
|
730
|
+
const W = american ? 5.64 : 5.6, cb = american ? 3.05 : 3.0, top = american ? 10.7 : 11.0, r = american ? 0.06 : 0.07;
|
|
731
|
+
const p = paint(0, 'paint', { tint: '#ffffff', roughness: 0.35 });
|
|
732
|
+
const parts = [];
|
|
733
|
+
if (american) {
|
|
734
|
+
// gooseneck: one pole 1.8 m behind, curving up to the crossbar
|
|
735
|
+
parts.push(bar([0, 0, -1.8], [0, cb - 1.0, -1.8], 0.12, R), tube([[0, cb - 1.0, -1.8], [0, cb - 0.3, -1.5], [0, cb, -0.6], [0, cb, 0]], 0.1, 16, 10));
|
|
736
|
+
parts.push(bar([-W / 2, cb, 0], [W / 2, cb, 0], r, R), bar([-W / 2, cb, 0], [-W / 2, top, 0], r * 0.8, R), bar([W / 2, cb, 0], [W / 2, top, 0], r * 0.8, R));
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
parts.push(bar([-W / 2, 0, 0], [-W / 2, top, 0], r, R), bar([W / 2, 0, 0], [W / 2, top, 0], r, R), bar([-W / 2, cb, 0], [W / 2, cb, 0], r * 0.9, R));
|
|
740
|
+
}
|
|
741
|
+
addMesh(mergeGeos(parts), p, 'posts');
|
|
742
|
+
const padP = paint(2, 'rubber', { tint: '#ffffff', roughness: 0.9 });
|
|
743
|
+
if (american)
|
|
744
|
+
addMesh(box(0.5, 1.9, 0.5, [0, 0.95, -1.8]), padP, 'pad');
|
|
745
|
+
else
|
|
746
|
+
addMesh(mergeGeos([bar([-W / 2, 0, 0], [-W / 2, 1.9, 0], 0.16, 12), bar([W / 2, 0, 0], [W / 2, 1.9, 0], 0.16, 12)]), padP, 'pads');
|
|
747
|
+
}
|
|
748
|
+
const apply = (cfg) => {
|
|
749
|
+
for (const m of nets) {
|
|
750
|
+
const uu = m.material.userData.uniforms;
|
|
751
|
+
if (!uu)
|
|
752
|
+
continue;
|
|
753
|
+
if (cfg.netTension !== undefined)
|
|
754
|
+
uu.uSag.value = (1 - cfg.netTension) * (m.userData.sagK ?? 1);
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
return { object, mats, nets, apply, dispose: () => { geos.forEach((g) => g.dispose()); mats.forEach(disposePair); } };
|
|
758
|
+
}
|