@woosh/meep-engine 3.14.3 → 3.14.5

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 (137) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/mat4/m4_rigidity_defect.d.ts +30 -0
  3. package/src/core/geom/3d/mat4/m4_rigidity_defect.d.ts.map +1 -0
  4. package/src/core/geom/3d/mat4/m4_rigidity_defect.js +71 -0
  5. package/src/engine/Engine.d.ts.map +1 -1
  6. package/src/engine/Engine.js +733 -726
  7. package/src/engine/network/NetworkSession.d.ts +24 -4
  8. package/src/engine/network/NetworkSession.d.ts.map +1 -1
  9. package/src/engine/network/NetworkSession.js +28 -4
  10. package/src/engine/network/orchestrator/NetworkPeer.d.ts +96 -7
  11. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  12. package/src/engine/network/orchestrator/NetworkPeer.js +392 -68
  13. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +6 -1
  14. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
  15. package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +6 -0
  16. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +105 -13
  17. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
  18. package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +306 -59
  19. package/src/engine/network/replication/Replicator.d.ts +43 -7
  20. package/src/engine/network/replication/Replicator.d.ts.map +1 -1
  21. package/src/engine/network/replication/Replicator.js +309 -111
  22. package/src/engine/network/sim/ActionLog.d.ts +23 -3
  23. package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
  24. package/src/engine/network/sim/ActionLog.js +26 -3
  25. package/src/engine/network/sim/ActionRecordCursor.d.ts +82 -0
  26. package/src/engine/network/sim/ActionRecordCursor.d.ts.map +1 -0
  27. package/src/engine/network/sim/ActionRecordCursor.js +120 -0
  28. package/src/engine/network/sim/RewindEngine.d.ts.map +1 -1
  29. package/src/engine/network/sim/RewindEngine.js +13 -29
  30. package/src/engine/network/sim/SimAction.d.ts +15 -0
  31. package/src/engine/network/sim/SimAction.d.ts.map +1 -1
  32. package/src/engine/network/sim/SimAction.js +15 -0
  33. package/src/engine/network/sim/SimActionExecutor.d.ts +51 -2
  34. package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
  35. package/src/engine/network/sim/SimActionExecutor.js +74 -7
  36. package/src/engine/network/transport/Channel.d.ts +1 -1
  37. package/src/engine/network/transport/Channel.js +1 -1
  38. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  39. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  40. package/src/engine/simulation/Ticker.js +4 -1
  41. package/src/engine/ui/GUIEngine.d.ts.map +1 -1
  42. package/src/engine/ui/GUIEngine.js +6 -0
  43. package/src/shade/playground/particle_system/README.md +98 -0
  44. package/src/shade/playground/particle_system/index.html +124 -0
  45. package/src/shade/playground/particle_system/main.d.ts +2 -0
  46. package/src/shade/playground/particle_system/main.d.ts.map +1 -0
  47. package/src/shade/playground/particle_system/main.js +180 -0
  48. package/src/shade/playground/particle_system/particle_scene.d.ts +52 -0
  49. package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -0
  50. package/src/shade/playground/particle_system/particle_scene.js +553 -0
  51. package/src/shade/playground/skinned_mesh_soup/README.md +143 -0
  52. package/src/shade/playground/skinned_mesh_soup/index.html +202 -0
  53. package/src/shade/playground/skinned_mesh_soup/main.d.ts +7 -0
  54. package/src/shade/playground/skinned_mesh_soup/main.d.ts.map +1 -0
  55. package/src/shade/playground/skinned_mesh_soup/main.js +567 -0
  56. package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts +29 -0
  57. package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts.map +1 -0
  58. package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.js +428 -0
  59. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  60. package/src/shade/renderer/animation/GPUAnimationManager.js +1958 -1796
  61. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +357 -0
  62. package/src/shade/renderer/animation/skin_bind_transform.d.ts +25 -0
  63. package/src/shade/renderer/animation/skin_bind_transform.d.ts.map +1 -0
  64. package/src/shade/renderer/animation/skin_bind_transform.js +33 -0
  65. package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts +67 -0
  66. package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts.map +1 -0
  67. package/src/shade/renderer/animation/skin_clip_scale_conflicts.js +199 -0
  68. package/src/shade/renderer/animation/skin_frame_defect.d.ts +34 -0
  69. package/src/shade/renderer/animation/skin_frame_defect.d.ts.map +1 -0
  70. package/src/shade/renderer/animation/skin_frame_defect.js +132 -0
  71. package/src/shade/renderer/animation/skin_joint_matrix.d.ts +24 -0
  72. package/src/shade/renderer/animation/skin_joint_matrix.d.ts.map +1 -0
  73. package/src/shade/renderer/animation/skin_joint_matrix.js +46 -0
  74. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts +70 -0
  75. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts.map +1 -0
  76. package/src/shade/renderer/animation/skin_normalize_mesh_frame.js +216 -0
  77. package/src/shade/renderer/animation/skin_test_fixtures.d.ts +86 -0
  78. package/src/shade/renderer/animation/skin_test_fixtures.d.ts.map +1 -0
  79. package/src/shade/renderer/animation/skin_test_fixtures.js +250 -0
  80. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.d.ts +18 -4
  81. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.d.ts.map +1 -1
  82. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +18 -4
  83. package/src/shade/renderer/particles/DESIGN.md +5 -5
  84. package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts.map +1 -1
  85. package/src/shade/renderer/particles/cull/chunk_particle_cull.js +3 -1
  86. package/src/shade/renderer/particles/data/chunk_particle_emitter.js +1 -1
  87. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +43 -0
  88. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -0
  89. package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +44 -0
  90. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +33 -59
  91. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
  92. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +185 -118
  93. package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts +15 -10
  94. package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
  95. package/src/shade/renderer/particles/graph/compile_particle_graph.js +205 -170
  96. package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts +44 -0
  97. package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts.map +1 -0
  98. package/src/shade/renderer/particles/graph/particle_graph_authoring.js +108 -0
  99. package/src/shade/renderer/particles/isa/InstructionStream.d.ts +89 -0
  100. package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -0
  101. package/src/shade/renderer/particles/isa/InstructionStream.js +162 -0
  102. package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts +1 -48
  103. package/src/shade/renderer/particles/isa/ParticleAssembler.d.ts.map +1 -1
  104. package/src/shade/renderer/particles/isa/ParticleAssembler.js +25 -114
  105. package/src/shade/renderer/particles/particle_prototype.d.ts.map +1 -1
  106. package/src/shade/renderer/particles/particle_prototype.js +181 -180
  107. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +4 -4
  108. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
  109. package/src/shade/renderer/particles/runtime/create_particle_effect.js +19 -19
  110. package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.d.ts.map +1 -1
  111. package/src/shade/renderer/particles/shaders/chunk_particle_curve_animation.js +4 -2
  112. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts +4 -4
  113. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts.map +1 -1
  114. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.js +7 -5
  115. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts.map +1 -1
  116. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +3 -1
  117. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  118. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +3 -1
  119. package/src/shade/renderer/particles/shaders/shader_particle_finalize.d.ts.map +1 -1
  120. package/src/shade/renderer/particles/shaders/shader_particle_finalize.js +5 -2
  121. package/src/shade/renderer/particles/shaders/shader_particle_render.d.ts.map +1 -1
  122. package/src/shade/renderer/particles/shaders/shader_particle_render.js +9 -2
  123. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  124. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +3 -1
  125. package/src/shade/renderer/particles/vm/chunk_particle_vm.js +1 -1
  126. package/src/shade/renderer/scene/SkinnedMesh.d.ts +11 -0
  127. package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +1 -1
  128. package/src/shade/renderer/scene/SkinnedMesh.js +11 -0
  129. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.d.ts +33 -3
  130. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.d.ts.map +1 -1
  131. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.js +58 -37
  132. package/src/shade/renderer/particles/graph/ParticleGraph.d.ts +0 -54
  133. package/src/shade/renderer/particles/graph/ParticleGraph.d.ts.map +0 -1
  134. package/src/shade/renderer/particles/graph/ParticleGraph.js +0 -99
  135. package/src/shade/renderer/particles/prototypeParticleSystem.d.ts +0 -2
  136. package/src/shade/renderer/particles/prototypeParticleSystem.d.ts.map +0 -1
  137. package/src/shade/renderer/particles/prototypeParticleSystem.js +0 -448
