@woosh/meep-engine 3.53.0 → 3.54.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 (194) hide show
  1. package/package.json +1 -1
  2. package/src/core/math/noise/worley_noise_2d.d.ts +14 -0
  3. package/src/core/math/noise/worley_noise_2d.d.ts.map +1 -0
  4. package/src/core/math/noise/worley_noise_2d.js +74 -0
  5. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.d.ts +6 -1
  6. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.d.ts.map +1 -1
  7. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.js +363 -325
  8. package/src/engine/graphics/texture/atlas/ManagedTextureAtlas.d.ts +93 -14
  9. package/src/engine/graphics/texture/atlas/ManagedTextureAtlas.d.ts.map +1 -1
  10. package/src/engine/graphics/texture/atlas/ManagedTextureAtlas.js +322 -183
  11. package/src/engine/graphics/texture/atlas/ReferencedTextureAtlas.d.ts +9 -0
  12. package/src/engine/graphics/texture/atlas/ReferencedTextureAtlas.d.ts.map +1 -1
  13. package/src/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +110 -83
  14. package/src/engine/graphics/texture/atlas/SpriteAtlas.d.ts +42 -0
  15. package/src/engine/graphics/texture/atlas/SpriteAtlas.d.ts.map +1 -0
  16. package/src/engine/{graphics3/decal/DecalAtlas.js → graphics/texture/atlas/SpriteAtlas.js} +202 -194
  17. package/src/engine/graphics/texture/atlas/SpriteAtlasFill.d.ts +12 -0
  18. package/src/engine/graphics/texture/atlas/SpriteAtlasFill.d.ts.map +1 -0
  19. package/src/engine/graphics/texture/atlas/SpriteAtlasFill.js +30 -0
  20. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts +7 -0
  21. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
  22. package/src/engine/graphics/texture/atlas/TextureAtlas.js +11 -1
  23. package/src/engine/graphics/texture/atlas/sprite_atlas_border.d.ts +52 -0
  24. package/src/engine/graphics/texture/atlas/sprite_atlas_border.d.ts.map +1 -0
  25. package/src/engine/{graphics3/decal/decal_atlas_border.js → graphics/texture/atlas/sprite_atlas_border.js} +69 -69
  26. package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
  27. package/src/engine/graphics3/DecalSystem.js +447 -450
  28. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +9 -22
  29. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  30. package/src/engine/graphics3/GPUParticleEmitterSystem.js +892 -951
  31. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  32. package/src/generation/foliage/FOLIAGE_DESIGN.md +2 -1
  33. package/src/generation/foliage/config/BarkMaterialConfig.d.ts +4 -3
  34. package/src/generation/foliage/config/BarkMaterialConfig.d.ts.map +1 -1
  35. package/src/generation/foliage/config/BarkMaterialConfig.js +6 -6
  36. package/src/generation/foliage/config/FoliageBarkType.d.ts +11 -0
  37. package/src/generation/foliage/config/FoliageBarkType.d.ts.map +1 -0
  38. package/src/generation/foliage/config/FoliageBarkType.js +16 -0
  39. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts +58 -5
  40. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts.map +1 -1
  41. package/src/generation/foliage/config/FoliageMaterialConfig.js +279 -181
  42. package/src/generation/foliage/index.d.ts +2 -1
  43. package/src/generation/foliage/index.js +44 -43
  44. package/src/generation/foliage/material/make_bark_material.d.ts.map +1 -1
  45. package/src/generation/foliage/material/make_bark_material.js +23 -95
  46. package/src/generation/foliage/material/make_foliage_material.d.ts +29 -3
  47. package/src/generation/foliage/material/make_foliage_material.d.ts.map +1 -1
  48. package/src/generation/foliage/material/make_foliage_material.js +605 -359
  49. package/src/generation/foliage/mesh/branch_collar_project.d.ts.map +1 -1
  50. package/src/generation/foliage/mesh/branch_collar_project.js +205 -203
  51. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts +15 -0
  52. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts.map +1 -1
  53. package/src/generation/foliage/mesh/foliage_collect_attachments.js +569 -502
  54. package/src/generation/foliage/mesh/tree_mesh_build_bark.d.ts.map +1 -1
  55. package/src/generation/foliage/mesh/tree_mesh_build_bark.js +1049 -1047
  56. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts +2 -1
  57. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts.map +1 -1
  58. package/src/generation/foliage/mesh/tree_mesh_build_foliage.js +454 -415
  59. package/src/generation/foliage/presets/make_tree_species_preset.d.ts.map +1 -1
  60. package/src/generation/foliage/presets/make_tree_species_preset.js +870 -837
  61. package/src/generation/foliage/skeleton/crown_envelope_evaluate.js +121 -121
  62. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.d.ts +10 -7
  63. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.d.ts.map +1 -1
  64. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.js +206 -179
  65. package/src/generation/foliage/skeleton/tree_skeleton_grow.d.ts.map +1 -1
  66. package/src/generation/foliage/skeleton/tree_skeleton_grow.js +1871 -1104
  67. package/src/shade/playground/foliage_wind/foliage_world.d.ts +6 -0
  68. package/src/shade/playground/foliage_wind/foliage_world.d.ts.map +1 -1
  69. package/src/shade/playground/foliage_wind/foliage_world.js +602 -593
  70. package/src/shade/playground/foliage_wind/index.html +260 -254
  71. package/src/shade/playground/foliage_wind/main.js +275 -263
  72. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  73. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  74. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  75. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  76. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  77. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +1 -1
  78. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +3 -3
  79. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  80. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +350 -294
  81. package/src/shade/renderer/hiz/generate_downsampler_code.d.ts +0 -2
  82. package/src/shade/renderer/hiz/generate_downsampler_code.d.ts.map +1 -1
  83. package/src/shade/renderer/hiz/generate_downsampler_code.js +99 -100
  84. package/src/shade/renderer/hiz/hzb_pick_alignment.d.ts +15 -0
  85. package/src/shade/renderer/hiz/hzb_pick_alignment.d.ts.map +1 -0
  86. package/src/shade/renderer/hiz/hzb_pick_alignment.js +148 -0
  87. package/src/shade/renderer/hiz/hzb_plan_reduction_steps.d.ts +28 -0
  88. package/src/shade/renderer/hiz/hzb_plan_reduction_steps.d.ts.map +1 -0
  89. package/src/shade/renderer/hiz/hzb_plan_reduction_steps.js +58 -0
  90. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +5 -5
  91. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  92. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +57 -67
  93. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +3 -3
  94. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +1 -1
  95. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.js +111 -112
  96. package/src/shade/renderer/hiz/shader_hzb_spd.d.ts +5 -0
  97. package/src/shade/renderer/hiz/shader_hzb_spd.d.ts.map +1 -0
  98. package/src/shade/renderer/hiz/shader_hzb_spd.js +160 -0
  99. package/src/shade/renderer/light/GPULightCollection.d.ts +8 -0
  100. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  101. package/src/shade/renderer/light/GPULightCollection.js +67 -0
  102. package/src/shade/renderer/light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.d.ts +7 -0
  103. package/src/shade/renderer/light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.d.ts.map +1 -0
  104. package/src/shade/renderer/light/environment/FAR_FIELD_IRRADIANCE_SH_RESOURCE.js +13 -0
  105. package/src/shade/renderer/light/environment/shader_project_far_field_irradiance_sh.d.ts +10 -0
  106. package/src/shade/renderer/light/environment/shader_project_far_field_irradiance_sh.d.ts.map +1 -0
  107. package/src/shade/renderer/light/environment/shader_project_far_field_irradiance_sh.js +72 -0
  108. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.d.ts +39 -0
  109. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.d.ts.map +1 -0
  110. package/src/shade/renderer/light/world_cluster/WORLD_LIGHT_CLUSTER.js +90 -0
  111. package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.d.ts +12 -0
  112. package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.d.ts.map +1 -0
  113. package/src/shade/renderer/light/world_cluster/chunk_world_light_cluster_lookup.js +56 -0
  114. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts +31 -0
  115. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.d.ts.map +1 -0
  116. package/src/shade/renderer/light/world_cluster/graph_build_world_light_clusters.js +196 -0
  117. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.d.ts +10 -0
  118. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.d.ts.map +1 -0
  119. package/src/shade/renderer/light/world_cluster/shader_world_cluster_assign_lights.js +163 -0
  120. package/src/shade/renderer/light/world_cluster/shader_world_cluster_cull_lights.d.ts +4 -0
  121. package/src/shade/renderer/light/world_cluster/shader_world_cluster_cull_lights.d.ts.map +1 -0
  122. package/src/shade/renderer/light/world_cluster/shader_world_cluster_cull_lights.js +137 -0
  123. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  124. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  125. package/src/shade/renderer/particles/GPUParticleSystem.js +4 -3
  126. package/src/shade/renderer/particles/graph_particles_avboit.d.ts +4 -3
  127. package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
  128. package/src/shade/renderer/particles/graph_particles_avboit.js +28 -11
  129. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts +9 -13
  130. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts.map +1 -1
  131. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.js +25 -20
  132. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
  133. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +21 -38
  134. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  135. package/src/shade/renderer/shader/chunk/depth/chunk_query_depth_from_screen_space_bb.d.ts.map +1 -1
  136. package/src/shade/renderer/shader/chunk/depth/chunk_query_depth_from_screen_space_bb.js +62 -62
  137. package/src/shade/renderer/shader/chunk/random/lowbias/chunk_hash_lowbias32_vec2u.d.ts +6 -0
  138. package/src/shade/renderer/shader/chunk/random/lowbias/chunk_hash_lowbias32_vec2u.d.ts.map +1 -0
  139. package/src/shade/renderer/shader/chunk/random/lowbias/chunk_hash_lowbias32_vec2u.js +19 -0
  140. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_get_mip_resolution.d.ts.map +1 -1
  141. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_get_mip_resolution.js +16 -10
  142. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_hierarchical_raymarch.d.ts.map +1 -1
  143. package/src/shade/renderer/shader/chunk/screen_space/chunk_ffx_sssr_hierarchical_raymarch.js +118 -117
  144. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts +41 -7
  145. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts.map +1 -1
  146. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.js +162 -58
  147. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.js +1 -1
  148. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +10 -12
  149. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  150. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +583 -606
  151. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_sample.d.ts +24 -0
  152. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_sample.d.ts.map +1 -0
  153. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.d.ts.map +1 -1
  154. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +1 -1
  155. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +640 -640
  156. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts +113 -0
  157. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.d.ts.map +1 -0
  158. package/src/shade/renderer/volumetrics/irradiance_clipmap/GPUIrradianceClipmap.js +484 -0
  159. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.d.ts +50 -0
  160. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.d.ts.map +1 -0
  161. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP.js +60 -0
  162. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.d.ts +25 -0
  163. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.d.ts.map +1 -0
  164. package/src/shade/renderer/volumetrics/irradiance_clipmap/IRRADIANCE_CLIPMAP_STRUCT.js +47 -0
  165. package/src/shade/renderer/volumetrics/irradiance_clipmap/IrradianceCascadePlacement.d.ts +74 -0
  166. package/src/shade/renderer/volumetrics/irradiance_clipmap/IrradianceCascadePlacement.d.ts.map +1 -0
  167. package/src/shade/renderer/volumetrics/irradiance_clipmap/IrradianceCascadePlacement.js +173 -0
  168. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts +26 -0
  169. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.d.ts.map +1 -0
  170. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_gi_sample.js +83 -0
  171. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.d.ts +31 -0
  172. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.d.ts.map +1 -0
  173. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_local_light.js +113 -0
  174. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.d.ts +121 -0
  175. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.d.ts.map +1 -0
  176. package/src/shade/renderer/volumetrics/irradiance_clipmap/chunk_irradiance_clipmap_sample.js +356 -0
  177. package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.d.ts +16 -0
  178. package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.d.ts.map +1 -0
  179. package/src/shade/renderer/volumetrics/irradiance_clipmap/shader_irradiance_clipmap_build.js +191 -0
  180. package/src/shade/renderer/volumetrics/shader_volumetrics_classify_froxels.d.ts +16 -0
  181. package/src/shade/renderer/volumetrics/shader_volumetrics_classify_froxels.d.ts.map +1 -0
  182. package/src/shade/renderer/volumetrics/shader_volumetrics_lighting_regimes.d.ts +23 -0
  183. package/src/shade/renderer/volumetrics/shader_volumetrics_lighting_regimes.d.ts.map +1 -0
  184. package/src/shade/renderer/wind/GPUSceneWind.d.ts +1 -1
  185. package/src/shade/renderer/wind/GPUSceneWind.js +1 -1
  186. package/src/engine/graphics3/decal/DecalAtlasFill.js +0 -32
  187. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_froxel_centre_ws.js +0 -49
  188. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.js +0 -50
  189. package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi_sample.js +0 -105
  190. package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.js +0 -80
  191. package/src/shade/renderer/volumetrics/gi/shader_volumetrics_gi.js +0 -51
  192. package/src/shade/renderer/volumetrics/gi/volumetrics_gi_resources.js +0 -39
  193. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.js +0 -121
  194. package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.js +0 -237
