@flighthq/scene3d-gl 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/anisotropyPbrGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/anisotropyPbrGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/anisotropyPbrGlMeshMaterialRenderer.js +45 -0
- package/dist/anisotropyPbrGlMeshMaterialRenderer.js.map +1 -0
- package/dist/blinnPhongGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/blinnPhongGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/blinnPhongGlMeshMaterialRenderer.js +102 -0
- package/dist/blinnPhongGlMeshMaterialRenderer.js.map +1 -0
- package/dist/clearcoatPbrGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/clearcoatPbrGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/clearcoatPbrGlMeshMaterialRenderer.js +45 -0
- package/dist/clearcoatPbrGlMeshMaterialRenderer.js.map +1 -0
- package/dist/customShaderGlMeshMaterialRenderer.d.ts +6 -0
- package/dist/customShaderGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/customShaderGlMeshMaterialRenderer.js +136 -0
- package/dist/customShaderGlMeshMaterialRenderer.js.map +1 -0
- package/dist/depthGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/depthGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/depthGlMeshMaterialRenderer.js +36 -0
- package/dist/depthGlMeshMaterialRenderer.js.map +1 -0
- package/dist/drawGlScene3D.d.ts +3 -0
- package/dist/drawGlScene3D.d.ts.map +1 -0
- package/dist/drawGlScene3D.js +251 -0
- package/dist/drawGlScene3D.js.map +1 -0
- package/dist/emissiveGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/emissiveGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/emissiveGlMeshMaterialRenderer.js +49 -0
- package/dist/emissiveGlMeshMaterialRenderer.js.map +1 -0
- package/dist/enableGlScene3DColorSpaceGuards.d.ts +4 -0
- package/dist/enableGlScene3DColorSpaceGuards.d.ts.map +1 -0
- package/dist/enableGlScene3DColorSpaceGuards.js +22 -0
- package/dist/enableGlScene3DColorSpaceGuards.js.map +1 -0
- package/dist/enableGlScene3DCustomShaderGuards.d.ts +4 -0
- package/dist/enableGlScene3DCustomShaderGuards.d.ts.map +1 -0
- package/dist/enableGlScene3DCustomShaderGuards.js +71 -0
- package/dist/enableGlScene3DCustomShaderGuards.js.map +1 -0
- package/dist/enableGlScene3DDeformGuards.d.ts +4 -0
- package/dist/enableGlScene3DDeformGuards.d.ts.map +1 -0
- package/dist/enableGlScene3DDeformGuards.js +42 -0
- package/dist/enableGlScene3DDeformGuards.js.map +1 -0
- package/dist/enableGlScene3DForwardLightSelectionGuards.d.ts +4 -0
- package/dist/enableGlScene3DForwardLightSelectionGuards.d.ts.map +1 -0
- package/dist/enableGlScene3DForwardLightSelectionGuards.js +21 -0
- package/dist/enableGlScene3DForwardLightSelectionGuards.js.map +1 -0
- package/dist/explainGlScene3DForwardLightSelection.d.ts +3 -0
- package/dist/explainGlScene3DForwardLightSelection.d.ts.map +1 -0
- package/dist/explainGlScene3DForwardLightSelection.js +19 -0
- package/dist/explainGlScene3DForwardLightSelection.js.map +1 -0
- package/dist/glClassicPrelude.d.ts +9 -0
- package/dist/glClassicPrelude.d.ts.map +1 -0
- package/dist/glClassicPrelude.js +288 -0
- package/dist/glClassicPrelude.js.map +1 -0
- package/dist/glDebugPrelude.d.ts +9 -0
- package/dist/glDebugPrelude.d.ts.map +1 -0
- package/dist/glDebugPrelude.js +143 -0
- package/dist/glDebugPrelude.js.map +1 -0
- package/dist/glEnvironmentCube.d.ts +5 -0
- package/dist/glEnvironmentCube.d.ts.map +1 -0
- package/dist/glEnvironmentCube.js +65 -0
- package/dist/glEnvironmentCube.js.map +1 -0
- package/dist/glEnvironmentIblBake.d.ts +4 -0
- package/dist/glEnvironmentIblBake.d.ts.map +1 -0
- package/dist/glEnvironmentIblBake.js +365 -0
- package/dist/glEnvironmentIblBake.js.map +1 -0
- package/dist/glEnvironmentSkybox.d.ts +3 -0
- package/dist/glEnvironmentSkybox.d.ts.map +1 -0
- package/dist/glEnvironmentSkybox.js +95 -0
- package/dist/glEnvironmentSkybox.js.map +1 -0
- package/dist/glLitProgram.d.ts +5 -0
- package/dist/glLitProgram.d.ts.map +1 -0
- package/dist/glLitProgram.js +211 -0
- package/dist/glLitProgram.js.map +1 -0
- package/dist/glMatcapPrelude.d.ts +8 -0
- package/dist/glMatcapPrelude.d.ts.map +1 -0
- package/dist/glMatcapPrelude.js +118 -0
- package/dist/glMatcapPrelude.js.map +1 -0
- package/dist/glMeshMaterialRegistry.d.ts +5 -0
- package/dist/glMeshMaterialRegistry.d.ts.map +1 -0
- package/dist/glMeshMaterialRegistry.js +29 -0
- package/dist/glMeshMaterialRegistry.js.map +1 -0
- package/dist/glMeshProgram.d.ts +14 -0
- package/dist/glMeshProgram.d.ts.map +1 -0
- package/dist/glMeshProgram.js +193 -0
- package/dist/glMeshProgram.js.map +1 -0
- package/dist/glMeshUpload.d.ts +4 -0
- package/dist/glMeshUpload.d.ts.map +1 -0
- package/dist/glMeshUpload.js +190 -0
- package/dist/glMeshUpload.js.map +1 -0
- package/dist/glParticleEmitter3D.d.ts +4 -0
- package/dist/glParticleEmitter3D.d.ts.map +1 -0
- package/dist/glParticleEmitter3D.js +342 -0
- package/dist/glParticleEmitter3D.js.map +1 -0
- package/dist/glPbrPrelude.d.ts +8 -0
- package/dist/glPbrPrelude.d.ts.map +1 -0
- package/dist/glPbrPrelude.js +569 -0
- package/dist/glPbrPrelude.js.map +1 -0
- package/dist/glPbrProgramCache.d.ts +4 -0
- package/dist/glPbrProgramCache.d.ts.map +1 -0
- package/dist/glPbrProgramCache.js +63 -0
- package/dist/glPbrProgramCache.js.map +1 -0
- package/dist/glPbrStandardBlock.d.ts +13 -0
- package/dist/glPbrStandardBlock.d.ts.map +1 -0
- package/dist/glPbrStandardBlock.js +107 -0
- package/dist/glPbrStandardBlock.js.map +1 -0
- package/dist/glScene3DRuntime.d.ts +5 -0
- package/dist/glScene3DRuntime.d.ts.map +1 -0
- package/dist/glScene3DRuntime.js +99 -0
- package/dist/glScene3DRuntime.js.map +1 -0
- package/dist/glScene3DTestHelper.d.ts +20 -0
- package/dist/glScene3DTestHelper.d.ts.map +1 -0
- package/dist/glScene3DTestHelper.js +178 -0
- package/dist/glScene3DTestHelper.js.map +1 -0
- package/dist/glScene3DTime.d.ts +4 -0
- package/dist/glScene3DTime.d.ts.map +1 -0
- package/dist/glScene3DTime.js +18 -0
- package/dist/glScene3DTime.js.map +1 -0
- package/dist/glShadedBuiltInModifiers.d.ts +11 -0
- package/dist/glShadedBuiltInModifiers.d.ts.map +1 -0
- package/dist/glShadedBuiltInModifiers.js +386 -0
- package/dist/glShadedBuiltInModifiers.js.map +1 -0
- package/dist/glShadedModifierSnippet.d.ts +4 -0
- package/dist/glShadedModifierSnippet.d.ts.map +1 -0
- package/dist/glShadedModifierSnippet.js +24 -0
- package/dist/glShadedModifierSnippet.js.map +1 -0
- package/dist/glShadedPrelude.d.ts +5 -0
- package/dist/glShadedPrelude.d.ts.map +1 -0
- package/dist/glShadedPrelude.js +393 -0
- package/dist/glShadedPrelude.js.map +1 -0
- package/dist/glShadowMap.d.ts +3 -0
- package/dist/glShadowMap.d.ts.map +1 -0
- package/dist/glShadowMap.js +150 -0
- package/dist/glShadowMap.js.map +1 -0
- package/dist/glToonPrelude.d.ts +7 -0
- package/dist/glToonPrelude.d.ts.map +1 -0
- package/dist/glToonPrelude.js +172 -0
- package/dist/glToonPrelude.js.map +1 -0
- package/dist/glUnlitPrelude.d.ts +9 -0
- package/dist/glUnlitPrelude.d.ts.map +1 -0
- package/dist/glUnlitPrelude.js +160 -0
- package/dist/glUnlitPrelude.js.map +1 -0
- package/dist/glWireframePrelude.d.ts +6 -0
- package/dist/glWireframePrelude.d.ts.map +1 -0
- package/dist/glWireframePrelude.js +56 -0
- package/dist/glWireframePrelude.js.map +1 -0
- package/dist/glWireframeUpload.d.ts +4 -0
- package/dist/glWireframeUpload.d.ts.map +1 -0
- package/dist/glWireframeUpload.js +85 -0
- package/dist/glWireframeUpload.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/iridescencePbrGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/iridescencePbrGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/iridescencePbrGlMeshMaterialRenderer.js +54 -0
- package/dist/iridescencePbrGlMeshMaterialRenderer.js.map +1 -0
- package/dist/lambertGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/lambertGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/lambertGlMeshMaterialRenderer.js +70 -0
- package/dist/lambertGlMeshMaterialRenderer.js.map +1 -0
- package/dist/matcapGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/matcapGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/matcapGlMeshMaterialRenderer.js +49 -0
- package/dist/matcapGlMeshMaterialRenderer.js.map +1 -0
- package/dist/normalGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/normalGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/normalGlMeshMaterialRenderer.js +39 -0
- package/dist/normalGlMeshMaterialRenderer.js.map +1 -0
- package/dist/phongGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/phongGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/phongGlMeshMaterialRenderer.js +90 -0
- package/dist/phongGlMeshMaterialRenderer.js.map +1 -0
- package/dist/prepareGlScene3DForwardLights.d.ts +3 -0
- package/dist/prepareGlScene3DForwardLights.d.ts.map +1 -0
- package/dist/prepareGlScene3DForwardLights.js +117 -0
- package/dist/prepareGlScene3DForwardLights.js.map +1 -0
- package/dist/presentGlScene3D.d.ts +3 -0
- package/dist/presentGlScene3D.d.ts.map +1 -0
- package/dist/presentGlScene3D.js +21 -0
- package/dist/presentGlScene3D.js.map +1 -0
- package/dist/registerStandardPbrGlMaterial.d.ts +3 -0
- package/dist/registerStandardPbrGlMaterial.d.ts.map +1 -0
- package/dist/registerStandardPbrGlMaterial.js +11 -0
- package/dist/registerStandardPbrGlMaterial.js.map +1 -0
- package/dist/shadedGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/shadedGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/shadedGlMeshMaterialRenderer.js +135 -0
- package/dist/shadedGlMeshMaterialRenderer.js.map +1 -0
- package/dist/sheenPbrGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/sheenPbrGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/sheenPbrGlMeshMaterialRenderer.js +54 -0
- package/dist/sheenPbrGlMeshMaterialRenderer.js.map +1 -0
- package/dist/specularGlossinessPbrGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/specularGlossinessPbrGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/specularGlossinessPbrGlMeshMaterialRenderer.js +121 -0
- package/dist/specularGlossinessPbrGlMeshMaterialRenderer.js.map +1 -0
- package/dist/specularPbrGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/specularPbrGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/specularPbrGlMeshMaterialRenderer.js +55 -0
- package/dist/specularPbrGlMeshMaterialRenderer.js.map +1 -0
- package/dist/standardPbrGlMeshMaterialRenderer.d.ts +3 -0
- package/dist/standardPbrGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/standardPbrGlMeshMaterialRenderer.js +35 -0
- package/dist/standardPbrGlMeshMaterialRenderer.js.map +1 -0
- package/dist/subsurfacePbrGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/subsurfacePbrGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/subsurfacePbrGlMeshMaterialRenderer.js +57 -0
- package/dist/subsurfacePbrGlMeshMaterialRenderer.js.map +1 -0
- package/dist/toonGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/toonGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/toonGlMeshMaterialRenderer.js +81 -0
- package/dist/toonGlMeshMaterialRenderer.js.map +1 -0
- package/dist/transmissionVolumePbrGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/transmissionVolumePbrGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/transmissionVolumePbrGlMeshMaterialRenderer.js +62 -0
- package/dist/transmissionVolumePbrGlMeshMaterialRenderer.js.map +1 -0
- package/dist/unlitGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/unlitGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/unlitGlMeshMaterialRenderer.js +61 -0
- package/dist/unlitGlMeshMaterialRenderer.js.map +1 -0
- package/dist/vertexColorGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/vertexColorGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/vertexColorGlMeshMaterialRenderer.js +54 -0
- package/dist/vertexColorGlMeshMaterialRenderer.js.map +1 -0
- package/dist/wireframeGlMeshMaterialRenderer.d.ts +4 -0
- package/dist/wireframeGlMeshMaterialRenderer.d.ts.map +1 -0
- package/dist/wireframeGlMeshMaterialRenderer.js +50 -0
- package/dist/wireframeGlMeshMaterialRenderer.js.map +1 -0
- package/package.json +58 -0
- package/src/anisotropyPbrGlMeshMaterialRenderer.test.ts +86 -0
- package/src/blinnPhongGlMeshMaterialRenderer.test.ts +94 -0
- package/src/clearcoatPbrGlMeshMaterialRenderer.test.ts +81 -0
- package/src/customShaderGlMeshMaterialRenderer.test.ts +169 -0
- package/src/depthGlMeshMaterialRenderer.test.ts +69 -0
- package/src/drawGlScene3D.test.ts +304 -0
- package/src/emissiveGlMeshMaterialRenderer.test.ts +60 -0
- package/src/enableGlScene3DColorSpaceGuards.test.ts +63 -0
- package/src/enableGlScene3DCustomShaderGuards.test.ts +85 -0
- package/src/enableGlScene3DDeformGuards.test.ts +98 -0
- package/src/enableGlScene3DForwardLightSelectionGuards.test.ts +84 -0
- package/src/explainGlScene3DForwardLightSelection.test.ts +32 -0
- package/src/glClassicPrelude.test.ts +214 -0
- package/src/glDebugPrelude.test.ts +94 -0
- package/src/glEnvironmentCube.test.ts +71 -0
- package/src/glEnvironmentIblBake.test.ts +31 -0
- package/src/glEnvironmentSkybox.test.ts +24 -0
- package/src/glLitProgram.test.ts +181 -0
- package/src/glMatcapPrelude.test.ts +74 -0
- package/src/glMeshMaterialRegistry.test.ts +52 -0
- package/src/glMeshProgram.test.ts +406 -0
- package/src/glMeshUpload.test.ts +311 -0
- package/src/glParticleEmitter3D.test.ts +280 -0
- package/src/glPbrPrelude.test.ts +178 -0
- package/src/glPbrProgramCache.test.ts +116 -0
- package/src/glPbrStandardBlock.test.ts +85 -0
- package/src/glScene3DRuntime.test.ts +108 -0
- package/src/glScene3DTime.test.ts +31 -0
- package/src/glShadedBuiltInModifiers.test.ts +377 -0
- package/src/glShadedModifierSnippet.test.ts +46 -0
- package/src/glShadedPrelude.test.ts +177 -0
- package/src/glShadowMap.test.ts +219 -0
- package/src/glToonPrelude.test.ts +118 -0
- package/src/glUnlitPrelude.test.ts +156 -0
- package/src/glWireframePrelude.test.ts +46 -0
- package/src/glWireframeUpload.test.ts +50 -0
- package/src/iridescencePbrGlMeshMaterialRenderer.test.ts +86 -0
- package/src/lambertGlMeshMaterialRenderer.test.ts +92 -0
- package/src/matcapGlMeshMaterialRenderer.test.ts +70 -0
- package/src/normalGlMeshMaterialRenderer.test.ts +69 -0
- package/src/phongGlMeshMaterialRenderer.test.ts +94 -0
- package/src/prepareGlScene3DForwardLights.test.ts +106 -0
- package/src/presentGlScene3D.test.ts +98 -0
- package/src/registerStandardPbrGlMaterial.test.ts +14 -0
- package/src/shadedGlMeshMaterialRenderer.test.ts +106 -0
- package/src/sheenPbrGlMeshMaterialRenderer.test.ts +83 -0
- package/src/specularGlossinessPbrGlMeshMaterialRenderer.test.ts +96 -0
- package/src/specularPbrGlMeshMaterialRenderer.test.ts +83 -0
- package/src/standardPbrGlMeshMaterialRenderer.test.ts +88 -0
- package/src/subsurfacePbrGlMeshMaterialRenderer.test.ts +86 -0
- package/src/toonGlMeshMaterialRenderer.test.ts +92 -0
- package/src/transmissionVolumePbrGlMeshMaterialRenderer.test.ts +93 -0
- package/src/unlitGlMeshMaterialRenderer.test.ts +88 -0
- package/src/vertexColorGlMeshMaterialRenderer.test.ts +60 -0
- package/src/wireframeGlMeshMaterialRenderer.test.ts +69 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createCamera3D, setCamera3DViewMatrix4FromLookAt } from '@flighthq/camera';
|
|
2
|
+
import { createPointLight } from '@flighthq/lighting';
|
|
3
|
+
import { addLogSink, createMemoryLogSink, getMemoryLogSinkEntries, removeLogSink } from '@flighthq/log';
|
|
4
|
+
import { createStandardPbrMaterial } from '@flighthq/materials';
|
|
5
|
+
import { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
6
|
+
import { addNodeChild } from '@flighthq/node';
|
|
7
|
+
import { prepareScene3DRender } from '@flighthq/render';
|
|
8
|
+
import { createMesh, createNode3D, Node3DKind } from '@flighthq/scene3d';
|
|
9
|
+
import type { Camera3D, Scene3DLightsLike } from '@flighthq/types';
|
|
10
|
+
|
|
11
|
+
import { drawGlScene3D } from './drawGlScene3D';
|
|
12
|
+
import {
|
|
13
|
+
areGlScene3DForwardLightSelectionGuardsEnabled,
|
|
14
|
+
enableGlScene3DForwardLightSelectionGuards,
|
|
15
|
+
} from './enableGlScene3DForwardLightSelectionGuards';
|
|
16
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
17
|
+
import { prepareGlScene3DForwardLights } from './prepareGlScene3DForwardLights';
|
|
18
|
+
import { registerStandardPbrGlMaterial } from './registerStandardPbrGlMaterial';
|
|
19
|
+
|
|
20
|
+
function camera(): Camera3D {
|
|
21
|
+
const result = createCamera3D({
|
|
22
|
+
far: 100,
|
|
23
|
+
near: 0.1,
|
|
24
|
+
projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' },
|
|
25
|
+
});
|
|
26
|
+
setCamera3DViewMatrix4FromLookAt(result, { x: 0, y: 0, z: 5 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 });
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function excessLights(): Scene3DLightsLike {
|
|
31
|
+
return {
|
|
32
|
+
ambient: null,
|
|
33
|
+
directional: null,
|
|
34
|
+
point: Array.from({ length: 5 }, (_, x) => createPointLight({ position: { x: x - 2, y: 0, z: 2 }, range: 10 })),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('areGlScene3DForwardLightSelectionGuardsEnabled', () => {
|
|
39
|
+
it('reports false until guards are installed, then true', () => {
|
|
40
|
+
const { state } = makeGlScene3DState();
|
|
41
|
+
expect(areGlScene3DForwardLightSelectionGuardsEnabled(state)).toBe(false);
|
|
42
|
+
enableGlScene3DForwardLightSelectionGuards(state);
|
|
43
|
+
expect(areGlScene3DForwardLightSelectionGuardsEnabled(state)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('enableGlScene3DForwardLightSelectionGuards', () => {
|
|
48
|
+
it('warns when excess punctual lights draw without a prepared selection', () => {
|
|
49
|
+
const { state } = makeGlScene3DState();
|
|
50
|
+
registerStandardPbrGlMaterial(state);
|
|
51
|
+
enableGlScene3DForwardLightSelectionGuards(state);
|
|
52
|
+
const scene = createNode3D(Node3DKind);
|
|
53
|
+
addNodeChild(scene, createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]));
|
|
54
|
+
const sink = createMemoryLogSink(8);
|
|
55
|
+
addLogSink(sink.sink);
|
|
56
|
+
try {
|
|
57
|
+
drawGlScene3D(state, scene, camera(), excessLights());
|
|
58
|
+
const entries = getMemoryLogSinkEntries(sink);
|
|
59
|
+
expect(entries).toHaveLength(1);
|
|
60
|
+
expect(String((entries[0].data as Record<string, unknown>).message)).toContain('prepareGlScene3DForwardLights');
|
|
61
|
+
} finally {
|
|
62
|
+
removeLogSink(sink.sink);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('stays silent when the explicit per-object selection is supplied', () => {
|
|
67
|
+
const { state } = makeGlScene3DState();
|
|
68
|
+
registerStandardPbrGlMaterial(state);
|
|
69
|
+
enableGlScene3DForwardLightSelectionGuards(state);
|
|
70
|
+
const scene = createNode3D(Node3DKind);
|
|
71
|
+
addNodeChild(scene, createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]));
|
|
72
|
+
const view = camera();
|
|
73
|
+
const lights = excessLights();
|
|
74
|
+
const selected = prepareGlScene3DForwardLights(state, prepareScene3DRender(state, scene, view, lights), lights);
|
|
75
|
+
const sink = createMemoryLogSink(8);
|
|
76
|
+
addLogSink(sink.sink);
|
|
77
|
+
try {
|
|
78
|
+
drawGlScene3D(state, scene, view, lights, selected);
|
|
79
|
+
expect(getMemoryLogSinkEntries(sink)).toHaveLength(0);
|
|
80
|
+
} finally {
|
|
81
|
+
removeLogSink(sink.sink);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createPointLight } from '@flighthq/lighting';
|
|
2
|
+
import type { GlScene3DForwardLightList, Scene3DLightsLike } from '@flighthq/types';
|
|
3
|
+
|
|
4
|
+
import { explainGlScene3DForwardLightSelection } from './explainGlScene3DForwardLightSelection';
|
|
5
|
+
|
|
6
|
+
function lights(count: number): Scene3DLightsLike {
|
|
7
|
+
return {
|
|
8
|
+
ambient: null,
|
|
9
|
+
directional: null,
|
|
10
|
+
point: Array.from({ length: count }, () => createPointLight()),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('explainGlScene3DForwardLightSelection', () => {
|
|
15
|
+
it('reports a required selection when input truncation would occur', () => {
|
|
16
|
+
expect(explainGlScene3DForwardLightSelection(lights(5))).toEqual({
|
|
17
|
+
pointLightCount: 5,
|
|
18
|
+
reason: 'selection-required',
|
|
19
|
+
selectionPrepared: false,
|
|
20
|
+
spotLightCount: 0,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('reports when a selection was prepared', () => {
|
|
25
|
+
const prepared: GlScene3DForwardLightList = { meshCount: 0, meshLightBlocks: [] };
|
|
26
|
+
expect(explainGlScene3DForwardLightSelection(lights(5), prepared).reason).toBe('selection-prepared');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('reports when punctual inputs fit the fixed budget', () => {
|
|
30
|
+
expect(explainGlScene3DForwardLightSelection(lights(4)).reason).toBe('within-budget');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { GlClassicDefineKey } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
buildGlClassicDefineKey,
|
|
5
|
+
compileGlClassicProgram,
|
|
6
|
+
ensureGlClassicProgram,
|
|
7
|
+
getGlClassicFragmentSource,
|
|
8
|
+
getGlClassicFragmentSourceForKey,
|
|
9
|
+
getGlClassicVertexSource,
|
|
10
|
+
getGlClassicVertexSourceForKey,
|
|
11
|
+
} from './glClassicPrelude';
|
|
12
|
+
import { getGlScene3DRuntime } from './glScene3DRuntime';
|
|
13
|
+
import { makeFakeGl2, makeGlScene3DState } from './glScene3DTestHelper';
|
|
14
|
+
|
|
15
|
+
const LAMBERT: GlClassicDefineKey = {
|
|
16
|
+
alphaMaskEnabled: false,
|
|
17
|
+
hasAlphaMap: false,
|
|
18
|
+
hasDiffuseMap: false,
|
|
19
|
+
hasNormalMap: false,
|
|
20
|
+
hasSpecularMap: false,
|
|
21
|
+
hasUvTransform: false,
|
|
22
|
+
lightingModel: 'lambert',
|
|
23
|
+
};
|
|
24
|
+
const PHONG: GlClassicDefineKey = { ...LAMBERT, lightingModel: 'phong' };
|
|
25
|
+
const BLINNPHONG: GlClassicDefineKey = { ...LAMBERT, lightingModel: 'blinnphong' };
|
|
26
|
+
|
|
27
|
+
describe('buildGlClassicDefineKey', () => {
|
|
28
|
+
it('encodes the lighting model first, then the feature flags', () => {
|
|
29
|
+
expect(buildGlClassicDefineKey(LAMBERT)).toBe('l-------');
|
|
30
|
+
expect(buildGlClassicDefineKey(PHONG)).toBe('p-------');
|
|
31
|
+
expect(buildGlClassicDefineKey(BLINNPHONG)).toBe('b-------');
|
|
32
|
+
expect(
|
|
33
|
+
buildGlClassicDefineKey({
|
|
34
|
+
alphaMaskEnabled: true,
|
|
35
|
+
hasAlphaMap: true,
|
|
36
|
+
hasDiffuseMap: true,
|
|
37
|
+
hasNormalMap: true,
|
|
38
|
+
hasSpecularMap: true,
|
|
39
|
+
hasUvTransform: true,
|
|
40
|
+
lightingModel: 'phong',
|
|
41
|
+
}),
|
|
42
|
+
).toBe('pmdsnau-');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('encodes a non-identity uv transform in the u slot ahead of skin', () => {
|
|
46
|
+
expect(buildGlClassicDefineKey({ ...LAMBERT, hasUvTransform: true })).toBe('l-----u-');
|
|
47
|
+
expect(buildGlClassicDefineKey({ ...LAMBERT, hasUvTransform: true })).not.toBe(buildGlClassicDefineKey(LAMBERT));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('sets the trailing skin flag so a skinned variant keys distinctly', () => {
|
|
51
|
+
expect(buildGlClassicDefineKey({ ...LAMBERT, hasSkin: true })).toBe('l------k');
|
|
52
|
+
expect(buildGlClassicDefineKey({ ...LAMBERT, hasSkin: true })).not.toBe(buildGlClassicDefineKey(LAMBERT));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('produces distinct strings per lighting model so they never collide', () => {
|
|
56
|
+
const keys = new Set([
|
|
57
|
+
buildGlClassicDefineKey(LAMBERT),
|
|
58
|
+
buildGlClassicDefineKey(PHONG),
|
|
59
|
+
buildGlClassicDefineKey(BLINNPHONG),
|
|
60
|
+
]);
|
|
61
|
+
expect(keys.size).toBe(3);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('compileGlClassicProgram', () => {
|
|
66
|
+
it('compiles, links, and resolves the classic + lit uniform locations', () => {
|
|
67
|
+
const gl = makeFakeGl2();
|
|
68
|
+
const program = compileGlClassicProgram(gl, PHONG);
|
|
69
|
+
expect(program.locDiffuse).not.toBeNull();
|
|
70
|
+
expect(program.locSpecular).not.toBeNull();
|
|
71
|
+
expect(program.locShininess).not.toBeNull();
|
|
72
|
+
expect(program.locNormalMap).not.toBeNull();
|
|
73
|
+
expect(program.locNormalScale).not.toBeNull();
|
|
74
|
+
expect(program.locAlphaCutoff).not.toBeNull();
|
|
75
|
+
// Spread from resolveGlLitLocations.
|
|
76
|
+
expect(program.locDirectional).not.toBeNull();
|
|
77
|
+
expect(program.locCameraPosition).not.toBeNull();
|
|
78
|
+
expect(gl.calls.some((c) => c.name === 'linkProgram')).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('ensureGlClassicProgram', () => {
|
|
83
|
+
it('caches a variant under the classic namespace and reuses it', () => {
|
|
84
|
+
const { state, gl } = makeGlScene3DState();
|
|
85
|
+
const first = ensureGlClassicProgram(state, LAMBERT);
|
|
86
|
+
const links = gl.calls.filter((c) => c.name === 'linkProgram').length;
|
|
87
|
+
const second = ensureGlClassicProgram(state, LAMBERT);
|
|
88
|
+
expect(second).toBe(first);
|
|
89
|
+
expect(gl.calls.filter((c) => c.name === 'linkProgram').length).toBe(links);
|
|
90
|
+
expect([...getGlScene3DRuntime(state).programCache.keys()].some((k) => k.startsWith('classic:'))).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('caches a distinct entry per lighting model', () => {
|
|
94
|
+
const { state } = makeGlScene3DState();
|
|
95
|
+
const lambert = ensureGlClassicProgram(state, LAMBERT);
|
|
96
|
+
const phong = ensureGlClassicProgram(state, PHONG);
|
|
97
|
+
const blinnPhong = ensureGlClassicProgram(state, BLINNPHONG);
|
|
98
|
+
expect(lambert).not.toBe(phong);
|
|
99
|
+
expect(phong).not.toBe(blinnPhong);
|
|
100
|
+
expect(lambert).not.toBe(blinnPhong);
|
|
101
|
+
expect(getGlScene3DRuntime(state).programCache.size).toBe(3);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('folds the render-state skinned-run flag into a distinct HAS_SKIN variant', () => {
|
|
105
|
+
const { state } = makeGlScene3DState();
|
|
106
|
+
const rigid = ensureGlClassicProgram(state, LAMBERT);
|
|
107
|
+
getGlScene3DRuntime(state).activeSkinnedRun = true;
|
|
108
|
+
const skinned = ensureGlClassicProgram(state, LAMBERT);
|
|
109
|
+
|
|
110
|
+
expect(skinned).not.toBe(rigid);
|
|
111
|
+
expect([...getGlScene3DRuntime(state).programCache.keys()]).toContain('classic:l------k');
|
|
112
|
+
expect(skinned.locJointTexture).not.toBeNull();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('getGlClassicFragmentSource', () => {
|
|
117
|
+
it('returns the fragment body without a version/define block', () => {
|
|
118
|
+
const source = getGlClassicFragmentSource();
|
|
119
|
+
expect(source).toContain('fragColor');
|
|
120
|
+
expect(source).not.toContain('#version 300 es');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('getGlClassicFragmentSourceForKey', () => {
|
|
125
|
+
it('includes the lighting-model and feature defines only when set', () => {
|
|
126
|
+
expect(getGlClassicFragmentSourceForKey(LAMBERT)).not.toContain('#define LIGHTING_PHONG');
|
|
127
|
+
expect(getGlClassicFragmentSourceForKey(LAMBERT)).not.toContain('#define LIGHTING_BLINNPHONG');
|
|
128
|
+
expect(getGlClassicFragmentSourceForKey(PHONG)).toContain('#define LIGHTING_PHONG');
|
|
129
|
+
expect(getGlClassicFragmentSourceForKey(BLINNPHONG)).toContain('#define LIGHTING_BLINNPHONG');
|
|
130
|
+
expect(getGlClassicFragmentSourceForKey({ ...PHONG, hasDiffuseMap: true })).toContain('#define HAS_DIFFUSE_MAP');
|
|
131
|
+
expect(getGlClassicFragmentSourceForKey({ ...PHONG, hasSpecularMap: true })).toContain('#define HAS_SPECULAR_MAP');
|
|
132
|
+
expect(getGlClassicFragmentSourceForKey({ ...PHONG, hasNormalMap: true })).toContain('#define HAS_NORMAL_MAP');
|
|
133
|
+
expect(getGlClassicFragmentSourceForKey({ ...PHONG, hasAlphaMap: true })).toContain('#define HAS_ALPHA_MAP');
|
|
134
|
+
expect(getGlClassicFragmentSourceForKey({ ...PHONG, alphaMaskEnabled: true })).toContain('#define ALPHA_MASK');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('gates the alpha-map define off the key and always carries the guarded sampler + coverage multiply', () => {
|
|
138
|
+
// The define is what varies by key; the #ifdef-guarded sampler and multiply are unconditional raw
|
|
139
|
+
// text (same as the normal/specular maps), compiled out unless HAS_ALPHA_MAP is defined.
|
|
140
|
+
expect(getGlClassicFragmentSourceForKey(BLINNPHONG)).not.toContain('#define HAS_ALPHA_MAP');
|
|
141
|
+
const withAlpha = getGlClassicFragmentSourceForKey({ ...BLINNPHONG, hasAlphaMap: true });
|
|
142
|
+
expect(withAlpha).toContain('#define HAS_ALPHA_MAP');
|
|
143
|
+
expect(withAlpha).toContain('uniform sampler2D u_alphaMap;');
|
|
144
|
+
expect(withAlpha).toContain('diffuse.a *= texture(u_alphaMap, v_uv0).g;');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('includes the standard light block uniforms', () => {
|
|
148
|
+
const source = getGlClassicFragmentSourceForKey(LAMBERT);
|
|
149
|
+
expect(source).toContain('u_directionalRadiance');
|
|
150
|
+
expect(source).toContain('u_ambientRadiance');
|
|
151
|
+
expect(source).toContain('u_cameraPosition');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('shadow-maps the directional term (and only the directional term)', () => {
|
|
155
|
+
const source = getGlClassicFragmentSourceForKey(LAMBERT);
|
|
156
|
+
// The shared light block already declares the shadow uniforms + sampleDirectionalShadow; the classic
|
|
157
|
+
// path multiplies its directional contribution by that factor, matching the PBR family.
|
|
158
|
+
expect(source).toContain('sampleDirectionalShadow(v_worldPosition)');
|
|
159
|
+
// Exactly one call — point/spot/ambient stay unshadowed, so the helper appears once in main().
|
|
160
|
+
expect(source.match(/sampleDirectionalShadow\(v_worldPosition\)/g)).toHaveLength(1);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('uses reflect() for Phong and the half vector for BlinnPhong', () => {
|
|
164
|
+
expect(getGlClassicFragmentSourceForKey(PHONG)).toContain('reflect(');
|
|
165
|
+
expect(getGlClassicFragmentSourceForKey(BLINNPHONG)).toContain('normalize(lightDir + viewDir)');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('emits the MAX_FORWARD_LIGHTS spec constant and loops the punctual light arrays', () => {
|
|
169
|
+
const source = getGlClassicFragmentSourceForKey(LAMBERT);
|
|
170
|
+
expect(source).toContain('#define MAX_FORWARD_LIGHTS');
|
|
171
|
+
expect(source).toContain('u_pointLights');
|
|
172
|
+
expect(source).toContain('u_spotLights');
|
|
173
|
+
expect(source).toContain('u_hemisphereLights');
|
|
174
|
+
// Every light type routes through the one shared classic BRDF.
|
|
175
|
+
expect(source).toContain('shadeClassicLight');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('getGlClassicVertexSource', () => {
|
|
180
|
+
it('returns the vertex body without a version/define block', () => {
|
|
181
|
+
const source = getGlClassicVertexSource();
|
|
182
|
+
expect(source).toContain('a_position');
|
|
183
|
+
expect(source).not.toContain('#version 300 es');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('getGlClassicVertexSourceForKey', () => {
|
|
188
|
+
it('passes world position, normal, tangent, and uv varyings', () => {
|
|
189
|
+
const source = getGlClassicVertexSourceForKey(LAMBERT);
|
|
190
|
+
expect(source).toContain('v_worldPosition');
|
|
191
|
+
expect(source).toContain('v_normal');
|
|
192
|
+
expect(source).toContain('v_tangent');
|
|
193
|
+
expect(source).toContain('v_uv0');
|
|
194
|
+
expect(source).toContain('#version 300 es');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('injects the skin declarations and HAS_SKIN define only for the skinned variant', () => {
|
|
198
|
+
// The #ifdef HAS_SKIN guard lives in the body unconditionally; only the #define and the injected
|
|
199
|
+
// attribute/uniform declarations are skin-specific.
|
|
200
|
+
const rigid = getGlClassicVertexSourceForKey(LAMBERT);
|
|
201
|
+
expect(rigid).not.toContain('#define HAS_SKIN');
|
|
202
|
+
expect(rigid).not.toContain('a_joints0');
|
|
203
|
+
expect(rigid).not.toContain('mat4 skinMatrix()');
|
|
204
|
+
|
|
205
|
+
const skinned = getGlClassicVertexSourceForKey({ ...LAMBERT, hasSkin: true });
|
|
206
|
+
expect(skinned).toContain('#define HAS_SKIN');
|
|
207
|
+
expect(skinned).not.toContain('#define MAX_JOINTS');
|
|
208
|
+
expect(skinned).toContain('sampler2D u_jointTexture');
|
|
209
|
+
expect(skinned).toContain('texelFetch');
|
|
210
|
+
expect(skinned).toContain('mat4 skinMatrix()');
|
|
211
|
+
expect(skinned).toContain('a_joints0');
|
|
212
|
+
expect(skinned).toContain('a_weights0');
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { GlDebugDefineKey } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
bindGlDebugNormalMap,
|
|
5
|
+
bindGlDebugRange,
|
|
6
|
+
buildGlDebugDefineKey,
|
|
7
|
+
compileGlDebugProgram,
|
|
8
|
+
ensureGlDebugProgram,
|
|
9
|
+
getGlDebugFragmentSourceForKey,
|
|
10
|
+
getGlDebugVertexSourceForKey,
|
|
11
|
+
} from './glDebugPrelude';
|
|
12
|
+
import { getGlScene3DRuntime } from './glScene3DRuntime';
|
|
13
|
+
import { makeFakeGl2, makeGlScene3DState } from './glScene3DTestHelper';
|
|
14
|
+
|
|
15
|
+
const DEPTH: GlDebugDefineKey = { hasNormalMap: false, mode: 'depth' };
|
|
16
|
+
const NORMAL: GlDebugDefineKey = { hasNormalMap: false, mode: 'normal' };
|
|
17
|
+
const NORMAL_MAP: GlDebugDefineKey = { hasNormalMap: true, mode: 'normal' };
|
|
18
|
+
|
|
19
|
+
describe('bindGlDebugNormalMap', () => {
|
|
20
|
+
it('uploads the normal scale and binds no texture when no map is present', () => {
|
|
21
|
+
const { state, gl } = makeGlScene3DState();
|
|
22
|
+
const program = compileGlDebugProgram(gl, NORMAL);
|
|
23
|
+
bindGlDebugNormalMap(state, program, null, 2);
|
|
24
|
+
expect(gl.calls.some((c) => c.name === 'uniform1f')).toBe(true);
|
|
25
|
+
expect(gl.calls.some((c) => c.name === 'bindTexture')).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('bindGlDebugRange', () => {
|
|
30
|
+
it('uploads the near and far linearization range', () => {
|
|
31
|
+
const { state, gl } = makeGlScene3DState();
|
|
32
|
+
const program = compileGlDebugProgram(gl, DEPTH);
|
|
33
|
+
bindGlDebugRange(state, program, 0.1, 100);
|
|
34
|
+
expect(gl.calls.filter((c) => c.name === 'uniform1f').length).toBeGreaterThanOrEqual(2);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('buildGlDebugDefineKey', () => {
|
|
39
|
+
it('produces distinct stable strings per mode and normal-map flag', () => {
|
|
40
|
+
expect(buildGlDebugDefineKey(DEPTH)).toBe('d-');
|
|
41
|
+
expect(buildGlDebugDefineKey(NORMAL)).toBe('n-');
|
|
42
|
+
expect(buildGlDebugDefineKey(NORMAL_MAP)).toBe('nm');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('compileGlDebugProgram', () => {
|
|
47
|
+
it('compiles, links, and resolves the debug uniforms including the normal matrix', () => {
|
|
48
|
+
const gl = makeFakeGl2();
|
|
49
|
+
const program = compileGlDebugProgram(gl, NORMAL);
|
|
50
|
+
expect(program.locNormalMatrix).not.toBeNull();
|
|
51
|
+
expect(program.locNormalScale).not.toBeNull();
|
|
52
|
+
expect(program.locViewProjection).not.toBeNull();
|
|
53
|
+
expect(gl.calls.some((c) => c.name === 'linkProgram')).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('ensureGlDebugProgram', () => {
|
|
58
|
+
it('caches variants under the debug namespace with distinct depth and normal entries', () => {
|
|
59
|
+
const { state, gl } = makeGlScene3DState();
|
|
60
|
+
const depthFirst = ensureGlDebugProgram(state, DEPTH);
|
|
61
|
+
const depthSecond = ensureGlDebugProgram(state, DEPTH);
|
|
62
|
+
expect(depthSecond).toBe(depthFirst);
|
|
63
|
+
|
|
64
|
+
const normalProgram = ensureGlDebugProgram(state, NORMAL);
|
|
65
|
+
expect(normalProgram).not.toBe(depthFirst);
|
|
66
|
+
|
|
67
|
+
const keys = [...getGlScene3DRuntime(state).programCache.keys()];
|
|
68
|
+
expect(keys.some((k) => k.startsWith('debug:'))).toBe(true);
|
|
69
|
+
expect(keys).toContain('debug:d-');
|
|
70
|
+
expect(keys).toContain('debug:n-');
|
|
71
|
+
// The two distinct variants compiled exactly once each.
|
|
72
|
+
expect(gl.calls.filter((c) => c.name === 'linkProgram').length).toBe(2);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('getGlDebugFragmentSourceForKey', () => {
|
|
77
|
+
it('gates the mode and normal-map branches by define', () => {
|
|
78
|
+
expect(getGlDebugFragmentSourceForKey(DEPTH)).toContain('#define DEPTH_MODE');
|
|
79
|
+
expect(getGlDebugFragmentSourceForKey(DEPTH)).not.toContain('#define NORMAL_MODE');
|
|
80
|
+
expect(getGlDebugFragmentSourceForKey(NORMAL)).toContain('#define NORMAL_MODE');
|
|
81
|
+
expect(getGlDebugFragmentSourceForKey(NORMAL)).not.toContain('#define HAS_NORMAL_MAP');
|
|
82
|
+
expect(getGlDebugFragmentSourceForKey(NORMAL_MAP)).toContain('#define HAS_NORMAL_MAP');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('getGlDebugVertexSourceForKey', () => {
|
|
87
|
+
it('carries the model, normal, and view-projection uniforms for both modes', () => {
|
|
88
|
+
const source = getGlDebugVertexSourceForKey(NORMAL);
|
|
89
|
+
expect(source).toContain('u_model');
|
|
90
|
+
expect(source).toContain('u_normalMatrix');
|
|
91
|
+
expect(source).toContain('u_viewProjection');
|
|
92
|
+
expect(getGlDebugVertexSourceForKey(DEPTH)).toContain('a_position');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { CubeTexture, Environment, ImageResource } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import { ensureGlEnvironmentSourceCube, getGlCubeFaceTarget, updateGlEnvironmentCubeFace } from './glEnvironmentCube';
|
|
4
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
5
|
+
|
|
6
|
+
// The GPU upload + sampling is validated by the functional `env-skybox` capture (jsdom has no real
|
|
7
|
+
// WebGL2 cubemap). These cover the CPU-side guards: the face-target arithmetic, the "no complete
|
|
8
|
+
// cube" sentinel path that callers depend on to no-op, and the data-only face upload path.
|
|
9
|
+
|
|
10
|
+
function dataFace(size: number): ImageResource {
|
|
11
|
+
return { source: null, data: new Uint8ClampedArray(size * size * 4), width: size, height: size } as ImageResource;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function dataOnlyEnvironment(size: number): Environment {
|
|
15
|
+
const face = dataFace(size);
|
|
16
|
+
const cube = {
|
|
17
|
+
colorSpace: 'srgb',
|
|
18
|
+
faces: [face, face, face, face, face, face],
|
|
19
|
+
sampler: {},
|
|
20
|
+
} as unknown as CubeTexture;
|
|
21
|
+
return { environment: cube, intensity: 1 } as Environment;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('ensureGlEnvironmentSourceCube', () => {
|
|
25
|
+
it('returns null when the environment has no source cube', () => {
|
|
26
|
+
const { state } = makeGlScene3DState();
|
|
27
|
+
const environment = { environment: null, intensity: 1 } as Environment;
|
|
28
|
+
expect(ensureGlEnvironmentSourceCube(state, environment)).toBe(null);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('uploads a data-only cube through the raw-pixel texImage2D overload', () => {
|
|
32
|
+
const { state, gl } = makeGlScene3DState();
|
|
33
|
+
const environment = dataOnlyEnvironment(4);
|
|
34
|
+
const texture = ensureGlEnvironmentSourceCube(state, environment);
|
|
35
|
+
expect(texture).not.toBe(null);
|
|
36
|
+
const uploads = gl.calls.filter((c) => c.name === 'texImage2D');
|
|
37
|
+
expect(uploads.length).toBe(6);
|
|
38
|
+
// The raw-pixel overload passes width/height/border and the data buffer (9 args), not an element (6 args).
|
|
39
|
+
for (const upload of uploads) {
|
|
40
|
+
expect(upload.args.length).toBe(9);
|
|
41
|
+
expect(upload.args[3]).toBe(4);
|
|
42
|
+
expect(upload.args[4]).toBe(4);
|
|
43
|
+
expect(upload.args[8]).toBeInstanceOf(Uint8ClampedArray);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('getGlCubeFaceTarget', () => {
|
|
49
|
+
it('maps face index onto TEXTURE_CUBE_MAP_POSITIVE_X + face', () => {
|
|
50
|
+
const gl = { TEXTURE_CUBE_MAP_POSITIVE_X: 0x8515 } as WebGL2RenderingContext;
|
|
51
|
+
expect(getGlCubeFaceTarget(gl, 0)).toBe(0x8515);
|
|
52
|
+
expect(getGlCubeFaceTarget(gl, 5)).toBe(0x851a);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('updateGlEnvironmentCubeFace', () => {
|
|
57
|
+
it('returns false when no source cube has been built yet', () => {
|
|
58
|
+
const { state, gl } = makeGlScene3DState();
|
|
59
|
+
expect(updateGlEnvironmentCubeFace(state, 2, dataFace(4))).toBe(false);
|
|
60
|
+
expect(gl.calls.some((c) => c.name === 'texImage2D')).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('restamps a single face of the built cube without rebuilding the other five', () => {
|
|
64
|
+
const { state, gl } = makeGlScene3DState();
|
|
65
|
+
ensureGlEnvironmentSourceCube(state, dataOnlyEnvironment(4));
|
|
66
|
+
const afterBuild = gl.calls.filter((c) => c.name === 'texImage2D').length;
|
|
67
|
+
expect(afterBuild).toBe(6);
|
|
68
|
+
expect(updateGlEnvironmentCubeFace(state, 2, dataFace(4))).toBe(true);
|
|
69
|
+
expect(gl.calls.filter((c) => c.name === 'texImage2D').length).toBe(afterBuild + 1);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Environment } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import { bakeGlEnvironmentIbl, destroyGlEnvironmentIblBakePrograms } from './glEnvironmentIblBake';
|
|
4
|
+
import { getGlScene3DRuntime } from './glScene3DRuntime';
|
|
5
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
6
|
+
|
|
7
|
+
// The GPU bake (irradiance / prefiltered specular / BRDF LUT) is validated by the functional `env-ibl`
|
|
8
|
+
// capture — software jsdom has no float-cube render path. This covers the guard: with no source cube
|
|
9
|
+
// the bake is a no-op and leaves runtime.ibl null, so the PBR ambient falls back to the flat term.
|
|
10
|
+
|
|
11
|
+
describe('bakeGlEnvironmentIbl', () => {
|
|
12
|
+
it('is a no-op leaving runtime.ibl null when the environment has no source cube', () => {
|
|
13
|
+
const { state } = makeGlScene3DState();
|
|
14
|
+
const environment = { environment: null, intensity: 1 } as Environment;
|
|
15
|
+
bakeGlEnvironmentIbl(state, environment);
|
|
16
|
+
expect(getGlScene3DRuntime(state).ibl).toBe(null);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('destroyGlEnvironmentIblBakePrograms', () => {
|
|
21
|
+
// The bake shader programs are created only along the float-cube render path, which jsdom cannot
|
|
22
|
+
// drive (see the note above); their teardown is exercised end-to-end by the functional capture.
|
|
23
|
+
// Here we cover the guard: with no bake having run for the state, teardown is a safe, repeatable
|
|
24
|
+
// no-op that issues no GL deletes.
|
|
25
|
+
it('is a safe no-op when no bake ran for the state', () => {
|
|
26
|
+
const { state, gl } = makeGlScene3DState();
|
|
27
|
+
destroyGlEnvironmentIblBakePrograms(state);
|
|
28
|
+
destroyGlEnvironmentIblBakePrograms(state);
|
|
29
|
+
expect(gl.calls.some((c) => c.name.startsWith('delete'))).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createCamera3D, createPerspectiveProjection } from '@flighthq/camera';
|
|
2
|
+
import type { Environment } from '@flighthq/types';
|
|
3
|
+
|
|
4
|
+
import { drawGlEnvironmentSkybox } from './glEnvironmentSkybox';
|
|
5
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
6
|
+
|
|
7
|
+
// The skybox draw itself is validated by the functional `env-skybox` capture. This covers the guard:
|
|
8
|
+
// with no complete source cube the pass is a no-op (it must not touch GL), so an app that always calls
|
|
9
|
+
// it before drawGlScene3D pays nothing until an environment is bound.
|
|
10
|
+
|
|
11
|
+
describe('drawGlEnvironmentSkybox', () => {
|
|
12
|
+
it('is a no-op when the environment has no source cube', () => {
|
|
13
|
+
const { state, gl } = makeGlScene3DState();
|
|
14
|
+
const environment = { environment: null, intensity: 1 } as Environment;
|
|
15
|
+
const camera = createCamera3D({
|
|
16
|
+
far: 100,
|
|
17
|
+
near: 0.1,
|
|
18
|
+
projection: createPerspectiveProjection({ aspect: 1, fovY: 1 }),
|
|
19
|
+
});
|
|
20
|
+
const before = gl.calls.length;
|
|
21
|
+
expect(() => drawGlEnvironmentSkybox(state, environment, camera, 1)).not.toThrow();
|
|
22
|
+
expect(gl.calls.length).toBe(before);
|
|
23
|
+
});
|
|
24
|
+
});
|