@flighthq/scene3d-wgpu 0.2.1-next.840.857425c
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/dist/anisotropyPbrWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/anisotropyPbrWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/anisotropyPbrWgpuMeshMaterialRenderer.js +52 -0
- package/dist/anisotropyPbrWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/blinnPhongWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/blinnPhongWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/blinnPhongWgpuMeshMaterialRenderer.js +66 -0
- package/dist/blinnPhongWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/clearcoatPbrWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/clearcoatPbrWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/clearcoatPbrWgpuMeshMaterialRenderer.js +52 -0
- package/dist/clearcoatPbrWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/customShaderWgpuMeshMaterialRenderer.d.ts +7 -0
- package/dist/customShaderWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/customShaderWgpuMeshMaterialRenderer.js +225 -0
- package/dist/customShaderWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/depthWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/depthWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/depthWgpuMeshMaterialRenderer.js +42 -0
- package/dist/depthWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/drawWgpuScene3D.d.ts +4 -0
- package/dist/drawWgpuScene3D.d.ts.map +1 -0
- package/dist/drawWgpuScene3D.js +171 -0
- package/dist/drawWgpuScene3D.js.map +1 -0
- package/dist/emissiveWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/emissiveWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/emissiveWgpuMeshMaterialRenderer.js +51 -0
- package/dist/emissiveWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/enableWgpuScene3DCustomShaderGuards.d.ts +5 -0
- package/dist/enableWgpuScene3DCustomShaderGuards.d.ts.map +1 -0
- package/dist/enableWgpuScene3DCustomShaderGuards.js +56 -0
- package/dist/enableWgpuScene3DCustomShaderGuards.js.map +1 -0
- package/dist/enableWgpuScene3DForwardLightSelectionGuards.d.ts +4 -0
- package/dist/enableWgpuScene3DForwardLightSelectionGuards.d.ts.map +1 -0
- package/dist/enableWgpuScene3DForwardLightSelectionGuards.js +17 -0
- package/dist/enableWgpuScene3DForwardLightSelectionGuards.js.map +1 -0
- package/dist/explainWgpuScene3DForwardLightSelection.d.ts +3 -0
- package/dist/explainWgpuScene3DForwardLightSelection.d.ts.map +1 -0
- package/dist/explainWgpuScene3DForwardLightSelection.js +18 -0
- package/dist/explainWgpuScene3DForwardLightSelection.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/iridescencePbrWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/iridescencePbrWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/iridescencePbrWgpuMeshMaterialRenderer.js +60 -0
- package/dist/iridescencePbrWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/lambertWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/lambertWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/lambertWgpuMeshMaterialRenderer.js +61 -0
- package/dist/lambertWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/matcapWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/matcapWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/matcapWgpuMeshMaterialRenderer.js +54 -0
- package/dist/matcapWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/normalWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/normalWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/normalWgpuMeshMaterialRenderer.js +47 -0
- package/dist/normalWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/phongWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/phongWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/phongWgpuMeshMaterialRenderer.js +63 -0
- package/dist/phongWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/prepareWgpuScene3DForwardLights.d.ts +3 -0
- package/dist/prepareWgpuScene3DForwardLights.d.ts.map +1 -0
- package/dist/prepareWgpuScene3DForwardLights.js +110 -0
- package/dist/prepareWgpuScene3DForwardLights.js.map +1 -0
- package/dist/registerStandardPbrWgpuMaterial.d.ts +3 -0
- package/dist/registerStandardPbrWgpuMaterial.d.ts.map +1 -0
- package/dist/registerStandardPbrWgpuMaterial.js +11 -0
- package/dist/registerStandardPbrWgpuMaterial.js.map +1 -0
- package/dist/shadedWgpuMeshMaterialRenderer.d.ts +11 -0
- package/dist/shadedWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/shadedWgpuMeshMaterialRenderer.js +47 -0
- package/dist/shadedWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/sheenPbrWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/sheenPbrWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/sheenPbrWgpuMeshMaterialRenderer.js +58 -0
- package/dist/sheenPbrWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/specularGlossinessPbrWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/specularGlossinessPbrWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/specularGlossinessPbrWgpuMeshMaterialRenderer.js +126 -0
- package/dist/specularGlossinessPbrWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/specularPbrWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/specularPbrWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/specularPbrWgpuMeshMaterialRenderer.js +65 -0
- package/dist/specularPbrWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/standardPbrWgpuMeshMaterialRenderer.d.ts +9 -0
- package/dist/standardPbrWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/standardPbrWgpuMeshMaterialRenderer.js +174 -0
- package/dist/standardPbrWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/subsurfacePbrWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/subsurfacePbrWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/subsurfacePbrWgpuMeshMaterialRenderer.js +68 -0
- package/dist/subsurfacePbrWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/toonWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/toonWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/toonWgpuMeshMaterialRenderer.js +71 -0
- package/dist/toonWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/transmissionVolumePbrWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/transmissionVolumePbrWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/transmissionVolumePbrWgpuMeshMaterialRenderer.js +74 -0
- package/dist/transmissionVolumePbrWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/unlitWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/unlitWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/unlitWgpuMeshMaterialRenderer.js +57 -0
- package/dist/unlitWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/vertexColorWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/vertexColorWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/vertexColorWgpuMeshMaterialRenderer.js +55 -0
- package/dist/vertexColorWgpuMeshMaterialRenderer.js.map +1 -0
- package/dist/wgpuClassicPrelude.d.ts +8 -0
- package/dist/wgpuClassicPrelude.d.ts.map +1 -0
- package/dist/wgpuClassicPrelude.js +319 -0
- package/dist/wgpuClassicPrelude.js.map +1 -0
- package/dist/wgpuCustomMaterialAbi.d.ts +3 -0
- package/dist/wgpuCustomMaterialAbi.d.ts.map +1 -0
- package/dist/wgpuCustomMaterialAbi.js +5 -0
- package/dist/wgpuCustomMaterialAbi.js.map +1 -0
- package/dist/wgpuDebugPrelude.d.ts +7 -0
- package/dist/wgpuDebugPrelude.d.ts.map +1 -0
- package/dist/wgpuDebugPrelude.js +133 -0
- package/dist/wgpuDebugPrelude.js.map +1 -0
- package/dist/wgpuEnvironmentCube.d.ts +4 -0
- package/dist/wgpuEnvironmentCube.d.ts.map +1 -0
- package/dist/wgpuEnvironmentCube.js +66 -0
- package/dist/wgpuEnvironmentCube.js.map +1 -0
- package/dist/wgpuEnvironmentIblBake.d.ts +4 -0
- package/dist/wgpuEnvironmentIblBake.d.ts.map +1 -0
- package/dist/wgpuEnvironmentIblBake.js +458 -0
- package/dist/wgpuEnvironmentIblBake.js.map +1 -0
- package/dist/wgpuEnvironmentSkybox.d.ts +3 -0
- package/dist/wgpuEnvironmentSkybox.d.ts.map +1 -0
- package/dist/wgpuEnvironmentSkybox.js +159 -0
- package/dist/wgpuEnvironmentSkybox.js.map +1 -0
- package/dist/wgpuMatcapPrelude.d.ts +7 -0
- package/dist/wgpuMatcapPrelude.d.ts.map +1 -0
- package/dist/wgpuMatcapPrelude.js +134 -0
- package/dist/wgpuMatcapPrelude.js.map +1 -0
- package/dist/wgpuMeshMaterialRegistry.d.ts +5 -0
- package/dist/wgpuMeshMaterialRegistry.d.ts.map +1 -0
- package/dist/wgpuMeshMaterialRegistry.js +29 -0
- package/dist/wgpuMeshMaterialRegistry.js.map +1 -0
- package/dist/wgpuMeshPipeline.d.ts +43 -0
- package/dist/wgpuMeshPipeline.d.ts.map +1 -0
- package/dist/wgpuMeshPipeline.js +1007 -0
- package/dist/wgpuMeshPipeline.js.map +1 -0
- package/dist/wgpuMeshUpload.d.ts +3 -0
- package/dist/wgpuMeshUpload.d.ts.map +1 -0
- package/dist/wgpuMeshUpload.js +58 -0
- package/dist/wgpuMeshUpload.js.map +1 -0
- package/dist/wgpuParticleEmitter3D.d.ts +4 -0
- package/dist/wgpuParticleEmitter3D.d.ts.map +1 -0
- package/dist/wgpuParticleEmitter3D.js +403 -0
- package/dist/wgpuParticleEmitter3D.js.map +1 -0
- package/dist/wgpuPbrPipelineCache.d.ts +4 -0
- package/dist/wgpuPbrPipelineCache.d.ts.map +1 -0
- package/dist/wgpuPbrPipelineCache.js +52 -0
- package/dist/wgpuPbrPipelineCache.js.map +1 -0
- package/dist/wgpuPbrPrelude.d.ts +7 -0
- package/dist/wgpuPbrPrelude.d.ts.map +1 -0
- package/dist/wgpuPbrPrelude.js +551 -0
- package/dist/wgpuPbrPrelude.js.map +1 -0
- package/dist/wgpuScene3DRuntime.d.ts +5 -0
- package/dist/wgpuScene3DRuntime.d.ts.map +1 -0
- package/dist/wgpuScene3DRuntime.js +81 -0
- package/dist/wgpuScene3DRuntime.js.map +1 -0
- package/dist/wgpuScene3DTestHelper.d.ts +14 -0
- package/dist/wgpuScene3DTestHelper.d.ts.map +1 -0
- package/dist/wgpuScene3DTestHelper.js +121 -0
- package/dist/wgpuScene3DTestHelper.js.map +1 -0
- package/dist/wgpuScene3DTime.d.ts +4 -0
- package/dist/wgpuScene3DTime.d.ts.map +1 -0
- package/dist/wgpuScene3DTime.js +11 -0
- package/dist/wgpuScene3DTime.js.map +1 -0
- package/dist/wgpuShadedModifierSnippet.d.ts +4 -0
- package/dist/wgpuShadedModifierSnippet.d.ts.map +1 -0
- package/dist/wgpuShadedModifierSnippet.js +18 -0
- package/dist/wgpuShadedModifierSnippet.js.map +1 -0
- package/dist/wgpuShadedPrelude.d.ts +16 -0
- package/dist/wgpuShadedPrelude.d.ts.map +1 -0
- package/dist/wgpuShadedPrelude.js +625 -0
- package/dist/wgpuShadedPrelude.js.map +1 -0
- package/dist/wgpuShadowMap.d.ts +4 -0
- package/dist/wgpuShadowMap.d.ts.map +1 -0
- package/dist/wgpuShadowMap.js +155 -0
- package/dist/wgpuShadowMap.js.map +1 -0
- package/dist/wgpuSkinPalette.d.ts +7 -0
- package/dist/wgpuSkinPalette.d.ts.map +1 -0
- package/dist/wgpuSkinPalette.js +173 -0
- package/dist/wgpuSkinPalette.js.map +1 -0
- package/dist/wgpuToonPrelude.d.ts +8 -0
- package/dist/wgpuToonPrelude.d.ts.map +1 -0
- package/dist/wgpuToonPrelude.js +192 -0
- package/dist/wgpuToonPrelude.js.map +1 -0
- package/dist/wgpuUnlitPrelude.d.ts +9 -0
- package/dist/wgpuUnlitPrelude.d.ts.map +1 -0
- package/dist/wgpuUnlitPrelude.js +142 -0
- package/dist/wgpuUnlitPrelude.js.map +1 -0
- package/dist/wgpuWireframePrelude.d.ts +6 -0
- package/dist/wgpuWireframePrelude.d.ts.map +1 -0
- package/dist/wgpuWireframePrelude.js +77 -0
- package/dist/wgpuWireframePrelude.js.map +1 -0
- package/dist/wgpuWireframeUpload.d.ts +3 -0
- package/dist/wgpuWireframeUpload.d.ts.map +1 -0
- package/dist/wgpuWireframeUpload.js +68 -0
- package/dist/wgpuWireframeUpload.js.map +1 -0
- package/dist/wireframeWgpuMeshMaterialRenderer.d.ts +4 -0
- package/dist/wireframeWgpuMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/wireframeWgpuMeshMaterialRenderer.js +69 -0
- package/dist/wireframeWgpuMeshMaterialRenderer.js.map +1 -0
- package/package.json +57 -0
- package/src/anisotropyPbrWgpuMeshMaterialRenderer.test.ts +90 -0
- package/src/blinnPhongWgpuMeshMaterialRenderer.test.ts +82 -0
- package/src/clearcoatPbrWgpuMeshMaterialRenderer.test.ts +90 -0
- package/src/customShaderWgpuMeshMaterialRenderer.test.ts +207 -0
- package/src/depthWgpuMeshMaterialRenderer.test.ts +69 -0
- package/src/drawWgpuScene3D.test.ts +247 -0
- package/src/emissiveWgpuMeshMaterialRenderer.test.ts +64 -0
- package/src/enableWgpuScene3DCustomShaderGuards.test.ts +73 -0
- package/src/enableWgpuScene3DForwardLightSelectionGuards.test.ts +39 -0
- package/src/explainWgpuScene3DForwardLightSelection.test.ts +15 -0
- package/src/iridescencePbrWgpuMeshMaterialRenderer.test.ts +90 -0
- package/src/lambertWgpuMeshMaterialRenderer.test.ts +77 -0
- package/src/matcapWgpuMeshMaterialRenderer.test.ts +69 -0
- package/src/normalWgpuMeshMaterialRenderer.test.ts +69 -0
- package/src/phongWgpuMeshMaterialRenderer.test.ts +79 -0
- package/src/prepareWgpuScene3DForwardLights.test.ts +84 -0
- package/src/registerStandardPbrWgpuMaterial.test.ts +15 -0
- package/src/shadedWgpuMeshMaterialRenderer.test.ts +63 -0
- package/src/sheenPbrWgpuMeshMaterialRenderer.test.ts +87 -0
- package/src/specularGlossinessPbrWgpuMeshMaterialRenderer.test.ts +100 -0
- package/src/specularPbrWgpuMeshMaterialRenderer.test.ts +90 -0
- package/src/standardPbrWgpuMeshMaterialRenderer.test.ts +172 -0
- package/src/subsurfacePbrWgpuMeshMaterialRenderer.test.ts +90 -0
- package/src/toonWgpuMeshMaterialRenderer.test.ts +89 -0
- package/src/transmissionVolumePbrWgpuMeshMaterialRenderer.test.ts +97 -0
- package/src/unlitWgpuMeshMaterialRenderer.test.ts +94 -0
- package/src/vertexColorWgpuMeshMaterialRenderer.test.ts +62 -0
- package/src/wgpuClassicPrelude.test.ts +176 -0
- package/src/wgpuDebugPrelude.test.ts +79 -0
- package/src/wgpuEnvironmentCube.test.ts +86 -0
- package/src/wgpuEnvironmentIblBake.test.ts +79 -0
- package/src/wgpuEnvironmentSkybox.test.ts +56 -0
- package/src/wgpuMatcapPrelude.test.ts +70 -0
- package/src/wgpuMeshMaterialRegistry.test.ts +55 -0
- package/src/wgpuMeshPipeline.test.ts +844 -0
- package/src/wgpuMeshUpload.test.ts +177 -0
- package/src/wgpuParticleEmitter3D.test.ts +200 -0
- package/src/wgpuPbrPipelineCache.test.ts +67 -0
- package/src/wgpuPbrPrelude.test.ts +162 -0
- package/src/wgpuScene3DRuntime.test.ts +35 -0
- package/src/wgpuScene3DTime.test.ts +20 -0
- package/src/wgpuShadedModifierSnippet.test.ts +33 -0
- package/src/wgpuShadedPrelude.test.ts +394 -0
- package/src/wgpuShadowMap.test.ts +169 -0
- package/src/wgpuSkinPalette.test.ts +88 -0
- package/src/wgpuToonPrelude.test.ts +93 -0
- package/src/wgpuUnlitPrelude.test.ts +102 -0
- package/src/wgpuWireframePrelude.test.ts +54 -0
- package/src/wgpuWireframeUpload.test.ts +36 -0
- package/src/wireframeWgpuMeshMaterialRenderer.test.ts +69 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { createCamera3D, setCamera3DViewMatrix4FromLookAt } from '@flighthq/camera';
|
|
2
|
+
import { createVector3 } from '@flighthq/geometry';
|
|
3
|
+
import { createAmbientLight, createDirectionalLight } from '@flighthq/lighting';
|
|
4
|
+
import { createStandardPbrMaterial } from '@flighthq/materials';
|
|
5
|
+
import { CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT, createBoxMeshGeometry, createMeshGeometry } from '@flighthq/mesh';
|
|
6
|
+
import { addNodeChild, invalidateNodeLocalTransform } from '@flighthq/node';
|
|
7
|
+
import { createParticleEmitter3D, reserveParticleEmitter3D } from '@flighthq/particleemitter';
|
|
8
|
+
import { createMesh, createNode3D, Node3DKind } from '@flighthq/scene3d';
|
|
9
|
+
import type { Camera3D, ParticleEmitter3D, Scene3DLightsLike, Skeleton3D } from '@flighthq/types';
|
|
10
|
+
|
|
11
|
+
import { drawWgpuScene3D, isWgpuMeshGpuSkinned } from './drawWgpuScene3D';
|
|
12
|
+
import { registerStandardPbrWgpuMaterial } from './registerStandardPbrWgpuMaterial';
|
|
13
|
+
import { getWgpuScene3DRuntime } from './wgpuScene3DRuntime';
|
|
14
|
+
import { makeWgpuScene3DState } from './wgpuScene3DTestHelper';
|
|
15
|
+
import { registerWgpuGpuSkinning } from './wgpuSkinPalette';
|
|
16
|
+
|
|
17
|
+
function makeCamera(): Camera3D {
|
|
18
|
+
const camera = createCamera3D({
|
|
19
|
+
far: 100,
|
|
20
|
+
near: 0.1,
|
|
21
|
+
projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' },
|
|
22
|
+
});
|
|
23
|
+
setCamera3DViewMatrix4FromLookAt(camera, { x: 0, y: 0, z: 5 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 });
|
|
24
|
+
return camera;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const LIGHTS: Scene3DLightsLike = {
|
|
28
|
+
ambient: createAmbientLight({ color: 0xffffffff, intensity: 0.2 }),
|
|
29
|
+
directional: createDirectionalLight({ color: 0xffffffff, direction: createVector3(0, -1, -1), intensity: 1 }),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
describe('drawWgpuScene3D', () => {
|
|
33
|
+
it('draws each visible mesh subset with its registered material renderer', () => {
|
|
34
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
35
|
+
registerStandardPbrWgpuMaterial(state);
|
|
36
|
+
|
|
37
|
+
const scene = createNode3D(Node3DKind);
|
|
38
|
+
const mesh = createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]);
|
|
39
|
+
addNodeChild(scene, mesh);
|
|
40
|
+
|
|
41
|
+
drawWgpuScene3D(state, scene, makeCamera(), LIGHTS);
|
|
42
|
+
|
|
43
|
+
expect(fake.calls.some((c) => c.name === 'setPipeline')).toBe(true);
|
|
44
|
+
expect(fake.calls.some((c) => c.name === 'drawIndexed')).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('skips a subset whose material has no registered renderer (no fallback)', () => {
|
|
48
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
49
|
+
// No registerStandardPbrWgpuMaterial: nothing resolves.
|
|
50
|
+
const scene = createNode3D(Node3DKind);
|
|
51
|
+
const mesh = createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]);
|
|
52
|
+
addNodeChild(scene, mesh);
|
|
53
|
+
|
|
54
|
+
drawWgpuScene3D(state, scene, makeCamera(), LIGHTS);
|
|
55
|
+
expect(fake.calls.some((c) => c.name === 'drawIndexed')).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('binds once for a run of subsets sharing a material', () => {
|
|
59
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
60
|
+
registerStandardPbrWgpuMaterial(state);
|
|
61
|
+
|
|
62
|
+
const scene = createNode3D(Node3DKind);
|
|
63
|
+
const material = createStandardPbrMaterial();
|
|
64
|
+
const meshA = createMesh(createBoxMeshGeometry(), [material]);
|
|
65
|
+
const meshB = createMesh(createBoxMeshGeometry(), [material]);
|
|
66
|
+
addNodeChild(scene, meshA);
|
|
67
|
+
addNodeChild(scene, meshB);
|
|
68
|
+
|
|
69
|
+
drawWgpuScene3D(state, scene, makeCamera(), LIGHTS);
|
|
70
|
+
|
|
71
|
+
// Same material across both meshes: pipeline bound once, drawn twice.
|
|
72
|
+
expect(fake.calls.filter((c) => c.name === 'setPipeline').length).toBe(1);
|
|
73
|
+
expect(fake.calls.filter((c) => c.name === 'drawIndexed').length).toBe(2);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('does not draw a disabled mesh', () => {
|
|
77
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
78
|
+
registerStandardPbrWgpuMaterial(state);
|
|
79
|
+
|
|
80
|
+
const scene = createNode3D(Node3DKind);
|
|
81
|
+
const mesh = createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]);
|
|
82
|
+
mesh.enabled = false;
|
|
83
|
+
addNodeChild(scene, mesh);
|
|
84
|
+
|
|
85
|
+
drawWgpuScene3D(state, scene, makeCamera(), LIGHTS);
|
|
86
|
+
expect(fake.calls.some((c) => c.name === 'drawIndexed')).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('partitions opaque and blended subsets into distinct pipeline runs', () => {
|
|
90
|
+
const { state } = makeWgpuScene3DState();
|
|
91
|
+
registerStandardPbrWgpuMaterial(state);
|
|
92
|
+
const scene = createNode3D(Node3DKind);
|
|
93
|
+
const blendedMaterial = createStandardPbrMaterial();
|
|
94
|
+
blendedMaterial.alphaMode = 'blend';
|
|
95
|
+
const blended = createMesh(createBoxMeshGeometry(), [blendedMaterial]);
|
|
96
|
+
const opaque = createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]);
|
|
97
|
+
addNodeChild(scene, blended);
|
|
98
|
+
addNodeChild(scene, opaque);
|
|
99
|
+
|
|
100
|
+
drawWgpuScene3D(state, scene, makeCamera(), LIGHTS);
|
|
101
|
+
|
|
102
|
+
const runtime = getWgpuScene3DRuntime(state);
|
|
103
|
+
expect(runtime.opaqueDrawList.map((entry) => entry.mesh)).toEqual([opaque]);
|
|
104
|
+
expect(runtime.blendedDrawList.map((entry) => entry.mesh)).toEqual([blended]);
|
|
105
|
+
expect(Array.from(runtime.pipelineCache.keys()).some((key) => key.endsWith('|opaque|rigid'))).toBe(true);
|
|
106
|
+
expect(Array.from(runtime.pipelineCache.keys()).some((key) => key.endsWith('|blend|rigid'))).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('routes resolved node alpha through the blended pass and draw proxy', () => {
|
|
110
|
+
const { state } = makeWgpuScene3DState();
|
|
111
|
+
registerStandardPbrWgpuMaterial(state);
|
|
112
|
+
const scene = createNode3D(Node3DKind);
|
|
113
|
+
const mesh = createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]);
|
|
114
|
+
mesh.alpha = 0.5;
|
|
115
|
+
addNodeChild(scene, mesh);
|
|
116
|
+
|
|
117
|
+
drawWgpuScene3D(state, scene, makeCamera(), LIGHTS);
|
|
118
|
+
|
|
119
|
+
const runtime = getWgpuScene3DRuntime(state);
|
|
120
|
+
expect(runtime.opaqueDrawList).toHaveLength(0);
|
|
121
|
+
expect(runtime.blendedDrawList[0]!.alpha).toBeCloseTo(0.5);
|
|
122
|
+
expect(Array.from(runtime.pipelineCache.keys()).some((key) => key.endsWith('|blend|rigid'))).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('sorts blended subsets back-to-front by projected depth', () => {
|
|
126
|
+
const { state } = makeWgpuScene3DState();
|
|
127
|
+
registerStandardPbrWgpuMaterial(state);
|
|
128
|
+
const scene = createNode3D(Node3DKind);
|
|
129
|
+
const material = createStandardPbrMaterial();
|
|
130
|
+
material.alphaMode = 'blend';
|
|
131
|
+
const far = createMesh(createBoxMeshGeometry(), [material]);
|
|
132
|
+
const near = createMesh(createBoxMeshGeometry(), [material]);
|
|
133
|
+
far.position.z = -3;
|
|
134
|
+
near.position.z = -1;
|
|
135
|
+
invalidateNodeLocalTransform(far);
|
|
136
|
+
invalidateNodeLocalTransform(near);
|
|
137
|
+
addNodeChild(scene, near);
|
|
138
|
+
addNodeChild(scene, far);
|
|
139
|
+
|
|
140
|
+
drawWgpuScene3D(state, scene, makeCamera(), LIGHTS);
|
|
141
|
+
|
|
142
|
+
expect(getWgpuScene3DRuntime(state).blendedDrawList.map((entry) => entry.mesh)).toEqual([far, near]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('sorts blended subsets back-to-front with an orthographic camera', () => {
|
|
146
|
+
const { state } = makeWgpuScene3DState();
|
|
147
|
+
registerStandardPbrWgpuMaterial(state);
|
|
148
|
+
const scene = createNode3D(Node3DKind);
|
|
149
|
+
const material = createStandardPbrMaterial();
|
|
150
|
+
material.alphaMode = 'blend';
|
|
151
|
+
const far = createMesh(createBoxMeshGeometry(), [material]);
|
|
152
|
+
const near = createMesh(createBoxMeshGeometry(), [material]);
|
|
153
|
+
far.position.z = -3;
|
|
154
|
+
near.position.z = -1;
|
|
155
|
+
invalidateNodeLocalTransform(far);
|
|
156
|
+
invalidateNodeLocalTransform(near);
|
|
157
|
+
addNodeChild(scene, near);
|
|
158
|
+
addNodeChild(scene, far);
|
|
159
|
+
const camera = createCamera3D({
|
|
160
|
+
far: 100,
|
|
161
|
+
near: 0.1,
|
|
162
|
+
projection: { halfHeight: 1, halfWidth: 1, kind: 'orthographic' },
|
|
163
|
+
});
|
|
164
|
+
setCamera3DViewMatrix4FromLookAt(camera, { x: 0, y: 0, z: 5 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 });
|
|
165
|
+
drawWgpuScene3D(state, scene, camera, LIGHTS);
|
|
166
|
+
expect(getWgpuScene3DRuntime(state).blendedDrawList.map((entry) => entry.mesh)).toEqual([far, near]);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('reuses opaque and blended draw records across frames', () => {
|
|
170
|
+
const { state } = makeWgpuScene3DState();
|
|
171
|
+
registerStandardPbrWgpuMaterial(state);
|
|
172
|
+
const scene = createNode3D(Node3DKind);
|
|
173
|
+
const opaque = createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]);
|
|
174
|
+
const material = createStandardPbrMaterial();
|
|
175
|
+
material.alphaMode = 'blend';
|
|
176
|
+
const blended = createMesh(createBoxMeshGeometry(), [material]);
|
|
177
|
+
addNodeChild(scene, opaque);
|
|
178
|
+
addNodeChild(scene, blended);
|
|
179
|
+
const camera = makeCamera();
|
|
180
|
+
|
|
181
|
+
drawWgpuScene3D(state, scene, camera, LIGHTS);
|
|
182
|
+
const runtime = getWgpuScene3DRuntime(state);
|
|
183
|
+
const opaqueEntry = runtime.opaqueDrawList[0];
|
|
184
|
+
const blendedEntry = runtime.blendedDrawList[0];
|
|
185
|
+
drawWgpuScene3D(state, scene, camera, LIGHTS);
|
|
186
|
+
|
|
187
|
+
expect(runtime.opaqueDrawList[0]).toBe(opaqueEntry);
|
|
188
|
+
expect(runtime.blendedDrawList[0]).toBe(blendedEntry);
|
|
189
|
+
expect(runtime.opaquePool).toHaveLength(0);
|
|
190
|
+
expect(runtime.blendedPool).toHaveLength(0);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('draws a scene ParticleEmitter3D as a final pass without a manual emitter call', () => {
|
|
194
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
195
|
+
const scene = createNode3D(Node3DKind);
|
|
196
|
+
addNodeChild(scene, makeParticleEmitter2D(3));
|
|
197
|
+
// No mesh and no manual drawWgpuScene3DParticleEmitter3Ds — drawWgpuScene3D must render the emitter itself
|
|
198
|
+
// (a 6-index instanced quad draw), mirroring drawGlScene3D's automatic emitter pass.
|
|
199
|
+
drawWgpuScene3D(state, scene, makeCamera(), LIGHTS);
|
|
200
|
+
const draw = fake.calls.find((c) => c.name === 'drawIndexed');
|
|
201
|
+
expect(draw).toBeDefined();
|
|
202
|
+
expect(draw!.args[0]).toBe(6);
|
|
203
|
+
expect(draw!.args[1]).toBe(3);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe('isWgpuMeshGpuSkinned', () => {
|
|
208
|
+
it('selects GPU skinning only for a skin plus joints0 and weights0 geometry', () => {
|
|
209
|
+
const { state } = makeWgpuScene3DState();
|
|
210
|
+
registerWgpuGpuSkinning(state);
|
|
211
|
+
const rigid = createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]);
|
|
212
|
+
expect(isWgpuMeshGpuSkinned(state, rigid)).toBe(false);
|
|
213
|
+
|
|
214
|
+
const skinned = createMesh(
|
|
215
|
+
createMeshGeometry({
|
|
216
|
+
indices: new Uint16Array([0, 0, 0]),
|
|
217
|
+
layout: CANONICAL_SKINNED_MESH_GEOMETRY_LAYOUT,
|
|
218
|
+
vertices: new Float32Array(20),
|
|
219
|
+
}),
|
|
220
|
+
[createStandardPbrMaterial()],
|
|
221
|
+
);
|
|
222
|
+
skinned.skin = { skeleton: { jointMatrices: new Float32Array(16) } as Skeleton3D };
|
|
223
|
+
expect(isWgpuMeshGpuSkinned(state, skinned)).toBe(true);
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
function makeParticleEmitter2D(count: number): ParticleEmitter3D {
|
|
228
|
+
const emitter = createParticleEmitter3D();
|
|
229
|
+
reserveParticleEmitter3D(emitter, count);
|
|
230
|
+
const data = emitter.data;
|
|
231
|
+
data.particleCount = count;
|
|
232
|
+
for (let i = 0; i < count; i++) {
|
|
233
|
+
const tt = i * 4;
|
|
234
|
+
data.transforms[tt] = i;
|
|
235
|
+
data.transforms[tt + 1] = i;
|
|
236
|
+
data.transforms[tt + 2] = 0;
|
|
237
|
+
data.transforms[tt + 3] = 1;
|
|
238
|
+
data.positionsZ[i] = 0;
|
|
239
|
+
data.alphas[i] = 1;
|
|
240
|
+
const ct = i * 3;
|
|
241
|
+
data.colors[ct] = 1;
|
|
242
|
+
data.colors[ct + 1] = 1;
|
|
243
|
+
data.colors[ct + 2] = 1;
|
|
244
|
+
data.ids[i] = 0;
|
|
245
|
+
}
|
|
246
|
+
return emitter;
|
|
247
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createCamera3D } from '@flighthq/camera';
|
|
2
|
+
import { createMatrix3, createMatrix4 } from '@flighthq/geometry';
|
|
3
|
+
import { createEmissiveMaterial } from '@flighthq/materials';
|
|
4
|
+
import { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
5
|
+
import type { Camera3D, Scene3DLightBlock, Scene3DRenderProxy } from '@flighthq/types';
|
|
6
|
+
import { EmissiveMaterialKind } from '@flighthq/types';
|
|
7
|
+
|
|
8
|
+
import { emissiveWgpuMeshMaterialRenderer, registerEmissiveWgpuMaterial } from './emissiveWgpuMeshMaterialRenderer';
|
|
9
|
+
import { getWgpuMeshMaterialRenderer } from './wgpuMeshMaterialRegistry';
|
|
10
|
+
import { makeWgpuScene3DState } from './wgpuScene3DTestHelper';
|
|
11
|
+
|
|
12
|
+
function makeCamera(): Camera3D {
|
|
13
|
+
return createCamera3D({ far: 100, near: 0.1, projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' } });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const NO_LIGHTS: Scene3DLightBlock = {
|
|
17
|
+
ambientCount: 0,
|
|
18
|
+
data: new Float32Array(12),
|
|
19
|
+
directionalCount: 0,
|
|
20
|
+
hemisphereCount: 0,
|
|
21
|
+
pointCount: 0,
|
|
22
|
+
spotCount: 0,
|
|
23
|
+
version: 1,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function makeProxy(): Scene3DRenderProxy {
|
|
27
|
+
const geometry = createBoxMeshGeometry();
|
|
28
|
+
return {
|
|
29
|
+
material: createEmissiveMaterial(),
|
|
30
|
+
normalMatrix: createMatrix3(),
|
|
31
|
+
subset: geometry.subsets[0],
|
|
32
|
+
worldMatrix: createMatrix4(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe('emissiveWgpuMeshMaterialRenderer', () => {
|
|
37
|
+
it('bind uploads the emissive color and issues frame + material binds', () => {
|
|
38
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
39
|
+
emissiveWgpuMeshMaterialRenderer.bind(
|
|
40
|
+
state,
|
|
41
|
+
createEmissiveMaterial({ emissiveStrength: 3 }),
|
|
42
|
+
NO_LIGHTS,
|
|
43
|
+
makeCamera(),
|
|
44
|
+
);
|
|
45
|
+
expect(fake.calls.some((c) => c.name === 'setPipeline')).toBe(true);
|
|
46
|
+
expect(fake.calls.some((c) => c.name === 'writeBuffer')).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('draw issues an indexed draw over the subset after bind', () => {
|
|
50
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
51
|
+
const proxy = makeProxy();
|
|
52
|
+
emissiveWgpuMeshMaterialRenderer.bind(state, proxy.material, NO_LIGHTS, makeCamera());
|
|
53
|
+
emissiveWgpuMeshMaterialRenderer.draw(state, proxy, createBoxMeshGeometry());
|
|
54
|
+
expect(fake.calls.some((c) => c.name === 'drawIndexed')).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('registerEmissiveWgpuMaterial', () => {
|
|
59
|
+
it('installs the renderer for EmissiveMaterialKind', () => {
|
|
60
|
+
const { state } = makeWgpuScene3DState();
|
|
61
|
+
registerEmissiveWgpuMaterial(state);
|
|
62
|
+
expect(getWgpuMeshMaterialRenderer(state, EmissiveMaterialKind)).toBe(emissiveWgpuMeshMaterialRenderer);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { addLogSink, createMemoryLogSink, getMemoryLogSinkEntries, removeLogSink } from '@flighthq/log';
|
|
2
|
+
import { createCustomShaderMaterial } from '@flighthq/materials';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
areWgpuScene3DCustomShaderGuardsEnabled,
|
|
6
|
+
enableWgpuScene3DCustomShaderGuards,
|
|
7
|
+
runWgpuCustomShaderGuards,
|
|
8
|
+
} from './enableWgpuScene3DCustomShaderGuards';
|
|
9
|
+
import { makeWgpuScene3DState } from './wgpuScene3DTestHelper';
|
|
10
|
+
|
|
11
|
+
describe('areWgpuScene3DCustomShaderGuardsEnabled', () => {
|
|
12
|
+
it('reports installation state', () => {
|
|
13
|
+
const { state } = makeWgpuScene3DState();
|
|
14
|
+
expect(areWgpuScene3DCustomShaderGuardsEnabled(state)).toBe(false);
|
|
15
|
+
enableWgpuScene3DCustomShaderGuards(state);
|
|
16
|
+
expect(areWgpuScene3DCustomShaderGuardsEnabled(state)).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('enableWgpuScene3DCustomShaderGuards', () => {
|
|
21
|
+
it('is idempotent', () => {
|
|
22
|
+
const { state } = makeWgpuScene3DState();
|
|
23
|
+
enableWgpuScene3DCustomShaderGuards(state);
|
|
24
|
+
enableWgpuScene3DCustomShaderGuards(state);
|
|
25
|
+
expect(areWgpuScene3DCustomShaderGuardsEnabled(state)).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('runWgpuCustomShaderGuards', () => {
|
|
30
|
+
it('warns on bad vector lengths, capacity overflow, and missing reserved bindings', () => {
|
|
31
|
+
const { state } = makeWgpuScene3DState();
|
|
32
|
+
enableWgpuScene3DCustomShaderGuards(state);
|
|
33
|
+
const uniforms: Record<string, number[]> = {};
|
|
34
|
+
for (let i = 0; i < 33; i++) uniforms[`value${i}`] = [i];
|
|
35
|
+
uniforms['invalid'] = [1, 2, 3, 4, 5];
|
|
36
|
+
const material = createCustomShaderMaterial({
|
|
37
|
+
shaderKey: 'guard-contract-test',
|
|
38
|
+
textures: { a: {} as never },
|
|
39
|
+
uniforms,
|
|
40
|
+
});
|
|
41
|
+
const sink = createMemoryLogSink(16);
|
|
42
|
+
addLogSink(sink.sink);
|
|
43
|
+
try {
|
|
44
|
+
runWgpuCustomShaderGuards(
|
|
45
|
+
state,
|
|
46
|
+
material.shaderKey,
|
|
47
|
+
'@group(0) @binding(0) var<uniform> frame: vec4f;',
|
|
48
|
+
material,
|
|
49
|
+
);
|
|
50
|
+
const messages = getMemoryLogSinkEntries(sink).map((entry) =>
|
|
51
|
+
String((entry.data as Record<string, unknown>).message),
|
|
52
|
+
);
|
|
53
|
+
expect(messages.some((message) => message.includes('1–4 components'))).toBe(true);
|
|
54
|
+
expect(messages.some((message) => message.includes('32-vec4'))).toBe(true);
|
|
55
|
+
expect(messages.some((message) => message.includes('@group(1) @binding(0)'))).toBe(true);
|
|
56
|
+
expect(messages.some((message) => message.includes('@group(3) @binding(1)'))).toBe(true);
|
|
57
|
+
} finally {
|
|
58
|
+
removeLogSink(sink.sink);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('does nothing until enabled', () => {
|
|
63
|
+
const { state } = makeWgpuScene3DState();
|
|
64
|
+
const sink = createMemoryLogSink(2);
|
|
65
|
+
addLogSink(sink.sink);
|
|
66
|
+
try {
|
|
67
|
+
runWgpuCustomShaderGuards(state, 'disabled-guard-test', '', createCustomShaderMaterial());
|
|
68
|
+
expect(getMemoryLogSinkEntries(sink)).toHaveLength(0);
|
|
69
|
+
} finally {
|
|
70
|
+
removeLogSink(sink.sink);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createPointLight } from '@flighthq/lighting';
|
|
2
|
+
import { addLogSink, createMemoryLogSink, getMemoryLogSinkEntries, removeLogSink } from '@flighthq/log';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
areWgpuScene3DForwardLightSelectionGuardsEnabled,
|
|
6
|
+
enableWgpuScene3DForwardLightSelectionGuards,
|
|
7
|
+
} from './enableWgpuScene3DForwardLightSelectionGuards';
|
|
8
|
+
import { getWgpuScene3DRuntime } from './wgpuScene3DRuntime';
|
|
9
|
+
import { makeWgpuScene3DState } from './wgpuScene3DTestHelper';
|
|
10
|
+
|
|
11
|
+
describe('areWgpuScene3DForwardLightSelectionGuardsEnabled', () => {
|
|
12
|
+
it('reports false before installation and true afterward', () => {
|
|
13
|
+
const { state } = makeWgpuScene3DState();
|
|
14
|
+
expect(areWgpuScene3DForwardLightSelectionGuardsEnabled(state)).toBe(false);
|
|
15
|
+
enableWgpuScene3DForwardLightSelectionGuards(state);
|
|
16
|
+
expect(areWgpuScene3DForwardLightSelectionGuardsEnabled(state)).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('enableWgpuScene3DForwardLightSelectionGuards', () => {
|
|
21
|
+
it('installs the opt-in warning with actionable selection guidance', () => {
|
|
22
|
+
const { state } = makeWgpuScene3DState();
|
|
23
|
+
enableWgpuScene3DForwardLightSelectionGuards(state);
|
|
24
|
+
const sink = createMemoryLogSink(4);
|
|
25
|
+
addLogSink(sink.sink);
|
|
26
|
+
try {
|
|
27
|
+
getWgpuScene3DRuntime(state).forwardLightSelectionGuard!({
|
|
28
|
+
ambient: null,
|
|
29
|
+
directional: null,
|
|
30
|
+
point: Array.from({ length: 5 }, () => createPointLight()),
|
|
31
|
+
});
|
|
32
|
+
const entries = getMemoryLogSinkEntries(sink);
|
|
33
|
+
expect(entries).toHaveLength(1);
|
|
34
|
+
expect(String((entries[0].data as Record<string, unknown>).message)).toContain('prepareWgpuScene3DForwardLights');
|
|
35
|
+
} finally {
|
|
36
|
+
removeLogSink(sink.sink);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createPointLight } from '@flighthq/lighting';
|
|
2
|
+
|
|
3
|
+
import { explainWgpuScene3DForwardLightSelection } from './explainWgpuScene3DForwardLightSelection';
|
|
4
|
+
|
|
5
|
+
describe('explainWgpuScene3DForwardLightSelection', () => {
|
|
6
|
+
it('distinguishes within-budget, required, and prepared selection', () => {
|
|
7
|
+
const within = { ambient: null, directional: null, point: [createPointLight()] };
|
|
8
|
+
expect(explainWgpuScene3DForwardLightSelection(within).reason).toBe('within-budget');
|
|
9
|
+
const excess = { ambient: null, directional: null, point: Array.from({ length: 5 }, () => createPointLight()) };
|
|
10
|
+
expect(explainWgpuScene3DForwardLightSelection(excess).reason).toBe('selection-required');
|
|
11
|
+
expect(explainWgpuScene3DForwardLightSelection(excess, { meshCount: 0, meshLightBlocks: [] }).reason).toBe(
|
|
12
|
+
'selection-prepared',
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createCamera3D } from '@flighthq/camera';
|
|
2
|
+
import { createMatrix3, createMatrix4 } from '@flighthq/geometry';
|
|
3
|
+
import { createIridescencePbrMaterial } from '@flighthq/materials';
|
|
4
|
+
import { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
5
|
+
import type { Camera3D, Matrix3, Matrix4, Scene3DLightBlock, Scene3DRenderProxy } from '@flighthq/types';
|
|
6
|
+
import { IridescencePbrMaterialKind } from '@flighthq/types';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
iridescencePbrWgpuMeshMaterialRenderer,
|
|
10
|
+
registerIridescencePbrWgpuMaterial,
|
|
11
|
+
} from './iridescencePbrWgpuMeshMaterialRenderer';
|
|
12
|
+
import { getWgpuMeshMaterialRenderer } from './wgpuMeshMaterialRegistry';
|
|
13
|
+
import { makeWgpuScene3DState } from './wgpuScene3DTestHelper';
|
|
14
|
+
|
|
15
|
+
function makeCamera(): Camera3D {
|
|
16
|
+
return createCamera3D({ far: 100, near: 0.1, projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' } });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function makeLights(): Scene3DLightBlock {
|
|
20
|
+
const data = new Float32Array(12);
|
|
21
|
+
data[1] = -1;
|
|
22
|
+
data[4] = 1;
|
|
23
|
+
data[5] = 1;
|
|
24
|
+
data[6] = 1;
|
|
25
|
+
data[8] = 0.1;
|
|
26
|
+
data[9] = 0.1;
|
|
27
|
+
data[10] = 0.1;
|
|
28
|
+
return { ambientCount: 1, data, directionalCount: 1, hemisphereCount: 0, pointCount: 0, spotCount: 0, version: 1 };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function makeProxy(): Scene3DRenderProxy {
|
|
32
|
+
const geometry = createBoxMeshGeometry();
|
|
33
|
+
return {
|
|
34
|
+
material: createIridescencePbrMaterial({ iridescence: 1 }),
|
|
35
|
+
normalMatrix: createMatrix3() as Matrix3,
|
|
36
|
+
subset: geometry.subsets[0],
|
|
37
|
+
worldMatrix: createMatrix4() as Matrix4,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('iridescencePbrWgpuMeshMaterialRenderer', () => {
|
|
42
|
+
it('bind selects a pipeline and binds frame + material groups + uploads uniforms', () => {
|
|
43
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
44
|
+
iridescencePbrWgpuMeshMaterialRenderer.bind(
|
|
45
|
+
state,
|
|
46
|
+
createIridescencePbrMaterial({ iridescence: 1 }),
|
|
47
|
+
makeLights(),
|
|
48
|
+
makeCamera(),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(fake.calls.some((c) => c.name === 'createRenderPipeline')).toBe(true);
|
|
52
|
+
expect(fake.calls.some((c) => c.name === 'setPipeline')).toBe(true);
|
|
53
|
+
expect(fake.calls.filter((c) => c.name === 'setBindGroup').length).toBeGreaterThanOrEqual(2);
|
|
54
|
+
expect(fake.calls.some((c) => c.name === 'writeBuffer')).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('bind compiles the IRIDESCENCE extension variant', () => {
|
|
58
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
59
|
+
iridescencePbrWgpuMeshMaterialRenderer.bind(state, createIridescencePbrMaterial(), makeLights(), makeCamera());
|
|
60
|
+
const moduleCall = fake.calls.find((c) => c.name === 'createShaderModule');
|
|
61
|
+
const code = (moduleCall!.args[0] as { code: string }).code;
|
|
62
|
+
expect(code).toContain('const IRIDESCENCE : bool = true;');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('draw uploads geometry and issues an indexed draw over the subset range', () => {
|
|
66
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
67
|
+
const proxy = makeProxy();
|
|
68
|
+
iridescencePbrWgpuMeshMaterialRenderer.bind(state, proxy.material, makeLights(), makeCamera());
|
|
69
|
+
iridescencePbrWgpuMeshMaterialRenderer.draw(state, proxy, createBoxMeshGeometry());
|
|
70
|
+
|
|
71
|
+
const drawCall = fake.calls.find((c) => c.name === 'drawIndexed');
|
|
72
|
+
expect(drawCall).toBeDefined();
|
|
73
|
+
expect(drawCall!.args[0]).toBe(proxy.subset.indexCount);
|
|
74
|
+
expect(drawCall!.args[2]).toBe(proxy.subset.indexOffset);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('draw is a no-op when bind has not selected a pipeline', () => {
|
|
78
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
79
|
+
iridescencePbrWgpuMeshMaterialRenderer.draw(state, makeProxy(), createBoxMeshGeometry());
|
|
80
|
+
expect(fake.calls.some((c) => c.name === 'drawIndexed')).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('registerIridescencePbrWgpuMaterial', () => {
|
|
85
|
+
it('installs the renderer for IridescencePbrMaterialKind', () => {
|
|
86
|
+
const { state } = makeWgpuScene3DState();
|
|
87
|
+
registerIridescencePbrWgpuMaterial(state);
|
|
88
|
+
expect(getWgpuMeshMaterialRenderer(state, IridescencePbrMaterialKind)).toBe(iridescencePbrWgpuMeshMaterialRenderer);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createCamera3D } from '@flighthq/camera';
|
|
2
|
+
import { createMatrix3, createMatrix4 } from '@flighthq/geometry';
|
|
3
|
+
import { createLambertMaterial } from '@flighthq/materials';
|
|
4
|
+
import { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
5
|
+
import type { Camera3D, Matrix3, Matrix4, Scene3DLightBlock, Scene3DRenderProxy } from '@flighthq/types';
|
|
6
|
+
import { LambertMaterialKind } from '@flighthq/types';
|
|
7
|
+
|
|
8
|
+
import { lambertWgpuMeshMaterialRenderer, registerLambertWgpuMaterial } from './lambertWgpuMeshMaterialRenderer';
|
|
9
|
+
import { getWgpuMeshMaterialRenderer } from './wgpuMeshMaterialRegistry';
|
|
10
|
+
import { makeWgpuScene3DState } from './wgpuScene3DTestHelper';
|
|
11
|
+
|
|
12
|
+
function makeCamera(): Camera3D {
|
|
13
|
+
return createCamera3D({ far: 100, near: 0.1, projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' } });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function makeLights(): Scene3DLightBlock {
|
|
17
|
+
const data = new Float32Array(12);
|
|
18
|
+
data[0] = 0;
|
|
19
|
+
data[1] = -1;
|
|
20
|
+
data[2] = 0;
|
|
21
|
+
data[4] = 1;
|
|
22
|
+
data[5] = 1;
|
|
23
|
+
data[6] = 1;
|
|
24
|
+
data[8] = 0.1;
|
|
25
|
+
data[9] = 0.1;
|
|
26
|
+
data[10] = 0.1;
|
|
27
|
+
return { ambientCount: 1, data, directionalCount: 1, hemisphereCount: 0, pointCount: 0, spotCount: 0, version: 1 };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function makeProxy(): Scene3DRenderProxy {
|
|
31
|
+
const geometry = createBoxMeshGeometry();
|
|
32
|
+
return {
|
|
33
|
+
material: createLambertMaterial(),
|
|
34
|
+
normalMatrix: createMatrix3() as Matrix3,
|
|
35
|
+
subset: geometry.subsets[0],
|
|
36
|
+
worldMatrix: createMatrix4() as Matrix4,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('lambertWgpuMeshMaterialRenderer', () => {
|
|
41
|
+
it('bind selects a pipeline and binds frame + material groups + uploads uniforms', () => {
|
|
42
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
43
|
+
lambertWgpuMeshMaterialRenderer.bind(state, createLambertMaterial(), makeLights(), makeCamera());
|
|
44
|
+
|
|
45
|
+
expect(fake.calls.some((c) => c.name === 'createRenderPipeline')).toBe(true);
|
|
46
|
+
expect(fake.calls.some((c) => c.name === 'setPipeline')).toBe(true);
|
|
47
|
+
expect(fake.calls.filter((c) => c.name === 'setBindGroup').length).toBeGreaterThanOrEqual(2);
|
|
48
|
+
expect(fake.calls.some((c) => c.name === 'writeBuffer')).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('draw uploads geometry and issues an indexed draw over the subset range', () => {
|
|
52
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
53
|
+
const proxy = makeProxy();
|
|
54
|
+
const geometry = createBoxMeshGeometry();
|
|
55
|
+
lambertWgpuMeshMaterialRenderer.bind(state, proxy.material, makeLights(), makeCamera());
|
|
56
|
+
lambertWgpuMeshMaterialRenderer.draw(state, proxy, geometry);
|
|
57
|
+
|
|
58
|
+
const drawCall = fake.calls.find((c) => c.name === 'drawIndexed');
|
|
59
|
+
expect(drawCall).toBeDefined();
|
|
60
|
+
expect(drawCall!.args[0]).toBe(proxy.subset.indexCount);
|
|
61
|
+
expect(drawCall!.args[2]).toBe(proxy.subset.indexOffset);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('draw is a no-op when bind has not selected a pipeline', () => {
|
|
65
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
66
|
+
lambertWgpuMeshMaterialRenderer.draw(state, makeProxy(), createBoxMeshGeometry());
|
|
67
|
+
expect(fake.calls.some((c) => c.name === 'drawIndexed')).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('registerLambertWgpuMaterial', () => {
|
|
72
|
+
it('installs the renderer for LambertMaterialKind', () => {
|
|
73
|
+
const { state } = makeWgpuScene3DState();
|
|
74
|
+
registerLambertWgpuMaterial(state);
|
|
75
|
+
expect(getWgpuMeshMaterialRenderer(state, LambertMaterialKind)).toBe(lambertWgpuMeshMaterialRenderer);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createCamera3D } from '@flighthq/camera';
|
|
2
|
+
import { createMatrix3, createMatrix4 } from '@flighthq/geometry';
|
|
3
|
+
import { createMatcapMaterial } from '@flighthq/materials';
|
|
4
|
+
import { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
5
|
+
import type { Camera3D, Scene3DLightBlock, Scene3DRenderProxy } from '@flighthq/types';
|
|
6
|
+
import { MatcapMaterialKind } from '@flighthq/types';
|
|
7
|
+
|
|
8
|
+
import { matcapWgpuMeshMaterialRenderer, registerMatcapWgpuMaterial } from './matcapWgpuMeshMaterialRenderer';
|
|
9
|
+
import { getWgpuMeshMaterialRenderer } from './wgpuMeshMaterialRegistry';
|
|
10
|
+
import { makeWgpuScene3DState } from './wgpuScene3DTestHelper';
|
|
11
|
+
|
|
12
|
+
function makeCamera(): Camera3D {
|
|
13
|
+
return createCamera3D({ far: 100, near: 0.1, projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' } });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const NO_LIGHTS: Scene3DLightBlock = {
|
|
17
|
+
ambientCount: 0,
|
|
18
|
+
data: new Float32Array(12),
|
|
19
|
+
directionalCount: 0,
|
|
20
|
+
hemisphereCount: 0,
|
|
21
|
+
pointCount: 0,
|
|
22
|
+
spotCount: 0,
|
|
23
|
+
version: 1,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function makeProxy(): Scene3DRenderProxy {
|
|
27
|
+
const geometry = createBoxMeshGeometry();
|
|
28
|
+
return {
|
|
29
|
+
material: createMatcapMaterial(),
|
|
30
|
+
normalMatrix: createMatrix3(),
|
|
31
|
+
subset: geometry.subsets[0],
|
|
32
|
+
worldMatrix: createMatrix4(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe('matcapWgpuMeshMaterialRenderer', () => {
|
|
37
|
+
it('bind selects a pipeline and binds the frame + material groups', () => {
|
|
38
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
39
|
+
matcapWgpuMeshMaterialRenderer.bind(state, createMatcapMaterial(), NO_LIGHTS, makeCamera());
|
|
40
|
+
expect(fake.calls.some((c) => c.name === 'createRenderPipeline')).toBe(true);
|
|
41
|
+
expect(fake.calls.some((c) => c.name === 'setPipeline')).toBe(true);
|
|
42
|
+
expect(fake.calls.filter((c) => c.name === 'setBindGroup').length).toBeGreaterThanOrEqual(2);
|
|
43
|
+
expect(fake.calls.some((c) => c.name === 'writeBuffer')).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('draw issues an indexed draw over the subset after bind', () => {
|
|
47
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
48
|
+
const proxy = makeProxy();
|
|
49
|
+
matcapWgpuMeshMaterialRenderer.bind(state, proxy.material, NO_LIGHTS, makeCamera());
|
|
50
|
+
matcapWgpuMeshMaterialRenderer.draw(state, proxy, createBoxMeshGeometry());
|
|
51
|
+
const drawCall = fake.calls.find((c) => c.name === 'drawIndexed');
|
|
52
|
+
expect(drawCall).toBeDefined();
|
|
53
|
+
expect(drawCall!.args[0]).toBe(proxy.subset.indexCount);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('draw is a no-op when bind has not selected a pipeline', () => {
|
|
57
|
+
const { fake, state } = makeWgpuScene3DState();
|
|
58
|
+
matcapWgpuMeshMaterialRenderer.draw(state, makeProxy(), createBoxMeshGeometry());
|
|
59
|
+
expect(fake.calls.some((c) => c.name === 'drawIndexed')).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('registerMatcapWgpuMaterial', () => {
|
|
64
|
+
it('installs the renderer for MatcapMaterialKind', () => {
|
|
65
|
+
const { state } = makeWgpuScene3DState();
|
|
66
|
+
registerMatcapWgpuMaterial(state);
|
|
67
|
+
expect(getWgpuMeshMaterialRenderer(state, MatcapMaterialKind)).toBe(matcapWgpuMeshMaterialRenderer);
|
|
68
|
+
});
|
|
69
|
+
});
|