@woosh/meep-engine 3.68.0 → 3.69.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 (190) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts +2 -17
  3. package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts.map +1 -1
  4. package/editor/ecs/component/editors/ecs/Trail3DEditor.js +3 -18
  5. package/package.json +1 -1
  6. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +33 -65
  7. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
  8. package/src/engine/graphics/ecs/trail3d/Trail3D.js +37 -245
  9. package/src/engine/graphics/ecs/trail3d/Trail3DFlags.d.ts +0 -4
  10. package/src/engine/graphics/ecs/trail3d/Trail3DFlags.js +4 -10
  11. package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.d.ts +13 -10
  12. package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.d.ts.map +1 -1
  13. package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.js +28 -37
  14. package/src/engine/graphics/ecs/trail3d/trail_knot_count.d.ts +10 -0
  15. package/src/engine/graphics/ecs/trail3d/trail_knot_count.d.ts.map +1 -0
  16. package/src/engine/graphics/ecs/trail3d/trail_knot_count.js +27 -0
  17. package/src/engine/graphics/trail/TrailKnots.d.ts +62 -0
  18. package/src/engine/graphics/trail/TrailKnots.d.ts.map +1 -0
  19. package/src/engine/graphics/trail/TrailKnots.js +137 -0
  20. package/src/engine/graphics/trail/tube/TubeXMaterialSpec.d.ts +1 -1
  21. package/src/engine/graphics/trail/tube/TubeXMaterialSpec.js +1 -1
  22. package/src/engine/graphics3/EntityRowHold.d.ts +48 -0
  23. package/src/engine/graphics3/EntityRowHold.d.ts.map +1 -0
  24. package/src/engine/graphics3/EntityRowHold.js +128 -0
  25. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +6 -2
  26. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  27. package/src/engine/graphics3/GPUParticleEmitterSystem.js +11 -44
  28. package/src/engine/graphics3/Trail3DSystem.d.ts +69 -35
  29. package/src/engine/graphics3/Trail3DSystem.d.ts.map +1 -1
  30. package/src/engine/graphics3/Trail3DSystem.js +380 -81
  31. package/src/engine/physics/fluid/ecs/FluidObstacle.d.ts +17 -0
  32. package/src/engine/physics/fluid/ecs/FluidObstacle.d.ts.map +1 -1
  33. package/src/engine/physics/fluid/ecs/FluidObstacle.js +22 -2
  34. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +6 -5
  35. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts.map +1 -1
  36. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +14 -3
  37. package/src/engine/physics/fluid/obstacle/FLUID_OBSTACLE_RECORD_STRUCT.d.ts +9 -1
  38. package/src/engine/physics/fluid/obstacle/FLUID_OBSTACLE_RECORD_STRUCT.d.ts.map +1 -1
  39. package/src/engine/physics/fluid/obstacle/FLUID_OBSTACLE_RECORD_STRUCT.js +11 -1
  40. package/src/engine/physics/fluid/obstacle/fluid_obstacle_record_create.d.ts +5 -0
  41. package/src/engine/physics/fluid/obstacle/fluid_obstacle_record_create.d.ts.map +1 -1
  42. package/src/engine/physics/fluid/obstacle/fluid_obstacle_record_create.js +2 -0
  43. package/src/engine/physics/fluid/obstacle/fluid_obstacle_write_gpu_record.d.ts +4 -3
  44. package/src/engine/physics/fluid/obstacle/fluid_obstacle_write_gpu_record.d.ts.map +1 -1
  45. package/src/engine/physics/fluid/obstacle/fluid_obstacle_write_gpu_record.js +11 -3
  46. package/src/shade/playground/trails/index.html +50 -0
  47. package/src/shade/playground/trails/main.d.ts +2 -0
  48. package/src/shade/playground/trails/main.d.ts.map +1 -0
  49. package/src/shade/playground/trails/main.js +258 -0
  50. package/src/shade/renderer/animation/animation_curve_write_keys.d.ts +18 -0
  51. package/src/shade/renderer/animation/animation_curve_write_keys.d.ts.map +1 -0
  52. package/src/shade/renderer/animation/animation_curve_write_keys.js +34 -0
  53. package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts.map +1 -1
  54. package/src/shade/renderer/deferred/tonemap/ColorGrade.js +13 -36
  55. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +2 -2
  56. package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.d.ts +2 -13
  57. package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.d.ts.map +1 -1
  58. package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.js +6 -21
  59. package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.d.ts +17 -6
  60. package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.d.ts.map +1 -1
  61. package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.js +38 -178
  62. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts +7 -9
  63. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts.map +1 -1
  64. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.js +17 -106
  65. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.d.ts +7 -17
  66. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.d.ts.map +1 -1
  67. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.js +18 -118
  68. package/src/shade/renderer/fluid/forcing/FLUID_FORCING.d.ts +14 -0
  69. package/src/shade/renderer/fluid/forcing/FLUID_FORCING.d.ts.map +1 -1
  70. package/src/shade/renderer/fluid/forcing/FLUID_FORCING.js +18 -0
  71. package/src/shade/renderer/fluid/forcing/FLUID_FORCING_APPLY_STRUCT.d.ts +3 -1
  72. package/src/shade/renderer/fluid/forcing/FLUID_FORCING_APPLY_STRUCT.d.ts.map +1 -1
  73. package/src/shade/renderer/fluid/forcing/FLUID_FORCING_APPLY_STRUCT.js +4 -1
  74. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +9 -0
  75. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts.map +1 -1
  76. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.js +20 -4
  77. package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_cell.d.ts +11 -2
  78. package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_cell.d.ts.map +1 -1
  79. package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_cell.js +59 -2
  80. package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.d.ts +4 -1
  81. package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.d.ts.map +1 -1
  82. package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.js +367 -341
  83. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.d.ts +6 -2
  84. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.d.ts.map +1 -1
  85. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.js +339 -282
  86. package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts +3 -2
  87. package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts.map +1 -1
  88. package/src/shade/renderer/grass/press/shader_grass_press_mark.js +5 -3
  89. package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_draw.d.ts +49 -0
  90. package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_draw.d.ts.map +1 -0
  91. package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_draw.js +144 -0
  92. package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_splat.d.ts +71 -0
  93. package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_splat.d.ts.map +1 -0
  94. package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_splat.js +152 -0
  95. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_unlit_varyings.d.ts +21 -0
  96. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_unlit_varyings.d.ts.map +1 -0
  97. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_unlit_varyings.js +30 -0
  98. package/src/shade/renderer/rasterize/native/avboit/graph_avboit_unlit.d.ts +101 -0
  99. package/src/shade/renderer/rasterize/native/avboit/graph_avboit_unlit.d.ts.map +1 -0
  100. package/src/shade/renderer/rasterize/native/avboit/graph_avboit_unlit.js +306 -0
  101. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.d.ts +9 -0
  102. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.d.ts.map +1 -1
  103. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.js +11 -4
  104. package/src/shade/renderer/trails/GPUTrailAVBOITChannel.d.ts +69 -0
  105. package/src/shade/renderer/trails/GPUTrailAVBOITChannel.d.ts.map +1 -0
  106. package/src/shade/renderer/trails/GPUTrailAVBOITChannel.js +129 -0
  107. package/src/shade/renderer/trails/GPUTrailSystem.d.ts +164 -0
  108. package/src/shade/renderer/trails/GPUTrailSystem.d.ts.map +1 -0
  109. package/src/shade/renderer/trails/GPUTrailSystem.js +725 -0
  110. package/src/shade/renderer/trails/data/TRAIL_DATABASE_SPEC.d.ts +14 -0
  111. package/src/shade/renderer/trails/data/TRAIL_DATABASE_SPEC.d.ts.map +1 -0
  112. package/src/shade/renderer/trails/data/TRAIL_DATABASE_SPEC.js +38 -0
  113. package/src/shade/renderer/trails/data/TRAIL_KNOT.d.ts +66 -0
  114. package/src/shade/renderer/trails/data/TRAIL_KNOT.d.ts.map +1 -0
  115. package/src/shade/renderer/trails/data/TRAIL_KNOT.js +154 -0
  116. package/src/shade/renderer/trails/data/TRAIL_RECORD.d.ts +19 -0
  117. package/src/shade/renderer/trails/data/TRAIL_RECORD.d.ts.map +1 -0
  118. package/src/shade/renderer/trails/data/TRAIL_RECORD.js +68 -0
  119. package/src/shade/renderer/trails/data/TRAIL_STATE.d.ts +18 -0
  120. package/src/shade/renderer/trails/data/TRAIL_STATE.d.ts.map +1 -0
  121. package/src/shade/renderer/trails/data/TRAIL_STATE.js +43 -0
  122. package/src/shade/renderer/trails/graph_trails.d.ts +58 -0
  123. package/src/shade/renderer/trails/graph_trails.d.ts.map +1 -0
  124. package/src/shade/renderer/trails/graph_trails.js +155 -0
  125. package/src/shade/renderer/trails/graph_trails_avboit.d.ts +126 -0
  126. package/src/shade/renderer/trails/graph_trails_avboit.d.ts.map +1 -0
  127. package/src/shade/renderer/trails/graph_trails_avboit.js +300 -0
  128. package/src/shade/renderer/trails/runtime/TrailFadeCurves.d.ts +56 -0
  129. package/src/shade/renderer/trails/runtime/TrailFadeCurves.d.ts.map +1 -0
  130. package/src/shade/renderer/trails/runtime/TrailFadeCurves.js +220 -0
  131. package/src/shade/renderer/trails/runtime/TrailKnotPool.d.ts +49 -0
  132. package/src/shade/renderer/trails/runtime/TrailKnotPool.d.ts.map +1 -0
  133. package/src/shade/renderer/trails/runtime/TrailKnotPool.js +162 -0
  134. package/src/shade/renderer/trails/runtime/TrailParameters.d.ts +52 -0
  135. package/src/shade/renderer/trails/runtime/TrailParameters.d.ts.map +1 -0
  136. package/src/shade/renderer/trails/runtime/TrailParameters.js +126 -0
  137. package/src/shade/renderer/trails/shaders/chunk_trail_frame_transport.d.ts +23 -0
  138. package/src/shade/renderer/trails/shaders/chunk_trail_frame_transport.d.ts.map +1 -0
  139. package/src/shade/renderer/trails/shaders/chunk_trail_frame_transport.js +57 -0
  140. package/src/shade/renderer/trails/shaders/chunk_trail_knot_alpha.d.ts +14 -0
  141. package/src/shade/renderer/trails/shaders/chunk_trail_knot_alpha.d.ts.map +1 -0
  142. package/src/shade/renderer/trails/shaders/chunk_trail_knot_alpha.js +40 -0
  143. package/src/shade/renderer/trails/shaders/chunk_trail_seed.d.ts +18 -0
  144. package/src/shade/renderer/trails/shaders/chunk_trail_seed.d.ts.map +1 -0
  145. package/src/shade/renderer/trails/shaders/chunk_trail_seed.js +64 -0
  146. package/src/shade/renderer/trails/shaders/chunk_trail_segment.d.ts +14 -0
  147. package/src/shade/renderer/trails/shaders/chunk_trail_segment.d.ts.map +1 -0
  148. package/src/shade/renderer/trails/shaders/chunk_trail_segment.js +51 -0
  149. package/src/shade/renderer/trails/shaders/chunk_trail_update.d.ts +48 -0
  150. package/src/shade/renderer/trails/shaders/chunk_trail_update.d.ts.map +1 -0
  151. package/src/shade/renderer/trails/shaders/chunk_trail_update.js +197 -0
  152. package/src/shade/renderer/trails/shaders/chunk_trail_vertex.d.ts +34 -0
  153. package/src/shade/renderer/trails/shaders/chunk_trail_vertex.d.ts.map +1 -0
  154. package/src/shade/renderer/trails/shaders/chunk_trail_vertex.js +111 -0
  155. package/src/shade/renderer/trails/shaders/shader_trail_avboit.d.ts +24 -0
  156. package/src/shade/renderer/trails/shaders/shader_trail_avboit.d.ts.map +1 -0
  157. package/src/shade/renderer/trails/shaders/shader_trail_avboit.js +78 -0
  158. package/src/shade/renderer/trails/shaders/shader_trail_avboit_segments.d.ts +23 -0
  159. package/src/shade/renderer/trails/shaders/shader_trail_avboit_segments.d.ts.map +1 -0
  160. package/src/shade/renderer/trails/shaders/shader_trail_avboit_segments.js +130 -0
  161. package/src/shade/renderer/trails/shaders/shader_trail_draw_args.d.ts +29 -0
  162. package/src/shade/renderer/trails/shaders/shader_trail_draw_args.d.ts.map +1 -0
  163. package/src/shade/renderer/trails/shaders/shader_trail_draw_args.js +78 -0
  164. package/src/shade/renderer/trails/shaders/shader_trail_seed.d.ts +18 -0
  165. package/src/shade/renderer/trails/shaders/shader_trail_seed.d.ts.map +1 -0
  166. package/src/shade/renderer/trails/shaders/shader_trail_seed.js +79 -0
  167. package/src/shade/renderer/trails/shaders/shader_trail_update.d.ts +20 -0
  168. package/src/shade/renderer/trails/shaders/shader_trail_update.d.ts.map +1 -0
  169. package/src/shade/renderer/trails/shaders/shader_trail_update.js +101 -0
  170. package/src/engine/graphics/ecs/trail3d/advance_trail.d.ts +0 -18
  171. package/src/engine/graphics/ecs/trail3d/advance_trail.d.ts.map +0 -1
  172. package/src/engine/graphics/ecs/trail3d/advance_trail.js +0 -42
  173. package/src/engine/graphics/ecs/trail3d/seed_trail_tube.d.ts +0 -14
  174. package/src/engine/graphics/ecs/trail3d/seed_trail_tube.d.ts.map +0 -1
  175. package/src/engine/graphics/ecs/trail3d/seed_trail_tube.js +0 -75
  176. package/src/engine/graphics/ecs/trail3d/trail_head_position.d.ts +0 -17
  177. package/src/engine/graphics/ecs/trail3d/trail_head_position.d.ts.map +0 -1
  178. package/src/engine/graphics/ecs/trail3d/trail_head_position.js +0 -19
  179. package/src/engine/graphics/trail/tube/TubeX.d.ts +0 -186
  180. package/src/engine/graphics/trail/tube/TubeX.d.ts.map +0 -1
  181. package/src/engine/graphics/trail/tube/TubeX.js +0 -575
  182. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts +0 -16
  183. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts.map +0 -1
  184. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +0 -34
  185. package/src/engine/graphics/trail/tube/tube_attributes_spec.d.ts +0 -27
  186. package/src/engine/graphics/trail/tube/tube_attributes_spec.d.ts.map +0 -1
  187. package/src/engine/graphics/trail/tube/tube_attributes_spec.js +0 -70
  188. package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts +0 -38
  189. package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts.map +0 -1
  190. package/src/engine/graphics3/trail/trail_tube_write_geometry.js +0 -119
