@woosh/meep-engine 3.61.0 → 3.61.2

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 (170) hide show
  1. package/package.json +1 -1
  2. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  3. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +4 -0
  4. package/src/engine/graphics/ecs/decal/v2/Decal.d.ts +46 -2
  5. package/src/engine/graphics/ecs/decal/v2/Decal.d.ts.map +1 -1
  6. package/src/engine/graphics/ecs/decal/v2/Decal.js +74 -5
  7. package/src/engine/graphics/ecs/decal/v2/DecalSerializationAdapter.d.ts.map +1 -1
  8. package/src/engine/graphics/ecs/decal/v2/DecalSerializationAdapter.js +13 -1
  9. package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_1_2.d.ts +9 -0
  10. package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_1_2.d.ts.map +1 -0
  11. package/src/engine/graphics/ecs/decal/v2/DecalSerializationUpgrader_1_2.js +37 -0
  12. package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
  13. package/src/engine/graphics3/DecalSystem.js +462 -447
  14. package/src/engine/graphics3/decal/DECAL_GPU_RECORD.d.ts +14 -0
  15. package/src/engine/graphics3/decal/DECAL_GPU_RECORD.d.ts.map +1 -1
  16. package/src/engine/graphics3/decal/DECAL_GPU_RECORD.js +53 -13
  17. package/src/engine/graphics3/decal/chunk_decal_record.d.ts +6 -1
  18. package/src/engine/graphics3/decal/chunk_decal_record.d.ts.map +1 -1
  19. package/src/engine/graphics3/decal/chunk_decal_record.js +28 -9
  20. package/src/engine/graphics3/decal/decal_gpu_records.d.ts +2 -1
  21. package/src/engine/graphics3/decal/decal_gpu_records.d.ts.map +1 -1
  22. package/src/engine/graphics3/decal/decal_gpu_records.js +26 -5
  23. package/src/engine/graphics3/decal/decal_material_samplers.d.ts +6 -0
  24. package/src/engine/graphics3/decal/decal_material_samplers.d.ts.map +1 -1
  25. package/src/engine/graphics3/decal/decal_material_samplers.js +13 -0
  26. package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +1 -1
  27. package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +1 -1
  28. package/src/engine/graphics3/decal/shader_decal_composite.d.ts +5 -0
  29. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  30. package/src/engine/graphics3/decal/shader_decal_composite.js +69 -28
  31. package/src/engine/network/sim/SimActionExecutor.d.ts +4 -0
  32. package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
  33. package/src/engine/network/sim/SimActionExecutor.js +4 -0
  34. package/src/generation/foliage/material/foliage_atlas_cutout.d.ts.map +1 -1
  35. package/src/generation/foliage/material/foliage_atlas_cutout.js +21 -5
  36. package/src/generation/foliage/mesh/tree_cluster_representatives.d.ts +8 -0
  37. package/src/generation/foliage/mesh/tree_cluster_representatives.d.ts.map +1 -1
  38. package/src/generation/foliage/mesh/tree_cluster_representatives.js +178 -78
  39. package/src/generation/foliage/mesh/tree_mesh_build_bark.d.ts.map +1 -1
  40. package/src/generation/foliage/mesh/tree_mesh_build_bark.js +178 -97
  41. package/src/generation/foliage/skeleton/ShootLightField.d.ts +1 -0
  42. package/src/generation/foliage/skeleton/ShootLightField.d.ts.map +1 -1
  43. package/src/generation/foliage/skeleton/ShootLightField.js +88 -17
  44. package/src/generation/foliage/skeleton/ShootWoodHash.d.ts +8 -4
  45. package/src/generation/foliage/skeleton/ShootWoodHash.d.ts.map +1 -1
  46. package/src/generation/foliage/skeleton/ShootWoodHash.js +216 -27
  47. package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
  48. package/src/shade/playground/probe_clipmap/main.js +4 -0
  49. package/src/shade/playground/probe_clipmap_box/README.md +55 -0
  50. package/src/shade/playground/probe_clipmap_box/index.html +185 -0
  51. package/src/shade/playground/probe_clipmap_box/main.d.ts +8 -0
  52. package/src/shade/playground/probe_clipmap_box/main.d.ts.map +1 -0
  53. package/src/shade/playground/probe_clipmap_box/main.js +323 -0
  54. package/src/shade/playground/probe_clipmap_box/scene.d.ts +93 -0
  55. package/src/shade/playground/probe_clipmap_box/scene.d.ts.map +1 -0
  56. package/src/shade/playground/probe_clipmap_box/scene.js +256 -0
  57. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  58. package/src/shade/renderer/Renderer.js +6 -1
  59. package/src/shade/renderer/buffer/graph_read_buffer.d.ts +3 -1
  60. package/src/shade/renderer/buffer/graph_read_buffer.d.ts.map +1 -1
  61. package/src/shade/renderer/buffer/graph_read_buffer.js +9 -6
  62. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v2/chunk_brick4_sh3_color_decode.d.ts +12 -0
  63. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v2/chunk_brick4_sh3_color_decode.d.ts.map +1 -1
  64. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v2/chunk_brick4_sh3_color_decode.js +49 -24
  65. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v2/chunk_brick4_sh3_color_encode.d.ts +12 -0
  66. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v2/chunk_brick4_sh3_color_encode.d.ts.map +1 -1
  67. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v2/chunk_brick4_sh3_color_encode.js +50 -19
  68. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v2/chunk_sh3_compress.d.ts.map +1 -1
  69. package/src/shade/renderer/global_illumination/brick4/gpu/encoding/v2/chunk_sh3_compress.js +6 -2
  70. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +5 -1
  71. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  72. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +7 -3
  73. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +6 -4
  74. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  75. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +10 -9
  76. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +12 -0
  77. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
  78. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +14 -0
  79. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +17 -22
  80. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
  81. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +53 -65
  82. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.d.ts +2 -3
  83. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.d.ts.map +1 -1
  84. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_pick.js +9 -20
  85. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts +2 -2
  86. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
  87. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +14 -19
  88. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +1 -1
  89. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
  90. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +8 -4
  91. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_admit.js +1 -1
  92. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_bounce.d.ts +2 -1
  93. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_bounce.d.ts.map +1 -1
  94. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_bounce.js +13 -18
  95. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_candidate.d.ts +1 -1
  96. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_candidate.d.ts.map +1 -1
  97. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_candidate.js +4 -2
  98. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_diffuse.d.ts.map +1 -1
  99. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_diffuse.js +4 -6
  100. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_indirect_lighting.d.ts.map +1 -1
  101. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_indirect_lighting.js +4 -7
  102. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts +4 -3
  103. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.d.ts.map +1 -1
  104. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_project.js +14 -13
  105. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts +3 -4
  106. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.d.ts.map +1 -1
  107. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_trace.js +7 -15
  108. package/src/shade/renderer/light/environment/shader_downsample_environment_map.d.ts.map +1 -1
  109. package/src/shade/renderer/light/environment/shader_downsample_environment_map.js +5 -1
  110. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  111. package/src/shade/renderer/particles/GPUParticleSystem.js +1 -7
  112. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts +26 -8
  113. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts.map +1 -1
  114. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.js +170 -142
  115. package/src/shade/renderer/rasterize/forward/chunk_forward_sample_indirect_probe_clipmap.d.ts +1 -1
  116. package/src/shade/renderer/rasterize/forward/chunk_forward_sample_indirect_probe_clipmap.d.ts.map +1 -1
  117. package/src/shade/renderer/rasterize/forward/chunk_forward_sample_indirect_probe_clipmap.js +5 -8
  118. package/src/shade/renderer/restir/di/chunk_restir_resolve.d.ts.map +1 -1
  119. package/src/shade/renderer/restir/di/chunk_restir_resolve.js +91 -94
  120. package/src/shade/renderer/restir/reservoir/chunk_rtxdi_stream_sample.js +1 -1
  121. package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_decode.d.ts +14 -0
  122. package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_decode.d.ts.map +1 -1
  123. package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_decode.js +36 -21
  124. package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_sample_gather.d.ts +22 -0
  125. package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_sample_gather.d.ts.map +1 -0
  126. package/src/shade/renderer/shader/chunk/color/rgbe9995/chunk_rgbe9995_sample_gather.js +64 -0
  127. package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts +15 -4
  128. package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.d.ts.map +1 -1
  129. package/src/shade/renderer/shader/chunk/filter/chunk_multiscale_mean.js +33 -7
  130. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_vertex_normal.d.ts.map +1 -1
  131. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_vertex_normal.js +13 -18
  132. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_vertex_normal.d.ts.map +1 -1
  133. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_encode_vertex_normal.js +28 -30
  134. package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_u32_to_vec2.d.ts.map +1 -1
  135. package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_u32_to_vec2.js +15 -16
  136. package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_vec2_to_u32.d.ts +0 -1
  137. package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_vec2_to_u32.d.ts.map +1 -1
  138. package/src/shade/renderer/shader/chunk/geometry/vec2/chunk_unit_vec2_to_u32.js +15 -18
  139. package/src/shade/renderer/shader/chunk/noise/chunk_sobol_sample_vec2.d.ts +40 -0
  140. package/src/shade/renderer/shader/chunk/noise/chunk_sobol_sample_vec2.d.ts.map +1 -0
  141. package/src/shade/renderer/shader/chunk/noise/chunk_sobol_sample_vec2.js +75 -0
  142. package/src/shade/renderer/shader/chunk/noise/chunk_vdc_radical_inverse.d.ts.map +1 -1
  143. package/src/shade/renderer/shader/chunk/noise/chunk_vdc_radical_inverse.js +3 -1
  144. package/src/shade/renderer/shader/chunk/random/chunk_hash_lk.d.ts +28 -0
  145. package/src/shade/renderer/shader/chunk/random/chunk_hash_lk.d.ts.map +1 -0
  146. package/src/shade/renderer/shader/chunk/random/chunk_hash_lk.js +41 -0
  147. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh3_color_add_scaled.d.ts +12 -0
  148. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh3_color_add_scaled.d.ts.map +1 -0
  149. package/src/shade/renderer/shader/chunk/spherical_harmonics/chunk_sh3_color_add_scaled.js +24 -0
  150. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts +3 -2
  151. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts.map +1 -1
  152. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.js +4 -0
  153. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts +2 -5
  154. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts.map +1 -1
  155. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.js +3 -20
  156. package/src/shade/renderer/water/chunk_water_gi.d.ts +1 -1
  157. package/src/shade/renderer/water/chunk_water_gi.d.ts.map +1 -1
  158. package/src/shade/renderer/water/chunk_water_gi.js +6 -8
  159. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +8 -1
  160. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  161. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +79 -35
  162. package/src/core/math/hash/squirrel3.d.ts +0 -17
  163. package/src/core/math/hash/squirrel3.d.ts.map +0 -1
  164. package/src/core/math/hash/squirrel3.js +0 -30
  165. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_sh3_color_encode.d.ts +0 -2
  166. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_sh3_color_encode.d.ts.map +0 -1
  167. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_sh3_color_encode.js +0 -6
  168. package/src/shade/renderer/shader/chunk/random/chunk_hash_squirrel3.d.ts +0 -12
  169. package/src/shade/renderer/shader/chunk/random/chunk_hash_squirrel3.d.ts.map +0 -1
  170. package/src/shade/renderer/shader/chunk/random/chunk_hash_squirrel3.js +0 -30