@@ -1,180 +1,181 @@
1
- import { EMITTER_BLEND, EMITTER_PROJECTION } from "./data/PARTICLE_EMITTER_LAYOUT.js";
2
- import { ParticleGraph } from "./graph/ParticleGraph.js";
3
- import { VM_BUILTIN, VM_RANDOM_MODE } from "./isa/ParticleVMISA.js";
4
- import { ParticleLayout } from "./layout/ParticleLayout.js";
5
- import { create_particle_effect } from "./runtime/create_particle_effect.js";
6
-
7
- /**
8
- * Worked examples of authoring particle effects with the node graph + wiring them into a
9
- * {@link GPUParticleSystem}. Demonstrates: distinct per-particle lifecycle (no built-in age the
10
- * effect declares it), AnimationCurve sampling over life, flipbook frames, random emission,
11
- * additive vs alpha blending, projection modes, soft depth and lighting toggles.
12
- *
13
- * `curves` is a `GPUAnimationManager` (or anything with `register_curve(AnimationCurve) -> handle`);
14
- * `size_curve` / `alpha_curve` are meep `AnimationCurve`s. Both are optional — passing `undefined`
15
- * yields effects that use constant size/alpha instead (curve handle 0, evaluated as 0 by the
16
- * disabled provider), which is handy for the headless tests.
17
- */
18
-
19
- /**
20
- * A fire effect: buoyant, turbulent, additive-blended camera-facing sprites whose size and opacity
21
- * follow curves over their lifetime and whose flipbook advances with age.
22
- *
23
- * @param {object} [options]
24
- * @param {number} [options.size_curve] curve handle for size(normalizedAge)
25
- * @param {number} [options.alpha_curve] curve handle for alpha(normalizedAge)
26
- * @param {number} [options.flipbook_frames]
27
- * @returns {{layout: ParticleLayout, effect: object, emitter: object}}
28
- */
29
- export function fire_effect({ size_curve, alpha_curve, flipbook_frames = 16 } = {}) {
30
- const layout = new ParticleLayout([
31
- { name: "position", components: 3 },
32
- { name: "velocity", components: 3 },
33
- { name: "age", components: 1 },
34
- { name: "lifetime", components: 1 },
35
- { name: "size", components: 1 },
36
- { name: "color", components: 4 },
37
- { name: "frame", components: 1 },
38
- ]);
39
-
40
- // --- SPAWN: seed each particle's state ---
41
- const spawn = new ParticleGraph();
42
- {
43
- // position = emitter position + a small disk of jitter on the ground
44
- const ep = spawn.add("builtin", { id: VM_BUILTIN.EMITTER_POSITION });
45
- const disk = spawn.add("random", { mode: VM_RANDOM_MODE.UNIT_DISK });
46
- const jitter = spawn.add("scale"); spawn.wire(jitter, "v", disk); spawn.wire(jitter, "s", [0.2]);
47
- const p0 = spawn.add("add"); spawn.wire(p0, "a", ep); spawn.wire(p0, "b", jitter);
48
- spawn.wire(spawn.add("setAttribute", { name: "position" }), "value", p0);
49
-
50
- // velocity = upward buoyancy + signed turbulence
51
- const up = spawn.add("const", { value: [0, 2.0, 0, 0] });
52
- const turb = spawn.add("random", { mode: VM_RANDOM_MODE.UNIFORM_SIGNED });
53
- const turbS = spawn.add("scale"); spawn.wire(turbS, "v", turb); spawn.wire(turbS, "s", [0.5]);
54
- const v0 = spawn.add("add"); spawn.wire(v0, "a", up); spawn.wire(v0, "b", turbS);
55
- spawn.wire(spawn.add("setAttribute", { name: "velocity" }), "value", v0);
56
-
57
- spawn.wire(spawn.add("setAttribute", { name: "age" }), "value", [0]);
58
- // lifetime 1.2..1.8s
59
- const lr = spawn.add("random", { mode: VM_RANDOM_MODE.UNIFORM });
60
- const ls = spawn.add("mad"); spawn.wire(ls, "a", lr); spawn.wire(ls, "b", [0.6]); spawn.wire(ls, "c", [1.2]);
61
- spawn.wire(spawn.add("setAttribute", { name: "lifetime" }), "value", ls);
62
-
63
- spawn.wire(spawn.add("setAttribute", { name: "size" }), "value", [0.4]);
64
- spawn.wire(spawn.add("setAttribute", { name: "color" }), "value", [1.0, 0.6, 0.2, 1.0]);
65
- spawn.wire(spawn.add("setAttribute", { name: "frame" }), "value", [0]);
66
- }
67
-
68
- // --- UPDATE: advance + destroy ---
69
- const update = new ParticleGraph();
70
- {
71
- const dt = update.add("builtin", { id: VM_BUILTIN.DELTA_TIME });
72
-
73
- // age += dt
74
- const age = update.add("attribute", { name: "age" });
75
- const newAge = update.add("add"); update.wire(newAge, "a", age); update.wire(newAge, "b", dt);
76
- update.wire(update.add("setAttribute", { name: "age" }), "value", newAge);
77
-
78
- // normalizedAge = age / lifetime
79
- const lifetime = update.add("attribute", { name: "lifetime" });
80
- const nAge = update.add("div"); update.wire(nAge, "a", newAge); update.wire(nAge, "b", lifetime);
81
-
82
- // kill when normalizedAge >= 1
83
- const dead = update.add("compare", { op: "ge" }); update.wire(dead, "a", nAge); update.wire(dead, "b", [1]);
84
- update.wire(update.add("kill"), "condition", dead);
85
-
86
- // buoyant rise + drift: velocity += (0, 1, 0)*dt ; position += velocity*dt
87
- const vel = update.add("attribute", { name: "velocity" });
88
- const buoy = update.add("const", { value: [0, 1.0, 0, 0] });
89
- const buoyDt = update.add("scale"); update.wire(buoyDt, "v", buoy); update.wire(buoyDt, "s", dt);
90
- const nVel = update.add("add"); update.wire(nVel, "a", vel); update.wire(nVel, "b", buoyDt);
91
- update.wire(update.add("setAttribute", { name: "velocity" }), "value", nVel);
92
- const pos = update.add("attribute", { name: "position" });
93
- const velDt = update.add("scale"); update.wire(velDt, "v", nVel); update.wire(velDt, "s", dt);
94
- const nPos = update.add("add"); update.wire(nPos, "a", pos); update.wire(nPos, "b", velDt);
95
- update.wire(update.add("setAttribute", { name: "position" }), "value", nPos);
96
-
97
- // size + alpha from curves over life (or constants if no curve provided)
98
- if (size_curve !== undefined) {
99
- const sc = update.add("curve", { handle: size_curve }); update.wire(sc, "t", nAge);
100
- update.wire(update.add("setAttribute", { name: "size" }), "value", sc);
101
- }
102
- const color = update.add("attribute", { name: "color" });
103
- if (alpha_curve !== undefined) {
104
- const ac = update.add("curve", { handle: alpha_curve }); update.wire(ac, "t", nAge);
105
- // color.rgb unchanged, color.a = ac : rebuild via select on a swizzle mask is overkill;
106
- // simplest: multiply color by (1,1,1,ac_normalized) approximate by scaling alpha only.
107
- const alphaVec = update.add("mul");
108
- update.wire(alphaVec, "a", color);
109
- update.wire(alphaVec, "b", ac); // broadcasts ac to all lanes (rgb also fades — a warm ember look)
110
- update.wire(update.add("setAttribute", { name: "color" }), "value", alphaVec);
111
- }
112
-
113
- // flipbook frame = floor(normalizedAge * frames)
114
- const framesN = update.add("const", { value: [flipbook_frames, 0, 0, 0] });
115
- const fscaled = update.add("mul"); update.wire(fscaled, "a", nAge); update.wire(fscaled, "b", framesN);
116
- const fframe = update.add("floor"); update.wire(fframe, "x", fscaled);
117
- update.wire(update.add("setAttribute", { name: "frame" }), "value", fframe);
118
- }
119
-
120
- const effect = create_particle_effect({ layout, spawn, update });
121
-
122
- const emitter = {
123
- effect,
124
- spawn_rate: 120,
125
- flags: { blend: EMITTER_BLEND.ADDITIVE, projection: EMITTER_PROJECTION.BILLBOARD },
126
- render: { position: "position", size: "size", color: "color", frame: "frame" },
127
- bounds: { center: [0, 1.5, 0], radius: 2.5 },
128
- // A 4x4 flipbook packed as the whole atlas here; a real atlas would use a sub-rect.
129
- atlas_region: [0, 0, 1, 1],
130
- flipbook: [4, 4],
131
- };
132
-
133
- return { layout, effect, emitter };
134
- }
135
-
136
- /**
137
- * A smoke effect: slow, expanding, alpha-blended, lit, soft-depth billboards. Same authoring
138
- * surface as fire; the differences are all data (blend/lighting/soft flags, rates, colours).
139
- * @returns {{layout: ParticleLayout, effect: object, emitter: object}}
140
- */
141
- export function smoke_effect() {
142
- const { layout, effect } = fire_effect({ flipbook_frames: 8 });
143
- const emitter = {
144
- effect,
145
- spawn_rate: 25,
146
- flags: { blend: EMITTER_BLEND.ALPHA, projection: EMITTER_PROJECTION.BILLBOARD, soft_depth: true, lighting: true, sort: true },
147
- render: { position: "position", size: "size", color: "color", frame: "frame" },
148
- bounds: { center: [0, 3, 0], radius: 5 },
149
- atlas_region: [0, 0, 1, 1],
150
- flipbook: [1, 1],
151
- };
152
- return { layout, effect, emitter };
153
- }
154
-
155
- /**
156
- * Register a fire + smoke plume on a particle system, optionally attaching the fire to a bone.
157
- *
158
- * The per-frame loop the integrator runs:
159
- * ```js
160
- * const spawn_requests = system.update(dt, { is_visible }); // CPU: bone transforms + spawn counts
161
- * system.uploadFrame(spawn_requests); // upload emitters (if dirty) + requests
162
- * graph_particles({ graph, gpu: system.gpuHandles(), frame }); // reset -> emit -> simulate -> indirect
163
- * // caller appends the depth sort + billboard render into its own graph
164
- * ```
165
- *
166
- * @param {import("./GPUParticleSystem.js").GPUParticleSystem} system
167
- * @param {object} [options]
168
- * @param {object} [options.bone] a Node3D to attach the fire emitter to
169
- * @returns {{fire: number, smoke: number}} emitter indices
170
- */
171
- export function register_campfire(system, { bone } = {}) {
172
- const fire = system.addEmitter(fire_effect().emitter);
173
- const smoke = system.addEmitter(smoke_effect().emitter);
174
- if (bone !== undefined) {
175
- system.attachToNode(fire, bone);
176
- system.attachToNode(smoke, bone);
177
- }
178
- system.buildEmitterData();
179
- return { fire, smoke };
180
- }
1
+ import { EMITTER_BLEND, EMITTER_PROJECTION } from "./data/PARTICLE_EMITTER_LAYOUT.js";
2
+ import { NodeGraph } from "../../../core/model/node-graph/NodeGraph.js";
3
+ import { particle_node, particle_wire } from "./graph/particle_graph_authoring.js";
4
+ import { VM_BUILTIN, VM_RANDOM_MODE } from "./isa/ParticleVMISA.js";
5
+ import { ParticleLayout } from "./layout/ParticleLayout.js";
6
+ import { create_particle_effect } from "./runtime/create_particle_effect.js";
7
+
8
+ /**
9
+ * Worked examples of authoring particle effects with the node graph + wiring them into a
10
+ * {@link GPUParticleSystem}. Demonstrates: distinct per-particle lifecycle (no built-in age the
11
+ * effect declares it), AnimationCurve sampling over life, flipbook frames, random emission,
12
+ * additive vs alpha blending, projection modes, soft depth and lighting toggles.
13
+ *
14
+ * `curves` is a `GPUAnimationManager` (or anything with `register_curve(AnimationCurve) -> handle`);
15
+ * `size_curve` / `alpha_curve` are meep `AnimationCurve`s. Both are optional passing `undefined`
16
+ * yields effects that use constant size/alpha instead (curve handle 0, evaluated as 0 by the
17
+ * disabled provider), which is handy for the headless tests.
18
+ */
19
+
20
+ /**
21
+ * A fire effect: buoyant, turbulent, additive-blended camera-facing sprites whose size and opacity
22
+ * follow curves over their lifetime and whose flipbook advances with age.
23
+ *
24
+ * @param {object} [options]
25
+ * @param {number} [options.size_curve] curve handle for size(normalizedAge)
26
+ * @param {number} [options.alpha_curve] curve handle for alpha(normalizedAge)
27
+ * @param {number} [options.flipbook_frames]
28
+ * @returns {{layout: ParticleLayout, effect: object, emitter: object}}
29
+ */
30
+ export function fire_effect({ size_curve, alpha_curve, flipbook_frames = 16 } = {}) {
31
+ const layout = new ParticleLayout([
32
+ { name: "position", components: 3 },
33
+ { name: "velocity", components: 3 },
34
+ { name: "age", components: 1 },
35
+ { name: "lifetime", components: 1 },
36
+ { name: "size", components: 1 },
37
+ { name: "color", components: 4 },
38
+ { name: "frame", components: 1 },
39
+ ]);
40
+
41
+ // --- SPAWN: seed each particle's state ---
42
+ const spawn = new NodeGraph();
43
+ {
44
+ // position = emitter position + a small disk of jitter on the ground
45
+ const ep = particle_node(spawn, "builtin", { id: VM_BUILTIN.EMITTER_POSITION });
46
+ const disk = particle_node(spawn, "random", { mode: VM_RANDOM_MODE.UNIT_DISK });
47
+ const jitter = particle_node(spawn, "scale"); particle_wire(spawn, jitter, "v", disk); particle_wire(spawn, jitter, "s", [0.2]);
48
+ const p0 = particle_node(spawn, "add"); particle_wire(spawn, p0, "a", ep); particle_wire(spawn, p0, "b", jitter);
49
+ particle_wire(spawn, particle_node(spawn, "setAttribute", { name: "position" }), "value", p0);
50
+
51
+ // velocity = upward buoyancy + signed turbulence
52
+ const up = particle_node(spawn, "const", { value: [0, 2.0, 0, 0] });
53
+ const turb = particle_node(spawn, "random", { mode: VM_RANDOM_MODE.UNIFORM_SIGNED });
54
+ const turbS = particle_node(spawn, "scale"); particle_wire(spawn, turbS, "v", turb); particle_wire(spawn, turbS, "s", [0.5]);
55
+ const v0 = particle_node(spawn, "add"); particle_wire(spawn, v0, "a", up); particle_wire(spawn, v0, "b", turbS);
56
+ particle_wire(spawn, particle_node(spawn, "setAttribute", { name: "velocity" }), "value", v0);
57
+
58
+ particle_wire(spawn, particle_node(spawn, "setAttribute", { name: "age" }), "value", [0]);
59
+ // lifetime 1.2..1.8s
60
+ const lr = particle_node(spawn, "random", { mode: VM_RANDOM_MODE.UNIFORM });
61
+ const ls = particle_node(spawn, "mad"); particle_wire(spawn, ls, "a", lr); particle_wire(spawn, ls, "b", [0.6]); particle_wire(spawn, ls, "c", [1.2]);
62
+ particle_wire(spawn, particle_node(spawn, "setAttribute", { name: "lifetime" }), "value", ls);
63
+
64
+ particle_wire(spawn, particle_node(spawn, "setAttribute", { name: "size" }), "value", [0.4]);
65
+ particle_wire(spawn, particle_node(spawn, "setAttribute", { name: "color" }), "value", [1.0, 0.6, 0.2, 1.0]);
66
+ particle_wire(spawn, particle_node(spawn, "setAttribute", { name: "frame" }), "value", [0]);
67
+ }
68
+
69
+ // --- UPDATE: advance + destroy ---
70
+ const update = new NodeGraph();
71
+ {
72
+ const dt = particle_node(update, "builtin", { id: VM_BUILTIN.DELTA_TIME });
73
+
74
+ // age += dt
75
+ const age = particle_node(update, "attribute", { name: "age" });
76
+ const newAge = particle_node(update, "add"); particle_wire(update, newAge, "a", age); particle_wire(update, newAge, "b", dt);
77
+ particle_wire(update, particle_node(update, "setAttribute", { name: "age" }), "value", newAge);
78
+
79
+ // normalizedAge = age / lifetime
80
+ const lifetime = particle_node(update, "attribute", { name: "lifetime" });
81
+ const nAge = particle_node(update, "div"); particle_wire(update, nAge, "a", newAge); particle_wire(update, nAge, "b", lifetime);
82
+
83
+ // kill when normalizedAge >= 1
84
+ const dead = particle_node(update, "compare", { op: "ge" }); particle_wire(update, dead, "a", nAge); particle_wire(update, dead, "b", [1]);
85
+ particle_wire(update, particle_node(update, "kill"), "condition", dead);
86
+
87
+ // buoyant rise + drift: velocity += (0, 1, 0)*dt ; position += velocity*dt
88
+ const vel = particle_node(update, "attribute", { name: "velocity" });
89
+ const buoy = particle_node(update, "const", { value: [0, 1.0, 0, 0] });
90
+ const buoyDt = particle_node(update, "scale"); particle_wire(update, buoyDt, "v", buoy); particle_wire(update, buoyDt, "s", dt);
91
+ const nVel = particle_node(update, "add"); particle_wire(update, nVel, "a", vel); particle_wire(update, nVel, "b", buoyDt);
92
+ particle_wire(update, particle_node(update, "setAttribute", { name: "velocity" }), "value", nVel);
93
+ const pos = particle_node(update, "attribute", { name: "position" });
94
+ const velDt = particle_node(update, "scale"); particle_wire(update, velDt, "v", nVel); particle_wire(update, velDt, "s", dt);
95
+ const nPos = particle_node(update, "add"); particle_wire(update, nPos, "a", pos); particle_wire(update, nPos, "b", velDt);
96
+ particle_wire(update, particle_node(update, "setAttribute", { name: "position" }), "value", nPos);
97
+
98
+ // size + alpha from curves over life (or constants if no curve provided)
99
+ if (size_curve !== undefined) {
100
+ const sc = particle_node(update, "curve", { handle: size_curve }); particle_wire(update, sc, "t", nAge);
101
+ particle_wire(update, particle_node(update, "setAttribute", { name: "size" }), "value", sc);
102
+ }
103
+ const color = particle_node(update, "attribute", { name: "color" });
104
+ if (alpha_curve !== undefined) {
105
+ const ac = particle_node(update, "curve", { handle: alpha_curve }); particle_wire(update, ac, "t", nAge);
106
+ // color.rgb unchanged, color.a = ac : rebuild via select on a swizzle mask is overkill;
107
+ // simplest: multiply color by (1,1,1,ac_normalized) — approximate by scaling alpha only.
108
+ const alphaVec = particle_node(update, "mul");
109
+ particle_wire(update, alphaVec, "a", color);
110
+ particle_wire(update, alphaVec, "b", ac); // broadcasts ac to all lanes (rgb also fades — a warm ember look)
111
+ particle_wire(update, particle_node(update, "setAttribute", { name: "color" }), "value", alphaVec);
112
+ }
113
+
114
+ // flipbook frame = floor(normalizedAge * frames)
115
+ const framesN = particle_node(update, "const", { value: [flipbook_frames, 0, 0, 0] });
116
+ const fscaled = particle_node(update, "mul"); particle_wire(update, fscaled, "a", nAge); particle_wire(update, fscaled, "b", framesN);
117
+ const fframe = particle_node(update, "floor"); particle_wire(update, fframe, "x", fscaled);
118
+ particle_wire(update, particle_node(update, "setAttribute", { name: "frame" }), "value", fframe);
119
+ }
120
+
121
+ const effect = create_particle_effect({ layout, spawn, update });
122
+
123
+ const emitter = {
124
+ effect,
125
+ spawn_rate: 120,
126
+ flags: { blend: EMITTER_BLEND.ADDITIVE, projection: EMITTER_PROJECTION.BILLBOARD },
127
+ render: { position: "position", size: "size", color: "color", frame: "frame" },
128
+ bounds: { center: [0, 1.5, 0], radius: 2.5 },
129
+ // A 4x4 flipbook packed as the whole atlas here; a real atlas would use a sub-rect.
130
+ atlas_region: [0, 0, 1, 1],
131
+ flipbook: [4, 4],
132
+ };
133
+
134
+ return { layout, effect, emitter };
135
+ }
136
+
137
+ /**
138
+ * A smoke effect: slow, expanding, alpha-blended, lit, soft-depth billboards. Same authoring
139
+ * surface as fire; the differences are all data (blend/lighting/soft flags, rates, colours).
140
+ * @returns {{layout: ParticleLayout, effect: object, emitter: object}}
141
+ */
142
+ export function smoke_effect() {
143
+ const { layout, effect } = fire_effect({ flipbook_frames: 8 });
144
+ const emitter = {
145
+ effect,
146
+ spawn_rate: 25,
147
+ flags: { blend: EMITTER_BLEND.ALPHA, projection: EMITTER_PROJECTION.BILLBOARD, soft_depth: true, lighting: true, sort: true },
148
+ render: { position: "position", size: "size", color: "color", frame: "frame" },
149
+ bounds: { center: [0, 3, 0], radius: 5 },
150
+ atlas_region: [0, 0, 1, 1],
151
+ flipbook: [1, 1],
152
+ };
153
+ return { layout, effect, emitter };
154
+ }
155
+
156
+ /**
157
+ * Register a fire + smoke plume on a particle system, optionally attaching the fire to a bone.
158
+ *
159
+ * The per-frame loop the integrator runs:
160
+ * ```js
161
+ * const spawn_requests = system.update(dt, { is_visible }); // CPU: bone transforms + spawn counts
162
+ * system.uploadFrame(spawn_requests); // upload emitters (if dirty) + requests
163
+ * graph_particles({ graph, gpu: system.gpuHandles(), frame }); // reset -> emit -> simulate -> indirect
164
+ * // caller appends the depth sort + billboard render into its own graph
165
+ * ```
166
+ *
167
+ * @param {import("./GPUParticleSystem.js").GPUParticleSystem} system
168
+ * @param {object} [options]
169
+ * @param {object} [options.bone] a Node3D to attach the fire emitter to
170
+ * @returns {{fire: number, smoke: number}} emitter indices
171
+ */
172
+ export function register_campfire(system, { bone } = {}) {
173
+ const fire = system.addEmitter(fire_effect().emitter);
174
+ const smoke = system.addEmitter(smoke_effect().emitter);
175
+ if (bone !== undefined) {
176
+ system.attachToNode(fire, bone);
177
+ system.attachToNode(smoke, bone);
178
+ }
179
+ system.buildEmitterData();
180
+ return { fire, smoke };
181
+ }
@@ -7,14 +7,14 @@
7
7
  *