@@ -1,118 +1,250 @@
1
1
  import { assert } from "../../core/assert.js";
2
- import { DynamicMeshAVBOITChannel } from "../../shade/renderer/dynamic/DynamicMeshAVBOITChannel.js";
3
2
  import { FramePhase } from "../../shade/renderer/extension/FramePhase.js";
4
3
  import { RenderExtension } from "../../shade/renderer/extension/RenderExtension.js";
5
- import { Geometry } from "../../shade/renderer/geometry/Geometry.js";
6
- import { DynamicMesh } from "../../shade/renderer/scene/DynamicMesh.js";
7
- import { DynamicMeshBatch } from "../../shade/renderer/scene/DynamicMeshBatch.js";
4
+ import { row_of_entity } from "../../shade/renderer/scene/rows/GPUSceneRows.js";
5
+ import { graph_import_buffer } from "../../shade/renderer/shader/graph/graph_import_buffer.js";
6
+ import { GPUTrailAVBOITChannel } from "../../shade/renderer/trails/GPUTrailAVBOITChannel.js";
7
+ import { GPUTrailSystem } from "../../shade/renderer/trails/GPUTrailSystem.js";
8
+ import { TrailParameters } from "../../shade/renderer/trails/runtime/TrailParameters.js";
8
9
  import { System } from "../ecs/System.js";
