@threenative/core 0.3.2 → 0.3.3
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/capabilities.json +1344 -88
- package/dist/{assets-kyoF7JlJ.d.ts → assets-CYKk2WTu.d.ts} +7 -0
- package/dist/{audio-BFiGneTL.d.ts → audio-7i3Xl0l3.d.ts} +32 -1
- package/dist/{canvas-layer-BLVijiUJ.d.ts → canvas-layer-C1SnMoJ-.d.ts} +1 -1
- package/dist/{game-XGrTzapq.d.ts → game-D_6r-k4Y.d.ts} +365 -7
- package/dist/{gpu-readback-D2iRvoe9.d.ts → gpu-readback-CMklJs6r.d.ts} +1 -1
- package/dist/hot.d.ts +5 -5
- package/dist/hot.js +20 -3
- package/dist/index.d.ts +1040 -19
- package/dist/index.js +5111 -470
- package/dist/playtest.d.ts +4 -4
- package/dist/playtest.js +44 -9
- package/dist/react.d.ts +2 -2
- package/dist/{renderer-C6hqZpoG.d.ts → renderer-Cy4qeBOA.d.ts} +247 -13
- package/dist/ui-layer.d.ts +13 -7
- package/dist/ui-layer.js +2 -1
- package/dist/world.d.ts +3 -3
- package/mcp/blender-server.mjs +1 -1
- package/mcp/engine-server.mjs +33 -5
- package/mcp/servers.mjs +3 -3
- package/package.json +6 -6
- package/patches/three@0.185.1.patch +179 -19
package/mcp/engine-server.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { realpathSync, readFileSync, existsSync } from 'fs';
|
|
2
|
+
import { realpathSync, readFileSync, existsSync, mkdirSync, appendFileSync } from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { createInterface } from 'readline';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
@@ -417,6 +417,22 @@ var TOOL_DEFINITIONS = [
|
|
|
417
417
|
function toolDefinitions() {
|
|
418
418
|
return TOOL_DEFINITIONS;
|
|
419
419
|
}
|
|
420
|
+
var DEFAULT_LOG_FILE = path.join(".threenative", "engine-mcp.log");
|
|
421
|
+
var logBroken = false;
|
|
422
|
+
function logToolCall(entry) {
|
|
423
|
+
if (logBroken) return;
|
|
424
|
+
const target = process.env.THREENATIVE_ENGINE_MCP_LOG ?? DEFAULT_LOG_FILE;
|
|
425
|
+
if (target === "off") return;
|
|
426
|
+
try {
|
|
427
|
+
mkdirSync(path.dirname(path.resolve(target)), { recursive: true });
|
|
428
|
+
appendFileSync(target, `${JSON.stringify({ time: (/* @__PURE__ */ new Date()).toISOString(), ...entry })}
|
|
429
|
+
`);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
logBroken = true;
|
|
432
|
+
process.stderr.write(`engine-mcp log disabled (${target}): ${String(error)}
|
|
433
|
+
`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
420
436
|
function jsonRpcError(id, code, message) {
|
|
421
437
|
return JSON.stringify({ id, jsonrpc: "2.0", error: { code, message } });
|
|
422
438
|
}
|
|
@@ -439,12 +455,22 @@ function handleToolCall(params, manifestFile) {
|
|
|
439
455
|
const scope = argumentsValue.scope ?? "mechanic";
|
|
440
456
|
if (scope !== "request" && scope !== "mechanic")
|
|
441
457
|
throw new Error("engine_search_capabilities scope must be 'request' or 'mechanic'.");
|
|
442
|
-
|
|
458
|
+
const response = searchCapabilities(argumentsValue.situation, manifestFile, scope);
|
|
459
|
+
logToolCall({
|
|
460
|
+
results: response.results.map((result) => result.symbol),
|
|
461
|
+
scope,
|
|
462
|
+
situation: argumentsValue.situation,
|
|
463
|
+
tool: name,
|
|
464
|
+
verdict: response.verdict
|
|
465
|
+
});
|
|
466
|
+
return toolText(response);
|
|
443
467
|
}
|
|
444
468
|
if (name === "engine_capability_detail") {
|
|
445
469
|
if (typeof argumentsValue.symbol !== "string")
|
|
446
470
|
throw new Error("engine_capability_detail requires a string 'symbol' argument.");
|
|
447
|
-
|
|
471
|
+
const detail = capabilityDetail(argumentsValue.symbol, manifestFile);
|
|
472
|
+
logToolCall({ symbol: argumentsValue.symbol, tool: name });
|
|
473
|
+
return toolText(detail);
|
|
448
474
|
}
|
|
449
475
|
throw new Error(`Unknown engine MCP tool '${String(name)}'.`);
|
|
450
476
|
}
|
|
@@ -464,7 +490,7 @@ function handleLine(line, manifestFile) {
|
|
|
464
490
|
capabilities: { tools: { listChanged: false } },
|
|
465
491
|
instructions: AUTHORING_INSTRUCTIONS,
|
|
466
492
|
protocolVersion: "2025-06-18",
|
|
467
|
-
serverInfo: { name: "threenative-engine-mcp", version: "0.2.
|
|
493
|
+
serverInfo: { name: "threenative-engine-mcp", version: "0.2.3" }
|
|
468
494
|
});
|
|
469
495
|
}
|
|
470
496
|
if (request.method === "tools/list") {
|
|
@@ -476,7 +502,9 @@ function handleLine(line, manifestFile) {
|
|
|
476
502
|
}
|
|
477
503
|
return jsonRpcError(request.id, -32601, `Method not found: ${request.method}`);
|
|
478
504
|
} catch (error) {
|
|
479
|
-
|
|
505
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
506
|
+
if (request.method === "tools/call") logToolCall({ error: message, request: request.params });
|
|
507
|
+
return jsonRpcError(request.id, -32e3, message);
|
|
480
508
|
}
|
|
481
509
|
}
|
|
482
510
|
function runServer(manifestFile = defaultManifestPath()) {
|
package/mcp/servers.mjs
CHANGED
|
@@ -38,10 +38,10 @@ export const MCP_SERVERS = Object.freeze({
|
|
|
38
38
|
* separately. The pinned engine version remains an emergency fallback for a legacy development
|
|
39
39
|
* checkout whose bundle has not been built. */
|
|
40
40
|
export const MCP_PACKAGES = Object.freeze({
|
|
41
|
-
assets: Object.freeze({ name: "threenative-asset-mcp", version: "0.
|
|
41
|
+
assets: Object.freeze({ name: "threenative-asset-mcp", version: "0.9.1" }),
|
|
42
42
|
sculpt: Object.freeze({ name: "threenative-sculpt-mcp", version: "0.1.1" }),
|
|
43
|
-
engine: Object.freeze({ name: "threenative-engine-mcp", version: "0.2.
|
|
44
|
-
blender: Object.freeze({ name: "threenative-blender-mcp", version: "0.1.
|
|
43
|
+
engine: Object.freeze({ name: "threenative-engine-mcp", version: "0.2.3" }),
|
|
44
|
+
blender: Object.freeze({ name: "threenative-blender-mcp", version: "0.1.3" }),
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
/** How one host spells a stdio MCP server. Hosts agree on what to run and disagree on where to
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@threenative/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"license": "MIT AND GPL-2.0-or-later",
|
|
5
5
|
"description": "The small vanilla Three.js game runtime foundation.",
|
|
6
6
|
"keywords": [
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"dependencies": {
|
|
77
77
|
"three": "0.185.1",
|
|
78
78
|
"three-mesh-bvh": "0.9.14",
|
|
79
|
-
"threenative-asset-mcp": "0.
|
|
79
|
+
"threenative-asset-mcp": "0.9.1",
|
|
80
80
|
"threenative-sculpt-mcp": "0.1.1",
|
|
81
81
|
"zustand": "5.0.14"
|
|
82
82
|
},
|
|
@@ -89,12 +89,12 @@
|
|
|
89
89
|
"react-reconciler": "0.33.0",
|
|
90
90
|
"tsup": "8.5.1",
|
|
91
91
|
"typescript": "5.9.3",
|
|
92
|
-
"threenative
|
|
93
|
-
"
|
|
94
|
-
"threenative-
|
|
92
|
+
"@threenative/playtest": "0.3.3",
|
|
93
|
+
"threenative-engine-mcp": "0.2.3",
|
|
94
|
+
"threenative-blender-mcp": "0.1.3"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
|
-
"@threenative/playtest": ">=0.3.
|
|
97
|
+
"@threenative/playtest": ">=0.3.3 <0.4.0",
|
|
98
98
|
"react": ">=19.2.0 <20",
|
|
99
99
|
"react-reconciler": ">=0.33.0 <0.34"
|
|
100
100
|
},
|
|
@@ -25,7 +25,7 @@ index 0bb9262cd029f411933d077fd44197a51ec5e8e9..868a634b4294be5f32664912a50813d5
|
|
|
25
25
|
* Constructs a new batched mesh.
|
|
26
26
|
*
|
|
27
27
|
diff --git a/build/three.webgpu.js b/build/three.webgpu.js
|
|
28
|
-
index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..
|
|
28
|
+
index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..f7de72cffef2eb577abb0b90051a18392c88f865 100644
|
|
29
29
|
--- a/build/three.webgpu.js
|
|
30
30
|
+++ b/build/three.webgpu.js
|
|
31
31
|
@@ -18500,7 +18500,8 @@ const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder ) => {
|
|
@@ -105,7 +105,25 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
105
105
|
/**
|
|
106
106
|
* An event listener which is defined by `RenderObjects`. It performs
|
|
107
107
|
* clean up tasks when `dispose()` on this render object.
|
|
108
|
-
@@ -
|
|
108
|
+
@@ -30098,7 +30127,7 @@ class RenderObject {
|
|
109
|
+
*/
|
|
110
|
+
getChainArray() {
|
|
111
|
+
|
|
112
|
+
- return [ this.object, this.material, this.context, this.lightsNode ];
|
|
113
|
+
+ return [ this.object, this.material, this.context, this.lightsNode, this._sourceMaterial ?? this.material ];
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@@ -30680,6 +30709,8 @@ class RenderObjects {
|
|
118
|
+
_chainKeys$3[ 1 ] = material;
|
|
119
|
+
_chainKeys$3[ 2 ] = renderContext;
|
|
120
|
+
_chainKeys$3[ 3 ] = lightsNode;
|
|
121
|
+
+ // Material groups share the shadow override, but must retain separate GPU bindings.
|
|
122
|
+
+ _chainKeys$3[ 4 ] = this.renderer._currentSourceMaterial ?? material;
|
|
123
|
+
|
|
124
|
+
//
|
|
125
|
+
|
|
126
|
+
@@ -30707,9 +30738,18 @@ class RenderObjects {
|
|
109
127
|
|
|
110
128
|
}
|
|
111
129
|
|
|
@@ -118,15 +136,15 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
118
136
|
+ const sourceTracked = sourceMaterial !== null && sourceMaterial !== undefined;
|
|
119
137
|
+ const sourceReplaced = sourceTracked && renderObject._sourceMaterial !== sourceMaterial;
|
|
120
138
|
+ const sourceChanged = sourceTracked && ( sourceReplaced || renderObject.sourceVersion !== sourceMaterial.version );
|
|
139
|
+
+
|
|
140
|
+
+ if ( renderObject.version !== material.version || sourceChanged || renderObject.needsUpdate ) {
|
|
121
141
|
|
|
122
142
|
- if ( renderObject.initialCacheKey !== renderObject.getCacheKey() ) {
|
|
123
|
-
+ if ( renderObject.version !== material.version || sourceChanged || renderObject.needsUpdate ) {
|
|
124
|
-
+
|
|
125
143
|
+ if ( sourceReplaced || renderObject.initialCacheKey !== renderObject.getCacheKey() ) {
|
|
126
144
|
|
|
127
145
|
renderObject.dispose();
|
|
128
146
|
|
|
129
|
-
@@ -30719,6 +
|
|
147
|
+
@@ -30719,6 +30759,8 @@ class RenderObjects {
|
|
130
148
|
|
|
131
149
|
renderObject.version = material.version;
|
|
132
150
|
|
|
@@ -135,7 +153,15 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
135
153
|
}
|
|
136
154
|
|
|
137
155
|
}
|
|
138
|
-
@@ -
|
|
156
|
+
@@ -30731,6 +30773,7 @@ class RenderObjects {
|
|
157
|
+
_chainKeys$3[ 1 ] = null;
|
|
158
|
+
_chainKeys$3[ 2 ] = null;
|
|
159
|
+
_chainKeys$3[ 3 ] = null;
|
|
160
|
+
+ _chainKeys$3[ 4 ] = null;
|
|
161
|
+
|
|
162
|
+
//
|
|
163
|
+
|
|
164
|
+
@@ -39725,7 +39768,8 @@ class VelocityNode extends TempNode {
|
|
139
165
|
|
|
140
166
|
const previousModelMatrix = getPreviousMatrix( object );
|
|
141
167
|
|
|
@@ -145,7 +171,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
145
171
|
|
|
146
172
|
//
|
|
147
173
|
|
|
148
|
-
@@ -53841,7 +
|
|
174
|
+
@@ -53841,7 +53885,7 @@ class NodeBuilder {
|
|
149
175
|
|
|
150
176
|
const mrt = this.renderer.getMRT();
|
|
151
177
|
|
|
@@ -154,7 +180,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
154
180
|
|
|
155
181
|
}
|
|
156
182
|
|
|
157
|
-
@@ -62731,6 +
|
|
183
|
+
@@ -62731,6 +62775,7 @@ class Renderer {
|
|
158
184
|
let materialDisplacementMap;
|
|
159
185
|
let materialDisplacementScale;
|
|
160
186
|
let materialDisplacementBias;
|
|
@@ -162,7 +188,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
162
188
|
|
|
163
189
|
const previousSourceMaterial = this._currentSourceMaterial;
|
|
164
190
|
|
|
165
|
-
@@ -62756,6 +
|
|
191
|
+
@@ -62756,6 +62801,7 @@ class Renderer {
|
|
166
192
|
materialDisplacementMap = overrideMaterial.displacementMap;
|
|
167
193
|
materialDisplacementScale = overrideMaterial.displacementScale;
|
|
168
194
|
materialDisplacementBias = overrideMaterial.displacementBias;
|
|
@@ -170,7 +196,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
170
196
|
|
|
171
197
|
if ( material.positionNode && material.positionNode.isNode ) {
|
|
172
198
|
|
|
173
|
-
@@ -62763,7 +
|
|
199
|
+
@@ -62763,7 +62809,15 @@ class Renderer {
|
|
174
200
|
|
|
175
201
|
}
|
|
176
202
|
|
|
@@ -187,7 +213,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
187
213
|
overrideMaterial.alphaMap = material.alphaMap;
|
|
188
214
|
overrideMaterial.displacementMap = material.displacementMap;
|
|
189
215
|
overrideMaterial.displacementScale = material.displacementScale;
|
|
190
|
-
@@ -62825,6 +
|
|
216
|
+
@@ -62825,6 +62879,7 @@ class Renderer {
|
|
191
217
|
scene.overrideMaterial.displacementMap = materialDisplacementMap;
|
|
192
218
|
scene.overrideMaterial.displacementScale = materialDisplacementScale;
|
|
193
219
|
scene.overrideMaterial.displacementBias = materialDisplacementBias;
|
|
@@ -195,7 +221,31 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
195
221
|
|
|
196
222
|
}
|
|
197
223
|
|
|
198
|
-
@@ -
|
|
224
|
+
@@ -78999,15 +78999,22 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
225
|
+
let snippet;
|
|
226
|
+
|
|
227
|
+
if ( depthSnippet ) {
|
|
228
|
+
|
|
229
|
+
snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ depthSnippet }, u32( ${ levelSnippet } ) )`;
|
|
230
|
+
|
|
231
|
+
} else {
|
|
232
|
+
|
|
233
|
+
- snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, u32( ${ levelSnippet } ) )`;
|
|
234
|
+
+ // naga v25 (wgpu-native v25.0.2.2) rejects a u32 sample index on a
|
|
235
|
+
+ // multisampled textureLoad; i32 is accepted by every backend. Mip-level
|
|
236
|
+
+ // operands (including the array form above) stay u32.
|
|
237
|
+
+ const sampleData = texture ? this.renderer?.backend?.utils?.getTextureSampleData?.( texture ) : null;
|
|
238
|
+
+ const backendMultisampled = sampleData !== null && sampleData !== undefined && sampleData.primarySamples > 1;
|
|
239
|
+
+ const targetMultisampled = Boolean( texture && texture.renderTarget && texture.renderTarget.samples > 1 );
|
|
240
|
+
+ const indexType = ( backendMultisampled || targetMultisampled ) ? 'i32' : 'u32';
|
|
241
|
+
+ snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ indexType }( ${ levelSnippet } ) )`;
|
|
242
|
+
|
|
243
|
+
if ( this.renderer.backend.compatibilityMode && texture.isDepthTexture ) {
|
|
244
|
+
|
|
245
|
+
snippet += '.x';
|
|
246
|
+
|
|
247
|
+
}
|
|
248
|
+
@@ -79586,7 +79641,8 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
199
249
|
|
|
200
250
|
uniformGPU = buffer;
|
|
201
251
|
|
|
@@ -205,7 +255,23 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
205
255
|
|
|
206
256
|
} else {
|
|
207
257
|
|
|
208
|
-
@@ -
|
|
258
|
+
@@ -80329,9 +80385,12 @@ ${ flowData.code }
|
|
259
|
+
const textureNode = uniform.node;
|
|
260
|
+
const texture = textureNode.value;
|
|
261
|
+
|
|
262
|
+
- // Cube textures always need samplers (they use textureSampleLevel, not textureLoad)
|
|
263
|
+
- // Also textureGather always need sampler.
|
|
264
|
+
- const needsSampler = texture.isCubeTexture === true || ( this.isUnfilterable( texture ) === false && textureNode.isStorageTextureNode !== true ) || textureNode.gatherNode !== null;
|
|
265
|
+
+ // The bind group layout freezes sampler inclusion once, in getUniformFromNode
|
|
266
|
+
+ // (nodeData.uniformGPU). Re-deriving it here from live texture filter state can
|
|
267
|
+
+ // disagree with that layout and shift every later binding index.
|
|
268
|
+
+ const nodeData = this.getDataFromNode( textureNode, shaderStage, this.globalCache );
|
|
269
|
+
+ const uniformGPU = nodeData.uniformGPU;
|
|
270
|
+
+ const needsSampler = Array.isArray( uniformGPU ) && uniformGPU[ 0 ].isSampler === true && uniformGPU[ 0 ].isSampledTexture !== true;
|
|
271
|
+
|
|
272
|
+
if ( needsSampler ) {
|
|
273
|
+
|
|
274
|
+
@@ -83814,6 +83873,14 @@ class WebGPUBackend extends Backend {
|
|
209
275
|
*/
|
|
210
276
|
this.compatibilityMode = null;
|
|
211
277
|
|
|
@@ -220,7 +286,7 @@ index 2e490796d4d4aa9479b04a5c46ddbf31cc05b1a1..d05599255744ccfca19dbfd0433da612
|
|
|
220
286
|
/**
|
|
221
287
|
* A reference to the device.
|
|
222
288
|
*
|
|
223
|
-
@@ -83926,7 +
|
|
289
|
+
@@ -83926,7 +83993,9 @@ class WebGPUBackend extends Backend {
|
|
224
290
|
xrCompatible: renderer.xr.enabled
|
|
225
291
|
};
|
|
226
292
|
|
|
@@ -375,7 +441,7 @@ index 5f3d213a016b37db3b2e5d022da3dd0234d86f6d..7a9a168f29773f37ef9a51ef9f07ade9
|
|
|
375
441
|
|
|
376
442
|
} else if ( binding.isStorageBuffer ) {
|
|
377
443
|
diff --git a/src/renderers/common/RenderObject.js b/src/renderers/common/RenderObject.js
|
|
378
|
-
index c900fa4c9654f8cf3512e96de52a2c1c83e189a6..
|
|
444
|
+
index c900fa4c9654f8cf3512e96de52a2c1c83e189a6..3a8b0012abf054f50ab66466bab7efa16fe1bc09 100644
|
|
379
445
|
--- a/src/renderers/common/RenderObject.js
|
|
380
446
|
+++ b/src/renderers/common/RenderObject.js
|
|
381
447
|
@@ -299,6 +299,18 @@ class RenderObject {
|
|
@@ -397,11 +463,29 @@ index c900fa4c9654f8cf3512e96de52a2c1c83e189a6..d0a87eabffc8f667ebe9b2f0eccda0dd
|
|
|
397
463
|
/**
|
|
398
464
|
* An event listener which is defined by `RenderObjects`. It performs
|
|
399
465
|
* clean up tasks when `dispose()` on this render object.
|
|
466
|
+
@@ -486,7 +498,7 @@ class RenderObject {
|
|
467
|
+
*/
|
|
468
|
+
getChainArray() {
|
|
469
|
+
|
|
470
|
+
- return [ this.object, this.material, this.context, this.lightsNode ];
|
|
471
|
+
+ return [ this.object, this.material, this.context, this.lightsNode, this._sourceMaterial ?? this.material ];
|
|
472
|
+
|
|
473
|
+
}
|
|
474
|
+
|
|
400
475
|
diff --git a/src/renderers/common/RenderObjects.js b/src/renderers/common/RenderObjects.js
|
|
401
|
-
index 676cc2bd4169427ed420f2467b1e6684bd2aeebc..
|
|
476
|
+
index 676cc2bd4169427ed420f2467b1e6684bd2aeebc..72569d0339314d26eb7a35ff2a8462a803c852a6 100644
|
|
402
477
|
--- a/src/renderers/common/RenderObjects.js
|
|
403
478
|
+++ b/src/renderers/common/RenderObjects.js
|
|
404
|
-
@@ -
|
|
479
|
+
@@ -97,6 +97,8 @@ class RenderObjects {
|
|
480
|
+
_chainKeys[ 1 ] = material;
|
|
481
|
+
_chainKeys[ 2 ] = renderContext;
|
|
482
|
+
_chainKeys[ 3 ] = lightsNode;
|
|
483
|
+
+ // Material groups share the shadow override, but must retain separate GPU bindings.
|
|
484
|
+
+ _chainKeys[ 4 ] = this.renderer._currentSourceMaterial ?? material;
|
|
485
|
+
|
|
486
|
+
//
|
|
487
|
+
|
|
488
|
+
@@ -124,9 +126,18 @@ class RenderObjects {
|
|
405
489
|
|
|
406
490
|
}
|
|
407
491
|
|
|
@@ -422,7 +506,7 @@ index 676cc2bd4169427ed420f2467b1e6684bd2aeebc..0fa661db793548254a15ff43f39207ab
|
|
|
422
506
|
|
|
423
507
|
renderObject.dispose();
|
|
424
508
|
|
|
425
|
-
@@ -136,6 +
|
|
509
|
+
@@ -136,6 +147,8 @@ class RenderObjects {
|
|
426
510
|
|
|
427
511
|
renderObject.version = material.version;
|
|
428
512
|
|
|
@@ -431,6 +515,14 @@ index 676cc2bd4169427ed420f2467b1e6684bd2aeebc..0fa661db793548254a15ff43f39207ab
|
|
|
431
515
|
}
|
|
432
516
|
|
|
433
517
|
}
|
|
518
|
+
@@ -148,6 +161,7 @@ class RenderObjects {
|
|
519
|
+
_chainKeys[ 1 ] = null;
|
|
520
|
+
_chainKeys[ 2 ] = null;
|
|
521
|
+
_chainKeys[ 3 ] = null;
|
|
522
|
+
+ _chainKeys[ 4 ] = null;
|
|
523
|
+
|
|
524
|
+
//
|
|
525
|
+
|
|
434
526
|
diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js
|
|
435
527
|
index 8dfc2ae055ad7b9d4c00e91f7d52a39e747caebe..e9d27b3ddad3b1f080a717d1f6683b60afd2b259 100644
|
|
436
528
|
--- a/src/renderers/common/Renderer.js
|
|
@@ -507,9 +599,33 @@ index 06e203693ed9b168a2bddf773cd8ce8dc23196e4..37b5e0be09967e8acbd38e570b71eb20
|
|
|
507
599
|
if ( adapter === null ) {
|
|
508
600
|
|
|
509
601
|
diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
|
|
510
|
-
index be07536301cd47ef2544e4a54f6abe17407f52af..
|
|
602
|
+
index be07536301cd47ef2544e4a54f6abe17407f52af..3b064a25a73fb35bcaed653794bda420bb004dec 100644
|
|
511
603
|
--- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
|
|
512
604
|
+++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js
|
|
605
|
+
@@ -736,15 +736,22 @@ generateTextureLoad( texture, textureProperty, uvIndexSnippet, levelSnippet, depthSnippet, offsetSnippet ) {
|
|
606
|
+
let snippet;
|
|
607
|
+
|
|
608
|
+
if ( depthSnippet ) {
|
|
609
|
+
|
|
610
|
+
snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ depthSnippet }, u32( ${ levelSnippet } ) )`;
|
|
611
|
+
|
|
612
|
+
} else {
|
|
613
|
+
|
|
614
|
+
- snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, u32( ${ levelSnippet } ) )`;
|
|
615
|
+
+ // naga v25 (wgpu-native v25.0.2.2) rejects a u32 sample index on a
|
|
616
|
+
+ // multisampled textureLoad; i32 is accepted by every backend. Mip-level
|
|
617
|
+
+ // operands (including the array form above) stay u32.
|
|
618
|
+
+ const sampleData = texture ? this.renderer?.backend?.utils?.getTextureSampleData?.( texture ) : null;
|
|
619
|
+
+ const backendMultisampled = sampleData !== null && sampleData !== undefined && sampleData.primarySamples > 1;
|
|
620
|
+
+ const targetMultisampled = Boolean( texture && texture.renderTarget && texture.renderTarget.samples > 1 );
|
|
621
|
+
+ const indexType = ( backendMultisampled || targetMultisampled ) ? 'i32' : 'u32';
|
|
622
|
+
+ snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ indexType }( ${ levelSnippet } ) )`;
|
|
623
|
+
|
|
624
|
+
if ( this.renderer.backend.compatibilityMode && texture.isDepthTexture ) {
|
|
625
|
+
|
|
626
|
+
snippet += '.x';
|
|
627
|
+
|
|
628
|
+
}
|
|
513
629
|
@@ -1323,7 +1323,8 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
514
630
|
|
|
515
631
|
uniformGPU = buffer;
|
|
@@ -520,3 +636,47 @@ index be07536301cd47ef2544e4a54f6abe17407f52af..6144b98ddeb5d53c5cf280bb51401385
|
|
|
520
636
|
|
|
521
637
|
} else {
|
|
522
638
|
|
|
639
|
+
@@ -2066,9 +2067,12 @@ ${ flowData.code }
|
|
640
|
+
const textureNode = uniform.node;
|
|
641
|
+
const texture = textureNode.value;
|
|
642
|
+
|
|
643
|
+
- // Cube textures always need samplers (they use textureSampleLevel, not textureLoad)
|
|
644
|
+
- // Also textureGather always need sampler.
|
|
645
|
+
- const needsSampler = texture.isCubeTexture === true || ( this.isUnfilterable( texture ) === false && textureNode.isStorageTextureNode !== true ) || textureNode.gatherNode !== null;
|
|
646
|
+
+ // The bind group layout freezes sampler inclusion once, in getUniformFromNode
|
|
647
|
+
+ // (nodeData.uniformGPU). Re-deriving it here from live texture filter state can
|
|
648
|
+
+ // disagree with that layout and shift every later binding index.
|
|
649
|
+
+ const nodeData = this.getDataFromNode( textureNode, shaderStage, this.globalCache );
|
|
650
|
+
+ const uniformGPU = nodeData.uniformGPU;
|
|
651
|
+
+ const needsSampler = Array.isArray( uniformGPU ) && uniformGPU[ 0 ].isSampler === true && uniformGPU[ 0 ].isSampledTexture !== true;
|
|
652
|
+
|
|
653
|
+
if ( needsSampler ) {
|
|
654
|
+
|
|
655
|
+
diff --git a/build/three.webgpu.nodes.js b/build/three.webgpu.nodes.js
|
|
656
|
+
index f707acc24f5711fdae065ce920b08435b0869c1b..4c679d417f79af7994f296c005fcfaf3416c3f7a 100644
|
|
657
|
+
--- a/build/three.webgpu.nodes.js
|
|
658
|
+
+++ b/build/three.webgpu.nodes.js
|
|
659
|
+
@@ -78999,15 +78999,22 @@ class WGSLNodeBuilder extends NodeBuilder {
|
|
660
|
+
let snippet;
|
|
661
|
+
|
|
662
|
+
if ( depthSnippet ) {
|
|
663
|
+
|
|
664
|
+
snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ depthSnippet }, u32( ${ levelSnippet } ) )`;
|
|
665
|
+
|
|
666
|
+
} else {
|
|
667
|
+
|
|
668
|
+
- snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, u32( ${ levelSnippet } ) )`;
|
|
669
|
+
+ // naga v25 (wgpu-native v25.0.2.2) rejects a u32 sample index on a
|
|
670
|
+
+ // multisampled textureLoad; i32 is accepted by every backend. Mip-level
|
|
671
|
+
+ // operands (including the array form above) stay u32.
|
|
672
|
+
+ const sampleData = texture ? this.renderer?.backend?.utils?.getTextureSampleData?.( texture ) : null;
|
|
673
|
+
+ const backendMultisampled = sampleData !== null && sampleData !== undefined && sampleData.primarySamples > 1;
|
|
674
|
+
+ const targetMultisampled = Boolean( texture && texture.renderTarget && texture.renderTarget.samples > 1 );
|
|
675
|
+
+ const indexType = ( backendMultisampled || targetMultisampled ) ? 'i32' : 'u32';
|
|
676
|
+
+ snippet = `textureLoad( ${ textureProperty }, ${ uvIndexSnippet }, ${ indexType }( ${ levelSnippet } ) )`;
|
|
677
|
+
|
|
678
|
+
if ( this.renderer.backend.compatibilityMode && texture.isDepthTexture ) {
|
|
679
|
+
|
|
680
|
+
snippet += '.x';
|
|
681
|
+
|
|
682
|
+
}
|