@typegpu/three 0.9.0 → 0.9.2

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/index.cjs CHANGED
@@ -28,13 +28,19 @@ const WGSLNodeBuilder__default = /*#__PURE__*/_interopDefaultCompat(WGSLNodeBuil
28
28
 
29
29
  class StageData {
30
30
  stage;
31
- names;
32
31
  namespace;
33
- codeGeneratedThusFar;
34
32
  constructor(stage) {
35
33
  this.stage = stage;
36
- this.names = /* @__PURE__ */ new WeakMap();
37
34
  this.namespace = tgpu__default["~unstable"].namespace();
35
+ }
36
+ }
37
+ class GenerateStageData extends StageData {
38
+ names;
39
+ type = "generate";
40
+ codeGeneratedThusFar;
41
+ constructor(stage) {
42
+ super(stage);
43
+ this.names = /* @__PURE__ */ new WeakMap();
38
44
  this.codeGeneratedThusFar = "";
39
45
  this.namespace.on("name", (event) => {
40
46
  if (tgpu.isVariable(event.target)) {
@@ -43,16 +49,29 @@ class StageData {
43
49
  });
44
50
  }
45
51
  }
52
+ class AnalyzeStageData extends StageData {
53
+ type = "analyze";
54
+ }
46
55
  class BuilderData {
47
- stageDataMap;
56
+ generateStageDataMap;
57
+ analyzeStageDataMap;
48
58
  constructor() {
49
- this.stageDataMap = /* @__PURE__ */ new Map();
59
+ this.generateStageDataMap = /* @__PURE__ */ new Map();
60
+ this.analyzeStageDataMap = /* @__PURE__ */ new Map();
61
+ }
62
+ getGenerateStageData(stage) {
63
+ let stageData = this.generateStageDataMap.get(stage);
64
+ if (!stageData) {
65
+ stageData = new GenerateStageData(stage);
66
+ this.generateStageDataMap.set(stage, stageData);
67
+ }
68
+ return stageData;
50
69
  }
51
- getStageData(stage) {
52
- let stageData = this.stageDataMap.get(stage);
70
+ getAnalyzeStageData(stage) {
71
+ let stageData = this.analyzeStageDataMap.get(stage);
53
72
  if (!stageData) {
54
- stageData = new StageData(stage);
55
- this.stageDataMap.set(stage, stageData);
73
+ stageData = new AnalyzeStageData(stage);
74
+ this.analyzeStageDataMap.set(stage, stageData);
56
75
  }
57
76
  return stageData;
58
77
  }
@@ -89,7 +108,7 @@ class TgpuFnNode extends THREE__namespace.Node {
89
108
  builderData = new BuilderData();
90
109
  builderDataMap.set(builder, builderData);
91
110
  }
92
- const stageData = builderData.getStageData(builder.shaderStage);
111
+ const stageData = builderData.getGenerateStageData(builder.shaderStage);
93
112
  if (!nodeData.custom) {
94
113
  if (currentlyGeneratingFnNodeCtx !== void 0) {
95
114
  console.warn("[@typegpu/three] Nested function generation detected");
@@ -136,16 +155,48 @@ fn ${functionId}`
136
155
  }
137
156
  return nodeData.custom.nodeFunction;
138
157
  }
158
+ #analyzeFunction(builder) {
159
+ let builderData = builderDataMap.get(builder);
160
+ if (!builderData) {
161
+ builderData = new BuilderData();
162
+ builderDataMap.set(builder, builderData);
163
+ }
164
+ const stageData = builderData.getAnalyzeStageData(builder.shaderStage);
165
+ const ctx = {
166
+ builder,
167
+ stageData,
168
+ dependencies: []
169
+ };
170
+ currentlyGeneratingFnNodeCtx = ctx;
171
+ try {
172
+ tgpu__default.resolve({
173
+ names: stageData.namespace,
174
+ template: "___ID___ fnName",
175
+ externals: { fnName: this.#impl }
176
+ });
177
+ } finally {
178
+ currentlyGeneratingFnNodeCtx = void 0;
179
+ }
180
+ }
181
+ /**
182
+ * Replicating Three.js `analyze` traversal.
183
+ * Setting `needsInterpolation` flag to true in varying nodes
184
+ */
185
+ analyze(builder, output) {
186
+ super.analyze(builder, output);
187
+ this.#analyzeFunction(builder);
188
+ }
139
189
  generate(builder, output) {
140
190
  this.#getNodeFunction(builder);
141
191
  const nodeData = builder.getDataFromNode(this);
142
192
  const builderData = builderDataMap.get(builder);
143
- const stageData = builderData.getStageData(builder.shaderStage);
144
- for (const dep of nodeData.custom.dependencies) {
193
+ const stageData = builderData.getGenerateStageData(builder.shaderStage);
194
+ const uniqueDeps = [...new Set(nodeData.custom.dependencies)];
195
+ for (const dep of uniqueDeps) {
145
196
  dep.node.build(builder);
146
197
  }
147
198
  nodeData.custom.priorCode.build(builder);
148
- for (const dep of nodeData.custom.dependencies) {
199
+ for (const dep of uniqueDeps) {
149
200
  if (!dep.var) {
150
201
  continue;
151
202
  }
@@ -154,10 +205,7 @@ fn ${functionId}`
154
205
  builder.addLineFlowCode(`${varName} = ${varValue};
155
206
  `, this);
156
207
  }
157
- if (output === "property") {
158
- return nodeData.custom.functionId;
159
- }
160
- return `${nodeData.custom.functionId}()`;
208
+ return output === "property" ? nodeData.custom.functionId : `${nodeData.custom.functionId}()`;
161
209
  }
162
210
  }
163
211
  function toTSL(fn) {
@@ -165,29 +213,44 @@ function toTSL(fn) {
165
213
  }
166
214
  class TSLAccessor {
167
215
  #dataType;
168
- var;
216
+ #var;
169
217
  node;
170
218
  constructor(node, dataType) {
171
219
  this.node = node;
172
220
  this.#dataType = dataType;
173
221
  if (
174
- // @ts-expect-error: The properties exist on the node
175
- !node.isStorageBufferNode && !node.isUniformNode || node.isTextureNode
222
+ // @ts-expect-error: they are assigned at runtime
223
+ !node.isStorageBufferNode && !node.isUniformNode || // @ts-expect-error: it is assigned at runtime
224
+ node.isTextureNode
176
225
  ) {
177
- this.var = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu__default.privateVar(dataType), "var"));
226
+ this.#var = tgpu__default.privateVar(dataType);
178
227
  }
179
228
  }
229
+ get var() {
230
+ return this.#var;
231
+ }
180
232
  get $() {
181
233
  const ctx = currentlyGeneratingFnNodeCtx;
182
234
  if (!ctx) {
183
235
  throw new Error("Can only access TSL nodes on the GPU.");
184
236
  }
237
+ if (ctx.stageData.type === "analyze") {
238
+ this.node.traverse((node) => {
239
+ node.analyze(ctx.builder);
240
+ });
241
+ return tgpu__default["~unstable"].rawCodeSnippet("", this.#dataType, "runtime").$;
242
+ }
185
243
  ctx.dependencies.push(this);
244
+ const builtNode = this.node.build(ctx.builder);
245
+ const trueVaryingNode = this.node.isVaryingNode && builtNode.includes("varyings.");
246
+ if (trueVaryingNode) {
247
+ this.#var = void 0;
248
+ }
186
249
  if (this.var) {
187
250
  return this.var.$;
188
251
  }
189
252
  return tgpu__default["~unstable"].rawCodeSnippet(
190
- this.node.build(ctx.builder),
253
+ builtNode,
191
254
  this.#dataType
192
255
  ).$;
193
256
  }
@@ -221,7 +284,14 @@ const fromTSL = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu__default["~u
221
284
  if (!sharedBuilder) {
222
285
  sharedBuilder = new WGSLNodeBuilder__default();
223
286
  }
224
- const nodeType = node.getNodeType(sharedBuilder);
287
+ let nodeType = null;
288
+ try {
289
+ nodeType = node.getNodeType(sharedBuilder);
290
+ } catch (e) {
291
+ console.warn(
292
+ `fromTSL: failed to infer node type via getNodeType; skipping type comparison.`
293
+ );
294
+ }
225
295
  if (nodeType) {
226
296
  const wgslTypeFromTSL = sharedBuilder.getType(nodeType);
227
297
  if (wgslTypeFromTSL !== wgslTypeFromTgpu) {
@@ -258,7 +328,7 @@ const wgslTypeToGlslType = {
258
328
 
259
329
  function uniform(value, dataType) {
260
330
  let glslType = wgslTypeToGlslType[dataType.type];
261
- if (value.isNode) {
331
+ if (value.isNode || value.isColor) {
262
332
  glslType = void 0;
263
333
  }
264
334
  return fromTSL(TSL.uniform(value, glslType), dataType);
package/index.d.cts CHANGED
@@ -8,9 +8,9 @@ import InputNode from 'three/src/nodes/core/InputNode.js';
8
8
  declare function toTSL(fn: () => unknown): THREE.TSL.NodeObject<THREE.Node>;
9
9
  declare class TSLAccessor<T extends d.AnyWgslData, TNode extends THREE.Node> {
10
10
  #private;
11
- readonly var: TgpuVar<'private', T> | undefined;
12
11
  readonly node: THREE.TSL.NodeObject<TNode>;
13
12
  constructor(node: THREE.TSL.NodeObject<TNode>, dataType: T);
13
+ get var(): TgpuVar<'private', T> | undefined;
14
14
  get $(): d.InferGPU<T>;
15
15
  }
16
16
  declare const fromTSL: typegpu.TgpuComptime<(<T extends d.AnyWgslData, TNode extends THREE.Node>(node: THREE.TSL.NodeObject<TNode>, type: (length: number) => T) => TSLAccessor<T, TNode>) & (<T extends d.AnyWgslData, TNode extends THREE.Node>(node: THREE.TSL.NodeObject<TNode>, type: T) => TSLAccessor<T, TNode>)>;
package/index.d.mts CHANGED
@@ -8,9 +8,9 @@ import InputNode from 'three/src/nodes/core/InputNode.js';
8
8
  declare function toTSL(fn: () => unknown): THREE.TSL.NodeObject<THREE.Node>;
9
9
  declare class TSLAccessor<T extends d.AnyWgslData, TNode extends THREE.Node> {
10
10
  #private;
11
- readonly var: TgpuVar<'private', T> | undefined;
12
11
  readonly node: THREE.TSL.NodeObject<TNode>;
13
12
  constructor(node: THREE.TSL.NodeObject<TNode>, dataType: T);
13
+ get var(): TgpuVar<'private', T> | undefined;
14
14
  get $(): d.InferGPU<T>;
15
15
  }
16
16
  declare const fromTSL: typegpu.TgpuComptime<(<T extends d.AnyWgslData, TNode extends THREE.Node>(node: THREE.TSL.NodeObject<TNode>, type: (length: number) => T) => TSLAccessor<T, TNode>) & (<T extends d.AnyWgslData, TNode extends THREE.Node>(node: THREE.TSL.NodeObject<TNode>, type: T) => TSLAccessor<T, TNode>)>;
package/index.d.ts CHANGED
@@ -8,9 +8,9 @@ import InputNode from 'three/src/nodes/core/InputNode.js';
8
8
  declare function toTSL(fn: () => unknown): THREE.TSL.NodeObject<THREE.Node>;
9
9
  declare class TSLAccessor<T extends d.AnyWgslData, TNode extends THREE.Node> {
10
10
  #private;
11
- readonly var: TgpuVar<'private', T> | undefined;
12
11
  readonly node: THREE.TSL.NodeObject<TNode>;
13
12
  constructor(node: THREE.TSL.NodeObject<TNode>, dataType: T);
13
+ get var(): TgpuVar<'private', T> | undefined;
14
14
  get $(): d.InferGPU<T>;
15
15
  }
16
16
  declare const fromTSL: typegpu.TgpuComptime<(<T extends d.AnyWgslData, TNode extends THREE.Node>(node: THREE.TSL.NodeObject<TNode>, type: (length: number) => T) => TSLAccessor<T, TNode>) & (<T extends d.AnyWgslData, TNode extends THREE.Node>(node: THREE.TSL.NodeObject<TNode>, type: T) => TSLAccessor<T, TNode>)>;
package/index.mjs CHANGED
@@ -7,13 +7,19 @@ import WGSLNodeBuilder from 'three/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
7
7
 
8
8
  class StageData {
9
9
  stage;
10
- names;
11
10
  namespace;
12
- codeGeneratedThusFar;
13
11
  constructor(stage) {
14
12
  this.stage = stage;
15
- this.names = /* @__PURE__ */ new WeakMap();
16
13
  this.namespace = tgpu["~unstable"].namespace();
14
+ }
15
+ }
16
+ class GenerateStageData extends StageData {
17
+ names;
18
+ type = "generate";
19
+ codeGeneratedThusFar;
20
+ constructor(stage) {
21
+ super(stage);
22
+ this.names = /* @__PURE__ */ new WeakMap();
17
23
  this.codeGeneratedThusFar = "";
18
24
  this.namespace.on("name", (event) => {
19
25
  if (isVariable(event.target)) {
@@ -22,16 +28,29 @@ class StageData {
22
28
  });
23
29
  }
24
30
  }
31
+ class AnalyzeStageData extends StageData {
32
+ type = "analyze";
33
+ }
25
34
  class BuilderData {
26
- stageDataMap;
35
+ generateStageDataMap;
36
+ analyzeStageDataMap;
27
37
  constructor() {
28
- this.stageDataMap = /* @__PURE__ */ new Map();
38
+ this.generateStageDataMap = /* @__PURE__ */ new Map();
39
+ this.analyzeStageDataMap = /* @__PURE__ */ new Map();
40
+ }
41
+ getGenerateStageData(stage) {
42
+ let stageData = this.generateStageDataMap.get(stage);
43
+ if (!stageData) {
44
+ stageData = new GenerateStageData(stage);
45
+ this.generateStageDataMap.set(stage, stageData);
46
+ }
47
+ return stageData;
29
48
  }
30
- getStageData(stage) {
31
- let stageData = this.stageDataMap.get(stage);
49
+ getAnalyzeStageData(stage) {
50
+ let stageData = this.analyzeStageDataMap.get(stage);
32
51
  if (!stageData) {
33
- stageData = new StageData(stage);
34
- this.stageDataMap.set(stage, stageData);
52
+ stageData = new AnalyzeStageData(stage);
53
+ this.analyzeStageDataMap.set(stage, stageData);
35
54
  }
36
55
  return stageData;
37
56
  }
@@ -68,7 +87,7 @@ class TgpuFnNode extends THREE.Node {
68
87
  builderData = new BuilderData();
69
88
  builderDataMap.set(builder, builderData);
70
89
  }
71
- const stageData = builderData.getStageData(builder.shaderStage);
90
+ const stageData = builderData.getGenerateStageData(builder.shaderStage);
72
91
  if (!nodeData.custom) {
73
92
  if (currentlyGeneratingFnNodeCtx !== void 0) {
74
93
  console.warn("[@typegpu/three] Nested function generation detected");
@@ -115,16 +134,48 @@ fn ${functionId}`
115
134
  }
116
135
  return nodeData.custom.nodeFunction;
117
136
  }
137
+ #analyzeFunction(builder) {
138
+ let builderData = builderDataMap.get(builder);
139
+ if (!builderData) {
140
+ builderData = new BuilderData();
141
+ builderDataMap.set(builder, builderData);
142
+ }
143
+ const stageData = builderData.getAnalyzeStageData(builder.shaderStage);
144
+ const ctx = {
145
+ builder,
146
+ stageData,
147
+ dependencies: []
148
+ };
149
+ currentlyGeneratingFnNodeCtx = ctx;
150
+ try {
151
+ tgpu.resolve({
152
+ names: stageData.namespace,
153
+ template: "___ID___ fnName",
154
+ externals: { fnName: this.#impl }
155
+ });
156
+ } finally {
157
+ currentlyGeneratingFnNodeCtx = void 0;
158
+ }
159
+ }
160
+ /**
161
+ * Replicating Three.js `analyze` traversal.
162
+ * Setting `needsInterpolation` flag to true in varying nodes
163
+ */
164
+ analyze(builder, output) {
165
+ super.analyze(builder, output);
166
+ this.#analyzeFunction(builder);
167
+ }
118
168
  generate(builder, output) {
119
169
  this.#getNodeFunction(builder);
120
170
  const nodeData = builder.getDataFromNode(this);
121
171
  const builderData = builderDataMap.get(builder);
122
- const stageData = builderData.getStageData(builder.shaderStage);
123
- for (const dep of nodeData.custom.dependencies) {
172
+ const stageData = builderData.getGenerateStageData(builder.shaderStage);
173
+ const uniqueDeps = [...new Set(nodeData.custom.dependencies)];
174
+ for (const dep of uniqueDeps) {
124
175
  dep.node.build(builder);
125
176
  }
126
177
  nodeData.custom.priorCode.build(builder);
127
- for (const dep of nodeData.custom.dependencies) {
178
+ for (const dep of uniqueDeps) {
128
179
  if (!dep.var) {
129
180
  continue;
130
181
  }
@@ -133,10 +184,7 @@ fn ${functionId}`
133
184
  builder.addLineFlowCode(`${varName} = ${varValue};
134
185
  `, this);
135
186
  }
136
- if (output === "property") {
137
- return nodeData.custom.functionId;
138
- }
139
- return `${nodeData.custom.functionId}()`;
187
+ return output === "property" ? nodeData.custom.functionId : `${nodeData.custom.functionId}()`;
140
188
  }
141
189
  }
142
190
  function toTSL(fn) {
@@ -144,29 +192,44 @@ function toTSL(fn) {
144
192
  }
145
193
  class TSLAccessor {
146
194
  #dataType;
147
- var;
195
+ #var;
148
196
  node;
149
197
  constructor(node, dataType) {
150
198
  this.node = node;
151
199
  this.#dataType = dataType;
152
200
  if (
153
- // @ts-expect-error: The properties exist on the node
154
- !node.isStorageBufferNode && !node.isUniformNode || node.isTextureNode
201
+ // @ts-expect-error: they are assigned at runtime
202
+ !node.isStorageBufferNode && !node.isUniformNode || // @ts-expect-error: it is assigned at runtime
203
+ node.isTextureNode
155
204
  ) {
156
- this.var = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu.privateVar(dataType), "var"));
205
+ this.#var = tgpu.privateVar(dataType);
157
206
  }
158
207
  }
208
+ get var() {
209
+ return this.#var;
210
+ }
159
211
  get $() {
160
212
  const ctx = currentlyGeneratingFnNodeCtx;
161
213
  if (!ctx) {
162
214
  throw new Error("Can only access TSL nodes on the GPU.");
163
215
  }
216
+ if (ctx.stageData.type === "analyze") {
217
+ this.node.traverse((node) => {
218
+ node.analyze(ctx.builder);
219
+ });
220
+ return tgpu["~unstable"].rawCodeSnippet("", this.#dataType, "runtime").$;
221
+ }
164
222
  ctx.dependencies.push(this);
223
+ const builtNode = this.node.build(ctx.builder);
224
+ const trueVaryingNode = this.node.isVaryingNode && builtNode.includes("varyings.");
225
+ if (trueVaryingNode) {
226
+ this.#var = void 0;
227
+ }
165
228
  if (this.var) {
166
229
  return this.var.$;
167
230
  }
168
231
  return tgpu["~unstable"].rawCodeSnippet(
169
- this.node.build(ctx.builder),
232
+ builtNode,
170
233
  this.#dataType
171
234
  ).$;
172
235
  }
@@ -200,7 +263,14 @@ const fromTSL = ((globalThis.__TYPEGPU_AUTONAME__ ?? (a => a))(tgpu["~unstable"]
200
263
  if (!sharedBuilder) {
201
264
  sharedBuilder = new WGSLNodeBuilder();
202
265
  }
203
- const nodeType = node.getNodeType(sharedBuilder);
266
+ let nodeType = null;
267
+ try {
268
+ nodeType = node.getNodeType(sharedBuilder);
269
+ } catch (e) {
270
+ console.warn(
271
+ `fromTSL: failed to infer node type via getNodeType; skipping type comparison.`
272
+ );
273
+ }
204
274
  if (nodeType) {
205
275
  const wgslTypeFromTSL = sharedBuilder.getType(nodeType);
206
276
  if (wgslTypeFromTSL !== wgslTypeFromTgpu) {
@@ -237,7 +307,7 @@ const wgslTypeToGlslType = {
237
307
 
238
308
  function uniform(value, dataType) {
239
309
  let glslType = wgslTypeToGlslType[dataType.type];
240
- if (value.isNode) {
310
+ if (value.isNode || value.isColor) {
241
311
  glslType = void 0;
242
312
  }
243
313
  return fromTSL(uniform$1(value, glslType), dataType);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@typegpu/three",
3
3
  "type": "module",
4
- "version": "0.9.0",
4
+ "version": "0.9.2",
5
5
  "description": "Utilities for integrating TypeGPU with Three.js",
6
6
  "exports": {
7
7
  "./package.json": "./package.json",