@woosh/meep-engine 3.56.0 → 3.57.0

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 (625) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/DUPLICATION_REVIEW_2026_10_24.md +1072 -0
  4. package/src/core/binary/BitSet.js +1 -1
  5. package/src/core/binary/FLOAT16_MAX.d.ts +2 -0
  6. package/src/core/binary/FLOAT16_MAX.d.ts.map +1 -0
  7. package/src/core/binary/FLOAT16_MAX.js +1 -0
  8. package/src/core/binary/base58/base58_encode.js +2 -2
  9. package/src/core/binary/type/TypeBuilder.js +1 -1
  10. package/src/core/bvh2/bvh3/BVH.js +1 -1
  11. package/src/core/bvh2/bvh3/ebvh_optimize_treelet.d.ts.map +1 -1
  12. package/src/core/bvh2/bvh3/ebvh_optimize_treelet.js +7 -3
  13. package/src/core/bvh2/bvh3/ebvh_update_hierarchy_bounds.d.ts.map +1 -1
  14. package/src/core/bvh2/bvh3/ebvh_update_hierarchy_bounds.js +5 -1
  15. package/src/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.d.ts.map +1 -1
  16. package/src/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js +195 -246
  17. package/src/core/collection/CuckooFilter.js +1 -1
  18. package/src/core/collection/array/isArrayEqual.d.ts.map +1 -1
  19. package/src/core/collection/array/isArrayEqual.js +33 -58
  20. package/src/core/collection/array/typed/array_buffer_hash.d.ts.map +1 -1
  21. package/src/core/collection/array/typed/array_buffer_hash.js +41 -39
  22. package/src/core/collection/array/typed/float32_array_hash.js +38 -38
  23. package/src/core/collection/array/typed/uint16_array_hash.d.ts +2 -1
  24. package/src/core/collection/array/typed/uint16_array_hash.d.ts.map +1 -1
  25. package/src/core/collection/array/typed/uint16_array_hash.js +30 -29
  26. package/src/core/collection/array/typed/uint32_array_hash.d.ts +2 -1
  27. package/src/core/collection/array/typed/uint32_array_hash.d.ts.map +1 -1
  28. package/src/core/collection/array/typed/uint32_array_hash.js +21 -20
  29. package/src/core/collection/array/typed/uint8_array_hash.d.ts +2 -1
  30. package/src/core/collection/array/typed/uint8_array_hash.d.ts.map +1 -1
  31. package/src/core/collection/array/typed/uint8_array_hash.js +37 -36
  32. package/src/core/collection/array/typed/uint_array_for_count.js +2 -2
  33. package/src/core/collection/list/List.d.ts.map +1 -1
  34. package/src/core/collection/list/List.js +3 -19
  35. package/src/core/collection/table/RowFirstTable.d.ts.map +1 -1
  36. package/src/core/collection/table/RowFirstTable.js +4 -28
  37. package/src/core/debug/matchers/DescribeAs.d.ts.map +1 -1
  38. package/src/core/debug/matchers/DescribeAs.js +62 -64
  39. package/src/core/function/FunctionCompiler.js +4 -4
  40. package/src/core/function/objectsEqual.d.ts.map +1 -1
  41. package/src/core/function/objectsEqual.js +3 -2
  42. package/src/core/geom/2d/bvh/BVH2D.d.ts.map +1 -1
  43. package/src/core/geom/2d/bvh/BVH2D.js +4 -3
  44. package/src/core/geom/2d/convex-hull/convex_hull_fixed_count_2d_relaxation.d.ts +5 -2
  45. package/src/core/geom/2d/convex-hull/convex_hull_fixed_count_2d_relaxation.d.ts.map +1 -1
  46. package/src/core/geom/2d/convex-hull/convex_hull_fixed_count_2d_relaxation.js +49 -11
  47. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.d.ts.map +1 -1
  48. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.js +122 -159
  49. package/src/core/geom/2d/hash-grid/shg_query_elements_line.d.ts.map +1 -1
  50. package/src/core/geom/2d/hash-grid/shg_query_elements_line.js +8 -5
  51. package/src/core/geom/2d/polygon/polygon2_rasterize_coverage.d.ts +18 -0
  52. package/src/core/geom/2d/polygon/polygon2_rasterize_coverage.d.ts.map +1 -0
  53. package/src/core/geom/2d/polygon/polygon2_rasterize_coverage.js +97 -0
  54. package/src/core/geom/2d/r-tree/StaticR2Tree.js +2 -2
  55. package/src/core/geom/2d/triangle/tri2_rasterize_coverage.d.ts +21 -0
  56. package/src/core/geom/2d/triangle/tri2_rasterize_coverage.d.ts.map +1 -0
  57. package/src/core/geom/2d/triangle/tri2_rasterize_coverage.js +54 -0
  58. package/src/core/geom/3d/aabb/aabb3_build_frustum.d.ts.map +1 -1
  59. package/src/core/geom/3d/aabb/aabb3_build_frustum.js +20 -53
  60. package/src/core/geom/3d/aabb/aabb3_score_boxes_SAH.d.ts +1 -1
  61. package/src/core/geom/3d/aabb/aabb3_score_boxes_SAH.d.ts.map +1 -1
  62. package/src/core/geom/3d/aabb/aabb3_score_boxes_SAH.js +11 -42
  63. package/src/core/geom/3d/atlas/pack/atlas_pack_charts.d.ts.map +1 -1
  64. package/src/core/geom/3d/atlas/pack/atlas_pack_charts.js +30 -32
  65. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.d.ts.map +1 -1
  66. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +1 -5
  67. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.d.ts.map +1 -1
  68. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +1 -15
  69. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts.map +1 -1
  70. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +105 -116
  71. package/src/core/geom/3d/line/line3_compute_segment_point_distance_sqr.d.ts.map +1 -1
  72. package/src/core/geom/3d/line/line3_compute_segment_point_distance_sqr.js +36 -54
  73. package/src/core/geom/3d/plane/plane3_compute_intersection_objects.d.ts +5 -10
  74. package/src/core/geom/3d/plane/plane3_compute_intersection_objects.d.ts.map +1 -1
  75. package/src/core/geom/3d/plane/plane3_compute_intersection_objects.js +20 -82
  76. package/src/core/geom/3d/plane/plane3_slerp.d.ts.map +1 -1
  77. package/src/core/geom/3d/plane/plane3_slerp.js +87 -102
  78. package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts +12 -0
  79. package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts.map +1 -1
  80. package/src/core/geom/3d/polygon/polygon3_compute_normal.js +107 -60
  81. package/src/core/geom/3d/ray/ray3_compute_closest_point.d.ts.map +1 -1
  82. package/src/core/geom/3d/ray/ray3_compute_closest_point.js +37 -43
  83. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  84. package/src/core/geom/3d/shape/ConvexHullShape3D.js +5 -16
  85. package/src/core/geom/3d/shape/MeshShape3D.js +2 -56
  86. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  87. package/src/core/geom/3d/shape/TransformedShape3D.js +10 -7
  88. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  89. package/src/core/geom/3d/shape/UnionShape3D.js +9 -3
  90. package/src/core/geom/3d/shape/json/shape_to_type.js +2 -2
  91. package/src/core/geom/3d/sphere/harmonics/SH3_COEFFICIENTS.js +17 -17
  92. package/src/core/geom/3d/tetrahedra/TetrahedralMesh.d.ts.map +1 -1
  93. package/src/core/geom/3d/tetrahedra/TetrahedralMesh.js +6 -2
  94. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.d.ts.map +1 -1
  95. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +3 -2
  96. package/src/core/geom/3d/tetrahedra/tetrahedron_contains_point.d.ts +20 -2
  97. package/src/core/geom/3d/tetrahedra/tetrahedron_contains_point.d.ts.map +1 -1
  98. package/src/core/geom/3d/tetrahedra/tetrahedron_contains_point.js +52 -37
  99. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.d.ts.map +1 -1
  100. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js +40 -56
  101. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
  102. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +251 -260
  103. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts.map +1 -1
  104. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.js +104 -113
  105. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_resolve_t_junctions.d.ts.map +1 -1
  106. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_resolve_t_junctions.js +227 -238
  107. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
  108. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +15 -24
  109. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.d.ts.map +1 -1
  110. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js +31 -115
  111. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_fuse_duplicate_edges.d.ts.map +1 -1
  112. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_fuse_duplicate_edges.js +142 -150
  113. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_replace.d.ts.map +1 -1
  114. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_replace.js +151 -194
  115. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_incenter.d.ts.map +1 -1
  116. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_incenter.js +60 -85
  117. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_distance_field.d.ts.map +1 -1
  118. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_distance_field.js +156 -171
  119. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.d.ts.map +1 -1
  120. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.js +8 -44
  121. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.d.ts.map +1 -1
  122. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.js +2 -7
  123. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.d.ts.map +1 -1
  124. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +3 -2
  125. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_segment_penetrates_surface.d.ts.map +1 -1
  126. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_segment_penetrates_surface.js +128 -133
  127. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.d.ts +8 -14
  128. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.d.ts.map +1 -1
  129. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.js +36 -82
  130. package/src/core/geom/3d/triangle/tri3_ray_intersection.d.ts.map +1 -1
  131. package/src/core/geom/3d/triangle/tri3_ray_intersection.js +7 -144
  132. package/src/core/geom/ConicRay.d.ts.map +1 -1
  133. package/src/core/geom/ConicRay.js +230 -237
  134. package/src/core/localization/LocaleDataset.d.ts +6 -0
  135. package/src/core/localization/LocaleDataset.d.ts.map +1 -1
  136. package/src/core/localization/LocaleDataset.js +48 -37
  137. package/src/core/localization/LocalizationEngine.d.ts.map +1 -1
  138. package/src/core/localization/LocalizationEngine.js +3 -2
  139. package/src/core/math/lookup/ParameterLookupTable.d.ts.map +1 -1
  140. package/src/core/math/lookup/ParameterLookupTable.js +3 -1
  141. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -1
  142. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +330 -400
  143. package/src/core/math/spline/spline3_hermite_nearest_point.d.ts +16 -0
  144. package/src/core/math/spline/spline3_hermite_nearest_point.d.ts.map +1 -1
  145. package/src/core/math/spline/spline3_hermite_nearest_point.js +134 -167
  146. package/src/core/model/DebouncedObservedBoolean.d.ts +1 -2
  147. package/src/core/model/DebouncedObservedBoolean.d.ts.map +1 -1
  148. package/src/core/model/DebouncedObservedBoolean.js +1 -1
  149. package/src/core/model/node-graph/node/NodeInstance.d.ts.map +1 -1
  150. package/src/core/model/node-graph/node/NodeInstance.js +2 -7
  151. package/src/core/model/node-graph/node/parameter/NodeParameterDescription.js +1 -1
  152. package/src/core/model/object/optional_equality.d.ts +3 -3
  153. package/src/core/model/object/optional_equality.d.ts.map +1 -1
  154. package/src/core/model/object/optional_equality.js +10 -27
  155. package/src/core/model/object/write_property.js +1 -1
  156. package/src/core/model/reactive/evaluation/MultiPredicateEvaluator.js +2 -2
  157. package/src/core/primitives/strings/string_compute_byte_size.d.ts +3 -3
  158. package/src/core/primitives/strings/string_compute_byte_size.d.ts.map +1 -1
  159. package/src/core/primitives/strings/string_compute_byte_size.js +8 -21
  160. package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
  161. package/src/core/process/executor/ConcurrentExecutor.js +14 -2
  162. package/src/engine/achievements/gateway/StorageAchievementGateway.d.ts +6 -3
  163. package/src/engine/achievements/gateway/StorageAchievementGateway.d.ts.map +1 -1
  164. package/src/engine/achievements/gateway/StorageAchievementGateway.js +31 -75
  165. package/src/engine/asset/loaders/SVGAssetLoader.d.ts.map +1 -1
  166. package/src/engine/asset/loaders/SVGAssetLoader.js +33 -31
  167. package/src/engine/control/first-person/math/criticallyDampedSpring.d.ts.map +1 -1
  168. package/src/engine/control/first-person/math/criticallyDampedSpring.js +3 -14
  169. package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
  170. package/src/engine/ecs/EntityComponentDataset.js +4 -1
  171. package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.d.ts +0 -2
  172. package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.d.ts.map +1 -1
  173. package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.js +3 -3
  174. package/src/engine/ecs/terrain/ecs/layers/TerrainLayer.js +2 -2
  175. package/src/engine/graphics/sh3/path_tracer/geometry/sort_triangles_by_distance_to_point.js +1 -1
  176. package/src/engine/graphics/texture/sampler/sampler2d_channel_compute_min_indices.js +1 -1
  177. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  178. package/src/engine/graphics3/WaterSystem.js +2 -1
  179. package/src/engine/graphics3/prefab/prefab_skin_frame.d.ts.map +1 -1
  180. package/src/engine/graphics3/prefab/prefab_skin_frame.js +1 -12
  181. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts +1 -1
  182. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
  183. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +141 -137
  184. package/src/engine/input/ecs/systems/InputControllerSystem.d.ts.map +1 -1
  185. package/src/engine/input/ecs/systems/InputControllerSystem.js +13 -3
  186. package/src/engine/input/ecs/systems/InputSystem.d.ts.map +1 -1
  187. package/src/engine/input/ecs/systems/InputSystem.js +2 -1
  188. package/src/engine/intelligence/behavior/composite/ParallelBehavior.d.ts +1 -1
  189. package/src/engine/intelligence/behavior/composite/ParallelBehavior.d.ts.map +1 -1
  190. package/src/engine/intelligence/behavior/composite/ParallelBehavior.js +4 -1
  191. package/src/engine/intelligence/behavior/decorator/InvertStatusBehavior.d.ts.map +1 -1
  192. package/src/engine/intelligence/behavior/decorator/InvertStatusBehavior.js +3 -1
  193. package/src/engine/intelligence/behavior/decorator/RepeatBehavior.d.ts.map +1 -1
  194. package/src/engine/intelligence/behavior/decorator/RepeatBehavior.js +3 -1
  195. package/src/engine/intelligence/behavior/decorator/RepeatBehaviorSerializationAdapter.d.ts.map +1 -1
  196. package/src/engine/intelligence/behavior/decorator/RepeatBehaviorSerializationAdapter.js +39 -33
  197. package/src/engine/intelligence/behavior/util/ConditionalBehavior.d.ts +5 -0
  198. package/src/engine/intelligence/behavior/util/ConditionalBehavior.d.ts.map +1 -1
  199. package/src/engine/intelligence/behavior/util/ConditionalBehavior.js +93 -75
  200. package/src/engine/intelligence/behavior/util/RotationBehavior.d.ts.map +1 -1
  201. package/src/engine/intelligence/behavior/util/RotationBehavior.js +79 -76
  202. package/src/engine/intelligence/blackboard/BlackboardStack.d.ts +1 -0
  203. package/src/engine/intelligence/blackboard/BlackboardStack.d.ts.map +1 -1
  204. package/src/engine/intelligence/blackboard/BlackboardStack.js +159 -123
  205. package/src/engine/intelligence/mcts/StateNode.d.ts.map +1 -1
  206. package/src/engine/intelligence/mcts/StateNode.js +366 -364
  207. package/src/engine/intelligence/resource/ResourceAllocationSolver.js +1 -1
  208. package/src/engine/navigation/ecs/components/Path.d.ts.map +1 -1
  209. package/src/engine/navigation/ecs/components/Path.js +2 -1
  210. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
  211. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +5 -2
  212. package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -1
  213. package/src/engine/physics/body/SolverBodyState.js +2 -6
  214. package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
  215. package/src/engine/physics/ccd/linear_sweep.js +3 -16
  216. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts.map +1 -1
  217. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.js +2 -16
  218. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts +13 -1
  219. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
  220. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +15 -4
  221. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  222. package/src/engine/physics/ecs/PhysicsSystem.js +2 -5
  223. package/src/engine/physics/inertia/locked_inverse_inertia.d.ts +12 -0
  224. package/src/engine/physics/inertia/locked_inverse_inertia.d.ts.map +1 -0
  225. package/src/engine/physics/inertia/locked_inverse_inertia.js +22 -0
  226. package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
  227. package/src/engine/physics/integration/integrate_velocity.js +3 -18
  228. package/src/engine/physics/mls-mpm/MLS_MPM.d.ts +1 -0
  229. package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
  230. package/src/engine/physics/mls-mpm/MLS_MPM.js +5 -4
  231. package/src/engine/physics/narrowphase/convex_decomposition.d.ts.map +1 -1
  232. package/src/engine/physics/narrowphase/convex_decomposition.js +7 -16
  233. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
  234. package/src/engine/physics/vehicle/RaycastVehicle.js +15 -5
  235. package/src/engine/save/StorageBackedList.d.ts +5 -0
  236. package/src/engine/save/StorageBackedList.d.ts.map +1 -1
  237. package/src/engine/save/StorageBackedList.js +161 -144
  238. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  239. package/src/engine/simulation/Ticker.js +394 -391
  240. package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts.map +1 -1
  241. package/src/engine/sound/simulation/probe/AcousticProbeField.js +6 -19
  242. package/src/engine/sound/simulation/probe/probe_edge_visible.d.ts +15 -0
  243. package/src/engine/sound/simulation/probe/probe_edge_visible.d.ts.map +1 -1
  244. package/src/engine/sound/simulation/probe/probe_edge_visible.js +59 -41
  245. package/src/engine/sound/simulation/probe/probe_refine_connectivity.d.ts.map +1 -1
  246. package/src/engine/sound/simulation/probe/probe_refine_connectivity.js +164 -181
  247. package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.d.ts.map +1 -1
  248. package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.js +20 -5
  249. package/src/format/image/avif/av1/decode/Av1FrameContext.d.ts.map +1 -1
  250. package/src/format/image/avif/av1/decode/Av1FrameContext.js +3 -10
  251. package/src/format/image/avif/av1/decode/reconstruct.d.ts +20 -0
  252. package/src/format/image/avif/av1/decode/reconstruct.d.ts.map +1 -1
  253. package/src/format/image/avif/av1/decode/reconstruct.js +22 -9
  254. package/src/format/image/avif/av1/encode/choose_restoration.js +2 -18
  255. package/src/format/image/avif/av1/encode/quantise.d.ts.map +1 -1
  256. package/src/format/image/avif/av1/encode/quantise.js +1 -55
  257. package/src/format/image/avif/av1/filter/loop_restoration.d.ts +9 -0
  258. package/src/format/image/avif/av1/filter/loop_restoration.d.ts.map +1 -1
  259. package/src/format/image/avif/av1/filter/loop_restoration.js +1 -1
  260. package/src/format/texture/basis/transcode/etc1s_to_bc7.js +2 -33
  261. package/src/format/texture/basis/transcode/fit_endpoints.d.ts +11 -0
  262. package/src/format/texture/basis/transcode/fit_endpoints.d.ts.map +1 -1
  263. package/src/format/texture/basis/transcode/fit_endpoints.js +1 -1
  264. package/src/format/texture/basis/uastc/uastc_decode_block.d.ts.map +1 -1
  265. package/src/format/texture/basis/uastc/uastc_decode_block.js +2 -19
  266. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +220 -0
  267. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +172 -0
  268. package/src/generation/foliage/FOLIAGE_DESIGN.md +165 -15
  269. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +158 -0
  270. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +248 -0
  271. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +694 -0
  272. package/src/generation/foliage/analysis/CrownOpacity.d.ts +118 -0
  273. package/src/generation/foliage/analysis/CrownOpacity.d.ts.map +1 -0
  274. package/src/generation/foliage/analysis/CrownOpacity.js +182 -0
  275. package/src/generation/foliage/analysis/crown_opacity_frames.d.ts +10 -0
  276. package/src/generation/foliage/analysis/crown_opacity_frames.d.ts.map +1 -0
  277. package/src/generation/foliage/analysis/crown_opacity_frames.js +132 -0
  278. package/src/generation/foliage/analysis/crown_opacity_measure.d.ts +51 -0
  279. package/src/generation/foliage/analysis/crown_opacity_measure.d.ts.map +1 -0
  280. package/src/generation/foliage/analysis/crown_opacity_measure.js +649 -0
  281. package/src/generation/foliage/analysis/foliage_measure_leaf_area.d.ts +66 -0
  282. package/src/generation/foliage/analysis/foliage_measure_leaf_area.d.ts.map +1 -0
  283. package/src/generation/foliage/analysis/foliage_measure_leaf_area.js +192 -0
  284. package/src/generation/foliage/analysis/tree_crown_opacity.d.ts +27 -0
  285. package/src/generation/foliage/analysis/tree_crown_opacity.d.ts.map +1 -0
  286. package/src/generation/foliage/analysis/tree_crown_opacity.js +58 -0
  287. package/src/generation/foliage/animation/build_tree_wind_rig.d.ts +3 -2
  288. package/src/generation/foliage/animation/build_tree_wind_rig.d.ts.map +1 -1
  289. package/src/generation/foliage/animation/build_tree_wind_rig.js +12 -30
  290. package/src/generation/foliage/animation/tree_wind_bend_limit.d.ts +12 -0
  291. package/src/generation/foliage/animation/tree_wind_bend_limit.d.ts.map +1 -0
  292. package/src/generation/foliage/animation/tree_wind_bend_limit.js +13 -0
  293. package/src/generation/foliage/asset/TreeAsset.d.ts +5 -0
  294. package/src/generation/foliage/asset/TreeAsset.d.ts.map +1 -1
  295. package/src/generation/foliage/asset/TreeAsset.js +6 -0
  296. package/src/generation/foliage/asset/generate_tree.d.ts.map +1 -1
  297. package/src/generation/foliage/asset/generate_tree.js +23 -6
  298. package/src/generation/foliage/config/{BranchLevelConfig.d.ts → BranchGeneratorConfig.d.ts} +61 -75
  299. package/src/generation/foliage/config/BranchGeneratorConfig.d.ts.map +1 -0
  300. package/src/generation/foliage/config/{BranchLevelConfig.js → BranchGeneratorConfig.js} +83 -126
  301. package/src/generation/foliage/config/BranchSpawnRule.d.ts +118 -0
  302. package/src/generation/foliage/config/BranchSpawnRule.d.ts.map +1 -0
  303. package/src/generation/foliage/config/BranchSpawnRule.js +217 -0
  304. package/src/generation/foliage/config/FoliageCardType.d.ts +1 -1
  305. package/src/generation/foliage/config/FoliageCardType.js +2 -2
  306. package/src/generation/foliage/config/FoliageConfig.d.ts +23 -45
  307. package/src/generation/foliage/config/FoliageConfig.d.ts.map +1 -1
  308. package/src/generation/foliage/config/FoliageConfig.js +47 -93
  309. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts +12 -20
  310. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts.map +1 -1
  311. package/src/generation/foliage/config/FoliageMaterialConfig.js +261 -279
  312. package/src/generation/foliage/config/FoliageSpawnRule.d.ts +77 -0
  313. package/src/generation/foliage/config/FoliageSpawnRule.d.ts.map +1 -0
  314. package/src/generation/foliage/config/FoliageSpawnRule.js +135 -0
  315. package/src/generation/foliage/config/FoliageTileContent.d.ts +9 -0
  316. package/src/generation/foliage/config/FoliageTileContent.d.ts.map +1 -0
  317. package/src/generation/foliage/config/FoliageTileContent.js +12 -0
  318. package/src/generation/foliage/config/LeafApexShape.d.ts +12 -0
  319. package/src/generation/foliage/config/LeafApexShape.d.ts.map +1 -0
  320. package/src/generation/foliage/config/LeafApexShape.js +17 -0
  321. package/src/generation/foliage/config/LeafArchitecture.d.ts +155 -0
  322. package/src/generation/foliage/config/LeafArchitecture.d.ts.map +1 -0
  323. package/src/generation/foliage/config/LeafArchitecture.js +288 -0
  324. package/src/generation/foliage/config/LeafBaseShape.d.ts +14 -0
  325. package/src/generation/foliage/config/LeafBaseShape.d.ts.map +1 -0
  326. package/src/generation/foliage/config/LeafBaseShape.js +21 -0
  327. package/src/generation/foliage/config/LeafLobation.d.ts +10 -0
  328. package/src/generation/foliage/config/LeafLobation.d.ts.map +1 -0
  329. package/src/generation/foliage/config/LeafLobation.js +14 -0
  330. package/src/generation/foliage/config/PhyllotaxisMode.d.ts +11 -0
  331. package/src/generation/foliage/config/PhyllotaxisMode.d.ts.map +1 -1
  332. package/src/generation/foliage/config/PhyllotaxisMode.js +36 -0
  333. package/src/generation/foliage/config/ShootBudZone.d.ts +105 -0
  334. package/src/generation/foliage/config/ShootBudZone.d.ts.map +1 -0
  335. package/src/generation/foliage/config/ShootBudZone.js +219 -0
  336. package/src/generation/foliage/config/ShootGrowthConfig.d.ts +84 -0
  337. package/src/generation/foliage/config/ShootGrowthConfig.d.ts.map +1 -0
  338. package/src/generation/foliage/config/ShootGrowthConfig.js +146 -0
  339. package/src/generation/foliage/config/ShootSystemRule.d.ts +144 -0
  340. package/src/generation/foliage/config/ShootSystemRule.d.ts.map +1 -0
  341. package/src/generation/foliage/config/ShootSystemRule.js +316 -0
  342. package/src/generation/foliage/config/TreeLODConfig.d.ts +1 -1
  343. package/src/generation/foliage/config/TreeLODConfig.js +1 -1
  344. package/src/generation/foliage/config/TreeSpeciesConfig.d.ts +40 -8
  345. package/src/generation/foliage/config/TreeSpeciesConfig.d.ts.map +1 -1
  346. package/src/generation/foliage/config/TreeSpeciesConfig.js +124 -68
  347. package/src/generation/foliage/config/TreeWindConfig.d.ts +23 -10
  348. package/src/generation/foliage/config/TreeWindConfig.d.ts.map +1 -1
  349. package/src/generation/foliage/config/TreeWindConfig.js +34 -25
  350. package/src/generation/foliage/index.d.ts +19 -2
  351. package/src/generation/foliage/index.js +63 -44
  352. package/src/generation/foliage/material/FoliageAtlasCutout.d.ts +59 -0
  353. package/src/generation/foliage/material/FoliageAtlasCutout.d.ts.map +1 -0
  354. package/src/generation/foliage/material/FoliageAtlasCutout.js +93 -0
  355. package/src/generation/foliage/material/compute_foliage_variant_atlas_layout.d.ts +26 -0
  356. package/src/generation/foliage/material/compute_foliage_variant_atlas_layout.d.ts.map +1 -0
  357. package/src/generation/foliage/material/compute_foliage_variant_atlas_layout.js +63 -0
  358. package/src/generation/foliage/material/foliage_atlas_cutout.d.ts +34 -0
  359. package/src/generation/foliage/material/foliage_atlas_cutout.d.ts.map +1 -0
  360. package/src/generation/foliage/material/foliage_atlas_cutout.js +174 -0
  361. package/src/generation/foliage/material/foliage_tile.d.ts +110 -0
  362. package/src/generation/foliage/material/foliage_tile.d.ts.map +1 -0
  363. package/src/generation/foliage/material/foliage_tile.js +286 -0
  364. package/src/generation/foliage/material/leaf_geometry.d.ts +103 -0
  365. package/src/generation/foliage/material/leaf_geometry.d.ts.map +1 -0
  366. package/src/generation/foliage/material/leaf_geometry.js +528 -0
  367. package/src/generation/foliage/material/make_foliage_material.d.ts +13 -33
  368. package/src/generation/foliage/material/make_foliage_material.d.ts.map +1 -1
  369. package/src/generation/foliage/material/make_foliage_material.js +356 -605
  370. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts +39 -4
  371. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts.map +1 -1
  372. package/src/generation/foliage/mesh/foliage_collect_attachments.js +637 -569
  373. package/src/generation/foliage/mesh/interpolate_ring_skin_weights4.d.ts +48 -0
  374. package/src/generation/foliage/mesh/interpolate_ring_skin_weights4.d.ts.map +1 -0
  375. package/src/generation/foliage/mesh/interpolate_ring_skin_weights4.js +232 -0
  376. package/src/generation/foliage/mesh/tree_mesh_build_bark.d.ts.map +1 -1
  377. package/src/generation/foliage/mesh/tree_mesh_build_bark.js +1023 -1049
  378. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts +9 -2
  379. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts.map +1 -1
  380. package/src/generation/foliage/mesh/tree_mesh_build_foliage.js +467 -454
  381. package/src/generation/foliage/presets/make_tree_species_preset.d.ts.map +1 -1
  382. package/src/generation/foliage/presets/make_tree_species_preset.js +1648 -870
  383. package/src/generation/foliage/skeleton/ShootFrameworkHash.d.ts +35 -0
  384. package/src/generation/foliage/skeleton/ShootFrameworkHash.d.ts.map +1 -0
  385. package/src/generation/foliage/skeleton/ShootFrameworkHash.js +129 -0
  386. package/src/generation/foliage/skeleton/ShootGirth.d.ts +51 -0
  387. package/src/generation/foliage/skeleton/ShootGirth.d.ts.map +1 -0
  388. package/src/generation/foliage/skeleton/ShootGirth.js +212 -0
  389. package/src/generation/foliage/skeleton/ShootLightField.d.ts +73 -0
  390. package/src/generation/foliage/skeleton/ShootLightField.d.ts.map +1 -0
  391. package/src/generation/foliage/skeleton/ShootLightField.js +197 -0
  392. package/src/generation/foliage/skeleton/TreeSkeleton.d.ts +106 -3
  393. package/src/generation/foliage/skeleton/TreeSkeleton.d.ts.map +1 -1
  394. package/src/generation/foliage/skeleton/TreeSkeleton.js +217 -39
  395. package/src/generation/foliage/skeleton/flare_lobe_phase_offset.d.ts +13 -0
  396. package/src/generation/foliage/skeleton/flare_lobe_phase_offset.d.ts.map +1 -0
  397. package/src/generation/foliage/skeleton/flare_lobe_phase_offset.js +16 -0
  398. package/src/generation/foliage/skeleton/foliage_card_density.d.ts +71 -0
  399. package/src/generation/foliage/skeleton/foliage_card_density.d.ts.map +1 -0
  400. package/src/generation/foliage/skeleton/foliage_card_density.js +101 -0
  401. package/src/generation/foliage/skeleton/foliage_senescence.d.ts +101 -0
  402. package/src/generation/foliage/skeleton/foliage_senescence.d.ts.map +1 -0
  403. package/src/generation/foliage/skeleton/foliage_senescence.js +149 -0
  404. package/src/generation/foliage/skeleton/foliage_unit_cards.d.ts +76 -0
  405. package/src/generation/foliage/skeleton/foliage_unit_cards.d.ts.map +1 -0
  406. package/src/generation/foliage/skeleton/foliage_unit_cards.js +95 -0
  407. package/src/generation/foliage/skeleton/shoot_demography.d.ts +22 -0
  408. package/src/generation/foliage/skeleton/shoot_demography.d.ts.map +1 -0
  409. package/src/generation/foliage/skeleton/shoot_demography.js +96 -0
  410. package/src/generation/foliage/skeleton/shoot_geometry.d.ts +66 -0
  411. package/src/generation/foliage/skeleton/shoot_geometry.d.ts.map +1 -0
  412. package/src/generation/foliage/skeleton/shoot_geometry.js +109 -0
  413. package/src/generation/foliage/skeleton/shoot_systems_simulate.d.ts +166 -0
  414. package/src/generation/foliage/skeleton/shoot_systems_simulate.d.ts.map +1 -0
  415. package/src/generation/foliage/skeleton/shoot_systems_simulate.js +588 -0
  416. package/src/generation/foliage/skeleton/tree_skeleton_assign_skin_joints.d.ts +28 -12
  417. package/src/generation/foliage/skeleton/tree_skeleton_assign_skin_joints.d.ts.map +1 -1
  418. package/src/generation/foliage/skeleton/tree_skeleton_assign_skin_joints.js +137 -102
  419. package/src/generation/foliage/skeleton/tree_skeleton_compute_joint_mechanics.d.ts +112 -0
  420. package/src/generation/foliage/skeleton/tree_skeleton_compute_joint_mechanics.d.ts.map +1 -0
  421. package/src/generation/foliage/skeleton/tree_skeleton_compute_joint_mechanics.js +408 -0
  422. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.d.ts.map +1 -1
  423. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.js +208 -206
  424. package/src/generation/foliage/skeleton/tree_skeleton_grow.d.ts +5 -2
  425. package/src/generation/foliage/skeleton/tree_skeleton_grow.d.ts.map +1 -1
  426. package/src/generation/foliage/skeleton/tree_skeleton_grow.js +2048 -1871
  427. package/src/generation/foliage/skeleton/tree_skeleton_grow_shoots.d.ts +17 -0
  428. package/src/generation/foliage/skeleton/tree_skeleton_grow_shoots.d.ts.map +1 -0
  429. package/src/generation/foliage/skeleton/tree_skeleton_grow_shoots.js +231 -0
  430. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_motion.d.ts +14 -0
  431. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_motion.d.ts.map +1 -0
  432. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_motion.js +26 -0
  433. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_parents.d.ts +10 -0
  434. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_parents.d.ts.map +1 -0
  435. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_parents.js +20 -0
  436. package/src/generation/grid/generation/road/GridTaskGenerateRoads.d.ts.map +1 -1
  437. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +4 -1
  438. package/src/generation/markers/GridCellActionPlaceMarkerGroup.d.ts +1 -0
  439. package/src/generation/markers/GridCellActionPlaceMarkerGroup.d.ts.map +1 -1
  440. package/src/generation/markers/GridCellActionPlaceMarkerGroup.js +98 -86
  441. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +263 -0
  442. package/src/shade/descriptor/WebGPUExtensionType.d.ts +17 -0
  443. package/src/shade/descriptor/WebGPUExtensionType.d.ts.map +1 -1
  444. package/src/shade/descriptor/WebGPUExtensionType.js +31 -1
  445. package/src/shade/playground/foliage_wind/FoliageRigOverlay.d.ts +37 -0
  446. package/src/shade/playground/foliage_wind/FoliageRigOverlay.d.ts.map +1 -0
  447. package/src/shade/playground/foliage_wind/FoliageRigOverlay.js +491 -0
  448. package/src/shade/playground/foliage_wind/foliage_world.d.ts.map +1 -1
  449. package/src/shade/playground/foliage_wind/foliage_world.js +600 -602
  450. package/src/shade/playground/foliage_wind/index.html +6 -0
  451. package/src/shade/playground/foliage_wind/main.js +14 -0
  452. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  453. package/src/shade/renderer/animation/GPUAnimationManager.js +2 -1
  454. package/src/shade/renderer/animation/foliage/FoliageWindJoint.d.ts +2 -1
  455. package/src/shade/renderer/animation/foliage/FoliageWindJoint.d.ts.map +1 -1
  456. package/src/shade/renderer/animation/foliage/FoliageWindJoint.js +2 -1
  457. package/src/shade/renderer/animation/foliage/chunk_foliage_wind_step.d.ts.map +1 -1
  458. package/src/shade/renderer/animation/foliage/chunk_foliage_wind_step.js +4 -2
  459. package/src/shade/renderer/buffer/table/GPUTypedTable.js +1 -1
  460. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
  461. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +403 -401
  462. package/src/shade/renderer/camera/OrbitalCameraController.d.ts +1 -62
  463. package/src/shade/renderer/camera/OrbitalCameraController.d.ts.map +1 -1
  464. package/src/shade/renderer/camera/OrbitalCameraController.js +1 -374
  465. package/src/shade/renderer/deferred/indirect/chunk_lpv_2d_cache_sample_irradiance.d.ts.map +1 -1
  466. package/src/shade/renderer/deferred/indirect/chunk_lpv_2d_cache_sample_irradiance.js +3 -0
  467. package/src/shade/renderer/deferred/indirect/chunk_lpv_sample_light.d.ts.map +1 -1
  468. package/src/shade/renderer/deferred/indirect/chunk_lpv_sample_light.js +6 -1
  469. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.js +1 -1
  470. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +2 -2
  471. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts.map +1 -1
  472. package/src/shade/renderer/fluid/GPUFluidCascade.js +6 -3
  473. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +361 -0
  474. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_parent.d.ts.map +1 -1
  475. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_parent.js +335 -241
  476. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_ghosts.d.ts.map +1 -1
  477. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_ghosts.js +108 -101
  478. package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_query_select.d.ts.map +1 -1
  479. package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_query_select.js +69 -67
  480. package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.d.ts +7 -6
  481. package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.d.ts.map +1 -1
  482. package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.js +341 -287
  483. package/src/shade/renderer/fluid/mac/FluidMacPasses.d.ts +6 -4
  484. package/src/shade/renderer/fluid/mac/FluidMacPasses.d.ts.map +1 -1
  485. package/src/shade/renderer/fluid/mac/FluidMacPasses.js +427 -400
  486. package/src/shade/renderer/fluid/mac/chunk_fluid_mac_seams.d.ts +10 -5
  487. package/src/shade/renderer/fluid/mac/chunk_fluid_mac_seams.d.ts.map +1 -1
  488. package/src/shade/renderer/fluid/mac/chunk_fluid_mac_seams.js +532 -190
  489. package/src/shade/renderer/fluid/mac/chunk_v3_mac_lattice_corners.d.ts.map +1 -1
  490. package/src/shade/renderer/fluid/mac/chunk_v3_mac_lattice_corners.js +231 -237
  491. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.d.ts +2 -2
  492. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.d.ts.map +1 -1
  493. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.js +282 -245
  494. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_predict.d.ts +1 -3
  495. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_predict.d.ts.map +1 -1
  496. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_predict.js +139 -109
  497. package/src/shade/renderer/fluid/mac/shader_fluid_mac_curl.d.ts +6 -6
  498. package/src/shade/renderer/fluid/mac/shader_fluid_mac_curl.d.ts.map +1 -1
  499. package/src/shade/renderer/fluid/mac/shader_fluid_mac_curl.js +257 -222
  500. package/src/shade/renderer/fluid/mac/shader_fluid_mac_divergence.d.ts +8 -7
  501. package/src/shade/renderer/fluid/mac/shader_fluid_mac_divergence.d.ts.map +1 -1
  502. package/src/shade/renderer/fluid/mac/shader_fluid_mac_divergence.js +119 -92
  503. package/src/shade/renderer/fluid/mac/shader_fluid_mac_gradient.d.ts +6 -9
  504. package/src/shade/renderer/fluid/mac/shader_fluid_mac_gradient.d.ts.map +1 -1
  505. package/src/shade/renderer/fluid/mac/shader_fluid_mac_gradient.js +229 -181
  506. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +17 -7
  507. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts.map +1 -1
  508. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.js +634 -621
  509. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_residual_restrict.d.ts +8 -6
  510. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_residual_restrict.d.ts.map +1 -1
  511. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_residual_restrict.js +166 -149
  512. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_smooth.d.ts +8 -6
  513. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_smooth.d.ts.map +1 -1
  514. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_smooth.js +219 -183
  515. package/src/shade/renderer/fluid/publish/shader_fluid_wind_publish.d.ts.map +1 -1
  516. package/src/shade/renderer/fluid/publish/shader_fluid_wind_publish.js +142 -122
  517. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +674 -663
  518. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +1 -1
  519. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  520. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +63 -29
  521. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts.map +1 -1
  522. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js +77 -87
  523. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/meshopt_clusterizer.d.ts.map +1 -1
  524. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/meshopt_clusterizer.js +4 -3
  525. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts +11 -11
  526. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts.map +1 -1
  527. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.js +17 -19
  528. package/src/shade/renderer/global_illumination/lpv/graph_bake_irradiance_lookup_2d.js +4 -4
  529. package/src/shade/renderer/global_illumination/probe/octahedral/sample/chunk_lpv_mesh_interpolate_radiance_via_atlas.d.ts.map +1 -1
  530. package/src/shade/renderer/global_illumination/probe/octahedral/sample/chunk_lpv_mesh_interpolate_radiance_via_atlas.js +5 -1
  531. package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
  532. package/src/shade/renderer/loader/gltf/load_gltf.js +11 -35
  533. package/src/shade/renderer/loader/usd/load_usd.d.ts.map +1 -1
  534. package/src/shade/renderer/loader/usd/load_usd.js +6 -19
  535. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
  536. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +199 -193
  537. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts.map +1 -1
  538. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.js +151 -143
  539. package/src/shade/renderer/postprocess/bloom/chunk_karis_blur4x4_sample.d.ts.map +1 -1
  540. package/src/shade/renderer/postprocess/bloom/chunk_karis_blur4x4_sample.js +11 -5
  541. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.d.ts.map +1 -1
  542. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.js +6 -18
  543. package/src/shade/renderer/postprocess/gtao/GTAO.js +1 -1
  544. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
  545. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +3 -5
  546. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +1 -1
  547. package/src/shade/renderer/postprocess/nss/chunk_nss_luma_derivative.d.ts.map +1 -1
  548. package/src/shade/renderer/postprocess/nss/chunk_nss_luma_derivative.js +7 -3
  549. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
  550. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +4 -2
  551. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.d.ts.map +1 -1
  552. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +4 -1
  553. package/src/shade/renderer/postprocess/ssr/downsample_karis.d.ts.map +1 -1
  554. package/src/shade/renderer/postprocess/ssr/downsample_karis.js +6 -3
  555. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts.map +1 -1
  556. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.js +4 -3
  557. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader.d.ts.map +1 -1
  558. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader.js +4 -1
  559. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts.map +1 -1
  560. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.js +4 -2
  561. package/src/shade/renderer/postprocess/taa/chunk_taa_tonemap.d.ts.map +1 -1
  562. package/src/shade/renderer/postprocess/taa/chunk_taa_tonemap.js +35 -39
  563. package/src/shade/renderer/postprocess/taa/chunk_taa_tonemap_inv.d.ts.map +1 -1
  564. package/src/shade/renderer/postprocess/taa/chunk_taa_tonemap_inv.js +20 -17
  565. package/src/shade/renderer/postprocess/taa/shader_taa.d.ts.map +1 -1
  566. package/src/shade/renderer/postprocess/taa/shader_taa.js +6 -3
  567. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  568. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +3 -1
  569. package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.js +1 -1
  570. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.js +2 -2
  571. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +1 -1
  572. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_integrate.d.ts.map +1 -1
  573. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_integrate.js +92 -83
  574. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.js +1 -1
  575. package/src/shade/renderer/shader/ShaderDescriptor.d.ts.map +1 -1
  576. package/src/shade/renderer/shader/ShaderDescriptor.js +12 -4
  577. package/src/shade/renderer/shader/chunk/bvh/chunk_bvh_query_aabb_cooperative.d.ts.map +1 -1
  578. package/src/shade/renderer/shader/chunk/bvh/chunk_bvh_query_aabb_cooperative.js +294 -286
  579. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_interpolate_probe_irradiance8.js +5 -5
  580. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe.d.ts.map +1 -1
  581. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe.js +5 -3
  582. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe_irradiance.d.ts.map +1 -1
  583. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe_irradiance.js +9 -6
  584. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_sample_sh3_at.d.ts.map +1 -1
  585. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_sample_sh3_at.js +33 -38
  586. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  587. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +690 -675
  588. package/src/shade/renderer/shader/type/WebGPUArray.d.ts +3 -0
  589. package/src/shade/renderer/shader/type/WebGPUArray.d.ts.map +1 -1
  590. package/src/shade/renderer/shader/type/WebGPUArray.js +185 -167
  591. package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
  592. package/src/shade/renderer/shader/type/WebGPUStruct.js +461 -455
  593. package/src/shade/renderer/shader/type/serde/read_gpu_typed_buffer.d.ts.map +1 -1
  594. package/src/shade/renderer/shader/type/serde/read_gpu_typed_buffer.js +335 -211
  595. package/src/shade/renderer/shader/type/serde/write_gpu_typed_buffer.d.ts.map +1 -1
  596. package/src/shade/renderer/shader/type/serde/write_gpu_typed_buffer.js +329 -221
  597. package/src/shade/renderer/shader/type/type_has_f16.d.ts +12 -0
  598. package/src/shade/renderer/shader/type/type_has_f16.d.ts.map +1 -0
  599. package/src/shade/renderer/shader/type/type_has_f16.js +66 -0
  600. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
  601. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +16 -2
  602. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.d.ts.map +1 -1
  603. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.js +5 -2
  604. package/src/shade/renderer/water/shader_water_avboit.js +1 -1
  605. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  606. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +260 -23
  607. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts +6 -7
  608. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  609. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +269 -69
  610. package/src/shade/wgsl/validate_wgsl_source.d.ts.map +1 -1
  611. package/src/shade/wgsl/validate_wgsl_source.js +27 -1
  612. package/src/view/controller/controls/ListController.d.ts.map +1 -1
  613. package/src/view/controller/controls/ListController.js +118 -113
  614. package/src/view/controller/controls/Vector1Control.js +97 -97
  615. package/src/view/elements/image/SvgImageView.d.ts +1 -0
  616. package/src/view/elements/image/SvgImageView.d.ts.map +1 -1
  617. package/src/view/elements/image/SvgImageView.js +92 -90
  618. package/src/view/minimap/dom/MinimapTerrainView.d.ts +1 -0
  619. package/src/view/minimap/dom/MinimapTerrainView.d.ts.map +1 -1
  620. package/src/view/minimap/dom/MinimapTerrainView.js +151 -147
  621. package/src/view/tooltip/gml/TooltipParser.js +2 -2
  622. package/src/engine/network/PriorityFetch.d.ts +0 -59
  623. package/src/engine/network/PriorityFetch.d.ts.map +0 -1
  624. package/src/engine/network/PriorityFetch.js +0 -193
  625. package/src/generation/foliage/config/BranchLevelConfig.d.ts.map +0 -1