9
10
  import { Transform64 } from "../ecs/transform/Transform64.js";
10
- import { advance_trail } from "../graphics/ecs/trail3d/advance_trail.js";
11
- import { seed_trail_tube } from "../graphics/ecs/trail3d/seed_trail_tube.js";
11
+ import { trail_knot_count } from "../graphics/ecs/trail3d/trail_knot_count.js";
12
12
  import Trail3D from "../graphics/ecs/trail3d/Trail3D.js";
13
13
  import { Trail3DFlags } from "../graphics/ecs/trail3d/Trail3DFlags.js";
14
- import {
15
- TubeXFixedPhysicsSimulator
16
- } from "../graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js";
17
- import { trail_tube_write_geometry } from "./trail/trail_tube_write_geometry.js";
14
+ import { TrailSpawnMode } from "../graphics/trail/TrailSpawnMode.js";
15
+ import { EntityRowHold } from "./EntityRowHold.js";
18
16
 
19
17
  /**
20
- * Draws {@link Trail3D}'s tubes under Shade, as dynamic geometry inside the transparency pipeline.
18
+ * @typedef {Object} Trail3DEntry
19
+ * @property {number} entity
20
+ * @property {Trail3D} trail
21
+ * @property {EntityRowHold} hold the entity's row, kept current as it moves
22
+ * @property {import("../../shade/renderer/trails/GPUTrailSystem.js").GPUTrailContext|null} context the
23
+ * trail in the runtime, or null while it is not registered
24
+ * @property {number} knot_count knots of the trail's ring, fixed when it is built
25
+ * @property {number} radial radial segments, fixed when it is built
26
+ * @property {number} clears the component's `clearCount` as last seen
27
+ */
28
+
29
+ /**
30
+ * Knots a trail is built with: its authored layout's, or as many as its maxAge lays down.
21
31
  *
22
- * **A trail is dynamic content by definition** — every knot of it is rewritten as the entity moves
23
- * and every knot's opacity as it ages — so its geometry is a {@link DynamicMesh} (D5/I-10), not a
24
- * meshlet build, the same as paths and debug shapes.
32
+ * @param {Trail3D} trail
33
+ * @returns {number}
34
+ */
35
+ function knot_count_of(trail) {
36
+ return trail.knots !== null ? trail.knots.count : trail_knot_count(trail.maxAge);
37
+ }
38
+
39
+ /**
40
+ * Copy a component onto runtime parameters.
25
41
  *
26
- * **It is drawn through AVBOIT, not over it.** The batch is a transparency channel
27
- * ({@link DynamicMeshAVBOITChannel}, registered with the facade): its fragments go into the same
28
- * extinction volume and accumulators as the transparent meshes and the GPU particles, so a trail
29
- * behind glass is seen through the glass, a trail inside smoke is hidden by the smoke in front of it
30
- * and hides the smoke behind it, and two trails crossing composite by depth, not by the order they
31
- * were linked in. Everything drawn at `AfterTransparency` runs after the transparency resolve they
32
- * are part of, so path display and the selection highlight land over a trail rather than under it.
33
- * Under MBOIT, the legacy transparency mode, trails are not drawn — as the particles are not.
42
+ * @param {TrailParameters} parameters
43
+ * @param {Trail3D} trail
44
+ * @param {Trail3DEntry} entry
45
+ */
46
+ export function apply_trail3d(parameters, trail, entry) {
47
+ const knots = trail.knots;
48
+
49
+ parameters.knot_count = entry.knot_count;
50
+ parameters.radial = entry.radial;
51
+ parameters.max_age = trail.maxAge;
52
+ parameters.width = trail.width;
53
+ parameters.distance_mode = trail.spawnMode === TrailSpawnMode.Distance;
54
+ parameters.spawn_distance = trail.spawnDistance;
55
+ parameters.spawning = trail.getFlag(Trail3DFlags.Spawning);
56
+ parameters.warmup = trail.warmup;
57
+
58
+ const color = trail.color;
59
+
60
+ parameters.color[0] = color.x;
61
+ parameters.color[1] = color.y;
62
+ parameters.color[2] = color.z;
63
+ parameters.color[3] = color.w;
64
+
65
+ const offset = trail.offset;
66
+
67
+ parameters.offset[0] = offset.x;
68
+ parameters.offset[1] = offset.y;
69
+ parameters.offset[2] = offset.z;
70
+
71
+ parameters.fade = trail.fade;
72
+ parameters.authored = knots;
73
+ parameters.authored_version = knots === null ? -1 : knots.version;
74
+ }
75
+
76
+ /**
77
+ * Records the trails' simulation into the frame, once the hierarchy has composed this frame's matrices
78
+ * and before the transparency pipeline draws.
79
+ */
80
+ class Trail3DFrameExtension extends RenderExtension {
81
+ name = "trails";
82
+
83
+ phase = FramePhase.AfterLighting;
84
+
85
+ /**
86
+ * @type {Trail3DSystem}
87
+ */
88
+ #system;
89
+
90
+ /**
91
+ * @param {Trail3DSystem} system
92
+ */
93
+ constructor(system) {
94
+ super();
95
+
96
+ this.#system = system;
97
+ }
98
+
99
+ record(frame) {
100
+ this.#system.record(frame);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Draws every {@link Trail3D}: simulated on the GPU, its head following the entity's `transforms` row,
106
+ * its knots seeded and committed in compute and its tube extruded in the vertex stage, drawn inside the
107
+ * transparency pipeline (AVBOIT), so a trail behind glass or smoke composites with them by depth.
108
+ * Nothing per knot happens on the CPU.
34
109
  *
35
- * **The simulation is not this system's, and that is deliberate.** {@link seed_trail_tube} and
36
- * {@link advance_trail} are what the legacy `Trail3DSystem` runs too: the knots a trail lays down
37
- * are a property of the trail, not of the renderer drawing it, and two implementations of them
38
- * would be two trails that age differently. What is this system's is turning those knots into
39
- * vertices, once a frame, per trail.
110
+ * **What is the CPU's:** registering a trail with the scene context's {@link GPUTrailSystem} — a row, a
111
+ * knot run, the entity's transform row held through {@link EntityRowHold} — comparing each component's
112
+ * fields once a tick with what was staged and restaging what differs, and moving the clock by the
113
+ * simulation time it is given. A game writes `trail.color` or `trail.maxAge` from code that never heard
114
+ * of this system; there is no change signal to send.
40
115
  *
41
- * **Every trail is rewritten every frame it exists**, moving or not, because the simulator ages
42
- * every knot on every tick and the age is what the vertex opacity carries. There is no quiet trail
43
- * — a trail that has stopped is a trail fading out.
116
+ * **The head follows announced transforms.** A trail reads where its entity is from the GPU row, which a
117
+ * transform write reaches when it is announced (`TRANSFORM64_EVENT_CHANGE`), as meshes and particle
118
+ * emitters do.
44
119
  *
45
- * **What the tube material had and this does not**: a texture, lighting, and per-trail depth
46
- * writing. The dynamic path has one vertex layout — position and colour — and a trail is drawn as
47
- * unlit straight alpha, depth tested against the opaque scene and never written, which is what a
48
- * trail wants and what the game's trails are authored as. `Trail3D.textureURL` therefore draws
49
- * untinted here; nothing in the game's own effects sets it to anything a flat colour does not
50
- * carry.
120
+ * A trail's knot count (from its `maxAge`, or its authored layout's) and its `radialSegments` are fixed
121
+ * when it registers; a replaced authored layout brings its own count.
51
122
  */
52
123
  export class Trail3DSystem extends System {
53
124
  dependencies = [Trail3D, Transform64];
54
125
 
55
126
  /**
56
- * @type {GraphicsEngine}
127
+ * @type {import("./GraphicsEngine.js").GraphicsEngine}
57
128
  */
58
129
  #graphics;
59
130
 
60
131
  /**
61
- * @type {DynamicMeshBatch}
132
+ * @type {import("../../shade/renderer/scene/Scene.js").Scene}
62
133
  */
63
- #batch = new DynamicMeshBatch();
134
+ #scene;
64
135
 
65
136
  /**
66
- * The batch as the transparency pipeline sees it, and the owner of its GPU buffers.
67
- *
68
- * @type {DynamicMeshAVBOITChannel}
137
+ * @type {import("../ecs/EntityComponentDataset.js").EntityComponentDataset|null}
69
138
  */
70
- #channel = new DynamicMeshAVBOITChannel(this.#batch);
139
+ #dataset = null;
71
140
 
72
141
  /**
73
- * @type {Map<number, {trail: Trail3D, transform: Transform, mesh: DynamicMesh}>}
142
+ * @type {Map<number, Trail3DEntry>}
74
143
  */
75
144
  #entries = new Map();
76
145
 
77
146
  /**
78
- * @type {TubeXFixedPhysicsSimulator}
147
+ * @type {GPUTrailSystem|null}
79
148
  */
80
- #simulator = new TubeXFixedPhysicsSimulator();
149
+ #runtime = null;
81
150
 
82
151
  /**
83
- * What the transparency channel draws. Read-only — the meshes follow the entities.
84
- *
85
- * @returns {DynamicMeshBatch}
152
+ * The scene context {@link #runtime} was made for.
153
+ * @type {import("../../shade/renderer/scene/GPUSceneContext.js").GPUSceneContext|null}
86
154
  */
87
- get batch() {
88
- return this.#batch;
89
- }
155
+ #runtime_context = null;
156
+
157
+ /**
158
+ * @type {Trail3DFrameExtension|null}
159
+ */
160
+ #extension = null;
161
+
162
+ /**
163
+ * @type {TrailParameters}
164
+ */
165
+ #scratch = new TrailParameters();
90
166
 
91
167
  /**
92
- * @param {GraphicsEngine} graphics
168
+ * What draws the trails, inside the transparency pipeline.
169
+ * @type {GPUTrailAVBOITChannel}
93
170
  */
94
- constructor(graphics) {
171
+ #channel = new GPUTrailAVBOITChannel();
172
+
173
+ /**
174
+ * @type {function(): (import("../../shade/renderer/scene/GPUSceneContext.js").GPUSceneContext|null)}
175
+ */
176
+ #current_context = () => this.#graphics.scene_context(this.#scene);
177
+
178
+ /**
179
+ * @param {import("./GraphicsEngine.js").GraphicsEngine} graphics
180
+ * @param {import("../../shade/renderer/scene/Scene.js").Scene} scene the scene the trails' entities are in
181
+ */
182
+ constructor(graphics, scene) {
95
183
  super();
96
184
 
97
185
  assert.defined(graphics, 'graphics');
186
+ assert.defined(scene, 'scene');
187
+ assert.equal(scene.isScene, true, 'scene.isScene !== true');
98
188
 
99
189
  this.#graphics = graphics;
190
+ this.#scene = scene;
191
+ }
192
+
193
+ /**
194
+ * The runtime the scene's trails are registered with, or null while there is no scene context.
195
+ *
196
+ * @returns {GPUTrailSystem|null}
197
+ */
198
+ get runtime() {
199
+ return this.#runtime;
100
200
  }
101
201
 
102
202
  async startup(entityManager) {
103
203
  this.entityManager = entityManager;
104
204
 
205
+ this.#extension = new Trail3DFrameExtension(this);
206
+
207
+ this.#graphics.add_extension(this.#extension);
105
208
  this.#graphics.add_transparency_channel(this.#channel);
106
209
  }
107
210
 
108
211
  async shutdown(entityManager) {
109
- // says whether it was there, so a shutdown after a startup that never ran is harmless
212
+ if (this.#extension !== null) {
213
+ this.#graphics.remove_extension(this.#extension);
214
+ this.#extension = null;
215
+ }
216
+
110
217
  this.#graphics.remove_transparency_channel(this.#channel);
218
+ this.#channel.end_frame();
219
+
220
+ this.#detach_dataset();
221
+ this.#destroy_runtime();
222
+ }
223
+
224
+ /**
225
+ * @param {import("../ecs/EntityComponentDataset.js").EntityComponentDataset} dataset
226
+ */
227
+ handleDatasetAttached(dataset) {
228
+ this.#detach_dataset();
111
229
 
112
- this.#channel.destroy();
230
+ // the scene's rows are this dataset's entities; the first system to place one says so
231
+ const scene = this.#scene;
113
232
 
114
- this.#batch.clear();
115
- this.#entries.clear();
233
+ assert.ok(
234
+ scene.dataset === null || scene.dataset === undefined || scene.dataset === dataset,
235
+ 'the scene is bound to another dataset'
236
+ );
237
+
238
+ scene.dataset = dataset;
239
+
240
+ this.#dataset = dataset;
241
+ }
242
+
243
+ /**
244
+ * @param {import("../ecs/EntityComponentDataset.js").EntityComponentDataset} dataset
245
+ */
246
+ handleDatasetDetached(dataset) {
247
+ this.#detach_dataset();
116
248
  }
117
249
 
118
250
  /**
@@ -121,56 +253,223 @@ export class Trail3DSystem extends System {
121
253
  * @param {number} entity
122
254
  */
123
255
  link(trail, transform, entity) {
124
- if (!trail.getFlag(Trail3DFlags.Built)) {
125
- seed_trail_tube(trail, transform);
256
+ /** @type {Trail3DEntry} */
257
+ const entry = {
258
+ entity,
259
+ trail,
260
+ hold: new EntityRowHold(this.#dataset, entity, this.#current_context),
261
+ context: null,
262
+ knot_count: 0,
263
+ radial: 0,
264
+ clears: trail.clearCount,
265
+ };
266
+
267
+ this.#entries.set(entity, entry);
268
+
269
+ // registered now when there is a context, so a trail added mid-level is seeded where its entity
270
+ // stands from its first frame
271
+ if (this.#ensure_runtime() !== null) {
272
+ this.#register(entry);
273
+ }
274
+ }
275
+
276
+ /**
277
+ * @param {Trail3D} trail
278
+ * @param {Transform64} transform
279
+ * @param {number} entity
280
+ */
281
+ unlink(trail, transform, entity) {
282
+ this.#drop(entity);
283
+ }
284
+
285
+ /**
286
+ * Move the clock by the simulation time given, register what is waiting, and restage every trail
287
+ * whose component changed.
288
+ *
289
+ * @param {number} [time_delta_seconds]
290
+ */
291
+ update(time_delta_seconds = 0) {
292
+ const runtime = this.#ensure_runtime();
293
+
294
+ if (runtime === null) {
295
+ return;
296
+ }
297
+
298
+ runtime.advance(time_delta_seconds);
299
+
300
+ for (const entry of this.#entries.values()) {
301
+ this.#sync(runtime, entry);
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Record the frame's trail work — called by the frame extension.
307
+ *
308
+ * @param {import("../../shade/renderer/extension/FrameContext.js").FrameContext} frame
309
+ */
310
+ record(frame) {
311
+ // whatever the last frame left is not this frame's: a frame that records nothing draws nothing
312
+ this.#channel.end_frame();
313
+
314
+ if (frame.view.scene.scene !== this.#scene) {
315
+ return;
316
+ }
317
+
318
+ const runtime = this.#ensure_runtime();
319
+
320
+ if (runtime === null) {
321
+ return;
126
322
  }
127
323
 
128
- // the extrusion produces world-space vertices, and what the draw applies is the mesh's own
129
- // transform, identity until moved
130
- const mesh = DynamicMesh.from(new Geometry());
324
+ for (const entry of this.#entries.values()) {
325
+ this.#sync(runtime, entry);
326
+ }
131
327
 
132
- trail_tube_write_geometry(mesh.geometry, trail.tube);
328
+ // Read the way the velocity pass and the particles read it: the hierarchy is encoded ahead of
329
+ // the frame's graph, so the matrices in it are this frame's.
330
+ const scene_database = graph_import_buffer(frame.graph, frame.view.scene.scene_database_buffer, "scene_database/trails");
133
331
 
134
- this.#batch.add(mesh);
332
+ const simulation = runtime.record_simulation(frame.graph, scene_database);
135
333
 
136
- this.#entries.set(entity, { trail, transform, mesh });
334
+ this.#channel.begin_frame(frame.graph, frame.view, runtime, simulation);
335
+ }
336
+
337
+ /**
338
+ * @param {GPUTrailSystem} runtime
339
+ * @param {Trail3DEntry} entry
340
+ */
341
+ #sync(runtime, entry) {
342
+ const context = entry.context;
343
+
344
+ if (context === null) {
345
+ this.#register(entry);
346
+ return;
347
+ }
348
+
349
+ const trail = entry.trail;
350
+
351
+ // a build-time change: an authored layout replaced, or one added or taken away
352
+ const knots = trail.knots;
353
+
354
+ if (knots !== context.staged.authored
355
+ || (knots !== null && knots.version !== context.staged.authored_version)) {
356
+ entry.knot_count = knot_count_of(trail);
357
+ }
358
+
359
+ const scratch = this.#scratch;
360
+
361
+ apply_trail3d(scratch, trail, entry);
362
+
363
+ if (!scratch.equals(context.staged)) {
364
+ runtime.stage(context, scratch);
365
+ }
366
+
367
+ if (entry.clears !== trail.clearCount) {
368
+ entry.clears = trail.clearCount;
369
+ runtime.clear(context);
370
+ }
371
+ }
372
+
373
+ /**
374
+ * @param {Trail3DEntry} entry
375
+ */
376
+ #register(entry) {
377
+ const runtime = this.#runtime;
378
+ const trail = entry.trail;
379
+
380
+ entry.knot_count = knot_count_of(trail);
381
+ entry.radial = trail.radialSegments;
382
+ entry.clears = trail.clearCount;
383
+
384
+ entry.hold.acquire(this.#runtime_context);
385
+
386
+ const scratch = this.#scratch;
387
+
388
+ apply_trail3d(scratch, trail, entry);
389
+
390
+ const context = runtime.add(scratch, row_of_entity(entry.entity));
391
+
392
+ if (context === null) {
393
+ // the knot pool cannot grow to house it on this device
394
+ entry.hold.release();
395
+ return;
396
+ }
397
+
398
+ entry.context = context;
137
399
  }
138
400
 
139
401
  /**
140
- * @param {Trail3D} trail
141
- * @param {Transform64} transform
142
402
  * @param {number} entity
143
403
  */
144
- unlink(trail, transform, entity) {
404
+ #drop(entity) {
145
405
  const entry = this.#entries.get(entity);
146
406
 
147
407
  if (entry === undefined) {
148
408
  return;
149
409
  }
150
410
 
151
- this.#batch.remove(entry.mesh);
411
+ if (entry.context !== null && this.#runtime !== null) {
412
+ this.#runtime.remove(entry.context);
413
+ }
152
414
 
153
- // residency deliberately survives leaving the batch, so nothing else would free it
154
- this.#channel.release(entry.mesh.geometry);
415
+ entry.context = null;
416
+ entry.hold.dispose();
155
417
 
156
418
  this.#entries.delete(entity);
419
+ }
420
+
421
+ /**
422
+ * The runtime for the scene context the scene renders through now, made fresh when that context is
423
+ * new: a device restart builds a new one, and every trail registers again and is seeded anew.
424
+ *
425
+ * @returns {GPUTrailSystem|null}
426
+ */
427
+ #ensure_runtime() {
428
+ const context = this.#current_context();
429
+
430
+ if (context === this.#runtime_context) {
431
+ return this.#runtime;
432
+ }
433
+
434
+ this.#destroy_runtime();
435
+
436
+ if (context === null) {
437
+ return null;
438
+ }
439
+
440
+ this.#runtime = new GPUTrailSystem(context.graphics);
441
+ this.#runtime_context = context;
157
442
 
158
- trail.dispose();
443
+ return this.#runtime;
159
444
  }
160
445
 
161
- update(timeDelta) {
446
+ #destroy_runtime() {
162
447
  for (const entry of this.#entries.values()) {
163
- const trail = entry.trail;
448
+ // the trails and rows went with the context that held them
449
+ entry.context = null;
450
+ entry.hold.release();
451
+ }
164
452
 
165
- advance_trail(trail, entry.transform, timeDelta, this.#simulator);
453
+ if (this.#runtime !== null) {
454
+ this.#runtime.destroy();
455
+ }
166
456
 
167
- // The component's own bounds serve CPU culling, which this path does not do (§4.5), and
168
- // the depth range the transparency pipeline needs the channel reads off the written
169
- // geometry — so the flag the head raises has nobody else to answer it.
170
- trail.clearFlag(Trail3DFlags.BoundsNeedUpdate);
457
+ this.#runtime = null;
458
+ this.#runtime_context = null;
459
+ }
171
460
 
172
- trail_tube_write_geometry(entry.mesh.geometry, trail.tube);
461
+ #detach_dataset() {
462
+ for (const entity of Array.from(this.#entries.keys())) {
463
+ this.#drop(entity);
173
464
  }
465
+
466
+ const dataset = this.#dataset;
467
+
468
+ if (dataset !== null && this.#scene.dataset === dataset) {
469
+ this.#scene.dataset = null;
470
+ }
471
+
472
+ this.#dataset = null;
174
473
  }
175
474
  }
176
475
 
@@ -38,6 +38,22 @@ export class FluidObstacle {
38
38
  * @type {number}
39
39
  */
40
40
  inflation: number;
41
+ /**
42
+ * How the obstacle stops the wind, 1/m. `0` (default): it is **solid**, and the air takes its
43
+ * velocity wherever it is. Above `0`: it is **porous**, the air passes through it and is slowed
44
+ * toward its velocity by `drag · |u − v| · (u − v)` each second, `u` the air's velocity and `v`
45
+ * the obstacle's.
46
+ *
47
+ * For something made of leaves or blades it is their drag coefficient times their one-sided area
48
+ * per cubic metre of the volume: a canopy of grass, a crop, the crown of a tree, a hedge. Their drag
49
+ * is spread through the volume, which is what makes the wind slow inside and fast above, and the
50
+ * shear at its top roll up into the gusts that run over a field.
51
+ *
52
+ * Only the GPU fluid has porous obstacles; the CPU fluid leaves them out.
53
+ *
54
+ * @type {number}
55
+ */
56
+ drag: number;
41
57
  /**
42
58
  * True when the previous tick stamped a non-zero wall velocity. A body
43
59
  * that stops (or sleeps — the solver zeroes velocities on sleep) must
@@ -58,6 +74,7 @@ export class FluidObstacle {
58
74
  hash(): number;
59
75
  toJSON(): {
60
76
  inflation: number;
77
+ drag: number;
61
78
  };
62
79
  fromJSON(json: any): void;
63
80
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"FluidObstacle.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/ecs/FluidObstacle.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;IAEI;;;;;;;;;;;;OAYG;IACH,WAFU,MAAM,CAEF;IAEd;;;;;;;OAOG;IACH,aAFU,OAAO,CAEG;IAEpB;;;OAGG;IACH,cAHW,aAAa,GACZ,OAAO,CAUlB;IAED;;OAEG;IACH,QAFY,MAAM,CAIjB;IAED;;MAIC;IAED,0BAEC;IASL;;;OAGG;IACH,0BAFU,OAAO,CAEsB;CAZtC;;kBAIS,MAAM"}
1
+ {"version":3,"file":"FluidObstacle.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/fluid/ecs/FluidObstacle.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;IAEI;;;;;;;;;;;;OAYG;IACH,WAFU,MAAM,CAEF;IAEd;;;;;;;;;;;;;;OAcG;IACH,MAFU,MAAM,CAEP;IAET;;;;;;;OAOG;IACH,aAFU,OAAO,CAEG;IAEpB;;;OAGG;IACH,cAHW,aAAa,GACZ,OAAO,CAUlB;IAED;;OAEG;IACH,QAFY,MAAM,CAIjB;IAED;;;MAKC;IAED,0BAGC;IASL;;;OAGG;IACH,0BAFU,OAAO,CAEsB;CAZtC;;kBAIS,MAAM"}
@@ -1,3 +1,4 @@
1
+ import { hash_mix2 } from "../../../../core/math/hash/hash_mix2.js";
1
2
  import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
2
3
 
3
4
  /**
@@ -42,6 +43,23 @@ export class FluidObstacle {
42
43
  */
43
44
  inflation = 0;
44
45
 
46
+ /**
47
+ * How the obstacle stops the wind, 1/m. `0` (default): it is **solid**, and the air takes its
48
+ * velocity wherever it is. Above `0`: it is **porous**, the air passes through it and is slowed
49
+ * toward its velocity by `drag · |u − v| · (u − v)` each second, `u` the air's velocity and `v`
50
+ * the obstacle's.
51
+ *
52
+ * For something made of leaves or blades it is their drag coefficient times their one-sided area
53
+ * per cubic metre of the volume: a canopy of grass, a crop, the crown of a tree, a hedge. Their drag
54
+ * is spread through the volume, which is what makes the wind slow inside and fast above, and the
55
+ * shear at its top roll up into the gusts that run over a field.
56
+ *
57
+ * Only the GPU fluid has porous obstacles; the CPU fluid leaves them out.
58
+ *
59
+ * @type {number}
60
+ */
61
+ drag = 0;
62
+
45
63
  /**
46
64
  * True when the previous tick stamped a non-zero wall velocity. A body
47
65
  * that stops (or sleeps — the solver zeroes velocities on sleep) must
@@ -63,24 +81,26 @@ export class FluidObstacle {
63
81
  if (other === null || other === undefined || other.isFluidObstacle !== true) {
64
82
  return false;
65
83
  }
66
- return this.inflation === other.inflation;
84
+ return this.inflation === other.inflation && this.drag === other.drag;
67
85
  }
68
86
 
69
87
  /**
70
88
  * @return {number}
71
89
  */
72
90
  hash() {
73
- return computeHashFloat(this.inflation);
91
+ return hash_mix2(computeHashFloat(this.inflation), computeHashFloat(this.drag));
74
92
  }
75
93
 
76
94
  toJSON() {
77
95
  return {
78
96
  inflation: this.inflation,
97
+ drag: this.drag,
79
98
  };
80
99
  }
81
100
 
82
101
  fromJSON(json) {
83
102
  if (json.inflation !== undefined) this.inflation = json.inflation;
103
+ this.drag = json.drag ?? 0;
84
104
  }
85
105
  }
86
106