@woosh/meep-engine 3.14.4 → 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 (89) 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/network/orchestrator/NetworkPeer.d.ts +56 -8
  6. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  7. package/src/engine/network/orchestrator/NetworkPeer.js +263 -67
  8. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +6 -1
  9. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
  10. package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +6 -0
  11. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +13 -7
  12. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
  13. package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +103 -138
  14. package/src/engine/network/replication/Replicator.d.ts +29 -6
  15. package/src/engine/network/replication/Replicator.d.ts.map +1 -1
  16. package/src/engine/network/replication/Replicator.js +265 -108
  17. package/src/engine/network/sim/ActionLog.d.ts +2 -0
  18. package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
  19. package/src/engine/network/sim/ActionLog.js +2 -0
  20. package/src/engine/network/sim/ActionRecordCursor.d.ts +82 -0
  21. package/src/engine/network/sim/ActionRecordCursor.d.ts.map +1 -0
  22. package/src/engine/network/sim/ActionRecordCursor.js +120 -0
  23. package/src/engine/network/sim/RewindEngine.d.ts.map +1 -1
  24. package/src/engine/network/sim/RewindEngine.js +13 -29
  25. package/src/engine/network/sim/SimActionExecutor.d.ts +2 -3
  26. package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
  27. package/src/engine/network/sim/SimActionExecutor.js +10 -11
  28. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  29. package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -1
  30. package/src/shade/playground/particle_system/particle_scene.js +38 -37
  31. package/src/shade/playground/skinned_mesh_soup/README.md +39 -18
  32. package/src/shade/playground/skinned_mesh_soup/index.html +9 -6
  33. package/src/shade/playground/skinned_mesh_soup/main.d.ts.map +1 -1
  34. package/src/shade/playground/skinned_mesh_soup/main.js +16 -13
  35. package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts +8 -2
  36. package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts.map +1 -1
  37. package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.js +23 -4
  38. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  39. package/src/shade/renderer/animation/GPUAnimationManager.js +1958 -1796
  40. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +357 -0
  41. package/src/shade/renderer/animation/skin_bind_transform.d.ts +25 -0
  42. package/src/shade/renderer/animation/skin_bind_transform.d.ts.map +1 -0
  43. package/src/shade/renderer/animation/skin_bind_transform.js +33 -0
  44. package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts +67 -0
  45. package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts.map +1 -0
  46. package/src/shade/renderer/animation/skin_clip_scale_conflicts.js +199 -0
  47. package/src/shade/renderer/animation/skin_frame_defect.d.ts +34 -0
  48. package/src/shade/renderer/animation/skin_frame_defect.d.ts.map +1 -0
  49. package/src/shade/renderer/animation/skin_frame_defect.js +132 -0
  50. package/src/shade/renderer/animation/skin_joint_matrix.d.ts +24 -0
  51. package/src/shade/renderer/animation/skin_joint_matrix.d.ts.map +1 -0
  52. package/src/shade/renderer/animation/skin_joint_matrix.js +46 -0
  53. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts +70 -0
  54. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts.map +1 -0
  55. package/src/shade/renderer/animation/skin_normalize_mesh_frame.js +216 -0
  56. package/src/shade/renderer/animation/skin_test_fixtures.d.ts +86 -0
  57. package/src/shade/renderer/animation/skin_test_fixtures.d.ts.map +1 -0
  58. package/src/shade/renderer/animation/skin_test_fixtures.js +250 -0
  59. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.d.ts +18 -4
  60. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.d.ts.map +1 -1
  61. package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +18 -4
  62. package/src/shade/renderer/particles/DESIGN.md +5 -5
  63. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +43 -0
  64. package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -0
  65. package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +44 -0
  66. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +33 -59
  67. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
  68. package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +185 -118
  69. package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts +15 -10
  70. package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
  71. package/src/shade/renderer/particles/graph/compile_particle_graph.js +205 -170
  72. package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts +44 -0
  73. package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts.map +1 -0
  74. package/src/shade/renderer/particles/graph/particle_graph_authoring.js +108 -0
  75. package/src/shade/renderer/particles/particle_prototype.d.ts.map +1 -1
  76. package/src/shade/renderer/particles/particle_prototype.js +181 -180
  77. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +4 -4
  78. package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
  79. package/src/shade/renderer/particles/runtime/create_particle_effect.js +19 -19
  80. package/src/shade/renderer/particles/vm/chunk_particle_vm.js +1 -1
  81. package/src/shade/renderer/scene/SkinnedMesh.d.ts +11 -0
  82. package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +1 -1
  83. package/src/shade/renderer/scene/SkinnedMesh.js +11 -0
  84. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.d.ts +33 -3
  85. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.d.ts.map +1 -1
  86. package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.js +58 -37
  87. package/src/shade/renderer/particles/graph/ParticleGraph.d.ts +0 -54
  88. package/src/shade/renderer/particles/graph/ParticleGraph.d.ts.map +0 -1
  89. package/src/shade/renderer/particles/graph/ParticleGraph.js +0 -99
