@woosh/meep-engine 3.29.0 → 3.30.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 (226) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/package.json +1 -1
  4. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  5. package/src/core/binary/BinaryBuffer.js +14 -2
  6. package/src/core/bvh2/bvh3/BVH.d.ts.map +1 -1
  7. package/src/core/bvh2/bvh3/BVH.js +14 -1
  8. package/src/core/cache/FrequencySketch.d.ts +3 -1
  9. package/src/core/cache/FrequencySketch.d.ts.map +1 -1
  10. package/src/core/cache/FrequencySketch.js +10 -4
  11. package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -1
  12. package/src/core/cache/wtinylfu/CacheWTinylfu.js +24 -2
  13. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts +20 -0
  14. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
  15. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +69 -4
  16. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +48 -18
  17. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
  18. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +262 -70
  19. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts +25 -0
  20. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts.map +1 -0
  21. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.js +228 -0
  22. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts +13 -0
  23. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts.map +1 -1
  24. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.js +10 -128
  25. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.d.ts.map +1 -1
  26. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.js +11 -1
  27. package/src/core/graph/mn_graph_collapse_weighted_edge.js +6 -6
  28. package/src/core/math/lookup/ParameterLookupTable.d.ts.map +1 -1
  29. package/src/core/math/lookup/ParameterLookupTable.js +10 -5
  30. package/src/core/math/noise/sdnoise.d.ts.map +1 -1
  31. package/src/core/math/noise/sdnoise.js +16 -10
  32. package/src/core/model/object/ImmutableObjectPool.d.ts.map +1 -1
  33. package/src/core/model/object/ImmutableObjectPool.js +17 -7
  34. package/src/core/process/executor/ConcurrentExecutor.d.ts +1 -5
  35. package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
  36. package/src/core/process/executor/ConcurrentExecutor.js +95 -1
  37. package/src/core/process/task/task_find_dependency_cycle.d.ts +17 -0
  38. package/src/core/process/task/task_find_dependency_cycle.d.ts.map +1 -0
  39. package/src/core/process/task/task_find_dependency_cycle.js +81 -0
  40. package/src/core/wfc/WFCCellChoice.d.ts +17 -0
  41. package/src/core/wfc/WFCCellChoice.d.ts.map +1 -0
  42. package/src/core/wfc/WFCCellChoice.js +36 -0
  43. package/src/core/wfc/WFCLattice.d.ts +105 -0
  44. package/src/core/wfc/WFCLattice.d.ts.map +1 -0
  45. package/src/core/wfc/WFCLattice.js +235 -0
  46. package/src/core/wfc/WFCRuleTable.d.ts +172 -0
  47. package/src/core/wfc/WFCRuleTable.d.ts.map +1 -0
  48. package/src/core/wfc/WFCRuleTable.js +334 -0
  49. package/src/core/wfc/WFCState.d.ts +273 -0
  50. package/src/core/wfc/WFCState.d.ts.map +1 -0
  51. package/src/core/wfc/WFCState.js +552 -0
  52. package/src/core/wfc/WFCStatus.d.ts +11 -0
  53. package/src/core/wfc/WFCStatus.d.ts.map +1 -0
  54. package/src/core/wfc/WFCStatus.js +28 -0
  55. package/src/core/wfc/WFCTileWeights.d.ts +48 -0
  56. package/src/core/wfc/WFCTileWeights.d.ts.map +1 -0
  57. package/src/core/wfc/WFCTileWeights.js +86 -0
  58. package/src/core/wfc/WFC_UNRESOLVED.d.ts +12 -0
  59. package/src/core/wfc/WFC_UNRESOLVED.d.ts.map +1 -0
  60. package/src/core/wfc/WFC_UNRESOLVED.js +11 -0
  61. package/src/core/wfc/wfc_estimate_bytes.d.ts +24 -0
  62. package/src/core/wfc/wfc_estimate_bytes.d.ts.map +1 -0
  63. package/src/core/wfc/wfc_estimate_bytes.js +47 -0
  64. package/src/core/wfc/wfc_rule_table_tile_components.d.ts +27 -0
  65. package/src/core/wfc/wfc_rule_table_tile_components.d.ts.map +1 -0
  66. package/src/core/wfc/wfc_rule_table_tile_components.js +80 -0
  67. package/src/core/wfc/wfc_solve.d.ts +21 -0
  68. package/src/core/wfc/wfc_solve.d.ts.map +1 -0
  69. package/src/core/wfc/wfc_solve.js +154 -0
  70. package/src/core/wfc/wfc_state_apply_boundary.d.ts +18 -0
  71. package/src/core/wfc/wfc_state_apply_boundary.d.ts.map +1 -0
  72. package/src/core/wfc/wfc_state_apply_boundary.js +61 -0
  73. package/src/core/wfc/wfc_state_assign.d.ts +11 -0
  74. package/src/core/wfc/wfc_state_assign.d.ts.map +1 -0
  75. package/src/core/wfc/wfc_state_assign.js +36 -0
  76. package/src/core/wfc/wfc_state_ban_bits.d.ts +17 -0
  77. package/src/core/wfc/wfc_state_ban_bits.d.ts.map +1 -0
  78. package/src/core/wfc/wfc_state_ban_bits.js +81 -0
  79. package/src/core/wfc/wfc_state_collapse.d.ts +12 -0
  80. package/src/core/wfc/wfc_state_collapse.d.ts.map +1 -0
  81. package/src/core/wfc/wfc_state_collapse.js +73 -0
  82. package/src/core/wfc/wfc_state_flush_dirty.d.ts +12 -0
  83. package/src/core/wfc/wfc_state_flush_dirty.d.ts.map +1 -0
  84. package/src/core/wfc/wfc_state_flush_dirty.js +39 -0
  85. package/src/core/wfc/wfc_state_propagate.d.ts +14 -0
  86. package/src/core/wfc/wfc_state_propagate.d.ts.map +1 -0
  87. package/src/core/wfc/wfc_state_propagate.js +155 -0
  88. package/src/core/wfc/wfc_state_read.d.ts +11 -0
  89. package/src/core/wfc/wfc_state_read.d.ts.map +1 -0
  90. package/src/core/wfc/wfc_state_read.js +38 -0
  91. package/src/core/wfc/wfc_state_restrict.d.ts +18 -0
  92. package/src/core/wfc/wfc_state_restrict.d.ts.map +1 -0
  93. package/src/core/wfc/wfc_state_restrict.js +39 -0
  94. package/src/core/wfc/wfc_state_set_eligible.d.ts +24 -0
  95. package/src/core/wfc/wfc_state_set_eligible.d.ts.map +1 -0
  96. package/src/core/wfc/wfc_state_set_eligible.js +49 -0
  97. package/src/core/wfc/wfc_state_undo_to.d.ts +13 -0
  98. package/src/core/wfc/wfc_state_undo_to.d.ts.map +1 -0
  99. package/src/core/wfc/wfc_state_undo_to.js +84 -0
  100. package/src/core/wfc/wfc_words_per_cell.d.ts +11 -0
  101. package/src/core/wfc/wfc_words_per_cell.d.ts.map +1 -0
  102. package/src/core/wfc/wfc_words_per_cell.js +16 -0
  103. package/src/engine/ecs/EntityComponentDataset.d.ts +8 -0
  104. package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
  105. package/src/engine/ecs/EntityComponentDataset.js +37 -14
  106. package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts +8 -1
  107. package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
  108. package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +37 -2
  109. package/src/engine/ecs/guid/UUID.d.ts.map +1 -1
  110. package/src/engine/ecs/guid/UUID.js +45 -17
  111. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +1 -1
  112. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +3 -1
  113. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
  114. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +366 -360
  115. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.d.ts.map +1 -1
  116. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.js +24 -10
  117. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
  118. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +24 -5
  119. package/src/engine/interpolation/Interpoland.d.ts +7 -1
  120. package/src/engine/interpolation/Interpoland.d.ts.map +1 -1
  121. package/src/engine/interpolation/Interpoland.js +8 -1
  122. package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
  123. package/src/engine/interpolation/InterpolationSystem.js +3 -0
  124. package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +5 -3
  125. package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
  126. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +14 -3
  127. package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -1
  128. package/src/engine/interpolation/pose_interpoland.js +2 -0
  129. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  130. package/src/engine/network/orchestrator/NetworkPeer.js +15 -10
  131. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  132. package/src/engine/physics/cloth/ecs/ClothWorld.js +17 -0
  133. package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
  134. package/src/engine/physics/mls-mpm/MLS_MPM.js +14 -0
  135. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts +2 -1
  136. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts.map +1 -1
  137. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +1 -1
  138. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts +1 -0
  139. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -1
  140. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +1 -0
  141. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
  142. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts +2 -1
  143. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -1
  144. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +12 -3
  145. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts +1 -0
  146. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts.map +1 -1
  147. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +1 -0
  148. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
  149. package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
  150. package/src/engine/sound/sopra/runtime/EventInstance.js +4 -1
  151. package/src/format/image/avif/av1/decode/decode_coefficients.d.ts.map +1 -1
  152. package/src/format/image/avif/av1/decode/decode_coefficients.js +248 -259
  153. package/src/format/image/avif/av1/decode/read_golomb.d.ts +21 -0
  154. package/src/format/image/avif/av1/decode/read_golomb.d.ts.map +1 -0
  155. package/src/format/image/avif/av1/decode/read_golomb.js +49 -0
  156. package/src/format/image/avif/av1/filter/loop_restoration.js +461 -451
  157. package/src/format/image/avif/av1/util/round2.d.ts +19 -2
  158. package/src/format/image/avif/av1/util/round2.d.ts.map +1 -1
  159. package/src/format/image/avif/av1/util/round2.js +51 -27
  160. package/src/format/image/avif/heif/parse_avif_file.js +24 -0
  161. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
  162. package/src/shade/renderer/buffer/table/GPUTypedTable.js +14 -0
  163. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +46 -3
  164. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +91 -1
  165. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +0 -13
  166. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  167. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +0 -15
  168. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.js +5 -5
  169. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts +11 -1
  170. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts.map +1 -1
  171. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.js +11 -1
  172. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +18 -9
  173. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -1
  174. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.js +20 -26
  175. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +1 -3
  176. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -1
  177. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.js +2 -5
  178. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +1 -1
  179. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
  180. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +14 -1
  181. package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
  182. package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +12 -3
  183. package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.d.ts.map +1 -1
  184. package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.js +7 -1
  185. package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
  186. package/src/shade/renderer/lightmap/GPULightMap.js +349 -336
  187. package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
  188. package/src/shade/renderer/loader/gltf/load_gltf.js +1137 -1128
  189. package/src/shade/renderer/particles/DESIGN.md +14 -5
  190. package/src/shade/renderer/particles/GPUParticleSystem.d.ts +25 -10
  191. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  192. package/src/shade/renderer/particles/GPUParticleSystem.js +182 -38
  193. package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
  194. package/src/shade/renderer/particles/graph/groups/simplex_reference.js +16 -10
  195. package/src/shade/renderer/particles/graph_particles.d.ts +4 -2
  196. package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
  197. package/src/shade/renderer/particles/graph_particles.js +8 -2
  198. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
  199. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +8 -2
  200. package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
  201. package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +10 -4
  202. package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts +31 -0
  203. package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts.map +1 -0
  204. package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.js +62 -0
  205. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +13 -0
  206. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
  207. package/src/shade/renderer/particles/vm/ParticleVMReference.js +95 -17
  208. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +21 -0
  209. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -0
  210. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +28 -0
  211. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  212. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +19 -2
  213. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  214. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +19 -2
  215. package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
  216. package/src/shade/renderer/shader/type/WebGPUStruct.js +12 -0
  217. package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts +8 -2
  218. package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts.map +1 -1
  219. package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js +41 -6
  220. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2383 -2356
  221. package/src/view/tooltip/DomTooltipManager.d.ts +7 -0
  222. package/src/view/tooltip/DomTooltipManager.d.ts.map +1 -1
  223. package/src/view/tooltip/DomTooltipManager.js +22 -0
  224. package/src/view/tooltip/DomTooltipObserver.d.ts.map +1 -1
  225. package/src/view/tooltip/DomTooltipObserver.js +13 -3
  226. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.js +0 -45
