@3driseai/3drise-viewer 0.1.42 → 0.1.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +4 -3
@@ -0,0 +1,658 @@
1
+ // Emitted from Claude outputs/sports-lab/court.mjs by gen_family.mjs — edit the lab module and re-emit.
2
+ // sports/core/court — Court (type 'sports', config.type 'court'). Shared core: ../sportsCore.
3
+ // Construction: a playing surface at regulation size with its markings drawn as anti-aliased signed
4
+ // distances in the fragment (every line of the rulebook, metres, sharp at any zoom), on a procedural
5
+ // surface (acrylic, clay, grass, parquet, ice, tartan, turf, sand, concrete, paint) with wear where
6
+ // the play is and puddles when wet; the sport's fixtures (nets, goals, hoops) built from the shared
7
+ // goal builders. Lines are drawn on the mesh itself — no decals, no textures. Size: real metres;
8
+ // the transform places it.
9
+ // Night: the court is lit the urban way, with no real lights — its surface gathers the lamps of the
10
+ // one lamp list (street lamps, other courts' floodlights) and adds their pools, scaled by the night
11
+ // factor from the scene sun; its own floodlight masts register their heads in the same list, so
12
+ // they light the court evenly and spill onto the paving around it.
13
+ import * as THREE from 'three';
14
+ import { setColor, createCommonUniforms, applyCommonConfig, updateCommon, QUALITY_S, litMaterial, propMesh, partMaterial, mergeGeos, box, bar, buildGoal, GOAL_COLORS, disposePair, registerLamp, unregisterLamp, touchLamps, setLamps, placeLamps, createLampUniforms, createSunUniforms, GLSL_COMMON } from '../sportsCore.js';
15
+ /**
16
+ * The sports: regulation size (length × width, metres), line width, the run-off the venue adds around
17
+ * the lines [ends, sides], the natural surface, the three colour slots when the config leaves them
18
+ * white (surface, lines, accent), the fixtures and where they stand (x along the length).
19
+ */
20
+ export const SPORTS = {
21
+ tennis: { L: 23.77, W: 10.97, line: 0.05, mow: 1.32, runoff: [6.4, 3.66], surface: 'hardcourt', colors: ['#2a62a8', '#ffffff', '#3b7f44'], fixtures: [{ kind: 'tennisNet', x: 0, rot: 1 }] },
22
+ basketball: { L: 28, W: 15, line: 0.05, runoff: [2, 2], surface: 'wood', colors: ['#c89659', '#ffffff', '#a2372f'], fixtures: [{ kind: 'basketballHoop', x: 12.425, rot: -1 }, { kind: 'basketballHoop', x: -12.425, rot: 1 }] },
23
+ soccer: { L: 105, W: 68, line: 0.12, mow: 5.25, runoff: [6, 4], surface: 'grass', colors: ['#3d8a39', '#ffffff', '#3d8a39'], fixtures: [{ kind: 'footballGoal', x: 52.5, rot: -1 }, { kind: 'footballGoal', x: -52.5, rot: 1 }] },
24
+ futsal: { L: 40, W: 20, line: 0.08, runoff: [2, 1.5], surface: 'wood', colors: ['#c89659', '#ffffff', '#2a62a8'], fixtures: [{ kind: 'futsalGoal', x: 20, rot: -1 }, { kind: 'futsalGoal', x: -20, rot: 1 }] },
25
+ handball: { L: 40, W: 20, line: 0.05, runoff: [2, 1], surface: 'wood', colors: ['#c89659', '#ffffff', '#2f6cb5'], fixtures: [{ kind: 'handballGoal', x: 20, rot: -1 }, { kind: 'handballGoal', x: -20, rot: 1 }] },
26
+ volleyball: { L: 18, W: 9, line: 0.05, runoff: [5, 3], surface: 'hardcourt', colors: ['#d4823a', '#ffffff', '#2a62a8'], fixtures: [{ kind: 'volleyballNet', x: 0, rot: 1 }] },
27
+ badminton: { L: 13.4, W: 6.1, line: 0.04, runoff: [2, 1.5], surface: 'hardcourt', colors: ['#3b8a4a', '#ffffff', '#2d6d3a'], fixtures: [{ kind: 'badmintonNet', x: 0, rot: 1 }] },
28
+ padel: { L: 20, W: 10, line: 0.05, runoff: [0.5, 0.5], surface: 'turf', colors: ['#2a62a8', '#ffffff', '#1f4f8a'], fixtures: [{ kind: 'padelNet', x: 0, rot: 1 }] },
29
+ pickleball: { L: 13.41, W: 6.1, line: 0.05, runoff: [3, 2], surface: 'hardcourt', colors: ['#2a62a8', '#ffffff', '#3b8a4a'], fixtures: [{ kind: 'pickleballNet', x: 0, rot: 1 }] },
30
+ iceHockey: { L: 60.96, W: 25.91, line: 0.05, runoff: [0, 0], corner: 8.53, surface: 'ice', colors: ['#e9f1f6', '#d8322d', '#2f6cd0'], fixtures: [{ kind: 'hockeyNet', x: 27.13, rot: -1 }, { kind: 'hockeyNet', x: -27.13, rot: 1 }] },
31
+ athleticsTrack: { L: 176.9, W: 92.5, line: 0.05, runoff: [3, 3], surface: 'tartan', colors: ['#b8453a', '#ffffff', '#3d8a39'], fixtures: [] },
32
+ tableTennis: { L: 2.74, W: 1.525, line: 0.02, runoff: [0, 0], table: 0.76, surface: 'painted', colors: ['#1d3f86', '#ffffff', '#1d3f86'], fixtures: [{ kind: 'tableTennisNet', x: 0, rot: 1 }] },
33
+ // NFL: 120 × 53⅓ yd with the end zones; the uprights' crossbar stands over each end line
34
+ americanFootball: { L: 109.728, W: 48.768, line: 0.1016, mow: 4.572, runoff: [6, 8], surface: 'grass', colors: ['#3d8a39', '#ffffff', '#1f3f7a'], fixtures: [{ kind: 'americanUprights', x: 54.864, rot: -1 }, { kind: 'americanUprights', x: -54.864, rot: 1 }] },
35
+ };
36
+ export const SPORT_LIST = Object.keys(SPORTS);
37
+ /** Earlier names of a sport (saved scenes keep working): 'football' was the soccer pitch. */
38
+ export const SPORT_ALIASES = { football: 'soccer' };
39
+ /** The sport key a config means (an alias resolved; unknown -> tennis). */
40
+ export function sportKey(k) { const a = SPORT_ALIASES[k] || k; return SPORTS[a] ? a : 'tennis'; }
41
+ /**
42
+ * Floodlights per sport. sides: n masts along each long side, heads on an arm over the slab edge,
43
+ * tilted toward the court (street-lamp pools); towers: four stadium towers outside the corners, each a
44
+ * bank of rows × cols projectors aimed into the field, registered as PROJECTOR lamps (aimed long-throw
45
+ * beams: lamp.aim + lamp.spread) — every tower throws one beam at each quarter of the field, so the
46
+ * whole surface is lit evenly. h = head height (m), set = how far outside the corner the tower stands,
47
+ * radius = reach, I = lamp intensity (urban units), spread = cos of a beam's outer half-angle.
48
+ */
49
+ export const FLOOD = {
50
+ tennis: { mode: 'sides', n: 3, h: 9, radius: 26, I: 0.9 },
51
+ basketball: { mode: 'sides', n: 3, h: 10, radius: 26, I: 0.9 },
52
+ soccer: { mode: 'towers', h: 32, rows: 4, cols: 5, set: 6, radius: 170, I: 0.2, spread: 0.74 },
53
+ futsal: { mode: 'sides', n: 3, h: 10, radius: 28, I: 0.95 },
54
+ handball: { mode: 'sides', n: 3, h: 10, radius: 28, I: 0.95 },
55
+ volleyball: { mode: 'sides', n: 2, h: 9, radius: 24, I: 0.9 },
56
+ badminton: { mode: 'sides', n: 2, h: 7, radius: 18, I: 0.8 },
57
+ padel: { mode: 'sides', n: 2, h: 7, radius: 18, I: 0.8 },
58
+ pickleball: { mode: 'sides', n: 2, h: 7, radius: 18, I: 0.8 },
59
+ iceHockey: { mode: 'sides', n: 4, h: 12, radius: 30, I: 1.0 },
60
+ athleticsTrack: { mode: 'towers', h: 38, rows: 5, cols: 6, set: 8, radius: 240, I: 0.2, spread: 0.74 },
61
+ tableTennis: null,
62
+ americanFootball: { mode: 'towers', h: 34, rows: 4, cols: 5, set: 7, radius: 175, I: 0.2, spread: 0.74 },
63
+ };
64
+ export const LIGHTING = ['auto', 'day', 'night'];
65
+ export const SURFACES = ['auto', 'hardcourt', 'clay', 'grass', 'wood', 'ice', 'tartan', 'turf', 'sand', 'concrete', 'painted'];
66
+ export function createUniforms() {
67
+ return Object.assign(createCommonUniforms(), {
68
+ // the fixtures (nets, goals, hoops) keep their own natural colours: the court's colour slots are surface / lines / zone
69
+ fixtureU: createCommonUniforms(),
70
+ uSport: { value: 0 }, uSurface: { value: 0 }, uMow: { value: 5 }, uLineW: { value: 0.05 }, uWet: { value: 0 }, uTableY: { value: -1 },
71
+ uSurfCol: { value: new THREE.Color('#2a62a8') }, uLineCol: { value: new THREE.Color('#ffffff') }, uZoneCol: { value: new THREE.Color('#3b7f44') },
72
+ uFloodColor: { value: new THREE.Color('#fff1dc') }, uFloodGlow: { value: 1 },
73
+ }, createSunUniforms(), createLampUniforms());
74
+ }
75
+ export function applyConfig(u, c, inst = undefined) {
76
+ applyCommonConfig(u, c, inst && inst.mats);
77
+ const S = SPORTS[sportKey(c.sport)];
78
+ // the three colour slots (surface, lines, surround / zone): the sport's own while naturalColors is on
79
+ const nat = c.naturalColors !== false;
80
+ setColor(u.uSurfCol, nat ? S.colors[0] : c.color);
81
+ setColor(u.uLineCol, nat ? S.colors[1] : c.secondaryColor);
82
+ setColor(u.uZoneCol, nat ? S.colors[2] : c.accentColor);
83
+ if (c.sport !== undefined) {
84
+ u.uSport.value = SPORT_LIST.indexOf(sportKey(c.sport));
85
+ u.uMow.value = S.mow || 5;
86
+ }
87
+ if (c.surface !== undefined)
88
+ u.uSurface.value = Math.max(0, SURFACES.indexOf(c.surface === 'auto' ? S.surface : c.surface) - 1);
89
+ if (c.lineScale !== undefined)
90
+ u.uLineW.value = S.line * c.lineScale;
91
+ if (c.wetness !== undefined)
92
+ u.uWet.value = c.wetness;
93
+ // fixtures: the court's wear / gloss / seed / opacity, their own colours (set at build from GOAL_COLORS)
94
+ const fu = u.fixtureU;
95
+ applyCommonConfig(fu, { wear: c.wear, gloss: c.gloss, seed: c.seed, opacity: c.opacity }, inst && inst.fixtureMats);
96
+ if (inst)
97
+ for (const f of inst.fixtures)
98
+ f.apply({ netTension: 0.75 });
99
+ // floodlights: the heads' glow and their entries in the lamp list
100
+ if (c.floodColor !== undefined)
101
+ setColor(u.uFloodColor, c.floodColor);
102
+ if (c.floodIntensity !== undefined)
103
+ u.uFloodGlow.value = c.floodIntensity;
104
+ // setLamps bumps the shared lamp list only on a real change: apply runs every frame while any
105
+ // knob animates, and a bump per frame re-gathers / re-bakes every lamp-lit surface in the scene
106
+ if (inst && inst.lamps.length)
107
+ setLamps(inst.lamps, c.floodColor, c.floodIntensity !== undefined ? inst.lamps[0].base * c.floodIntensity : undefined);
108
+ }
109
+ // ---------------------------------------------------------------------------- GLSL: the markings
110
+ // p = (x along the length, z across), metres from the centre. Every sport writes:
111
+ // dL distance to the nearest line (drawn in the line colour, width uLineW)
112
+ // dA distance to the nearest accent-coloured line (hockey blue lines), width wA
113
+ // zone 1 inside the accent-filled area (a key, a crease, the surround, the infield)
114
+ // field 1 inside the playing area (for the surface's own wear focus)
115
+ const MARKINGS_GLSL = /* glsl */ `
116
+ uniform float uSport, uSurface, uLineW, uWet, uTableY, uMow;
117
+ uniform vec3 uSurfCol, uLineCol, uZoneCol;
118
+ // a stadium (two straights + two semicircles): distance to its outline
119
+ float lnStadium(vec2 p, float a, float r) { return abs(length(vec2(max(abs(p.x) - a, 0.0), p.y)) - r); }
120
+ float inBox(vec2 p, vec2 hb) { return step(abs(p.x), hb.x) * step(abs(p.y), hb.y); }
121
+ // dashes are a coverage mask (dOn), never a jump in the distance field — a discontinuous distance
122
+ // blows up fwidth and smears the line across a whole band
123
+ float dashOn(float s, float len) { return step(0.5, fract(s / (2.0 * len))); }
124
+
125
+ // a seven-segment digit (0–9), centred, 1.2 × 1.83 m (the field numbers' 4 × 6 ft), distance to its strokes' centre lines
126
+ float segD(vec2 uv, vec2 a, vec2 b) { return sdSeg(uv, a, b); }
127
+ float digit7(vec2 uv, int n) {
128
+ float w = 0.49, h = 0.805, d = 1e9;
129
+ bool A = n != 1 && n != 4, B = n != 5 && n != 6, C = n != 2, D = n != 1 && n != 4 && n != 7;
130
+ bool E = n == 0 || n == 2 || n == 6 || n == 8, F = n != 1 && n != 2 && n != 3 && n != 7, G = n != 0 && n != 1 && n != 7;
131
+ if (A) d = min(d, segD(uv, vec2(-w, h), vec2(w, h)));
132
+ if (B) d = min(d, segD(uv, vec2(w, h), vec2(w, 0.0)));
133
+ if (C) d = min(d, segD(uv, vec2(w, 0.0), vec2(w, -h)));
134
+ if (D) d = min(d, segD(uv, vec2(-w, -h), vec2(w, -h)));
135
+ if (E) d = min(d, segD(uv, vec2(-w, 0.0), vec2(-w, -h)));
136
+ if (F) d = min(d, segD(uv, vec2(-w, h), vec2(-w, 0.0)));
137
+ if (G) d = min(d, segD(uv, vec2(-w, 0.0), vec2(w, 0.0)));
138
+ return d;
139
+ }
140
+
141
+ void markings(vec2 p, out float dL, out float dA, out float wA, out float dD, out float dOn, out float zone, out float field) {
142
+ dL = 1e9; dA = 1e9; wA = uLineW; dD = 1e9; dOn = 0.0; zone = 0.0; field = 0.0;
143
+ vec2 q = abs(p); // every court is symmetric about both axes
144
+ int s = int(uSport + 0.5);
145
+ if (s == 0) { // tennis 23.77 × 10.97
146
+ vec2 H = vec2(11.885, 5.485);
147
+ dL = lnBox(p, vec2(0.0), H);
148
+ dL = min(dL, sdSeg(q, vec2(0.0, 4.115), vec2(11.885, 4.115))); // singles sideline
149
+ dL = min(dL, sdSeg(q, vec2(6.4, 0.0), vec2(6.4, 4.115))); // service line
150
+ dL = min(dL, sdSeg(q, vec2(0.0, 0.0), vec2(6.4, 0.0))); // centre service line
151
+ dL = min(dL, sdSeg(q, vec2(11.885, 0.0), vec2(11.785, 0.0))); // centre mark
152
+ field = inBox(p, H); zone = 1.0 - field;
153
+ } else if (s == 1) { // basketball FIBA 28 × 15
154
+ vec2 H = vec2(14.0, 7.5);
155
+ dL = lnBox(p, vec2(0.0), H);
156
+ dL = min(dL, sdSeg(q, vec2(0.0, 0.0), vec2(0.0, 7.5))); // halfway
157
+ dL = min(dL, lnCircle(p, vec2(0.0), 1.8)); // centre circle
158
+ dL = min(dL, lnBox(q, vec2(11.1, 0.0), vec2(2.9, 2.45))); // the key 5.8 × 4.9
159
+ dL = min(dL, lnCircle(q, vec2(8.2, 0.0), 1.8)); // free-throw circle
160
+ vec2 bk = vec2(12.425, 0.0); // basket
161
+ dL = min(dL, lnArc(q, bk, 6.75, 3.14159 - 1.3598, 3.14159 + 1.3598)); // 3-pt arc (meets the straights at |z| = 6.6)
162
+ dL = min(dL, sdSeg(q, vec2(14.0, 6.6), vec2(11.01, 6.6))); // 3-pt straight
163
+ dL = min(dL, lnArc(q, bk, 1.25, 1.5708, 4.7124)); // no-charge semicircle
164
+ field = inBox(p, H); zone = inBox(q - vec2(11.1, 0.0), vec2(2.9, 2.45));
165
+ } else if (s == 2) { // soccer 105 × 68
166
+ vec2 H = vec2(52.5, 34.0);
167
+ dL = lnBox(p, vec2(0.0), H);
168
+ dL = min(dL, sdSeg(q, vec2(0.0, 0.0), vec2(0.0, 34.0))); // halfway
169
+ dL = min(dL, lnCircle(p, vec2(0.0), 9.15));
170
+ dL = min(dL, length(p) - 0.2); // centre spot
171
+ dL = min(dL, lnBox(q, vec2(44.25, 0.0), vec2(8.25, 20.16))); // penalty area 16.5 × 40.32
172
+ dL = min(dL, lnBox(q, vec2(49.75, 0.0), vec2(2.75, 9.16))); // goal area 5.5 × 18.32
173
+ dL = min(dL, length(q - vec2(41.5, 0.0)) - 0.2); // penalty mark
174
+ dL = min(dL, lnArc(q, vec2(41.5, 0.0), 9.15, 3.14159 - 0.9273, 3.14159 + 0.9273)); // penalty arc, outside the area
175
+ dL = min(dL, lnArc(q, vec2(52.5, 34.0), 1.0, 3.14159, 4.7124)); // corner arc
176
+ field = inBox(p, H);
177
+ } else if (s == 3 || s == 4) { // futsal / handball 40 × 20
178
+ vec2 H = vec2(20.0, 10.0);
179
+ dL = lnBox(p, vec2(0.0), H);
180
+ dL = min(dL, sdSeg(q, vec2(0.0, 0.0), vec2(0.0, 10.0)));
181
+ float r = s == 3 ? 6.0 : 6.0;
182
+ // the D: quarter circles around the posts (±1.5) joined by a straight
183
+ float dD = min(lnArc(q, vec2(20.0, 1.5), r, 1.5708, 3.14159), sdSeg(q, vec2(20.0 - r, 0.0), vec2(20.0 - r, 1.5)));
184
+ dL = min(dL, dD);
185
+ if (s == 3) {
186
+ dL = min(dL, lnCircle(p, vec2(0.0), 3.0));
187
+ dL = min(dL, length(q - vec2(14.0, 0.0)) - 0.15); // penalty mark 6 m
188
+ dL = min(dL, length(q - vec2(10.0, 0.0)) - 0.15); // second mark 10 m
189
+ } else {
190
+ // 9 m dashed line, 7 m mark, 4 m goalkeeper line
191
+ vec2 c = vec2(20.0, 1.5);
192
+ float a = atan(q.y - 1.5, q.x - 20.0);
193
+ float d9 = min(lnArc(q, c, 9.0, 1.5708, 3.14159), sdSeg(q, vec2(11.0, 0.0), vec2(11.0, 1.5)));
194
+ float along = q.y < 1.5 && q.x < 11.5 ? q.y : (a - 1.5708) * 9.0 + 1.5;
195
+ dD = d9; dOn = dashOn(along, 0.15);
196
+ dL = min(dL, sdSeg(q, vec2(13.0, 0.0), vec2(13.0, 0.5))); // 7 m mark (1 m long)
197
+ dL = min(dL, sdSeg(q, vec2(16.0, 0.0), vec2(16.0, 0.075))); // 4 m line
198
+ zone = step(sdSeg(q, vec2(20.0, 0.0), vec2(20.0, 1.5)), r); // the goal area, filled
199
+ }
200
+ field = inBox(p, H);
201
+ } else if (s == 5) { // volleyball 18 × 9
202
+ vec2 H = vec2(9.0, 4.5);
203
+ dL = lnBox(p, vec2(0.0), H);
204
+ dL = min(dL, sdSeg(q, vec2(0.0, 0.0), vec2(0.0, 4.5)));
205
+ dL = min(dL, sdSeg(q, vec2(3.0, 0.0), vec2(3.0, 4.5))); // attack line
206
+ field = inBox(p, H); zone = 1.0 - field;
207
+ } else if (s == 6) { // badminton 13.4 × 6.1
208
+ vec2 H = vec2(6.7, 3.05);
209
+ dL = lnBox(p, vec2(0.0), H);
210
+ dL = min(dL, sdSeg(q, vec2(0.0, 2.59), vec2(6.7, 2.59))); // singles sideline
211
+ dL = min(dL, sdSeg(q, vec2(1.98, 0.0), vec2(1.98, 3.05))); // short service line
212
+ dL = min(dL, sdSeg(q, vec2(5.94, 0.0), vec2(5.94, 3.05))); // doubles long service line
213
+ dL = min(dL, sdSeg(q, vec2(1.98, 0.0), vec2(6.7, 0.0))); // centre line
214
+ field = inBox(p, H); zone = 1.0 - field;
215
+ } else if (s == 7) { // padel 20 × 10
216
+ vec2 H = vec2(10.0, 5.0);
217
+ dL = lnBox(p, vec2(0.0), H);
218
+ dL = min(dL, sdSeg(q, vec2(6.95, 0.0), vec2(6.95, 5.0))); // service line
219
+ dL = min(dL, sdSeg(q, vec2(0.0, 0.0), vec2(6.95, 0.0))); // centre service line
220
+ field = inBox(p, H); zone = 1.0 - field;
221
+ } else if (s == 8) { // pickleball 13.41 × 6.1
222
+ vec2 H = vec2(6.705, 3.05);
223
+ dL = lnBox(p, vec2(0.0), H);
224
+ dL = min(dL, sdSeg(q, vec2(2.13, 0.0), vec2(2.13, 3.05))); // non-volley line
225
+ dL = min(dL, sdSeg(q, vec2(2.13, 0.0), vec2(6.705, 0.0))); // centre line
226
+ field = inBox(p, H); zone = step(q.x, 2.13) * field;
227
+ } else if (s == 9) { // ice hockey NHL 60.96 × 25.91
228
+ float gl = 27.13;
229
+ dL = sdSeg(q, vec2(gl, 0.0), vec2(gl, 13.0)); // goal line (red)
230
+ dA = sdSeg(q, vec2(7.62, 0.0), vec2(7.62, 13.0)); wA = 0.3; // blue lines
231
+ dD = sdSeg(q, vec2(0.0, 0.0), vec2(0.0, 13.0)); dOn = dashOn(q.y, 0.2); // centre red line (dashed look)
232
+ dL = min(dL, lnCircle(p, vec2(0.0), 4.57) + 0.0); // centre faceoff circle
233
+ vec2 fo = vec2(gl - 6.1, 6.71);
234
+ dL = min(dL, lnCircle(q, fo, 4.57)); // end-zone circles
235
+ dL = min(dL, length(q - fo) - 0.3); // faceoff dots
236
+ dL = min(dL, length(q - vec2(6.1, 6.71)) - 0.3); // neutral-zone dots
237
+ dL = min(dL, length(p) - 0.15);
238
+ dL = min(dL, sdSeg(q, vec2(fo.x - 0.6, fo.y - 0.9), vec2(fo.x - 0.6, fo.y + 0.9))); // hash marks (simplified)
239
+ dL = min(dL, sdSeg(q, vec2(fo.x + 0.6, fo.y - 0.9), vec2(fo.x + 0.6, fo.y + 0.9)));
240
+ dL = min(dL, lnArc(q, vec2(gl, 0.0), 1.83, 1.5708, 4.7124)); // crease outline (red)
241
+ zone = step(length(q - vec2(gl, 0.0)), 1.83) * step(q.x, gl); // crease fill (light blue)
242
+ field = 1.0;
243
+ } else if (s == 10) { // athletics track: 400 m, 8 lanes of 1.22 m, r 36.5, straights 84.39
244
+ float a = 42.195, r0 = 36.5;
245
+ for (int k = 0; k <= 8; k++) dL = min(dL, lnStadium(p, a, r0 + 1.22 * float(k)));
246
+ float rr = length(vec2(max(abs(p.x) - a, 0.0), p.y));
247
+ field = step(r0, rr) * step(rr, r0 + 9.76);
248
+ zone = 1.0 - field;
249
+ // finish line across the home straight at the start of the bend
250
+ dL = min(dL, sdSeg(vec2(p.x, p.y), vec2(a, r0), vec2(a, r0 + 9.76)));
251
+ } else if (s == 12) { // American football (NFL) 120 × 53⅓ yd, in metres
252
+ float Y = 0.9144, hx = 54.864, hz = 24.384, gl = 45.72;
253
+ // the 6 ft white border outside the sidelines and end lines
254
+ vec2 bq = q - vec2(hx, hz) - 0.915;
255
+ // (filled shapes: zero inside, so the line test |dL| < lw/2 paints them whole)
256
+ dL = max(abs(length(max(bq, 0.0)) + min(max(bq.x, bq.y), 0.0)) - 0.915 + uLineW * 0.5, 0.0);
257
+ dL = min(dL, max(sdSeg(q, vec2(gl, 0.0), vec2(gl, hz)) - 0.1016 + uLineW * 0.5, 0.0)); // goal lines, 8 in
258
+ // grids with the nearest index CLAMPED (never a branch per index): the distance stays continuous,
259
+ // so fwidth never spikes and paints a phantom line where the nearest mark changes
260
+ float n5 = clamp(floor(q.x / (5.0 * Y) + 0.5), 0.0, 10.0);
261
+ dL = min(dL, length(vec2(q.x - n5 * 5.0 * Y, max(q.y - hz, 0.0)))); // yard lines every 5 yd
262
+ float n1 = clamp(floor(q.x / Y + 0.5), 0.0, 50.0), dx = q.x - n1 * Y; // 1-yd hashes: inbounds (NFL) and sidelines
263
+ dL = min(dL, length(vec2(dx, max(abs(q.y - 3.124) - 0.305, 0.0))));
264
+ dL = min(dL, length(vec2(dx, max(abs(q.y - (hz - 0.407)) - 0.305, 0.0))));
265
+ dL = min(dL, sdSeg(q, vec2(gl - 1.8288, 0.0), vec2(gl - 1.8288, 0.457))); // try line at the 2
266
+ // field numbers every 10 yd, 6 ft tall, tops 12 yd in from the sideline, read from the near sideline
267
+ float k = clamp(floor(p.x / (10.0 * Y) + 0.5), -4.0, 4.0);
268
+ {
269
+ float sg = p.y < 0.0 ? -1.0 : 1.0;
270
+ vec2 uv = vec2(sg * (p.x - k * 10.0 * Y), -(q.y - (hz - 10.973 + 0.915)));
271
+ dL = min(dL, max(digit7(uv + vec2(0.95, 0.0), int(5.0 - abs(k) + 0.5)) - 0.1 + uLineW * 0.5, 0.0));
272
+ dL = min(dL, max(digit7(uv - vec2(0.95, 0.0), 0) - 0.1 + uLineW * 0.5, 0.0));
273
+ // direction arrows beside the numbers, pointing to the nearer goal (not at the 50)
274
+ if (abs(k) > 0.5) {
275
+ float dir = sign(k);
276
+ vec2 t = vec2((p.x - (k * 10.0 * Y + dir * 2.0)) * dir, q.y - (hz - 10.973 + 0.35));
277
+ float f = max(-0.15 - t.x, abs(t.y) - (0.3 - t.x) * 0.667);
278
+ dL = min(dL, max(f, 0.0));
279
+ }
280
+ }
281
+ field = inBox(p, vec2(hx, hz));
282
+ zone = step(gl, q.x) * field; // the end zones
283
+ } else { // table tennis 2.74 × 1.525 (lines 2 cm along the edges, 3 mm centre line)
284
+ vec2 H = vec2(1.37 - 0.01, 0.7625 - 0.01);
285
+ dL = lnBox(p, vec2(0.0), H);
286
+ float dc = abs(p.y); // centre line along the length
287
+ dL = min(dL, dc + (uLineW - 0.003) * 0.5); // 3 mm line in the same width unit
288
+ field = 1.0;
289
+ }
290
+ }
291
+ `;
292
+ // ---------------------------------------------------------------------------- GLSL: the surfaces
293
+ const SURFACE_GLSL = /* glsl */ `
294
+ // Returns albedo, roughness and a height (for the bump) of the bare surface at p (metres), given the
295
+ // in-play flag and the wear focus (0..1, higher where the play is).
296
+ vec3 surfAlb; float surfRough; float surfH; float lineSoft; float lineBump;
297
+ void surfaceAt(vec2 p, float focus, float worn) {
298
+ int k = int(uSurface + 0.5);
299
+ vec3 base = uSurfCol;
300
+ lineSoft = 1.0; lineBump = 0.0; surfH = 0.0;
301
+ if (k == 0) { // hardcourt: acrylic over asphalt — fine grit, ball marks and scuffs lighten it
302
+ float grit = snoise3(vec3(p * 900.0, 1.0));
303
+ surfAlb = base * (0.94 + 0.12 * grit) * (0.97 + 0.06 * sfbm2(p * 0.7));
304
+ surfAlb = mix(surfAlb, surfAlb * 1.35 + 0.05, worn * 0.7);
305
+ surfRough = 0.72 + 0.1 * grit - worn * 0.12;
306
+ surfH = grit * 0.00012;
307
+ } else if (k == 1) { // clay: crushed brick, brushed streaks along the length, damp patches
308
+ float gr = snoise3(vec3(p * 500.0, 2.0));
309
+ float streak = snoise2(vec2(p.x * 2.5, p.y * 90.0));
310
+ float damp = sfbm2(p * 0.35 + uSeed);
311
+ surfAlb = base * (0.9 + 0.18 * gr) * (0.92 + 0.12 * streak) * mix(1.0, 0.72, smoothstep(0.55, 0.8, damp) * 0.6);
312
+ surfAlb = mix(surfAlb, surfAlb * 0.8, worn * 0.6); // drag marks darken the clay
313
+ surfRough = 0.95;
314
+ surfH = gr * 0.0003 + streak * 0.0002;
315
+ lineSoft = 1.6; lineBump = 0.002; // tape lines, slightly raised, dusty edge
316
+ } else if (k == 2) { // grass: mowing stripes across the length (uMow metres: ~1.3 on a tennis lawn, ~5 on a pitch), blade noise, worn to earth where played
317
+ float tri = abs(fract(p.x / (2.0 * uMow)) - 0.5); // 0..0.5, one stripe each side of 0.25
318
+ float bw = fwidth(p.x) / (2.0 * uMow) + 1e-4;
319
+ float band = smoothstep(0.25 - bw, 0.25 + bw, tri);
320
+ float blades = snoise2(p * 60.0) * 0.5 + snoise2(p * 200.0 + 5.0) * 0.5;
321
+ float ptch = sfbm2(p * 0.15 + uSeed * 3.0);
322
+ surfAlb = base * mix(0.92, 1.08, band) * (0.85 + 0.3 * blades) * (0.9 + 0.2 * ptch);
323
+ vec3 earth = vec3(0.42, 0.33, 0.22);
324
+ surfAlb = mix(surfAlb, earth * (0.8 + 0.4 * blades), worn * 0.85);
325
+ surfRough = 0.95;
326
+ surfH = blades * 0.004 * (1.0 - worn);
327
+ lineSoft = 2.2; // chalk / paint on blades
328
+ } else if (k == 3) { // parquet: boards 5.7 cm wide along the length, staggered ends, varnish dulled where played
329
+ float bw = 0.057;
330
+ float row = floor(p.y / bw);
331
+ float h = shash12(vec2(row, uSeed));
332
+ float len = 1.8 + 0.8 * h;
333
+ float seg = floor((p.x + h * 7.0) / len);
334
+ float tone = shash12(vec2(row * 3.1, seg + uSeed));
335
+ float far = smoothstep(0.15, 0.5, fwidth(p.y) / bw); // boards thinner than a pixel: average them out
336
+ float grain = snoise2(vec2(p.x * 6.0 + seg, p.y * 260.0 + row * 7.0)) * (1.0 - far) + 0.5 * far;
337
+ vec3 wood = base * mix(0.86, 1.14, mix(tone, 0.5, far)) * (0.9 + 0.18 * grain);
338
+ float gap = min(abs(fract(p.y / bw) - 0.5) * bw, abs(fract((p.x + h * 7.0) / len) - 0.5) * len);
339
+ float gapL = (1.0 - smoothstep(0.0, 0.0015 + fwidth(p.x) * 0.5, gap - 0.0007)) * (1.0 - far);
340
+ surfAlb = mix(wood, wood * 0.55, gapL * 0.8);
341
+ surfAlb = mix(surfAlb, surfAlb * 0.9, worn * 0.4);
342
+ surfRough = mix(0.32, 0.6, worn) + 0.08 * grain;
343
+ surfH = -gapL * 0.0006 + grain * 0.00002;
344
+ } else if (k == 4) { // ice: pale blue-white, skate scratches in two directions, markings painted under it
345
+ float sc1 = snoise2(vec2(p.x * 1.5 + p.y * 0.3, p.y * 220.0));
346
+ float sc2 = snoise2(vec2(p.y * 1.2 - p.x * 0.2, p.x * 180.0 + 9.0));
347
+ float scr = max(sc1, sc2);
348
+ float frost = sfbm2(p * 0.4 + uSeed);
349
+ surfAlb = base * (0.96 + 0.04 * frost);
350
+ surfAlb = mix(surfAlb, vec3(1.0), worn * 0.5 * smoothstep(0.6, 0.95, scr)); // snow dust in the cuts
351
+ surfRough = mix(0.06, 0.3, worn * smoothstep(0.5, 0.95, scr)) + 0.02 * frost;
352
+ surfH = -worn * smoothstep(0.75, 0.95, scr) * 0.0004;
353
+ lineSoft = 2.5; // under the ice: soft-edged, slightly veiled
354
+ } else if (k == 5) { // tartan: rubber granules
355
+ float gr = snoise3(vec3(p * 420.0, 3.0));
356
+ surfAlb = base * (0.82 + 0.3 * gr) * (0.95 + 0.08 * sfbm2(p * 0.3));
357
+ surfAlb = mix(surfAlb, surfAlb * 1.2 + 0.03, worn * 0.5);
358
+ surfRough = 0.9;
359
+ surfH = gr * 0.0005;
360
+ } else if (k == 6) { // artificial turf: fibres with a nap direction, sand infill
361
+ float fib = snoise2(vec2(p.x * 300.0, p.y * 40.0));
362
+ float sand = step(0.93, snoise2(p * 700.0));
363
+ surfAlb = base * (0.85 + 0.3 * fib);
364
+ surfAlb = mix(surfAlb, vec3(0.75, 0.7, 0.6), sand * 0.5 + worn * 0.35);
365
+ surfRough = 0.9;
366
+ surfH = fib * 0.001;
367
+ } else if (k == 7) { // sand: ripples and footprints
368
+ float rip = sfbm2(p * vec2(2.5, 0.8));
369
+ float grain = snoise3(vec3(p * 600.0, 4.0));
370
+ surfAlb = base * (0.9 + 0.1 * rip) * (0.94 + 0.12 * grain);
371
+ surfAlb = mix(surfAlb, surfAlb * 0.85, worn * 0.4);
372
+ surfRough = 1.0;
373
+ surfH = rip * 0.012 + grain * 0.0006 - worn * 0.004 * smoothstep(0.4, 0.7, sfbm2(p * 3.0));
374
+ lineSoft = 3.0;
375
+ } else if (k == 8) { // concrete: panels, tone variation, cracks
376
+ float tone = sfbm2(p * 0.25 + uSeed);
377
+ float crack = 1.0 - abs(2.0 * sfbm2(p * 0.9 + 3.0) - 1.0);
378
+ float cr = smoothstep(0.93, 0.99, crack);
379
+ float joint = min(abs(fract(p.x / 4.0) - 0.5) * 4.0, abs(fract(p.y / 4.0) - 0.5) * 4.0);
380
+ float jl = 1.0 - smoothstep(0.004, 0.012, joint);
381
+ surfAlb = base * (0.85 + 0.3 * tone) * (1.0 - 0.5 * cr) * (1.0 - 0.4 * jl);
382
+ surfAlb = mix(surfAlb, surfAlb * 0.75, worn * 0.5);
383
+ surfRough = 0.9 - 0.1 * tone;
384
+ surfH = -cr * 0.0015 - jl * 0.002;
385
+ } else { // painted (a table top): matte paint, slight orange peel, scuffed edges
386
+ float peel = snoise2(p * 500.0);
387
+ surfAlb = base * (0.97 + 0.05 * peel);
388
+ surfAlb = mix(surfAlb, surfAlb * 1.3 + 0.05, worn * 0.5);
389
+ surfRough = 0.55 + 0.05 * peel + worn * 0.2;
390
+ surfH = peel * 0.00003;
391
+ }
392
+ }
393
+ `;
394
+ export function courtMaterial(u) {
395
+ return litMaterial(u, {
396
+ name: 'court', roughness: 0.7, metalness: 0,
397
+ depthBias: [-4, -8], // beats urban paving (-2 / -4) it may be laid on
398
+ pars: 'float courtRough; float courtH;\n' + GLSL_COMMON + MARKINGS_GLSL + SURFACE_GLSL,
399
+ // the lamp list's pools (street lamps, floodlights), faded in by the night factor — urban's lampLight
400
+ emissive: /* glsl */ `totalEmissiveRadiance += diffuseColor.rgb * lampLight(vWorldPos, normalize(inverseTransformDirection(normal, viewMatrix)));`,
401
+ color: /* glsl */ `
402
+ vec2 p = vObj.xz;
403
+ float dL, dA, wA, dD, dOn, zone, field;
404
+ markings(p, dL, dA, wA, dD, dOn, zone, field);
405
+ // table top only (a flat court has uTableY < 0 → always true)
406
+ float top = step(uTableY - 0.002, vObj.y);
407
+ if (top < 0.5) { dL = 1e9; dA = 1e9; dD = 1e9; zone = 0.0; }
408
+ // wear concentrates where the lines are dense and inside the field
409
+ float focus = field * (0.2 + 0.8 * (1.0 - smoothstep(0.0, 1.2, min(dL, dA)))) + zone * 0.3;
410
+ if (int(uSport + 0.5) == 0) // tennis wears behind the baselines (centre), then around the service T
411
+ focus = field * 0.1 + exp(-pow((abs(p.x) - 11.6) / 1.4, 2.0) - pow(p.y / 2.6, 2.0)) + 0.45 * exp(-pow((abs(p.x) - 6.4) / 0.9, 2.0) - pow(p.y / 1.3, 2.0));
412
+ if (int(uSport + 0.5) == 12) // a gridiron wears between the hashes, most between the 20s
413
+ focus = field * (0.15 + 0.85 * exp(-pow(p.y / 6.0, 2.0)) * (0.5 + 0.5 * exp(-pow(p.x / 30.0, 2.0))));
414
+ if (int(uSport + 0.5) == 2) // a pitch wears in the goalmouths and around the centre spot
415
+ focus = field * (0.1 + 0.9 * exp(-pow((abs(p.x) - 52.5) / 7.0, 2.0) - pow(p.y / 9.0, 2.0)) + 0.5 * exp(-dot(p, p) / 60.0));
416
+ float worn = scuffs(vec3(p.x, 0.0, p.y), 1.6, focus) ;
417
+ surfaceAt(p, focus, worn);
418
+ vec3 alb = mix(surfAlb, surfAlb * (uZoneCol / max(uSurfCol, vec3(0.02))), zone);
419
+ // lines, anti-aliased, softened per surface; worn lines thin and fade
420
+ float lw = uLineW * mix(1.0, 0.85, worn);
421
+ float lineA = 1.0 - smoothstep(lw * 0.5 - fwidth(dL) * lineSoft * 0.7, lw * 0.5 + fwidth(dL) * lineSoft * 0.7, abs(dL));
422
+ float accA = 1.0 - smoothstep(wA * 0.5 - fwidth(dA) * lineSoft * 0.7, wA * 0.5 + fwidth(dA) * lineSoft * 0.7, abs(dA));
423
+ float dashA = (1.0 - smoothstep(lw * 0.5 - fwidth(dD) * lineSoft * 0.7, lw * 0.5 + fwidth(dD) * lineSoft * 0.7, abs(dD))) * dOn;
424
+ lineA = max(lineA, dashA);
425
+ lineA *= 1.0 - worn * 0.55;
426
+ alb = mix(alb, uZoneCol, accA * 0.95);
427
+ alb = mix(alb, uLineCol, lineA * 0.95);
428
+ float h = surfH + lineA * lineBump;
429
+ // wet: dark and glossy, puddles in the hollows
430
+ float puddle = smoothstep(1.0 - uWet * 0.6, 1.05 - uWet * 0.6, sfbm2(p * 0.22 + uSeed * 2.0 + 11.0)) * step(0.01, uWet);
431
+ float damp = clamp(uWet * 0.6 + puddle, 0.0, 1.0);
432
+ alb *= mix(1.0, 0.55, damp);
433
+ courtRough = clamp(mix(mix(surfRough, 0.35, lineA * 0.5), 0.06, damp) / max(uGloss, 0.05), 0.03, 1.0);
434
+ courtH = mix(h, 0.0, puddle);
435
+ diffuseColor.rgb *= alb;`,
436
+ rough: 'courtRough',
437
+ normal: /* glsl */ `normal = bumpNormal(normal, courtH);`,
438
+ });
439
+ }
440
+ export function build(u, c) {
441
+ const S = SPORTS[sportKey(c.sport)];
442
+ const q = QUALITY_S[c.quality] || 1;
443
+ const mx = S.runoff[0] * (c.margin ?? 1), mz = S.runoff[1] * (c.margin ?? 1);
444
+ const L = S.L + 2 * mx, W = S.W + 2 * mz;
445
+ const object = new THREE.Group();
446
+ object.name = 'court';
447
+ const geos = [], mats = [], fixtures = [];
448
+ const pair = courtMaterial(u);
449
+ mats.push(pair);
450
+ if (S.table) {
451
+ // a table: the top box (markings on its top face), an apron, four legs, a brace
452
+ const t = S.table, th = 0.025;
453
+ const top = new THREE.BoxGeometry(S.L, th, S.W);
454
+ top.translate(0, t - th / 2, 0);
455
+ geos.push(top);
456
+ object.add(propMesh(top, pair, 'top'));
457
+ u.uTableY.value = t - 0.001;
458
+ const legP = partMaterial(u, { kind: 'metal', slot: 3, tint: '#3a3a3a', roughness: 0.45, metalness: 1, axis: [0, 1, 0] });
459
+ mats.push(legP);
460
+ const legs = [];
461
+ for (const sx of [-1, 1])
462
+ for (const sz of [-1, 1])
463
+ legs.push(box(0.05, t - th, 0.05, [sx * (S.L / 2 - 0.25), (t - th) / 2, sz * (S.W / 2 - 0.2)]));
464
+ legs.push(box(S.L - 0.5, 0.04, 0.04, [0, 0.2, 0]), box(S.L - 0.1, 0.06, 0.02, [0, t - th - 0.03, S.W / 2 - 0.01]), box(S.L - 0.1, 0.06, 0.02, [0, t - th - 0.03, -S.W / 2 + 0.01]));
465
+ const lg = mergeGeos(legs);
466
+ geos.push(lg);
467
+ object.add(propMesh(lg, legP, 'legs'));
468
+ }
469
+ else {
470
+ u.uTableY.value = -1;
471
+ // the slab: a rounded rectangle (hockey rink corners), flat on the ground
472
+ const rc = Math.min((S.corner || 0) + Math.min(mx, mz) * 0.0, L / 2, W / 2);
473
+ const shape = new THREE.Shape();
474
+ const hx = L / 2, hz = W / 2, r = Math.max(0, rc);
475
+ if (r > 0.01) {
476
+ shape.moveTo(-hx + r, -hz);
477
+ shape.lineTo(hx - r, -hz);
478
+ shape.absarc(hx - r, -hz + r, r, -Math.PI / 2, 0, false);
479
+ shape.lineTo(hx, hz - r);
480
+ shape.absarc(hx - r, hz - r, r, 0, Math.PI / 2, false);
481
+ shape.lineTo(-hx + r, hz);
482
+ shape.absarc(-hx + r, hz - r, r, Math.PI / 2, Math.PI, false);
483
+ shape.lineTo(-hx, -hz + r);
484
+ shape.absarc(-hx + r, -hz + r, r, Math.PI, Math.PI * 1.5, false);
485
+ }
486
+ else {
487
+ shape.moveTo(-hx, -hz);
488
+ shape.lineTo(hx, -hz);
489
+ shape.lineTo(hx, hz);
490
+ shape.lineTo(-hx, hz);
491
+ shape.closePath();
492
+ }
493
+ // one slab 6 cm thick (top face at y = 0.02): a single geometry, so nothing can z-fight under the markings
494
+ const g = new THREE.ExtrudeGeometry(shape, { depth: 0.06, bevelEnabled: false, curveSegments: 12 + Math.round(12 * q) });
495
+ g.rotateX(Math.PI / 2);
496
+ g.translate(0, 0.02, 0); // XY → XZ, extruded downward; the top sits 2 cm proud of the ground
497
+ g.computeVertexNormals();
498
+ geos.push(g);
499
+ object.add(propMesh(g, pair, 'surface', { cast: false, receive: true }));
500
+ }
501
+ if (c.fixtures !== false) {
502
+ const G = GOAL_COLORS[S.fixtures[0] && S.fixtures[0].kind];
503
+ if (G) {
504
+ setColor(u.fixtureU.uColor, G.colors[0]);
505
+ setColor(u.fixtureU.uSecondaryColor, G.colors[1]);
506
+ setColor(u.fixtureU.uAccentColor, G.colors[2]);
507
+ }
508
+ for (const f of S.fixtures) {
509
+ const gl = buildGoal(f.kind, u.fixtureU, { quality: q, sag: 0.25, mesh: G ? G.mesh : undefined });
510
+ gl.object.position.set(f.x, S.table ? S.table : 0, 0);
511
+ gl.object.rotation.y = f.rot * Math.PI / 2;
512
+ object.add(gl.object);
513
+ fixtures.push(gl);
514
+ }
515
+ }
516
+ // ---- floodlights: masts with heads that glow at night, registered in the lamp list
517
+ const lamps = [], heads = [], aims = [];
518
+ const F = FLOOD[sportKey(c.sport)] || null;
519
+ if (c.floodlights !== false && F && !S.table) {
520
+ let lensP = null;
521
+ const poleP = partMaterial(u, { kind: 'metal', slot: 3, tint: '#8c9196', roughness: 0.45, metalness: 1, axis: [0, 1, 0] });
522
+ mats.push(poleP);
523
+ const headP = litMaterial(u, {
524
+ name: 'flood-head', roughness: 0.35, metalness: 0.6,
525
+ pars: /* glsl */ `uniform vec3 uFloodColor; uniform float uFloodGlow; uniform float uNight;`,
526
+ color: /* glsl */ `diffuseColor.rgb *= vec3(0.16);`,
527
+ // the lens faces down toward the court: it glows by the night factor, brighter than 1 so bloom can take it
528
+ emissive: /* glsl */ `{ vec3 nw = normalize(inverseTransformDirection(normal, viewMatrix)); totalEmissiveRadiance += uFloodColor * (uFloodGlow * uNight * 6.0 * (1.0 - smoothstep(-0.6, -0.2, nw.y))); }`,
529
+ });
530
+ mats.push(headP);
531
+ const poles = [], headGeos = [];
532
+ const addHead = (pole, head, tiltX, tiltZ, size) => {
533
+ const [w, d] = size;
534
+ headGeos.push(box(w, 0.14, d, head, [tiltX, 0, tiltZ]));
535
+ heads.push(new THREE.Vector3(head[0], head[1] - 0.15, head[2]));
536
+ };
537
+ if (F.mode === 'sides') {
538
+ // poles stand ON the slab's outer edge line: courts laid side by side share them (their poles
539
+ // coincide), as at a real club, instead of two poles a step apart
540
+ const zEdge = W / 2, reach = 1.6;
541
+ for (let i = 0; i < F.n; i++) {
542
+ const x = -L / 2 + L * (i + 0.5) / F.n;
543
+ for (const sz of [-1, 1]) {
544
+ const zp = sz * zEdge, zh = sz * (zEdge - reach);
545
+ poles.push(bar([x, 0, zp], [x, F.h + 0.2, zp], 0.07 + F.h * 0.004, 10), bar([x, F.h + 0.15, zp], [x, F.h + 0.15, zh], 0.04, 8));
546
+ addHead([x, 0, zp], [x, F.h, zh], sz * -0.5, 0, [1.0, 0.55]);
547
+ }
548
+ }
549
+ }
550
+ else {
551
+ // stadium towers: a lattice-like mast (four legs braced to a platform) with a bank of
552
+ // projectors — dark housings, each with a lens that glows by the night factor
553
+ lensP = litMaterial(u, {
554
+ name: 'flood-lens', roughness: 0.2, metalness: 0,
555
+ pars: /* glsl */ `uniform vec3 uFloodColor; uniform float uFloodGlow; uniform float uNight;`,
556
+ color: /* glsl */ `diffuseColor.rgb = mix(vec3(0.55), uFloodColor, 0.3);`,
557
+ emissive: /* glsl */ `totalEmissiveRadiance += uFloodColor * (uFloodGlow * uNight * 9.0);`,
558
+ });
559
+ mats.push(lensP);
560
+ const lensGeos = [];
561
+ const o3 = new THREE.Object3D();
562
+ for (const sx of [-1, 1])
563
+ for (const sz of [-1, 1]) {
564
+ const xp = sx * (L / 2 + F.set), zp = sz * (W / 2 + F.set), H = F.h;
565
+ // the mast: four legs from a wide base to the platform, a few braces
566
+ const b0 = 1.6, b1 = 0.7;
567
+ for (const lx of [-1, 1])
568
+ for (const lz of [-1, 1])
569
+ poles.push(bar([xp + lx * b0, 0, zp + lz * b0], [xp + lx * b1, H - 2.2, zp + lz * b1], 0.16, 8));
570
+ for (let k = 1; k <= 4; k++) {
571
+ const y = (H - 2.2) * k / 5, t = k / 5, bb = b0 + (b1 - b0) * t;
572
+ poles.push(bar([xp - bb, y, zp - bb], [xp + bb, y, zp - bb], 0.07, 6), bar([xp - bb, y, zp + bb], [xp + bb, y, zp + bb], 0.07, 6), bar([xp - bb, y, zp - bb], [xp - bb, y, zp + bb], 0.07, 6), bar([xp + bb, y, zp - bb], [xp + bb, y, zp + bb], 0.07, 6));
573
+ }
574
+ // the bank: a frame facing the field centre, tilted down toward it
575
+ const to = new THREE.Vector3(-xp, -H, -zp).normalize();
576
+ const yaw = Math.atan2(to.x, to.z);
577
+ const bw = F.cols * 1.0 + 0.4, bh = F.rows * 0.85 + 0.4;
578
+ const bankC = new THREE.Vector3(xp, H, zp).addScaledVector(new THREE.Vector3(to.x, 0, to.z).normalize(), 0.9);
579
+ o3.position.copy(bankC);
580
+ o3.rotation.set(0, yaw, 0);
581
+ o3.rotateX(0.55);
582
+ o3.updateMatrix();
583
+ const frame = new THREE.BoxGeometry(bw, bh, 0.25);
584
+ frame.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, -0.45));
585
+ frame.applyMatrix4(o3.matrix);
586
+ poles.push(frame);
587
+ poles.push(box(bw * 0.8, 0.3, 3.2, [xp, H - 2.2, zp], [0, yaw, 0])); // the service platform
588
+ for (let r = 0; r < F.rows; r++)
589
+ for (let k = 0; k < F.cols; k++) {
590
+ const lx = (k - (F.cols - 1) / 2) * 1.0, ly = (r - (F.rows - 1) / 2) * 0.85;
591
+ const m = new THREE.Matrix4().makeTranslation(lx, ly, 0).premultiply(o3.matrix);
592
+ const hous = new THREE.BoxGeometry(0.82, 0.68, 0.5);
593
+ hous.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, -0.1));
594
+ hous.applyMatrix4(m);
595
+ poles.push(hous);
596
+ const lens = new THREE.PlaneGeometry(0.7, 0.56);
597
+ lens.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.152));
598
+ lens.applyMatrix4(m);
599
+ lensGeos.push(lens);
600
+ }
601
+ // one projector beam per field quarter, from the bank
602
+ for (const qx of [-1, 1])
603
+ for (const qz of [-1, 1]) {
604
+ const tgt = new THREE.Vector3(qx * L / 4, 0, qz * W / 4);
605
+ heads.push(bankC.clone());
606
+ aims.push(tgt.sub(bankC).normalize());
607
+ }
608
+ }
609
+ const lg = mergeGeos(lensGeos);
610
+ geos.push(lg);
611
+ object.add(propMesh(lg, lensP, 'projector lenses', { cast: false, receive: false }));
612
+ }
613
+ const pg = mergeGeos(poles);
614
+ geos.push(pg);
615
+ object.add(propMesh(pg, poleP, 'floodlight masts'));
616
+ if (headGeos.length) {
617
+ const hg = mergeGeos(headGeos);
618
+ geos.push(hg);
619
+ object.add(propMesh(hg, headP, 'floodlight heads', { receive: false }));
620
+ }
621
+ heads.forEach((p, i) => lamps.push(registerLamp({ id: aims[i] ? 'court-projector' : 'court-flood', position: p.clone(), color: new THREE.Color(c.floodColor || '#fff1dc'), radius: F.radius, intensity: F.I * (c.floodIntensity ?? 1), base: F.I, on: true,
622
+ aim: aims[i] ? aims[i].clone() : undefined, spread: F.spread })));
623
+ }
624
+ const inst = { mats: mats.map((p) => p.material), fixtures, fixtureMats: fixtures.flatMap((f) => f.mats.map((p) => p.material)), lamps };
625
+ applyConfig(u, c, inst);
626
+ // the playing surface's local bounds: the shared time-lighting hook (useTimeLighting) gathers
627
+ // the lamps that reach it, in world space, whenever the lamp list changes or the court moves
628
+ const localBox = new THREE.Box3(new THREE.Vector3(-L / 2, -0.1, -W / 2), new THREE.Vector3(L / 2, (S.table || 0) + 0.2, W / 2));
629
+ const lastWorld = new THREE.Matrix4();
630
+ return {
631
+ object,
632
+ materials: inst.mats.concat(inst.fixtureMats),
633
+ /** the uniform sets that take the sun block (uNight) and gather lamps — the TimeControls pattern */
634
+ uniformSets: [u],
635
+ bounds: function () { return localBox; },
636
+ apply: function (cfg) { applyConfig(u, cfg, inst); },
637
+ /** per frame: the shared clock, and this court's floodlights kept at their world positions (like urban's lamps) */
638
+ update: function (dt) {
639
+ updateCommon(u, dt);
640
+ if (!lamps.length)
641
+ return;
642
+ object.updateWorldMatrix(true, false);
643
+ if (!lastWorld.equals(object.matrixWorld)) {
644
+ lastWorld.copy(object.matrixWorld);
645
+ placeLamps(lamps, heads, object.matrixWorld); // positions, reach and falloff follow the object's scale
646
+ for (let i = 0; i < lamps.length; i++)
647
+ if (aims[i])
648
+ lamps[i].aim.copy(aims[i]).transformDirection(object.matrixWorld);
649
+ touchLamps();
650
+ }
651
+ },
652
+ stats: { size: [L, W], fixtures: fixtures.length, floodlights: lamps.length },
653
+ dispose: function () { for (const Lp of lamps)
654
+ unregisterLamp(Lp); geos.forEach((g) => g.dispose()); fixtures.forEach((f) => f.dispose()); mats.forEach(disposePair); },
655
+ };
656
+ }
657
+ /** Knobs whose change rebuilds the object (dispose + build); every other knob is a live push through apply. */
658
+ export const STRUCTURAL = ['sport', 'surface', 'margin', 'fixtures', 'floodlights', 'quality', 'seed'];