@@ -0,0 +1,43 @@
1
+ /**
2
+ * A particle node type: a {@link NodeDescription} that additionally knows how to lower an instance
3
+ * of itself into VM instructions.
4
+ *
5
+ * Ports carry all the structure the compiler needs — input ports name the operands, the presence of
6
+ * an output port says whether the node produces a value — so nothing about the graph's shape is
7
+ * duplicated outside the description.
8
+ */
9
+ export class ParticleNodeDescription extends NodeDescription {
10
+ /**
11
+ * Lower one instance of this node into the target instruction stream.
12
+ *
13
+ * `context` provides:
14
+ * - `context.in(port_name)` → the register holding that input's value (resolved by the compiler),
15
+ * - `context.out` → the output register (value-producing nodes only, `undefined` for sinks),
16
+ * - `context.parameters` → the {@link NodeInstance}'s parameters,
17
+ * - `context.layout` → the {@link ../layout/ParticleLayout.js} for attribute offsets,
18
+ * - `context.asm` → the {@link ../isa/ParticleAssembler.js} for interning constants.
19
+ *
20
+ * @type {function(InstructionStream, {in: function(string):number, out: number|undefined, parameters: object, layout: ParticleLayout, asm: ParticleAssembler}):void}
21
+ */
22
+ emit: (arg0: InstructionStream, arg1: {
23
+ in: (arg0: string) => number;
24
+ out: number | undefined;
25
+ parameters: object;
26
+ layout: ParticleLayout;
27
+ asm: ParticleAssembler;
28
+ }) => void;
29
+ /**
30
+ * A node with no output ports produces no value, so nothing can consume it; the only reason to
31
+ * emit it is its side effect (an attribute write, a kill). Such nodes are the roots the compiler
32
+ * traverses from.
33
+ * @returns {boolean}
34
+ */
35
+ get is_sink(): boolean;
36
+ /**
37
+ * @readonly
38
+ * @type {boolean}
39
+ */
40
+ readonly isParticleNodeDescription: boolean;
41
+ }
42
+ import { NodeDescription } from "../../../../core/model/node-graph/node/NodeDescription.js";
43
+ //# sourceMappingURL=ParticleNodeDescription.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParticleNodeDescription.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/graph/ParticleNodeDescription.js"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AACH;IAEI;;;;;;;;;;;OAWG;IACH,sCAFsC;QAAC,EAAE,SAAW,MAAM,KAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAC,SAAS,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,iBAAiB;QAAC,GAAG,oBAAmB;KAAC,KAAE,IAAI,CAExJ;IAEZ;;;;;OAKG;IACH,uBAEC;IAGL;;;OAGG;IACH,oCAFU,OAAO,CAE0C;CAN1D;gCApC+B,2DAA2D"}
@@ -0,0 +1,44 @@
1
+ import { noop } from "../../../../core/function/noop.js";
2
+ import { NodeDescription } from "../../../../core/model/node-graph/node/NodeDescription.js";
3
+ import { PortDirection } from "../../../../core/model/node-graph/node/PortDirection.js";
4
+
5
+ /**
6
+ * A particle node type: a {@link NodeDescription} that additionally knows how to lower an instance
7
+ * of itself into VM instructions.
8
+ *
9
+ * Ports carry all the structure the compiler needs — input ports name the operands, the presence of
10
+ * an output port says whether the node produces a value — so nothing about the graph's shape is
11
+ * duplicated outside the description.
12
+ */
13
+ export class ParticleNodeDescription extends NodeDescription {
14
+
15
+ /**
16
+ * Lower one instance of this node into the target instruction stream.
17
+ *
18
+ * `context` provides:
19
+ * - `context.in(port_name)` → the register holding that input's value (resolved by the compiler),
20
+ * - `context.out` → the output register (value-producing nodes only, `undefined` for sinks),
21
+ * - `context.parameters` → the {@link NodeInstance}'s parameters,
22
+ * - `context.layout` → the {@link ../layout/ParticleLayout.js} for attribute offsets,
23
+ * - `context.asm` → the {@link ../isa/ParticleAssembler.js} for interning constants.
24
+ *
25
+ * @type {function(InstructionStream, {in: function(string):number, out: number|undefined, parameters: object, layout: ParticleLayout, asm: ParticleAssembler}):void}
26
+ */
27
+ emit = noop;
28
+
29
+ /**
30
+ * A node with no output ports produces no value, so nothing can consume it; the only reason to
31
+ * emit it is its side effect (an attribute write, a kill). Such nodes are the roots the compiler
32
+ * traverses from.
33
+ * @returns {boolean}
34
+ */
35
+ get is_sink() {
36
+ return !this.hasPortsByDirection(PortDirection.Out);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * @readonly
42
+ * @type {boolean}
43
+ */
44
+ ParticleNodeDescription.prototype.isParticleNodeDescription = true;
@@ -1,60 +1,34 @@
1
- export namespace PARTICLE_NODE_TYPES {
2
- export namespace _const {
3
- export { inputs };
4
- export let produces_value: boolean;
5
- export let is_sink: boolean;
6
- export { emit };
7
- }
8
- export { _const as const };
9
- export namespace builtin { }
10
- export namespace attribute { }
11
- export namespace random { }
12
- export namespace setAttribute {
13
- export { inputs };
14
- let produces_value_1: boolean;
15
- export { produces_value_1 as produces_value };
16
- let is_sink_1: boolean;
17
- export { is_sink_1 as is_sink };
18
- export { emit };
19
- }
20
- export namespace kill { }
21
- export namespace add {
22
- export { inputs };
23
- let produces_value_2: boolean;
24
- export { produces_value_2 as produces_value };
25
- let is_sink_2: boolean;
26
- export { is_sink_2 as is_sink };
27
- export { emit };
28
- }
29
- export namespace sub { }
30
- export namespace mul { }
31
- export namespace div { }
32
- export namespace min { }
33
- export namespace max { }
34
- export namespace pow { }
35
- export namespace step { }
36
- export namespace mad { }
37
- export namespace mix { }
38
- export namespace clamp { }
39
- export namespace smoothstep { }
40
- export namespace scale { }
41
- export namespace neg { }
42
- export namespace abs { }
43
- export namespace floor { }
44
- export namespace fract { }
45
- export namespace sqrt { }
46
- export namespace sin { }
47
- export namespace cos { }
48
- export namespace exp { }
49
- export namespace log { }
50
- export namespace normalize { }
51
- export namespace length { }
52
- export namespace dot { }
53
- export namespace cross { }
54
- export namespace distance { }
55
- export namespace swizzle { }
56
- export namespace compare { }
57
- export namespace select { }
58
- export namespace curve { }
59
- }
1
+ /**
2
+ * Look up a node type by name.
3
+ * @param {string} name
4
+ * @returns {ParticleNodeDescription}
5
+ * @throws if no such node type exists
6
+ */
7
+ export function get_particle_node_description(name: string): ParticleNodeDescription;
8
+ /**
9
+ * Standard particle-node library, expressed as {@link ParticleNodeDescription}s in a
10
+ * {@link NodeRegistry}. Nodes are value-producing operations (constants, builtins, attribute reads,
11
+ * math, curves, random) or sinks (attribute writes, kill).
12
+ *
13
+ * These lower to the same opcodes the JS reference + WGSL interpreter agree on, so any effect
14
+ * authored here is executable and testable end-to-end on the CPU before it ever hits the GPU.
15
+ */
16
+ /**
17
+ * Every value the particle VM computes lives in a `vec4f` register, so a single data type describes
18
+ * every port in the library: any output may drive any input.
19
+ * @type {DataType}
20
+ */
21
+ export const PARTICLE_VALUE_TYPE: DataType;
22
+ /**
23
+ * Name of the single output port carried by every value-producing node.
24
+ * @type {string}
25
+ */
26
+ export const PARTICLE_NODE_OUTPUT_PORT: string;
27
+ /**
28
+ * @type {NodeRegistry}
29
+ */
30
+ export const PARTICLE_NODE_REGISTRY: NodeRegistry;
31
+ import { ParticleNodeDescription } from "./ParticleNodeDescription.js";
32
+ import { DataType } from "../../../../core/model/node-graph/type/DataType.js";
33
+ import { NodeRegistry } from "../../../../core/model/node-graph/node/NodeRegistry.js";
60
34
  //# sourceMappingURL=ParticleNodeRegistry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ParticleNodeRegistry.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/graph/ParticleNodeRegistry.js"],"names":[],"mappings":""}
1
+ {"version":3,"file":"ParticleNodeRegistry.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/graph/ParticleNodeRegistry.js"],"names":[],"mappings":"AA0KA;;;;;GAKG;AACH,oDAJW,MAAM,GACJ,uBAAuB,CAWnC;AAlLD;;;;;;;GAOG;AAEH;;;;GAIG;AACH,kCAFU,QAAQ,CAE0C;AAE5D;;;GAGG;AACH,wCAFU,MAAM,CAE+B;AAE/C;;GAEG;AACH,qCAFU,YAAY,CAEmC;wCA3BjB,8BAA8B;yBAF7C,oDAAoD;6BAFhD,wDAAwD"}
@@ -1,118 +1,185 @@
1
- import { pack_swizzle, VM_OP } from "../isa/ParticleVMISA.js";
2
-
3
- /**
4
- * Standard particle-node library. Each entry describes a node type's input ports and whether it
5
- * produces a value or is a sink, plus an `emit(stream, ctx)` that lowers it to VM instructions.
6
- *
7
- * `ctx` provides:
8
- * - `ctx.in(port)` the register holding that input's value (resolved by the compiler),
9
- * - `ctx.out` the output register (value-producing nodes only),
10
- * - `ctx.params` the node's static params,
11
- * - `ctx.layout` → the {@link ../layout/ParticleLayout.js} for attribute offsets,
12
- * - `ctx.asm` the {@link ../isa/ParticleAssembler.js} for interning constants.
13
- *
14
- * These lower to the same opcodes the JS reference + WGSL interpreter agree on, so any effect
15
- * authored here is executable and testable end-to-end on the CPU before it ever hits the GPU.
16
- */
17
-
18
- /** value-producing node with no inputs */
19
- function source(inputs, emit) {
20
- return { inputs, produces_value: true, is_sink: false, emit };
21
- }
22
-
23
- /** value-producing node */
24
- function op(inputs, emit) {
25
- return { inputs, produces_value: true, is_sink: false, emit };
26
- }
27
-
28
- /** side-effecting sink (no output) */
29
- function sink(inputs, emit) {
30
- return { inputs, produces_value: false, is_sink: true, emit };
31
- }
32
-
33
- function unary(opcode) {
34
- return op(["x"], (s, c) => s.emit(opcode, c.out, c.in("x")));
35
- }
36
-
37
- function binary(opcode) {
38
- return op(["a", "b"], (s, c) => s.emit(opcode, c.out, c.in("a"), c.in("b")));
39
- }
40
-
41
- const COMPARE_OPCODES = {
42
- lt: VM_OP.CMP_LT,
43
- le: VM_OP.CMP_LE,
44
- gt: VM_OP.CMP_GT,
45
- ge: VM_OP.CMP_GE,
46
- eq: VM_OP.CMP_EQ,
47
- };
48
-
49
- export const PARTICLE_NODE_TYPES = {
50
- // --- sources ---
51
- const: source([], (s, c) => {
52
- const v = c.params.value ?? [0, 0, 0, 0];
53
- s.loadConst(c.out, c.asm.constant(v[0] ?? 0, v[1] ?? 0, v[2] ?? 0, v[3] ?? 0));
54
- }),
55
- builtin: source([], (s, c) => s.loadBuiltin(c.out, c.params.id)),
56
- attribute: source([], (s, c) => {
57
- const a = c.layout.get(c.params.name);
58
- s.loadAttr(c.out, a.offset, a.components);
59
- }),
60
- random: source([], (s, c) => s.random(c.out, c.params.mode)),
61
-
62
- // --- sinks ---
63
- setAttribute: sink(["value"], (s, c) => {
64
- const a = c.layout.get(c.params.name);
65
- s.storeAttr(c.in("value"), a.offset, a.components);
66
- }),
67
- kill: sink(["condition"], (s, c) => s.kill(c.in("condition"))),
68
-
69
- // --- arithmetic (component-wise) ---
70
- add: binary(VM_OP.ADD),
71
- sub: binary(VM_OP.SUB),
72
- mul: binary(VM_OP.MUL),
73
- div: binary(VM_OP.DIV),
74
- min: binary(VM_OP.MIN),
75
- max: binary(VM_OP.MAX),
76
- pow: binary(VM_OP.POW),
77
- step: binary(VM_OP.STEP),
78
- mad: op(["a", "b", "c"], (s, c) => s.emit(VM_OP.MAD, c.out, c.in("a"), c.in("b"), c.in("c"))),
79
- mix: op(["a", "b", "t"], (s, c) => s.emit(VM_OP.MIX, c.out, c.in("a"), c.in("b"), c.in("t"))),
80
- clamp: op(["x", "lo", "hi"], (s, c) => s.emit(VM_OP.CLAMP, c.out, c.in("x"), c.in("lo"), c.in("hi"))),
81
- smoothstep: op(["e0", "e1", "x"], (s, c) => s.emit(VM_OP.SMOOTHSTEP, c.out, c.in("e0"), c.in("e1"), c.in("x"))),
82
- scale: op(["v", "s"], (s, c) => s.emit(VM_OP.SCALE, c.out, c.in("v"), c.in("s"))),
83
-
84
- // --- unary math ---
85
- neg: unary(VM_OP.NEG),
86
- abs: unary(VM_OP.ABS),
87
- floor: unary(VM_OP.FLOOR),
88
- fract: unary(VM_OP.FRACT),
89
- sqrt: unary(VM_OP.SQRT),
90
- sin: unary(VM_OP.SIN),
91
- cos: unary(VM_OP.COS),
92
- exp: unary(VM_OP.EXP),
93
- log: unary(VM_OP.LOG),
94
- normalize: unary(VM_OP.NORMALIZE),
95
- length: unary(VM_OP.LENGTH),
96
-
97
- // --- vector ---
98
- dot: binary(VM_OP.DOT),
99
- cross: binary(VM_OP.CROSS),
100
- distance: binary(VM_OP.DISTANCE),
101
- swizzle: op(["x"], (s, c) => {
102
- const p = c.params.pattern ?? [0, 1, 2, 3];
103
- s.emit(VM_OP.SWIZZLE, c.out, c.in("x"), pack_swizzle(p[0], p[1], p[2], p[3]));
104
- }),
105
-
106
- // --- logic ---
107
- compare: op(["a", "b"], (s, c) => {
108
- const opcode = COMPARE_OPCODES[c.params.op];
109
- if (opcode === undefined) {
110
- throw new Error(`Unknown compare op '${c.params.op}'`);
111
- }
112
- s.emit(opcode, c.out, c.in("a"), c.in("b"));
113
- }),
114
- select: op(["a", "b", "condition"], (s, c) => s.emit(VM_OP.SELECT, c.out, c.in("a"), c.in("b"), c.in("condition"))),
115
-
116
- // --- curves ---
117
- curve: op(["t"], (s, c) => s.curve(c.out, c.params.handle, c.in("t"))),
118
- };
1
+ import { NodeRegistry } from "../../../../core/model/node-graph/node/NodeRegistry.js";
2
+ import { PortDirection } from "../../../../core/model/node-graph/node/PortDirection.js";
3
+ import { DataType } from "../../../../core/model/node-graph/type/DataType.js";
4
+ import { pack_swizzle, VM_OP } from "../isa/ParticleVMISA.js";
5
+ import { ParticleNodeDescription } from "./ParticleNodeDescription.js";
6
+
7
+ /**
8
+ * Standard particle-node library, expressed as {@link ParticleNodeDescription}s in a
9
+ * {@link NodeRegistry}. Nodes are value-producing operations (constants, builtins, attribute reads,
10
+ * math, curves, random) or sinks (attribute writes, kill).
11
+ *
12
+ * These lower to the same opcodes the JS reference + WGSL interpreter agree on, so any effect
13
+ * authored here is executable and testable end-to-end on the CPU before it ever hits the GPU.
14
+ */
15
+
16
+ /**
17
+ * Every value the particle VM computes lives in a `vec4f` register, so a single data type describes
18
+ * every port in the library: any output may drive any input.
19
+ * @type {DataType}
20
+ */
21
+ export const PARTICLE_VALUE_TYPE = DataType.from(0, "vec4");
22
+
23
+ /**
24
+ * Name of the single output port carried by every value-producing node.
25
+ * @type {string}
26
+ */
27
+ export const PARTICLE_NODE_OUTPUT_PORT = "out";
28
+
29
+ /**
30
+ * @type {NodeRegistry}
31
+ */
32
+ export const PARTICLE_NODE_REGISTRY = new NodeRegistry();
33
+
34
+ /**
35
+ * Node descriptions are addressed by name while authoring; the registry itself is keyed by the
36
+ * numeric IDs assigned below.
37
+ *
38
+ * IDs are handed out in declaration order rather than by {@link NodeDescription}'s global counter,
39
+ * so they do not drift with unrelated node usage elsewhere in the engine. Appending new node types
40
+ * at the end of the library is therefore safe; reordering or removing existing ones renumbers every
41
+ * node that follows, which would invalidate already-serialized graphs.
42
+ *
43
+ * @type {Map<string, ParticleNodeDescription>}
44
+ */
45
+ const descriptions_by_name = new Map();
46
+
47
+ /**
48
+ * @param {string} name
49
+ * @param {string[]} inputs input port names, in operand order
50
+ * @param {function} emit see {@link ParticleNodeDescription#emit}
51
+ * @param {object} [options]
52
+ * @param {boolean} [options.produces_value] when false the node is a sink and gets no output port
53
+ * @returns {ParticleNodeDescription}
54
+ */
55
+ function define(name, inputs, emit, { produces_value = true } = {}) {
56
+ if (descriptions_by_name.has(name)) {
57
+ throw new Error(`Duplicate particle node type '${name}'`);
58
+ }
59
+
60
+ const description = new ParticleNodeDescription();
61
+
62
+ description.name = name;
63
+ description.id = descriptions_by_name.size;
64
+
65
+ for (const input of inputs) {
66
+ description.createPort(PARTICLE_VALUE_TYPE, input, PortDirection.In);
67
+ }
68
+
69
+ if (produces_value) {
70
+ description.createPort(PARTICLE_VALUE_TYPE, PARTICLE_NODE_OUTPUT_PORT, PortDirection.Out);
71
+ }
72
+
73
+ description.emit = emit;
74
+
75
+ PARTICLE_NODE_REGISTRY.addNode(description);
76
+ descriptions_by_name.set(name, description);
77
+
78
+ return description;
79
+ }
80
+
81
+ /** side-effecting sink (no output) */
82
+ function sink(name, inputs, emit) {
83
+ return define(name, inputs, emit, { produces_value: false });
84
+ }
85
+
86
+ function unary(name, opcode) {
87
+ return define(name, ["x"], (s, c) => s.emit(opcode, c.out, c.in("x")));
88
+ }
89
+
90
+ function binary(name, opcode) {
91
+ return define(name, ["a", "b"], (s, c) => s.emit(opcode, c.out, c.in("a"), c.in("b")));
92
+ }
93
+
94
+ const COMPARE_OPCODES = {
95
+ lt: VM_OP.CMP_LT,
96
+ le: VM_OP.CMP_LE,
97
+ gt: VM_OP.CMP_GT,
98
+ ge: VM_OP.CMP_GE,
99
+ eq: VM_OP.CMP_EQ,
100
+ };
101
+
102
+ // --- sources ---
103
+ define("const", [], (s, c) => {
104
+ const v = c.parameters.value ?? [0, 0, 0, 0];
105
+ s.loadConst(c.out, c.asm.constant(v[0] ?? 0, v[1] ?? 0, v[2] ?? 0, v[3] ?? 0));
106
+ });
107
+ define("builtin", [], (s, c) => s.loadBuiltin(c.out, c.parameters.id));
108
+ define("attribute", [], (s, c) => {
109
+ const a = c.layout.get(c.parameters.name);
110
+ s.loadAttr(c.out, a.offset, a.components);
111
+ });
112
+ define("random", [], (s, c) => s.random(c.out, c.parameters.mode));
113
+
114
+ // --- sinks ---
115
+ sink("setAttribute", ["value"], (s, c) => {
116
+ const a = c.layout.get(c.parameters.name);
117
+ s.storeAttr(c.in("value"), a.offset, a.components);
118
+ });
119
+ sink("kill", ["condition"], (s, c) => s.kill(c.in("condition")));
120
+
121
+ // --- arithmetic (component-wise) ---
122
+ binary("add", VM_OP.ADD);
123
+ binary("sub", VM_OP.SUB);
124
+ binary("mul", VM_OP.MUL);
125
+ binary("div", VM_OP.DIV);
126
+ binary("min", VM_OP.MIN);
127
+ binary("max", VM_OP.MAX);
128
+ binary("pow", VM_OP.POW);
129
+ binary("step", VM_OP.STEP);
130
+ define("mad", ["a", "b", "c"], (s, c) => s.emit(VM_OP.MAD, c.out, c.in("a"), c.in("b"), c.in("c")));
131
+ define("mix", ["a", "b", "t"], (s, c) => s.emit(VM_OP.MIX, c.out, c.in("a"), c.in("b"), c.in("t")));
132
+ define("clamp", ["x", "lo", "hi"], (s, c) => s.emit(VM_OP.CLAMP, c.out, c.in("x"), c.in("lo"), c.in("hi")));
133
+ define("smoothstep", ["e0", "e1", "x"], (s, c) => s.emit(VM_OP.SMOOTHSTEP, c.out, c.in("e0"), c.in("e1"), c.in("x")));
134
+ define("scale", ["v", "s"], (s, c) => s.emit(VM_OP.SCALE, c.out, c.in("v"), c.in("s")));
135
+
136
+ // --- unary math ---
137
+ unary("neg", VM_OP.NEG);
138
+ unary("abs", VM_OP.ABS);
139
+ unary("floor", VM_OP.FLOOR);
140
+ unary("fract", VM_OP.FRACT);
141
+ unary("sqrt", VM_OP.SQRT);
142
+ unary("sin", VM_OP.SIN);
143
+ unary("cos", VM_OP.COS);
144
+ unary("exp", VM_OP.EXP);
145
+ unary("log", VM_OP.LOG);
146
+ unary("normalize", VM_OP.NORMALIZE);
147
+ unary("length", VM_OP.LENGTH);
148
+
149
+ // --- vector ---
150
+ binary("dot", VM_OP.DOT);
151
+ binary("cross", VM_OP.CROSS);
152
+ binary("distance", VM_OP.DISTANCE);
153
+ define("swizzle", ["x"], (s, c) => {
154
+ const p = c.parameters.pattern ?? [0, 1, 2, 3];
155
+ s.emit(VM_OP.SWIZZLE, c.out, c.in("x"), pack_swizzle(p[0], p[1], p[2], p[3]));
156
+ });
157
+
158
+ // --- logic ---
159
+ define("compare", ["a", "b"], (s, c) => {
160
+ const opcode = COMPARE_OPCODES[c.parameters.op];
161
+ if (opcode === undefined) {
162
+ throw new Error(`Unknown compare op '${c.parameters.op}'`);
163
+ }
164
+ s.emit(opcode, c.out, c.in("a"), c.in("b"));
165
+ });
166
+ define("select", ["a", "b", "condition"], (s, c) => s.emit(VM_OP.SELECT, c.out, c.in("a"), c.in("b"), c.in("condition")));
167
+
168
+ // --- curves ---
169
+ define("curve", ["t"], (s, c) => s.curve(c.out, c.parameters.handle, c.in("t")));
170
+
171
+ /**
172
+ * Look up a node type by name.
173
+ * @param {string} name
174
+ * @returns {ParticleNodeDescription}
175
+ * @throws if no such node type exists
176
+ */
177
+ export function get_particle_node_description(name) {
178
+ const description = descriptions_by_name.get(name);
179
+
180
+ if (description === undefined) {
181
+ throw new Error(`Unknown particle node type '${name}'`);
182
+ }
183
+
184
+ return description;
185
+ }
@@ -1,33 +1,38 @@
1
1
  /**
2
- * Lower a single-phase {@link ../graph/ParticleGraph.js} into a VM instruction stream.
2
+ * Lower a single-phase particle {@link ../../../../core/model/node-graph/NodeGraph.js NodeGraph}
3
+ * into a VM instruction stream.
4
+ *
5
+ * The graph describes one lifecycle phase (SPAWN or UPDATE) of a particle effect: nodes are
6
+ * {@link ParticleNodeDescription} instances, and every connection wires a producing node's `out`
7
+ * port to a consuming node's named input port.
3
8
  *
4
9
  * The pipeline:
5
10
  * 1. Walk the graph from its sinks (attribute writes / kill), flattening it into an SSA-style value
6
- * list in dependency order. Literal input values are materialised as constant-load values, and
7
- * shared sub-expressions are evaluated once (DAG sharing via memoisation).
11
+ * list in dependency order. Shared sub-expressions are evaluated once (DAG sharing via
12
+ * memoisation).
8
13
  * 2. Compute each value's last use.
9
14
  * 3. Linear-scan register allocation: registers are returned to the pool at a value's last use, so a
10
15
  * result can reuse a just-consumed input register (VM ops read all sources before writing dst).
11
- * 4. Emit each value's opcode via its node definition.
16
+ * 4. Emit each value's opcode via its node description.
12
17
  *
13
- * @param {import("./ParticleGraph.js").ParticleGraph} graph
18
+ * @param {NodeGraph} graph
14
19
  * @param {import("../isa/ParticleAssembler.js").InstructionStream} stream target section
15
20
  * @param {ParticleAssembler} asm assembler owning the shared constant pool
16
21
  * @param {import("../layout/ParticleLayout.js").ParticleLayout} layout attribute schema
17
22
  */
18
- export function compile_particle_graph(graph: import("./ParticleGraph.js").ParticleGraph, stream: any, asm: ParticleAssembler, layout: import("../layout/ParticleLayout.js").ParticleLayout): void;
23
+ export function compile_particle_graph(graph: NodeGraph, stream: any, asm: ParticleAssembler, layout: import("../layout/ParticleLayout.js").ParticleLayout): void;
19
24
  /**
20
25
  * Compile a full effect (SPAWN + UPDATE) into a {@link ../isa/ParticleProgram.js}.
21
26
  *
22
27
  * @param {object} params
23
- * @param {import("./ParticleGraph.js").ParticleGraph} [params.spawn]
24
- * @param {import("./ParticleGraph.js").ParticleGraph} [params.update]
28
+ * @param {NodeGraph} [params.spawn]
29
+ * @param {NodeGraph} [params.update]
25
30
  * @param {import("../layout/ParticleLayout.js").ParticleLayout} params.layout
26
31
  * @returns {import("../isa/ParticleProgram.js").ParticleProgram}
27
32
  */
28
33
  export function compile_particle_effect({ spawn, update, layout }: {
29
- spawn?: import("./ParticleGraph.js").ParticleGraph;
30
- update?: import("./ParticleGraph.js").ParticleGraph;
34
+ spawn?: NodeGraph;
35
+ update?: NodeGraph;
31
36
  layout: import("../layout/ParticleLayout.js").ParticleLayout;
32
37
  }): import("../isa/ParticleProgram.js").ParticleProgram;
33
38
  import { ParticleAssembler } from "../isa/ParticleAssembler.js";
@@ -1 +1 @@
1
- {"version":3,"file":"compile_particle_graph.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/graph/compile_particle_graph.js"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AACH,8CALW,OAAO,oBAAoB,EAAE,aAAa,oBAE1C,iBAAiB,UACjB,OAAO,6BAA6B,EAAE,cAAc,QAqD9D;AAED;;;;;;;;GAQG;AACH;IAL+D,KAAK,GAAzD,OAAO,oBAAoB,EAAE,aAAa;IACU,MAAM,GAA1D,OAAO,oBAAoB,EAAE,aAAa;IACmB,MAAM,EAAnE,OAAO,6BAA6B,EAAE,cAAc;IAClD,OAAO,2BAA2B,EAAE,eAAe,CAa/D;kCA7FiC,6BAA6B"}
1
+ {"version":3,"file":"compile_particle_graph.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/graph/compile_particle_graph.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,2EAHW,iBAAiB,UACjB,OAAO,6BAA6B,EAAE,cAAc,QAqD9D;AAED;;;;;;;;GAQG;AACH;IAL8B,KAAK;IACL,MAAM;IACoC,MAAM,EAAnE,OAAO,6BAA6B,EAAE,cAAc;IAClD,OAAO,2BAA2B,EAAE,eAAe,CAa/D;kCAjGiC,6BAA6B"}