@@ -72,9 +72,55 @@ const RING = {
72
72
  /** @type {import("../config/BranchGeneratorConfig.js").BranchGeneratorConfig|null} */
73
73
  lvl_cfg: null,
74
74
  collars: [],
75
- knots: []
75
+ knots: [],
76
+ active_collars: [],
77
+ active_collar_theta: [],
78
+ active_knots: [],
79
+ active_knot_theta: [],
80
+ is_circle: false
76
81
  };
77
82
 
83
+ function ring_prepare(ring) {
84
+ const ac = ring.active_collars;
85
+ const act = ring.active_collar_theta;
86
+ ac.length = 0;
87
+ act.length = 0;
88
+ const rx = ring.rx, ry = ring.ry, rz = ring.rz;
89
+ const sx = ring.sx, sy = ring.sy, sz = ring.sz;
90
+ const t_norm = ring.t_norm;
91
+ if (ring.collar_amp > 1e-4) {
92
+ const collars = ring.collars;
93
+ for (let i = 0; i < collars.length; i++) {
94
+ const cc = collars[i];
95
+ if (Math.abs(t_norm - cc.t_c) < 3.2 * cc.sigma_t) {
96
+ ac.push(cc);
97
+ act.push(Math.atan2(
98
+ cc.tx_c * sx + cc.ty_c * sy + cc.tz_c * sz,
99
+ cc.tx_c * rx + cc.ty_c * ry + cc.tz_c * rz
100
+ ));
101
+ }
102
+ }
103
+ }
104
+ const ak = ring.active_knots;
105
+ const akt = ring.active_knot_theta;
106
+ ak.length = 0;
107
+ akt.length = 0;
108
+ if (ring.knot_scale > 0.05) {
109
+ const knots = ring.knots;
110
+ for (let i = 0; i < knots.length; i++) {
111
+ const kn = knots[i];
112
+ if (Math.abs(t_norm - kn.t_k) < 3.2 * kn.sigma_t) {
113
+ ak.push(kn);
114
+ akt.push(Math.atan2(
115
+ kn.tx_k * sx + kn.ty_k * sy + kn.tz_k * sz,
116
+ kn.tx_k * rx + kn.ty_k * ry + kn.tz_k * rz
117
+ ));
118
+ }
119
+ }
120
+ }
121
+ ring.is_circle = !(ring.flare_env > 0.0) && !(ring.flute_env > 1e-5) && !(ring.oval_ecc > 1e-4) && ac.length === 0 && ak.length === 0;
122
+ }
123
+
78
124
  /**
79
125
  * The collars of the branches `b` carries, where each joins it: a boss on its bark, and the grain flowing round it.
80
126
  *
@@ -160,6 +206,14 @@ function circle_sides(r, tolerance) {
160
206
  * @type {number}
161
207
  */
