@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.
Files changed (408) hide show
  1. package/dist/animationsV2/ClipsRuntime.d.ts +16 -1
  2. package/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
  3. package/dist/animationsV2/ClipsRuntime.js +144 -25
  4. package/dist/animationsV2/appliers.d.ts +9 -0
  5. package/dist/animationsV2/appliers.d.ts.map +1 -1
  6. package/dist/animationsV2/appliers.js +62 -2
  7. package/dist/animationsV2/clipUtils.d.ts +30 -0
  8. package/dist/animationsV2/clipUtils.d.ts.map +1 -1
  9. package/dist/animationsV2/clipUtils.js +163 -17
  10. package/dist/animationsV2/elementSettings.d.ts +5 -0
  11. package/dist/animationsV2/elementSettings.d.ts.map +1 -1
  12. package/dist/animationsV2/elementSettings.js +6 -3
  13. package/dist/animationsV2/sequenceUtils.js +4 -4
  14. package/dist/animationsV2/types.d.ts +23 -0
  15. package/dist/animationsV2/types.d.ts.map +1 -1
  16. package/dist/api/sceneHandle.js +2 -2
  17. package/dist/components/CreatedObject.d.ts +3 -0
  18. package/dist/components/CreatedObject.d.ts.map +1 -1
  19. package/dist/components/CreatedObject.js +6 -2
  20. package/dist/components/CustomPrimitive.d.ts.map +1 -1
  21. package/dist/components/CustomPrimitive.js +1 -10
  22. package/dist/components/EnvironmentManager.d.ts.map +1 -1
  23. package/dist/components/EnvironmentManager.js +11 -5
  24. package/dist/components/GeometryShape.d.ts +5 -2
  25. package/dist/components/GeometryShape.d.ts.map +1 -1
  26. package/dist/components/GeometryShape.js +71 -10
  27. package/dist/components/LandObject.d.ts.map +1 -1
  28. package/dist/components/LandObject.js +1 -1
  29. package/dist/components/LayoutInstances.d.ts +2 -1
  30. package/dist/components/LayoutInstances.d.ts.map +1 -1
  31. package/dist/components/LayoutInstances.js +39 -6
  32. package/dist/components/LightObject.d.ts.map +1 -1
  33. package/dist/components/LightObject.js +3 -0
  34. package/dist/components/ObjectNode.d.ts.map +1 -1
  35. package/dist/components/ObjectNode.js +62 -13
  36. package/dist/components/OceanBuilder.d.ts +2 -0
  37. package/dist/components/OceanBuilder.d.ts.map +1 -1
  38. package/dist/components/OceanBuilder.js +2 -1
  39. package/dist/components/RenderEffects.d.ts +8 -0
  40. package/dist/components/RenderEffects.d.ts.map +1 -0
  41. package/dist/components/RenderEffects.js +120 -0
  42. package/dist/components/SceneBuilder.d.ts.map +1 -1
  43. package/dist/components/SceneBuilder.js +18 -9
  44. package/dist/components/SportsObject.d.ts +9 -0
  45. package/dist/components/SportsObject.d.ts.map +1 -0
  46. package/dist/components/SportsObject.js +20 -0
  47. package/dist/components/UrbanObject.d.ts +9 -0
  48. package/dist/components/UrbanObject.d.ts.map +1 -0
  49. package/dist/components/UrbanObject.js +28 -0
  50. package/dist/components/VegetationObject.d.ts +9 -0
  51. package/dist/components/VegetationObject.d.ts.map +1 -0
  52. package/dist/components/VegetationObject.js +26 -0
  53. package/dist/components/WaterObject.d.ts +15 -0
  54. package/dist/components/WaterObject.d.ts.map +1 -0
  55. package/dist/components/WaterObject.js +29 -0
  56. package/dist/components/index.d.ts +7 -2
  57. package/dist/components/index.d.ts.map +1 -1
  58. package/dist/components/index.js +6 -1
  59. package/dist/components/land/LandPatch.d.ts +3 -1
  60. package/dist/components/land/LandPatch.d.ts.map +1 -1
  61. package/dist/components/land/LandPatch.js +80 -7
  62. package/dist/components/layout/InstancedLayout.d.ts +19 -0
  63. package/dist/components/layout/InstancedLayout.d.ts.map +1 -0
  64. package/dist/components/layout/InstancedLayout.js +154 -0
  65. package/dist/components/layout/LayoutGrounds.d.ts +19 -0
  66. package/dist/components/layout/LayoutGrounds.d.ts.map +1 -0
  67. package/dist/components/layout/LayoutGrounds.js +104 -0
  68. package/dist/components/layout/LinkProxies.d.ts +5 -0
  69. package/dist/components/layout/LinkProxies.d.ts.map +1 -0
  70. package/dist/components/layout/LinkProxies.js +51 -0
  71. package/dist/components/layout/LinkedSource.d.ts +14 -0
  72. package/dist/components/layout/LinkedSource.d.ts.map +1 -0
  73. package/dist/components/layout/LinkedSource.js +129 -0
  74. package/dist/components/projector/Projector.d.ts +14 -0
  75. package/dist/components/projector/Projector.d.ts.map +1 -0
  76. package/dist/components/projector/Projector.js +44 -0
  77. package/dist/components/projector/index.d.ts +4 -0
  78. package/dist/components/projector/index.d.ts.map +1 -0
  79. package/dist/components/projector/index.js +2 -0
  80. package/dist/components/projector/projectorCore.d.ts +19 -0
  81. package/dist/components/projector/projectorCore.d.ts.map +1 -0
  82. package/dist/components/projector/projectorCore.js +545 -0
  83. package/dist/components/sports/SportsBall.d.ts +9 -0
  84. package/dist/components/sports/SportsBall.d.ts.map +1 -0
  85. package/dist/components/sports/SportsBall.js +23 -0
  86. package/dist/components/sports/SportsCourt.d.ts +9 -0
  87. package/dist/components/sports/SportsCourt.d.ts.map +1 -0
  88. package/dist/components/sports/SportsCourt.js +23 -0
  89. package/dist/components/sports/SportsEquipment.d.ts +9 -0
  90. package/dist/components/sports/SportsEquipment.d.ts.map +1 -0
  91. package/dist/components/sports/SportsEquipment.js +23 -0
  92. package/dist/components/sports/SportsGoal.d.ts +9 -0
  93. package/dist/components/sports/SportsGoal.d.ts.map +1 -0
  94. package/dist/components/sports/SportsGoal.js +23 -0
  95. package/dist/components/sports/core/ball.d.ts +166 -0
  96. package/dist/components/sports/core/ball.d.ts.map +1 -0
  97. package/dist/components/sports/core/ball.js +402 -0
  98. package/dist/components/sports/core/court.d.ts +442 -0
  99. package/dist/components/sports/core/court.d.ts.map +1 -0
  100. package/dist/components/sports/core/court.js +658 -0
  101. package/dist/components/sports/core/equipment.d.ts +103 -0
  102. package/dist/components/sports/core/equipment.d.ts.map +1 -0
  103. package/dist/components/sports/core/equipment.js +403 -0
  104. package/dist/components/sports/core/goal.d.ts +98 -0
  105. package/dist/components/sports/core/goal.d.ts.map +1 -0
  106. package/dist/components/sports/core/goal.js +44 -0
  107. package/dist/components/sports/index.d.ts +11 -0
  108. package/dist/components/sports/index.d.ts.map +1 -0
  109. package/dist/components/sports/index.js +8 -0
  110. package/dist/components/sports/sportsCore.d.ts +195 -0
  111. package/dist/components/sports/sportsCore.d.ts.map +1 -0
  112. package/dist/components/sports/sportsCore.js +758 -0
  113. package/dist/components/sports/useSportsInstance.d.ts +35 -0
  114. package/dist/components/sports/useSportsInstance.d.ts.map +1 -0
  115. package/dist/components/sports/useSportsInstance.js +47 -0
  116. package/dist/components/terrain/TerrainSurface.d.ts.map +1 -1
  117. package/dist/components/terrain/TerrainSurface.js +14 -0
  118. package/dist/components/urban/UrbanBlock.d.ts +9 -0
  119. package/dist/components/urban/UrbanBlock.d.ts.map +1 -0
  120. package/dist/components/urban/UrbanBlock.js +23 -0
  121. package/dist/components/urban/UrbanBuilding.d.ts +9 -0
  122. package/dist/components/urban/UrbanBuilding.d.ts.map +1 -0
  123. package/dist/components/urban/UrbanBuilding.js +23 -0
  124. package/dist/components/urban/UrbanJunction.d.ts +9 -0
  125. package/dist/components/urban/UrbanJunction.d.ts.map +1 -0
  126. package/dist/components/urban/UrbanJunction.js +23 -0
  127. package/dist/components/urban/UrbanPark.d.ts +9 -0
  128. package/dist/components/urban/UrbanPark.d.ts.map +1 -0
  129. package/dist/components/urban/UrbanPark.js +23 -0
  130. package/dist/components/urban/UrbanParkingLot.d.ts +9 -0
  131. package/dist/components/urban/UrbanParkingLot.d.ts.map +1 -0
  132. package/dist/components/urban/UrbanParkingLot.js +23 -0
  133. package/dist/components/urban/UrbanPlaza.d.ts +9 -0
  134. package/dist/components/urban/UrbanPlaza.d.ts.map +1 -0
  135. package/dist/components/urban/UrbanPlaza.js +23 -0
  136. package/dist/components/urban/UrbanRoad.d.ts +9 -0
  137. package/dist/components/urban/UrbanRoad.d.ts.map +1 -0
  138. package/dist/components/urban/UrbanRoad.js +23 -0
  139. package/dist/components/urban/UrbanSignage.d.ts +9 -0
  140. package/dist/components/urban/UrbanSignage.d.ts.map +1 -0
  141. package/dist/components/urban/UrbanSignage.js +23 -0
  142. package/dist/components/urban/UrbanSkyline.d.ts +9 -0
  143. package/dist/components/urban/UrbanSkyline.d.ts.map +1 -0
  144. package/dist/components/urban/UrbanSkyline.js +23 -0
  145. package/dist/components/urban/UrbanStreetFurniture.d.ts +9 -0
  146. package/dist/components/urban/UrbanStreetFurniture.d.ts.map +1 -0
  147. package/dist/components/urban/UrbanStreetFurniture.js +23 -0
  148. package/dist/components/urban/UrbanStreetGrid.d.ts +9 -0
  149. package/dist/components/urban/UrbanStreetGrid.d.ts.map +1 -0
  150. package/dist/components/urban/UrbanStreetGrid.js +34 -0
  151. package/dist/components/urban/UrbanTraffic.d.ts +9 -0
  152. package/dist/components/urban/UrbanTraffic.d.ts.map +1 -0
  153. package/dist/components/urban/UrbanTraffic.js +23 -0
  154. package/dist/components/urban/core/block.d.ts +119 -0
  155. package/dist/components/urban/core/block.d.ts.map +1 -0
  156. package/dist/components/urban/core/block.js +266 -0
  157. package/dist/components/urban/core/building.d.ts +128 -0
  158. package/dist/components/urban/core/building.d.ts.map +1 -0
  159. package/dist/components/urban/core/building.js +741 -0
  160. package/dist/components/urban/core/junction.d.ts +152 -0
  161. package/dist/components/urban/core/junction.d.ts.map +1 -0
  162. package/dist/components/urban/core/junction.js +617 -0
  163. package/dist/components/urban/core/park.d.ts +67 -0
  164. package/dist/components/urban/core/park.d.ts.map +1 -0
  165. package/dist/components/urban/core/park.js +176 -0
  166. package/dist/components/urban/core/parkingLot.d.ts +82 -0
  167. package/dist/components/urban/core/parkingLot.d.ts.map +1 -0
  168. package/dist/components/urban/core/parkingLot.js +284 -0
  169. package/dist/components/urban/core/plaza.d.ts +24 -0
  170. package/dist/components/urban/core/plaza.d.ts.map +1 -0
  171. package/dist/components/urban/core/plaza.js +243 -0
  172. package/dist/components/urban/core/road.d.ts +113 -0
  173. package/dist/components/urban/core/road.d.ts.map +1 -0
  174. package/dist/components/urban/core/road.js +347 -0
  175. package/dist/components/urban/core/signage.d.ts +83 -0
  176. package/dist/components/urban/core/signage.d.ts.map +1 -0
  177. package/dist/components/urban/core/signage.js +209 -0
  178. package/dist/components/urban/core/skyline.d.ts +100 -0
  179. package/dist/components/urban/core/skyline.d.ts.map +1 -0
  180. package/dist/components/urban/core/skyline.js +89 -0
  181. package/dist/components/urban/core/streetFurniture.d.ts +75 -0
  182. package/dist/components/urban/core/streetFurniture.d.ts.map +1 -0
  183. package/dist/components/urban/core/streetFurniture.js +193 -0
  184. package/dist/components/urban/core/streetGrid.d.ts +134 -0
  185. package/dist/components/urban/core/streetGrid.d.ts.map +1 -0
  186. package/dist/components/urban/core/streetGrid.js +422 -0
  187. package/dist/components/urban/core/traffic.d.ts +125 -0
  188. package/dist/components/urban/core/traffic.d.ts.map +1 -0
  189. package/dist/components/urban/core/traffic.js +305 -0
  190. package/dist/components/urban/index.d.ts +29 -0
  191. package/dist/components/urban/index.d.ts.map +1 -0
  192. package/dist/components/urban/index.js +16 -0
  193. package/dist/components/urban/textureSetResolver.d.ts +8 -0
  194. package/dist/components/urban/textureSetResolver.d.ts.map +1 -0
  195. package/dist/components/urban/textureSetResolver.js +16 -0
  196. package/dist/components/urban/urbanCore.d.ts +262 -0
  197. package/dist/components/urban/urbanCore.d.ts.map +1 -0
  198. package/dist/components/urban/urbanCore.js +928 -0
  199. package/dist/components/urban/useUrbanInstance.d.ts +53 -0
  200. package/dist/components/urban/useUrbanInstance.d.ts.map +1 -0
  201. package/dist/components/urban/useUrbanInstance.js +100 -0
  202. package/dist/components/vegetation/VegetationBush.d.ts +9 -0
  203. package/dist/components/vegetation/VegetationBush.d.ts.map +1 -0
  204. package/dist/components/vegetation/VegetationBush.js +23 -0
  205. package/dist/components/vegetation/VegetationCrops.d.ts +9 -0
  206. package/dist/components/vegetation/VegetationCrops.d.ts.map +1 -0
  207. package/dist/components/vegetation/VegetationCrops.js +23 -0
  208. package/dist/components/vegetation/VegetationFlowers.d.ts +9 -0
  209. package/dist/components/vegetation/VegetationFlowers.d.ts.map +1 -0
  210. package/dist/components/vegetation/VegetationFlowers.js +23 -0
  211. package/dist/components/vegetation/VegetationForest.d.ts +9 -0
  212. package/dist/components/vegetation/VegetationForest.d.ts.map +1 -0
  213. package/dist/components/vegetation/VegetationForest.js +23 -0
  214. package/dist/components/vegetation/VegetationFruit.d.ts +9 -0
  215. package/dist/components/vegetation/VegetationFruit.d.ts.map +1 -0
  216. package/dist/components/vegetation/VegetationFruit.js +23 -0
  217. package/dist/components/vegetation/VegetationGrass.d.ts +9 -0
  218. package/dist/components/vegetation/VegetationGrass.d.ts.map +1 -0
  219. package/dist/components/vegetation/VegetationGrass.js +23 -0
  220. package/dist/components/vegetation/VegetationHedge.d.ts +9 -0
  221. package/dist/components/vegetation/VegetationHedge.d.ts.map +1 -0
  222. package/dist/components/vegetation/VegetationHedge.js +23 -0
  223. package/dist/components/vegetation/VegetationIvy.d.ts +9 -0
  224. package/dist/components/vegetation/VegetationIvy.d.ts.map +1 -0
  225. package/dist/components/vegetation/VegetationIvy.js +23 -0
  226. package/dist/components/vegetation/VegetationTree.d.ts +9 -0
  227. package/dist/components/vegetation/VegetationTree.d.ts.map +1 -0
  228. package/dist/components/vegetation/VegetationTree.js +23 -0
  229. package/dist/components/vegetation/VegetationVegetable.d.ts +9 -0
  230. package/dist/components/vegetation/VegetationVegetable.d.ts.map +1 -0
  231. package/dist/components/vegetation/VegetationVegetable.js +23 -0
  232. package/dist/components/vegetation/core/bush.d.ts +1256 -0
  233. package/dist/components/vegetation/core/bush.d.ts.map +1 -0
  234. package/dist/components/vegetation/core/bush.js +376 -0
  235. package/dist/components/vegetation/core/crops.d.ts +1033 -0
  236. package/dist/components/vegetation/core/crops.d.ts.map +1 -0
  237. package/dist/components/vegetation/core/crops.js +378 -0
  238. package/dist/components/vegetation/core/flowers.d.ts +648 -0
  239. package/dist/components/vegetation/core/flowers.d.ts.map +1 -0
  240. package/dist/components/vegetation/core/flowers.js +225 -0
  241. package/dist/components/vegetation/core/forest.d.ts +138 -0
  242. package/dist/components/vegetation/core/forest.d.ts.map +1 -0
  243. package/dist/components/vegetation/core/forest.js +310 -0
  244. package/dist/components/vegetation/core/fruit.d.ts +99 -0
  245. package/dist/components/vegetation/core/fruit.d.ts.map +1 -0
  246. package/dist/components/vegetation/core/fruit.js +77 -0
  247. package/dist/components/vegetation/core/grass.d.ts +103 -0
  248. package/dist/components/vegetation/core/grass.d.ts.map +1 -0
  249. package/dist/components/vegetation/core/grass.js +102 -0
  250. package/dist/components/vegetation/core/hedge.d.ts +193 -0
  251. package/dist/components/vegetation/core/hedge.d.ts.map +1 -0
  252. package/dist/components/vegetation/core/hedge.js +243 -0
  253. package/dist/components/vegetation/core/ivy.d.ts +213 -0
  254. package/dist/components/vegetation/core/ivy.d.ts.map +1 -0
  255. package/dist/components/vegetation/core/ivy.js +173 -0
  256. package/dist/components/vegetation/core/tree.d.ts +1117 -0
  257. package/dist/components/vegetation/core/tree.d.ts.map +1 -0
  258. package/dist/components/vegetation/core/tree.js +408 -0
  259. package/dist/components/vegetation/core/vegetable.d.ts +99 -0
  260. package/dist/components/vegetation/core/vegetable.d.ts.map +1 -0
  261. package/dist/components/vegetation/core/vegetable.js +76 -0
  262. package/dist/components/vegetation/index.d.ts +23 -0
  263. package/dist/components/vegetation/index.d.ts.map +1 -0
  264. package/dist/components/vegetation/index.js +14 -0
  265. package/dist/components/vegetation/produceCore.d.ts +929 -0
  266. package/dist/components/vegetation/produceCore.d.ts.map +1 -0
  267. package/dist/components/vegetation/produceCore.js +723 -0
  268. package/dist/components/vegetation/useVegetationInstance.d.ts +39 -0
  269. package/dist/components/vegetation/useVegetationInstance.d.ts.map +1 -0
  270. package/dist/components/vegetation/useVegetationInstance.js +45 -0
  271. package/dist/components/vegetation/vegetationCore.d.ts +412 -0
  272. package/dist/components/vegetation/vegetationCore.d.ts.map +1 -0
  273. package/dist/components/vegetation/vegetationCore.js +1728 -0
  274. package/dist/components/volumetricClouds/VolumetricCloudsPass.d.ts +5 -0
  275. package/dist/components/volumetricClouds/VolumetricCloudsPass.d.ts.map +1 -1
  276. package/dist/components/volumetricClouds/VolumetricCloudsPass.js +39 -13
  277. package/dist/components/water/OceanSurface.d.ts.map +1 -1
  278. package/dist/components/water/OceanSurface.js +3 -2
  279. package/dist/components/water/WaterBody.d.ts.map +1 -1
  280. package/dist/components/water/WaterBody.js +31 -10
  281. package/dist/components/water/WaterFountain.d.ts +9 -0
  282. package/dist/components/water/WaterFountain.d.ts.map +1 -0
  283. package/dist/components/water/WaterFountain.js +24 -0
  284. package/dist/components/water/WaterStream.d.ts +9 -0
  285. package/dist/components/water/WaterStream.d.ts.map +1 -0
  286. package/dist/components/water/WaterStream.js +24 -0
  287. package/dist/components/water/WaterWaterfall.d.ts +9 -0
  288. package/dist/components/water/WaterWaterfall.d.ts.map +1 -0
  289. package/dist/components/water/WaterWaterfall.js +24 -0
  290. package/dist/components/water/core/fountain.d.ts +70 -0
  291. package/dist/components/water/core/fountain.d.ts.map +1 -0
  292. package/dist/components/water/core/fountain.js +619 -0
  293. package/dist/components/water/core/stream.d.ts +72 -0
  294. package/dist/components/water/core/stream.d.ts.map +1 -0
  295. package/dist/components/water/core/stream.js +167 -0
  296. package/dist/components/water/core/waterfall.d.ts +78 -0
  297. package/dist/components/water/core/waterfall.d.ts.map +1 -0
  298. package/dist/components/water/core/waterfall.js +325 -0
  299. package/dist/components/water/index.d.ts +8 -0
  300. package/dist/components/water/index.d.ts.map +1 -1
  301. package/dist/components/water/index.js +7 -0
  302. package/dist/components/water/types.d.ts +3 -0
  303. package/dist/components/water/types.d.ts.map +1 -1
  304. package/dist/components/water/useWaterInstance.d.ts +43 -0
  305. package/dist/components/water/useWaterInstance.d.ts.map +1 -0
  306. package/dist/components/water/useWaterInstance.js +89 -0
  307. package/dist/components/water/waterCore.d.ts +179 -0
  308. package/dist/components/water/waterCore.d.ts.map +1 -0
  309. package/dist/components/water/waterCore.js +516 -0
  310. package/dist/constants.d.ts.map +1 -1
  311. package/dist/constants.js +9 -1
  312. package/dist/hooks/index.d.ts +7 -2
  313. package/dist/hooks/index.d.ts.map +1 -1
  314. package/dist/hooks/index.js +5 -2
  315. package/dist/hooks/useActionPatch.d.ts +23 -0
  316. package/dist/hooks/useActionPatch.d.ts.map +1 -0
  317. package/dist/hooks/useActionPatch.js +38 -0
  318. package/dist/hooks/useCloudSun.d.ts.map +1 -1
  319. package/dist/hooks/useCloudSun.js +3 -2
  320. package/dist/hooks/useCommittedInstance.d.ts +6 -0
  321. package/dist/hooks/useCommittedInstance.d.ts.map +1 -0
  322. package/dist/hooks/useCommittedInstance.js +42 -0
  323. package/dist/hooks/useConfigLive.d.ts +1 -1
  324. package/dist/hooks/useConfigLive.d.ts.map +1 -1
  325. package/dist/hooks/useContinuousEffects.d.ts.map +1 -1
  326. package/dist/hooks/useContinuousEffects.js +36 -20
  327. package/dist/hooks/useGridLayout.d.ts +7 -0
  328. package/dist/hooks/useGridLayout.d.ts.map +1 -0
  329. package/dist/hooks/useGridLayout.js +10 -0
  330. package/dist/hooks/useInteractionEffects.d.ts.map +1 -1
  331. package/dist/hooks/useInteractionEffects.js +48 -9
  332. package/dist/hooks/useLightLive.d.ts.map +1 -1
  333. package/dist/hooks/useLightLive.js +2 -0
  334. package/dist/hooks/useLinkStatus.d.ts +7 -0
  335. package/dist/hooks/useLinkStatus.d.ts.map +1 -0
  336. package/dist/hooks/useLinkStatus.js +20 -0
  337. package/dist/hooks/useMaterialApplication.d.ts +6 -1
  338. package/dist/hooks/useMaterialApplication.d.ts.map +1 -1
  339. package/dist/hooks/useMaterialApplication.js +7 -2
  340. package/dist/hooks/useOceanMaterial.d.ts +11 -1
  341. package/dist/hooks/useOceanMaterial.d.ts.map +1 -1
  342. package/dist/hooks/useOceanMaterial.js +185 -27
  343. package/dist/hooks/usePathAnimation.d.ts +8 -0
  344. package/dist/hooks/usePathAnimation.d.ts.map +1 -1
  345. package/dist/hooks/usePathAnimation.js +140 -59
  346. package/dist/hooks/useResolvedLayout.d.ts +10 -0
  347. package/dist/hooks/useResolvedLayout.d.ts.map +1 -0
  348. package/dist/hooks/useResolvedLayout.js +17 -0
  349. package/dist/hooks/useTimeLighting.d.ts +36 -0
  350. package/dist/hooks/useTimeLighting.d.ts.map +1 -0
  351. package/dist/hooks/useTimeLighting.js +67 -0
  352. package/dist/index.d.ts +4 -0
  353. package/dist/index.d.ts.map +1 -1
  354. package/dist/index.js +4 -0
  355. package/dist/operations/catalogue/shaderLayers.d.ts.map +1 -1
  356. package/dist/operations/catalogue/shaderLayers.js +26 -3
  357. package/dist/render/renderLive.d.ts +11 -0
  358. package/dist/render/renderLive.d.ts.map +1 -0
  359. package/dist/render/renderLive.js +57 -0
  360. package/dist/shaders/glsl/oceanCore.glsl.d.ts +2 -2
  361. package/dist/shaders/glsl/oceanCore.glsl.d.ts.map +1 -1
  362. package/dist/shaders/glsl/oceanCore.glsl.js +35 -5
  363. package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts +12 -0
  364. package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts.map +1 -1
  365. package/dist/shaders/ocean/OceanSurfaceMaterial.js +55 -7
  366. package/dist/shaders/terrain/TerrainMaterial.d.ts.map +1 -1
  367. package/dist/shaders/terrain/TerrainMaterial.js +28 -0
  368. package/dist/triggers/kinds/change.js +1 -1
  369. package/dist/utils/composerHostedPass.d.ts +74 -0
  370. package/dist/utils/composerHostedPass.d.ts.map +1 -0
  371. package/dist/utils/composerHostedPass.js +63 -0
  372. package/dist/utils/geometryScript.d.ts +17 -0
  373. package/dist/utils/geometryScript.d.ts.map +1 -0
  374. package/dist/utils/geometryScript.js +92 -0
  375. package/dist/utils/geometryScript.worker.d.ts +2 -0
  376. package/dist/utils/geometryScript.worker.d.ts.map +1 -0
  377. package/dist/utils/geometryScript.worker.js +42 -0
  378. package/dist/utils/groundRegistry.d.ts +11 -0
  379. package/dist/utils/groundRegistry.d.ts.map +1 -0
  380. package/dist/utils/groundRegistry.js +39 -0
  381. package/dist/utils/instancingSupport.d.ts +9 -0
  382. package/dist/utils/instancingSupport.d.ts.map +1 -0
  383. package/dist/utils/instancingSupport.js +13 -0
  384. package/dist/utils/landPatch.d.ts +26 -5
  385. package/dist/utils/landPatch.d.ts.map +1 -1
  386. package/dist/utils/landPatch.js +124 -9
  387. package/dist/utils/layoutPlacement.d.ts +30 -0
  388. package/dist/utils/layoutPlacement.d.ts.map +1 -0
  389. package/dist/utils/layoutPlacement.js +113 -0
  390. package/dist/utils/linkRegistry.d.ts +15 -0
  391. package/dist/utils/linkRegistry.d.ts.map +1 -0
  392. package/dist/utils/linkRegistry.js +11 -0
  393. package/dist/utils/shapes.d.ts +28 -2
  394. package/dist/utils/shapes.d.ts.map +1 -1
  395. package/dist/utils/shapes.js +32 -3
  396. package/dist/utils/sharedParts.d.ts +61 -0
  397. package/dist/utils/sharedParts.d.ts.map +1 -0
  398. package/dist/utils/sharedParts.js +268 -0
  399. package/dist/utils/svgAsset.d.ts +6 -0
  400. package/dist/utils/svgAsset.d.ts.map +1 -0
  401. package/dist/utils/svgAsset.js +46 -0
  402. package/dist/utils/waterLight.d.ts +29 -0
  403. package/dist/utils/waterLight.d.ts.map +1 -0
  404. package/dist/utils/waterLight.js +67 -0
  405. package/dist/utils/waterReflection.d.ts +185 -0
  406. package/dist/utils/waterReflection.d.ts.map +1 -0
  407. package/dist/utils/waterReflection.js +616 -0
  408. 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
+ }