@@ -1,454 +1,467 @@
1
- import { clamp } from "../../../core/math/clamp.js";
2
- import { lerp } from "../../../core/math/lerp.js";
3
- import { Geometry } from "../../../shade/renderer/geometry/Geometry.js";
4
- import {
5
- geometry_obtain_attribute,
6
- geometry_obtain_index
7
- } from "../../../shade/renderer/geometry/geometry_obtain_attribute.js";
8
- import { StandardAttributes } from "../../../shade/renderer/geometry/StandardAttributes.js";
9
- import { FoliageCardType } from "../config/FoliageCardType.js";
10
- import { TreeLODConfig } from "../config/TreeLODConfig.js";
11
- import { TreeSpeciesConfig } from "../config/TreeSpeciesConfig.js";
12
- import { compute_foliage_variant_atlas_layout } from "../material/make_foliage_material.js";
13
- import { tree_mesh_apply_canopy_ao } from "./tree_mesh_apply_canopy_ao.js";
14
- import { geometry_obtain_joints_attribute } from "./tree_mesh_build_bark.js";
15
-
16
- /**
17
- * Synthesizes the foliage `Geometry` for a given LOD from a `FoliageAttachmentSet`,
18
- * applying golden-ratio low-discrepancy LOD decimation ($h_i < k_{\text{keep}}$),
19
- * optical area-compensation scaling ($s_{\text{comp}} = k_{\text{keep}}^{-\eta}$),
20
- * botanical 3D card geometry (`FoldedVCard`, `CrossCard`, `TriStarCluster`, `RibbedFrond`, `OctagonalCutoutHull`),
21
- * MaxRect-packed ontogenetic variant UV remapping, hemisphere-safe normal puffing,
22
- * volumetric canopy AO, and 4-joint skeletal wind skinning.
23
- *
24
- * Returns `null` if foliage is disabled or `lod_config.foliage_keep_ratio <= 0` or `attachments.count === 0`.
25
- *
26
- * @param {import("../skeleton/TreeSkeleton.js").TreeSkeleton} skeleton
27
- * @param {import("./foliage_collect_attachments.js").FoliageAttachmentSet} attachments
28
- * @param {TreeSpeciesConfig} [config]
29
- * @param {TreeLODConfig} [lod_config]
30
- * @param {Geometry} [out]
31
- * @returns {Geometry|null}
32
- */
33
- export function tree_mesh_build_foliage(
34
- skeleton,
35
- attachments,
36
- config = new TreeSpeciesConfig(),
37
- lod_config = new TreeLODConfig(),
38
- out = new Geometry()
39
- ) {
40
- const species = config && config.isTreeSpeciesConfig ? config : TreeSpeciesConfig.from(config);
41
- const lod_cfg = lod_config && lod_config.isTreeLODConfig ? lod_config : TreeLODConfig.from(lod_config);
42
- const fol = species.foliage;
43
- const keep_ratio = clamp(lod_cfg.foliage_keep_ratio ?? 1.0, 0.0, 1.0);
44
- if (!fol.enabled || !attachments || attachments.count === 0 || keep_ratio <= 0.0) {
45
- return null;
46
- }
47
-
48
- // Filter active attachment indices by golden-ratio rank
49
- const active_indices = [];
50
- for (let i = 0; i < attachments.count; i++) {
51
- if (attachments.ranks[i] < keep_ratio) {
52
- active_indices.push(i);
53
- }
54
- }
55
- if (active_indices.length === 0 && attachments.count > 0) {
56
- active_indices.push(0);
57
- }
58
- const card_count = active_indices.length;
59
-
60
- // Area compensation scale: k_keep^(-eta)
61
- const eta = clamp(lod_cfg.foliage_area_compensation_exponent ?? 0.42, 0.0, 0.6);
62
- const area_comp = Math.pow(Math.max(0.08, keep_ratio), -eta);
63
-
64
- // Determine effective card type for this LOD
65
- let card_type = fol.card_type;
66
- if (lod_cfg.simplify_cards) {
67
- card_type = FoliageCardType.FoldedVCard;
68
- }
69
-
70
- const frond_segs = Math.max(2, fol.frond_segments | 0);
71
- let verts_per_card = 6;
72
- let tris_per_card = 4;
73
-
74
- switch (card_type) {
75
- case FoliageCardType.CrossCard:
76
- verts_per_card = 8;
77
- tris_per_card = 4;
78
- break;
79
- case FoliageCardType.TriStarCluster:
80
- verts_per_card = 12;
81
- tris_per_card = 6;
82
- break;
83
- case FoliageCardType.RibbedFrond:
84
- verts_per_card = 3 * (frond_segs + 1);
85
- tris_per_card = 4 * frond_segs;
86
- break;
87
- case FoliageCardType.OctagonalCutoutHull:
88
- verts_per_card = 7;
89
- tris_per_card = 6;
90
- break;
91
- case FoliageCardType.FoldedVCard:
92
- default:
93
- verts_per_card = 6;
94
- tris_per_card = 4;
95
- break;
96
- }
97
-
98
- const total_vertices = card_count * verts_per_card;
99
- const total_indices = card_count * tris_per_card * 3;
100
-
101
- const positions = geometry_obtain_attribute(out, StandardAttributes.Position, 3, total_vertices);
102
- const normals = geometry_obtain_attribute(out, StandardAttributes.Normal, 3, total_vertices);
103
- const uv0 = geometry_obtain_attribute(out, StandardAttributes.TextureCoordinates0, 2, total_vertices);
104
- const uv1 = geometry_obtain_attribute(out, StandardAttributes.TextureCoordinates1, 2, total_vertices);
105
- const colors = geometry_obtain_attribute(out, StandardAttributes.Color, 3, total_vertices);
106
- const joints = geometry_obtain_joints_attribute(out, total_vertices);
107
- const weights = geometry_obtain_attribute(out, StandardAttributes.SkinningWeights, 4, total_vertices);
108
- const indices = geometry_obtain_index(out, total_indices);
109
-
110
- const fol_mat = species.foliage_material;
111
- const layout = compute_foliage_variant_atlas_layout(
112
- fol_mat?.resolution ?? 128,
113
- fol_mat?.variant_count ?? 4
114
- );
115
- const uv_rects = layout.uv_rects;
116
- const variant_count = layout.variant_count;
117
-
118
- const base_w = Math.max(0.05, fol.card_width) * area_comp;
119
- const base_l = Math.max(0.05, fol.card_length) * area_comp;
120
- const fold_angle = clamp(fol.fold_angle, 0.0, 0.65);
121
- const sin_fold = Math.sin(fold_angle);
122
- const cos_fold = Math.cos(fold_angle);
123
- const arch_h = fol.arch_height;
124
- const droop = fol.camber_droop;
125
- const color_var_amp = clamp(fol.color_variation ?? 0.08, 0.0, 0.35);
126
- const anchor_u = clamp(fol_mat?.stem_anchor_u ?? 0.5, 0.1, 0.9);
127
- const anchor_v = clamp(fol_mat?.stem_anchor_v ?? 0.0, 0.0, 0.25);
128
- const juv_shift = clamp(fol_mat?.juvenile_color_shift ?? 0.38, 0.0, 1.0);
129
- const senescence = clamp(Math.max(fol_mat?.senescence ?? 0.0, fol_mat?.autumn_tint ?? 0.0), 0.0, 1.0);
130
- const antho_pot = clamp(fol_mat?.anthocyanin_potential ?? 0.35, 0.0, 1.0);
131
-
132
- let v_cursor = 0;
133
- let i_cursor = 0;
134
- let rect_u0 = 0.0;
135
- let rect_v0 = 0.0;
136
- let rect_u1 = 1.0;
137
- let rect_v1 = 1.0;
138
-
139
- /**
140
- * Helper to write a single foliage vertex in card-local coordinates `(x_w, y_u, z_f)`
141
- * and local normal `(nx_w, ny_u, nz_f)`, remapping local `[0, 1]^2` card UVs into the
142
- * card's assigned MaxRect-packed atlas tile `[rect_u0, rect_v0, rect_u1, rect_v1]`.
143
- */
144
- const write_card_vertex = (
145
- px0, py0, pz0,
146
- wx, wy, wz,
147
- ux, uy, uz,
148
- fx, fy, fz,
149
- x_w, y_u, z_f,
150
- nx_w, ny_u, nz_f,
151
- u_tex, v_tex,
152
- flutter_w, phase_w,
153
- r_col, g_col, b_col,
154
- j0, j1, j2, j3,
155
- w0, w1, w2, w3
156
- ) => {
157
- const v3 = v_cursor * 3;
158
- const v2 = v_cursor * 2;
159
- const v4 = v_cursor * 4;
160
-
161
- positions[v3] = px0 + wx * x_w + ux * y_u + fx * z_f;
162
- positions[v3 + 1] = py0 + wy * x_w + uy * y_u + fy * z_f;
163
- positions[v3 + 2] = pz0 + wz * x_w + uz * y_u + fz * z_f;
164
-
165
- let gnx = wx * nx_w + ux * ny_u + fx * nz_f;
166
- let gny = wy * nx_w + uy * ny_u + fy * nz_f;
167
- let gnz = wz * nx_w + uz * ny_u + fz * nz_f;
168
- const nl = Math.sqrt(gnx * gnx + gny * gny + gnz * gnz);
169
- if (nl > 1e-8) {
170
- gnx /= nl;
171
- gny /= nl;
172
- gnz /= nl;
173
- }
174
- normals[v3] = gnx;
175
- normals[v3 + 1] = gny;
176
- normals[v3 + 2] = gnz;
177
-
178
- uv0[v2] = lerp(rect_u0, rect_u1, u_tex);
179
- uv0[v2 + 1] = lerp(rect_v0, rect_v1, v_tex);
180
-
181
- uv1[v2] = clamp(flutter_w, 0.0, 1.0);
182
- uv1[v2 + 1] = clamp(phase_w, 0.0, 1.0);
183
-
184
- colors[v3] = r_col;
185
- colors[v3 + 1] = g_col;
186
- colors[v3 + 2] = b_col;
187
-
188
- joints[v4] = j0;
189
- joints[v4 + 1] = j1;
190
- joints[v4 + 2] = j2;
191
- joints[v4 + 3] = j3;
192
-
193
- weights[v4] = w0;
194
- weights[v4 + 1] = w1;
195
- weights[v4 + 2] = w2;
196
- weights[v4 + 3] = w3;
197
-
198
- return v_cursor++;
199
- };
200
-
201
- const has_j4 = Boolean(attachments.joints4 && attachments.weights4 && attachments.joints4.length >= attachments.count * 4);
202
- const has_variants = Boolean(attachments.variant_indices && attachments.variant_indices.length >= attachments.count);
203
- const has_maturities = Boolean(attachments.maturities && attachments.maturities.length >= attachments.count);
204
- const has_sun = Boolean(attachments.sun_exposure && attachments.sun_exposure.length >= attachments.count);
205
-
206
- for (let c = 0; c < card_count; c++) {
207
- const att_idx = active_indices[c];
208
- const a3 = att_idx * 3;
209
- const a2 = att_idx * 2;
210
- const a4 = att_idx * 4;
211
-
212
- const px0 = attachments.positions[a3];
213
- const py0 = attachments.positions[a3 + 1];
214
- const pz0 = attachments.positions[a3 + 2];
215
-
216
- const fx = attachments.forward[a3];
217
- const fy = attachments.forward[a3 + 1];
218
- const fz = attachments.forward[a3 + 2];
219
-
220
- const ux = attachments.up[a3];
221
- const uy = attachments.up[a3 + 1];
222
- const uz = attachments.up[a3 + 2];
223
-
224
- const wx = attachments.right[a3];
225
- const wy = attachments.right[a3 + 1];
226
- const wz = attachments.right[a3 + 2];
227
-
228
- const maturity = has_maturities ? attachments.maturities[att_idx] : 0.5;
229
- const sun_exp = has_sun ? attachments.sun_exposure[att_idx] : 0.65;
230
- const v_idx = has_variants
231
- ? clamp(attachments.variant_indices[att_idx], 0, variant_count - 1)
232
- : clamp(Math.floor(maturity * variant_count), 0, variant_count - 1);
233
- const r4 = v_idx * 4;
234
- rect_u0 = uv_rects[r4];
235
- rect_v0 = uv_rects[r4 + 1];
236
- rect_u1 = uv_rects[r4 + 2];
237
- rect_v1 = uv_rects[r4 + 3];
238
-
239
- const scale = attachments.scales[att_idx];
240
- const card_w = base_w * scale;
241
- const card_l = base_l * scale;
242
- const half_w = card_w * 0.5;
243
- const left_w = -card_w * anchor_u;
244
- const right_w = card_w * (1.0 - anchor_u);
245
-
246
- const phase = attachments.phases[att_idx];
247
- const b_sway = attachments.branch_sway[att_idx];
248
- const c_jit = attachments.color_jitter[att_idx] * color_var_amp;
249
-
250
- // Continuous ontogenetic & crown-shell photoprotective modulation in vertex colors
251
- const juv_lift = (0.5 - maturity) * juv_shift * 0.14;
252
- let r_col = clamp(1.0 + c_jit * 1.1 + juv_lift * 0.9, 0.68, 1.0);
253
- let g_col = clamp(1.0 + c_jit * 0.8 + juv_lift * 1.1, 0.68, 1.0);
254
- let b_col = clamp(1.0 - c_jit * 0.6 - juv_lift * 0.5, 0.68, 1.0);
255
-
256
- if (senescence > 1e-4) {
257
- // Sun-exposed outer crown shell expresses stronger anthocyanin/carotenoid warmth than shaded interior
258
- const shade_cool = (1.0 - sun_exp) * senescence * 0.22;
259
- const sun_antho = sun_exp * senescence * antho_pot * 0.25;
260
- r_col = clamp(r_col * (1.0 - shade_cool * 0.45 + sun_antho * 0.18), 0.65, 1.0);
261
- g_col = clamp(g_col * (1.0 + shade_cool * 0.15 - sun_antho * 0.35), 0.55, 1.0);
262
- b_col = clamp(b_col * (1.0 - sun_antho * 0.30), 0.55, 1.0);
263
- }
264
-
265
- const j0 = has_j4 ? attachments.joints4[a4] : attachments.joints[a2];
266
- const j1 = has_j4 ? attachments.joints4[a4 + 1] : attachments.joints[a2 + 1];
267
- const j2 = has_j4 ? attachments.joints4[a4 + 2] : 0;
268
- const j3 = has_j4 ? attachments.joints4[a4 + 3] : 0;
269
- const w0 = has_j4 ? attachments.weights4[a4] : attachments.weights[a2];
270
- const w1 = has_j4 ? attachments.weights4[a4 + 1] : attachments.weights[a2 + 1];
271
- const w2 = has_j4 ? attachments.weights4[a4 + 2] : 0.0;
272
- const w3 = has_j4 ? attachments.weights4[a4 + 3] : 0.0;
273
-
274
- const base_v = v_cursor;
275
-
276
- if (card_type === FoliageCardType.CrossCard) {
277
- // Plane 0: horizontal (W, F) with normal +U (since W x F = +U)
278
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, left_w, 0, 0, 0, 1, 0, 0.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
279
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, right_w, 0, 0, 0, 1, 0, 1.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
280
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, left_w, 0, card_l, 0, 1, 0, 0.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
281
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, right_w, 0, card_l, 0, 1, 0, 1.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
282
-
283
- indices[i_cursor++] = base_v + 0;
284
- indices[i_cursor++] = base_v + 1;
285
- indices[i_cursor++] = base_v + 3;
286
- indices[i_cursor++] = base_v + 0;
287
- indices[i_cursor++] = base_v + 3;
288
- indices[i_cursor++] = base_v + 2;
289
-
290
- // Plane 1: vertical (-U to +U, F) with normal +W (since (-U) x F = +W)
291
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0, -left_w, 0, 1, 0, 0, 0.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
292
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0, -right_w, 0, 1, 0, 0, 1.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
293
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0, -left_w, card_l, 1, 0, 0, 0.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
294
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0, -right_w, card_l, 1, 0, 0, 1.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
295
-
296
- indices[i_cursor++] = base_v + 4;
297
- indices[i_cursor++] = base_v + 5;
298
- indices[i_cursor++] = base_v + 7;
299
- indices[i_cursor++] = base_v + 4;
300
- indices[i_cursor++] = base_v + 7;
301
- indices[i_cursor++] = base_v + 6;
302
- } else if (card_type === FoliageCardType.TriStarCluster) {
303
- for (let p = 0; p < 3; p++) {
304
- const ang = (p * Math.PI) / 3.0;
305
- const ca = Math.cos(ang);
306
- const sa = Math.sin(ang);
307
- const p_left_x = ca * left_w;
308
- const p_left_y = sa * left_w;
309
- const p_right_x = ca * right_w;
310
- const p_right_y = sa * right_w;
311
- const pn_x = -sa;
312
- const pn_y = ca;
313
- const p_base = base_v + p * 4;
314
-
315
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, p_left_x, p_left_y, 0, pn_x, pn_y, 0, 0.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
316
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, p_right_x, p_right_y, 0, pn_x, pn_y, 0, 1.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
317
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, p_left_x, p_left_y, card_l, pn_x, pn_y, 0, 0.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
318
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, p_right_x, p_right_y, card_l, pn_x, pn_y, 0, 1.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
319
-
320
- indices[i_cursor++] = p_base + 0;
321
- indices[i_cursor++] = p_base + 1;
322
- indices[i_cursor++] = p_base + 3;
323
- indices[i_cursor++] = p_base + 0;
324
- indices[i_cursor++] = p_base + 3;
325
- indices[i_cursor++] = p_base + 2;
326
- }
327
- } else if (card_type === FoliageCardType.RibbedFrond) {
328
- const lift_wing = half_w * sin_fold;
329
- const span_w = half_w * cos_fold;
330
-
331
- for (let s = 0; s <= frond_segs; s++) {
332
- const t = s / frond_segs;
333
- const v_tex = anchor_v + (1.0 - anchor_v) * t;
334
- const z_s = t * card_l;
335
- const y_spine = card_l * (arch_h * 4.0 * t * (1.0 - t) - droop * t * t);
336
- const env_w = Math.sin(Math.PI * (0.12 + 0.82 * t));
337
- const w_s = span_w * env_w;
338
- const lift_s = lift_wing * env_w;
339
- const flutter = clamp(b_sway * 0.3 + 0.7 * t, 0.0, 1.0);
340
-
341
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, -w_s, y_spine + lift_s, z_s, -sin_fold * 0.35, cos_fold, 0, 0.0, v_tex, flutter, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
342
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0.0, y_spine, z_s, 0.0, 1.0, 0, anchor_u, v_tex, flutter * 0.75, phase, r_col * 0.94, g_col * 0.94, b_col * 0.94, j0, j1, j2, j3, w0, w1, w2, w3);
343
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, w_s, y_spine + lift_s, z_s, sin_fold * 0.35, cos_fold, 0, 1.0, v_tex, flutter, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
344
-
345
- if (s > 0) {
346
- const r0 = base_v + (s - 1) * 3;
347
- const r1 = base_v + s * 3;
348
- indices[i_cursor++] = r0 + 0;
349
- indices[i_cursor++] = r0 + 1;
350
- indices[i_cursor++] = r1 + 1;
351
- indices[i_cursor++] = r0 + 0;
352
- indices[i_cursor++] = r1 + 1;
353
- indices[i_cursor++] = r1 + 0;
354
-
355
- indices[i_cursor++] = r0 + 1;
356
- indices[i_cursor++] = r0 + 2;
357
- indices[i_cursor++] = r1 + 2;
358
- indices[i_cursor++] = r0 + 1;
359
- indices[i_cursor++] = r1 + 2;
360
- indices[i_cursor++] = r1 + 1;
361
- }
362
- }
363
- } else if (card_type === FoliageCardType.OctagonalCutoutHull) {
364
- const lift = half_w * sin_fold * 0.7;
365
- const span = half_w * cos_fold;
366
- const camber = card_l * arch_h;
367
-
368
- // v0: base petiole (0, 0, 0) at exact UV (anchor_u, anchor_v)
369
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0.0, 0.0, 0.0, 0, 1, 0, anchor_u, anchor_v, b_sway * 0.25, phase, r_col * 0.9, g_col * 0.9, b_col * 0.9, j0, j1, j2, j3, w0, w1, w2, w3);
370
- // v1: lower-left
371
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, -span * 0.85, lift * 0.7, card_l * 0.28, -sin_fold * 0.3, cos_fold, 0, 0.08, 0.28, b_sway * 0.55, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
372
- // v2: lower-right
373
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, span * 0.85, lift * 0.7, card_l * 0.28, sin_fold * 0.3, cos_fold, 0, 0.92, 0.28, b_sway * 0.55, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
374
- // v3: center midrib
375
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0.0, camber, card_l * 0.52, 0, 1, 0, anchor_u, 0.52, b_sway * 0.65, phase, r_col * 0.96, g_col * 0.96, b_col * 0.96, j0, j1, j2, j3, w0, w1, w2, w3);
376
- // v4: upper-left
377
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, -span, lift, card_l * 0.74, -sin_fold * 0.3, cos_fold, 0, 0.04, 0.74, 0.9, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
378
- // v5: upper-right
379
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, span, lift, card_l * 0.74, sin_fold * 0.3, cos_fold, 0, 0.96, 0.74, 0.9, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
380
- // v6: apex tip
381
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0.0, -card_l * droop * 0.5, card_l, 0, 1, 0, anchor_u, 0.98, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
382
-
383
- // 6 outward (+U) triangles fanning around center midrib v3
384
- indices[i_cursor++] = base_v + 0;
385
- indices[i_cursor++] = base_v + 2;
386
- indices[i_cursor++] = base_v + 3;
387
-
388
- indices[i_cursor++] = base_v + 0;
389
- indices[i_cursor++] = base_v + 3;
390
- indices[i_cursor++] = base_v + 1;
391
-
392
- indices[i_cursor++] = base_v + 3;
393
- indices[i_cursor++] = base_v + 2;
394
- indices[i_cursor++] = base_v + 5;
395
-
396
- indices[i_cursor++] = base_v + 3;
397
- indices[i_cursor++] = base_v + 5;
398
- indices[i_cursor++] = base_v + 6;
399
-
400
- indices[i_cursor++] = base_v + 3;
401
- indices[i_cursor++] = base_v + 6;
402
- indices[i_cursor++] = base_v + 4;
403
-
404
- indices[i_cursor++] = base_v + 3;
405
- indices[i_cursor++] = base_v + 4;
406
- indices[i_cursor++] = base_v + 1;
407
- } else {
408
- // Default: FoldedVCard (6 vertices, 4 triangles)
409
- const lift = half_w * sin_fold;
410
- const span = half_w * cos_fold;
411
- const tip_droop = -card_l * droop * 0.5;
412
- const mid_arch = card_l * arch_h * 0.5;
413
-
414
- // Row 0 (base, z = 0)
415
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, -span, lift, 0.0, -sin_fold * 0.35, cos_fold, 0, 0.0, anchor_v, b_sway * 0.45, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
416
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0.0, 0.0, 0.0, 0.0, 1.0, 0, anchor_u, anchor_v, b_sway * 0.25, phase, r_col * 0.92, g_col * 0.92, b_col * 0.92, j0, j1, j2, j3, w0, w1, w2, w3);
417
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, span, lift, 0.0, sin_fold * 0.35, cos_fold, 0, 1.0, anchor_v, b_sway * 0.45, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
418
-
419
- // Row 1 (tip, z = card_l)
420
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, -span, lift + tip_droop, card_l, -sin_fold * 0.35, cos_fold, 0, 0.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
421
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0.0, mid_arch + tip_droop, card_l, 0.0, 1.0, 0, anchor_u, 1.0, 0.85, phase, r_col * 0.96, g_col * 0.96, b_col * 0.96, j0, j1, j2, j3, w0, w1, w2, w3);
422
- write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, span, lift + tip_droop, card_l, sin_fold * 0.35, cos_fold, 0, 1.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
423
-
424
- // Left wing quad
425
- indices[i_cursor++] = base_v + 0;
426
- indices[i_cursor++] = base_v + 1;
427
- indices[i_cursor++] = base_v + 4;
428
- indices[i_cursor++] = base_v + 0;
429
- indices[i_cursor++] = base_v + 4;
430
- indices[i_cursor++] = base_v + 3;
431
-
432
- // Right wing quad
433
- indices[i_cursor++] = base_v + 1;
434
- indices[i_cursor++] = base_v + 2;
435
- indices[i_cursor++] = base_v + 5;
436
- indices[i_cursor++] = base_v + 1;
437
- indices[i_cursor++] = base_v + 5;
438
- indices[i_cursor++] = base_v + 4;
439
- }
440
- }
441
-
442
- // Apply volumetric canopy AO and hemisphere-safe normal puffing BEFORE ensureTangents()
443
- tree_mesh_apply_canopy_ao(out, skeleton.crown_center, skeleton.crown_radii, {
444
- ao_core_darkening: fol.ao_core_darkening ?? 0.35,
445
- normal_puffiness: fol.normal_puffiness ?? 0.35,
446
- preserve_existing_color: true
447
- });
448
-
449
- out.name = `${species.name || "tree"}_foliage_lod${lod_cfg.level}`;
450
- out.ensureBounds();
451
- out.ensureTangents();
452
-
453
- return out;
454
- }
1
+ import { clamp } from "../../../core/math/clamp.js";
2
+ import { lerp } from "../../../core/math/lerp.js";
3
+ import { Geometry } from "../../../shade/renderer/geometry/Geometry.js";
4
+ import {
5
+ geometry_obtain_attribute,
6
+ geometry_obtain_index
7
+ } from "../../../shade/renderer/geometry/geometry_obtain_attribute.js";
8
+ import { StandardAttributes } from "../../../shade/renderer/geometry/StandardAttributes.js";
9
+ import { FoliageCardType } from "../config/FoliageCardType.js";
10
+ import { TreeLODConfig } from "../config/TreeLODConfig.js";
11
+ import { TreeSpeciesConfig } from "../config/TreeSpeciesConfig.js";
12
+ import { compute_foliage_variant_atlas_layout } from "../material/compute_foliage_variant_atlas_layout.js";
13
+ import { foliage_tile_layout } from "../material/foliage_tile.js";
14
+ import { CUTOUT_HULL_VERTEX_COUNT } from "../material/FoliageAtlasCutout.js";
15
+ import { tree_mesh_apply_canopy_ao } from "./tree_mesh_apply_canopy_ao.js";
16
+ import { geometry_obtain_joints_attribute } from "./tree_mesh_build_bark.js";
17
+
18
+ /**
19
+ * `CutoutHull` outline when there is no fitted atlas: the whole tile, counter-clockwise.
20
+ * @type {Float32Array}
21
+ */
22
+ const WHOLE_TILE_HULL = new Float32Array([0, 0, 1, 0, 1, 0.5, 1, 1, 0, 1, 0, 0.5]);
23
+
24
+ /**
25
+ * Synthesizes the foliage `Geometry` for a given LOD from a `FoliageAttachmentSet`,
26
+ * applying golden-ratio low-discrepancy LOD decimation ($h_i < k_{\text{keep}}$),
27
+ * optical area-compensation scaling ($s_{\text{comp}} = k_{\text{keep}}^{-\eta}$),
28
+ * botanical 3D card geometry (`FoldedVCard`, `CrossCard`, `TriStarCluster`, `RibbedFrond`, `CutoutHull`),
29
+ * MaxRect-packed ontogenetic variant UV remapping, hemisphere-safe normal puffing,
30
+ * volumetric canopy AO, and 4-joint skeletal wind skinning.
31
+ *
32
+ * Returns `null` if foliage is disabled or `lod_config.foliage_keep_ratio <= 0` or `attachments.count === 0`.
33
+ *
34
+ * Vertex colours carry each card's tint divided by `attachments.tint_scale`, so a tint brighter than 1 survives the
35
+ * `unorm8` encoding: the material drawing these cards sets its `diffuse_color` to `tint_scale` (as `generate_tree`
36
+ * does for the synthesized material). Each card's `attachments.dryness` rolls it shut, flattens its arch and lets
37
+ * its tip hang, towards a leaf with no turgor.
38
+ *
39
+ * @param {import("../skeleton/TreeSkeleton.js").TreeSkeleton} skeleton
40
+ * @param {import("./foliage_collect_attachments.js").FoliageAttachmentSet} attachments
41
+ * @param {TreeSpeciesConfig} [config]
42
+ * @param {TreeLODConfig} [lod_config]
43
+ * @param {import("../material/FoliageAtlasCutout.js").FoliageAtlasCutout|null} [cutout] shape of the atlas the
44
+ * cards show, from `make_foliage_material`; `CutoutHull` cards cover whole tiles without it
45
+ * @param {Geometry} [out]
46
+ * @returns {Geometry|null}
47
+ */
48
+ export function tree_mesh_build_foliage(
49
+ skeleton,
50
+ attachments,
51
+ config = new TreeSpeciesConfig(),
52
+ lod_config = new TreeLODConfig(),
53
+ cutout = null,
54
+ out = new Geometry()
55
+ ) {
56
+ const species = config && config.isTreeSpeciesConfig ? config : TreeSpeciesConfig.from(config);
57
+ const lod_cfg = lod_config && lod_config.isTreeLODConfig ? lod_config : TreeLODConfig.from(lod_config);
58
+ const fol = species.foliage;
59
+ const keep_ratio = clamp(lod_cfg.foliage_keep_ratio ?? 1.0, 0.0, 1.0);
60
+ if (!attachments || attachments.count === 0 || keep_ratio <= 0.0) {
61
+ return null;
62
+ }
63
+
64
+ // Filter active attachment indices by golden-ratio rank
65
+ const active_indices = [];
66
+ for (let i = 0; i < attachments.count; i++) {
67
+ if (attachments.ranks[i] < keep_ratio) {
68
+ active_indices.push(i);
69
+ }
70
+ }
71
+ if (active_indices.length === 0 && attachments.count > 0) {
72
+ active_indices.push(0);
73
+ }
74
+ const card_count = active_indices.length;
75
+
76
+ // Area compensation scale: k_keep^(-eta)
77
+ const eta = clamp(lod_cfg.foliage_area_compensation_exponent ?? 0.42, 0.0, 0.6);
78
+ const area_comp = Math.pow(Math.max(0.08, keep_ratio), -eta);
79
+
80
+ // Determine effective card type for this LOD
81
+ let card_type = fol.card_type;
82
+ if (lod_cfg.simplify_cards) {
83
+ card_type = FoliageCardType.FoldedVCard;
84
+ }
85
+
86
+ const frond_segs = Math.max(2, fol.frond_segments | 0);
87
+ let verts_per_card = 6;
88
+ let tris_per_card = 4;
89
+
90
+ switch (card_type) {
91
+ case FoliageCardType.CrossCard:
92
+ verts_per_card = 8;
93
+ tris_per_card = 4;
94
+ break;
95
+ case FoliageCardType.TriStarCluster:
96
+ verts_per_card = 12;
97
+ tris_per_card = 6;
98
+ break;
99
+ case FoliageCardType.RibbedFrond:
100
+ verts_per_card = 3 * (frond_segs + 1);
101
+ tris_per_card = 4 * frond_segs;
102
+ break;
103
+ case FoliageCardType.CutoutHull:
104
+ verts_per_card = CUTOUT_HULL_VERTEX_COUNT + 1;
105
+ tris_per_card = CUTOUT_HULL_VERTEX_COUNT;
106
+ break;
107
+ case FoliageCardType.FoldedVCard:
108
+ default:
109
+ verts_per_card = 6;
110
+ tris_per_card = 4;
111
+ break;
112
+ }
113
+
114
+ const total_vertices = card_count * verts_per_card;
115
+ const total_indices = card_count * tris_per_card * 3;
116
+
117
+ const positions = geometry_obtain_attribute(out, StandardAttributes.Position, 3, total_vertices);
118
+ const normals = geometry_obtain_attribute(out, StandardAttributes.Normal, 3, total_vertices);
119
+ const uv0 = geometry_obtain_attribute(out, StandardAttributes.TextureCoordinates0, 2, total_vertices);
120
+ const uv1 = geometry_obtain_attribute(out, StandardAttributes.TextureCoordinates1, 2, total_vertices);
121
+ const colors = geometry_obtain_attribute(out, StandardAttributes.Color, 3, total_vertices);
122
+ const joints = geometry_obtain_joints_attribute(out, total_vertices);
123
+ const weights = geometry_obtain_attribute(out, StandardAttributes.SkinningWeights, 4, total_vertices);
124
+ const indices = geometry_obtain_index(out, total_indices);
125
+
126
+ // every card is the species' tile at its true size, attached where the tile's organ attaches
127
+ const tiles = foliage_tile_layout(species);
128
+ const layout = compute_foliage_variant_atlas_layout(tiles.resolution, tiles.variant_count);
129
+ const uv_rects = layout.uv_rects;
130
+ const variant_count = layout.variant_count;
131
+
132
+ // Hull cards trace the atlas the cards show; without one (a caller's own material) they cover whole tiles
133
+ const cutout_hulls = cutout !== null && cutout.variant_count === variant_count ? cutout.hulls : null;
134
+ const hull_source = cutout_hulls ?? WHOLE_TILE_HULL;
135
+ const hull_mid_v = new Float32Array(cutout_hulls !== null ? variant_count : 1);
136
+ for (let v = 0; v < hull_mid_v.length; v++) {
137
+ // the fan's centre on the midrib at the hull's mean height, where the stem keeps it inside the hull
138
+ let sum_v = 0.0;
139
+ for (let h = 0; h < CUTOUT_HULL_VERTEX_COUNT; h++) {
140
+ sum_v += hull_source[(v * CUTOUT_HULL_VERTEX_COUNT + h) * 2 + 1];
141
+ }
142
+ hull_mid_v[v] = clamp(sum_v / CUTOUT_HULL_VERTEX_COUNT, tiles.anchor_v + 0.02, tiles.anchor_v + 0.70);
143
+ }
144
+
145
+ // the card's width is the tile's; its length runs from the anchor to the tile's far edge
146
+ const base_w = tiles.card_size * area_comp;
147
+ const base_l = tiles.card_size * (1.0 - tiles.anchor_v) * area_comp;
148
+ const turgid_fold = clamp(fol.fold_angle, 0.0, 0.65);
149
+ const turgid_arch = fol.arch_height;
150
+ const turgid_droop = fol.camber_droop;
151
+ const anchor_u = tiles.anchor_u;
152
+ const anchor_v = tiles.anchor_v;
153
+
154
+ let v_cursor = 0;
155
+ let i_cursor = 0;
156
+ let rect_u0 = 0.0;
157
+ let rect_v0 = 0.0;
158
+ let rect_u1 = 1.0;
159
+ let rect_v1 = 1.0;
160
+
161
+ /**
162
+ * Helper to write a single foliage vertex in card-local coordinates `(x_w, y_u, z_f)`
163
+ * and local normal `(nx_w, ny_u, nz_f)`, remapping local `[0, 1]^2` card UVs into the
164
+ * card's assigned MaxRect-packed atlas tile `[rect_u0, rect_v0, rect_u1, rect_v1]`.
165
+ */
166
+ const write_card_vertex = (
167
+ px0, py0, pz0,
168
+ wx, wy, wz,
169
+ ux, uy, uz,
170
+ fx, fy, fz,
171
+ x_w, y_u, z_f,
172
+ nx_w, ny_u, nz_f,
173
+ u_tex, v_tex,
174
+ flutter_w, phase_w,
175
+ r_col, g_col, b_col,
176
+ j0, j1, j2, j3,
177
+ w0, w1, w2, w3
178
+ ) => {
179
+ const v3 = v_cursor * 3;
180
+ const v2 = v_cursor * 2;
181
+ const v4 = v_cursor * 4;
182
+
183
+ positions[v3] = px0 + wx * x_w + ux * y_u + fx * z_f;
184
+ positions[v3 + 1] = py0 + wy * x_w + uy * y_u + fy * z_f;
185
+ positions[v3 + 2] = pz0 + wz * x_w + uz * y_u + fz * z_f;
186
+
187
+ let gnx = wx * nx_w + ux * ny_u + fx * nz_f;
188
+ let gny = wy * nx_w + uy * ny_u + fy * nz_f;
189
+ let gnz = wz * nx_w + uz * ny_u + fz * nz_f;
190
+ const nl = Math.sqrt(gnx * gnx + gny * gny + gnz * gnz);
191
+ if (nl > 1e-8) {
192
+ gnx /= nl;
193
+ gny /= nl;
194
+ gnz /= nl;
195
+ }
196
+ normals[v3] = gnx;
197
+ normals[v3 + 1] = gny;
198
+ normals[v3 + 2] = gnz;
199
+
200
+ uv0[v2] = lerp(rect_u0, rect_u1, u_tex);
201
+ uv0[v2 + 1] = lerp(rect_v0, rect_v1, v_tex);
202
+
203
+ uv1[v2] = clamp(flutter_w, 0.0, 1.0);
204
+ uv1[v2 + 1] = clamp(phase_w, 0.0, 1.0);
205
+
206
+ colors[v3] = r_col;
207
+ colors[v3 + 1] = g_col;
208
+ colors[v3 + 2] = b_col;
209
+
210
+ joints[v4] = j0;
211
+ joints[v4 + 1] = j1;
212
+ joints[v4 + 2] = j2;
213
+ joints[v4 + 3] = j3;
214
+
215
+ weights[v4] = w0;
216
+ weights[v4 + 1] = w1;
217
+ weights[v4 + 2] = w2;
218
+ weights[v4 + 3] = w3;
219
+
220
+ return v_cursor++;
221
+ };
222
+
223
+ const has_j4 = Boolean(attachments.joints4 && attachments.weights4 && attachments.joints4.length >= attachments.count * 4);
224
+ const has_variants = Boolean(attachments.variant_indices && attachments.variant_indices.length >= attachments.count);
225
+ const has_maturities = Boolean(attachments.maturities && attachments.maturities.length >= attachments.count);
226
+ const has_tints = Boolean(attachments.tints && attachments.tints.length >= attachments.count * 3);
227
+ const has_dryness = Boolean(attachments.dryness && attachments.dryness.length >= attachments.count);
228
+ // vertex colours are unorm8: tints over 1 are carried as tint / tint_scale, the material's diffuse_color holding tint_scale
229
+ const inv_tint_scale = has_tints ? 1.0 / Math.max(1.0, attachments.tint_scale ?? 1.0) : 1.0;
230
+
231
+ for (let c = 0; c < card_count; c++) {
232
+ const att_idx = active_indices[c];
233
+ const a3 = att_idx * 3;
234
+ const a2 = att_idx * 2;
235
+ const a4 = att_idx * 4;
236
+
237
+ const px0 = attachments.positions[a3];
238
+ const py0 = attachments.positions[a3 + 1];
239
+ const pz0 = attachments.positions[a3 + 2];
240
+
241
+ const fx = attachments.forward[a3];
242
+ const fy = attachments.forward[a3 + 1];
243
+ const fz = attachments.forward[a3 + 2];
244
+
245
+ const ux = attachments.up[a3];
246
+ const uy = attachments.up[a3 + 1];
247
+ const uz = attachments.up[a3 + 2];
248
+
249
+ const wx = attachments.right[a3];
250
+ const wy = attachments.right[a3 + 1];
251
+ const wz = attachments.right[a3 + 2];
252
+
253
+ const maturity = has_maturities ? attachments.maturities[att_idx] : 0.5;
254
+ const v_idx = has_variants
255
+ ? clamp(attachments.variant_indices[att_idx], 0, variant_count - 1)
256
+ : clamp(Math.floor(maturity * variant_count), 0, variant_count - 1);
257
+ const r4 = v_idx * 4;
258
+ rect_u0 = uv_rects[r4];
259
+ rect_v0 = uv_rects[r4 + 1];
260
+ rect_u1 = uv_rects[r4 + 2];
261
+ rect_v1 = uv_rects[r4 + 3];
262
+
263
+ const scale = attachments.scales[att_idx];
264
+ const card_w = base_w * scale;
265
+ const card_l = base_l * scale;
266
+ const half_w = card_w * 0.5;
267
+ const left_w = -card_w * anchor_u;
268
+ const right_w = card_w * (1.0 - anchor_u);
269
+
270
+ const phase = attachments.phases[att_idx];
271
+ const b_sway = attachments.branch_sway[att_idx];
272
+
273
+ const r_col = has_tints ? attachments.tints[a3] * inv_tint_scale : 1.0;
274
+ const g_col = has_tints ? attachments.tints[a3 + 1] * inv_tint_scale : 1.0;
275
+ const b_col = has_tints ? attachments.tints[a3 + 2] * inv_tint_scale : 1.0;
276
+
277
+ // A drying leaf loses the turgor that holds it open and up: it rolls shut, its arch sags and its tip hangs.
278
+ // These are the limits a leaf without turgor goes to; the leaf falls before it reaches them.
279
+ const dry = has_dryness ? attachments.dryness[att_idx] : 0.0;
280
+ const fold_angle = turgid_fold + (Math.PI * 0.5 - turgid_fold) * dry;
281
+ const sin_fold = Math.sin(fold_angle);
282
+ const cos_fold = Math.cos(fold_angle);
283
+ const arch_h = turgid_arch * (1.0 - dry);
284
+ const droop = turgid_droop + (1.0 - turgid_droop) * dry;
285
+
286
+ const j0 = has_j4 ? attachments.joints4[a4] : attachments.joints[a2];
287
+ const j1 = has_j4 ? attachments.joints4[a4 + 1] : attachments.joints[a2 + 1];
288
+ const j2 = has_j4 ? attachments.joints4[a4 + 2] : 0;
289
+ const j3 = has_j4 ? attachments.joints4[a4 + 3] : 0;
290
+ const w0 = has_j4 ? attachments.weights4[a4] : attachments.weights[a2];
291
+ const w1 = has_j4 ? attachments.weights4[a4 + 1] : attachments.weights[a2 + 1];
292
+ const w2 = has_j4 ? attachments.weights4[a4 + 2] : 0.0;
293
+ const w3 = has_j4 ? attachments.weights4[a4 + 3] : 0.0;
294
+
295
+ const base_v = v_cursor;
296
+
297
+ if (card_type === FoliageCardType.CrossCard) {
298
+ // Plane 0: horizontal (W, F) with normal +U (since W x F = +U)
299
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, left_w, 0, 0, 0, 1, 0, 0.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
300
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, right_w, 0, 0, 0, 1, 0, 1.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
301
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, left_w, 0, card_l, 0, 1, 0, 0.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
302
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, right_w, 0, card_l, 0, 1, 0, 1.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
303
+
304
+ indices[i_cursor++] = base_v + 0;
305
+ indices[i_cursor++] = base_v + 1;
306
+ indices[i_cursor++] = base_v + 3;
307
+ indices[i_cursor++] = base_v + 0;
308
+ indices[i_cursor++] = base_v + 3;
309
+ indices[i_cursor++] = base_v + 2;
310
+
311
+ // Plane 1: vertical (-U to +U, F) with normal +W (since (-U) x F = +W)
312
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0, -left_w, 0, 1, 0, 0, 0.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
313
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0, -right_w, 0, 1, 0, 0, 1.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
314
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0, -left_w, card_l, 1, 0, 0, 0.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
315
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0, -right_w, card_l, 1, 0, 0, 1.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
316
+
317
+ indices[i_cursor++] = base_v + 4;
318
+ indices[i_cursor++] = base_v + 5;
319
+ indices[i_cursor++] = base_v + 7;
320
+ indices[i_cursor++] = base_v + 4;
321
+ indices[i_cursor++] = base_v + 7;
322
+ indices[i_cursor++] = base_v + 6;
323
+ } else if (card_type === FoliageCardType.TriStarCluster) {
324
+ for (let p = 0; p < 3; p++) {
325
+ const ang = (p * Math.PI) / 3.0;
326
+ const ca = Math.cos(ang);
327
+ const sa = Math.sin(ang);
328
+ const p_left_x = ca * left_w;
329
+ const p_left_y = sa * left_w;
330
+ const p_right_x = ca * right_w;
331
+ const p_right_y = sa * right_w;
332
+ const pn_x = -sa;
333
+ const pn_y = ca;
334
+ const p_base = base_v + p * 4;
335
+
336
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, p_left_x, p_left_y, 0, pn_x, pn_y, 0, 0.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
337
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, p_right_x, p_right_y, 0, pn_x, pn_y, 0, 1.0, anchor_v, b_sway * 0.4, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
338
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, p_left_x, p_left_y, card_l, pn_x, pn_y, 0, 0.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
339
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, p_right_x, p_right_y, card_l, pn_x, pn_y, 0, 1.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
340
+
341
+ indices[i_cursor++] = p_base + 0;
342
+ indices[i_cursor++] = p_base + 1;
343
+ indices[i_cursor++] = p_base + 3;
344
+ indices[i_cursor++] = p_base + 0;
345
+ indices[i_cursor++] = p_base + 3;
346
+ indices[i_cursor++] = p_base + 2;
347
+ }
348
+ } else if (card_type === FoliageCardType.RibbedFrond) {
349
+ const lift_wing = half_w * sin_fold;
350
+ const span_w = half_w * cos_fold;
351
+
352
+ for (let s = 0; s <= frond_segs; s++) {
353
+ const t = s / frond_segs;
354
+ const v_tex = anchor_v + (1.0 - anchor_v) * t;
355
+ const z_s = t * card_l;
356
+ const y_spine = card_l * (arch_h * 4.0 * t * (1.0 - t) - droop * t * t);
357
+ const env_w = Math.sin(Math.PI * (0.12 + 0.82 * t));
358
+ const w_s = span_w * env_w;
359
+ const lift_s = lift_wing * env_w;
360
+ const flutter = clamp(b_sway * 0.3 + 0.7 * t, 0.0, 1.0);
361
+
362
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, -w_s, y_spine + lift_s, z_s, -sin_fold * 0.35, cos_fold, 0, 0.0, v_tex, flutter, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
363
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0.0, y_spine, z_s, 0.0, 1.0, 0, anchor_u, v_tex, flutter * 0.75, phase, r_col * 0.94, g_col * 0.94, b_col * 0.94, j0, j1, j2, j3, w0, w1, w2, w3);
364
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, w_s, y_spine + lift_s, z_s, sin_fold * 0.35, cos_fold, 0, 1.0, v_tex, flutter, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
365
+
366
+ if (s > 0) {
367
+ const r0 = base_v + (s - 1) * 3;
368
+ const r1 = base_v + s * 3;
369
+ indices[i_cursor++] = r0 + 0;
370
+ indices[i_cursor++] = r0 + 1;
371
+ indices[i_cursor++] = r1 + 1;
372
+ indices[i_cursor++] = r0 + 0;
373
+ indices[i_cursor++] = r1 + 1;
374
+ indices[i_cursor++] = r1 + 0;
375
+
376
+ indices[i_cursor++] = r0 + 1;
377
+ indices[i_cursor++] = r0 + 2;
378
+ indices[i_cursor++] = r1 + 2;
379
+ indices[i_cursor++] = r0 + 1;
380
+ indices[i_cursor++] = r1 + 2;
381
+ indices[i_cursor++] = r1 + 1;
382
+ }
383
+ }
384
+ } else if (card_type === FoliageCardType.CutoutHull) {
385
+ // The tile's fitted hull, folded along the midrib u = anchor_u: each half turns about it by fold_angle,
386
+ // and the card arches and droops along its length like a frond's spine
387
+ const hull = hull_source;
388
+ const hull_base = hull_source === cutout_hulls ? v_idx * CUTOUT_HULL_VERTEX_COUNT * 2 : 0;
389
+ const len_per_v = card_l / (1.0 - anchor_v);
390
+ const mid_v = hull_mid_v[hull_source === cutout_hulls ? v_idx : 0];
391
+ const mid_t = clamp((mid_v - anchor_v) * len_per_v / card_l, 0.0, 1.0);
392
+
393
+ // midrib vertex, the fan's centre
394
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz,
395
+ 0.0, card_l * (arch_h * 4.0 * mid_t * (1.0 - mid_t) - droop * mid_t * mid_t), (mid_v - anchor_v) * len_per_v,
396
+ 0, 1, 0, anchor_u, mid_v, lerp(b_sway * 0.25, 1.0, mid_t), phase,
397
+ r_col * 0.96, g_col * 0.96, b_col * 0.96, j0, j1, j2, j3, w0, w1, w2, w3);
398
+
399
+ for (let h = 0; h < CUTOUT_HULL_VERTEX_COUNT; h++) {
400
+ const u_h = hull[hull_base + h * 2];
401
+ const v_h = hull[hull_base + h * 2 + 1];
402
+ const across = (u_h - anchor_u) * card_w;
403
+ const side = across < 0.0 ? -1.0 : 1.0;
404
+ const along = (v_h - anchor_v) * len_per_v;
405
+ const t = clamp(along / card_l, 0.0, 1.0);
406
+
407
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz,
408
+ across * cos_fold,
409
+ Math.abs(across) * sin_fold + card_l * (arch_h * 4.0 * t * (1.0 - t) - droop * t * t),
410
+ along,
411
+ side * -sin_fold * 0.35, cos_fold, 0,
412
+ u_h, v_h, lerp(b_sway * 0.25, 1.0, t), phase,
413
+ r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
414
+
415
+ // counter-clockwise in (u, v) is counter-clockwise in (W, F), which faces +U
416
+ indices[i_cursor++] = base_v;
417
+ indices[i_cursor++] = base_v + 1 + h;
418
+ indices[i_cursor++] = base_v + 1 + (h + 1) % CUTOUT_HULL_VERTEX_COUNT;
419
+ }
420
+ } else {
421
+ // Default: FoldedVCard (6 vertices, 4 triangles)
422
+ const lift = half_w * sin_fold;
423
+ const span = half_w * cos_fold;
424
+ const tip_droop = -card_l * droop * 0.5;
425
+ const mid_arch = card_l * arch_h * 0.5;
426
+
427
+ // Row 0 (base, z = 0)
428
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, -span, lift, 0.0, -sin_fold * 0.35, cos_fold, 0, 0.0, anchor_v, b_sway * 0.45, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
429
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0.0, 0.0, 0.0, 0.0, 1.0, 0, anchor_u, anchor_v, b_sway * 0.25, phase, r_col * 0.92, g_col * 0.92, b_col * 0.92, j0, j1, j2, j3, w0, w1, w2, w3);
430
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, span, lift, 0.0, sin_fold * 0.35, cos_fold, 0, 1.0, anchor_v, b_sway * 0.45, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
431
+
432
+ // Row 1 (tip, z = card_l)
433
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, -span, lift + tip_droop, card_l, -sin_fold * 0.35, cos_fold, 0, 0.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
434
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, 0.0, mid_arch + tip_droop, card_l, 0.0, 1.0, 0, anchor_u, 1.0, 0.85, phase, r_col * 0.96, g_col * 0.96, b_col * 0.96, j0, j1, j2, j3, w0, w1, w2, w3);
435
+ write_card_vertex(px0, py0, pz0, wx, wy, wz, ux, uy, uz, fx, fy, fz, span, lift + tip_droop, card_l, sin_fold * 0.35, cos_fold, 0, 1.0, 1.0, 1.0, phase, r_col, g_col, b_col, j0, j1, j2, j3, w0, w1, w2, w3);
436
+
437
+ // Left wing quad
438
+ indices[i_cursor++] = base_v + 0;
439
+ indices[i_cursor++] = base_v + 1;
440
+ indices[i_cursor++] = base_v + 4;
441
+ indices[i_cursor++] = base_v + 0;
442
+ indices[i_cursor++] = base_v + 4;
443
+ indices[i_cursor++] = base_v + 3;
444
+
445
+ // Right wing quad
446
+ indices[i_cursor++] = base_v + 1;
447
+ indices[i_cursor++] = base_v + 2;
448
+ indices[i_cursor++] = base_v + 5;
449
+ indices[i_cursor++] = base_v + 1;
450
+ indices[i_cursor++] = base_v + 5;
451
+ indices[i_cursor++] = base_v + 4;
452
+ }
453
+ }
454
+
455
+ // Apply volumetric canopy AO and hemisphere-safe normal puffing BEFORE ensureTangents()
456
+ tree_mesh_apply_canopy_ao(out, skeleton.crown_center, skeleton.crown_radii, {
457
+ ao_core_darkening: fol.ao_core_darkening ?? 0.35,
458
+ normal_puffiness: fol.normal_puffiness ?? 0.35,
459
+ preserve_existing_color: true
460
+ });
461
+
462
+ out.name = `${species.name || "tree"}_foliage_lod${lod_cfg.level}`;
463
+ out.ensureBounds();
464
+ out.ensureTangents();
465
+
466
+ return out;
467
+ }