162
208
  const BARK_REFERENCE_SIDES = 64;
209
+ const REF_THETA = new Float64Array(BARK_REFERENCE_SIDES);
210
+ const REF_COS = new Float64Array(BARK_REFERENCE_SIDES);
211
+ const REF_SIN = new Float64Array(BARK_REFERENCE_SIDES);
212
+ for (let j = 0; j < BARK_REFERENCE_SIDES; j++) {
213
+ REF_THETA[j] = PI2 * j / BARK_REFERENCE_SIDES;
214
+ REF_COS[j] = Math.cos(REF_THETA[j]);
215
+ REF_SIN[j] = Math.sin(REF_THETA[j]);
216
+ }
163
217
 
164
218
  /**
165
219
  * Fills {@link RING} for the candidate ring `c` of a plan's arrays.
@@ -183,19 +237,18 @@ function plan_fill_ring(plan, c, lvl_cfg, collars, knots) {
183
237
  RING.lvl_cfg = lvl_cfg;
184
238
  RING.collars = collars;
185
239
  RING.knots = knots;
240
+ ring_prepare(RING);
186
241
  }
187
242
 
188
243
  /**
189
244
  * Point of candidate ring `c` at angle `theta`, into `out` at `o`: its spine point plus its shaped radius along the
190
245
  * ring's radial direction. {@link RING} must hold the ring.
191
246
  */
