@forgeax/engine-render 0.1.24 → 0.1.25
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/README.md +100 -1
- package/dist/assembly/factory.d.ts +1 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/material/surface-projection.d.ts +24 -0
- package/dist/assembly/material/surface-projection.d.ts.map +1 -0
- package/dist/assembly/material-shader-policy.d.ts +20 -0
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts +1 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/assembly/webgpu-vertex-layouts.d.ts +89 -0
- package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -0
- package/dist/assets/asset-decoders.d.ts.map +1 -1
- package/dist/authoring.mjs +1 -1
- package/dist/{chunk-Q752SHI5.mjs → chunk-ADHHVYLW.mjs} +3 -3
- package/dist/chunk-ADHHVYLW.mjs.map +1 -0
- package/dist/{chunk-TFH5I7JJ.mjs → chunk-E34VL5VI.mjs} +3 -3
- package/dist/{chunk-TFH5I7JJ.mjs.map → chunk-E34VL5VI.mjs.map} +1 -1
- package/dist/{chunk-KZFTK6DO.mjs → chunk-HKXTW355.mjs} +7 -4
- package/dist/chunk-HKXTW355.mjs.map +1 -0
- package/dist/{chunk-YKB5DQV7.mjs → chunk-JSEDTZYS.mjs} +87 -19
- package/dist/chunk-JSEDTZYS.mjs.map +1 -0
- package/dist/{chunk-UQYS7FRM.mjs → chunk-KM2NOX2I.mjs} +4 -4
- package/dist/{chunk-UQYS7FRM.mjs.map → chunk-KM2NOX2I.mjs.map} +1 -1
- package/dist/{chunk-Z3YYH2MB.mjs → chunk-N3RT2EUR.mjs} +94 -41
- package/dist/chunk-N3RT2EUR.mjs.map +1 -0
- package/dist/{chunk-YRUYGA4A.mjs → chunk-TCLDX7SZ.mjs} +559 -138
- package/dist/chunk-TCLDX7SZ.mjs.map +1 -0
- package/dist/{chunk-RR5L4SRS.mjs → chunk-ZZ4YQ474.mjs} +2 -2
- package/dist/{chunk-RR5L4SRS.mjs.map → chunk-ZZ4YQ474.mjs.map} +1 -1
- package/dist/components/instances.d.ts +13 -31
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +865 -142
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +6 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +4 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +27 -9
- package/dist/index.mjs.map +1 -1
- package/dist/instance-buffer-cache.d.ts +7 -1
- package/dist/instance-buffer-cache.d.ts.map +1 -1
- package/dist/instances.d.ts +101 -0
- package/dist/instances.d.ts.map +1 -0
- package/dist/internal.mjs +5 -5
- package/dist/materials.d.ts +63 -2
- package/dist/materials.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +13 -2
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +22 -3
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +2 -1
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +11 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +1 -1
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/render-contract.d.ts +4 -0
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +4 -0
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +25 -1
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +4 -1
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +2 -0
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +12 -1
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/systems/skin-palette-allocator.d.ts +6 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/package.json +20 -20
- package/src/__tests__/gpu-scene.unit.test.ts +38 -2
- package/src/__tests__/instances-store.unit.test.ts +46 -0
- package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +5 -13
- package/src/__tests__/material-pass-policy.unit.test.ts +69 -0
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +69 -0
- package/src/__tests__/material-surface-authoring.test-d.ts +62 -0
- package/src/__tests__/materials-transmission-validation.unit.test.ts +24 -0
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +0 -4
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +42 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +33 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +30 -2
- package/src/__tests__/surface-adjacent-regressions.integration.test.ts +60 -0
- package/src/__tests__/surface-standard-pipeline.browser.test.ts +20 -0
- package/src/__tests__/surface-standard-pipeline.dawn.test.ts +20 -0
- package/src/__tests__/surface-standard-pipeline.fixture.ts +132 -0
- package/src/__tests__/surface-variant-provenance.integration.test.ts +33 -0
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +1 -1
- package/src/assembly/factory.ts +2 -0
- package/src/assembly/material/__tests__/standard-layer-plan-projection.integration.test.ts +18 -0
- package/src/assembly/material/__tests__/surface-pass-projection.integration.test.ts +62 -0
- package/src/assembly/material/surface-projection.ts +90 -0
- package/src/assembly/material-shader-policy.ts +88 -10
- package/src/assembly/recovery/renderer-recover.ts +1 -1
- package/src/assembly/webgpu-ready.ts +3 -16
- package/src/assembly/webgpu-renderer.ts +90 -158
- package/src/assembly/webgpu-vertex-layouts.ts +132 -0
- package/src/assets/asset-decoders.ts +27 -4
- package/src/components/instances.ts +14 -197
- package/src/gpu-driven/production-raster.ts +34 -1
- package/src/gpu-scene.ts +84 -29
- package/src/index.ts +1 -0
- package/src/instance-buffer-cache.ts +14 -1
- package/src/instances.ts +288 -0
- package/src/materials.ts +107 -12
- package/src/pipeline-builder.ts +14 -10
- package/src/pipeline-spec.ts +6 -14
- package/src/record/frame-snapshot.ts +19 -2
- package/src/record/frame.ts +31 -2
- package/src/record/main-pass-geometry.ts +498 -59
- package/src/record/main-pass-sprite-draws.ts +1 -1
- package/src/record/render-context.ts +4 -0
- package/src/record/shadow-pass.ts +46 -99
- package/src/recovery/render-system-candidate.ts +41 -4
- package/src/render-contract.ts +4 -0
- package/src/render-system-extract-tail.ts +107 -51
- package/src/render-system-extract.ts +53 -26
- package/src/render-system.ts +40 -5
- package/src/scene/render-scene-types.ts +2 -0
- package/src/scene/render-scene.ts +579 -36
- package/src/systems/skin-palette-allocator.ts +129 -7
- package/src/transmission/__tests__/surface-compatibility.integration.test.ts +32 -0
- package/dist/chunk-KZFTK6DO.mjs.map +0 -1
- package/dist/chunk-Q752SHI5.mjs.map +0 -1
- package/dist/chunk-YKB5DQV7.mjs.map +0 -1
- package/dist/chunk-YRUYGA4A.mjs.map +0 -1
- package/dist/chunk-Z3YYH2MB.mjs.map +0 -1
|
@@ -30,7 +30,6 @@ import { createRenderReadLease, type RenderReadLease } from '@forgeax/engine-ecs
|
|
|
30
30
|
import {
|
|
31
31
|
deriveVertexBufferLayout,
|
|
32
32
|
deriveVertexBufferLayoutFromProjection,
|
|
33
|
-
type GpuVertexBufferLayoutEntry,
|
|
34
33
|
type VertexLayoutProjection,
|
|
35
34
|
} from '@forgeax/engine-geometry';
|
|
36
35
|
import type {
|
|
@@ -176,7 +175,9 @@ import {
|
|
|
176
175
|
makeShaderDeviceAdapter,
|
|
177
176
|
normalizeMaterialShaderVariantSet,
|
|
178
177
|
prepareMaterialShaders,
|
|
178
|
+
resolveMaterialShaderBackendArtifactKey,
|
|
179
179
|
resolveMaterialShaderBindingContract,
|
|
180
|
+
resolveMaterialShaderUvSetCount,
|
|
180
181
|
resolveMaterialShaderVariantSet,
|
|
181
182
|
resolveMaterialShaderVertexInputContract,
|
|
182
183
|
type ShaderDeviceAdapterInternal,
|
|
@@ -214,6 +215,17 @@ import {
|
|
|
214
215
|
import { STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES } from './shader-prewarm-policy';
|
|
215
216
|
import { buildReadyWebGPU } from './webgpu-ready';
|
|
216
217
|
import { DEPTH_TEXTURE_FORMAT, HDR_COLOR_ATTACHMENT_FORMAT } from './webgpu-ready-contract';
|
|
218
|
+
import {
|
|
219
|
+
BILLBOARD_MATERIAL_INSTANCE_VERTEX_BUFFERS,
|
|
220
|
+
DEFAULT_VERTEX_ATTRS,
|
|
221
|
+
MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS,
|
|
222
|
+
PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
223
|
+
POSITION_SIZE_COLOR_INSTANCE_VERTEX_BUFFERS,
|
|
224
|
+
PREPARED_INSTANCE_VERTEX_ATTRS,
|
|
225
|
+
PREPARED_MATERIAL_INSTANCE_VERTEX_ATTRS,
|
|
226
|
+
TOPOLOGY_SEGMENT_INSTANCE_VERTEX_BUFFERS,
|
|
227
|
+
toGpuVertexBufferLayouts,
|
|
228
|
+
} from './webgpu-vertex-layouts';
|
|
217
229
|
|
|
218
230
|
export type { BundlerOptions } from './bundler-contract';
|
|
219
231
|
export { assembleMaterialProjection } from './material/assembly';
|
|
@@ -231,7 +243,9 @@ export {
|
|
|
231
243
|
allowsUnlitPreparedFallback,
|
|
232
244
|
isSharedMaterialUserRegionCompatible,
|
|
233
245
|
normalizeMaterialShaderVariantSet,
|
|
246
|
+
resolveMaterialShaderBackendArtifactKey,
|
|
234
247
|
resolveMaterialShaderBindingContract,
|
|
248
|
+
resolveMaterialShaderUvSetCount,
|
|
235
249
|
resolveMaterialShaderVariantSet,
|
|
236
250
|
resolveMaterialShaderVertexInputContract,
|
|
237
251
|
selectNoColorPbrVariant,
|
|
@@ -273,139 +287,6 @@ function adaptMipmapShaderModuleFactory(
|
|
|
273
287
|
// Transform.world mat4 derivation (audio listener sync + picking read the
|
|
274
288
|
// derived Transform.world from scripts).
|
|
275
289
|
|
|
276
|
-
// feat-20260611-fox-skinning-vertex-attribute-chain M4 / w16 (D-4): a single
|
|
277
|
-
// shared empty ArrayBuffer used by `buildPipelineContext` to synthesize the
|
|
278
|
-
// 6-key VertexAttributeMap when no caller-supplied attributes are available
|
|
279
|
-
// for the pbr-skin path. `deriveVertexBufferLayout` is the SSOT for the layout
|
|
280
|
-
// (vertex-attribute-layout.ts) and reads only key presence -- value identity
|
|
281
|
-
// never matters -- so a zero-byte ArrayBuffer per key produces the exact
|
|
282
|
-
// 6-attribute / 72-byte stride layout that the @forgeax::pbr-skin shader's
|
|
283
|
-
// @location(0..5) declarations expect.
|
|
284
|
-
const PBR_SKIN_SENTINEL_ATTR_BUFFER = new ArrayBuffer(0);
|
|
285
|
-
|
|
286
|
-
/** Default 4-attribute vertex layout used as fallback when meshAttributes is undefined. */
|
|
287
|
-
const DEFAULT_VERTEX_ATTRS: VertexAttributeMap = {
|
|
288
|
-
position: new Float32Array(0),
|
|
289
|
-
normal: new Float32Array(0),
|
|
290
|
-
uv: new Float32Array(0),
|
|
291
|
-
tangent: new Float32Array(0),
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
const PREPARED_INSTANCE_VERTEX_ATTRS: VertexAttributeMap = {
|
|
295
|
-
position: new Float32Array(0),
|
|
296
|
-
normal: new Float32Array(0),
|
|
297
|
-
uv: new Float32Array(0),
|
|
298
|
-
tangent: new Float32Array(0),
|
|
299
|
-
uv1: new Float32Array(0),
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const PREPARED_MATERIAL_INSTANCE_VERTEX_ATTRS: VertexAttributeMap = {
|
|
303
|
-
position: new Float32Array(0),
|
|
304
|
-
normal: new Float32Array(0),
|
|
305
|
-
uv: new Float32Array(0),
|
|
306
|
-
tangent: new Float32Array(0),
|
|
307
|
-
skinIndex: new Uint16Array(0),
|
|
308
|
-
skinWeight: new Float32Array(0),
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
const POSITION_SIZE_COLOR_INSTANCE_VERTEX_BUFFERS = [
|
|
312
|
-
{
|
|
313
|
-
arrayStride: 9 * 4,
|
|
314
|
-
stepMode: 'instance' as const,
|
|
315
|
-
attributes: [
|
|
316
|
-
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
317
|
-
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x2' as const },
|
|
318
|
-
{ shaderLocation: 2, offset: 5 * 4, format: 'float32x4' as const },
|
|
319
|
-
],
|
|
320
|
-
},
|
|
321
|
-
] satisfies readonly GPUVertexBufferLayout[];
|
|
322
|
-
|
|
323
|
-
const BILLBOARD_MATERIAL_INSTANCE_VERTEX_BUFFERS = [
|
|
324
|
-
{
|
|
325
|
-
arrayStride: 31 * 4,
|
|
326
|
-
stepMode: 'instance' as const,
|
|
327
|
-
attributes: [
|
|
328
|
-
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
329
|
-
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x2' as const },
|
|
330
|
-
{ shaderLocation: 2, offset: 5 * 4, format: 'float32x2' as const },
|
|
331
|
-
{ shaderLocation: 3, offset: 7 * 4, format: 'float32x4' as const },
|
|
332
|
-
{ shaderLocation: 4, offset: 11 * 4, format: 'float32x4' as const },
|
|
333
|
-
{ shaderLocation: 5, offset: 15 * 4, format: 'float32x4' as const },
|
|
334
|
-
{ shaderLocation: 6, offset: 19 * 4, format: 'float32x4' as const },
|
|
335
|
-
{ shaderLocation: 7, offset: 23 * 4, format: 'float32x4' as const },
|
|
336
|
-
{ shaderLocation: 8, offset: 27 * 4, format: 'float32x4' as const },
|
|
337
|
-
],
|
|
338
|
-
},
|
|
339
|
-
] satisfies readonly GPUVertexBufferLayout[];
|
|
340
|
-
|
|
341
|
-
const TOPOLOGY_SEGMENT_INSTANCE_VERTEX_BUFFERS = [
|
|
342
|
-
{
|
|
343
|
-
arrayStride: 12 * 4,
|
|
344
|
-
stepMode: 'instance' as const,
|
|
345
|
-
attributes: [
|
|
346
|
-
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
347
|
-
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x3' as const },
|
|
348
|
-
{ shaderLocation: 2, offset: 6 * 4, format: 'float32x4' as const },
|
|
349
|
-
{ shaderLocation: 3, offset: 10 * 4, format: 'float32x2' as const },
|
|
350
|
-
],
|
|
351
|
-
},
|
|
352
|
-
] satisfies readonly GPUVertexBufferLayout[];
|
|
353
|
-
|
|
354
|
-
const MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS = [
|
|
355
|
-
{
|
|
356
|
-
arrayStride: 12 * 4,
|
|
357
|
-
stepMode: 'vertex' as const,
|
|
358
|
-
attributes: [
|
|
359
|
-
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
360
|
-
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x3' as const },
|
|
361
|
-
{ shaderLocation: 2, offset: 6 * 4, format: 'float32x2' as const },
|
|
362
|
-
{ shaderLocation: 3, offset: 8 * 4, format: 'float32x4' as const },
|
|
363
|
-
],
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
arrayStride: 28 * 4,
|
|
367
|
-
stepMode: 'instance' as const,
|
|
368
|
-
attributes: [
|
|
369
|
-
{ shaderLocation: 4, offset: 0, format: 'float32x3' as const },
|
|
370
|
-
{ shaderLocation: 5, offset: 3 * 4, format: 'float32x3' as const },
|
|
371
|
-
{ shaderLocation: 6, offset: 6 * 4, format: 'float32x3' as const },
|
|
372
|
-
{ shaderLocation: 7, offset: 9 * 4, format: 'float32x3' as const },
|
|
373
|
-
{ shaderLocation: 8, offset: 12 * 4, format: 'float32x4' as const },
|
|
374
|
-
{ shaderLocation: 9, offset: 16 * 4, format: 'float32x4' as const },
|
|
375
|
-
{ shaderLocation: 10, offset: 20 * 4, format: 'float32x4' as const },
|
|
376
|
-
{ shaderLocation: 11, offset: 24 * 4, format: 'float32x4' as const },
|
|
377
|
-
],
|
|
378
|
-
},
|
|
379
|
-
] satisfies readonly GPUVertexBufferLayout[];
|
|
380
|
-
|
|
381
|
-
function toGpuVertexFormat(
|
|
382
|
-
format: GpuVertexBufferLayoutEntry['attributes'][number]['format'],
|
|
383
|
-
): GPUVertexFormat {
|
|
384
|
-
switch (format) {
|
|
385
|
-
case 'float32x2':
|
|
386
|
-
case 'float32x3':
|
|
387
|
-
case 'float32x4':
|
|
388
|
-
case 'uint16x4':
|
|
389
|
-
return format;
|
|
390
|
-
default:
|
|
391
|
-
throw new Error(`Unsupported geometry vertex format: ${format}`);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function toGpuVertexBufferLayouts(
|
|
396
|
-
entries: readonly GpuVertexBufferLayoutEntry[],
|
|
397
|
-
): readonly GPUVertexBufferLayout[] {
|
|
398
|
-
return entries.map((entry) => ({
|
|
399
|
-
arrayStride: entry.arrayStride,
|
|
400
|
-
...(entry.stepMode === undefined ? {} : { stepMode: entry.stepMode }),
|
|
401
|
-
attributes: entry.attributes.map((attribute) => ({
|
|
402
|
-
shaderLocation: attribute.shaderLocation,
|
|
403
|
-
offset: attribute.offset,
|
|
404
|
-
format: toGpuVertexFormat(attribute.format),
|
|
405
|
-
})),
|
|
406
|
-
}));
|
|
407
|
-
}
|
|
408
|
-
|
|
409
290
|
/**
|
|
410
291
|
* Bundler-layer injection accepted by `createRenderer` (and proxied by
|
|
411
292
|
* `createApp`) as the optional third argument.
|
|
@@ -1954,15 +1835,19 @@ async function makeWebGPURenderer(
|
|
|
1954
1835
|
vertexLayout === RENDER_FEATURE_VERTEX_LAYOUTS.meshGeometryMaterialInstance)
|
|
1955
1836
|
? getOrBuildPreparedMaterialPipelineLayout(materialShaderId)
|
|
1956
1837
|
: null;
|
|
1838
|
+
const materialShaderLookup =
|
|
1839
|
+
materialShaderId === undefined
|
|
1840
|
+
? undefined
|
|
1841
|
+
: getShader().findMaterialArtifact(materialShaderId);
|
|
1842
|
+
const materialShaderSource = materialShaderLookup?.ok
|
|
1843
|
+
? materialShaderLookup.value.source
|
|
1844
|
+
: undefined;
|
|
1957
1845
|
const vertexInputContract =
|
|
1958
1846
|
materialShaderId === undefined
|
|
1959
1847
|
? 'render-material'
|
|
1960
|
-
:
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
? resolveMaterialShaderVertexInputContract(lookup.value.source)
|
|
1964
|
-
: 'render-material';
|
|
1965
|
-
})();
|
|
1848
|
+
: materialShaderSource !== undefined
|
|
1849
|
+
? resolveMaterialShaderVertexInputContract(materialShaderSource)
|
|
1850
|
+
: 'render-material';
|
|
1966
1851
|
const currentState = currentPipelineState();
|
|
1967
1852
|
const pipelineLayout =
|
|
1968
1853
|
group0Layout !== null
|
|
@@ -1979,9 +1864,16 @@ async function makeWebGPURenderer(
|
|
|
1979
1864
|
? perShaderLayout.pipelineLayout
|
|
1980
1865
|
: selectPipelineLayoutForVariant(currentState, variantSet, layoutKind);
|
|
1981
1866
|
if (pipelineLayout === null) return null;
|
|
1867
|
+
const currentMaterialUvSetCounts = currentMaterialShaderUvSetCounts();
|
|
1982
1868
|
const resolvedUvSetCount =
|
|
1983
1869
|
materialShaderId !== undefined
|
|
1984
|
-
?
|
|
1870
|
+
? (currentMaterialUvSetCounts.get(materialShaderId) ??
|
|
1871
|
+
(materialShaderSource !== undefined
|
|
1872
|
+
? resolveMaterialShaderUvSetCount(
|
|
1873
|
+
materialShaderSource,
|
|
1874
|
+
currentMaterialUvSetCounts.get(materialShaderId),
|
|
1875
|
+
)
|
|
1876
|
+
: undefined))
|
|
1985
1877
|
: undefined;
|
|
1986
1878
|
const vertexBuffers: readonly GPUVertexBufferLayout[] =
|
|
1987
1879
|
vertexInputContract === 'none'
|
|
@@ -2252,15 +2144,31 @@ async function makeWebGPURenderer(
|
|
|
2252
2144
|
(currentState !== null ? currentState.colorAttachmentFormat : 'bgra8unorm-srgb');
|
|
2253
2145
|
// feat-20260629 M4: auto-fill shaderUvSetCount from naga reflection
|
|
2254
2146
|
// (stored in materialShaderUvSetCounts during prepareMaterialShaders).
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
|
|
2147
|
+
const pipelineShaderId = resolveMaterialShaderBackendArtifactKey(
|
|
2148
|
+
materialShaderId,
|
|
2149
|
+
internals.device.caps.backendKind,
|
|
2150
|
+
assets.getMaterialArtifact(materialShaderId),
|
|
2151
|
+
);
|
|
2152
|
+
const materialShaderLookup = getShader().findMaterialArtifact(pipelineShaderId);
|
|
2153
|
+
const materialShaderSource = materialShaderLookup.ok
|
|
2154
|
+
? materialShaderLookup.value.source
|
|
2155
|
+
: undefined;
|
|
2156
|
+
const currentMaterialUvSetCounts = currentMaterialShaderUvSetCounts();
|
|
2157
|
+
const materialUvSetCount = resolveMaterialShaderUvSetCount(
|
|
2158
|
+
materialShaderSource,
|
|
2159
|
+
currentMaterialUvSetCounts.get(pipelineShaderId),
|
|
2160
|
+
);
|
|
2161
|
+
if (materialUvSetCount !== undefined) {
|
|
2162
|
+
currentMaterialUvSetCounts.set(pipelineShaderId, materialUvSetCount);
|
|
2163
|
+
}
|
|
2164
|
+
const resolvedUvSetCount = shaderUvSetCount ?? materialUvSetCount;
|
|
2165
|
+
const manifestEntry = findMaterialShaderManifestEntry(pipelineShaderId);
|
|
2258
2166
|
// Sprite's omitted request is the boot-selected default artifact. The
|
|
2259
2167
|
// explicit empty key remains the PER_INSTANCE_REGION=true request used by
|
|
2260
2168
|
// SpriteInstances, so only that path is capability-rewritten here.
|
|
2261
2169
|
const resolvedVariantSet =
|
|
2262
2170
|
variantSet === undefined &&
|
|
2263
|
-
(
|
|
2171
|
+
(pipelineShaderId === 'forgeax::sprite' || pipelineShaderId === 'forgeax::sprite-lit')
|
|
2264
2172
|
? undefined
|
|
2265
2173
|
: resolveCachedMaterialShaderVariantSet(variantSet, manifestEntry);
|
|
2266
2174
|
// The record stage also composes engine capability axes for user shaders.
|
|
@@ -2272,6 +2180,12 @@ async function makeWebGPURenderer(
|
|
|
2272
2180
|
// A single-source manifest entry must stay on its canonical module key;
|
|
2273
2181
|
// otherwise a synthesized capability axis races the prewarmed module.
|
|
2274
2182
|
let effectiveVariantSet = normalizeMaterialShaderVariantSet(resolvedVariantSet, manifestEntry);
|
|
2183
|
+
// Runtime-published authored materials are installed from their immutable
|
|
2184
|
+
// cooked artifact, not from the build-time material-shader manifest. The
|
|
2185
|
+
// published artifact identity already selects the backend-specific bytes;
|
|
2186
|
+
// an absent manifest therefore means the source itself is the canonical
|
|
2187
|
+
// pipeline module and must not carry a synthesized capability key.
|
|
2188
|
+
if (manifestEntry === undefined) effectiveVariantSet = undefined;
|
|
2275
2189
|
if (
|
|
2276
2190
|
vertexLayoutProjection === undefined &&
|
|
2277
2191
|
manifestEntry?.variants.some((variant) => 'VERTEX_COLOR_AVAILABLE' in variant.defines)
|
|
@@ -2294,7 +2208,7 @@ async function makeWebGPURenderer(
|
|
|
2294
2208
|
}
|
|
2295
2209
|
if (
|
|
2296
2210
|
manifestEntry !== undefined &&
|
|
2297
|
-
!
|
|
2211
|
+
!pipelineShaderId.startsWith('forgeax::') &&
|
|
2298
2212
|
effectiveVariantSet !== undefined &&
|
|
2299
2213
|
effectiveVariantSet !== ''
|
|
2300
2214
|
) {
|
|
@@ -2323,7 +2237,7 @@ async function makeWebGPURenderer(
|
|
|
2323
2237
|
const colorFormat: GPUTextureFormat = isHdr ? 'rgba16float' : ldrColorFormat;
|
|
2324
2238
|
const isDepthOnlyPass = passKindPolicyTable[passKind]?.shape === 'depth-only';
|
|
2325
2239
|
const spec: PipelineSpec = {
|
|
2326
|
-
shader: { id:
|
|
2240
|
+
shader: { id: pipelineShaderId, passKind, variantSet: effectiveVariantSet },
|
|
2327
2241
|
attachments: {
|
|
2328
2242
|
colorFormats:
|
|
2329
2243
|
additionalColorFormats === undefined
|
|
@@ -2362,7 +2276,7 @@ async function makeWebGPURenderer(
|
|
|
2362
2276
|
const cached = currentPipelineCacheState().materialShaderPipelineCache.get(cacheKey);
|
|
2363
2277
|
if (cached !== undefined) return cached;
|
|
2364
2278
|
if (pipelineState === null) return null;
|
|
2365
|
-
const lookup = getShader().findMaterialArtifact(
|
|
2279
|
+
const lookup = getShader().findMaterialArtifact(pipelineShaderId);
|
|
2366
2280
|
if (!lookup.ok) {
|
|
2367
2281
|
if (shouldDeferMissingPreparedMaterialShader(vertexLayout)) {
|
|
2368
2282
|
// Prepared VFX draws bind only their feature-owned groups. The
|
|
@@ -2377,7 +2291,7 @@ async function makeWebGPURenderer(
|
|
|
2377
2291
|
// the standard pipeline from the engine-shipped PBR entry.
|
|
2378
2292
|
if (
|
|
2379
2293
|
renderState === undefined &&
|
|
2380
|
-
!allowsUnlitPreparedFallback(depthFormatOverride, vertexLayout,
|
|
2294
|
+
!allowsUnlitPreparedFallback(depthFormatOverride, vertexLayout, pipelineShaderId)
|
|
2381
2295
|
) {
|
|
2382
2296
|
// A prepared feature declares the vertex layout it owns. Falling back
|
|
2383
2297
|
// to default-unlit here would pair that data with the unlit shader's
|
|
@@ -2392,7 +2306,7 @@ async function makeWebGPURenderer(
|
|
|
2392
2306
|
return buildAndCachePipeline(
|
|
2393
2307
|
cacheKey,
|
|
2394
2308
|
unlitLookup.value,
|
|
2395
|
-
`pbr-pipeline-prepared-${
|
|
2309
|
+
`pbr-pipeline-prepared-${pipelineShaderId}`,
|
|
2396
2310
|
// Reuse the eagerly compiled engine unlit module. The adapter cache
|
|
2397
2311
|
// is seeded under this label during renderer readiness; a new label
|
|
2398
2312
|
// would keep color-only prepared passes in perpetual warm-up.
|
|
@@ -2420,7 +2334,7 @@ async function makeWebGPURenderer(
|
|
|
2420
2334
|
return buildAndCachePipeline(
|
|
2421
2335
|
cacheKey,
|
|
2422
2336
|
{ source: pbrEntry.wgsl, paramSchema: [] },
|
|
2423
|
-
`pbr-pipeline-fallback-${
|
|
2337
|
+
`pbr-pipeline-fallback-${pipelineShaderId}${isHdr ? '-hdr' : ''}`,
|
|
2424
2338
|
// w16-b: module identity is the fallback PBR source, stable across
|
|
2425
2339
|
// topology / renderState / HDR (all baked into the PSO) so every
|
|
2426
2340
|
// variant reuses one compiled module.
|
|
@@ -2454,8 +2368,8 @@ async function makeWebGPURenderer(
|
|
|
2454
2368
|
// layout resolution so buildPipelineContext derives the layout that
|
|
2455
2369
|
// matches the compiled module. Skin keeps its
|
|
2456
2370
|
// own id so the pbr-skin fail-fast (null layout) is preserved.
|
|
2457
|
-
|
|
2458
|
-
?
|
|
2371
|
+
pipelineShaderId === SKIN_MATERIAL_SHADER_ID
|
|
2372
|
+
? pipelineShaderId
|
|
2459
2373
|
: 'forgeax::default-standard-pbr',
|
|
2460
2374
|
// feat-20260611-fox-skinning-vertex-attribute-chain M4 / w16 (D-4):
|
|
2461
2375
|
// forward meshAttributes through the fallback path so the pbr-skin
|
|
@@ -2489,7 +2403,7 @@ async function makeWebGPURenderer(
|
|
|
2489
2403
|
if (effectiveVariantSet !== undefined) {
|
|
2490
2404
|
const registry = getShader();
|
|
2491
2405
|
for (const msEntry of registry.materialShaderManifestEntries()) {
|
|
2492
|
-
if (msEntry.identifier ===
|
|
2406
|
+
if (msEntry.identifier === pipelineShaderId) {
|
|
2493
2407
|
const variant = findVariantByKey(msEntry, effectiveVariantSet);
|
|
2494
2408
|
if (variant) {
|
|
2495
2409
|
// M3 / w12-w13: variant substitution carries the same source +
|
|
@@ -2516,7 +2430,7 @@ async function makeWebGPURenderer(
|
|
|
2516
2430
|
return buildAndCachePipeline(
|
|
2517
2431
|
cacheKey,
|
|
2518
2432
|
shaderEntry,
|
|
2519
|
-
`pbr-pipeline-${
|
|
2433
|
+
`pbr-pipeline-${pipelineShaderId}${isHdr ? '-hdr' : ''}${passKindLabelSegment}`,
|
|
2520
2434
|
// feat-20260609 M4 / w31: when variantSet is non-empty, the module identity
|
|
2521
2435
|
// includes the variant key so URP (STORAGE_BUFFER_AVAILABLE=true) and HDRP
|
|
2522
2436
|
// (CLUSTER_FORWARD_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true) variants
|
|
@@ -2530,8 +2444,8 @@ async function makeWebGPURenderer(
|
|
|
2530
2444
|
// empty-tail segment); module identity stays a function of the
|
|
2531
2445
|
// exact variantSet string.
|
|
2532
2446
|
effectiveVariantSet !== undefined
|
|
2533
|
-
? `module-${
|
|
2534
|
-
: `module-${
|
|
2447
|
+
? `module-${pipelineShaderId}#${effectiveVariantSet}`
|
|
2448
|
+
: `module-${pipelineShaderId}`,
|
|
2535
2449
|
isHdr,
|
|
2536
2450
|
renderState,
|
|
2537
2451
|
topology,
|
|
@@ -2547,7 +2461,7 @@ async function makeWebGPURenderer(
|
|
|
2547
2461
|
// bug-20260611-skin-pipeline-layout: thread the registered materialShaderId
|
|
2548
2462
|
// so buildPipelineContext can resolve LayoutKind='pbr-skin' for the skin
|
|
2549
2463
|
// shader (2-entry mesh-array BGL).
|
|
2550
|
-
|
|
2464
|
+
pipelineShaderId,
|
|
2551
2465
|
// feat-20260611-fox-skinning-vertex-attribute-chain M4 / w16 (D-4):
|
|
2552
2466
|
// forward meshAttributes so the pbr-skin path's vertex buffer layout
|
|
2553
2467
|
// is derived via the SSOT (deriveVertexBufferLayout). For URP/HDRP
|
|
@@ -3009,6 +2923,24 @@ async function makeWebGPURenderer(
|
|
|
3009
2923
|
featureDiagnostics: internals.featureHost?.diagnostics() ?? Object.freeze([]),
|
|
3010
2924
|
frustumStats: Object.freeze({ ...renderSystem.frustumStats }),
|
|
3011
2925
|
visibilityStats: Object.freeze({ ...renderSystem.visibilityStats }),
|
|
2926
|
+
instanceCollections: Object.freeze(
|
|
2927
|
+
renderSystem.instanceCollectionsInspection.map((collection) =>
|
|
2928
|
+
Object.freeze({
|
|
2929
|
+
...collection,
|
|
2930
|
+
uploadRanges: Object.freeze(
|
|
2931
|
+
collection.uploadRanges.map((range) => Object.freeze({ ...range })),
|
|
2932
|
+
),
|
|
2933
|
+
...(collection.error === undefined
|
|
2934
|
+
? {}
|
|
2935
|
+
: {
|
|
2936
|
+
error: Object.freeze({
|
|
2937
|
+
...collection.error,
|
|
2938
|
+
detail: Object.freeze({ ...collection.error.detail }),
|
|
2939
|
+
}),
|
|
2940
|
+
}),
|
|
2941
|
+
}),
|
|
2942
|
+
),
|
|
2943
|
+
),
|
|
3012
2944
|
renderScene: renderSystem.renderScene,
|
|
3013
2945
|
reflectionProbes: renderSystem.reflectionProbes,
|
|
3014
2946
|
ssrDependencies: renderSystem.ssrDependencies,
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { GpuVertexBufferLayoutEntry } from '@forgeax/engine-geometry';
|
|
2
|
+
import type { VertexAttributeMap } from '@forgeax/engine-types';
|
|
3
|
+
|
|
4
|
+
// feat-20260611-fox-skinning-vertex-attribute-chain M4 / w16 (D-4): a single
|
|
5
|
+
// shared empty ArrayBuffer used by the pipeline builder to synthesize the
|
|
6
|
+
// pbr-skin attribute map when no caller-supplied attributes are available.
|
|
7
|
+
// `deriveVertexBufferLayout` is the SSOT for the layout and reads only key
|
|
8
|
+
// presence, so a zero-byte buffer per key preserves the six-attribute stride.
|
|
9
|
+
export const PBR_SKIN_SENTINEL_ATTR_BUFFER = new ArrayBuffer(0);
|
|
10
|
+
|
|
11
|
+
/** Default 4-attribute vertex layout used as fallback when meshAttributes is undefined. */
|
|
12
|
+
export const DEFAULT_VERTEX_ATTRS: VertexAttributeMap = {
|
|
13
|
+
position: new Float32Array(0),
|
|
14
|
+
normal: new Float32Array(0),
|
|
15
|
+
uv: new Float32Array(0),
|
|
16
|
+
tangent: new Float32Array(0),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const PREPARED_INSTANCE_VERTEX_ATTRS: VertexAttributeMap = {
|
|
20
|
+
position: new Float32Array(0),
|
|
21
|
+
normal: new Float32Array(0),
|
|
22
|
+
uv: new Float32Array(0),
|
|
23
|
+
tangent: new Float32Array(0),
|
|
24
|
+
uv1: new Float32Array(0),
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const PREPARED_MATERIAL_INSTANCE_VERTEX_ATTRS: VertexAttributeMap = {
|
|
28
|
+
position: new Float32Array(0),
|
|
29
|
+
normal: new Float32Array(0),
|
|
30
|
+
uv: new Float32Array(0),
|
|
31
|
+
tangent: new Float32Array(0),
|
|
32
|
+
skinIndex: new Uint16Array(0),
|
|
33
|
+
skinWeight: new Float32Array(0),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const POSITION_SIZE_COLOR_INSTANCE_VERTEX_BUFFERS = [
|
|
37
|
+
{
|
|
38
|
+
arrayStride: 9 * 4,
|
|
39
|
+
stepMode: 'instance' as const,
|
|
40
|
+
attributes: [
|
|
41
|
+
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
42
|
+
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x2' as const },
|
|
43
|
+
{ shaderLocation: 2, offset: 5 * 4, format: 'float32x4' as const },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
] satisfies readonly GPUVertexBufferLayout[];
|
|
47
|
+
|
|
48
|
+
export const BILLBOARD_MATERIAL_INSTANCE_VERTEX_BUFFERS = [
|
|
49
|
+
{
|
|
50
|
+
arrayStride: 31 * 4,
|
|
51
|
+
stepMode: 'instance' as const,
|
|
52
|
+
attributes: [
|
|
53
|
+
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
54
|
+
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x2' as const },
|
|
55
|
+
{ shaderLocation: 2, offset: 5 * 4, format: 'float32x2' as const },
|
|
56
|
+
{ shaderLocation: 3, offset: 7 * 4, format: 'float32x4' as const },
|
|
57
|
+
{ shaderLocation: 4, offset: 11 * 4, format: 'float32x4' as const },
|
|
58
|
+
{ shaderLocation: 5, offset: 15 * 4, format: 'float32x4' as const },
|
|
59
|
+
{ shaderLocation: 6, offset: 19 * 4, format: 'float32x4' as const },
|
|
60
|
+
{ shaderLocation: 7, offset: 23 * 4, format: 'float32x4' as const },
|
|
61
|
+
{ shaderLocation: 8, offset: 27 * 4, format: 'float32x4' as const },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
] satisfies readonly GPUVertexBufferLayout[];
|
|
65
|
+
|
|
66
|
+
export const TOPOLOGY_SEGMENT_INSTANCE_VERTEX_BUFFERS = [
|
|
67
|
+
{
|
|
68
|
+
arrayStride: 12 * 4,
|
|
69
|
+
stepMode: 'instance' as const,
|
|
70
|
+
attributes: [
|
|
71
|
+
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
72
|
+
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x3' as const },
|
|
73
|
+
{ shaderLocation: 2, offset: 6 * 4, format: 'float32x4' as const },
|
|
74
|
+
{ shaderLocation: 3, offset: 10 * 4, format: 'float32x2' as const },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
] satisfies readonly GPUVertexBufferLayout[];
|
|
78
|
+
|
|
79
|
+
export const MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS = [
|
|
80
|
+
{
|
|
81
|
+
arrayStride: 12 * 4,
|
|
82
|
+
stepMode: 'vertex' as const,
|
|
83
|
+
attributes: [
|
|
84
|
+
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
85
|
+
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x3' as const },
|
|
86
|
+
{ shaderLocation: 2, offset: 6 * 4, format: 'float32x2' as const },
|
|
87
|
+
{ shaderLocation: 3, offset: 8 * 4, format: 'float32x4' as const },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
arrayStride: 28 * 4,
|
|
92
|
+
stepMode: 'instance' as const,
|
|
93
|
+
attributes: [
|
|
94
|
+
{ shaderLocation: 4, offset: 0, format: 'float32x3' as const },
|
|
95
|
+
{ shaderLocation: 5, offset: 3 * 4, format: 'float32x3' as const },
|
|
96
|
+
{ shaderLocation: 6, offset: 6 * 4, format: 'float32x3' as const },
|
|
97
|
+
{ shaderLocation: 7, offset: 9 * 4, format: 'float32x3' as const },
|
|
98
|
+
{ shaderLocation: 8, offset: 12 * 4, format: 'float32x4' as const },
|
|
99
|
+
{ shaderLocation: 9, offset: 16 * 4, format: 'float32x4' as const },
|
|
100
|
+
{ shaderLocation: 10, offset: 20 * 4, format: 'float32x4' as const },
|
|
101
|
+
{ shaderLocation: 11, offset: 24 * 4, format: 'float32x4' as const },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
] satisfies readonly GPUVertexBufferLayout[];
|
|
105
|
+
|
|
106
|
+
function toGpuVertexFormat(
|
|
107
|
+
format: GpuVertexBufferLayoutEntry['attributes'][number]['format'],
|
|
108
|
+
): GPUVertexFormat {
|
|
109
|
+
switch (format) {
|
|
110
|
+
case 'float32x2':
|
|
111
|
+
case 'float32x3':
|
|
112
|
+
case 'float32x4':
|
|
113
|
+
case 'uint16x4':
|
|
114
|
+
return format;
|
|
115
|
+
default:
|
|
116
|
+
throw new Error(`Unsupported geometry vertex format: ${format}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function toGpuVertexBufferLayouts(
|
|
121
|
+
entries: readonly GpuVertexBufferLayoutEntry[],
|
|
122
|
+
): readonly GPUVertexBufferLayout[] {
|
|
123
|
+
return entries.map((entry) => ({
|
|
124
|
+
arrayStride: entry.arrayStride,
|
|
125
|
+
...(entry.stepMode === undefined ? {} : { stepMode: entry.stepMode }),
|
|
126
|
+
attributes: entry.attributes.map((attribute) => ({
|
|
127
|
+
shaderLocation: attribute.shaderLocation,
|
|
128
|
+
offset: attribute.offset,
|
|
129
|
+
format: toGpuVertexFormat(attribute.format),
|
|
130
|
+
})),
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
err,
|
|
7
7
|
MATERIAL_TEXTURE_SLOTS,
|
|
8
8
|
type MaterialAsset,
|
|
9
|
+
type MaterialChildAsset,
|
|
10
|
+
type MaterialRootAsset,
|
|
9
11
|
ok,
|
|
10
12
|
type RenderPipelineAsset,
|
|
11
13
|
type SamplerAsset,
|
|
@@ -31,6 +33,12 @@ function validMaterial(value: unknown): value is MaterialAsset {
|
|
|
31
33
|
}
|
|
32
34
|
if (value.passes !== undefined && !Array.isArray(value.passes)) return false;
|
|
33
35
|
if (value.parameters !== undefined && !Array.isArray(value.parameters)) return false;
|
|
36
|
+
if (
|
|
37
|
+
value.parent !== undefined &&
|
|
38
|
+
['colorSpace', 'passes', 'parameters'].some((field) => Object.hasOwn(value, field))
|
|
39
|
+
) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
34
42
|
return value.values === undefined || record(value.values);
|
|
35
43
|
}
|
|
36
44
|
|
|
@@ -60,16 +68,22 @@ function resolveMaterialWireRefs(
|
|
|
60
68
|
if (refs.length === 0) return ok(value);
|
|
61
69
|
|
|
62
70
|
let changed = false;
|
|
63
|
-
|
|
71
|
+
const wire = value as unknown as { readonly parent?: unknown };
|
|
72
|
+
let parent = wire.parent;
|
|
64
73
|
if (typeof parent === 'number') {
|
|
65
74
|
const resolved = resolveMaterialWireRef(parent, refs, 'parent', guid);
|
|
66
75
|
if (!resolved.ok) return resolved;
|
|
67
|
-
parent = resolved.value
|
|
76
|
+
parent = resolved.value;
|
|
68
77
|
changed = true;
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
if (value.values === undefined) {
|
|
72
|
-
|
|
81
|
+
if (!changed) return ok(value);
|
|
82
|
+
if (parent !== undefined) {
|
|
83
|
+
const child = value as MaterialChildAsset;
|
|
84
|
+
return ok({ ...child, parent: parent as unknown as MaterialChildAsset['parent'] });
|
|
85
|
+
}
|
|
86
|
+
return ok(value);
|
|
73
87
|
}
|
|
74
88
|
|
|
75
89
|
const textureFields = new Set(
|
|
@@ -105,7 +119,16 @@ function resolveMaterialWireRefs(
|
|
|
105
119
|
values[field] = rewritten as unknown as NonNullable<MaterialAsset['values']>[string];
|
|
106
120
|
}
|
|
107
121
|
}
|
|
108
|
-
|
|
122
|
+
if (!changed) return ok(value);
|
|
123
|
+
if (parent !== undefined) {
|
|
124
|
+
const child = value as MaterialChildAsset;
|
|
125
|
+
return ok({
|
|
126
|
+
...child,
|
|
127
|
+
parent: parent as unknown as MaterialChildAsset['parent'],
|
|
128
|
+
values,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return ok({ ...value, values } as MaterialRootAsset);
|
|
109
132
|
}
|
|
110
133
|
|
|
111
134
|
export const materialContribution: AssetDecoderContribution<MaterialAsset, 'material'> = {
|