8
8
  * @param {object} params
9
9
  * @param {import("../layout/ParticleLayout.js").ParticleLayout} params.layout
10
- * @param {import("../graph/ParticleGraph.js").ParticleGraph} [params.spawn]
11
- * @param {import("../graph/ParticleGraph.js").ParticleGraph} [params.update]
10
+ * @param {NodeGraph} [params.spawn]
11
+ * @param {NodeGraph} [params.update]
12
12
  * @returns {{ layout: import("../layout/ParticleLayout.js").ParticleLayout, program: import("../isa/ParticleProgram.js").ParticleProgram }}
13
13
  */
14
14
  export function create_particle_effect({ layout, spawn, update }: {
15
15
  layout: import("../layout/ParticleLayout.js").ParticleLayout;
16
- spawn?: import("../graph/ParticleGraph.js").ParticleGraph;
17
- update?: import("../graph/ParticleGraph.js").ParticleGraph;
16
+ spawn?: NodeGraph;
17
+ update?: NodeGraph;
18
18
  }): {
19
19
  layout: import("../layout/ParticleLayout.js").ParticleLayout;
20
20
  program: import("../isa/ParticleProgram.js").ParticleProgram;
@@ -1 +1 @@
1
- {"version":3,"file":"create_particle_effect.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/runtime/create_particle_effect.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH;IALwE,MAAM,EAAnE,OAAO,6BAA6B,EAAE,cAAc;IACO,KAAK,GAAhE,OAAO,2BAA2B,EAAE,aAAa;IACU,MAAM,GAAjE,OAAO,2BAA2B,EAAE,aAAa;;YACrC,OAAO,6BAA6B,EAAE,cAAc;aAAW,OAAO,2BAA2B,EAAE,eAAe;EAKxI"}
1
+ {"version":3,"file":"create_particle_effect.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/runtime/create_particle_effect.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH;IALwE,MAAM,EAAnE,OAAO,6BAA6B,EAAE,cAAc;IACjC,KAAK;IACL,MAAM;;YACb,OAAO,6BAA6B,EAAE,cAAc;aAAW,OAAO,2BAA2B,EAAE,eAAe;EAKxI"}
@@ -1,19 +1,19 @@
1
- import { compile_particle_effect } from "../graph/compile_particle_graph.js";
2
-
3
- /**
4
- * Compile a particle effect: a per-particle attribute {@link ../layout/ParticleLayout.js} plus SPAWN
5
- * and UPDATE node graphs, lowered to VM bytecode.
6
- *
7
- * The returned value bundles the layout with the compiled program so the {@link ./EmitterRegistry.js}
8
- * can resolve render-attribute names to record offsets and pack the program into the arena.
9
- *
10
- * @param {object} params
11
- * @param {import("../layout/ParticleLayout.js").ParticleLayout} params.layout
12
- * @param {import("../graph/ParticleGraph.js").ParticleGraph} [params.spawn]
13
- * @param {import("../graph/ParticleGraph.js").ParticleGraph} [params.update]
14
- * @returns {{ layout: import("../layout/ParticleLayout.js").ParticleLayout, program: import("../isa/ParticleProgram.js").ParticleProgram }}
15
- */
16
- export function create_particle_effect({ layout, spawn, update }) {
17
- const program = compile_particle_effect({ spawn, update, layout });
18
- return { layout, program };
19
- }
1
+ import { compile_particle_effect } from "../graph/compile_particle_graph.js";
2
+
3
+ /**
4
+ * Compile a particle effect: a per-particle attribute {@link ../layout/ParticleLayout.js} plus SPAWN
5
+ * and UPDATE node graphs, lowered to VM bytecode.
6
+ *
7
+ * The returned value bundles the layout with the compiled program so the {@link ./EmitterRegistry.js}
8
+ * can resolve render-attribute names to record offsets and pack the program into the arena.
9
+ *
10
+ * @param {object} params
11
+ * @param {import("../layout/ParticleLayout.js").ParticleLayout} params.layout
12
+ * @param {NodeGraph} [params.spawn]
13
+ * @param {NodeGraph} [params.update]
14
+ * @returns {{ layout: import("../layout/ParticleLayout.js").ParticleLayout, program: import("../isa/ParticleProgram.js").ParticleProgram }}
15
+ */
16
+ export function create_particle_effect({ layout, spawn, update }) {
17
+ const program = compile_particle_effect({ spawn, update, layout });
18
+ return { layout, program };
19
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"chunk_particle_curve_animation.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/chunk_particle_curve_animation.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;GAgBG;AACH,6CAFU,SAAS,CAO0C;0BA1BnC,oCAAoC"}
1
+ {"version":3,"file":"chunk_particle_curve_animation.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/chunk_particle_curve_animation.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;GAgBG;AACH,6CAFU,SAAS,CAS0C;0BA5BnC,oCAAoC"}
@@ -21,9 +21,11 @@ const CURVE_TABLE = ANIMATION_DATABASE_SPEC.get("animation_curves");
21
21
  *
22
22
  * @type {CodeChunk}
23
23
  */
24
- export const chunk_particle_curve_animation = CodeChunk.from(/* wgsl */ `
24
+ export const chunk_particle_curve_animation = CodeChunk.from(
25
+ /* language=wgsl */
26
+ `
25
27
  fn particle_vm_sample_curve(handle : u32, t : f32) -> f32 {
26
28
  let header = ${CURVE_TABLE.marshalling_method_read}(&particle_curve_database, handle);
27
29
  return animation_curve_evaluate(t, &particle_curve_database, header);
28
30
  }
29
- `, [CURVE_TABLE.chunk_read, chunk_animation_curve_evaluate]);
31
+ `, [CURVE_TABLE.chunk_read, chunk_animation_curve_evaluate]);
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Pluggable lighting hook for the billboard fragment shader. `particle_apply_lighting(color, world,
3
- * normal)` returns the lit colour; the default is a pass-through (unlit / emissive).
2
+ * Pluggable lighting hook for the billboard fragment shader. `particle_apply_lighting(color, world, normal)`
3
+ * returns the lit colour; the default is a pass-through (unlit / emissive).
4
4
  *
5
- * The production feature swaps this for a chunk that binds {@link
6
- * ../../deferred/SHADING_LIGHT_RESOURCE_GROUP.js} and calls `shade_standard_material_direct` + IBL —
5
+ * The production feature swaps this for a chunk that binds
6
+ * {@link ../../deferred/SHADING_LIGHT_RESOURCE_GROUP.js} and calls `shade_standard_material_direct` + IBL —
7
7
  * i.e. billboards reuse the exact same lighting path as opaque surfaces when an emitter opts in via
8
8
  * its `lighting` flag. Keeping it a hook means unlit emitters compile without pulling in the whole
9
9
  * clustered-lighting resource group.
@@ -1 +1 @@
1
- {"version":3,"file":"chunk_particle_lighting.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/chunk_particle_lighting.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,4CAFU,SAAS,CAMhB;0BAlBuB,oCAAoC"}
1
+ {"version":3,"file":"chunk_particle_lighting.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/chunk_particle_lighting.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,4CAFU,SAAS,CAQhB;0BApBuB,oCAAoC"}
@@ -1,18 +1,20 @@
1
1
  import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
2
2
 
3
3
  /**
4
- * Pluggable lighting hook for the billboard fragment shader. `particle_apply_lighting(color, world,
5
- * normal)` returns the lit colour; the default is a pass-through (unlit / emissive).
4
+ * Pluggable lighting hook for the billboard fragment shader. `particle_apply_lighting(color, world, normal)`
5
+ * returns the lit colour; the default is a pass-through (unlit / emissive).
6
6
  *
7
- * The production feature swaps this for a chunk that binds {@link
8
- * ../../deferred/SHADING_LIGHT_RESOURCE_GROUP.js} and calls `shade_standard_material_direct` + IBL —
7
+ * The production feature swaps this for a chunk that binds
8
+ * {@link ../../deferred/SHADING_LIGHT_RESOURCE_GROUP.js} and calls `shade_standard_material_direct` + IBL —
9
9
  * i.e. billboards reuse the exact same lighting path as opaque surfaces when an emitter opts in via
10
10
  * its `lighting` flag. Keeping it a hook means unlit emitters compile without pulling in the whole
11
11
  * clustered-lighting resource group.
12
12
  *
13
13
  * @type {CodeChunk}
14
14
  */
15
- export const chunk_particle_lighting_unlit = CodeChunk.from(/* wgsl */ `
15
+ export const chunk_particle_lighting_unlit = CodeChunk.from(
16
+ /* language=wgsl */
17
+ `
16
18
  fn particle_apply_lighting(color : vec4<f32>, world_position : vec3<f32>, normal : vec3<f32>) -> vec4<f32> {
17
19
  return color;
18
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"chunk_particle_render_math.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/chunk_particle_render_math.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,yCAFU,SAAS,CAyFhB;0BAzGuB,oCAAoC"}
1
+ {"version":3,"file":"chunk_particle_render_math.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/chunk_particle_render_math.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,yCAFU,SAAS,CA2FhB;0BA3GuB,oCAAoC"}
@@ -16,7 +16,9 @@ import { EMITTER_BLEND, EMITTER_PROJECTION } from "../data/PARTICLE_EMITTER_LAYO
16
16
  *
17
17
  * @type {CodeChunk}
18
18
  */
19
- export const chunk_particle_render_math = CodeChunk.from(/* wgsl */ `
19
+ export const chunk_particle_render_math = CodeChunk.from(
20
+ /* language=wgsl */
21
+ `
20
22
  const PARTICLE_PROJ_BILLBOARD : u32 = ${EMITTER_PROJECTION.BILLBOARD}u;
21
23
  const PARTICLE_PROJ_STRETCHED : u32 = ${EMITTER_PROJECTION.STRETCHED}u;
22
24
  const PARTICLE_PROJ_HORIZONTAL : u32 = ${EMITTER_PROJECTION.HORIZONTAL}u;
@@ -1 +1 @@
1
- {"version":3,"file":"shader_particle_emit.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/shader_particle_emit.js"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;GAYG;AAEH,2CAHU,aAAa,CAGwB;AA6F/C,iDAIG;8BAhI2B,+BAA+B"}
1
+ {"version":3,"file":"shader_particle_emit.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/shader_particle_emit.js"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;GAYG;AAEH,2CAHU,aAAa,CAGwB;AA+F/C,iDAIG;8BAlI2B,+BAA+B"}
@@ -64,7 +64,9 @@ resources.createGroup()
64
64
  .addStorageBuffer("alive_out", WebGPUArray.u32, true)
65
65
  .addStorageBuffer("dead", WebGPUArray.u32, true)
66
66
  .addStorageBuffer("counters", WebGPUArray.from(atomic_u32), true);
67
- const body = CodeChunk.from(/* wgsl */ `
67
+ const body = CodeChunk.from(
68
+ /* language=wgsl */
69
+ `
68
70
  const PARTICLE_RECORD_WORDS : u32 = ${PARTICLE_RECORD_WORD_COUNT}u;
69
71
  const PARTICLE_USER_WORD_START : u32 = ${PARTICLE_RECORD_USER_WORD_START}u;
70
72
  const PARTICLE_HEADER_EMITTER : u32 = ${PARTICLE_RECORD_HEADER_EMITTER}u;
@@ -1 +1 @@
1
- {"version":3,"file":"shader_particle_finalize.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/shader_particle_finalize.js"],"names":[],"mappings":"AA4BA,2DAWG;AAaH,2DAwBG;;8BA1E2B,+BAA+B;iDAIZ,+BAA+B"}
1
+ {"version":3,"file":"shader_particle_finalize.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/shader_particle_finalize.js"],"names":[],"mappings":"AA4BA,2DAYG;AAaH,2DA0BG;;8BA7E2B,+BAA+B;iDAIZ,+BAA+B"}
@@ -29,7 +29,8 @@ reset_resources.createGroup().addStorageBuffer("counters", WebGPUArray.from(atom
29
29
  export const shader_particle_reset_counters = ComputeShader.from({
30
30
  label: "Particle Reset Counters",
31
31
  resources: reset_resources,
32
- body: CodeChunk.from(/* wgsl */ `
32
+ body: CodeChunk.from(
33
+ /* language=wgsl */ `
33
34
  @compute @workgroup_size(1, 1, 1)
34
35
  fn main() {
35
36
  atomicStore(&counters[${PARTICLE_COUNTER.ALIVE}u], 0u);
@@ -53,7 +54,9 @@ indirect_resources.createGroup()
53
54
  export const shader_particle_build_indirect = ComputeShader.from({
54
55
  label: "Particle Build Indirect",
55
56
  resources: indirect_resources,
56
- body: CodeChunk.from(/* wgsl */ `
57
+ body: CodeChunk.from(
58
+ /* language=wgsl */
59
+ `
57
60
  @compute @workgroup_size(1, 1, 1)
58
61
  fn main() {
59
62
  let alive = atomicLoad(&counters[${PARTICLE_COUNTER.ALIVE}u]);
@@ -1 +1 @@
1
- {"version":3,"file":"shader_particle_render.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/shader_particle_render.js"],"names":[],"mappings":"AA6JA;;;;;;;;;GASG;AACH;IAP4B,YAAY,GAA7B,MAAM;IACgB,YAAY,GAAlC,MAAM,GAAC,IAAI;IAEkB,aAAa;IAExC,wBAAwB,CAqBpC;;;;;;;;;;;;;;;;AAED,yFAAyF;AACzF,uEAAsD;yCA7Lb,iEAAiE;4CAO9D,sDAAsD"}
1
+ {"version":3,"file":"shader_particle_render.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/shaders/shader_particle_render.js"],"names":[],"mappings":"AA8JA;;;;;;;;;GASG;AACH;IAP4B,YAAY,GAA7B,MAAM;IACgB,YAAY,GAAlC,MAAM,GAAC,IAAI;IAEkB,aAAa;IAExC,wBAAwB,CA2BpC;;;;;;;;;;;;;;;;AAED,yFAAyF;AACzF,uEAAsD;yCApMb,iEAAiE;4CAO9D,sDAAsD"}