@@ -1,263 +1,275 @@
1
- import Vector3 from "../../../core/geom/Vector3.js";
2
- import { DEG_TO_RAD } from "../../../core/math/DEG_TO_RAD.js";
3
- import { GraphicsEngine } from "../../../engine/graphics3/GraphicsEngine.js";
4
- import { OrbitalCameraController } from "../../renderer/camera/OrbitalCameraController.js";
5
- import { create_frame_loop } from "../../renderer/create_frame_loop.js";
6
- import { Scene } from "../../renderer/scene/Scene.js";
7
- import { FrameRateMeter } from "../frame_rate_meter.js";
8
- import { createFoliageWindWorld, FOLIAGE_PLAYGROUND_PRESETS } from "./foliage_world.js";
9
-
10
- const MAX_SIMULATION_STEP = 0.05;
11
-
12
- const element = (id) => document.getElementById(id);
13
- const view_size = () => [Math.max(1, window.innerWidth), Math.max(1, window.innerHeight)];
14
-
15
- function show_message(text) {
16
- const node = element("message");
17
- if (node !== null) {
18
- node.textContent = text;
19
- }
20
- }
21
-
22
- async function main() {
23
- const graphics = new GraphicsEngine();
24
- const scene = new Scene();
25
-
26
- element("viewport").appendChild(graphics.viewport.el);
27
-
28
- const [initial_width, initial_height] = view_size();
29
- graphics.viewport.size.set(initial_width, initial_height);
30
-
31
- // --- Create the ECS Foliage & Wind World ---
32
- const world = await createFoliageWindWorld(graphics, scene, {
33
- mode: "specimen",
34
- preset: "oak",
35
- seed: 1337,
36
- lod: 0,
37
- windSpeed: 6.5,
38
- headingDeg: 25,
39
- gustStrength: 4.5,
40
- gustPeriod: 5.5,
41
- windWeight: 1.0,
42
- });
43
-
44
- // --- Camera & OrbitalCameraController ---
45
- const camera = graphics.camera.camera;
46
- camera.near = 0.03;
47
- camera.far = 300;
48
- camera.fov = 50 * DEG_TO_RAD;
49
-
50
- const controller = new OrbitalCameraController(camera, document.body);
51
- controller.look(new Vector3(11.0, 7.5, 14.5), new Vector3(0, 5.2, 0));
52
- controller.movement_speed_scale = 3.0;
53
- document.body.focus();
54
-
55
- function frame_camera_for_selection() {
56
- if (world.state.mode === "grove") {
57
- controller.look(new Vector3(22.0, 12.0, 26.0), new Vector3(0, 4.5, 0));
58
- } else {
59
- const is_small = world.state.preset === "shrub" || world.state.preset === "fern";
60
- if (is_small) {
61
- controller.look(new Vector3(3.5, 2.2, 4.2), new Vector3(0, 1.0, 0));
62
- } else {
63
- controller.look(new Vector3(11.0, 7.5, 14.5), new Vector3(0, 5.2, 0));
64
- }
65
- }
66
- }
67
-
68
- // --- Wire Interactive HUD Controls ---
69
- const preset_container = element("preset-buttons");
70
- const mode_container = element("mode-buttons");
71
- const lod_container = element("lod-buttons");
72
- const seed_input = /** @type {HTMLInputElement} */ (element("seed-input"));
73
- const seed_random_btn = element("seed-random-btn");
74
-
75
- function format_preset_label(preset) {
76
- return preset.charAt(0).toUpperCase() + preset.slice(1);
77
- }
78
-
79
- for (const preset of FOLIAGE_PLAYGROUND_PRESETS) {
80
- const btn = document.createElement("button");
81
- btn.textContent = format_preset_label(preset);
82
- btn.dataset.preset = preset;
83
- if (preset === world.state.preset) {
84
- btn.classList.add("active");
85
- }
86
- btn.addEventListener("click", () => {
87
- for (const child of preset_container.querySelectorAll("button")) {
88
- child.classList.toggle("active", child === btn);
89
- }
90
- world.rebuildTrees({ preset });
91
- if (world.state.mode === "specimen") {
92
- frame_camera_for_selection();
93
- }
94
- });
95
- preset_container.appendChild(btn);
96
- }
97
-
98
- for (const btn of mode_container.querySelectorAll("button")) {
99
- btn.addEventListener("click", () => {
100
- const mode = /** @type {"specimen"|"grove"} */ (btn.dataset.mode);
101
- for (const b of mode_container.querySelectorAll("button")) {
102
- b.classList.toggle("active", b === btn);
103
- }
104
- world.rebuildTrees({ mode });
105
- frame_camera_for_selection();
106
- });
107
- }
108
-
109
- for (const btn of lod_container.querySelectorAll("button")) {
110
- btn.addEventListener("click", () => {
111
- const lod = Number(btn.dataset.lod) | 0;
112
- for (const b of lod_container.querySelectorAll("button")) {
113
- b.classList.toggle("active", b === btn);
114
- }
115
- world.rebuildTrees({ lod });
116
- });
117
- }
118
-
119
- seed_input.addEventListener("change", () => {
120
- const seed = Number(seed_input.value) | 0;
121
- world.rebuildTrees({ seed });
122
- });
123
-
124
- seed_random_btn.addEventListener("click", () => {
125
- const seed = Math.floor(Math.random() * 99999) + 1;
126
- seed_input.value = String(seed);
127
- world.rebuildTrees({ seed });
128
- });
129
-
130
- function bind_slider(slider_id, val_id, unit_suffix, on_change) {
131
- const slider = /** @type {HTMLInputElement} */ (element(slider_id));
132
- const label = element(val_id);
133
- slider.addEventListener("input", () => {
134
- const v = Number(slider.value);
135
- label.textContent = `${v}${unit_suffix}`;
136
- on_change(v);
137
- });
138
- }
139
-
140
- bind_slider("slider-wind-speed", "val-wind-speed", " m/s", (windSpeed) => {
141
- world.setWind({ windSpeed });
142
- });
143
-
144
- bind_slider("slider-gust-strength", "val-gust-strength", " m/s", (gustStrength) => {
145
- world.setWind({ gustStrength });
146
- });
147
-
148
- bind_slider("slider-gust-period", "val-gust-period", " s", (gustPeriod) => {
149
- world.setWind({ gustPeriod });
150
- });
151
-
152
- bind_slider("slider-wind-heading", "val-wind-heading", "°", (headingDeg) => {
153
- world.setWind({ headingDeg });
154
- });
155
-
156
- bind_slider("slider-wind-weight", "val-wind-weight", "", (windWeight) => {
157
- element("val-wind-weight").textContent = windWeight.toFixed(2);
158
- world.setWind({ windWeight });
159
- });
160
-
161
- element("btn-trigger-gust").addEventListener("click", () => {
162
- world.triggerLocalGust();
163
- });
164
-
165
- Object.assign(globalThis, {
166
- graphics,
167
- scene,
168
- world,
169
- camera,
170
- controller,
171
- });
172
-
173
- // --- Start WebGPU Device ---
174
- graphics.on.contextFailed.add((failure) =>
175
- show_message(
176
- "There will be no rendering.\n\n" +
177
- failure.message +
178
- "\n\nThe page needs WebGPU with subgroups and immediate data, over http://localhost."
179
- )
180
- );
181
-
182
- try {
183
- await graphics.start();
184
- } catch (e) {
185
- console.error(e);
186
- return;
187
- }
188
-
189
- const renderer = graphics.renderer;
190
- globalThis.renderer = renderer;
191
-
192
- renderer.feature_automatic_exposure_enabled = false;
193
- renderer.exposure_compensation = 0;
194
-
195
- /** @type {(() => void)|null} */
196
- let stop_loop = null;
197
-
198
- renderer.graphics.device.lost?.then((info) => {
199
- stop_loop?.();
200
- show_message("The GPU device was lost.\n\n" + (info?.message ?? ""));
201
- });
202
-
203
- // --- Main Simulation & Render Loop ---
204
- const el_fps = element("stat-fps");
205
- const el_trees = element("stat-trees");
206
- const el_rigs = element("stat-rigs");
207
- const el_joints = element("stat-joints");
208
- const el_tris = element("stat-tris");
209
- const el_gust = element("stat-gust");
210
-
211
- const frame_rate = new FrameRateMeter();
212
- let frame_index = 0;
213
-
214
- function frame(raw_dt) {
215
- frame_rate.add(raw_dt);
216
- const dt = Math.min(raw_dt, MAX_SIMULATION_STEP);
217
-
218
- const [width, height] = view_size();
219
- if (width !== graphics.viewport.size.x || height !== graphics.viewport.size.y) {
220
- graphics.viewport.size.set(width, height);
221
- }
222
-
223
- controller.update();
224
- world.step(dt);
225
- graphics.render(dt);
226
- frame_index++;
227
-
228
- const stats = world.getStats();
229
- el_fps.textContent = frame_rate.fps.toFixed(0);
230
- el_trees.textContent = String(stats.treeCount);
231
- el_rigs.textContent = String(stats.activeRigCount);
232
- el_joints.textContent = stats.totalJoints.toLocaleString();
233
- el_tris.textContent = stats.totalTriangles.toLocaleString();
234
- el_gust.textContent = stats.gustActive ? "Active (Sweeping)" : "Idle";
235
- }
236
-
237
- function guarded_frame(dt) {
238
- try {
239
- frame(dt);
240
- } catch (e) {
241
- stop_loop?.();
242
- console.error(e);
243
- show_message("The frame failed and the loop has stopped.\n\n" + e.message);
244
- }
245
- }
246
-
247
- if (new URLSearchParams(location.search).has("debug")) {
248
- globalThis.__step = (n = 1, dt = 1 / 60) => {
249
- for (let i = 0; i < n; i++) {
250
- guarded_frame(dt);
251
- }
252
- return frame_index;
253
- };
254
- return;
255
- }
256
-
257
- stop_loop = create_frame_loop((dt) => guarded_frame(dt));
258
- }
259
-
260
- main().catch((e) => {
261
- console.error(e);
262
- show_message("The foliage playground failed to start — see the console.\n\n" + e.message);
263
- });
1
+ import Vector3 from "../../../core/geom/Vector3.js";
2
+ import { DEG_TO_RAD } from "../../../core/math/DEG_TO_RAD.js";
3
+ import { GraphicsEngine } from "../../../engine/graphics3/GraphicsEngine.js";
4
+ import { OrbitalCameraController } from "../../renderer/camera/OrbitalCameraController.js";
5
+ import { create_frame_loop } from "../../renderer/create_frame_loop.js";
6
+ import { Scene } from "../../renderer/scene/Scene.js";
7
+ import { FrameRateMeter } from "../frame_rate_meter.js";
8
+ import { createFoliageWindWorld, FOLIAGE_PLAYGROUND_PRESETS } from "./foliage_world.js";
9
+
10
+ const MAX_SIMULATION_STEP = 0.05;
11
+
12
+ const element = (id) => document.getElementById(id);
13
+ const view_size = () => [Math.max(1, window.innerWidth), Math.max(1, window.innerHeight)];
14
+
15
+ function show_message(text) {
16
+ const node = element("message");
17
+ if (node !== null) {
18
+ node.textContent = text;
19
+ }
20
+ }
21
+
22
+ async function main() {
23
+ const graphics = new GraphicsEngine();
24
+ const scene = new Scene();
25
+
26
+ element("viewport").appendChild(graphics.viewport.el);
27
+
28
+ const [initial_width, initial_height] = view_size();
29
+ graphics.viewport.size.set(initial_width, initial_height);
30
+
31
+ // --- Create the ECS Foliage & Wind World ---
32
+ const world = await createFoliageWindWorld(graphics, scene, {
33
+ mode: "specimen",
34
+ preset: "oak",
35
+ seed: 1337,
36
+ lod: 0,
37
+ showLeaves: true,
38
+ windSpeed: 6.5,
39
+ headingDeg: 25,
40
+ gustStrength: 4.5,
41
+ gustPeriod: 5.5,
42
+ windWeight: 1.0,
43
+ });
44
+
45
+ // --- Camera & OrbitalCameraController ---
46
+ const camera = graphics.camera.camera;
47
+ camera.near = 0.03;
48
+ camera.far = 300;
49
+ camera.fov = 50 * DEG_TO_RAD;
50
+
51
+ const controller = new OrbitalCameraController(camera, document.body);
52
+ controller.look(new Vector3(11.0, 7.5, 14.5), new Vector3(0, 5.2, 0));
53
+ controller.movement_speed_scale = 3.0;
54
+ document.body.focus();
55
+
56
+ function frame_camera_for_selection() {
57
+ if (world.state.mode === "grove") {
58
+ controller.look(new Vector3(22.0, 12.0, 26.0), new Vector3(0, 4.5, 0));
59
+ } else {
60
+ const is_small = world.state.preset === "shrub" || world.state.preset === "fern";
61
+ if (is_small) {
62
+ controller.look(new Vector3(3.5, 2.2, 4.2), new Vector3(0, 1.0, 0));
63
+ } else {
64
+ controller.look(new Vector3(11.0, 7.5, 14.5), new Vector3(0, 5.2, 0));
65
+ }
66
+ }
67
+ }
68
+
69
+ // --- Wire Interactive HUD Controls ---
70
+ const preset_container = element("preset-buttons");
71
+ const mode_container = element("mode-buttons");
72
+ const leaves_container = element("leaves-buttons");
73
+ const lod_container = element("lod-buttons");
74
+ const seed_input = /** @type {HTMLInputElement} */ (element("seed-input"));
75
+ const seed_random_btn = element("seed-random-btn");
76
+
77
+ function format_preset_label(preset) {
78
+ return preset.charAt(0).toUpperCase() + preset.slice(1);
79
+ }
80
+
81
+ for (const preset of FOLIAGE_PLAYGROUND_PRESETS) {
82
+ const btn = document.createElement("button");
83
+ btn.textContent = format_preset_label(preset);
84
+ btn.dataset.preset = preset;
85
+ if (preset === world.state.preset) {
86
+ btn.classList.add("active");
87
+ }
88
+ btn.addEventListener("click", () => {
89
+ for (const child of preset_container.querySelectorAll("button")) {
90
+ child.classList.toggle("active", child === btn);
91
+ }
92
+ world.rebuildTrees({ preset });
93
+ if (world.state.mode === "specimen") {
94
+ frame_camera_for_selection();
95
+ }
96
+ });
97
+ preset_container.appendChild(btn);
98
+ }
99
+
100
+ for (const btn of mode_container.querySelectorAll("button")) {
101
+ btn.addEventListener("click", () => {
102
+ const mode = /** @type {"specimen"|"grove"} */ (btn.dataset.mode);
103
+ for (const b of mode_container.querySelectorAll("button")) {
104
+ b.classList.toggle("active", b === btn);
105
+ }
106
+ world.rebuildTrees({ mode });
107
+ frame_camera_for_selection();
108
+ });
109
+ }
110
+
111
+ for (const btn of leaves_container.querySelectorAll("button")) {
112
+ btn.addEventListener("click", () => {
113
+ const showLeaves = btn.dataset.leaves === "true";
114
+ for (const b of leaves_container.querySelectorAll("button")) {
115
+ b.classList.toggle("active", b === btn);
116
+ }
117
+ world.rebuildTrees({ showLeaves });
118
+ });
119
+ }
120
+
121
+ for (const btn of lod_container.querySelectorAll("button")) {
122
+ btn.addEventListener("click", () => {
123
+ const lod = Number(btn.dataset.lod) | 0;
124
+ for (const b of lod_container.querySelectorAll("button")) {
125
+ b.classList.toggle("active", b === btn);
126
+ }
127
+ world.rebuildTrees({ lod });
128
+ });
129
+ }
130
+
131
+ seed_input.addEventListener("change", () => {
132
+ const seed = Number(seed_input.value) | 0;
133
+ world.rebuildTrees({ seed });
134
+ });
135
+
136
+ seed_random_btn.addEventListener("click", () => {
137
+ const seed = Math.floor(Math.random() * 99999) + 1;
138
+ seed_input.value = String(seed);
139
+ world.rebuildTrees({ seed });
140
+ });
141
+
142
+ function bind_slider(slider_id, val_id, unit_suffix, on_change) {
143
+ const slider = /** @type {HTMLInputElement} */ (element(slider_id));
144
+ const label = element(val_id);
145
+ slider.addEventListener("input", () => {
146
+ const v = Number(slider.value);
147
+ label.textContent = `${v}${unit_suffix}`;
148
+ on_change(v);
149
+ });
150
+ }
151
+
152
+ bind_slider("slider-wind-speed", "val-wind-speed", " m/s", (windSpeed) => {
153
+ world.setWind({ windSpeed });
154
+ });
155
+
156
+ bind_slider("slider-gust-strength", "val-gust-strength", " m/s", (gustStrength) => {
157
+ world.setWind({ gustStrength });
158
+ });
159
+
160
+ bind_slider("slider-gust-period", "val-gust-period", " s", (gustPeriod) => {
161
+ world.setWind({ gustPeriod });
162
+ });
163
+
164
+ bind_slider("slider-wind-heading", "val-wind-heading", "°", (headingDeg) => {
165
+ world.setWind({ headingDeg });
166
+ });
167
+
168
+ bind_slider("slider-wind-weight", "val-wind-weight", "", (windWeight) => {
169
+ element("val-wind-weight").textContent = windWeight.toFixed(2);
170
+ world.setWind({ windWeight });
171
+ });
172
+
173
+ element("btn-trigger-gust").addEventListener("click", () => {
174
+ world.triggerLocalGust();
175
+ });
176
+
177
+ Object.assign(globalThis, {
178
+ graphics,
179
+ scene,
180
+ world,
181
+ camera,
182
+ controller,
183
+ });
184
+
185
+ // --- Start WebGPU Device ---
186
+ graphics.on.contextFailed.add((failure) =>
187
+ show_message(
188
+ "There will be no rendering.\n\n" +
189
+ failure.message +
190
+ "\n\nThe page needs WebGPU with subgroups and immediate data, over http://localhost."
191
+ )
192
+ );
193
+
194
+ try {
195
+ await graphics.start();
196
+ } catch (e) {
197
+ console.error(e);
198
+ return;
199
+ }
200
+
201
+ const renderer = graphics.renderer;
202
+ globalThis.renderer = renderer;
203
+
204
+ renderer.feature_automatic_exposure_enabled = false;
205
+ renderer.exposure_compensation = 0;
206
+
207
+ /** @type {(() => void)|null} */
208
+ let stop_loop = null;
209
+
210
+ renderer.graphics.device.lost?.then((info) => {
211
+ stop_loop?.();
212
+ show_message("The GPU device was lost.\n\n" + (info?.message ?? ""));
213
+ });
214
+
215
+ // --- Main Simulation & Render Loop ---
216
+ const el_fps = element("stat-fps");
217
+ const el_trees = element("stat-trees");
218
+ const el_rigs = element("stat-rigs");
219
+ const el_joints = element("stat-joints");
220
+ const el_tris = element("stat-tris");
221
+ const el_gust = element("stat-gust");
222
+
223
+ const frame_rate = new FrameRateMeter();
224
+ let frame_index = 0;
225
+
226
+ function frame(raw_dt) {
227
+ frame_rate.add(raw_dt);
228
+ const dt = Math.min(raw_dt, MAX_SIMULATION_STEP);
229
+
230
+ const [width, height] = view_size();
231
+ if (width !== graphics.viewport.size.x || height !== graphics.viewport.size.y) {
232
+ graphics.viewport.size.set(width, height);
233
+ }
234
+
235
+ controller.update();
236
+ world.step(dt);
237
+ graphics.render(dt);
238
+ frame_index++;
239
+
240
+ const stats = world.getStats();
241
+ el_fps.textContent = frame_rate.fps.toFixed(0);
242
+ el_trees.textContent = String(stats.treeCount);
243
+ el_rigs.textContent = String(stats.activeRigCount);
244
+ el_joints.textContent = stats.totalJoints.toLocaleString();
245
+ el_tris.textContent = stats.totalTriangles.toLocaleString();
246
+ el_gust.textContent = stats.gustActive ? "Active (Sweeping)" : "Idle";
247
+ }
248
+
249
+ function guarded_frame(dt) {
250
+ try {
251
+ frame(dt);
252
+ } catch (e) {
253
+ stop_loop?.();
254
+ console.error(e);
255
+ show_message("The frame failed and the loop has stopped.\n\n" + e.message);
256
+ }
257
+ }
258
+
259
+ if (new URLSearchParams(location.search).has("debug")) {
260
+ globalThis.__step = (n = 1, dt = 1 / 60) => {
261
+ for (let i = 0; i < n; i++) {
262
+ guarded_frame(dt);
263
+ }
264
+ return frame_index;
265
+ };
266
+ return;
267
+ }
268
+
269
+ stop_loop = create_frame_loop((dt) => guarded_frame(dt));
270
+ }
271
+
272
+ main().catch((e) => {
273
+ console.error(e);
274
+ show_message("The foliage playground failed to start — see the console.\n\n" + e.message);
275
+ });
@@ -143,7 +143,7 @@ export class GPUFluidCascade {
143
143
  * @param {*} _resources
144
144
  * @param {{encoder: ShadeGPUCommandContext}} context
145
145
  */
146
- static "__#264@#execute_restrict"(record: object, _resources: any, context: {
146
+ static "__#266@#execute_restrict"(record: object, _resources: any, context: {
147
147
  encoder: ShadeGPUCommandContext;
148
148
  }): void;
149
149
  /**
@@ -151,7 +151,7 @@ export class GPUFluidCascade {
151
151
  * @param {*} _resources
152
152
  * @param {{encoder: ShadeGPUCommandContext}} context
153
153
  */
154
- static "__#264@#execute_fill"(record: object, _resources: any, context: {
154
+ static "__#266@#execute_fill"(record: object, _resources: any, context: {
155
155
  encoder: ShadeGPUCommandContext;
156
156
  }): void;
157
157
  /**
@@ -159,7 +159,7 @@ export class GPUFluidCascade {
159
159
  * @param {import("../../../engine/graphics/render/frame_graph/FramePassResources.js").FramePassResources} resources
160
160
  * @param {{encoder: ShadeGPUCommandContext}} context
161
161
  */
162
- static "__#264@#execute"(record: object, resources: import("../../../engine/graphics/render/frame_graph/FramePassResources.js").FramePassResources, context: {
162
+ static "__#266@#execute"(record: object, resources: import("../../../engine/graphics/render/frame_graph/FramePassResources.js").FramePassResources, context: {
163
163
  encoder: ShadeGPUCommandContext;
164
164
  }): void;
165
165
  /**
@@ -63,7 +63,7 @@ export class GPUFluidCascadeStack {
63
63
  * @param {*} _resources
64
64
  * @param {{encoder: import("../../device/ShadeGPUCommandContext.js").ShadeGPUCommandContext}} context
65
65
  */
66
- static "__#266@#execute_publish"(pass: object, _resources: any, context: {
66
+ static "__#268@#execute_publish"(pass: object, _resources: any, context: {
67
67
  encoder: import("../../device/ShadeGPUCommandContext.js").ShadeGPUCommandContext;
68
68
  }): void;
69
69
  /**
@@ -90,19 +90,19 @@ export class GPUFluidForcing {
90
90
  * @param {number} clear_size bytes cleared from 0 when the pool reuses the buffer
91
91
  * @returns {BufferResourceDescriptor}
92
92
  */
93
- static "__#267@#descriptor"(kept: BufferResourceDescriptor[], role: number, size: number, usage: number, clear_size: number): BufferResourceDescriptor;
93
+ static "__#269@#descriptor"(kept: BufferResourceDescriptor[], role: number, size: number, usage: number, clear_size: number): BufferResourceDescriptor;
94
94
  /**
95
95
  * @param {object} record
96
96
  * @param {FramePassResources} resources
97
97
  * @param {{encoder: ShadeGPUCommandContext}} context
98
98
  */
99
- static "__#267@#execute_extract"(record: object, resources: FramePassResources, context: {
99
+ static "__#269@#execute_extract"(record: object, resources: FramePassResources, context: {
100
100
  encoder: ShadeGPUCommandContext;
101
101
  }): void;
102
- static "__#267@#execute_clear"(record: any, _resources: any, context: any): void;
103
- static "__#267@#execute_query"(record: any, resources: any, context: any): void;
104
- static "__#267@#execute_command"(record: any, resources: any, context: any): void;
105
- static "__#267@#execute_apply"(record: any, resources: any, context: any): void;
102
+ static "__#269@#execute_clear"(record: any, _resources: any, context: any): void;
103
+ static "__#269@#execute_query"(record: any, resources: any, context: any): void;
104
+ static "__#269@#execute_command"(record: any, resources: any, context: any): void;
105
+ static "__#269@#execute_apply"(record: any, resources: any, context: any): void;
106
106
  /**
107
107
  * @param {object} options
108
108
  * @param {GPUDevice} options.device
@@ -41,7 +41,7 @@ export class GPUFluidPressureSolve {
41
41
  * @param {*} _resources
42
42
  * @param {{encoder: ShadeGPUCommandContext}} context
43
43
  */
44
- static "__#263@#execute"(data: {
44
+ static "__#265@#execute"(data: {
45
45
  solve: GPUFluidPressureSolve;
46
46
  arm: number;
47
47
  count: number;
@@ -49,7 +49,7 @@ export class GPUFluidQueries {
49
49
  * @param {import("../../../../engine/graphics/render/frame_graph/FramePassResources.js").FramePassResources} resources
50
50
  * @param {{encoder: import("../../../device/ShadeGPUCommandContext.js").ShadeGPUCommandContext}} context
51
51
  */
52
- static "__#268@#execute"(pass: object, resources: import("../../../../engine/graphics/render/frame_graph/FramePassResources.js").FramePassResources, context: {
52
+ static "__#270@#execute"(pass: object, resources: import("../../../../engine/graphics/render/frame_graph/FramePassResources.js").FramePassResources, context: {
53
53
  encoder: import("../../../device/ShadeGPUCommandContext.js").ShadeGPUCommandContext;
54
54
  }): void;
55
55
  /**
@@ -65,7 +65,7 @@ export class GPUGeometrySDFManager {
65
65
 
66
66
  this.#atlas = new GPUTileAtlas3D({
67
67
  device,
68
- format: "r32float",
68
+ formats: ["r32float"],
69
69
  // a cube: the build and every reader take one resolution for all three axes
70
70
  content_size: [DEFAULT_MESH_RESOLUTION, DEFAULT_MESH_RESOLUTION, DEFAULT_MESH_RESOLUTION],
71
71
  border: 0,