@@ -1,360 +1,366 @@
1
- import { assert } from "../../../../../core/assert.js";
2
- import { Deque } from "../../../../../core/collection/queue/Deque.js";
3
- import { SurfacePoint3 } from "../../../../../core/geom/3d/SurfacePoint3.js";
4
- import Quaternion from "../../../../../core/geom/Quaternion.js";
5
-
6
- import Vector3 from '../../../../../core/geom/Vector3.js';
7
- import { clamp01 } from "../../../../../core/math/clamp01.js";
8
- import { EPSILON } from "../../../../../core/math/EPSILON.js";
9
- import { min2 } from "../../../../../core/math/min2.js";
10
- import { ResourceAccessKind } from "../../../../../core/model/ResourceAccessKind.js";
11
- import { ResourceAccessSpecification } from "../../../../../core/model/ResourceAccessSpecification.js";
12
- import { System } from '../../../System.js';
13
- import { Transform64 } from '../../../transform/Transform64.js';
14
- import { obtainTerrain } from "../../util/obtainTerrain.js";
15
- import Terrain from "../Terrain.js";
16
-
17
- import ClingToTerrain from './ClingToTerrain.js';
18
- import { TRANSFORM64_EVENT_CHANGE } from "../../../transform/TRANSFORM64_EVENT_CHANGE.js";
19
- import { t64_announce_change } from "../../../transform/t64_announce_change.js";
20
- import { t64_read_rotation } from "../../../transform/t64_read_rotation.js";
21
- import { t64_read_translation } from "../../../transform/t64_read_translation.js";
22
-
23
-
24
- /**
25
- *
26
- * @type {SurfacePoint3}
27
- */
28
- const temp_sp = new SurfacePoint3();
29
-
30
- /**
31
- * @param {{update:function,ecd:EntityComponentDataset,entity:number}} datum
32
- */
33
- function deregister(datum) {
34
- datum.ecd.removeEntityEventListener(
35
- datum.entity, TRANSFORM64_EVENT_CHANGE, datum.update
36
- );
37
- }
38
-
39
- /**
40
- * How many entities can be updated in a single tick, this is useful for performance optimization.
41
- * If you have a large number of entities that need to be updated, updates can be executed over several ticks
42
- * @readonly
43
- * @type {number}
44
- */
45
- const DEFAULT_UPDATE_LIMIT = 1024;
46
-
47
- class ClingToTerrainSystem extends System {
48
-
49
-
50
- dependencies = [ClingToTerrain, Transform64];
51
-
52
-
53
- components_used = [
54
- ResourceAccessSpecification.from(Transform64, ResourceAccessKind.Read | ResourceAccessKind.Write),
55
- ResourceAccessSpecification.from(Terrain, ResourceAccessKind.Read)
56
- ];
57
-
58
- /**
59
- *
60
- * @type {Deque<number>}
61
- */
62
- updateQueue = new Deque();
63
-
64
- data = [];
65
-
66
- /**
67
- * How many entities can be updated in a single tick, this is useful for performance optimization, if you have a large number of entities that need to be updated, updates can be executed over several ticks
68
- * @type {number}
69
- */
70
- updateBatchLimit = DEFAULT_UPDATE_LIMIT;
71
-
72
- /**
73
- *
74
- * @param {number} entity
75
- */
76
- requestUpdate(entity) {
77
- if (!this.updateQueue.has(entity)) {
78
- //queue up update
79
- this.updateQueue.addLast(entity);
80
- }
81
- }
82
-
83
- /**
84
- *
85
- * @param {Transform64} transform
86
- * @param {ClingToTerrain} cling
87
- * @param {number} entityId
88
- */
89
- link(cling, transform, entityId) {
90
-
91
- const ecd = this.entityManager.dataset;
92
-
93
- /*
94
- The transform used to carry three signals and this listened to two of them. It carries
95
- none now, so the interest is registered against the entity and the transform's own change
96
- event: one listener where there were two, and it hears a write from anywhere - a spawner,
97
- a script, a physics step - rather than only one that went through a sub-object.
98
- */
99
- const update = () => this.requestUpdate(entityId);
100
-
101
- this.data[entityId] = {
102
- update,
103
- transform,
104
- component: cling,
105
- ecd,
106
- entity: entityId
107
- };
108
-
109
- ecd.addEntityEventListener(entityId, TRANSFORM64_EVENT_CHANGE, update);
110
-
111
- this.requestUpdate(entityId);
112
- }
113
-
114
- /**
115
- *
116
- * @param {Transform64} transform
117
- * @param {ClingToTerrain} cling
118
- * @param {number} entityId
119
- */
120
- unlink(cling, transform, entityId) {
121
- const datum = this.data[entityId];
122
-
123
- if (datum === undefined) {
124
- console.error(`Tried to remove entity ${entityId}, but it was not registered. This is an inconsistency bug.`, transform, cling, `component dump:`, this.entityManager.getComponents(entityId));
125
- return;
126
- }
127
-
128
- deregister(datum);
129
-
130
- delete this.data[entityId];
131
-
132
- //clear update request if one is pending
133
- this.updateQueue.remove(entityId);
134
- }
135
-
136
- update(timeDelta) {
137
- const em = this.entityManager;
138
-
139
- /**
140
- *
141
- * @type {EntityComponentDataset}
142
- */
143
- const dataset = em.dataset;
144
-
145
- if (dataset === null) {
146
- //no dataset attached
147
- return;
148
- }
149
-
150
- const updateCandidates = this.updateQueue;
151
-
152
- const terrain = obtainTerrain(dataset);
153
-
154
- if (terrain === null) {
155
- return;
156
- }
157
-
158
- const batchSize = min2(
159
- updateCandidates.size(),
160
- this.updateBatchLimit
161
- );
162
-
163
- for (let i = 0; i < batchSize; i++) {
164
- const entity = updateCandidates.removeFirst();
165
-
166
- const c = this.data[entity];
167
-
168
- if (c === undefined) {
169
- /*
170
- this should never happen as update queue is cleaned up when component is removed
171
- the entity is gone
172
- */
173
-
174
- //console.warn("ClingToTerrain component was in the update queue, but no data found. Skipping");
175
- continue;
176
- }
177
-
178
- const position_updated = doCling(c, terrain, timeDelta);
179
-
180
- if (!position_updated) {
181
- // no change, move back to update candidates
182
- this.requestUpdate(entity);
183
- }
184
- }
185
- }
186
- }
187
-
188
-
189
- const v3_temp = new Vector3();
190
- const v3_forward = new Vector3();
191
- const v3_up = new Vector3();
192
-
193
- const q_temp = new Quaternion();
194
-
195
- /**
196
- * The pose of whichever entity is being clung right now, read out of its transform.
197
- *
198
- * @type {Vector3}
199
- */
200
- const scratch_position = new Vector3();
201
-
202
- /**
203
- * @type {Quaternion}
204
- */
205
- const scratch_rotation = new Quaternion();
206
-
207
- /**
208
- * TODO look into "swing-twist decomposition"
209
- * @param {Quaternion} rotation
210
- * @param {Vector3} direction
211
- * @param {number} angularLimit
212
- * @param up
213
- * @param forward
214
- */
215
- export function alignToVector(rotation, direction, angularLimit, up = Vector3.up, forward = Vector3.forward) {
216
- assert.isNumber(angularLimit, 'angularLimit');
217
- assert.notNaN(angularLimit, 'angularLimit');
218
-
219
- //compute old facing direction vector
220
- v3_forward.copy(forward);
221
-
222
- v3_forward.applyQuaternion(rotation);
223
-
224
- //compute perpendicular vector between old and desired alignment
225
- v3_temp.crossVectors(direction, v3_forward);
226
-
227
- //compute new facing vector that is perpendicular to alignment direction
228
- v3_temp.crossVectors(v3_temp, direction);
229
-
230
- q_temp.lookRotation(v3_temp, direction);
231
-
232
- v3_up.copy(up);
233
-
234
- v3_up.applyQuaternion(rotation);
235
-
236
- const angle = v3_up.angleTo(direction);
237
-
238
- assert.notNaN(angle, 'angle');
239
-
240
- if (angle === 0) {
241
- //no change
242
- return;
243
- }
244
-
245
- const t = clamp01(angularLimit / angle);
246
-
247
- // TODO Consider replacing this with slerp
248
- rotation.lerp(q_temp, t);
249
- }
250
-
251
-
252
- /**
253
- *
254
- * @param {Vector3} point
255
- * @param {Transform64} t
256
- * @param {Vector3} normal
257
- * @param {ClingToTerrain} cling
258
- * @param {number} timeDelta
259
- */
260
- function processRaycastHit(point, t, normal, cling, timeDelta, el) {
261
- assert.defined(point, 'point');
262
- assert.equal(point.isVector3, true, 'point.isVector3 !== true');
263
- assert.isNumber(timeDelta, 'timeDelta');
264
-
265
- // filled by the caller, which had to read them to decide there was anything to do
266
- const position = scratch_position;
267
- const rotation = scratch_rotation;
268
-
269
- const hitDistance = position.distanceTo(point);
270
-
271
- //cache current position
272
- cling.__lastPosition.copy(position);
273
- cling.__lastRotation.copy(rotation);
274
-
275
- let moved = false;
276
-
277
- if (hitDistance > EPSILON) {
278
- t.setTranslation(position.x, point.y, position.z);
279
-
280
- moved = true;
281
- }
282
-
283
- let angularLimit;
284
-
285
- if (Number.isFinite(cling.rotationSpeed)) {
286
- angularLimit = cling.rotationSpeed * timeDelta;
287
- } else {
288
- angularLimit = Number.POSITIVE_INFINITY;
289
- }
290
-
291
- if (cling.normalAlign) {
292
- alignToVector(rotation, normal, angularLimit);
293
-
294
- if (
295
- rotation.x !== t.rotation_x || rotation.y !== t.rotation_y
296
- || rotation.z !== t.rotation_z || rotation.w !== t.rotation_w
297
- ) {
298
- t.setRotation(rotation.x, rotation.y, rotation.z, rotation.w);
299
-
300
- // the rotation is the only write that leaves the matrix behind
301
- t.updateMatrix();
302
-
303
- moved = true;
304
- }
305
- }
306
-
307
- /*
308
- Announced only when something actually changed. The system is listening to this entity itself,
309
- so an announcement it did not earn would queue the entity again, find nothing to do, and queue
310
- it again - a spin that never settles.
311
- */
312
- if (moved) {
313
- t64_announce_change(el.ecd, el.entity);
314
- }
315
- }
316
-
317
- /**
318
- *
319
- * @param {{component:ClingToTerrain,transform:Transform}} el
320
- * @param {Terrain} terrain
321
- * @param {number} timeDelta
322
- * @returns {boolean} whether position was updated or not
323
- */
324
- function doCling(el, terrain, timeDelta) {
325
- /**
326
- * @type {ClingToTerrain}
327
- */
328
- const cling = el.component;
329
-
330
- /**
331
- *
332
- * @type {Transform64}
333
- */
334
- const t = el.transform;
335
-
336
- // read out once; the comparison, the cast and the write below all want the same numbers, and
337
- // reading them through the component's view accessors would allocate two arrays per entity
338
- t64_read_translation(scratch_position, t);
339
- t64_read_rotation(scratch_rotation, t);
340
-
341
- if (
342
- cling.__lastPosition.roughlyEquals(scratch_position, 0.001)
343
- && cling.__lastRotation.roughlyEquals(scratch_rotation, 0.0001)
344
- ) {
345
- //do nothing, cached position matches with existing position
346
- return false;
347
- }
348
-
349
- const hit_found = terrain.raycastVerticalFirstSync(
350
- temp_sp, scratch_position.x, scratch_position.z
351
- );
352
-
353
- if (hit_found) {
354
- processRaycastHit(temp_sp.position, t, temp_sp.normal, cling, timeDelta, el);
355
- }
356
-
357
- return hit_found;
358
- }
359
-
360
- export default ClingToTerrainSystem;
1
+ import { assert } from "../../../../../core/assert.js";
2
+ import { Deque } from "../../../../../core/collection/queue/Deque.js";
3
+ import { SurfacePoint3 } from "../../../../../core/geom/3d/SurfacePoint3.js";
4
+ import Quaternion from "../../../../../core/geom/Quaternion.js";
5
+
6
+ import Vector3 from '../../../../../core/geom/Vector3.js';
7
+ import { clamp01 } from "../../../../../core/math/clamp01.js";
8
+ import { EPSILON } from "../../../../../core/math/EPSILON.js";
9
+ import { min2 } from "../../../../../core/math/min2.js";
10
+ import { ResourceAccessKind } from "../../../../../core/model/ResourceAccessKind.js";
11
+ import { ResourceAccessSpecification } from "../../../../../core/model/ResourceAccessSpecification.js";
12
+ import { System } from '../../../System.js';
13
+ import { Transform64 } from '../../../transform/Transform64.js';
14
+ import { obtainTerrain } from "../../util/obtainTerrain.js";
15
+ import Terrain from "../Terrain.js";
16
+
17
+ import ClingToTerrain from './ClingToTerrain.js';
18
+ import { TRANSFORM64_EVENT_CHANGE } from "../../../transform/TRANSFORM64_EVENT_CHANGE.js";
19
+ import { t64_announce_change } from "../../../transform/t64_announce_change.js";
20
+ import { t64_read_rotation } from "../../../transform/t64_read_rotation.js";
21
+ import { t64_read_translation } from "../../../transform/t64_read_translation.js";
22
+
23
+
24
+ /**
25
+ *
26
+ * @type {SurfacePoint3}
27
+ */
28
+ const temp_sp = new SurfacePoint3();
29
+
30
+ /**
31
+ * @param {{update:function,ecd:EntityComponentDataset,entity:number}} datum
32
+ */
33
+ function deregister(datum) {
34
+ datum.ecd.removeEntityEventListener(
35
+ datum.entity, TRANSFORM64_EVENT_CHANGE, datum.update
36
+ );
37
+ }
38
+
39
+ /**
40
+ * How many entities can be updated in a single tick, this is useful for performance optimization.
41
+ * If you have a large number of entities that need to be updated, updates can be executed over several ticks
42
+ * @readonly
43
+ * @type {number}
44
+ */
45
+ const DEFAULT_UPDATE_LIMIT = 1024;
46
+
47
+ class ClingToTerrainSystem extends System {
48
+
49
+
50
+ dependencies = [ClingToTerrain, Transform64];
51
+
52
+
53
+ components_used = [
54
+ ResourceAccessSpecification.from(Transform64, ResourceAccessKind.Read | ResourceAccessKind.Write),
55
+ ResourceAccessSpecification.from(Terrain, ResourceAccessKind.Read)
56
+ ];
57
+
58
+ /**
59
+ *
60
+ * @type {Deque<number>}
61
+ */
62
+ updateQueue = new Deque();
63
+
64
+ data = [];
65
+
66
+ /**
67
+ * How many entities can be updated in a single tick, this is useful for performance optimization, if you have a large number of entities that need to be updated, updates can be executed over several ticks
68
+ * @type {number}
69
+ */
70
+ updateBatchLimit = DEFAULT_UPDATE_LIMIT;
71
+
72
+ /**
73
+ *
74
+ * @param {number} entity
75
+ */
76
+ requestUpdate(entity) {
77
+ if (!this.updateQueue.has(entity)) {
78
+ //queue up update
79
+ this.updateQueue.addLast(entity);
80
+ }
81
+ }
82
+
83
+ /**
84
+ *
85
+ * @param {Transform64} transform
86
+ * @param {ClingToTerrain} cling
87
+ * @param {number} entityId
88
+ */
89
+ link(cling, transform, entityId) {
90
+
91
+ const ecd = this.entityManager.dataset;
92
+
93
+ /*
94
+ The transform used to carry three signals and this listened to two of them. It carries
95
+ none now, so the interest is registered against the entity and the transform's own change
96
+ event: one listener where there were two, and it hears a write from anywhere - a spawner,
97
+ a script, a physics step - rather than only one that went through a sub-object.
98
+ */
99
+ const update = () => this.requestUpdate(entityId);
100
+
101
+ this.data[entityId] = {
102
+ update,
103
+ transform,
104
+ component: cling,
105
+ ecd,
106
+ entity: entityId
107
+ };
108
+
109
+ ecd.addEntityEventListener(entityId, TRANSFORM64_EVENT_CHANGE, update);
110
+
111
+ this.requestUpdate(entityId);
112
+ }
113
+
114
+ /**
115
+ *
116
+ * @param {Transform64} transform
117
+ * @param {ClingToTerrain} cling
118
+ * @param {number} entityId
119
+ */
120
+ unlink(cling, transform, entityId) {
121
+ const datum = this.data[entityId];
122
+
123
+ if (datum === undefined) {
124
+ console.error(`Tried to remove entity ${entityId}, but it was not registered. This is an inconsistency bug.`, transform, cling, `component dump:`, this.entityManager.getComponents(entityId));
125
+ return;
126
+ }
127
+
128
+ deregister(datum);
129
+
130
+ delete this.data[entityId];
131
+
132
+ //clear update request if one is pending
133
+ this.updateQueue.remove(entityId);
134
+ }
135
+
136
+ update(timeDelta) {
137
+ const em = this.entityManager;
138
+
139
+ /**
140
+ *
141
+ * @type {EntityComponentDataset}
142
+ */
143
+ const dataset = em.dataset;
144
+
145
+ if (dataset === null) {
146
+ //no dataset attached
147
+ return;
148
+ }
149
+
150
+ const updateCandidates = this.updateQueue;
151
+
152
+ const terrain = obtainTerrain(dataset);
153
+
154
+ if (terrain === null) {
155
+ return;
156
+ }
157
+
158
+ const batchSize = min2(
159
+ updateCandidates.size(),
160
+ this.updateBatchLimit
161
+ );
162
+
163
+ for (let i = 0; i < batchSize; i++) {
164
+ const entity = updateCandidates.removeFirst();
165
+
166
+ const c = this.data[entity];
167
+
168
+ if (c === undefined) {
169
+ /*
170
+ this should never happen as update queue is cleaned up when component is removed
171
+ the entity is gone
172
+ */
173
+
174
+ //console.warn("ClingToTerrain component was in the update queue, but no data found. Skipping");
175
+ continue;
176
+ }
177
+
178
+ const position_updated = doCling(c, terrain, timeDelta);
179
+
180
+ if (!position_updated) {
181
+ // no change, move back to update candidates
182
+ this.requestUpdate(entity);
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+
189
+ const v3_temp = new Vector3();
190
+ const v3_forward = new Vector3();
191
+ const v3_up = new Vector3();
192
+
193
+ const q_temp = new Quaternion();
194
+
195
+ /**
196
+ * The pose of whichever entity is being clung right now, read out of its transform.
197
+ *
198
+ * @type {Vector3}
199
+ */
200
+ const scratch_position = new Vector3();
201
+
202
+ /**
203
+ * @type {Quaternion}
204
+ */
205
+ const scratch_rotation = new Quaternion();
206
+
207
+ /**
208
+ * TODO look into "swing-twist decomposition"
209
+ * @param {Quaternion} rotation
210
+ * @param {Vector3} direction
211
+ * @param {number} angularLimit
212
+ * @param up
213
+ * @param forward
214
+ */
215
+ export function alignToVector(rotation, direction, angularLimit, up = Vector3.up, forward = Vector3.forward) {
216
+ assert.isNumber(angularLimit, 'angularLimit');
217
+ assert.notNaN(angularLimit, 'angularLimit');
218
+
219
+ //compute old facing direction vector
220
+ v3_forward.copy(forward);
221
+
222
+ v3_forward.applyQuaternion(rotation);
223
+
224
+ //compute perpendicular vector between old and desired alignment
225
+ v3_temp.crossVectors(direction, v3_forward);
226
+
227
+ //compute new facing vector that is perpendicular to alignment direction
228
+ v3_temp.crossVectors(v3_temp, direction);
229
+
230
+ q_temp.lookRotation(v3_temp, direction);
231
+
232
+ v3_up.copy(up);
233
+
234
+ v3_up.applyQuaternion(rotation);
235
+
236
+ const angle = v3_up.angleTo(direction);
237
+
238
+ assert.notNaN(angle, 'angle');
239
+
240
+ if (angle === 0) {
241
+ //no change
242
+ return;
243
+ }
244
+
245
+ const t = clamp01(angularLimit / angle);
246
+
247
+ /**
248
+ * `slerp` and not `lerp`: a componentwise lerp neither renormalises nor takes the shortest arc,
249
+ * and `Transform64#setRotation` states that the quaternion is expected to be of unit length and
250
+ * that nothing there normalises it. A short quaternion composes into a sheared, non-orthogonal
251
+ * basis that `updateComponents()` then decomposes into the entity's scale and carries down every
252
+ * attachment below it. Measured worst case here was |q| = 0.707, against a near-antipodal normal.
253
+ */
254
+ rotation.slerp(q_temp, t);
255
+ }
256
+
257
+
258
+ /**
259
+ *
260
+ * @param {Vector3} point
261
+ * @param {Transform64} t
262
+ * @param {Vector3} normal
263
+ * @param {ClingToTerrain} cling
264
+ * @param {number} timeDelta
265
+ */
266
+ function processRaycastHit(point, t, normal, cling, timeDelta, el) {
267
+ assert.defined(point, 'point');
268
+ assert.equal(point.isVector3, true, 'point.isVector3 !== true');
269
+ assert.isNumber(timeDelta, 'timeDelta');
270
+
271
+ // filled by the caller, which had to read them to decide there was anything to do
272
+ const position = scratch_position;
273
+ const rotation = scratch_rotation;
274
+
275
+ const hitDistance = position.distanceTo(point);
276
+
277
+ //cache current position
278
+ cling.__lastPosition.copy(position);
279
+ cling.__lastRotation.copy(rotation);
280
+
281
+ let moved = false;
282
+
283
+ if (hitDistance > EPSILON) {
284
+ t.setTranslation(position.x, point.y, position.z);
285
+
286
+ moved = true;
287
+ }
288
+
289
+ let angularLimit;
290
+
291
+ if (Number.isFinite(cling.rotationSpeed)) {
292
+ angularLimit = cling.rotationSpeed * timeDelta;
293
+ } else {
294
+ angularLimit = Number.POSITIVE_INFINITY;
295
+ }
296
+
297
+ if (cling.normalAlign) {
298
+ alignToVector(rotation, normal, angularLimit);
299
+
300
+ if (
301
+ rotation.x !== t.rotation_x || rotation.y !== t.rotation_y
302
+ || rotation.z !== t.rotation_z || rotation.w !== t.rotation_w
303
+ ) {
304
+ t.setRotation(rotation.x, rotation.y, rotation.z, rotation.w);
305
+
306
+ // the rotation is the only write that leaves the matrix behind
307
+ t.updateMatrix();
308
+
309
+ moved = true;
310
+ }
311
+ }
312
+
313
+ /*
314
+ Announced only when something actually changed. The system is listening to this entity itself,
315
+ so an announcement it did not earn would queue the entity again, find nothing to do, and queue
316
+ it again - a spin that never settles.
317
+ */
318
+ if (moved) {
319
+ t64_announce_change(el.ecd, el.entity);
320
+ }
321
+ }
322
+
323
+ /**
324
+ *
325
+ * @param {{component:ClingToTerrain,transform:Transform}} el
326
+ * @param {Terrain} terrain
327
+ * @param {number} timeDelta
328
+ * @returns {boolean} whether position was updated or not
329
+ */
330
+ function doCling(el, terrain, timeDelta) {
331
+ /**
332
+ * @type {ClingToTerrain}
333
+ */
334
+ const cling = el.component;
335
+
336
+ /**
337
+ *
338
+ * @type {Transform64}
339
+ */
340
+ const t = el.transform;
341
+
342
+ // read out once; the comparison, the cast and the write below all want the same numbers, and
343
+ // reading them through the component's view accessors would allocate two arrays per entity
344
+ t64_read_translation(scratch_position, t);
345
+ t64_read_rotation(scratch_rotation, t);
346
+
347
+ if (
348
+ cling.__lastPosition.roughlyEquals(scratch_position, 0.001)
349
+ && cling.__lastRotation.roughlyEquals(scratch_rotation, 0.0001)
350
+ ) {
351
+ //do nothing, cached position matches with existing position
352
+ return false;
353
+ }
354
+
355
+ const hit_found = terrain.raycastVerticalFirstSync(
356
+ temp_sp, scratch_position.x, scratch_position.z
357
+ );
358
+
359
+ if (hit_found) {
360
+ processRaycastHit(temp_sp.position, t, temp_sp.normal, cling, timeDelta, el);
361
+ }
362
+
363
+ return hit_found;
364
+ }
365
+
366
+ export default ClingToTerrainSystem;