@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.
- package/package.json +1 -1
- package/src/core/geom/3d/mat4/m4_rigidity_defect.d.ts +30 -0
- package/src/core/geom/3d/mat4/m4_rigidity_defect.d.ts.map +1 -0
- package/src/core/geom/3d/mat4/m4_rigidity_defect.js +71 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts +56 -8
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +263 -67
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +6 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +6 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +13 -7
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +103 -138
- package/src/engine/network/replication/Replicator.d.ts +29 -6
- package/src/engine/network/replication/Replicator.d.ts.map +1 -1
- package/src/engine/network/replication/Replicator.js +265 -108
- package/src/engine/network/sim/ActionLog.d.ts +2 -0
- package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
- package/src/engine/network/sim/ActionLog.js +2 -0
- package/src/engine/network/sim/ActionRecordCursor.d.ts +82 -0
- package/src/engine/network/sim/ActionRecordCursor.d.ts.map +1 -0
- package/src/engine/network/sim/ActionRecordCursor.js +120 -0
- package/src/engine/network/sim/RewindEngine.d.ts.map +1 -1
- package/src/engine/network/sim/RewindEngine.js +13 -29
- package/src/engine/network/sim/SimActionExecutor.d.ts +2 -3
- package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
- package/src/engine/network/sim/SimActionExecutor.js +10 -11
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_scene.js +38 -37
- package/src/shade/playground/skinned_mesh_soup/README.md +39 -18
- package/src/shade/playground/skinned_mesh_soup/index.html +9 -6
- package/src/shade/playground/skinned_mesh_soup/main.d.ts.map +1 -1
- package/src/shade/playground/skinned_mesh_soup/main.js +16 -13
- package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts +8 -2
- package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts.map +1 -1
- package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.js +23 -4
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +1958 -1796
- package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +357 -0
- package/src/shade/renderer/animation/skin_bind_transform.d.ts +25 -0
- package/src/shade/renderer/animation/skin_bind_transform.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_bind_transform.js +33 -0
- package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts +67 -0
- package/src/shade/renderer/animation/skin_clip_scale_conflicts.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_clip_scale_conflicts.js +199 -0
- package/src/shade/renderer/animation/skin_frame_defect.d.ts +34 -0
- package/src/shade/renderer/animation/skin_frame_defect.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_frame_defect.js +132 -0
- package/src/shade/renderer/animation/skin_joint_matrix.d.ts +24 -0
- package/src/shade/renderer/animation/skin_joint_matrix.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_joint_matrix.js +46 -0
- package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts +70 -0
- package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_normalize_mesh_frame.js +216 -0
- package/src/shade/renderer/animation/skin_test_fixtures.d.ts +86 -0
- package/src/shade/renderer/animation/skin_test_fixtures.d.ts.map +1 -0
- package/src/shade/renderer/animation/skin_test_fixtures.js +250 -0
- package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.d.ts +18 -4
- package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/chunk_skin_blend_mesh_local.js +18 -4
- package/src/shade/renderer/particles/DESIGN.md +5 -5
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +43 -0
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +44 -0
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +33 -59
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +185 -118
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts +15 -10
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/compile_particle_graph.js +205 -170
- package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts +44 -0
- package/src/shade/renderer/particles/graph/particle_graph_authoring.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_graph_authoring.js +108 -0
- package/src/shade/renderer/particles/particle_prototype.d.ts.map +1 -1
- package/src/shade/renderer/particles/particle_prototype.js +181 -180
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +4 -4
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/create_particle_effect.js +19 -19
- package/src/shade/renderer/particles/vm/chunk_particle_vm.js +1 -1
- package/src/shade/renderer/scene/SkinnedMesh.d.ts +11 -0
- package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +1 -1
- package/src/shade/renderer/scene/SkinnedMesh.js +11 -0
- package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.d.ts +33 -3
- package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.d.ts.map +1 -1
- package/src/shade/renderer/shader/chunk/geometry/quaternion/dual/chunk_dual_quat_from_m4.js +58 -37
- package/src/shade/renderer/particles/graph/ParticleGraph.d.ts +0 -54
- package/src/shade/renderer/particles/graph/ParticleGraph.d.ts.map +0 -1
- package/src/shade/renderer/particles/graph/ParticleGraph.js +0 -99
|
@@ -1,170 +1,205 @@
|
|
|
1
|
-
import { PARTICLE_VM_REGISTER_COUNT } from "../ParticleConstants.js";
|
|
2
|
-
import { ParticleAssembler } from "../isa/ParticleAssembler.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* The
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
for (let
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
* @
|
|
81
|
-
*
|
|
82
|
-
* @
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
*
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
1
|
+
import { PARTICLE_VM_REGISTER_COUNT } from "../ParticleConstants.js";
|
|
2
|
+
import { ParticleAssembler } from "../isa/ParticleAssembler.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Lower a single-phase particle {@link ../../../../core/model/node-graph/NodeGraph.js NodeGraph}
|
|
6
|
+
* into a VM instruction stream.
|
|
7
|
+
*
|
|
8
|
+
* The graph describes one lifecycle phase (SPAWN or UPDATE) of a particle effect: nodes are
|
|
9
|
+
* {@link ParticleNodeDescription} instances, and every connection wires a producing node's `out`
|
|
10
|
+
* port to a consuming node's named input port.
|
|
11
|
+
*
|
|
12
|
+
* The pipeline:
|
|
13
|
+
* 1. Walk the graph from its sinks (attribute writes / kill), flattening it into an SSA-style value
|
|
14
|
+
* list in dependency order. Shared sub-expressions are evaluated once (DAG sharing via
|
|
15
|
+
* memoisation).
|
|
16
|
+
* 2. Compute each value's last use.
|
|
17
|
+
* 3. Linear-scan register allocation: registers are returned to the pool at a value's last use, so a
|
|
18
|
+
* result can reuse a just-consumed input register (VM ops read all sources before writing dst).
|
|
19
|
+
* 4. Emit each value's opcode via its node description.
|
|
20
|
+
*
|
|
21
|
+
* @param {NodeGraph} graph
|
|
22
|
+
* @param {import("../isa/ParticleAssembler.js").InstructionStream} stream target section
|
|
23
|
+
* @param {ParticleAssembler} asm assembler owning the shared constant pool
|
|
24
|
+
* @param {import("../layout/ParticleLayout.js").ParticleLayout} layout attribute schema
|
|
25
|
+
*/
|
|
26
|
+
export function compile_particle_graph(graph, stream, asm, layout) {
|
|
27
|
+
const values = flatten(graph);
|
|
28
|
+
const last_use = compute_last_use(values);
|
|
29
|
+
|
|
30
|
+
const register_of = new Array(values.length).fill(-1);
|
|
31
|
+
const free = [];
|
|
32
|
+
for (let r = PARTICLE_VM_REGISTER_COUNT - 1; r >= 0; r--) {
|
|
33
|
+
free.push(r);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < values.length; i++) {
|
|
37
|
+
const value = values[i];
|
|
38
|
+
const description = value.description;
|
|
39
|
+
|
|
40
|
+
// Capture the current registers of all inputs before we recycle any of them.
|
|
41
|
+
const input_registers = {};
|
|
42
|
+
for (const port of Object.keys(value.inputs)) {
|
|
43
|
+
input_registers[port] = register_of[value.inputs[port]];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Return registers whose owning value dies at this instruction (its last consumer).
|
|
47
|
+
const freed = new Set();
|
|
48
|
+
for (const port of Object.keys(value.inputs)) {
|
|
49
|
+
const source = value.inputs[port];
|
|
50
|
+
if (last_use[source] === i && register_of[source] >= 0 && !freed.has(source)) {
|
|
51
|
+
free.push(register_of[source]);
|
|
52
|
+
register_of[source] = -1;
|
|
53
|
+
freed.add(source);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let out;
|
|
58
|
+
if (!description.is_sink) {
|
|
59
|
+
if (free.length === 0) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Particle graph exceeds the VM register file (${PARTICLE_VM_REGISTER_COUNT} registers). ` +
|
|
62
|
+
`Simplify the effect or split it.`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
out = free.pop();
|
|
66
|
+
register_of[i] = out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
description.emit(stream, {
|
|
70
|
+
in: (port) => input_registers[port],
|
|
71
|
+
out,
|
|
72
|
+
parameters: value.parameters,
|
|
73
|
+
layout,
|
|
74
|
+
asm,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Compile a full effect (SPAWN + UPDATE) into a {@link ../isa/ParticleProgram.js}.
|
|
81
|
+
*
|
|
82
|
+
* @param {object} params
|
|
83
|
+
* @param {NodeGraph} [params.spawn]
|
|
84
|
+
* @param {NodeGraph} [params.update]
|
|
85
|
+
* @param {import("../layout/ParticleLayout.js").ParticleLayout} params.layout
|
|
86
|
+
* @returns {import("../isa/ParticleProgram.js").ParticleProgram}
|
|
87
|
+
*/
|
|
88
|
+
export function compile_particle_effect({ spawn, update, layout }) {
|
|
89
|
+
const asm = new ParticleAssembler();
|
|
90
|
+
if (spawn !== undefined) {
|
|
91
|
+
compile_particle_graph(spawn, asm.spawn, asm, layout);
|
|
92
|
+
}
|
|
93
|
+
if (update !== undefined) {
|
|
94
|
+
compile_particle_graph(update, asm.update, asm, layout);
|
|
95
|
+
}
|
|
96
|
+
const program = asm.build();
|
|
97
|
+
program.validate();
|
|
98
|
+
return program;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Flatten a graph into a dependency-ordered value list. Each value is
|
|
103
|
+
* `{ description, parameters, inputs: { port_name -> value_index } }`. The array order is a valid
|
|
104
|
+
* topological order (every input has a lower index).
|
|
105
|
+
* @param {NodeGraph} graph
|
|
106
|
+
* @returns {Array<{description: ParticleNodeDescription, parameters: object, inputs: object}>}
|
|
107
|
+
*/
|
|
108
|
+
function flatten(graph) {
|
|
109
|
+
const values = [];
|
|
110
|
+
const node_to_value = new Map();
|
|
111
|
+
const visiting = new Set();
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {NodeInstance} node
|
|
115
|
+
* @returns {number} index of the node's value
|
|
116
|
+
*/
|
|
117
|
+
function visit(node) {
|
|
118
|
+
const existing = node_to_value.get(node.id);
|
|
119
|
+
if (existing !== undefined) {
|
|
120
|
+
return existing;
|
|
121
|
+
}
|
|
122
|
+
if (visiting.has(node.id)) {
|
|
123
|
+
throw new Error(`Cycle detected in particle graph at node ${node.id}`);
|
|
124
|
+
}
|
|
125
|
+
visiting.add(node.id);
|
|
126
|
+
|
|
127
|
+
const description = require_particle_description(node);
|
|
128
|
+
|
|
129
|
+
const inputs = {};
|
|
130
|
+
for (const port of description.inPorts) {
|
|
131
|
+
inputs[port.name] = visit(resolve_input(node, port));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const vid = values.length;
|
|
135
|
+
values.push({ description, parameters: node.parameters, inputs });
|
|
136
|
+
node_to_value.set(node.id, vid);
|
|
137
|
+
visiting.delete(node.id);
|
|
138
|
+
return vid;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (const node of graph.getNodes()) {
|
|
142
|
+
if (require_particle_description(node).is_sink) {
|
|
143
|
+
visit(node);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return values;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @param {NodeInstance} node
|
|
152
|
+
* @returns {ParticleNodeDescription}
|
|
153
|
+
*/
|
|
154
|
+
function require_particle_description(node) {
|
|
155
|
+
const description = node.description;
|
|
156
|
+
|
|
157
|
+
if (description === null || description === undefined || description.isParticleNodeDescription !== true) {
|
|
158
|
+
throw new Error(`Node ${node.id} (${description}) is not a particle node, it cannot be compiled`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return description;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Find the node driving one input port of a node.
|
|
166
|
+
* @param {NodeInstance} node
|
|
167
|
+
* @param {Port} port an input port of the node's description
|
|
168
|
+
* @returns {NodeInstance}
|
|
169
|
+
* @throws if the port has no source, or more than one
|
|
170
|
+
*/
|
|
171
|
+
function resolve_input(node, port) {
|
|
172
|
+
const endpoint = node.getEndpoint(port.id);
|
|
173
|
+
|
|
174
|
+
const connections = endpoint.inConnections;
|
|
175
|
+
|
|
176
|
+
if (connections.length === 0) {
|
|
177
|
+
throw new Error(`Node ${node.id} (${node.description.name}) is missing a binding for input '${port.name}'`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (connections.length > 1) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`Node ${node.id} (${node.description.name}) has ${connections.length} connections into input '${port.name}', ` +
|
|
183
|
+
`an input may have at most one`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return connections[0].source.instance;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @param {Array} values
|
|
192
|
+
* @returns {number[]} last_use[i] = highest index that consumes value i (or -1 if never consumed)
|
|
193
|
+
*/
|
|
194
|
+
function compute_last_use(values) {
|
|
195
|
+
const last_use = new Array(values.length).fill(-1);
|
|
196
|
+
for (let i = 0; i < values.length; i++) {
|
|
197
|
+
for (const port of Object.keys(values[i].inputs)) {
|
|
198
|
+
const source = values[i].inputs[port];
|
|
199
|
+
if (i > last_use[source]) {
|
|
200
|
+
last_use[source] = i;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return last_use;
|
|
205
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authoring sugar for building particle effects on a plain
|
|
3
|
+
* {@link ../../../../core/model/node-graph/NodeGraph.js NodeGraph}.
|
|
4
|
+
*
|
|
5
|
+
* These are convenience wrappers only — the graph, its nodes and its connections are the standard
|
|
6
|
+
* node-graph model, so the editor, serializers and graph utilities all work on particle effects
|
|
7
|
+
* unchanged. Everything downstream (see {@link ./compile_particle_graph.js}) consumes `NodeGraph`
|
|
8
|
+
* directly and never sees these helpers.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Add a node to a particle graph.
|
|
12
|
+
*
|
|
13
|
+
* @param {NodeGraph} graph
|
|
14
|
+
* @param {string} type a node type name registered in {@link ./ParticleNodeRegistry.js}
|
|
15
|
+
* @param {object} [parameters] static parameters (attribute name, builtin id, constant value, …)
|
|
16
|
+
* @returns {number} the new node instance id
|
|
17
|
+
*/
|
|
18
|
+
export function particle_node(graph: NodeGraph, type: string, parameters?: object): number;
|
|
19
|
+
/**
|
|
20
|
+
* Bind an input port of a node to a source, replacing whatever was bound to it before.
|
|
21
|
+
*
|
|
22
|
+
* The source may be:
|
|
23
|
+
* - a node id (number) → that node's `out` port,
|
|
24
|
+
* - `{ node, port }` → an explicit port,
|
|
25
|
+
* - a number array → a literal `vec4`, materialised as a `const` node (scalar broadcast is NOT
|
|
26
|
+
* applied; `[5]` means `[5,0,0,0]`).
|
|
27
|
+
*
|
|
28
|
+
* @param {NodeGraph} graph
|
|
29
|
+
* @param {number} node_id
|
|
30
|
+
* @param {string} port input port name
|
|
31
|
+
* @param {number|number[]|{node: number, port?: string}} source
|
|
32
|
+
* @returns {number} connection id
|
|
33
|
+
*/
|
|
34
|
+
export function particle_wire(graph: NodeGraph, node_id: number, port: string, source: number | number[] | {
|
|
35
|
+
node: number;
|
|
36
|
+
port?: string;
|
|
37
|
+
}): number;
|
|
38
|
+
/**
|
|
39
|
+
* Wrap a scalar or vector as a literal source (so it is unambiguous vs. a node id).
|
|
40
|
+
* @param {...number} components
|
|
41
|
+
* @returns {number[]}
|
|
42
|
+
*/
|
|
43
|
+
export function literal(...components: number[]): number[];
|
|
44
|
+
//# sourceMappingURL=particle_graph_authoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"particle_graph_authoring.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/graph/particle_graph_authoring.js"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH;;;;;;;GAOG;AACH,sDAJW,MAAM,eACN,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;;;;;;;;GAcG;AACH,yDALW,MAAM,QACN,MAAM;UACiB,MAAM;WAAS,MAAM;IAC1C,MAAM,CA2BlB;AA6BD;;;;GAIG;AACH,uCAHc,MAAM,KACP,MAAM,EAAE,CAIpB"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { PortDirection } from "../../../../core/model/node-graph/node/PortDirection.js";
|
|
2
|
+
import { get_particle_node_description, PARTICLE_NODE_OUTPUT_PORT } from "./ParticleNodeRegistry.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Authoring sugar for building particle effects on a plain
|
|
6
|
+
* {@link ../../../../core/model/node-graph/NodeGraph.js NodeGraph}.
|
|
7
|
+
*
|
|
8
|
+
* These are convenience wrappers only — the graph, its nodes and its connections are the standard
|
|
9
|
+
* node-graph model, so the editor, serializers and graph utilities all work on particle effects
|
|
10
|
+
* unchanged. Everything downstream (see {@link ./compile_particle_graph.js}) consumes `NodeGraph`
|
|
11
|
+
* directly and never sees these helpers.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Add a node to a particle graph.
|
|
16
|
+
*
|
|
17
|
+
* @param {NodeGraph} graph
|
|
18
|
+
* @param {string} type a node type name registered in {@link ./ParticleNodeRegistry.js}
|
|
19
|
+
* @param {object} [parameters] static parameters (attribute name, builtin id, constant value, …)
|
|
20
|
+
* @returns {number} the new node instance id
|
|
21
|
+
*/
|
|
22
|
+
export function particle_node(graph, type, parameters = {}) {
|
|
23
|
+
const description = get_particle_node_description(type);
|
|
24
|
+
|
|
25
|
+
const id = graph.createNode(description);
|
|
26
|
+
|
|
27
|
+
graph.getNodeSafe(id).setParameters(parameters);
|
|
28
|
+
|
|
29
|
+
return id;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Bind an input port of a node to a source, replacing whatever was bound to it before.
|
|
34
|
+
*
|
|
35
|
+
* The source may be:
|
|
36
|
+
* - a node id (number) → that node's `out` port,
|
|
37
|
+
* - `{ node, port }` → an explicit port,
|
|
38
|
+
* - a number array → a literal `vec4`, materialised as a `const` node (scalar broadcast is NOT
|
|
39
|
+
* applied; `[5]` means `[5,0,0,0]`).
|
|
40
|
+
*
|
|
41
|
+
* @param {NodeGraph} graph
|
|
42
|
+
* @param {number} node_id
|
|
43
|
+
* @param {string} port input port name
|
|
44
|
+
* @param {number|number[]|{node: number, port?: string}} source
|
|
45
|
+
* @returns {number} connection id
|
|
46
|
+
*/
|
|
47
|
+
export function particle_wire(graph, node_id, port, source) {
|
|
48
|
+
const node = graph.getNodeSafe(node_id);
|
|
49
|
+
|
|
50
|
+
const port_object = node.description.findPortByNameAndDirection(port, PortDirection.In);
|
|
51
|
+
|
|
52
|
+
if (port_object === undefined) {
|
|
53
|
+
throw new Error(`Node ${node_id} (${node.description.name}) has no input port '${port}'`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// snapshot before connecting, so the replacement is only committed once the new wiring holds
|
|
57
|
+
const previous = node.getEndpoint(port_object.id).inConnections;
|
|
58
|
+
|
|
59
|
+
const resolved = resolve_source(graph, source);
|
|
60
|
+
|
|
61
|
+
const connection_id = graph.createConnectionByPortName(
|
|
62
|
+
resolved.node, resolved.port,
|
|
63
|
+
node_id, port
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// an input may have at most one source, so binding it again replaces rather than adds
|
|
67
|
+
for (const connection of previous) {
|
|
68
|
+
graph.deleteConnection(connection.id);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return connection_id;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {NodeGraph} graph
|
|
76
|
+
* @param {number|number[]|{node: number, port?: string}} source
|
|
77
|
+
* @returns {{node: number, port: string}}
|
|
78
|
+
*/
|
|
79
|
+
function resolve_source(graph, source) {
|
|
80
|
+
if (typeof source === "number") {
|
|
81
|
+
// Could be a node id or a scalar literal — ambiguous, so literals must use the array form.
|
|
82
|
+
return { node: source, port: PARTICLE_NODE_OUTPUT_PORT };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (Array.isArray(source)) {
|
|
86
|
+
const value = [source[0] ?? 0, source[1] ?? 0, source[2] ?? 0, source[3] ?? 0];
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
node: particle_node(graph, "const", { value }),
|
|
90
|
+
port: PARTICLE_NODE_OUTPUT_PORT
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (source !== null && typeof source === "object" && typeof source.node === "number") {
|
|
95
|
+
return { node: source.node, port: source.port ?? PARTICLE_NODE_OUTPUT_PORT };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
throw new Error(`Invalid input source: ${JSON.stringify(source)}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Wrap a scalar or vector as a literal source (so it is unambiguous vs. a node id).
|
|
103
|
+
* @param {...number} components
|
|
104
|
+
* @returns {number[]}
|
|
105
|
+
*/
|
|
106
|
+
export function literal(...components) {
|
|
107
|
+
return components;
|
|
108
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"particle_prototype.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/particles/particle_prototype.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"particle_prototype.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/particles/particle_prototype.js"],"names":[],"mappings":"AAOA;;;;;;;;;;GAUG;AAEH;;;;;;;;;GASG;AACH;IAL4B,UAAU,GAA3B,MAAM;IACW,WAAW,GAA5B,MAAM;IACW,eAAe,GAAhC,MAAM;;YACK,cAAc;YAAU,MAAM;aAAW,MAAM;EA2GpE;AAED;;;;GAIG;AACH;YAFsB,cAAc;YAAU,MAAM;aAAW,MAAM;EAcpE;AAED;;;;;;;;;;;;;;;GAeG;AACH,0CALW,OAAO,wBAAwB,EAAE,iBAAiB;IAEjC,IAAI,GAArB,MAAM;;UACG,MAAM;WAAS,MAAM;EAWxC;+BAhL8B,4BAA4B"}
|