192
- function plan_ring_point(out, o, plan, c, theta) {
193
- bark_ring_shape(SCRATCH_SHAPE, theta, RING);
247
+ function plan_ring_point(out, o, plan, c, theta, cos_t = Math.cos(theta), sin_t = Math.sin(theta)) {
248
+ bark_ring_shape(SCRATCH_SHAPE, theta, RING, cos_t, sin_t);
194
249
  // the weld's symmetric flare on the rings after the first
195
250
  const r_mult = SCRATCH_SHAPE[0] + (c > 0 ? plan.weld[c] * 0.18 : 0.0);
196
251
  const r = plan.radius[c] * Math.max(0.25, r_mult);
197
- const cos_t = Math.cos(theta);
198
- const sin_t = Math.sin(theta);
199
252
  out[o] = plan.points[c * 3] + r * (cos_t * RING.rx + sin_t * RING.sx);
200
253
  out[o + 1] = plan.points[c * 3 + 1] + r * (cos_t * RING.ry + sin_t * RING.sy);
201
254
  out[o + 2] = plan.points[c * 3 + 2] + r * (cos_t * RING.rz + sin_t * RING.sz);
@@ -209,7 +262,8 @@ function point_segment_distance(p, po, a, ao, b, bo) {
209
262
  const apx = p[po] - a[ao], apy = p[po + 1] - a[ao + 1], apz = p[po + 2] - a[ao + 2];
210
263
  const len2 = abx * abx + aby * aby + abz * abz;
211
264
  const t = len2 > 0 ? clamp((apx * abx + apy * aby + apz * abz) / len2, 0, 1) : 0;
212
- return Math.hypot(apx - t * abx, apy - t * aby, apz - t * abz);
265
+ const dx = apx - t * abx, dy = apy - t * aby, dz = apz - t * abz;
266
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
213
267
  }
214
268
 
215
269
  /**
@@ -370,8 +424,26 @@ function bark_plan_rings(skeleton, b, lvl_cfg, nodes, edges, collars, knots, wel
370
424
  const measured = new Float64Array(C * M * 3);
371
425
  for (let c = 0; c < C; c++) {
372
426
  plan_fill_ring(plan, c, lvl_cfg, collars, knots);
373
- for (let j = 0; j < M; j++) {
374
- plan_ring_point(measured, (c * M + j) * 3, plan, c, PI2 * j / M);
427
+ if (M === BARK_REFERENCE_SIDES && RING.is_circle) {
428
+ const r = plan.radius[c] * Math.max(0.25, 1.0 + (c > 0 ? plan.weld[c] * 0.18 : 0.0));
429
+ const px = plan.points[c * 3], py = plan.points[c * 3 + 1], pz = plan.points[c * 3 + 2];
430
+ const rx = RING.rx, ry = RING.ry, rz = RING.rz, sx = RING.sx, sy = RING.sy, sz = RING.sz;
431
+ const base = c * M * 3;
432
+ for (let j = 0; j < M; j++) {
433
+ const cos_t = REF_COS[j], sin_t = REF_SIN[j];
434
+ const o = base + j * 3;
435
+ measured[o] = px + r * (cos_t * rx + sin_t * sx);
436
+ measured[o + 1] = py + r * (cos_t * ry + sin_t * sy);
437
+ measured[o + 2] = pz + r * (cos_t * rz + sin_t * sz);
438
+ }
439
+ } else if (M === BARK_REFERENCE_SIDES) {
440
+ for (let j = 0; j < M; j++) {
441
+ plan_ring_point(measured, (c * M + j) * 3, plan, c, REF_THETA[j], REF_COS[j], REF_SIN[j]);
442
+ }
443
+ } else {
444
+ for (let j = 0; j < M; j++) {
445
+ plan_ring_point(measured, (c * M + j) * 3, plan, c, PI2 * j / M);
446
+ }
375
447
  }
376
448
  }
377
449
 
@@ -411,18 +483,27 @@ function bark_plan_rings(skeleton, b, lvl_cfg, nodes, edges, collars, knots, wel
411
483
  for (let c = 0; c < C; c++) {
412
484
  if (!keep[c]) continue;
413
485
  plan_fill_ring(plan, c, lvl_cfg, collars, knots);
486
+ polygon[0] = measured[c * M * 3];
487
+ polygon[1] = measured[c * M * 3 + 1];
488
+ polygon[2] = measured[c * M * 3 + 2];
414
489
  let sides = 3;
415
490
  for (; sides < M; sides++) {
416
- for (let k = 0; k <= sides; k++) {
417
- plan_ring_point(polygon, k * 3, plan, c, PI2 * (k % sides) / sides);
491
+ plan_ring_point(polygon, 3, plan, c, PI2 / sides);
492
+ plan_ring_point(SCRATCH_PLAN_MID, 0, plan, c, PI2 * 0.5 / sides);
493
+ if (point_segment_distance(SCRATCH_PLAN_MID, 0, polygon, 0, polygon, 3) > tolerance) continue;
494
+ for (let k = 2; k < sides; k++) {
495
+ plan_ring_point(polygon, k * 3, plan, c, PI2 * k / sides);
418
496
  }
497
+ polygon[sides * 3] = polygon[0];
498
+ polygon[sides * 3 + 1] = polygon[1];
499
+ polygon[sides * 3 + 2] = polygon[2];
419
500
  let within = true;
420
501
  for (let j = 0; j < M && within; j++) {
421
502
  const k = Math.floor(j * sides / M);
422
503
  within = point_segment_distance(measured, (c * M + j) * 3, polygon, k * 3, polygon, (k + 1) * 3) <= tolerance;
423
504
  }
424
505
  // a chord strays furthest from the wood at its middle
425
- for (let k = 0; k < sides && within; k++) {
506
+ for (let k = 1; k < sides && within; k++) {
426
507
  plan_ring_point(SCRATCH_PLAN_MID, 0, plan, c, PI2 * (k + 0.5) / sides);
427
508
  within = point_segment_distance(SCRATCH_PLAN_MID, 0, polygon, k * 3, polygon, (k + 1) * 3) <= tolerance;
428
509
  }
@@ -445,14 +526,19 @@ function bark_plan_rings(skeleton, b, lvl_cfg, nodes, edges, collars, knots, wel
445
526
  * @param {number} theta
446
527
  * @param {RING} ring
447
528
  */
448
- function bark_ring_shape(out, theta, ring) {
529
+ function bark_ring_shape(out, theta, ring, cos_t = Math.cos(theta), sin_t = Math.sin(theta)) {
530
+ if (ring.is_circle) {
531
+ out[0] = 1.0;
532
+ out[1] = 0.0;
533
+ out[2] = 0.0;
534
+ out[3] = 1.0;
535
+ return;
536
+ }
449
537
  const lvl_cfg = ring.lvl_cfg;
450
- const rx = ring.rx, ry = ring.ry, rz = ring.rz;
451
- const sx = ring.sx, sy = ring.sy, sz = ring.sz;
538
+ const ry = ring.ry;
539
+ const sy = ring.sy;
452
540
  const t_norm = ring.t_norm;
453
541
  const unwelded = ring.unwelded;
454
- const cos_t = Math.cos(theta);
455
- const sin_t = Math.sin(theta);
456
542
  const n_rad_y = cos_t * ry + sin_t * sy;
457
543
  const tan_theta_y = -sin_t * ry + cos_t * sy;
458
544
 
@@ -490,64 +576,52 @@ function bark_ring_shape(out, theta, ring) {
490
576
  }
491
577
 
492
578
  // 3. Parent-stem branch collar swelling bosses & UV grain streamline deflection around child branches
493
- const collars = ring.collars;
579
+ const collars = ring.active_collars;
580
+ const collar_thetas = ring.active_collar_theta;
494
581
  const parent_collar_amp = ring.collar_amp;
495
- if (parent_collar_amp > 1e-4 && collars.length > 0) {
496
- for (let c_idx = 0; c_idx < collars.length; c_idx++) {
497
- const cc = collars[c_idx];
498
- const dt = t_norm - cc.t_c;
499
- if (Math.abs(dt) < 3.2 * cc.sigma_t) {
500
- const theta_c = Math.atan2(
501
- cc.tx_c * sx + cc.ty_c * sy + cc.tz_c * sz,
502
- cc.tx_c * rx + cc.ty_c * ry + cc.tz_c * rz
503
- );
504
- const dtheta = Math.atan2(Math.sin(theta - theta_c), Math.cos(theta - theta_c));
505
- const sig_t = dt < 0.0 ? cc.sigma_t * 1.25 : cc.sigma_t * 0.90;
506
- const u_t = dt / sig_t;
507
- const u_th = dtheta / cc.sigma_theta;
508
- const dist2 = u_t * u_t + u_th * u_th;
509
- if (dist2 < 9.0) {
510
- const gauss = Math.exp(-0.5 * dist2);
511
- const b_amp = parent_collar_amp * (0.55 + 0.75 * cc.r_ratio);
512
- r_mult += b_amp * gauss;
513
- d_rmult_dtheta += -b_amp * gauss * (u_th / cc.sigma_theta);
514
- const r_uv2 = dist2 + 0.85;
515
- u_warp += 0.008 * unwelded * (u_th / r_uv2) * Math.exp(-0.35 * dist2);
516
- }
517
- }
582
+ for (let c_idx = 0; c_idx < collars.length; c_idx++) {
583
+ const cc = collars[c_idx];
584
+ const dt = t_norm - cc.t_c;
585
+ const theta_c = collar_thetas[c_idx];
586
+ const dtheta = Math.atan2(Math.sin(theta - theta_c), Math.cos(theta - theta_c));
587
+ const sig_t = dt < 0.0 ? cc.sigma_t * 1.25 : cc.sigma_t * 0.90;
588
+ const u_t = dt / sig_t;
589
+ const u_th = dtheta / cc.sigma_theta;
590
+ const dist2 = u_t * u_t + u_th * u_th;
591
+ if (dist2 < 9.0) {
592
+ const gauss = Math.exp(-0.5 * dist2);
593
+ const b_amp = parent_collar_amp * (0.55 + 0.75 * cc.r_ratio);
594
+ r_mult += b_amp * gauss;
595
+ d_rmult_dtheta += -b_amp * gauss * (u_th / cc.sigma_theta);
596
+ const r_uv2 = dist2 + 0.85;
597
+ u_warp += 0.008 * unwelded * (u_th / r_uv2) * Math.exp(-0.35 * dist2);
518
598
  }
519
599
  }
520
600
 
521
601
  // 4. Branch scars: volcanic callus rims + UV grain streamline deflection
522
- const knots = ring.knots;
602
+ const knots = ring.active_knots;
603
+ const knot_thetas = ring.active_knot_theta;
523
604
  const knot_scale = ring.knot_scale;
524
- if (knot_scale > 0.05 && knots.length > 0) {
525
- for (let k_idx = 0; k_idx < knots.length; k_idx++) {
526
- const kn = knots[k_idx];
527
- const dt = t_norm - kn.t_k;
528
- if (Math.abs(dt) < 3.2 * kn.sigma_t) {
529
- const theta_k = Math.atan2(
530
- kn.tx_k * sx + kn.ty_k * sy + kn.tz_k * sz,
531
- kn.tx_k * rx + kn.ty_k * ry + kn.tz_k * rz
532
- );
533
- const dtheta = Math.atan2(Math.sin(theta - theta_k), Math.cos(theta - theta_k));
534
- const u_t = dt / kn.sigma_t;
535
- const u_th = dtheta / kn.sigma_theta;
536
- const rho2 = u_t * u_t + u_th * u_th;
537
- if (rho2 < 9.0) {
538
- const k_amp = kn.amp_k * knot_scale;
539
- const exp_r = Math.exp(-0.85 * rho2);
540
- const w_rim = (0.30 + 1.90 * rho2) * exp_r;
541
- const dw_drho2 = (1.90 - 0.85 * (0.30 + 1.90 * rho2)) * exp_r;
542
- r_mult += k_amp * w_rim;
543
- d_rmult_dtheta += k_amp * dw_drho2 * (2.0 * u_th / kn.sigma_theta);
544
- if (rho2 < 0.65) {
545
- vert_ao *= (0.72 + 0.28 * (rho2 / 0.65));
546
- }
547
- const r_uv2 = rho2 + 0.85;
548
- u_warp += 0.009 * unwelded * (u_th / r_uv2) * Math.exp(-0.35 * rho2);
549
- }
605
+ for (let k_idx = 0; k_idx < knots.length; k_idx++) {
606
+ const kn = knots[k_idx];
607
+ const dt = t_norm - kn.t_k;
608
+ const theta_k = knot_thetas[k_idx];
609
+ const dtheta = Math.atan2(Math.sin(theta - theta_k), Math.cos(theta - theta_k));
610
+ const u_t = dt / kn.sigma_t;
611
+ const u_th = dtheta / kn.sigma_theta;
612
+ const rho2 = u_t * u_t + u_th * u_th;
613
+ if (rho2 < 9.0) {
614
+ const k_amp = kn.amp_k * knot_scale;
615
+ const exp_r = Math.exp(-0.85 * rho2);
616
+ const w_rim = (0.30 + 1.90 * rho2) * exp_r;
617
+ const dw_drho2 = (1.90 - 0.85 * (0.30 + 1.90 * rho2)) * exp_r;
618
+ r_mult += k_amp * w_rim;
619
+ d_rmult_dtheta += k_amp * dw_drho2 * (2.0 * u_th / kn.sigma_theta);
620
+ if (rho2 < 0.65) {
621
+ vert_ao *= (0.72 + 0.28 * (rho2 / 0.65));
550
622
  }
623
+ const r_uv2 = rho2 + 0.85;
624
+ u_warp += 0.009 * unwelded * (u_th / r_uv2) * Math.exp(-0.35 * rho2);
551
625
  }
552
626
  }
553
627
 
@@ -704,13 +778,13 @@ export function tree_mesh_build_bark(
704
778
  }
705
779
 
706
780
  const positions = geometry_obtain_attribute(out, StandardAttributes.Position, 3, total_vertices);
707
- const normals = geometry_obtain_attribute(out, StandardAttributes.Normal, 3, total_vertices);
708
- const uv0 = geometry_obtain_attribute(out, StandardAttributes.TextureCoordinates0, 2, total_vertices);
709
- const uv1 = geometry_obtain_attribute(out, StandardAttributes.TextureCoordinates1, 2, total_vertices);
710
- const colors = geometry_obtain_attribute(out, StandardAttributes.Color, 3, total_vertices);
711
- const joints = geometry_obtain_joints_attribute(out, total_vertices);
712
- const weights = geometry_obtain_attribute(out, StandardAttributes.SkinningWeights, 4, total_vertices);
713
- const indices = geometry_obtain_index(out, total_indices);
781
+ const normals = only_rings ? null : geometry_obtain_attribute(out, StandardAttributes.Normal, 3, total_vertices);
782
+ const uv0 = only_rings ? null : geometry_obtain_attribute(out, StandardAttributes.TextureCoordinates0, 2, total_vertices);
783
+ const uv1 = only_rings ? null : geometry_obtain_attribute(out, StandardAttributes.TextureCoordinates1, 2, total_vertices);
784
+ const colors = only_rings ? null : geometry_obtain_attribute(out, StandardAttributes.Color, 3, total_vertices);
785
+ const joints = only_rings ? null : geometry_obtain_joints_attribute(out, total_vertices);
786
+ const weights = only_rings ? null : geometry_obtain_attribute(out, StandardAttributes.SkinningWeights, 4, total_vertices);
787
+ const indices = only_rings ? null : geometry_obtain_index(out, total_indices);
714
788
 
715
789
  if (is_empty_fallback) {
716
790
  // Populate valid non-degenerate fallback quad so ensureTangents() and MeshoptClusterizer always succeed
@@ -1057,6 +1131,7 @@ export function tree_mesh_build_bark(
1057
1131
  RING.lvl_cfg = lvl_cfg;
1058
1132
  RING.collars = child_collars;
1059
1133
  RING.knots = branch_knots;
1134
+ ring_prepare(RING);
1060
1135
 
1061
1136
  for (let j = 0; j <= m_i; j++) {
1062
1137
  // Ensure exact periodic closure between j=0 and j=m_i
@@ -1076,7 +1151,7 @@ export function tree_mesh_build_bark(
1076
1151
  const tan_theta_z = -sin_t * rz + cos_t * sz;
1077
1152
 
1078
1153
  // The ring's smooth shape at theta (lobes, flutes, ovalization, collar bosses, knots), then its bark relief
1079
- bark_ring_shape(SCRATCH_SHAPE, theta, RING);
1154
+ bark_ring_shape(SCRATCH_SHAPE, theta, RING, cos_t, sin_t);
1080
1155
  let r_mult = SCRATCH_SHAPE[0];
1081
1156
  let d_rmult_dtheta = SCRATCH_SHAPE[1];
1082
1157
  let u_warp = SCRATCH_SHAPE[2];
@@ -1163,36 +1238,38 @@ export function tree_mesh_build_bark(
1163
1238
  }
1164
1239
 
1165
1240
  const v3 = v_cursor * 3;
1166
- const v2 = v_cursor * 2;
1167
- const v4 = v_cursor * 4;
1168
-
1169
1241
  positions[v3] = vx;
1170
1242
  positions[v3 + 1] = vy;
1171
1243
  positions[v3 + 2] = vz;
1172
1244
 
1173
- normals[v3] = vnx;
1174
- normals[v3 + 1] = vny;
1175
- normals[v3 + 2] = vnz;
1245
+ if (!only_rings) {
1246
+ const v2 = v_cursor * 2;
1247
+ const v4 = v_cursor * 4;
1248
+
1249
+ normals[v3] = vnx;
1250
+ normals[v3 + 1] = vny;
1251
+ normals[v3 + 2] = vnz;
1176
1252
 
1177
- uv0[v2] = k_wrap * (j / m_i) + twist_uv * arc_s + u_warp;
1178
- uv0[v2 + 1] = v_base;
1253
+ uv0[v2] = k_wrap * (j / m_i) + twist_uv * arc_s + u_warp;
1254
+ uv0[v2 + 1] = v_base;
1179
1255
 
1180
- uv1[v2] = wind_sway;
1181
- uv1[v2 + 1] = wind_phase;
1256
+ uv1[v2] = wind_sway;
1257
+ uv1[v2 + 1] = wind_phase;
1182
1258
 
1183
- colors[v3] = vert_ao;
1184
- colors[v3 + 1] = vert_ao;
1185
- colors[v3 + 2] = vert_ao;
1259
+ colors[v3] = vert_ao;
1260
+ colors[v3 + 1] = vert_ao;
1261
+ colors[v3 + 2] = vert_ao;
1186
1262
 
1187
- joints[v4] = ring_j0;
1188
- joints[v4 + 1] = ring_j1;
1189
- joints[v4 + 2] = ring_j2;
1190
- joints[v4 + 3] = ring_j3;
1263
+ joints[v4] = ring_j0;
1264
+ joints[v4 + 1] = ring_j1;
1265
+ joints[v4 + 2] = ring_j2;
1266
+ joints[v4 + 3] = ring_j3;
1191
1267
 
1192
- weights[v4] = ring_w0;
1193
- weights[v4 + 1] = ring_w1;
1194
- weights[v4 + 2] = ring_w2;
1195
- weights[v4 + 3] = ring_w3;
1268
+ weights[v4] = ring_w0;
1269
+ weights[v4 + 1] = ring_w1;
1270
+ weights[v4 + 2] = ring_w2;
1271
+ weights[v4 + 3] = ring_w3;
1272
+ }
1196
1273
 
1197
1274
  v_cursor++;
1198
1275
  }
@@ -1253,7 +1330,7 @@ export function tree_mesh_build_bark(
1253
1330
  w3: ring_w3
1254
1331
  });
1255
1332
 
1256
- if (prev_ring_start >= 0) {
1333
+ if (!only_rings && prev_ring_start >= 0) {
1257
1334
  i_cursor = branch_ring_triangulate(
1258
1335
  indices,
1259
1336
  i_cursor,
@@ -1302,6 +1379,10 @@ export function tree_mesh_build_bark(
1302
1379
  // living branch, a flat break on a dead stub
1303
1380
  const apex_start = v_cursor;
1304
1381
  const m_tip = prev_m;
1382
+ if (only_rings) {
1383
+ v_cursor += m_tip;
1384
+ continue;
1385
+ }
1305
1386
  const apex_height = skeleton.branch_dead[b] ? 0.0 : SHOOT_BUD_HEIGHT;
1306
1387
  const cap_ext = tip_r * apex_height;
1307
1388
  const cap_arc_s = tip_arc_s + cap_ext;
@@ -78,5 +78,6 @@ export class ShootLightField {
78
78
  * @returns {number}
79
79
  */
80
80
  irradiance_at(x: number, y: number, z: number): number;
81
+ #private;
81
82
  }
82
83
  //# sourceMappingURL=ShootLightField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShootLightField.d.ts","sourceRoot":"","sources":["../../../../../src/generation/foliage/skeleton/ShootLightField.js"],"names":[],"mappings":"AA+DA;;;;;;GAMG;AACH;IACI;;;;OAIG;IACH,iBAJW,UAAU,MAAM,CAAC,OACjB,UAAU,MAAM,CAAC,QACjB,MAAM,EAqBhB;IAjBG,aAAgB;IAChB,kBAAiC;IACjC,WAA0D;IAC1D,WAA0D;IAC1D,WAA0D;IAE1D;;;OAGG;IACH,WAFU,YAAY,CAEc;IACpC;;;OAGG;IACH,YAFU,YAAY,CAEuB;IAC7C,4BAAwC;IAG5C;;OAEG;IACH,cAEC;IAED;;;;;;OAMG;IACH,SALW,MAAM,KACN,MAAM,KACN,MAAM,GACJ,MAAM,CAQlB;IAED;;;;;;OAMG;IACH,WALW,MAAM,KACN,MAAM,KACN,MAAM,QACN,MAAM,QAKhB;IAED;;;;;OAKG;IACH,mBAJW,UAAU,MAAM,CAAC,KACjB,UAAU,MAAM,CAAC,QACjB,MAAM,QAUhB;IAED;;;;OAIG;IACH,gBAFW,MAAM,QAiChB;IAED;;;;;;;OAOG;IACH,iBALW,YAAY,GAAC,YAAY,GAAC,MAAM,EAAE,KAClC,MAAM,KACN,MAAM,KACN,MAAM,QAiBhB;IAED;;;;;;OAMG;IACH,iBALW,MAAM,KACN,MAAM,KACN,MAAM,GACJ,MAAM,CAKlB;CACJ"}
1
+ {"version":3,"file":"ShootLightField.d.ts","sourceRoot":"","sources":["../../../../../src/generation/foliage/skeleton/ShootLightField.js"],"names":[],"mappings":"AAiEA;;;;;;GAMG;AACH;IAQI;;;;OAIG;IACH,iBAJW,UAAU,MAAM,CAAC,OACjB,UAAU,MAAM,CAAC,QACjB,MAAM,EAqBhB;IAjBG,aAAgB;IAChB,kBAAiC;IACjC,WAA0D;IAC1D,WAA0D;IAC1D,WAA0D;IAE1D;;;OAGG;IACH,WAFU,YAAY,CAEc;IACpC;;;OAGG;IACH,YAFU,YAAY,CAEuB;IAC7C,4BAAwC;IAG5C;;OAEG;IACH,cAeC;IAED;;;;;;OAMG;IACH,SALW,MAAM,KACN,MAAM,KACN,MAAM,GACJ,MAAM,CAQlB;IAED;;;;;;OAMG;IACH,WALW,MAAM,KACN,MAAM,KACN,MAAM,QACN,MAAM,QAgBhB;IAED;;;;;OAKG;IACH,mBAJW,UAAU,MAAM,CAAC,KACjB,UAAU,MAAM,CAAC,QACjB,MAAM,QAUhB;IAED;;;;OAIG;IACH,gBAFW,MAAM,QAuEhB;IAED;;;;;;;OAOG;IACH,iBALW,YAAY,GAAC,YAAY,GAAC,MAAM,EAAE,KAClC,MAAM,KACN,MAAM,KACN,MAAM,QAiBhB;IAED;;;;;;OAMG;IACH,iBALW,MAAM,KACN,MAAM,KACN,MAAM,GACJ,MAAM,CAKlB;;CACJ"}
@@ -61,6 +61,8 @@ const SKY_WEIGHTS = (() => {
61
61
  return w;
62
62
  })();
63
63
 
64
+ const OPEN_SKY = SKY_WEIGHTS.reduce((s, w) => s + w, 0);
65
+
64
66
  /**
65
67
  * Light inside a crown from its own leaves: leaf area deposited in a voxel grid, and the relative irradiance each
66
68
  * voxel receives from a standard overcast sky through the leaf area between it and the sky, by Beer-Lambert,
@@ -69,6 +71,13 @@ const SKY_WEIGHTS = (() => {
69
71
  * The cost of {@link solve} is the grid's cells times its 17 sky directions, whatever the leaf count.
70
72
  */
71
73
  export class ShootLightField {
74
+ #min_i = Infinity;
75
+ #max_i = -1;
76
+ #min_j = Infinity;
77
+ #max_j = -1;
78
+ #min_k = Infinity;
79
+ #max_k = -1;
80
+
72
81
  /**
73
82
  * @param {ArrayLike<number>} min world corner of the grid
74
83
  * @param {ArrayLike<number>} max opposite corner
@@ -99,7 +108,20 @@ export class ShootLightField {
99
108
  * Removes all leaf area.
100
109
  */
101
110
  clear() {
102
- this.leaf_area.fill(0);
111
+ if (this.#max_j < 0) return;
112
+ const { nx, nz, leaf_area } = this;
113
+ for (let j = this.#min_j; j <= this.#max_j; j++) {
114
+ for (let k = this.#min_k; k <= this.#max_k; k++) {
115
+ const row = (j * nz + k) * nx;
116
+ leaf_area.fill(0, row + this.#min_i, row + this.#max_i + 1);
117
+ }
118
+ }
119
+ this.#min_i = Infinity;
120
+ this.#max_i = -1;
121
+ this.#min_j = Infinity;
122
+ this.#max_j = -1;
123
+ this.#min_k = Infinity;
124
+ this.#max_k = -1;
103
125
  }
104
126
 
105
127
  /**
@@ -125,8 +147,19 @@ export class ShootLightField {
125
147
  * @param {number} area
126
148
  */
127
149
  deposit(x, y, z, area) {
128
- const v = this.voxel(x, y, z);
129
- if (v >= 0) this.leaf_area[v] += area;
150
+ const i = Math.floor((x - this.min[0]) / this.cell);
151
+ const j = Math.floor((y - this.min[1]) / this.cell);
152
+ const k = Math.floor((z - this.min[2]) / this.cell);
153
+ if (i < 0 || j < 0 || k < 0 || i >= this.nx || j >= this.ny || k >= this.nz) return;
154
+ this.leaf_area[(j * this.nz + k) * this.nx + i] += area;
155
+ if (area > 0) {
156
+ if (i < this.#min_i) this.#min_i = i;
157
+ if (i > this.#max_i) this.#max_i = i;
158
+ if (j < this.#min_j) this.#min_j = j;
159
+ if (j > this.#max_j) this.#max_j = j;
160
+ if (k < this.#min_k) this.#min_k = k;
161
+ if (k > this.#max_k) this.#max_k = k;
162
+ }
130
163
  }
131
164
 
132
165
  /**
@@ -151,33 +184,71 @@ export class ShootLightField {
151
184
  * @param {number} clumping
152
185
  */
153
186
  solve(clumping) {
154
- const { nx, ny, nz, cell } = this;
155
- const k_ext = SPHERICAL_LEAF_PROJECTION * clamp(clumping, 0, 1);
156
- const volume = cell * cell * cell;
187
+ const { nx, nz, cell } = this;
157
188
  const irradiance = this.irradiance;
158
189
  const transmittance = this.transmittance;
159
190
  const leaf_area = this.leaf_area;
160
- irradiance.fill(0);
191
+ const max_j = this.#max_j;
192
+ irradiance.fill(OPEN_SKY);
193
+ if (max_j < 0) return;
194
+
195
+ const min_i = this.#min_i, max_i = this.#max_i;
196
+ const min_k = this.#min_k, max_k = this.#max_k;
197
+ for (let j = max_j; j >= 0; j--) {
198
+ const fall = max_j - j;
199
+ const k0 = Math.max(0, min_k - 2 * fall), k1 = Math.min(nz - 1, max_k + 2 * fall);
200
+ const i0 = Math.max(0, min_i - 2 * fall), i1 = Math.min(nx - 1, max_i + 2 * fall);
201
+ for (let k = k0; k <= k1; k++) {
202
+ const row = (j * nz + k) * nx;
203
+ irradiance.fill(0, row + i0, row + i1 + 1);
204
+ }
205
+ }
206
+
207
+ const k_ext = SPHERICAL_LEAF_PROJECTION * clamp(clumping, 0, 1);
208
+ const volume = cell * cell * cell;
161
209
  for (let d = 0; d < SKY_DIRECTIONS.length; d++) {
162
210
  const [dx, dy, dz] = SKY_DIRECTIONS[d];
163
211
  const step = Math.hypot(dx, dy, dz) * cell;
164
212
  const w = SKY_WEIGHTS[d];
165
213
  // a voxel sees the sky through the voxel one step towards it, which was swept the row before
166
- for (let j = ny - 1; j >= 0; j--) {
214
+ for (let j = max_j; j >= 0; j--) {
215
+ const fall = max_j - j;
216
+ const k0 = Math.max(0, min_k - 2 * fall), k1 = Math.min(nz - 1, max_k + 2 * fall);
217
+ const i0 = Math.max(0, min_i - 2 * fall), i1 = Math.min(nx - 1, max_i + 2 * fall);
218
+ const dk0 = dz < 0 ? min_k : Math.max(0, min_k - dz * fall);
219
+ const dk1 = dz > 0 ? max_k : Math.min(nz - 1, max_k - dz * fall);
220
+ const di0 = dx < 0 ? min_i : Math.max(0, min_i - dx * fall);
221
+ const di1 = dx > 0 ? max_i : Math.min(nx - 1, max_i - dx * fall);
222
+ const up_fall = fall - 1;
223
+ const uk0 = up_fall >= 0 ? (dz < 0 ? min_k : Math.max(0, min_k - dz * up_fall)) : nz;
224
+ const uk1 = up_fall >= 0 ? (dz > 0 ? max_k : Math.min(nz - 1, max_k - dz * up_fall)) : -1;
225
+ const ui0 = up_fall >= 0 ? (dx < 0 ? min_i : Math.max(0, min_i - dx * up_fall)) : nx;
226
+ const ui1 = up_fall >= 0 ? (dx > 0 ? max_i : Math.min(nx - 1, max_i - dx * up_fall)) : -1;
167
227
  const j1 = j + dy;
168
- for (let k = 0; k < nz; k++) {
169
- const k1 = k + dz;
170
- for (let i = 0; i < nx; i++) {
171
- const i1 = i + dx;
172
- const v = (j * nz + k) * nx + i;
228
+ for (let k = k0; k <= k1; k++) {
229
+ const row = (j * nz + k) * nx;
230
+ if (k < dk0 || k > dk1) {
231
+ for (let i = i0; i <= i1; i++) irradiance[row + i] += w;
232
+ continue;
233
+ }
234
+ for (let i = i0; i < di0; i++) irradiance[row + i] += w;
235
+ const k1_idx = k + dz;
236
+ const k_in = k1_idx >= uk0 && k1_idx <= uk1;
237
+ const u_row = (j1 * nz + k1_idx) * nx;
238
+ for (let i = di0; i <= di1; i++) {
239
+ const i1_idx = i + dx;
240
+ const v = row + i;
173
241
  let t = 1.0;
174
- if (j1 < ny && k1 >= 0 && k1 < nz && i1 >= 0 && i1 < nx) {
175
- const u = (j1 * nz + k1) * nx + i1;
176
- t = transmittance[u] * Math.exp(-k_ext * (leaf_area[u] / volume) * step);
242
+ if (k_in && i1_idx >= ui0 && i1_idx <= ui1) {
243
+ const u = u_row + i1_idx;
244
+ const lu = leaf_area[u];
245
+ t = lu > 0 ? transmittance[u] * Math.exp(-k_ext * (lu / volume) * step) : transmittance[u];
177
246
  }
178
247
  transmittance[v] = t;
179
- irradiance[v] += w * t * Math.exp(-k_ext * (leaf_area[v] / volume) * step * 0.5);
248
+ const lv = leaf_area[v];
249
+ irradiance[v] += lv > 0 ? w * t * Math.exp(-k_ext * (lv / volume) * step * 0.5) : w * t;
180
250
  }
251
+ for (let i = di1 + 1; i <= i1; i++) irradiance[row + i] += w;
181
252
  }
182
253
  }
183
254
  }
@@ -3,7 +3,7 @@
3
3
  * wood, and thinner wood that thicker wood has grown into can be found. A unit is added once, when it has grown, under
4
4
  * the cells its axis line crosses; a query reads every unit's place and radius from its axis as they are then, and
5
5
  * reaches as far round as the thickest wood ({@link max_radius}), so wood that thickens needs no re-adding. The units
6
- * of an axis that has died are passed over.
6
+ * of an axis that has died are passed over, and leave the hash at the next {@link refresh}.
7
7
  */
8
8
  export class ShootWoodHash {
9
9
  /**
@@ -26,8 +26,10 @@ export class ShootWoodHash {
26
26
  radii: number[];
27
27
  units: number[];
28
28
  }>);
29
- /** @type {Map<number, number[]>} */
30
- cells: Map<number, number[]>;
29
+ /** @type {Map<number, number[] & {max?: number}>} */
30
+ cells: Map<number, number[] & {
31
+ max?: number;
32
+ }>;
31
33
  /**
32
34
  * Thickest living unit (m) in each cell as of the last {@link refresh}, a unit added since counting as infinitely
33
35
  * thick, so a query that wants only wood at least so thick can pass a cell by.
@@ -69,7 +71,8 @@ export class ShootWoodHash {
69
71
  add(m: number, k: number): void;
70
72
  /**
71
73
  * Records each cell's thickest living unit, after the wood has thickened, and the thickest of all
72
- * ({@link max_radius}).
74
+ * ({@link max_radius}). A dead axis does not live again, so its units leave their cells, in the order the rest
75
+ * were added, and a cell left empty leaves the hash.
73
76
  */
74
77
  refresh(): void;
75
78
  /**
@@ -85,5 +88,6 @@ export class ShootWoodHash {
85
88
  * @returns {number}
86
89
  */
87
90
  hits(a: ArrayLike<number>, b: ArrayLike<number>, radius: number, min_radius: number, skip: (arg0: number, arg1: number, arg2: number, arg3: number) => boolean): number;
91
+ #private;
88
92
  }
89
93
  //# sourceMappingURL=ShootWoodHash.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ShootWoodHash.d.ts","sourceRoot":"","sources":["../../../../../src/generation/foliage/skeleton/ShootWoodHash.js"],"names":[],"mappings":"AAwBA;;;;;;GAMG;AACH;IA8FI;;;;;;OAMG;IACH,uBALW;QAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAC,KAClC,MAAM,KACN,MAAM,GACJ,MAAM,CAMlB;IApED;;OAEG;IACH,kBAFW,MAAM;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAC,CAAC,EAIrF;IAzCD,oCAAoC;IACpC,OADW,IAAI,MAAM,EAAE,MAAM,EAAE,CAAC,CACd;IAElB;;;;OAIG;IACH,UAFU,IAAI,MAAM,EAAE,MAAM,CAAC,CAER;IAErB;;;OAGG;IACH,YAFU,MAAM,CAED;IAEf;;;OAGG;IACH,WAFU,MAAM,EAAE,CAEH;IAEf;;;OAGG;IACH,YAFU,MAAM,EAAE,CAEF;IAEhB;;;OAGG;IACH,MAFU,MAAM,EAAE,CAER;IAEV,cAAU;IAMN;eAHqB,OAAO;gBAAU,MAAM,EAAE;eAAS,MAAM,EAAE;eAAS,MAAM,EAAE;QAGhE;IAGpB;;;;OAIG;IACH,OAHW,MAAM,KACN,MAAM,QAsBhB;IAED;;;OAGG;IACH,gBAiBC;IAeD;;;;;;;;;;;OAWG;IACH,QAPW,UAAU,MAAM,CAAC,KACjB,UAAU,MAAM,CAAC,UACjB,MAAM,cACN,MAAM,eACG,MAAM,QAAE,MAAM,QAAE,MAAM,QAAE,MAAM,KAAG,OAAO,GAC/C,MAAM,CA6ClB;CACJ"}
1
+ {"version":3,"file":"ShootWoodHash.d.ts","sourceRoot":"","sources":["../../../../../src/generation/foliage/skeleton/ShootWoodHash.js"],"names":[],"mappings":"AAiCA;;;;;;GAMG;AACH;IA0LI;;;;;;OAMG;IACH,uBALW;QAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAC,KAClC,MAAM,KACN,MAAM,GACJ,MAAM,CAMlB;IAvHD;;OAEG;IACH,kBAFW,MAAM;QAAC,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAC,CAAC,EAIrF;IAlFD,qDAAqD;IACrD,OADW,IAAI,MAAM,EAAE,MAAM,EAAE,GAAG;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAC/B;IAElB;;;;OAIG;IACH,UAFU,IAAI,MAAM,EAAE,MAAM,CAAC,CAER;IAErB;;;OAGG;IACH,YAFU,MAAM,CAED;IAEf;;;OAGG;IACH,WAFU,MAAM,EAAE,CAEH;IAEf;;;OAGG;IACH,YAFU,MAAM,EAAE,CAEF;IAEhB;;;OAGG;IACH,MAFU,MAAM,EAAE,CAER;IAEV,cAAU;IA+CN;eAHqB,OAAO;gBAAU,MAAM,EAAE;eAAS,MAAM,EAAE;eAAS,MAAM,EAAE;QAGhE;IAGpB;;;;OAIG;IACH,OAHW,MAAM,KACN,MAAM,QA8ChB;IAED;;;;OAIG;IACH,gBA2CC;IAeD;;;;;;;;;;;OAWG;IACH,QAPW,UAAU,MAAM,CAAC,KACjB,UAAU,MAAM,CAAC,UACjB,MAAM,cACN,MAAM,eACG,MAAM,QAAE,MAAM,QAAE,MAAM,QAAE,MAAM,KAAG,OAAO,GAC/C,MAAM,CAqIlB;;CACJ"}