@forgeax/engine-render 0.1.25 → 0.1.27
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 +18 -0
- package/dist/assembly/factory.d.ts +1 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +4 -17
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -1
- package/dist/assembly/material/surface-projection.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts +0 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/authoring.mjs +1 -1
- package/dist/{chunk-JSEDTZYS.mjs → chunk-GF523LHF.mjs} +6 -6
- package/dist/chunk-GF523LHF.mjs.map +1 -0
- package/dist/{chunk-TCLDX7SZ.mjs → chunk-TMIASV2N.mjs} +403 -457
- package/dist/chunk-TMIASV2N.mjs.map +1 -0
- package/dist/{chunk-N3RT2EUR.mjs → chunk-XDP6XE5V.mjs} +104 -56
- package/dist/chunk-XDP6XE5V.mjs.map +1 -0
- package/dist/construct-renderer.mjs +153 -13
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/extract/contracts.d.ts +6 -6
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/internal.mjs +1 -1
- package/dist/pipeline-spec-types.d.ts +2 -0
- package/dist/pipeline-spec-types.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +2 -2
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +20 -2
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +3 -3
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/material-uniforms.d.ts +4 -0
- package/dist/record/material-uniforms.d.ts.map +1 -0
- package/dist/record/render-context.d.ts +2 -2
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +2 -3
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +2 -0
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +27 -10
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts.map +1 -1
- package/dist/temporal/index.mjs +2 -2
- package/package.json +20 -20
- package/src/__tests__/fixtures/ai-weapon-spirit/README.md +20 -0
- package/src/__tests__/fixtures/ai-weapon-spirit/low-poly-toon.wgsl +178 -0
- package/src/__tests__/fixtures/ai-weapon-spirit/material.json +97 -0
- package/src/__tests__/fixtures/ai-weapon-spirit/painterly-surface.wgsl +55 -0
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +3 -3
- package/src/__tests__/gpu-driven-view.browser.test.ts +11 -3
- package/src/__tests__/material-contract-inventory.unit.test.ts +43 -0
- package/src/__tests__/{material-static-dynamic-route.unit.test.ts → material-layer-projection.unit.test.ts} +30 -47
- package/src/__tests__/material-program-dispatch.unit.test.ts +48 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +2 -2
- package/src/__tests__/skinned-shadow-caster.test.ts +14 -14
- package/src/__tests__/surface-variant-provenance.integration.test.ts +1 -1
- package/src/assembly/factory.ts +0 -3
- package/src/assembly/material/assembly.ts +8 -40
- package/src/assembly/material/standard-layer-projection.ts +3 -1
- package/src/assembly/material/surface-projection.ts +2 -1
- package/src/assembly/webgpu-renderer.ts +17 -9
- package/src/extract/contracts.ts +6 -6
- package/src/materials.ts +3 -3
- package/src/pipeline-spec-types.ts +2 -0
- package/src/pipeline-spec.ts +7 -3
- package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +68 -1
- package/src/record/frame.ts +7 -1
- package/src/record/main-pass-geometry.ts +340 -279
- package/src/record/main-pass-material.ts +87 -2
- package/src/record/main-pass-sprite-draws.ts +65 -168
- package/src/record/main-pass.ts +24 -246
- package/src/record/material-uniforms.ts +186 -0
- package/src/record/render-context.ts +4 -0
- package/src/record/shadow-pass.ts +86 -28
- package/src/recovery/render-system-candidate.ts +5 -1
- package/src/render-system-extract-tail.ts +54 -13
- package/src/render-system-extract.ts +103 -64
- package/src/render-system.ts +19 -0
- package/dist/assembly/material/pipeline-projection.d.ts +0 -30
- package/dist/assembly/material/pipeline-projection.d.ts.map +0 -1
- package/dist/chunk-JSEDTZYS.mjs.map +0 -1
- package/dist/chunk-N3RT2EUR.mjs.map +0 -1
- package/dist/chunk-TCLDX7SZ.mjs.map +0 -1
- package/src/assembly/material/pipeline-projection.ts +0 -63
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { probeVideoHighPerfUpload } from '@forgeax/engine-graphics-extras';
|
|
1
2
|
import {
|
|
2
3
|
type BindGroup,
|
|
3
4
|
type BindGroupEntry,
|
|
@@ -24,6 +25,11 @@ import {
|
|
|
24
25
|
resolveGeometryInstanceBuffer,
|
|
25
26
|
resolveGeometryInstancesBindGroup,
|
|
26
27
|
} from './main-pass-geometry';
|
|
28
|
+
import {
|
|
29
|
+
buildPerSubmeshMaterialBg,
|
|
30
|
+
type PerSubmeshMaterialBgDeps,
|
|
31
|
+
prepareMaterialSkylight,
|
|
32
|
+
} from './main-pass-material';
|
|
27
33
|
import {
|
|
28
34
|
getOrCreateFromChain,
|
|
29
35
|
MESH_PER_ENTITY_STRIDE,
|
|
@@ -31,7 +37,7 @@ import {
|
|
|
31
37
|
MESH_UBO_FULL_ARRAY_BYTES,
|
|
32
38
|
} from './mesh-ssbo';
|
|
33
39
|
import type { _InternalRenderPipelineContext } from './render-context';
|
|
34
|
-
import { STANDARD_PBR_UBO_SIZE } from './render-context';
|
|
40
|
+
import { MATERIAL_PER_ENTITY_STRIDE, STANDARD_PBR_UBO_SIZE } from './render-context';
|
|
35
41
|
import { POINTS_LINES_VIEW_BYTES, pointShadowViewOffset, VIEW_UNIFORM_BYTES } from './view-ubo';
|
|
36
42
|
|
|
37
43
|
export const SHADOW_CASTER_SLOT_STRIDE = 256;
|
|
@@ -247,11 +253,10 @@ function ensureTypedShadowViewBg(
|
|
|
247
253
|
}
|
|
248
254
|
|
|
249
255
|
interface ShadowDispatch {
|
|
250
|
-
readonly
|
|
256
|
+
readonly vertexEntry: string | undefined;
|
|
257
|
+
readonly fragmentEntry: string | undefined;
|
|
251
258
|
readonly materialShaderId: string;
|
|
252
259
|
readonly renderState: MaterialRenderState | undefined;
|
|
253
|
-
readonly materialSlot: number | undefined;
|
|
254
|
-
readonly paramSnapshot: DispatchEntry['paramSnapshot'];
|
|
255
260
|
}
|
|
256
261
|
|
|
257
262
|
type ShadowDispatchMap = ReadonlyMap<number, ReadonlyMap<number, ShadowDispatch>>;
|
|
@@ -265,20 +270,14 @@ export function shadowShaderMap(c: _InternalRenderPipelineContext): ShadowDispat
|
|
|
265
270
|
byMaterial = new Map<number, ShadowDispatch>();
|
|
266
271
|
shaders.set(entry.renderableIndex, byMaterial);
|
|
267
272
|
}
|
|
268
|
-
const candidateSlots =
|
|
269
|
-
c.materialSlotIndices[entry.renderableIndex] ?? c.materialSlotIndices.flat();
|
|
270
|
-
const materialSlot = candidateSlots.find(
|
|
271
|
-
(slot) => c.materialSlots[slot]?.materialHandle === entry.materialHandle,
|
|
272
|
-
);
|
|
273
273
|
byMaterial.set(entry.materialHandle, {
|
|
274
|
-
|
|
274
|
+
vertexEntry: entry.vertexEntry,
|
|
275
|
+
fragmentEntry: entry.fragmentEntry,
|
|
275
276
|
materialShaderId:
|
|
276
277
|
entry.materialShaderId === 'forgeax::default-standard-pbr'
|
|
277
278
|
? SHADOW_CASTER_SHADER_ID
|
|
278
279
|
: entry.materialShaderId,
|
|
279
280
|
renderState: entry.renderState,
|
|
280
|
-
materialSlot,
|
|
281
|
-
paramSnapshot: entry.paramSnapshot,
|
|
282
281
|
});
|
|
283
282
|
}
|
|
284
283
|
}
|
|
@@ -379,13 +378,15 @@ export function encodeDirectionalShadowPass(
|
|
|
379
378
|
`view-shadow-directional-${cascadeIndex}`,
|
|
380
379
|
);
|
|
381
380
|
const materialBg = ensureSpotShadowMaterialBg(c);
|
|
382
|
-
if (pipeline === null || viewBg === null || materialBg === null)
|
|
383
|
-
|
|
381
|
+
if (pipeline === null || viewBg === null || materialBg === null) {
|
|
382
|
+
c.frameState.directionalShadowCacheRecorded = false;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
384
385
|
pass.setViewport(viewport.x, viewport.y, viewport.w, viewport.h, 0, 1);
|
|
385
386
|
pass.setPipeline(pipeline);
|
|
386
387
|
pass.setBindGroup(0, viewBg, [0, 0]);
|
|
387
388
|
pass.setBindGroup(1, materialBg, [0]);
|
|
388
|
-
recordShadowCasterDraws(
|
|
389
|
+
const complete = recordShadowCasterDraws(
|
|
389
390
|
c,
|
|
390
391
|
pass,
|
|
391
392
|
pipeline,
|
|
@@ -393,6 +394,8 @@ export function encodeDirectionalShadowPass(
|
|
|
393
394
|
buildMatchedRenderableIndices(c.dispatch, { LightMode: ['ShadowCaster'] }),
|
|
394
395
|
shadowShaderMap(c),
|
|
395
396
|
);
|
|
397
|
+
c.frameState.directionalShadowCacheRecorded =
|
|
398
|
+
cascadeIndex === 0 ? complete : c.frameState.directionalShadowCacheRecorded && complete;
|
|
396
399
|
}
|
|
397
400
|
|
|
398
401
|
export function encodePointShadowPass(
|
|
@@ -577,7 +580,7 @@ function recordShadowCasterDraws(
|
|
|
577
580
|
shadowMeshBindGroup: BindGroup,
|
|
578
581
|
matchedIndices: Set<number> | null,
|
|
579
582
|
shadowDispatchByRenderableIdx: ShadowDispatchMap,
|
|
580
|
-
):
|
|
583
|
+
): boolean {
|
|
581
584
|
const { runtime, pipelineState, validatedOrdered } = c;
|
|
582
585
|
// M-3 / w12: vertexBuffer/indexBuffer state locals migrate to GpuBuffer
|
|
583
586
|
// (the wrapper) -- the de-dup compare uses wrapper identity (one wrapper
|
|
@@ -591,6 +594,8 @@ function recordShadowCasterDraws(
|
|
|
591
594
|
// bound by the setPipeline call above; the loop switches to a custom
|
|
592
595
|
// ShadowCaster PSO when a material supplies one.
|
|
593
596
|
let shadowLastPipeline: RenderPipeline = shadowPipeline;
|
|
597
|
+
let materialDeps: PerSubmeshMaterialBgDeps | undefined;
|
|
598
|
+
let complete = true;
|
|
594
599
|
|
|
595
600
|
for (let i = 0; i < validatedOrdered.length; i++) {
|
|
596
601
|
const entry = validatedOrdered[i];
|
|
@@ -601,7 +606,10 @@ function recordShadowCasterDraws(
|
|
|
601
606
|
|
|
602
607
|
if (entry.source.skin !== undefined) {
|
|
603
608
|
const skinPipeline = skinnedShadowPipeline(c, entry);
|
|
604
|
-
if (skinPipeline === null)
|
|
609
|
+
if (skinPipeline === null) {
|
|
610
|
+
complete = false;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
605
613
|
if (skinPipeline !== shadowLastPipeline) {
|
|
606
614
|
shadowPass.setPipeline(skinPipeline);
|
|
607
615
|
shadowLastPipeline = skinPipeline;
|
|
@@ -686,21 +694,17 @@ function recordShadowCasterDraws(
|
|
|
686
694
|
?.get(submeshMaterial.materialHandle ?? 0);
|
|
687
695
|
const entryShadowShaderId = shadowDispatch?.materialShaderId;
|
|
688
696
|
const entryShadowRenderState = shadowDispatch?.renderState;
|
|
689
|
-
//
|
|
690
|
-
//
|
|
691
|
-
|
|
692
|
-
// group against `pbr-pl` and Dawn rejects the draw at submit time.
|
|
693
|
-
let entryShadowPipeline =
|
|
697
|
+
// Canonical skin casters retain their palette layout. Authored casters
|
|
698
|
+
// select their own program below with the same geometry layout.
|
|
699
|
+
let entryShadowPipeline: RenderPipeline | null =
|
|
694
700
|
entry.source.skin !== undefined ? shadowLastPipeline : shadowPipeline;
|
|
695
701
|
if (
|
|
696
|
-
entry.source.skin === undefined &&
|
|
697
702
|
entryShadowShaderId !== undefined &&
|
|
698
703
|
(entryShadowShaderId !== 'forgeax::default-shadow-caster' ||
|
|
699
|
-
entryShadowRenderState !== undefined)
|
|
704
|
+
(entryShadowRenderState !== undefined && entry.source.skin === undefined))
|
|
700
705
|
) {
|
|
701
|
-
//
|
|
702
|
-
//
|
|
703
|
-
// dropping its draw.
|
|
706
|
+
// Pending or failed authored programs must not draw using a different
|
|
707
|
+
// caster. Retry preparation on the next frame with the same program.
|
|
704
708
|
entryShadowPipeline =
|
|
705
709
|
runtime.getMaterialShaderPipeline?.(
|
|
706
710
|
entryShadowShaderId,
|
|
@@ -710,12 +714,65 @@ function recordShadowCasterDraws(
|
|
|
710
714
|
undefined, // indexFormat
|
|
711
715
|
undefined, // variantSet — shadow caster has no variant axes
|
|
712
716
|
'shadow-caster', // passKind
|
|
713
|
-
|
|
717
|
+
undefined, // meshAttributes
|
|
718
|
+
1,
|
|
719
|
+
undefined, // colorFormatOverride
|
|
720
|
+
undefined, // shaderUvSetCount
|
|
721
|
+
undefined, // depthFormatOverride
|
|
722
|
+
undefined, // vertexLayout
|
|
723
|
+
entry.mesh.layoutProjection,
|
|
724
|
+
undefined, // shaderModuleMode
|
|
725
|
+
entry.source.skin === undefined ? 'pbr' : 'pbr-skin',
|
|
726
|
+
undefined, // additionalColorFormats
|
|
727
|
+
shadowDispatch?.vertexEntry,
|
|
728
|
+
shadowDispatch?.fragmentEntry,
|
|
729
|
+
) ?? null;
|
|
730
|
+
}
|
|
731
|
+
if (entryShadowPipeline === null) {
|
|
732
|
+
complete = false;
|
|
733
|
+
continue;
|
|
714
734
|
}
|
|
715
735
|
if (entryShadowPipeline !== shadowLastPipeline && entryShadowPipeline !== null) {
|
|
716
736
|
shadowPass.setPipeline(entryShadowPipeline);
|
|
717
737
|
shadowLastPipeline = entryShadowPipeline;
|
|
718
738
|
}
|
|
739
|
+
if (entryShadowShaderId !== undefined && entryShadowShaderId !== SHADOW_CASTER_SHADER_ID) {
|
|
740
|
+
materialDeps ??= {
|
|
741
|
+
runtime,
|
|
742
|
+
pipelineState,
|
|
743
|
+
world: c.world,
|
|
744
|
+
store: c.store,
|
|
745
|
+
materialSlice: STANDARD_PBR_UBO_SIZE,
|
|
746
|
+
videoHighPerfAvailable: probeVideoHighPerfUpload(runtime.device),
|
|
747
|
+
skylightResources: prepareMaterialSkylight(c).skylightResources,
|
|
748
|
+
resolveRenderTargetTextureSource: runtime.resolveRenderTargetTextureSource,
|
|
749
|
+
materialBgShared: c.frameState.materialBgShared,
|
|
750
|
+
materialBgAssemblyCache: c.materialBgAssemblyCache,
|
|
751
|
+
frameState: c.frameState,
|
|
752
|
+
bindGroupCounts: c.bindGroupCounts,
|
|
753
|
+
};
|
|
754
|
+
const materialSlot =
|
|
755
|
+
c.materialSlotIndices[i]?.[sm.materialSlot] ?? c.materialSlotIndices[i]?.[0];
|
|
756
|
+
if (materialSlot === undefined) {
|
|
757
|
+
throw new RhiError({
|
|
758
|
+
code: 'rhi-descriptor-invalid',
|
|
759
|
+
expected: 'a prepared material slot for each shadow submesh',
|
|
760
|
+
hint: 'repair material slot preparation before recording shadows',
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
const materialGroup = buildPerSubmeshMaterialBg(
|
|
764
|
+
materialDeps,
|
|
765
|
+
submeshMaterial,
|
|
766
|
+
entry.source.entityKey,
|
|
767
|
+
entry.world ?? c.world,
|
|
768
|
+
entryShadowShaderId,
|
|
769
|
+
);
|
|
770
|
+
shadowPass.setBindGroup(1, materialGroup, [materialSlot * MATERIAL_PER_ENTITY_STRIDE]);
|
|
771
|
+
} else {
|
|
772
|
+
const materialGroup = ensureSpotShadowMaterialBg(c);
|
|
773
|
+
if (materialGroup === null) continue;
|
|
774
|
+
shadowPass.setBindGroup(1, materialGroup, [0]);
|
|
775
|
+
}
|
|
719
776
|
for (const instanceDraw of shadowInstanceDraws) {
|
|
720
777
|
shadowPass.setBindGroup(3, instanceDraw.instanceBindGroup);
|
|
721
778
|
if (entry.mesh.indexed) {
|
|
@@ -726,6 +783,7 @@ function recordShadowCasterDraws(
|
|
|
726
783
|
}
|
|
727
784
|
}
|
|
728
785
|
}
|
|
786
|
+
return complete;
|
|
729
787
|
}
|
|
730
788
|
|
|
731
789
|
/**
|
|
@@ -639,7 +639,11 @@ export function createRenderSystemRecovery(
|
|
|
639
639
|
seed.frame.lights.pointShadow.length > 0 ||
|
|
640
640
|
seed.frame.lights.spot.some((spot) => spot.castShadow === true) ||
|
|
641
641
|
(seed.frame.lights.lightViewProj?.length ?? 0) > 0,
|
|
642
|
-
splitLdrSprite: computeSplitLdrSprite(
|
|
642
|
+
splitLdrSprite: computeSplitLdrSprite(
|
|
643
|
+
candidateValidated,
|
|
644
|
+
camera.tonemap !== 'none',
|
|
645
|
+
seed.frame.dispatch,
|
|
646
|
+
),
|
|
643
647
|
reflectionFallbackAvailable: candidateFrameState.reflectionFallbackDemand === true,
|
|
644
648
|
});
|
|
645
649
|
const candidatePostProcessIds = new Set<string>([
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
16
16
|
import { box3, frustum, type Mat4, mat4, type Vec3, vec3 } from '@forgeax/engine-math';
|
|
17
17
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
18
|
+
import type { MaterialCookProgramContext } from '@forgeax/engine-pack/material-cook';
|
|
18
19
|
import { RhiError } from '@forgeax/engine-rhi';
|
|
19
20
|
import { GlobalTransform, MorphWeights, Transform } from '@forgeax/engine-scene';
|
|
20
21
|
import type { MaterialShaderArtifact } from '@forgeax/engine-shader';
|
|
@@ -147,7 +148,8 @@ import {
|
|
|
147
148
|
isEngineInjectedTextureField,
|
|
148
149
|
materialColorParameterSchema,
|
|
149
150
|
materialNormalScale,
|
|
150
|
-
|
|
151
|
+
materialParamSchemaForMaterial,
|
|
152
|
+
materialProgramKeysForMaterial,
|
|
151
153
|
materialTextureFields,
|
|
152
154
|
materialTextureRef,
|
|
153
155
|
pipelineRenderState,
|
|
@@ -174,6 +176,7 @@ export function extractFrames(
|
|
|
174
176
|
pipelineState?: ExtractPipelineSurface | null,
|
|
175
177
|
materialSnapshotCachesByWorld?: MaterialSnapshotCachesByWorld,
|
|
176
178
|
options: {
|
|
179
|
+
readonly materialContext?: MaterialCookProgramContext;
|
|
177
180
|
readonly cull?: 'normal' | 'none';
|
|
178
181
|
readonly renderables?: 'full' | 'none';
|
|
179
182
|
readonly renderableEntitiesByWorld?: readonly (ReadonlySet<number> | undefined)[];
|
|
@@ -251,6 +254,9 @@ export function extractFrames(
|
|
|
251
254
|
return cache;
|
|
252
255
|
})(),
|
|
253
256
|
}),
|
|
257
|
+
...(options.materialContext === undefined
|
|
258
|
+
? {}
|
|
259
|
+
: { materialContext: options.materialContext }),
|
|
254
260
|
cull: options.cull === 'none' ? 'none' : isCameraOwner ? 'self' : 'external',
|
|
255
261
|
renderables: options.renderables ?? 'full',
|
|
256
262
|
...(options.renderableEntitiesByWorld?.[wi] === undefined
|
|
@@ -1559,6 +1565,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
1559
1565
|
// extraction. Shared handles are immutable inputs for the frame, while
|
|
1560
1566
|
// dispatch entries still need to be rebuilt for each entity.
|
|
1561
1567
|
const materialSnapshotCache: MaterialSnapshotCache = new Map();
|
|
1568
|
+
const skinnedMaterialSnapshotCache: MaterialSnapshotCache = new Map();
|
|
1562
1569
|
// tweak-20260611 M1: MeshRenderer renderable archetype walk routes
|
|
1563
1570
|
// through one World-owned Query. K-2 sniffing scheme B
|
|
1564
1571
|
// (`row.get(X) !== undefined` edge sniff) replaces the prior
|
|
@@ -1927,7 +1934,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
1927
1934
|
layerVal,
|
|
1928
1935
|
materialSnap.paramSnapshot,
|
|
1929
1936
|
0,
|
|
1930
|
-
materialSnap.
|
|
1937
|
+
materialSnap.materialProgramKeys,
|
|
1931
1938
|
);
|
|
1932
1939
|
}
|
|
1933
1940
|
} else if (handleRaw === 0 || assets === undefined || assets === null) {
|
|
@@ -1956,7 +1963,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
1956
1963
|
layerVal,
|
|
1957
1964
|
materialSnap.paramSnapshot,
|
|
1958
1965
|
0,
|
|
1959
|
-
materialSnap.
|
|
1966
|
+
materialSnap.materialProgramKeys,
|
|
1960
1967
|
);
|
|
1961
1968
|
}
|
|
1962
1969
|
} else {
|
|
@@ -2020,7 +2027,26 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
2020
2027
|
}
|
|
2021
2028
|
const resolved = resolvedResult.value;
|
|
2022
2029
|
const allPasses = resolved.passes;
|
|
2023
|
-
const
|
|
2030
|
+
const materialContext =
|
|
2031
|
+
context.materialContext === undefined
|
|
2032
|
+
? undefined
|
|
2033
|
+
: {
|
|
2034
|
+
...context.materialContext,
|
|
2035
|
+
geometry: hasSkin ? ('skinned' as const) : context.materialContext.geometry,
|
|
2036
|
+
};
|
|
2037
|
+
const materialProgramKeys = materialProgramKeysForMaterial(
|
|
2038
|
+
asset,
|
|
2039
|
+
assets,
|
|
2040
|
+
materialContext,
|
|
2041
|
+
);
|
|
2042
|
+
const authoredFirstPassShader = runtimeMaterialShaderIdForMaterial(
|
|
2043
|
+
allPasses,
|
|
2044
|
+
undefined,
|
|
2045
|
+
);
|
|
2046
|
+
const firstPassShader = runtimeMaterialShaderIdForMaterial(
|
|
2047
|
+
allPasses,
|
|
2048
|
+
materialProgramKeys,
|
|
2049
|
+
);
|
|
2024
2050
|
const pv = materialValuesToLinearRuntime(
|
|
2025
2051
|
resolved.values,
|
|
2026
2052
|
materialColorParameterSchema(resolved.parameters ?? [], firstPassShader, assets),
|
|
@@ -2047,9 +2073,10 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
2047
2073
|
}
|
|
2048
2074
|
}
|
|
2049
2075
|
|
|
2050
|
-
const materialParamSchema =
|
|
2076
|
+
const materialParamSchema = materialParamSchemaForMaterial(
|
|
2051
2077
|
resolved.parameters ?? [],
|
|
2052
|
-
|
|
2078
|
+
authoredFirstPassShader,
|
|
2079
|
+
allPasses,
|
|
2053
2080
|
);
|
|
2054
2081
|
// feat-20260611-fox-skinning-vertex-attribute-chain M4 / w17 (D-5):
|
|
2055
2082
|
// bidirectional Skin <-> pbr-skin material fail-fast at extract.
|
|
@@ -2338,6 +2365,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
2338
2365
|
}),
|
|
2339
2366
|
normalScale: normalScalePv,
|
|
2340
2367
|
materialShaderId: firstPassShader,
|
|
2368
|
+
materialProgramKeys,
|
|
2341
2369
|
materialHandle: handleRaw,
|
|
2342
2370
|
renderState: pipelineRenderState(allPasses[0]?.renderState),
|
|
2343
2371
|
paramSnapshot: paramSnap,
|
|
@@ -2399,7 +2427,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
2399
2427
|
layerVal,
|
|
2400
2428
|
paramSnap,
|
|
2401
2429
|
0,
|
|
2402
|
-
|
|
2430
|
+
materialProgramKeys,
|
|
2403
2431
|
);
|
|
2404
2432
|
}
|
|
2405
2433
|
}
|
|
@@ -2610,15 +2638,22 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
2610
2638
|
if (assets !== undefined && assets !== null) {
|
|
2611
2639
|
for (let mi = 1; mi < materialHandles.length; mi++) {
|
|
2612
2640
|
const subHandle = materialHandles[mi] ?? 0;
|
|
2613
|
-
const
|
|
2641
|
+
const subCache = hasSkin ? skinnedMaterialSnapshotCache : materialSnapshotCache;
|
|
2642
|
+
const cachedSubmaterial = subCache.get(subHandle);
|
|
2614
2643
|
materialsArr.push(
|
|
2615
2644
|
cachedSubmaterial?.snapshot ??
|
|
2616
2645
|
resolveMaterialSnapshot(
|
|
2617
2646
|
subHandle,
|
|
2618
2647
|
world,
|
|
2619
2648
|
assets,
|
|
2620
|
-
|
|
2621
|
-
persistentMaterialSnapshotCache,
|
|
2649
|
+
subCache,
|
|
2650
|
+
hasSkin ? undefined : persistentMaterialSnapshotCache,
|
|
2651
|
+
context.materialContext === undefined
|
|
2652
|
+
? undefined
|
|
2653
|
+
: {
|
|
2654
|
+
...context.materialContext,
|
|
2655
|
+
geometry: hasSkin ? 'skinned' : context.materialContext.geometry,
|
|
2656
|
+
},
|
|
2622
2657
|
),
|
|
2623
2658
|
);
|
|
2624
2659
|
}
|
|
@@ -2628,8 +2663,14 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
2628
2663
|
const subHandle = materialHandles[mi] ?? 0;
|
|
2629
2664
|
if (subHandle === handleRaw) continue;
|
|
2630
2665
|
const subEntry =
|
|
2631
|
-
materialSnapshotCache.get(subHandle) ??
|
|
2632
|
-
|
|
2666
|
+
(hasSkin ? skinnedMaterialSnapshotCache : materialSnapshotCache).get(subHandle) ??
|
|
2667
|
+
(hasSkin
|
|
2668
|
+
? undefined
|
|
2669
|
+
: readPersistentMaterialSnapshot(
|
|
2670
|
+
persistentMaterialSnapshotCache,
|
|
2671
|
+
subHandle,
|
|
2672
|
+
assets,
|
|
2673
|
+
));
|
|
2633
2674
|
appendMaterialDispatchEntries(
|
|
2634
2675
|
pendingDispatch,
|
|
2635
2676
|
subEntry?.passes ?? (subHandle === 0 ? [DEFAULT_FORWARD_PASS] : []),
|
|
@@ -2639,7 +2680,7 @@ export function extractFrame(world: World, context: PreparedExtractContext): Ext
|
|
|
2639
2680
|
layerVal,
|
|
2640
2681
|
materialsArr[mi]?.paramSnapshot,
|
|
2641
2682
|
0,
|
|
2642
|
-
materialsArr[mi]?.
|
|
2683
|
+
materialsArr[mi]?.materialProgramKeys,
|
|
2643
2684
|
);
|
|
2644
2685
|
}
|
|
2645
2686
|
}
|