@3driseai/3drise-viewer 0.1.42 → 0.1.44
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/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 +6 -4
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
// Emitted from Claude outputs/vegetation-lab/produce.mjs by gen_family.mjs — edit the lab module and re-emit.
|
|
2
|
+
// Produce — shared engine of the fruit and vegetable objects (lab). Inlined after common.mjs.
|
|
3
|
+
//
|
|
4
|
+
// Every kind is one profile: a lathe radius function ρ(t) along the height (t = 0 bottom .. 1 top),
|
|
5
|
+
// a cross-section modulation (lobes / ribs: cos(nθ)), per-kind displacement (dimples, kernels,
|
|
6
|
+
// bumps, warts) from seeded value noise, an elongation, a stem and a leaf / calyx, and a skin:
|
|
7
|
+
// two colours by ripeness, a pattern (stripes, streaks, pores, netting), gloss and translucency.
|
|
8
|
+
// The mesh is a parametric surface (u around, v along) built on the CPU with finite-difference
|
|
9
|
+
// normals, once per kind; a pile is instanced copies resting on the ground.
|
|
10
|
+
//
|
|
11
|
+
// Compound kinds build on the same primitive: grapes = many small spheroids on a cone, broccoli =
|
|
12
|
+
// a stalk + a dome of florets, pineapple = ovoid lattice + crown blades, corn = cob + husk blades,
|
|
13
|
+
// banana = a curved tube (a bent lathe).
|
|
14
|
+
import * as THREE from 'three';
|
|
15
|
+
import { mulberry32, NOISE_GLSL_V, VEG_UNIFORMS_GLSL, WIND_GLSL, LEAF_SDF_GLSL, LEAF_TYPES, leafGeometry, setLeaf, unitBounds } from './vegetationCore.js';
|
|
16
|
+
const TAU = Math.PI * 2;
|
|
17
|
+
// ρ(t) helpers
|
|
18
|
+
const ell = (t, p) => Math.pow(Math.max(0, Math.sin(Math.PI * t)), p || 1);
|
|
19
|
+
export const PRODUCE = {
|
|
20
|
+
// ------------------------------------------------------------------ fruit
|
|
21
|
+
apple: { h: 0.9, rho: (t) => 0.5 * ell(t, 0.62) * (1 - 0.12 * t), lobes: [5, 0.015], dimple: [0.12, 0.18], unripe: '#8fbf3f', ripe: '#c9302b', pattern: 'streaks', gloss: 0.55, sss: 0.3, stem: [0.1, 0.012, '#4a3a2a'], leaf: 1 },
|
|
22
|
+
orange: { h: 0.95, rho: (t) => 0.5 * ell(t, 0.58), lobes: [0, 0], dimple: [0.04, 0.06], bumps: [9, 0.01], unripe: '#7faf3a', ripe: '#f08a1f', pattern: 'pores', gloss: 0.3, sss: 0.15, stem: [0.03, 0.01, '#4a4a2a'], leaf: 0 },
|
|
23
|
+
lemon: { h: 1.0, rho: (t) => 0.42 * ell(t, 0.7) + 0.05 * Math.max(0, 1 - Math.abs(t - 0.5) * 3), lobes: [0, 0], dimple: [-0.08, -0.1], bumps: [8, 0.012], unripe: '#8fbf3f', ripe: '#f4d82a', pattern: 'pores', gloss: 0.4, sss: 0.2, stem: [0.03, 0.01, '#4a4a2a'], leaf: 0 },
|
|
24
|
+
pear: { h: 1.0, rho: (t) => 0.42 * (ell(t, 0.5) * (1 - 0.55 * Math.pow(t, 1.5)) + 0.12 * t * (1 - t)), lobes: [5, 0.01], dimple: [0.06, 0.1], unripe: '#9cc44a', ripe: '#d1c243', pattern: 'speckle', gloss: 0.25, sss: 0.2, stem: [0.14, 0.012, '#5a4a3a'], leaf: 0 },
|
|
25
|
+
banana: { h: 1.0, rho: (t) => 0.14 * ell(t, 0.3) * (1 - 0.3 * t), lobes: [5, 0.12], dimple: [0, 0], bend: 0.9, unripe: '#8fbf3f', ripe: '#f2d53a', pattern: 'banana', gloss: 0.35, sss: 0.1, stem: [0.08, 0.03, '#5a4a2a'], leaf: 0, lie: true },
|
|
26
|
+
cherry: { h: 0.9, rho: (t) => 0.5 * ell(t, 0.6), lobes: [2, 0.02], dimple: [0.1, 0.14], unripe: '#e0c050', ripe: '#8a1324', pattern: 'none', gloss: 0.95, sss: 0.35, stem: [0.9, 0.012, '#4a7a2a'], leaf: 0, small: 0.35, pairs: true },
|
|
27
|
+
strawberry: { h: 1.0, rho: (t) => 0.46 * Math.pow(ell(t, 0.5), 0.8) * (1 - 0.75 * Math.pow(t, 1.3)), lobes: [0, 0], dimple: [0, 0], seeds: [34, 0.018], unripe: '#dfe09a', ripe: '#d61f2c', pattern: 'seeds', gloss: 0.65, sss: 0.3, stem: [0.05, 0.015, '#3f7a2a'], leaf: 0, calyx: 7, small: 0.45, invert: true },
|
|
28
|
+
grapes: { h: 1.0, rho: (t) => 0.3, cluster: { n: 56, rad: 0.11, cone: 0.5 }, unripe: '#9fcf5a', ripe: '#4a2c66', pattern: 'bloom', gloss: 0.6, sss: 0.4, stem: [0.2, 0.012, '#6a7a3a'], leaf: 1 },
|
|
29
|
+
watermelon: { h: 0.78, rho: (t) => 0.5 * ell(t, 0.5), lobes: [0, 0], dimple: [0.03, 0.03], unripe: '#7a9a4a', ripe: '#2f6a2f', pattern: 'stripes', gloss: 0.3, sss: 0, stem: [0.08, 0.02, '#6a6a4a'], leaf: 0, big: 1.6 },
|
|
30
|
+
pineapple: { h: 1.0, rho: (t) => 0.4 * ell(t, 0.45) * (1 - 0.15 * t), lobes: [0, 0], dimple: [0, 0], lattice: [10, 0.04], unripe: '#7a9a3a', ripe: '#d9a53a', pattern: 'lattice', gloss: 0.3, sss: 0.1, stem: [0, 0, '#000'], leaf: 0, crown: 14, big: 1.1 },
|
|
31
|
+
// ---- more fruit
|
|
32
|
+
peach: { h: 0.9, rho: (t) => 0.5 * ell(t, 0.6), lobes: [0, 0], dimple: [0.1, 0.12], cleft: 0.08, unripe: '#c9c07a', ripe: '#f0a060', pattern: 'blush', gloss: 0.05, sss: 0.4, fuzz: 1, stem: [0.03, 0.012, '#5a4a3a'], leaf: 1 },
|
|
33
|
+
apricot: { h: 0.85, rho: (t) => 0.5 * ell(t, 0.6), lobes: [0, 0], dimple: [0.08, 0.1], cleft: 0.1, unripe: '#c9c070', ripe: '#f2a23a', pattern: 'blush', gloss: 0.1, sss: 0.4, fuzz: 0.6, stem: [0.03, 0.01, '#5a4a3a'], leaf: 0, small: 0.6 },
|
|
34
|
+
plum: { h: 0.95, rho: (t) => 0.48 * ell(t, 0.6), lobes: [0, 0], dimple: [0.08, 0.1], cleft: 0.06, unripe: '#8aa34a', ripe: '#4a1d4f', pattern: 'bloom', gloss: 0.7, sss: 0.3, stem: [0.1, 0.01, '#5a4a3a'], leaf: 0, small: 0.6 },
|
|
35
|
+
mango: { h: 1.0, rho: (t) => 0.4 * (ell(t, 0.5) * (1 - 0.35 * t) + 0.05 * (1 - t)), lobes: [2, 0.08], dimple: [0.02, 0.04], bend: 0.35, unripe: '#6fa33a', ripe: '#f0b830', pattern: 'blush', gloss: 0.5, sss: 0.3, stem: [0.04, 0.012, '#5a4a3a'], leaf: 0, lie: true },
|
|
36
|
+
kiwi: { h: 1.0, rho: (t) => 0.38 * ell(t, 0.55), lobes: [0, 0], dimple: [0, 0], unripe: '#9a8a5a', ripe: '#8a6a3a', pattern: 'speckle', gloss: 0.0, sss: 0.1, fuzz: 1, stem: [0, 0, '#000'], leaf: 0, small: 0.6, lie: true },
|
|
37
|
+
lime: { h: 0.9, rho: (t) => 0.46 * ell(t, 0.62), lobes: [0, 0], dimple: [-0.03, -0.04], bumps: [8, 0.01], unripe: '#4e9a2f', ripe: '#6fbf2f', pattern: 'pores', gloss: 0.45, sss: 0.2, stem: [0.02, 0.008, '#4a4a2a'], leaf: 0, small: 0.6 },
|
|
38
|
+
grapefruit: { h: 0.9, rho: (t) => 0.5 * ell(t, 0.6), lobes: [0, 0], dimple: [0.05, 0.06], bumps: [8, 0.008], unripe: '#9fbf4a', ripe: '#f2c24a', pattern: 'pores', gloss: 0.3, sss: 0.15, stem: [0.02, 0.01, '#4a4a2a'], leaf: 0, big: 1.3 },
|
|
39
|
+
pomegranate: { h: 1.0, rho: (t) => 0.48 * ell(t, 0.55) * (1 - 0.1 * t), lobes: [6, 0.03], dimple: [0.0, 0.08], unripe: '#c9a060', ripe: '#b8242a', pattern: 'speckle', gloss: 0.4, sss: 0.1, stem: [0, 0, '#000'], leaf: 0, calyx: 6, calyxUp: 1 },
|
|
40
|
+
fig: { h: 1.0, rho: (t) => 0.42 * (ell(t, 0.5) * (1 - 0.6 * Math.pow(t, 1.3)) + 0.06 * t * (1 - t)), lobes: [5, 0.02], dimple: [0, 0.06], unripe: '#7fa34a', ripe: '#4a2a5a', pattern: 'streaks', gloss: 0.3, sss: 0.3, stem: [0.1, 0.012, '#5a4a3a'], leaf: 0, small: 0.6 },
|
|
41
|
+
avocado: { h: 1.0, rho: (t) => 0.4 * (ell(t, 0.5) * (1 - 0.5 * Math.pow(t, 1.5)) + 0.08 * t * (1 - t)), lobes: [0, 0], dimple: [0, 0], bumps: [12, 0.012], unripe: '#4e8a2a', ripe: '#2a3a1a', pattern: 'warts', gloss: 0.15, sss: 0.05, stem: [0.03, 0.01, '#4a3a2a'], leaf: 0 },
|
|
42
|
+
coconut: { h: 1.0, rho: (t) => 0.42 * ell(t, 0.5) * (1 - 0.1 * t), lobes: [3, 0.05], dimple: [0.0, 0.0], unripe: '#7a6a4a', ripe: '#5a3f22', pattern: 'fibre', gloss: 0.0, sss: 0.0, stem: [0, 0, '#000'], leaf: 0, big: 1.2 },
|
|
43
|
+
melon: { h: 0.85, rho: (t) => 0.5 * ell(t, 0.55), lobes: [10, 0.015], dimple: [0.04, 0.04], unripe: '#9aa86a', ripe: '#c9b878', pattern: 'net', gloss: 0.1, sss: 0.0, stem: [0.04, 0.02, '#8a7a5a'], leaf: 0, big: 1.4 },
|
|
44
|
+
papaya: { h: 1.0, rho: (t) => 0.3 * (ell(t, 0.4) * (1 - 0.3 * t) + 0.05), lobes: [5, 0.04], dimple: [0, 0.03], unripe: '#5f9a3a', ripe: '#f0a03a', pattern: 'streaks', gloss: 0.4, sss: 0.2, stem: [0.03, 0.02, '#6a6a4a'], leaf: 0, lie: true, big: 1.2 },
|
|
45
|
+
dragonfruit: { h: 1.0, rho: (t) => 0.4 * ell(t, 0.55), lobes: [0, 0], dimple: [0, 0.04], lattice: [8, 0.03], unripe: '#8ab85a', ripe: '#e8307a', pattern: 'scales', gloss: 0.5, sss: 0.2, stem: [0, 0, '#000'], leaf: 0, scaleTips: 12 },
|
|
46
|
+
persimmon: { h: 0.75, rho: (t) => 0.5 * ell(t, 0.5), lobes: [4, 0.03], dimple: [0.08, 0.1], unripe: '#9fbf4a', ripe: '#f07a2a', pattern: 'none', gloss: 0.7, sss: 0.3, stem: [0.02, 0.012, '#5a4a3a'], leaf: 0, calyx: 4, calyxFlat: 1 },
|
|
47
|
+
raspberry: { h: 1.0, rho: (t) => 0.3, cluster: { n: 42, rad: 0.11, cone: 0.32, tight: true }, unripe: '#f0d0a0', ripe: '#d8203a', pattern: 'none', gloss: 0.6, sss: 0.4, stem: [0, 0, '#000'], leaf: 0, small: 0.35 },
|
|
48
|
+
blueberry: { h: 0.8, rho: (t) => 0.48 * ell(t, 0.6), lobes: [0, 0], dimple: [0.25, 0.05], unripe: '#9fcf7a', ripe: '#3a3f7a', pattern: 'bloom', gloss: 0.5, sss: 0.2, stem: [0, 0, '#000'], leaf: 0, small: 0.3 },
|
|
49
|
+
cherryPair: { h: 0.9, rho: (t) => 0.5 * ell(t, 0.6), lobes: [2, 0.02], dimple: [0.1, 0.14], unripe: '#e0c050', ripe: '#8a1324', pattern: 'none', gloss: 0.95, sss: 0.35, stem: [0.9, 0.012, '#4a7a2a'], leaf: 0, small: 0.35, pairs: true, hidden: true },
|
|
50
|
+
zucchini: { h: 1.0, rho: (t) => 0.13 * ell(Math.min(1, 0.25 + t * 0.6), 0.3) * (1 - 0.15 * t) + 0.03 * ell(t, 0.3), lobes: [5, 0.04], dimple: [0, 0], unripe: '#4e9a3a', ripe: '#2f5a2a', pattern: 'speckle', gloss: 0.6, sss: 0.1, stem: [0.06, 0.03, '#5a7a2a'], leaf: 0, lie: true, bend: 0.1 },
|
|
51
|
+
radish: { h: 0.8, rho: (t) => 0.42 * ell(Math.min(1, t * 1.15), 0.55), lobes: [0, 0], dimple: [0, 0], unripe: '#e8a0b0', ripe: '#d8203a', pattern: 'none', gloss: 0.5, sss: 0.2, stem: [0, 0, '#000'], leaf: 0, tops: 6, roots: 1, invert: true, small: 0.5 },
|
|
52
|
+
beet: { h: 0.9, rho: (t) => 0.46 * ell(Math.min(1, t * 1.1), 0.55), lobes: [0, 0], dimple: [0, 0], rings: [10, 0.004], unripe: '#a04a5a', ripe: '#5a1a3a', pattern: 'rings', gloss: 0.3, sss: 0.1, stem: [0, 0, '#000'], leaf: 0, tops: 6, roots: 1, invert: true, small: 0.7 },
|
|
53
|
+
turnip: { h: 0.8, rho: (t) => 0.46 * ell(Math.min(1, t * 1.1), 0.55), lobes: [0, 0], dimple: [0, 0], unripe: '#e8e0d0', ripe: '#b060a0', pattern: 'turnip', gloss: 0.3, sss: 0.2, stem: [0, 0, '#000'], leaf: 0, tops: 6, roots: 1, invert: true, small: 0.7 },
|
|
54
|
+
cauliflower: { h: 1.0, rho: (t) => 0.1, florets: { n: 140, rad: 0.075, dome: 0.4 }, unripe: '#f0ecd8', ripe: '#f4f0e0', pattern: 'florets', gloss: 0.05, sss: 0.1, stem: [0.4, 0.1, '#9ab878'], leaf: 0, wrap: 8 },
|
|
55
|
+
cabbage: { h: 0.85, rho: (t) => 0.5 * ell(t, 0.55), lobes: [0, 0], dimple: [0.05, 0.05], unripe: '#8fbf6a', ripe: '#7fb05a', pattern: 'veined', gloss: 0.3, sss: 0.3, stem: [0, 0, '#000'], leaf: 0, wrap: 7 },
|
|
56
|
+
garlic: { h: 0.9, rho: (t) => 0.46 * ell(t, 0.55) * (1 - 0.4 * Math.pow(t, 3)), lobes: [7, 0.09], dimple: [0.02, 0.05], unripe: '#f0e8dc', ripe: '#e8dcc8', pattern: 'cloves', gloss: 0.2, sss: 0.4, stem: [0.2, 0.03, '#c8b890'], leaf: 0, roots: 1, small: 0.6 },
|
|
57
|
+
chili: { h: 1.0, rho: (t) => 0.1 * ell(Math.min(1, 0.15 + t * 0.9), 0.35) * (1 - 0.6 * Math.pow(t, 2)) + 0.01, lobes: [3, 0.03], dimple: [0, 0], bend: 0.5, unripe: '#4e9a2f', ripe: '#d81e1e', pattern: 'none', gloss: 0.9, sss: 0.25, stem: [0.08, 0.025, '#4f7a2a'], leaf: 0, lie: true, small: 0.6, invert: true },
|
|
58
|
+
sweetPotato: { h: 1.0, rho: (t) => 0.22 * (ell(t, 0.5) * (1 - 0.2 * t) + 0.03), lobes: [3, 0.04], dimple: [0, 0], bumps: [6, 0.03], unripe: '#c07a5a', ripe: '#a0503a', pattern: 'eyes', gloss: 0.05, sss: 0.0, stem: [0, 0, '#000'], leaf: 0, lie: true },
|
|
59
|
+
butternut: { h: 1.0, rho: (t) => 0.32 * (ell(t, 0.45) * (0.55 + 0.45 * (1 - t)) + 0.08 * t * (1 - t)), lobes: [0, 0], dimple: [0.02, 0.06], unripe: '#c9b878', ripe: '#e8b870', pattern: 'none', gloss: 0.25, sss: 0.1, stem: [0.08, 0.03, '#6a5a2a'], leaf: 0, lie: true, big: 1.2 },
|
|
60
|
+
leek: { h: 1.0, rho: (t) => 0.09 * (1 - 0.2 * t) + 0.01, lobes: [0, 0], dimple: [0, 0], unripe: '#e8f0e0', ripe: '#d8e8c8', pattern: 'leek', gloss: 0.3, sss: 0.3, stem: [0, 0, '#000'], leaf: 0, tops: 5, topsBlade: 1, roots: 1, lie: true },
|
|
61
|
+
asparagus: { h: 1.0, rho: (t) => 0.05 * (1 - 0.3 * t) + 0.01, lobes: [0, 0], dimple: [0.0, 0.0], rings: [16, 0.004], unripe: '#8fbf5a', ripe: '#5a9a3a', pattern: 'spear', gloss: 0.4, sss: 0.3, stem: [0, 0, '#000'], leaf: 0, lie: true, small: 0.5 },
|
|
62
|
+
artichoke: { h: 1.0, rho: (t) => 0.42 * ell(Math.min(1, 0.2 + t * 0.8), 0.5) * (1 - 0.25 * t), lobes: [0, 0], dimple: [0, 0], lattice: [9, 0.05], unripe: '#7fa35a', ripe: '#5f8a4a', pattern: 'lattice', gloss: 0.2, sss: 0.1, stem: [0.2, 0.04, '#7a9a5a'], leaf: 0, invert: true },
|
|
63
|
+
mushroom: { h: 1.0, rho: (t) => 0.1, mushroom: { capR: 0.5, capH: 0.3, stemH: 0.6, stemR: 0.1 }, unripe: '#e8dcc8', ripe: '#c8a070', pattern: 'cap', gloss: 0.1, sss: 0.3, stem: [0, 0, '#000'], leaf: 0 },
|
|
64
|
+
flyAgaric: { h: 1.0, rho: (t) => 0.1, mushroom: { capR: 0.5, capH: 0.35, stemH: 0.7, stemR: 0.09 }, unripe: '#f0a040', ripe: '#d8201e', pattern: 'spots', gloss: 0.6, sss: 0.2, stem: [0, 0, '#000'], leaf: 0 },
|
|
65
|
+
peaPod: { h: 1.0, rho: (t) => 0.1 * ell(Math.min(1, 0.1 + t * 0.9), 0.4) + 0.01, lobes: [2, 0.25], dimple: [0, 0], bend: 0.3, pods: 6, unripe: '#8fc85a', ripe: '#6fa83a', pattern: 'pod', gloss: 0.5, sss: 0.4, stem: [0.06, 0.015, '#5a8a3a'], leaf: 0, lie: true, small: 0.6 },
|
|
66
|
+
beans: { h: 1.0, rho: (t) => 0.07 * ell(Math.min(1, 0.1 + t * 0.9), 0.35) + 0.01, lobes: [2, 0.2], dimple: [0, 0], bend: 0.5, pods: 7, unripe: '#8fc85a', ripe: '#5a9a3a', pattern: 'pod', gloss: 0.4, sss: 0.3, stem: [0.05, 0.012, '#5a8a3a'], leaf: 0, lie: true, small: 0.6 },
|
|
67
|
+
wheatHead: { h: 1.0, rho: (t) => 0.1 * ell(Math.min(1, 0.1 + t * 0.9), 0.4), lobes: [4, 0.15], dimple: [0, 0], kernels: [6, 0.05], unripe: '#9fbf5a', ripe: '#d8b860', pattern: 'kernels', gloss: 0.2, sss: 0.3, stem: [0, 0, '#000'], leaf: 0, hidden: true, awns: 1 },
|
|
68
|
+
sunflowerHead: { h: 0.35, rho: (t) => 0.5 * ell(Math.min(1, t * 1.3), 0.35), lobes: [0, 0], dimple: [0.15, 0.0], unripe: '#6a5a2a', ripe: '#4a2a12', pattern: 'disc', gloss: 0.1, sss: 0.0, stem: [0, 0, '#000'], leaf: 0, rays: 26, hidden: true },
|
|
69
|
+
// ------------------------------------------------------------------ vegetables
|
|
70
|
+
tomato: { h: 0.82, rho: (t) => 0.5 * ell(t, 0.55), lobes: [6, 0.03], dimple: [0.1, 0.16], unripe: '#8fbf3f', ripe: '#d8321e', pattern: 'none', gloss: 0.85, sss: 0.35, stem: [0.06, 0.015, '#4f7a2a'], leaf: 0, calyx: 5 },
|
|
71
|
+
pepper: { h: 1.0, rho: (t) => 0.42 * (ell(t, 0.35) * (1 - 0.2 * t) + 0.05 * (1 - t)), lobes: [4, 0.14], dimple: [0.12, 0.08], unripe: '#4e9a2f', ripe: '#d8281c', pattern: 'none', gloss: 0.9, sss: 0.2, stem: [0.1, 0.03, '#4f7a2a'], leaf: 0, calyx: 0 },
|
|
72
|
+
pumpkin: { h: 0.7, rho: (t) => 0.5 * ell(t, 0.5), lobes: [12, 0.07], dimple: [0.18, 0.2], unripe: '#9aa84a', ripe: '#e07a1a', pattern: 'ribs', gloss: 0.3, sss: 0, stem: [0.12, 0.05, '#6a5a2a'], leaf: 0, big: 1.5 },
|
|
73
|
+
carrot: { h: 1.0, rho: (t) => 0.13 * Math.pow(1 - t, 0.6) * ell(Math.min(1, 0.5 + t * 0.5), 0.5) + 0.01, lobes: [0, 0], dimple: [0, 0], rings: [24, 0.01], unripe: '#f0a040', ripe: '#f07a1a', pattern: 'rings', gloss: 0.3, sss: 0.1, stem: [0, 0, '#000'], leaf: 0, tops: 9, invert: true, small: 0.6, lie: true },
|
|
74
|
+
corn: { h: 1.0, rho: (t) => 0.17 * ell(Math.min(1, 0.3 + t * 0.7), 0.4) * (1 - 0.25 * t), lobes: [0, 0], dimple: [0, 0], kernels: [14, 0.02], unripe: '#e8e0a0', ripe: '#f2c22a', pattern: 'kernels', gloss: 0.6, sss: 0.3, stem: [0, 0, '#000'], leaf: 0, husk: 5, lie: true },
|
|
75
|
+
cucumber: { h: 1.0, rho: (t) => 0.14 * ell(Math.min(1, Math.max(0, 0.2 + t * 0.6)), 0.3) * (1 - 0.1 * t) + 0.03 * ell(t, 0.3), lobes: [0, 0], dimple: [0, 0], bumps: [7, 0.012], unripe: '#4e9a3a', ripe: '#2f6a2f', pattern: 'warts', gloss: 0.5, sss: 0.1, stem: [0.04, 0.015, '#5a7a2a'], leaf: 0, bend: 0.15, lie: true },
|
|
76
|
+
eggplant: { h: 1.0, rho: (t) => 0.4 * (ell(t, 0.45) * (1 - 0.5 * Math.pow(t, 1.4)) + 0.1 * t * (1 - t)), lobes: [0, 0], dimple: [0.04, 0.06], unripe: '#9a6ab8', ripe: '#2f1a3a', pattern: 'none', gloss: 0.95, sss: 0.15, stem: [0.12, 0.03, '#4f7a2a'], leaf: 0, calyx: 5 },
|
|
77
|
+
broccoli: { h: 1.0, rho: (t) => 0.1, florets: { n: 120, rad: 0.07, dome: 0.42 }, unripe: '#4f8a3a', ripe: '#2f6a3a', pattern: 'florets', gloss: 0.1, sss: 0.1, stem: [0.5, 0.11, '#8aa868'], leaf: 0 },
|
|
78
|
+
potato: { h: 0.75, rho: (t) => 0.48 * ell(t, 0.55), lobes: [3, 0.04], dimple: [0, 0], bumps: [8, 0.05], eyes: [10, 0.03], unripe: '#c9a86a', ripe: '#a8824a', pattern: 'eyes', gloss: 0.05, sss: 0, stem: [0, 0, '#000'], leaf: 0 },
|
|
79
|
+
onion: { h: 1.0, rho: (t) => 0.46 * ell(t, 0.6) * (1 - 0.35 * Math.pow(t, 2.5)), lobes: [0, 0], dimple: [0.05, 0.02], unripe: '#e8d2a8', ripe: '#c9893a', pattern: 'onion', gloss: 0.5, sss: 0.3, stem: [0.14, 0.02, '#b89a6a'], leaf: 0, roots: 1 },
|
|
80
|
+
};
|
|
81
|
+
export const FRUIT_LIST = ['apple', 'pear', 'peach', 'apricot', 'plum', 'cherry', 'orange', 'lemon', 'lime', 'grapefruit', 'banana', 'mango', 'papaya', 'kiwi', 'fig', 'avocado', 'pomegranate', 'persimmon', 'strawberry', 'raspberry', 'blueberry', 'grapes', 'watermelon', 'melon', 'pineapple', 'dragonfruit', 'coconut'];
|
|
82
|
+
export const VEGETABLE_LIST = ['tomato', 'pepper', 'chili', 'eggplant', 'cucumber', 'zucchini', 'pumpkin', 'butternut', 'corn', 'carrot', 'radish', 'beet', 'turnip', 'potato', 'sweetPotato', 'onion', 'garlic', 'leek', 'asparagus', 'broccoli', 'cauliflower', 'cabbage', 'artichoke', 'peaPod', 'beans', 'mushroom', 'flyAgaric'];
|
|
83
|
+
export const PATTERNS = ['none', 'streaks', 'pores', 'speckle', 'banana', 'seeds', 'bloom', 'stripes', 'lattice', 'ribs', 'rings', 'kernels', 'warts', 'florets', 'eyes', 'onion', 'blush', 'fibre', 'net', 'scales', 'turnip', 'veined', 'cloves', 'leek', 'spear', 'cap', 'spots', 'pod', 'disc'];
|
|
84
|
+
/**
|
|
85
|
+
* The body of one kind as a parametric surface in a unit-ish frame (height ≈ h, width ≈ 2·max ρ),
|
|
86
|
+
* y up from 0. Attributes: position, normal, uv (θ/2π, t), aSkin (t, noise seed). Returns the
|
|
87
|
+
* geometry and the attachment points (stem top, calyx).
|
|
88
|
+
*/
|
|
89
|
+
export function produceBody(kind, seed, detail) {
|
|
90
|
+
const P = PRODUCE[kind];
|
|
91
|
+
const rnd = mulberry32(seed * 1664525 + 1013904223);
|
|
92
|
+
const nu = Math.round(36 * (detail || 1)), nv = Math.round(28 * (detail || 1));
|
|
93
|
+
const pos = [], uv = [], skin = [], idx = [];
|
|
94
|
+
const lobesN = P.lobes ? P.lobes[0] : 0, lobesA = P.lobes ? P.lobes[1] : 0;
|
|
95
|
+
const noiseSeed = rnd() * 100;
|
|
96
|
+
const nz = (a, b) => {
|
|
97
|
+
const h = (x, y) => { const s = Math.sin(x * 127.1 + y * 311.7 + noiseSeed) * 43758.5453; return s - Math.floor(s); };
|
|
98
|
+
const i = Math.floor(a), j = Math.floor(b), fa = a - i, fb = b - j, sa = fa * fa * (3 - 2 * fa), sb = fb * fb * (3 - 2 * fb);
|
|
99
|
+
return (h(i, j) * (1 - sa) + h(i + 1, j) * sa) * (1 - sb) + (h(i, j + 1) * (1 - sa) + h(i + 1, j + 1) * sa) * sb;
|
|
100
|
+
};
|
|
101
|
+
const radiusAt = (th, t) => {
|
|
102
|
+
let r = P.rho(t) * (1 + lobesA * Math.cos(lobesN * th));
|
|
103
|
+
if (P.bumps)
|
|
104
|
+
r += P.bumps[1] * (nz(th / TAU * P.bumps[0], t * P.bumps[0] * 0.5) - 0.5) * 2 * ell(t, 0.3);
|
|
105
|
+
if (P.rings)
|
|
106
|
+
r += P.rings[1] * Math.sin(t * P.rings[0] * TAU) * (1 - t);
|
|
107
|
+
if (P.kernels)
|
|
108
|
+
r += P.kernels[1] * (0.5 + 0.5 * Math.cos(P.kernels[0] * th + Math.floor(t * 30) * Math.PI)) * Math.abs(Math.sin(t * 30 * Math.PI)) * ell(t, 0.3);
|
|
109
|
+
if (P.lattice)
|
|
110
|
+
r += P.lattice[1] * Math.abs(Math.sin(P.lattice[0] * th * 0.5 + t * 14) * Math.sin(P.lattice[0] * th * 0.5 - t * 14)) * ell(t, 0.3);
|
|
111
|
+
if (P.seeds)
|
|
112
|
+
r -= P.seeds[1] * Math.pow(Math.max(0, Math.cos(P.seeds[0] * th * 0.5 + t * 22) * Math.cos(P.seeds[0] * th * 0.5 - t * 22)), 4) * ell(t, 0.3);
|
|
113
|
+
if (P.eyes)
|
|
114
|
+
r -= P.eyes[1] * Math.pow(nz(th / TAU * P.eyes[0] * 0.5 + 9, t * 3 + 4), 8) * 10 * ell(t, 0.3);
|
|
115
|
+
if (P.cleft) {
|
|
116
|
+
const da = Math.atan2(Math.sin(th), Math.cos(th));
|
|
117
|
+
r -= P.cleft * Math.exp(-da * da / 0.05) * ell(t, 0.4);
|
|
118
|
+
} // the seam of a stone fruit
|
|
119
|
+
if (P.pods)
|
|
120
|
+
r *= 1 + 0.4 * Math.pow(Math.abs(Math.sin(t * P.pods * Math.PI)), 1.5) * Math.max(0, Math.cos(th)) * 0.8 + 0.15 * Math.pow(Math.abs(Math.sin(t * P.pods * Math.PI)), 1.5); // peas / beans bulging the pod
|
|
121
|
+
r *= 1 + 0.02 * (nz(th / TAU * 3 + 20, t * 2) - 0.5);
|
|
122
|
+
return Math.max(r, 0.002);
|
|
123
|
+
};
|
|
124
|
+
const dimpleY = (th, t) => {
|
|
125
|
+
// poles pushed in (apple, tomato) or out (lemon nipple)
|
|
126
|
+
const top = P.dimple ? P.dimple[0] : 0, bot = P.dimple ? P.dimple[1] : 0;
|
|
127
|
+
const rr = P.rho(t) / Math.max(P.rho(0.5), 1e-3);
|
|
128
|
+
return -top * Math.pow(Math.max(0, 1 - rr * 1.2), 1.5) * (t > 0.5 ? 1 : 0) + bot * Math.pow(Math.max(0, 1 - rr * 1.2), 1.5) * (t <= 0.5 ? 1 : 0);
|
|
129
|
+
};
|
|
130
|
+
const point = (th, t, out) => {
|
|
131
|
+
const r = radiusAt(th, t);
|
|
132
|
+
let x = Math.cos(th) * r, z = Math.sin(th) * r, y = t * P.h + dimpleY(th, t);
|
|
133
|
+
if (P.bend) { // banana / cucumber: bend the axis along x
|
|
134
|
+
const b = P.bend * (t - 0.5) * (t - 0.5) * 2;
|
|
135
|
+
x += b - P.bend * 0.1;
|
|
136
|
+
const ang = P.bend * (t - 0.5) * 1.2;
|
|
137
|
+
const cy = y - 0.5 * P.h;
|
|
138
|
+
const nx = x * Math.cos(ang) - cy * Math.sin(ang), ny = x * Math.sin(ang) + cy * Math.cos(ang);
|
|
139
|
+
x = nx;
|
|
140
|
+
y = ny + 0.5 * P.h;
|
|
141
|
+
}
|
|
142
|
+
return out.set(x, y, z);
|
|
143
|
+
};
|
|
144
|
+
const p = new THREE.Vector3(), pa = new THREE.Vector3(), pb = new THREE.Vector3(), n = new THREE.Vector3();
|
|
145
|
+
const nrm = [];
|
|
146
|
+
for (let j = 0; j <= nv; j++) {
|
|
147
|
+
const t = j / nv;
|
|
148
|
+
for (let i = 0; i <= nu; i++) {
|
|
149
|
+
const th = i / nu * TAU;
|
|
150
|
+
point(th, t, p);
|
|
151
|
+
point(th + 1e-3, t, pa).sub(p);
|
|
152
|
+
point(th, Math.min(1, t + 1e-3), pb).sub(p);
|
|
153
|
+
if (t >= 1)
|
|
154
|
+
point(th, t - 1e-3, pb).sub(p).negate();
|
|
155
|
+
n.crossVectors(pb, pa).normalize();
|
|
156
|
+
if (j === 0 || j === nv)
|
|
157
|
+
n.set(0, j === 0 ? -1 : 1, 0);
|
|
158
|
+
pos.push(p.x, p.y, p.z);
|
|
159
|
+
nrm.push(n.x, n.y, n.z);
|
|
160
|
+
uv.push(i / nu, t);
|
|
161
|
+
skin.push(t, nz(th, t * 4));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
for (let j = 0; j < nv; j++)
|
|
165
|
+
for (let i = 0; i < nu; i++) {
|
|
166
|
+
const a = j * (nu + 1) + i, b = a + nu + 1;
|
|
167
|
+
idx.push(a, b, a + 1, a + 1, b, b + 1);
|
|
168
|
+
}
|
|
169
|
+
const geo = new THREE.BufferGeometry();
|
|
170
|
+
geo.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3));
|
|
171
|
+
geo.setAttribute('normal', new THREE.Float32BufferAttribute(nrm, 3));
|
|
172
|
+
geo.setAttribute('uv', new THREE.Float32BufferAttribute(uv, 2));
|
|
173
|
+
geo.setAttribute('aSkin', new THREE.Float32BufferAttribute(skin, 2));
|
|
174
|
+
geo.setIndex(idx);
|
|
175
|
+
const top = point(0, 1, new THREE.Vector3()), bottom = point(0, 0, new THREE.Vector3());
|
|
176
|
+
return { geo, top, bottom, h: P.h, width: 2 * Math.max(P.rho(0.5), P.rho(0.3), P.rho(0.7)) };
|
|
177
|
+
}
|
|
178
|
+
/** Small instanced spheroids (grapes, florets): one sphere geometry with aPos/aInfo instances. */
|
|
179
|
+
export function clusterGeometry(centres, rad, detail) {
|
|
180
|
+
const sph = new THREE.SphereGeometry(1, 10 * (detail || 1) | 0, 7 * (detail || 1) | 0);
|
|
181
|
+
const g = new THREE.InstancedBufferGeometry();
|
|
182
|
+
g.setAttribute('position', sph.attributes.position);
|
|
183
|
+
g.setAttribute('normal', sph.attributes.normal);
|
|
184
|
+
g.setAttribute('uv', sph.attributes.uv);
|
|
185
|
+
g.setIndex(sph.index);
|
|
186
|
+
const n = centres.length;
|
|
187
|
+
const a = new Float32Array(n * 4);
|
|
188
|
+
for (let i = 0; i < n; i++) {
|
|
189
|
+
a[i * 4] = centres[i].x;
|
|
190
|
+
a[i * 4 + 1] = centres[i].y;
|
|
191
|
+
a[i * 4 + 2] = centres[i].z;
|
|
192
|
+
a[i * 4 + 3] = rad * (0.8 + 0.4 * centres[i].s);
|
|
193
|
+
}
|
|
194
|
+
g.setAttribute('aCentre', new THREE.InstancedBufferAttribute(a, 4));
|
|
195
|
+
g.instanceCount = n;
|
|
196
|
+
return g;
|
|
197
|
+
}
|
|
198
|
+
// ---------------------------------------------------------------------------- the skin
|
|
199
|
+
/**
|
|
200
|
+
* Produce skin: a lit material. Per piece: ripeness with a spread (colour from unripe → ripe,
|
|
201
|
+
* gloss rises with it), the kind's pattern, blemishes, translucency against the sun.
|
|
202
|
+
* Instanced: aXf = (x, y, z, yaw), aTilt = (tilt axis x, z, tilt, scale), aPiece = (hash, order, ripe offset, -).
|
|
203
|
+
*/
|
|
204
|
+
export function produceMaterial(u) {
|
|
205
|
+
// a physical material: the waxy skin is a clearcoat over the pigmented flesh, so highlights are sharp
|
|
206
|
+
// and white while the body colour stays matte underneath; the clearcoat follows the kind's gloss
|
|
207
|
+
const mat = new THREE.MeshPhysicalMaterial({ color: 0xffffff, roughness: 0.5, metalness: 0, side: THREE.DoubleSide, alphaToCoverage: true, clearcoat: 0.5, clearcoatRoughness: 0.2, envMapIntensity: 1.0 });
|
|
208
|
+
const own = {
|
|
209
|
+
uUnripe: { value: new THREE.Color('#8fbf3f') }, uRipe: { value: new THREE.Color('#c9302b') }, uTint: { value: new THREE.Color('#ffffff') },
|
|
210
|
+
uRipeness: { value: 1 }, uBlemish: { value: 0.2 }, uGloss: { value: 0.5 }, uSss: { value: 0.2 }, uPattern: { value: 0 }, uPieceH: { value: 1 },
|
|
211
|
+
uCount: { value: 1 }, uSizeVar: { value: 0.15 }, uStemColor: { value: new THREE.Color('#4a3a2a') },
|
|
212
|
+
};
|
|
213
|
+
mat.userData.uniforms = own;
|
|
214
|
+
const PARS = `#include <common>\n${VEG_UNIFORMS_GLSL}\n${NOISE_GLSL_V}\n${WIND_GLSL}
|
|
215
|
+
uniform float uRipeness, uCount, uSizeVar, uPieceH;
|
|
216
|
+
attribute vec2 aSkin; attribute vec2 aPart; attribute vec2 aCard; attribute vec4 aXf; attribute vec4 aTilt; attribute vec4 aPiece; attribute vec3 aRoot;
|
|
217
|
+
varying vec2 vSkin; varying vec3 vPiece; varying vec3 vLocal; varying vec2 vPart; varying vec2 vCard;
|
|
218
|
+
mat3 pieceRot(vec4 xf, vec4 tilt) {
|
|
219
|
+
float cy = cos(xf.w), sy = sin(xf.w);
|
|
220
|
+
mat3 yaw = mat3(cy, 0.0, -sy, 0.0, 1.0, 0.0, sy, 0.0, cy);
|
|
221
|
+
vec3 ax = normalize(vec3(tilt.x, 0.0, tilt.y) + 1e-5);
|
|
222
|
+
float ct = cos(tilt.z), st = sin(tilt.z);
|
|
223
|
+
mat3 rot = mat3(ct + ax.x * ax.x * (1.0 - ct), ax.y * ax.x * (1.0 - ct) + ax.z * st, ax.z * ax.x * (1.0 - ct) - ax.y * st,
|
|
224
|
+
ax.x * ax.y * (1.0 - ct) - ax.z * st, ct + ax.y * ax.y * (1.0 - ct), ax.z * ax.y * (1.0 - ct) + ax.x * st,
|
|
225
|
+
ax.x * ax.z * (1.0 - ct) + ax.y * st, ax.y * ax.z * (1.0 - ct) - ax.x * st, ct + ax.z * ax.z * (1.0 - ct));
|
|
226
|
+
return rot * yaw;
|
|
227
|
+
}
|
|
228
|
+
`;
|
|
229
|
+
const PRE = `
|
|
230
|
+
float sc = aTilt.w * (1.0 + uSizeVar * (aPiece.x - 0.5));
|
|
231
|
+
mat3 R = pieceRot(aXf, aTilt);
|
|
232
|
+
vec3 lp = position * sc;
|
|
233
|
+
// a piece lying on its side (tilt past ~70°) turns about its middle and rests at its half width
|
|
234
|
+
vec3 pivot = aTilt.z > 1.2 ? vec3(0.0, uPieceH * 0.5 * sc, 0.0) : vec3(0.0);
|
|
235
|
+
// growth: the piece scales with its plant from the plant's root (standalone: from the origin), and a
|
|
236
|
+
// fruit on a plant only sets once the plant is mostly grown (aPiece.w = 1 marks fruit on a plant)
|
|
237
|
+
float gsc = uGrowth * mix(1.0, smoothstep(0.55, 0.95, uGrowth), aPiece.w);
|
|
238
|
+
// the piece itself stays isotropic in the world (isoOff); a pile's resting heights are piece units too, a plant's attachment is not
|
|
239
|
+
vec3 base = vec3(aXf.x, aXf.y * mix(uMean / uScale.y, 1.0, aPiece.w), aXf.z);
|
|
240
|
+
vec3 pw = aRoot + (isoOff(R * ((lp - pivot) * gsc)) + base - aRoot) * uGrowth;
|
|
241
|
+
pw += plantDisplace(aXf.xyz, aXf.y, vec2(aPiece.x, 0.4)) * aPiece.w;
|
|
242
|
+
`;
|
|
243
|
+
const NRM = `
|
|
244
|
+
vec3 objectNormal = isoNrm(R * normal);
|
|
245
|
+
#ifdef USE_TANGENT
|
|
246
|
+
vec3 objectTangent = vec3(tangent.xyz);
|
|
247
|
+
#endif`;
|
|
248
|
+
const POS = `
|
|
249
|
+
vec3 transformed = pw;
|
|
250
|
+
vSkin = aSkin; vPiece = aPiece.xyz; vLocal = position; vPart = aPart; vCard = aCard;`;
|
|
251
|
+
const vert = (shader, depth = false) => {
|
|
252
|
+
for (const k in u)
|
|
253
|
+
shader.uniforms[k] = u[k];
|
|
254
|
+
for (const k in own)
|
|
255
|
+
shader.uniforms[k] = own[k];
|
|
256
|
+
shader.vertexShader = depth
|
|
257
|
+
? shader.vertexShader.replace('#include <common>', PARS).replace('#include <begin_vertex>', PRE + POS)
|
|
258
|
+
: shader.vertexShader.replace('#include <common>', PARS).replace('#include <beginnormal_vertex>', PRE + NRM).replace('#include <begin_vertex>', POS);
|
|
259
|
+
};
|
|
260
|
+
mat.onBeforeCompile = (shader) => {
|
|
261
|
+
vert(shader);
|
|
262
|
+
shader.fragmentShader = shader.fragmentShader
|
|
263
|
+
.replace('#include <common>', `#include <common>\n${VEG_UNIFORMS_GLSL}\n${NOISE_GLSL_V}
|
|
264
|
+
uniform vec3 uUnripe, uRipe, uTint, uStemColor; uniform float uRipeness, uBlemish, uGloss, uSss, uPattern, uPieceH;
|
|
265
|
+
varying vec2 vSkin; varying vec3 vPiece; varying vec3 vLocal; varying vec2 vPart; varying vec2 vCard;
|
|
266
|
+
${LEAF_SDF_GLSL}`)
|
|
267
|
+
.replace('#include <roughnessmap_fragment>', '#include <roughnessmap_fragment>\n roughnessFactor = vegRough;')
|
|
268
|
+
.replace('#include <map_fragment>', `
|
|
269
|
+
float vegRough = 0.5, vegClear = 0.0, vegHgt = 0.0;
|
|
270
|
+
float t = vSkin.x, h = vPiece.x;
|
|
271
|
+
if (vPart.x > 1.5) {
|
|
272
|
+
// a leaf card (leaf, calyx, crown, tops, husk): cut by its shape, green from the shared leaf colour
|
|
273
|
+
float lsd = leafSdf(vCard, vPart.y, h);
|
|
274
|
+
float aa = fwidth(lsd) * 0.9;
|
|
275
|
+
float cover = 1.0 - smoothstep(-aa, aa, lsd);
|
|
276
|
+
if (cover < 0.02) discard;
|
|
277
|
+
vec3 lc = (vPart.x > 3.5 ? vec3(0.95, 0.75, 0.15) : uColor) * (0.8 + 0.4 * vhash11(h * 7.0 + vSkin.y * 3.0));
|
|
278
|
+
lc *= 1.0 + 0.12 * (1.0 - smoothstep(0.0, 0.05, abs(vCard.x)));
|
|
279
|
+
diffuseColor.rgb *= lc; diffuseColor.a *= cover * uOpacity;
|
|
280
|
+
vegRough = 0.6; vegClear = 0.0;
|
|
281
|
+
} else if (vPart.x > 2.5) {
|
|
282
|
+
// gills under a cap: dark radial lines
|
|
283
|
+
float g = 0.5 + 0.5 * sin(atan(vLocal.z, vLocal.x) * 60.0);
|
|
284
|
+
diffuseColor.rgb *= mix(vec3(0.35, 0.25, 0.18), vec3(0.75, 0.62, 0.45), g) * uTint;
|
|
285
|
+
vegRough = 0.9; vegClear = 0.0;
|
|
286
|
+
} else if (vPart.x > 0.5) {
|
|
287
|
+
// the stem: woody, darker in the grooves
|
|
288
|
+
diffuseColor.rgb *= uStemColor * (0.8 + 0.3 * vnoise2v(vec2(vSkin.y * 30.0, t * 8.0)));
|
|
289
|
+
vegRough = 0.9; vegClear = 0.0;
|
|
290
|
+
} else {
|
|
291
|
+
float th = atan(vLocal.z, vLocal.x);
|
|
292
|
+
// ripeness: the knob with a per-piece offset and a top-down gradient (the sun side first); the cursor's warmth ripens locally
|
|
293
|
+
float ripeLocal = uRipeness + (vPiece.z - 0.5) * 0.35 + 0.15 * (t - 0.5);
|
|
294
|
+
float ripe = smoothstep(0.3, 0.7, ripeLocal);
|
|
295
|
+
vec3 skin = mix(uUnripe, uRipe, ripe);
|
|
296
|
+
float n1 = fbm2v(vec2(th * 2.0 + h * 10.0, t * 6.0));
|
|
297
|
+
float n2 = vnoise2v(vec2(th * 26.0, t * 52.0) + h * 7.0);
|
|
298
|
+
int pat = int(uPattern + 0.5);
|
|
299
|
+
if (pat == 1) { skin = mix(skin, uRipe * 1.2, 0.5 * ripe * smoothstep(0.45, 0.65, fbm2v(vec2(th * 4.0 + h * 10.0, t * 1.5)))); skin = mix(skin, uUnripe * 1.1, (1.0 - ripe) * 0.0 + 0.25 * smoothstep(0.55, 0.75, n1) * (1.0 - t)); }
|
|
300
|
+
else if (pat == 2) { skin *= 1.0 - 0.1 * smoothstep(0.55, 0.8, n2); }
|
|
301
|
+
else if (pat == 3) { skin *= 1.0 - 0.3 * smoothstep(0.72, 0.8, vnoise2v(vec2(th * 20.0, t * 40.0) + h)); }
|
|
302
|
+
else if (pat == 4) { float tip = smoothstep(0.85, 1.0, t) + smoothstep(0.1, 0.0, t); skin = mix(skin, vec3(0.25, 0.15, 0.08), tip * ripe); skin *= 1.0 - 0.15 * smoothstep(0.6, 0.75, n1) * ripe; }
|
|
303
|
+
else if (pat == 5) { float sd = pow(max(0.0, cos(34.0 * th * 0.5 + t * 22.0) * cos(34.0 * th * 0.5 - t * 22.0)), 4.0); skin = mix(skin, vec3(0.9, 0.8, 0.3), smoothstep(0.3, 0.6, sd)); }
|
|
304
|
+
else if (pat == 6) { skin = mix(skin, skin * 0.6 + vec3(0.35, 0.35, 0.45), 0.45 * smoothstep(0.4, 0.7, n1)); }
|
|
305
|
+
else if (pat == 7) { float st = smoothstep(0.35, 0.5, abs(fract(th / 6.2832 * 11.0 + 0.3 * n1) - 0.5)); skin = mix(skin * 0.55, skin * 1.25 + vec3(0.1, 0.15, 0.05), st); }
|
|
306
|
+
else if (pat == 8) { float la = abs(sin(10.0 * th * 0.5 + t * 14.0) * sin(10.0 * th * 0.5 - t * 14.0)); skin = mix(skin * 0.55, skin, smoothstep(0.05, 0.35, la)); skin = mix(skin, vec3(0.4, 0.5, 0.2), smoothstep(0.7, 0.95, la) * 0.5); }
|
|
307
|
+
else if (pat == 9) { skin *= 0.75 + 0.35 * smoothstep(0.2, 0.9, 0.5 + 0.5 * cos(12.0 * th)); }
|
|
308
|
+
else if (pat == 10) { skin *= 0.85 + 0.2 * (0.5 + 0.5 * sin(t * 24.0 * 6.2832)); }
|
|
309
|
+
else if (pat == 11) { float k = abs(sin(14.0 * th * 0.5)) * abs(sin(t * 30.0 * 3.1416)); skin = mix(skin * 0.7, skin * 1.1, smoothstep(0.1, 0.5, k)); }
|
|
310
|
+
else if (pat == 12) { skin *= 1.0 - 0.2 * smoothstep(0.6, 0.8, n2); skin = mix(skin, skin * vec3(1.2, 1.15, 0.8), 0.3 * smoothstep(0.5, 0.7, n1)); }
|
|
311
|
+
else if (pat == 13) { skin = mix(skin * 0.8, skin, n2); }
|
|
312
|
+
else if (pat == 14) { float eye = pow(vnoise2v(vec2(th * 2.0 + 9.0, t * 3.0 + 4.0 + h)), 8.0) * 10.0; skin = mix(skin, skin * 0.55, smoothstep(0.3, 0.9, eye)); skin *= 0.9 + 0.2 * n2; }
|
|
313
|
+
else if (pat == 15) { skin *= 0.85 + 0.3 * smoothstep(0.3, 0.7, 0.5 + 0.5 * cos(th * 3.0 + n1 * 4.0)); skin = mix(skin, skin * 0.7, smoothstep(0.85, 1.0, t)); }
|
|
314
|
+
else if (pat == 16) { // blush: the sun side flushes red over a yellow ground, finely speckled
|
|
315
|
+
float sunSide = 0.5 + 0.5 * cos(th + h * 6.2832); skin = mix(skin, uRipe * vec3(1.1, 0.55, 0.45), ripe * smoothstep(0.25, 0.9, sunSide + 0.3 * n1 - 0.2) * 0.85); skin *= 0.92 + 0.16 * n2; }
|
|
316
|
+
else if (pat == 17) { // coconut fibre: hairy brown streaks along the meridians, three dark eyes at the base
|
|
317
|
+
float fib = fbm2v(vec2(th * 3.0, t * 40.0 + h)); skin *= 0.6 + 0.8 * fib; float eye = smoothstep(0.08, 0.0, length(vec2(fract(th / 6.2832 * 3.0) - 0.5, (t - 0.06) * 4.0)) - 0.1); skin = mix(skin, skin * 0.3, eye * smoothstep(0.15, 0.05, t)); }
|
|
318
|
+
else if (pat == 18) { // cantaloupe netting: a raised pale net over a green-tan ground
|
|
319
|
+
float net = 1.0 - abs(2.0 * fbm2v(vec2(th * 4.0, t * 7.0) + h) - 1.0); net = smoothstep(0.75, 0.95, net); skin = mix(skin, vec3(0.86, 0.82, 0.68), net * 0.85); }
|
|
320
|
+
else if (pat == 19) { // dragon fruit: magenta skin, scale edges fading to green-yellow
|
|
321
|
+
float la = abs(sin(8.0 * th * 0.5 + t * 14.0) * sin(8.0 * th * 0.5 - t * 14.0)); skin = mix(skin, mix(vec3(0.85, 0.85, 0.3), vec3(0.4, 0.8, 0.35), 0.5), smoothstep(0.55, 0.95, la) * 0.9); }
|
|
322
|
+
else if (pat == 20) { // turnip: white below, purple shoulders
|
|
323
|
+
skin = mix(vec3(0.93, 0.9, 0.84), uRipe, smoothstep(0.45, 0.8, t) * ripe); }
|
|
324
|
+
else if (pat == 21) { // cabbage head: pale veins radiating from the top, waxy bloom
|
|
325
|
+
float v = 1.0 - smoothstep(0.0, 0.06, abs(fract(th / 6.2832 * 9.0 + 0.3 * n1) - 0.5) - 0.42); skin = mix(skin, skin * vec3(1.25, 1.2, 1.05), v * (1.0 - t * 0.5)); skin = mix(skin, skin * 0.8 + vec3(0.2, 0.2, 0.15), 0.3 * n1); }
|
|
326
|
+
else if (pat == 22) { // garlic: cloves as vertical segments, papery white with purple streaks
|
|
327
|
+
float seg = smoothstep(0.0, 0.5, abs(fract(th / 6.2832 * 7.0) - 0.5)); skin *= 0.85 + 0.2 * seg; skin = mix(skin, vec3(0.65, 0.45, 0.6), 0.35 * smoothstep(0.55, 0.8, fbm2v(vec2(th * 1.5, t * 6.0) + h))); }
|
|
328
|
+
else if (pat == 23) { // leek: white shank into green, vertical fibres
|
|
329
|
+
skin = mix(vec3(0.93, 0.92, 0.85), vec3(0.35, 0.6, 0.25), smoothstep(0.35, 0.85, t)); skin *= 0.9 + 0.2 * vnoise2v(vec2(th * 14.0, t * 2.0)); }
|
|
330
|
+
else if (pat == 24) { // asparagus: green spear, purple-tipped scales
|
|
331
|
+
float sc = smoothstep(0.3, 0.7, 0.5 + 0.5 * sin(t * 50.0 + th * 2.0)); skin = mix(skin, vec3(0.45, 0.25, 0.45), sc * smoothstep(0.6, 1.0, t) * 0.7); skin *= 0.9 + 0.2 * sc; }
|
|
332
|
+
else if (pat == 25) { // mushroom cap: cream rim to tan centre (t = radial on the cap)
|
|
333
|
+
skin = mix(uRipe, uUnripe, smoothstep(0.2, 0.9, t)); skin *= 0.9 + 0.2 * n1; }
|
|
334
|
+
else if (pat == 26) { // fly agaric: red cap with white warts
|
|
335
|
+
float w = smoothstep(0.6, 0.75, vnoise2v(vec2(th * 5.0, t * 6.0) + h * 3.0)); skin = mix(uRipe * (0.85 + 0.3 * n1), vec3(0.95, 0.92, 0.85), w); }
|
|
336
|
+
else if (pat == 27) { // pod: the seeds show as paler bulges, a seam along the top
|
|
337
|
+
float bul = pow(abs(sin(t * 6.0 * 3.1416)), 1.5); skin *= 0.85 + 0.3 * bul; skin = mix(skin, skin * 0.7, 1.0 - smoothstep(0.0, 0.12, abs(th - 3.1416))); }
|
|
338
|
+
else if (pat == 28) { // sunflower disc: a phyllotaxis of seeds, darker centre
|
|
339
|
+
float rr = t; float ang = th; float sp = 0.5 + 0.5 * cos(ang * 21.0 + rr * 60.0) * cos(ang * 13.0 - rr * 60.0); skin = mix(skin * 0.6, skin * 1.3, smoothstep(0.3, 0.8, sp)); skin = mix(skin, vec3(0.3, 0.18, 0.08), smoothstep(0.5, 0.0, rr) * 0.5); }
|
|
340
|
+
// blemishes and the per-piece scatter
|
|
341
|
+
skin *= 1.0 - uBlemish * 0.5 * smoothstep(0.78, 0.85, vnoise2v(vec2(th * 5.0, t * 9.0) + h * 13.0));
|
|
342
|
+
skin *= 0.9 + 0.2 * vhash11(h * 4.0);
|
|
343
|
+
diffuseColor.rgb *= skin * uTint;
|
|
344
|
+
vegRough = mix(0.75, 0.3, uGloss * mix(0.5, 1.0, ripe));
|
|
345
|
+
vegClear = uGloss * mix(0.4, 1.0, ripe);
|
|
346
|
+
// the peel in relief: pores, warts, kernels, ribs and netting as a height field for the bump
|
|
347
|
+
float relief = 0.0;
|
|
348
|
+
if (pat == 2) relief = -0.3 * smoothstep(0.45, 0.85, n2) - 0.1 * vnoise2v(vec2(th * 60.0, t * 120.0));
|
|
349
|
+
else if (pat == 12) relief = 0.8 * smoothstep(0.6, 0.85, n2);
|
|
350
|
+
else if (pat == 11) relief = 0.5 * abs(sin(14.0 * th * 0.5)) * abs(sin(t * 30.0 * 3.1416));
|
|
351
|
+
else if (pat == 8) relief = 0.6 * abs(sin(10.0 * th * 0.5 + t * 14.0) * sin(10.0 * th * 0.5 - t * 14.0));
|
|
352
|
+
else if (pat == 5) relief = -0.8 * pow(max(0.0, cos(34.0 * th * 0.5 + t * 22.0) * cos(34.0 * th * 0.5 - t * 22.0)), 4.0);
|
|
353
|
+
else if (pat == 14) relief = -0.5 * smoothstep(0.3, 0.9, pow(vnoise2v(vec2(th * 2.0 + 9.0, t * 3.0 + 4.0 + h)), 8.0) * 10.0);
|
|
354
|
+
else relief = 0.15 * (vnoise2v(vec2(th * 30.0, t * 40.0) + h) - 0.5);
|
|
355
|
+
vegHgt = relief * 0.003 + 0.0008 * (fbm2v(vec2(th * 6.0, t * 8.0) + h * 3.0) - 0.5);
|
|
356
|
+
}`)
|
|
357
|
+
.replace('#include <normal_fragment_maps>', `#include <normal_fragment_maps>
|
|
358
|
+
{
|
|
359
|
+
vec3 dpdx = dFdx(-vViewPosition), dpdy = dFdy(-vViewPosition);
|
|
360
|
+
float dhx = dFdx(vegHgt), dhy = dFdy(vegHgt);
|
|
361
|
+
vec3 r1 = cross(dpdy, normal), r2 = cross(normal, dpdx);
|
|
362
|
+
float det = dot(dpdx, r1);
|
|
363
|
+
vec3 grad = (r1 * dhx + r2 * dhy) / max(abs(det), 1e-7) * sign(det);
|
|
364
|
+
normal = normalize(normal - grad);
|
|
365
|
+
}`)
|
|
366
|
+
.replace('#include <clearcoat_normal_fragment_begin>', '#include <clearcoat_normal_fragment_begin>\n clearcoatNormal = normal;')
|
|
367
|
+
.replace('#include <lights_physical_fragment>', '#include <lights_physical_fragment>\n material.clearcoat = saturate(clearcoat * vegClear);')
|
|
368
|
+
.replace('#include <lights_fragment_end>', `#include <lights_fragment_end>
|
|
369
|
+
{
|
|
370
|
+
float back = max(dot(-normal, uSunDirView), 0.0);
|
|
371
|
+
reflectedLight.indirectDiffuse += diffuseColor.rgb * back * uSss * 0.8;
|
|
372
|
+
}`);
|
|
373
|
+
};
|
|
374
|
+
mat.customProgramCacheKey = () => 'veg-produce';
|
|
375
|
+
const depth = new THREE.MeshDepthMaterial({ depthPacking: THREE.RGBADepthPacking, side: THREE.DoubleSide });
|
|
376
|
+
depth.onBeforeCompile = (shader) => {
|
|
377
|
+
vert(shader, true);
|
|
378
|
+
shader.fragmentShader = shader.fragmentShader
|
|
379
|
+
.replace('#include <common>', `#include <common>\n${LEAF_SDF_GLSL}\nvarying vec2 vPart; varying vec2 vCard; varying vec3 vPiece;`)
|
|
380
|
+
.replace('#include <map_fragment>', `if (vPart.x > 1.5) { float lsd = leafSdf(vCard, vPart.y, vPiece.x); if (lsd > 0.0) discard; }`);
|
|
381
|
+
};
|
|
382
|
+
depth.customProgramCacheKey = () => 'veg-produce-depth';
|
|
383
|
+
return { material: mat, depth };
|
|
384
|
+
}
|
|
385
|
+
/** Instance buffers for `count` pieces on a geometry. */
|
|
386
|
+
/** Sets a produce material to a kind: colours, pattern, gloss, translucency, fuzz (sheen), stem colour. */
|
|
387
|
+
export function applyKind(mat, P) {
|
|
388
|
+
const o = mat.userData.uniforms;
|
|
389
|
+
o.uUnripe.value.set(P.unripe);
|
|
390
|
+
o.uRipe.value.set(P.ripe);
|
|
391
|
+
o.uPattern.value = Math.max(0, PATTERNS.indexOf(P.pattern));
|
|
392
|
+
o.uSss.value = P.sss;
|
|
393
|
+
o.uStemColor.value.set(P.stem[2]);
|
|
394
|
+
const fuzz = P.fuzz || 0;
|
|
395
|
+
if ((mat.sheen > 0) !== (fuzz > 0))
|
|
396
|
+
mat.needsUpdate = true;
|
|
397
|
+
mat.sheen = fuzz;
|
|
398
|
+
mat.sheenRoughness = 0.85;
|
|
399
|
+
mat.sheenColor.set(P.ripe).lerp(new THREE.Color(1, 1, 1), 0.6);
|
|
400
|
+
}
|
|
401
|
+
export function instancePieces(geo, count) {
|
|
402
|
+
const g = new THREE.InstancedBufferGeometry();
|
|
403
|
+
for (const k of ['position', 'normal', 'uv', 'aSkin', 'aPart', 'aCard'])
|
|
404
|
+
if (geo.attributes[k])
|
|
405
|
+
g.setAttribute(k, geo.attributes[k]);
|
|
406
|
+
g.setIndex(geo.index);
|
|
407
|
+
g.setAttribute('aXf', new THREE.InstancedBufferAttribute(new Float32Array(count * 4), 4));
|
|
408
|
+
g.setAttribute('aTilt', new THREE.InstancedBufferAttribute(new Float32Array(count * 4), 4));
|
|
409
|
+
g.setAttribute('aPiece', new THREE.InstancedBufferAttribute(new Float32Array(count * 4), 4));
|
|
410
|
+
g.setAttribute('aRoot', new THREE.InstancedBufferAttribute(new Float32Array(count * 3), 3));
|
|
411
|
+
g.instanceCount = count;
|
|
412
|
+
return g;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Where the pieces lie: one piece upright at the origin; a pile = pieces on the ground in a disc
|
|
416
|
+
* (seeded, no two overlapping: rejection sampling with a growing radius), each with a random yaw and
|
|
417
|
+
* a resting tilt, and a second layer sitting in the hollows when the disc is full.
|
|
418
|
+
* `size` = a piece's footprint (its width in unit space). Returns [{ x, y, z, yaw, tiltX, tiltZ, tilt, scale }].
|
|
419
|
+
*/
|
|
420
|
+
export function pilePlacement(count, size, rnd, upright, lie, restY) {
|
|
421
|
+
const out = [];
|
|
422
|
+
const tiltOf = (layer) => lie ? Math.PI / 2 + (rnd() - 0.5) * 0.3 : (upright ? 0.08 : 0.3) * (0.4 + rnd()) * (layer ? 1.5 : 1);
|
|
423
|
+
if (count <= 1)
|
|
424
|
+
return [{ x: 0, y: lie ? restY : 0, z: 0, yaw: rnd() * TAU, tiltX: 1, tiltZ: 0, tilt: lie ? Math.PI / 2 : (upright ? 0 : 0.15), scale: 1 }];
|
|
425
|
+
if (lie) {
|
|
426
|
+
// lying pieces: rows side by side (spacing = their width), a loose fan of yaws, a second row behind
|
|
427
|
+
const w = restY * 2.2, perRow = Math.max(1, Math.floor(0.95 / w)), rows = Math.ceil(count / perRow);
|
|
428
|
+
const base = rnd() * TAU;
|
|
429
|
+
for (let n = 0; n < count; n++) {
|
|
430
|
+
const row = Math.floor(n / perRow), col = n % perRow, inRow = Math.min(perRow, count - row * perRow);
|
|
431
|
+
const x = (col - (inRow - 1) / 2) * w + (rnd() - 0.5) * w * 0.2, z = (row - (rows - 1) / 2) * size * 0.55 + (rnd() - 0.5) * 0.04;
|
|
432
|
+
const cz = Math.cos(base), sz = Math.sin(base);
|
|
433
|
+
out.push({ x: x * cz - z * sz, y: restY + row * restY * 0.6, z: x * sz + z * cz, yaw: base + (rnd() - 0.5) * 0.5, tiltX: 1, tiltZ: 0, tilt: Math.PI / 2 + (rnd() - 0.5) * 0.2, scale: 1 });
|
|
434
|
+
}
|
|
435
|
+
return out;
|
|
436
|
+
}
|
|
437
|
+
// a sunflower spiral packs the disc without overlaps; when it would spill over the box, the next
|
|
438
|
+
// layer starts over the hollows (smaller disc, higher up)
|
|
439
|
+
const step = size * 0.52, rMax = 0.5 - size * 0.42;
|
|
440
|
+
let layer = 0, i = 0;
|
|
441
|
+
for (let n = 0; n < count; n++) {
|
|
442
|
+
let r = step * Math.sqrt(i) * (layer ? 0.75 : 1);
|
|
443
|
+
if (r > rMax && layer < 3) {
|
|
444
|
+
layer++;
|
|
445
|
+
i = 0;
|
|
446
|
+
r = 0;
|
|
447
|
+
}
|
|
448
|
+
const a = i * 2.39996 + layer * 1.3 + (rnd() - 0.5) * 0.3;
|
|
449
|
+
const jit = (rnd() - 0.5) * size * 0.12;
|
|
450
|
+
out.push({ x: Math.cos(a) * (r + jit), y: layer * size * 0.62 + (lie ? restY : 0), z: Math.sin(a) * (r + jit), yaw: rnd() * TAU, tiltX: rnd() - 0.5, tiltZ: rnd() - 0.5, tilt: tiltOf(layer), scale: 1 });
|
|
451
|
+
i++;
|
|
452
|
+
}
|
|
453
|
+
return out;
|
|
454
|
+
}
|
|
455
|
+
/** A leaf card layer for leaves / calyx / crown / tops / husk, using the common leaf material. */
|
|
456
|
+
export function leafCards(entries) {
|
|
457
|
+
const geo = leafGeometry(Math.max(1, entries.length));
|
|
458
|
+
entries.forEach((e, i) => setLeaf(geo, i, e.pos, e.along, e.normal, e.size, e.hash, 0.5, 1, 0, 0, 0, 0));
|
|
459
|
+
geo.instanceCount = entries.length;
|
|
460
|
+
for (const k of ['aPos', 'aQuat', 'aInfoA', 'aInfoB'])
|
|
461
|
+
geo.attributes[k].needsUpdate = true;
|
|
462
|
+
unitBounds(geo);
|
|
463
|
+
return geo;
|
|
464
|
+
}
|
|
465
|
+
// ---------------------------------------------------------------------------- assembling a piece
|
|
466
|
+
/** Merges simple part geometries (position, normal, uv) into one with aSkin / aPart / aCard. */
|
|
467
|
+
function mergeParts(parts) {
|
|
468
|
+
const pos = [], nrm = [], uv = [], skin = [], part = [], card = [], idx = [];
|
|
469
|
+
for (const p of parts) {
|
|
470
|
+
const base = pos.length / 3;
|
|
471
|
+
const g = p.geo, n = g.attributes.position.count;
|
|
472
|
+
for (let i = 0; i < n; i++) {
|
|
473
|
+
pos.push(g.attributes.position.getX(i), g.attributes.position.getY(i), g.attributes.position.getZ(i));
|
|
474
|
+
nrm.push(g.attributes.normal.getX(i), g.attributes.normal.getY(i), g.attributes.normal.getZ(i));
|
|
475
|
+
uv.push(g.attributes.uv ? g.attributes.uv.getX(i) : 0, g.attributes.uv ? g.attributes.uv.getY(i) : 0);
|
|
476
|
+
skin.push(g.attributes.aSkin ? g.attributes.aSkin.getX(i) : (p.t === undefined ? 0.5 : p.t), g.attributes.aSkin ? g.attributes.aSkin.getY(i) : (p.seed || 0));
|
|
477
|
+
part.push(p.part || 0, p.leafType || 0);
|
|
478
|
+
card.push(g.attributes.aCard ? g.attributes.aCard.getX(i) : 0, g.attributes.aCard ? g.attributes.aCard.getY(i) : 0);
|
|
479
|
+
}
|
|
480
|
+
const ix = g.index ? g.index.array : null;
|
|
481
|
+
if (ix)
|
|
482
|
+
for (let i = 0; i < ix.length; i++)
|
|
483
|
+
idx.push(base + ix[i]);
|
|
484
|
+
else
|
|
485
|
+
for (let i = 0; i < n; i++)
|
|
486
|
+
idx.push(base + i);
|
|
487
|
+
}
|
|
488
|
+
const geo = new THREE.BufferGeometry();
|
|
489
|
+
geo.setAttribute('position', new THREE.Float32BufferAttribute(pos, 3));
|
|
490
|
+
geo.setAttribute('normal', new THREE.Float32BufferAttribute(nrm, 3));
|
|
491
|
+
geo.setAttribute('uv', new THREE.Float32BufferAttribute(uv, 2));
|
|
492
|
+
geo.setAttribute('aSkin', new THREE.Float32BufferAttribute(skin, 2));
|
|
493
|
+
geo.setAttribute('aPart', new THREE.Float32BufferAttribute(part, 2));
|
|
494
|
+
geo.setAttribute('aCard', new THREE.Float32BufferAttribute(card, 2));
|
|
495
|
+
geo.setIndex(idx);
|
|
496
|
+
return geo;
|
|
497
|
+
}
|
|
498
|
+
/** A leaf card as a real quad in piece space (a 2×2 card scaled and framed), part 2. */
|
|
499
|
+
function cardGeo(pos, along, normal, size, seed) {
|
|
500
|
+
const y = along.clone().normalize();
|
|
501
|
+
const z = normal.clone().addScaledVector(y, -normal.dot(y)).normalize();
|
|
502
|
+
const x = new THREE.Vector3().crossVectors(y, z);
|
|
503
|
+
const g = new THREE.BufferGeometry();
|
|
504
|
+
const P = [], N = [], C = [];
|
|
505
|
+
for (const [cx, cy] of [[-1, -1], [1, -1], [1, 1], [-1, 1]]) {
|
|
506
|
+
const p = pos.clone().addScaledVector(x, cx * size).addScaledVector(y, (cy + 1) * size);
|
|
507
|
+
P.push(p.x, p.y, p.z);
|
|
508
|
+
N.push(z.x, z.y, z.z);
|
|
509
|
+
C.push(cx, cy);
|
|
510
|
+
}
|
|
511
|
+
g.setAttribute('position', new THREE.Float32BufferAttribute(P, 3));
|
|
512
|
+
g.setAttribute('normal', new THREE.Float32BufferAttribute(N, 3));
|
|
513
|
+
g.setAttribute('aCard', new THREE.Float32BufferAttribute(C, 2));
|
|
514
|
+
g.setIndex([0, 1, 2, 0, 2, 3]);
|
|
515
|
+
return { geo: g, part: 2, seed };
|
|
516
|
+
}
|
|
517
|
+
function stemGeo(from, dir, len, rad, bend, seg = 3) {
|
|
518
|
+
const g = new THREE.CylinderGeometry(rad * 0.7, rad, len, 7, seg || 3, false);
|
|
519
|
+
g.translate(0, len / 2, 0);
|
|
520
|
+
const p = g.attributes.position;
|
|
521
|
+
for (let i = 0; i < p.count; i++) {
|
|
522
|
+
const t = p.getY(i) / len;
|
|
523
|
+
p.setX(i, p.getX(i) + (bend || 0) * t * t * len);
|
|
524
|
+
}
|
|
525
|
+
g.computeVertexNormals();
|
|
526
|
+
const q = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), dir.clone().normalize());
|
|
527
|
+
g.applyQuaternion(q);
|
|
528
|
+
g.translate(from.x, from.y, from.z);
|
|
529
|
+
return { geo: g, part: 1, t: 2 };
|
|
530
|
+
}
|
|
531
|
+
function sphereAt(c, r, sx, sy, sz, detail) {
|
|
532
|
+
const g = new THREE.SphereGeometry(r, 10 * detail | 0, 7 * detail | 0);
|
|
533
|
+
g.scale(sx || 1, sy || 1, sz || 1);
|
|
534
|
+
g.translate(c.x, c.y, c.z);
|
|
535
|
+
return g;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* One complete piece of a kind in piece space (sits on y = 0, roughly unit tall or wide):
|
|
539
|
+
* the body (or cluster), its stem, and any leaf / calyx / crown / tops / husk cards.
|
|
540
|
+
* Returns { geo, height, width } (width = footprint).
|
|
541
|
+
*/
|
|
542
|
+
export function buildPiece(kind, seed, detail) {
|
|
543
|
+
const P = PRODUCE[kind];
|
|
544
|
+
const rnd = mulberry32(seed * 7919 + 13);
|
|
545
|
+
const parts = [];
|
|
546
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
547
|
+
const d = detail || 1;
|
|
548
|
+
let height = P.h, width = 1;
|
|
549
|
+
if (P.cluster) {
|
|
550
|
+
// grapes: spheroids packed in a hanging cone, the stalk above
|
|
551
|
+
const C = P.cluster, cs = [];
|
|
552
|
+
for (let i = 0; i < C.n; i++) {
|
|
553
|
+
const t = Math.pow(rnd(), 0.7); // more near the top
|
|
554
|
+
const r = C.cone * (1 - t * 0.85) * 0.85 * Math.sqrt(rnd());
|
|
555
|
+
const a = rnd() * TAU;
|
|
556
|
+
const c = new THREE.Vector3(Math.cos(a) * r, 0.12 + t * 0.8, Math.sin(a) * r);
|
|
557
|
+
// settle against neighbours
|
|
558
|
+
for (let k = 0; k < 2; k++)
|
|
559
|
+
for (const o of cs) {
|
|
560
|
+
const dd = c.distanceTo(o);
|
|
561
|
+
if (dd < C.rad * 1.7)
|
|
562
|
+
c.addScaledVector(c.clone().sub(o).normalize(), (C.rad * 1.7 - dd) * 0.5);
|
|
563
|
+
}
|
|
564
|
+
cs.push(c);
|
|
565
|
+
}
|
|
566
|
+
cs.sort((a, b) => b.y - a.y);
|
|
567
|
+
for (const c of cs) {
|
|
568
|
+
const g = sphereAt(c, C.rad * (0.85 + 0.3 * rnd()), 1, 1.05, 1, d);
|
|
569
|
+
const sk = new Float32Array(g.attributes.position.count * 2);
|
|
570
|
+
for (let i = 0; i < sk.length; i += 2) {
|
|
571
|
+
sk[i] = c.y;
|
|
572
|
+
sk[i + 1] = rnd();
|
|
573
|
+
}
|
|
574
|
+
g.setAttribute('aSkin', new THREE.Float32BufferAttribute(sk, 2));
|
|
575
|
+
parts.push({ geo: g, part: 0 });
|
|
576
|
+
}
|
|
577
|
+
if (P.stem[0] > 0)
|
|
578
|
+
parts.push(stemGeo(new THREE.Vector3(0, 0.9, 0), up, P.stem[0], P.stem[1], 0.3));
|
|
579
|
+
width = C.cone * 2;
|
|
580
|
+
height = 1.1;
|
|
581
|
+
}
|
|
582
|
+
else if (P.mushroom) {
|
|
583
|
+
// a mushroom: a thick stem, a cap (a flattened dome, rim curled under), gills as a dark underside disc
|
|
584
|
+
const M = P.mushroom;
|
|
585
|
+
parts.push(stemGeo(new THREE.Vector3(0, 0, 0), up, M.stemH, M.stemR, 0.15 * (rnd() - 0.5), 5));
|
|
586
|
+
const cap = new THREE.SphereGeometry(1, 28 * d | 0, 16 * d | 0, 0, TAU, 0, Math.PI * 0.62);
|
|
587
|
+
cap.scale(M.capR, M.capH, M.capR);
|
|
588
|
+
cap.translate(0, M.stemH - M.capH * 0.35, 0);
|
|
589
|
+
const cp = cap.attributes.position;
|
|
590
|
+
for (let i = 0; i < cp.count; i++) {
|
|
591
|
+
const x = cp.getX(i), z = cp.getZ(i), r = Math.hypot(x, z);
|
|
592
|
+
const bump = 1 + 0.03 * Math.sin(Math.atan2(z, x) * 9 + r * 20);
|
|
593
|
+
cp.setX(i, x * bump);
|
|
594
|
+
cp.setZ(i, z * bump);
|
|
595
|
+
}
|
|
596
|
+
cap.computeVertexNormals();
|
|
597
|
+
const skc = new Float32Array(cp.count * 2);
|
|
598
|
+
for (let i = 0; i < cp.count; i++) {
|
|
599
|
+
skc[i * 2] = Math.min(1, Math.hypot(cp.getX(i), cp.getZ(i)) / M.capR);
|
|
600
|
+
skc[i * 2 + 1] = rnd();
|
|
601
|
+
}
|
|
602
|
+
cap.setAttribute('aSkin', new THREE.Float32BufferAttribute(skc, 2));
|
|
603
|
+
parts.push({ geo: cap, part: 0 });
|
|
604
|
+
const gills = new THREE.CircleGeometry(M.capR * 0.95, 28 * d | 0);
|
|
605
|
+
gills.rotateX(Math.PI / 2);
|
|
606
|
+
gills.translate(0, M.stemH - M.capH * 0.2, 0);
|
|
607
|
+
parts.push({ geo: gills, part: 3, t: 0.5 });
|
|
608
|
+
width = M.capR * 2;
|
|
609
|
+
height = M.stemH + M.capH * 0.7;
|
|
610
|
+
}
|
|
611
|
+
else if (P.florets) {
|
|
612
|
+
// broccoli: a thick stalk forking into a dome of florets
|
|
613
|
+
const F = P.florets;
|
|
614
|
+
parts.push(stemGeo(new THREE.Vector3(0, 0, 0), up, P.stem[0], P.stem[1], 0, 4));
|
|
615
|
+
for (let i = 0; i < 6; i++) {
|
|
616
|
+
const a = i / 6 * TAU;
|
|
617
|
+
parts.push(stemGeo(new THREE.Vector3(0, P.stem[0] * 0.8, 0), new THREE.Vector3(Math.cos(a) * 0.5, 1, Math.sin(a) * 0.5), 0.3, P.stem[1] * 0.4, 0, 2));
|
|
618
|
+
}
|
|
619
|
+
for (let i = 0; i < F.n; i++) {
|
|
620
|
+
const u = rnd(), v = rnd();
|
|
621
|
+
const th = Math.acos(1 - u * 0.75), ph = v * TAU; // the top of a sphere
|
|
622
|
+
const c = new THREE.Vector3(Math.sin(th) * Math.cos(ph) * F.dome, 0.5 + Math.cos(th) * F.dome * 0.9, Math.sin(th) * Math.sin(ph) * F.dome);
|
|
623
|
+
const g = sphereAt(c, F.rad * (0.7 + 0.6 * rnd()), 1, 0.8, 1, d * 0.8);
|
|
624
|
+
const sk = new Float32Array(g.attributes.position.count * 2);
|
|
625
|
+
for (let k = 0; k < sk.length; k += 2) {
|
|
626
|
+
sk[k] = 0.5 + c.y * 0.3;
|
|
627
|
+
sk[k + 1] = rnd();
|
|
628
|
+
}
|
|
629
|
+
g.setAttribute('aSkin', new THREE.Float32BufferAttribute(sk, 2));
|
|
630
|
+
parts.push({ geo: g, part: 0 });
|
|
631
|
+
}
|
|
632
|
+
width = F.dome * 2.2;
|
|
633
|
+
height = 0.95;
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
const body = produceBody(kind, seed, d);
|
|
637
|
+
let bodies = [body];
|
|
638
|
+
if (P.pairs) {
|
|
639
|
+
const b2 = produceBody(kind, seed + 1, d);
|
|
640
|
+
body.geo.translate(-0.42, 0, 0);
|
|
641
|
+
b2.geo.translate(0.42, 0, 0.1);
|
|
642
|
+
bodies = [body, b2];
|
|
643
|
+
parts.push(stemGeo(new THREE.Vector3(-0.42, P.h * 0.98, 0), new THREE.Vector3(0.45, 1, 0), P.stem[0], P.stem[1], -0.2));
|
|
644
|
+
parts.push(stemGeo(new THREE.Vector3(0.42, P.h * 0.98, 0.1), new THREE.Vector3(-0.45, 1, -0.1), P.stem[0], P.stem[1], 0.2));
|
|
645
|
+
width = 1.6;
|
|
646
|
+
}
|
|
647
|
+
for (const b of bodies)
|
|
648
|
+
parts.push({ geo: b.geo, part: 0 });
|
|
649
|
+
width = Math.max(width, body.width);
|
|
650
|
+
if (P.invert) {
|
|
651
|
+
for (const p of parts) {
|
|
652
|
+
p.geo.rotateX(Math.PI);
|
|
653
|
+
p.geo.translate(0, P.h, 0);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
const top = P.invert ? new THREE.Vector3(body.bottom.x, P.h - body.bottom.y, body.bottom.z) : body.top;
|
|
657
|
+
if (!P.pairs && P.stem[0] > 0)
|
|
658
|
+
parts.push(stemGeo(top.clone().add(new THREE.Vector3(0, -0.01, 0)), new THREE.Vector3(0.15 * (rnd() - 0.5), 1, 0.15 * (rnd() - 0.5)), P.stem[0], P.stem[1], 0.3 * (rnd() - 0.5)));
|
|
659
|
+
const stemTop = top.clone().add(new THREE.Vector3(0, P.stem[0] * 0.9, 0));
|
|
660
|
+
if (P.leaf)
|
|
661
|
+
parts.push(Object.assign(cardGeo(stemTop, new THREE.Vector3(0.7, 0.5, 0.2).normalize(), new THREE.Vector3(0.2, 1, 0.3), 0.16, rnd()), { leafType: LEAF_TYPES.indexOf('broadleaf') }));
|
|
662
|
+
if (P.calyx)
|
|
663
|
+
for (let i = 0; i < P.calyx; i++) {
|
|
664
|
+
const a = i / P.calyx * TAU + rnd() * 0.4;
|
|
665
|
+
// sepals: spreading (tomato), flat and broad (persimmon) or a standing crown (pomegranate)
|
|
666
|
+
const along = P.calyxUp ? new THREE.Vector3(Math.cos(a) * 0.5, 1, Math.sin(a) * 0.5) : new THREE.Vector3(Math.cos(a), P.calyxFlat ? -0.05 : 0.15, Math.sin(a));
|
|
667
|
+
const nrm = P.calyxUp ? new THREE.Vector3(Math.cos(a), 0.3, Math.sin(a)) : up.clone();
|
|
668
|
+
parts.push(Object.assign(cardGeo(top.clone().add(new THREE.Vector3(0, -0.01, 0)), along, nrm, (P.calyxFlat ? 0.17 : P.calyxUp ? 0.09 : 0.11) + 0.04 * rnd(), rnd()), { leafType: LEAF_TYPES.indexOf(P.calyxFlat ? 'round' : 'lanceolate') }));
|
|
669
|
+
}
|
|
670
|
+
// wrapper leaves (cabbage, cauliflower): big round leaves cupped around the head from the base
|
|
671
|
+
if (P.wrap)
|
|
672
|
+
for (let i = 0; i < P.wrap; i++) {
|
|
673
|
+
const a = i / P.wrap * TAU + rnd() * 0.5, r = body.width * 0.42;
|
|
674
|
+
parts.push(Object.assign(cardGeo(new THREE.Vector3(Math.cos(a) * r, 0.04, Math.sin(a) * r), new THREE.Vector3(Math.cos(a) * 0.55, 1, Math.sin(a) * 0.55), new THREE.Vector3(Math.cos(a), -0.25, Math.sin(a)), 0.2 + 0.06 * rnd(), rnd()), { leafType: LEAF_TYPES.indexOf('round') }));
|
|
675
|
+
}
|
|
676
|
+
// dragon fruit: scale tips sticking out over the body
|
|
677
|
+
if (P.scaleTips)
|
|
678
|
+
for (let i = 0; i < P.scaleTips; i++) {
|
|
679
|
+
const a = i * 2.4 + rnd() * 0.3, tt = 0.15 + 0.75 * (i / P.scaleTips);
|
|
680
|
+
const r = body.width * 0.5 * 0.92 * Math.pow(Math.sin(Math.PI * tt), 0.5);
|
|
681
|
+
const c = new THREE.Vector3(Math.cos(a) * r, tt * P.h, Math.sin(a) * r);
|
|
682
|
+
parts.push(Object.assign(cardGeo(c, new THREE.Vector3(Math.cos(a) * 0.5, 1, Math.sin(a) * 0.5), new THREE.Vector3(Math.cos(a), 0.2, Math.sin(a)), 0.09 + 0.04 * rnd(), rnd()), { leafType: LEAF_TYPES.indexOf('lanceolate') }));
|
|
683
|
+
}
|
|
684
|
+
// wheat: awns, long thin blades fanning from the head
|
|
685
|
+
if (P.awns)
|
|
686
|
+
for (let i = 0; i < 14; i++) {
|
|
687
|
+
const a = i / 14 * TAU, tt = 0.3 + 0.65 * rnd();
|
|
688
|
+
parts.push(Object.assign(cardGeo(new THREE.Vector3(Math.cos(a) * 0.06, tt, Math.sin(a) * 0.06), new THREE.Vector3(Math.cos(a) * 0.25, 1, Math.sin(a) * 0.25), new THREE.Vector3(Math.cos(a), 0, Math.sin(a)), 0.25, rnd()), { leafType: LEAF_TYPES.indexOf('blade') }));
|
|
689
|
+
}
|
|
690
|
+
// sunflower: ray florets around the disc
|
|
691
|
+
if (P.rays)
|
|
692
|
+
for (let i = 0; i < P.rays; i++) {
|
|
693
|
+
const a = i / P.rays * TAU + rnd() * 0.1, r = body.width * 0.47;
|
|
694
|
+
parts.push(Object.assign(cardGeo(new THREE.Vector3(Math.cos(a) * r, P.h * 0.8, Math.sin(a) * r), new THREE.Vector3(Math.cos(a), 0.1 + 0.2 * rnd(), Math.sin(a)), up, 0.19 + 0.04 * rnd(), rnd()), { leafType: LEAF_TYPES.indexOf('petal'), part: 4 }));
|
|
695
|
+
}
|
|
696
|
+
if (P.crown)
|
|
697
|
+
for (let i = 0; i < P.crown; i++) {
|
|
698
|
+
const a = i / P.crown * TAU + rnd() * 0.5, r = 0.05 + 0.06 * rnd();
|
|
699
|
+
parts.push(Object.assign(cardGeo(top.clone().add(new THREE.Vector3(Math.cos(a) * r, 0, Math.sin(a) * r)), new THREE.Vector3(Math.cos(a) * (0.3 + 0.5 * rnd()), 1, Math.sin(a) * (0.3 + 0.5 * rnd())), new THREE.Vector3(Math.cos(a), 0.2, Math.sin(a)), 0.2 + 0.12 * rnd(), rnd()), { leafType: LEAF_TYPES.indexOf('blade') }));
|
|
700
|
+
height = P.h + 0.5;
|
|
701
|
+
}
|
|
702
|
+
if (P.tops)
|
|
703
|
+
for (let i = 0; i < P.tops; i++) {
|
|
704
|
+
const a = i / P.tops * TAU + rnd() * 0.5;
|
|
705
|
+
parts.push(Object.assign(cardGeo(top.clone(), new THREE.Vector3(Math.cos(a) * (0.25 + 0.3 * rnd()), 1, Math.sin(a) * (0.25 + 0.3 * rnd())), new THREE.Vector3(Math.cos(a), 0.3, Math.sin(a)), (P.topsBlade ? 0.3 : 0.16) + 0.1 * rnd(), rnd()), { leafType: LEAF_TYPES.indexOf(P.topsBlade ? 'blade' : (kind === 'radish' || kind === 'beet' || kind === 'turnip') ? 'round' : 'frond') }));
|
|
706
|
+
height = P.h + 0.4;
|
|
707
|
+
}
|
|
708
|
+
if (P.husk)
|
|
709
|
+
for (let i = 0; i < P.husk; i++) {
|
|
710
|
+
const a = i / P.husk * TAU + rnd() * 0.3, r = body.width * 0.55;
|
|
711
|
+
parts.push(Object.assign(cardGeo(new THREE.Vector3(Math.cos(a) * r * 0.6, 0.02, Math.sin(a) * r * 0.6), new THREE.Vector3(Math.cos(a) * (0.35 + 0.15 * rnd()), 1, Math.sin(a) * (0.35 + 0.15 * rnd())), new THREE.Vector3(Math.cos(a), 0.1, Math.sin(a)), 0.42 + 0.08 * rnd(), rnd()), { leafType: LEAF_TYPES.indexOf('blade') }));
|
|
712
|
+
}
|
|
713
|
+
if (P.roots)
|
|
714
|
+
for (let i = 0; i < 8; i++) {
|
|
715
|
+
const a = rnd() * TAU;
|
|
716
|
+
parts.push(stemGeo(new THREE.Vector3(Math.cos(a) * 0.05, 0.02, Math.sin(a) * 0.05), new THREE.Vector3(Math.cos(a) * 0.6, -0.5, Math.sin(a) * 0.6), 0.1 + 0.06 * rnd(), 0.006, 0, 1));
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const geo = mergeParts(parts);
|
|
720
|
+
for (const p of parts)
|
|
721
|
+
p.geo.dispose();
|
|
722
|
+
return { geo, height, width };
|
|
723
|
+
}
|