@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,92 @@
|
|
|
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, Scene3DLightBlock, Scene3DRenderProxy } from '@flighthq/types';
|
|
6
|
+
import { LambertMaterialKind } from '@flighthq/types';
|
|
7
|
+
|
|
8
|
+
import { getGlMeshMaterialRenderer } from './glMeshMaterialRegistry';
|
|
9
|
+
import { getGlScene3DRuntime } from './glScene3DRuntime';
|
|
10
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
11
|
+
import { lambertGlMeshMaterialRenderer, registerLambertGlMaterial } from './lambertGlMeshMaterialRenderer';
|
|
12
|
+
|
|
13
|
+
function makeCamera(): Camera3D {
|
|
14
|
+
return createCamera3D({ far: 100, near: 0.1, projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' } });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function makeLights(): Scene3DLightBlock {
|
|
18
|
+
// Directional { dir.xyz @0, _pad, radiance.rgb @4, _pad } + ambient { radiance.rgb @8 }.
|
|
19
|
+
const data = new Float32Array(12);
|
|
20
|
+
data[1] = -1;
|
|
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(),
|
|
35
|
+
subset: geometry.subsets[0],
|
|
36
|
+
worldMatrix: createMatrix4(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('lambertGlMeshMaterialRenderer', () => {
|
|
41
|
+
it('bind selects a program, sets depth/cull, and uploads light block + diffuse color', () => {
|
|
42
|
+
const { state, gl } = makeGlScene3DState();
|
|
43
|
+
lambertGlMeshMaterialRenderer.bind(state, createLambertMaterial(), makeLights(), makeCamera());
|
|
44
|
+
|
|
45
|
+
expect(gl.calls.some((c) => c.name === 'useProgram')).toBe(true);
|
|
46
|
+
expect(gl.calls.some((c) => c.name === 'enable' && c.args[0] === gl.DEPTH_TEST)).toBe(true);
|
|
47
|
+
expect(gl.calls.some((c) => c.name === 'enable' && c.args[0] === gl.CULL_FACE)).toBe(true);
|
|
48
|
+
expect(gl.calls.some((c) => c.name === 'uniformMatrix4fv')).toBe(true);
|
|
49
|
+
// The light block uploads two vec4 (directional + radiance) and the diffuse color adds a third.
|
|
50
|
+
expect(gl.calls.filter((c) => c.name === 'uniform4f').length).toBeGreaterThanOrEqual(3);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('bind caches the program under the classic namespace with a lambert key', () => {
|
|
54
|
+
const { state } = makeGlScene3DState();
|
|
55
|
+
lambertGlMeshMaterialRenderer.bind(state, createLambertMaterial(), makeLights(), makeCamera());
|
|
56
|
+
const keys = [...getGlScene3DRuntime(state).programCache.keys()];
|
|
57
|
+
expect(keys.some((k) => k.startsWith('classic:l'))).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('bind disables back-face culling for a double-sided material', () => {
|
|
61
|
+
const { state, gl } = makeGlScene3DState();
|
|
62
|
+
const material = createLambertMaterial();
|
|
63
|
+
material.doubleSided = true;
|
|
64
|
+
lambertGlMeshMaterialRenderer.bind(state, material, makeLights(), makeCamera());
|
|
65
|
+
expect(gl.calls.some((c) => c.name === 'disable' && c.args[0] === gl.CULL_FACE)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('draw uploads geometry and issues an indexed draw over the subset range', () => {
|
|
69
|
+
const { state, gl } = makeGlScene3DState();
|
|
70
|
+
const proxy = makeProxy();
|
|
71
|
+
lambertGlMeshMaterialRenderer.bind(state, proxy.material, makeLights(), makeCamera());
|
|
72
|
+
lambertGlMeshMaterialRenderer.draw(state, proxy, createBoxMeshGeometry());
|
|
73
|
+
|
|
74
|
+
const drawCall = gl.calls.find((c) => c.name === 'drawElements');
|
|
75
|
+
expect(drawCall).toBeDefined();
|
|
76
|
+
expect(drawCall!.args[1]).toBe(proxy.subset.indexCount);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('draw is a no-op when bind has not selected a program', () => {
|
|
80
|
+
const { state, gl } = makeGlScene3DState();
|
|
81
|
+
lambertGlMeshMaterialRenderer.draw(state, makeProxy(), createBoxMeshGeometry());
|
|
82
|
+
expect(gl.calls.some((c) => c.name === 'drawElements')).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('registerLambertGlMaterial', () => {
|
|
87
|
+
it('installs the renderer for LambertMaterialKind', () => {
|
|
88
|
+
const { state } = makeGlScene3DState();
|
|
89
|
+
registerLambertGlMaterial(state);
|
|
90
|
+
expect(getGlMeshMaterialRenderer(state, LambertMaterialKind)).toBe(lambertGlMeshMaterialRenderer);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
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 { getGlMeshMaterialRenderer } from './glMeshMaterialRegistry';
|
|
9
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
10
|
+
import { matcapGlMeshMaterialRenderer, registerMatcapGlMaterial } from './matcapGlMeshMaterialRenderer';
|
|
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('matcapGlMeshMaterialRenderer', () => {
|
|
37
|
+
it('bind selects a program, sets depth/cull, view-projection, u_view, and the tint uniform', () => {
|
|
38
|
+
const { state, gl } = makeGlScene3DState();
|
|
39
|
+
matcapGlMeshMaterialRenderer.bind(state, createMatcapMaterial(), NO_LIGHTS, makeCamera());
|
|
40
|
+
expect(gl.calls.some((c) => c.name === 'useProgram')).toBe(true);
|
|
41
|
+
expect(gl.calls.some((c) => c.name === 'enable' && c.args[0] === gl.DEPTH_TEST)).toBe(true);
|
|
42
|
+
// The view-projection and u_view are both mat4 uploads.
|
|
43
|
+
expect(gl.calls.filter((c) => c.name === 'uniformMatrix4fv').length).toBeGreaterThanOrEqual(2);
|
|
44
|
+
expect(gl.calls.some((c) => c.name === 'uniform4f')).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('draw issues an indexed draw over the subset after bind', () => {
|
|
48
|
+
const { state, gl } = makeGlScene3DState();
|
|
49
|
+
const proxy = makeProxy();
|
|
50
|
+
matcapGlMeshMaterialRenderer.bind(state, proxy.material, NO_LIGHTS, makeCamera());
|
|
51
|
+
matcapGlMeshMaterialRenderer.draw(state, proxy, createBoxMeshGeometry());
|
|
52
|
+
const draw = gl.calls.find((c) => c.name === 'drawElements');
|
|
53
|
+
expect(draw).toBeDefined();
|
|
54
|
+
expect(draw!.args[1]).toBe(proxy.subset.indexCount);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('draw is a no-op when bind has not selected a program', () => {
|
|
58
|
+
const { state, gl } = makeGlScene3DState();
|
|
59
|
+
matcapGlMeshMaterialRenderer.draw(state, makeProxy(), createBoxMeshGeometry());
|
|
60
|
+
expect(gl.calls.some((c) => c.name === 'drawElements')).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('registerMatcapGlMaterial', () => {
|
|
65
|
+
it('installs the renderer for MatcapMaterialKind', () => {
|
|
66
|
+
const { state } = makeGlScene3DState();
|
|
67
|
+
registerMatcapGlMaterial(state);
|
|
68
|
+
expect(getGlMeshMaterialRenderer(state, MatcapMaterialKind)).toBe(matcapGlMeshMaterialRenderer);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createCamera3D } from '@flighthq/camera';
|
|
2
|
+
import { createMatrix3, createMatrix4 } from '@flighthq/geometry';
|
|
3
|
+
import { createNormalMaterial } from '@flighthq/materials';
|
|
4
|
+
import { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
5
|
+
import type { Camera3D, Scene3DLightBlock, Scene3DRenderProxy } from '@flighthq/types';
|
|
6
|
+
import { NormalMaterialKind } from '@flighthq/types';
|
|
7
|
+
|
|
8
|
+
import { getGlMeshMaterialRenderer } from './glMeshMaterialRegistry';
|
|
9
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
10
|
+
import { normalGlMeshMaterialRenderer, registerNormalGlMaterial } from './normalGlMeshMaterialRenderer';
|
|
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: createNormalMaterial(),
|
|
30
|
+
normalMatrix: createMatrix3(),
|
|
31
|
+
subset: geometry.subsets[0],
|
|
32
|
+
worldMatrix: createMatrix4(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe('normalGlMeshMaterialRenderer', () => {
|
|
37
|
+
it('bind selects a program, sets depth/cull, view-projection, and the normal scale uniform', () => {
|
|
38
|
+
const { state, gl } = makeGlScene3DState();
|
|
39
|
+
normalGlMeshMaterialRenderer.bind(state, createNormalMaterial(), NO_LIGHTS, makeCamera());
|
|
40
|
+
expect(gl.calls.some((c) => c.name === 'useProgram')).toBe(true);
|
|
41
|
+
expect(gl.calls.some((c) => c.name === 'enable' && c.args[0] === gl.DEPTH_TEST)).toBe(true);
|
|
42
|
+
expect(gl.calls.some((c) => c.name === 'uniformMatrix4fv')).toBe(true);
|
|
43
|
+
expect(gl.calls.some((c) => c.name === 'uniform1f')).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('draw issues an indexed draw over the subset after bind', () => {
|
|
47
|
+
const { state, gl } = makeGlScene3DState();
|
|
48
|
+
const proxy = makeProxy();
|
|
49
|
+
normalGlMeshMaterialRenderer.bind(state, proxy.material, NO_LIGHTS, makeCamera());
|
|
50
|
+
normalGlMeshMaterialRenderer.draw(state, proxy, createBoxMeshGeometry());
|
|
51
|
+
const draw = gl.calls.find((c) => c.name === 'drawElements');
|
|
52
|
+
expect(draw).toBeDefined();
|
|
53
|
+
expect(draw!.args[1]).toBe(proxy.subset.indexCount);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('draw is a no-op when bind has not selected a program', () => {
|
|
57
|
+
const { state, gl } = makeGlScene3DState();
|
|
58
|
+
normalGlMeshMaterialRenderer.draw(state, makeProxy(), createBoxMeshGeometry());
|
|
59
|
+
expect(gl.calls.some((c) => c.name === 'drawElements')).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('registerNormalGlMaterial', () => {
|
|
64
|
+
it('installs the renderer for NormalMaterialKind', () => {
|
|
65
|
+
const { state } = makeGlScene3DState();
|
|
66
|
+
registerNormalGlMaterial(state);
|
|
67
|
+
expect(getGlMeshMaterialRenderer(state, NormalMaterialKind)).toBe(normalGlMeshMaterialRenderer);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createCamera3D } from '@flighthq/camera';
|
|
2
|
+
import { createMatrix3, createMatrix4 } from '@flighthq/geometry';
|
|
3
|
+
import { createPhongMaterial } from '@flighthq/materials';
|
|
4
|
+
import { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
5
|
+
import type { Camera3D, Scene3DLightBlock, Scene3DRenderProxy } from '@flighthq/types';
|
|
6
|
+
import { PhongMaterialKind } from '@flighthq/types';
|
|
7
|
+
|
|
8
|
+
import { getGlMeshMaterialRenderer } from './glMeshMaterialRegistry';
|
|
9
|
+
import { getGlScene3DRuntime } from './glScene3DRuntime';
|
|
10
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
11
|
+
import { phongGlMeshMaterialRenderer, registerPhongGlMaterial } from './phongGlMeshMaterialRenderer';
|
|
12
|
+
|
|
13
|
+
function makeCamera(): Camera3D {
|
|
14
|
+
return createCamera3D({ far: 100, near: 0.1, projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' } });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function makeLights(): Scene3DLightBlock {
|
|
18
|
+
// Directional { dir.xyz @0, _pad, radiance.rgb @4, _pad } + ambient { radiance.rgb @8 }.
|
|
19
|
+
const data = new Float32Array(12);
|
|
20
|
+
data[1] = -1;
|
|
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: createPhongMaterial(),
|
|
34
|
+
normalMatrix: createMatrix3(),
|
|
35
|
+
subset: geometry.subsets[0],
|
|
36
|
+
worldMatrix: createMatrix4(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('phongGlMeshMaterialRenderer', () => {
|
|
41
|
+
it('bind selects a program, uploads camera position + light block + diffuse/specular colors', () => {
|
|
42
|
+
const { state, gl } = makeGlScene3DState();
|
|
43
|
+
phongGlMeshMaterialRenderer.bind(state, createPhongMaterial(), makeLights(), makeCamera());
|
|
44
|
+
|
|
45
|
+
expect(gl.calls.some((c) => c.name === 'useProgram')).toBe(true);
|
|
46
|
+
expect(gl.calls.some((c) => c.name === 'enable' && c.args[0] === gl.DEPTH_TEST)).toBe(true);
|
|
47
|
+
expect(gl.calls.some((c) => c.name === 'uniformMatrix4fv')).toBe(true);
|
|
48
|
+
// Light block (2 vec4) + diffuse + specular colors → at least 4 uniform4f.
|
|
49
|
+
expect(gl.calls.filter((c) => c.name === 'uniform4f').length).toBeGreaterThanOrEqual(4);
|
|
50
|
+
// Camera3D position is a vec3; the ambient radiance is too — at least 2 uniform3f.
|
|
51
|
+
expect(gl.calls.filter((c) => c.name === 'uniform3f').length).toBeGreaterThanOrEqual(2);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('bind caches the program under the classic namespace with a phong key', () => {
|
|
55
|
+
const { state } = makeGlScene3DState();
|
|
56
|
+
phongGlMeshMaterialRenderer.bind(state, createPhongMaterial(), makeLights(), makeCamera());
|
|
57
|
+
const keys = [...getGlScene3DRuntime(state).programCache.keys()];
|
|
58
|
+
expect(keys.some((k) => k.startsWith('classic:p'))).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('bind disables back-face culling for a double-sided material', () => {
|
|
62
|
+
const { state, gl } = makeGlScene3DState();
|
|
63
|
+
const material = createPhongMaterial();
|
|
64
|
+
material.doubleSided = true;
|
|
65
|
+
phongGlMeshMaterialRenderer.bind(state, material, makeLights(), makeCamera());
|
|
66
|
+
expect(gl.calls.some((c) => c.name === 'disable' && c.args[0] === gl.CULL_FACE)).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('draw uploads geometry and issues an indexed draw over the subset range', () => {
|
|
70
|
+
const { state, gl } = makeGlScene3DState();
|
|
71
|
+
const proxy = makeProxy();
|
|
72
|
+
phongGlMeshMaterialRenderer.bind(state, proxy.material, makeLights(), makeCamera());
|
|
73
|
+
phongGlMeshMaterialRenderer.draw(state, proxy, createBoxMeshGeometry());
|
|
74
|
+
|
|
75
|
+
const drawCall = gl.calls.find((c) => c.name === 'drawElements');
|
|
76
|
+
expect(drawCall).toBeDefined();
|
|
77
|
+
expect(drawCall!.args[1]).toBe(proxy.subset.indexCount);
|
|
78
|
+
expect(gl.calls.some((c) => c.name === 'uniformMatrix3fv')).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('draw is a no-op when bind has not selected a program', () => {
|
|
82
|
+
const { state, gl } = makeGlScene3DState();
|
|
83
|
+
phongGlMeshMaterialRenderer.draw(state, makeProxy(), createBoxMeshGeometry());
|
|
84
|
+
expect(gl.calls.some((c) => c.name === 'drawElements')).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('registerPhongGlMaterial', () => {
|
|
89
|
+
it('installs the renderer for PhongMaterialKind', () => {
|
|
90
|
+
const { state } = makeGlScene3DState();
|
|
91
|
+
registerPhongGlMaterial(state);
|
|
92
|
+
expect(getGlMeshMaterialRenderer(state, PhongMaterialKind)).toBe(phongGlMeshMaterialRenderer);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { createCamera3D, setCamera3DViewMatrix4FromLookAt } from '@flighthq/camera';
|
|
2
|
+
import { createPointLight, createSpotLight } from '@flighthq/lighting';
|
|
3
|
+
import { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
4
|
+
import { addNodeChild } from '@flighthq/node';
|
|
5
|
+
import { prepareScene3DRender } from '@flighthq/render';
|
|
6
|
+
import { createMesh, createNode3D, Node3DKind } from '@flighthq/scene3d';
|
|
7
|
+
import type { Camera3D, Scene3DLightsLike } from '@flighthq/types';
|
|
8
|
+
import { SCENE_LIGHT_POINT_OFFSET } from '@flighthq/types';
|
|
9
|
+
|
|
10
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
11
|
+
import { prepareGlScene3DForwardLights } from './prepareGlScene3DForwardLights';
|
|
12
|
+
|
|
13
|
+
function camera(): Camera3D {
|
|
14
|
+
const result = createCamera3D({
|
|
15
|
+
far: 100,
|
|
16
|
+
near: 0.1,
|
|
17
|
+
projection: { aspect: 1, fovY: Math.PI / 2, kind: 'perspective' },
|
|
18
|
+
});
|
|
19
|
+
setCamera3DViewMatrix4FromLookAt(result, { x: 4, y: 0, z: 20 }, { x: 4, y: 0, z: 0 }, { x: 0, y: 1, z: 0 });
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function clusteredLights(): Scene3DLightsLike {
|
|
24
|
+
return {
|
|
25
|
+
ambient: null,
|
|
26
|
+
directional: null,
|
|
27
|
+
point: [
|
|
28
|
+
createPointLight({ position: { x: 8, y: 0, z: 0 }, range: -1 }),
|
|
29
|
+
createPointLight({ position: { x: 8, y: 1, z: 0 }, range: -1 }),
|
|
30
|
+
createPointLight({ position: { x: 8, y: -1, z: 0 }, range: -1 }),
|
|
31
|
+
createPointLight({ position: { x: 8, y: 0, z: 1 }, range: -1 }),
|
|
32
|
+
createPointLight({ position: { x: 0, y: 0, z: 0 }, range: -1 }),
|
|
33
|
+
createPointLight({ position: { x: 0, y: 1, z: 0 }, range: -1 }),
|
|
34
|
+
createPointLight({ position: { x: 0, y: -1, z: 0 }, range: -1 }),
|
|
35
|
+
createPointLight({ position: { x: 0, y: 0, z: 1 }, range: -1 }),
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('prepareGlScene3DForwardLights', () => {
|
|
41
|
+
it('packs four selected points and four selected spots from independent family budgets', () => {
|
|
42
|
+
const { state } = makeGlScene3DState();
|
|
43
|
+
const scene = createNode3D(Node3DKind);
|
|
44
|
+
addNodeChild(scene, createMesh(createBoxMeshGeometry(), []));
|
|
45
|
+
const lights: Scene3DLightsLike = {
|
|
46
|
+
ambient: null,
|
|
47
|
+
directional: null,
|
|
48
|
+
point: Array.from({ length: 6 }, (_, index) =>
|
|
49
|
+
createPointLight({ position: { x: index + 1, y: 0, z: 0 }, range: -1 }),
|
|
50
|
+
),
|
|
51
|
+
spot: Array.from({ length: 6 }, (_, index) =>
|
|
52
|
+
createSpotLight({
|
|
53
|
+
direction: { x: -1, y: 0, z: 0 },
|
|
54
|
+
innerConeDegrees: 45,
|
|
55
|
+
outerConeDegrees: 60,
|
|
56
|
+
position: { x: index + 1, y: 0, z: 0 },
|
|
57
|
+
range: -1,
|
|
58
|
+
}),
|
|
59
|
+
),
|
|
60
|
+
};
|
|
61
|
+
const renderList = prepareScene3DRender(state, scene, camera(), lights);
|
|
62
|
+
const block = prepareGlScene3DForwardLights(state, renderList, lights).meshLightBlocks[0];
|
|
63
|
+
expect(block.pointCount).toBe(4);
|
|
64
|
+
expect(block.spotCount).toBe(4);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('selects punctual lights per visible mesh and deduplicates identical tuples', () => {
|
|
68
|
+
const { state } = makeGlScene3DState();
|
|
69
|
+
const scene = createNode3D(Node3DKind);
|
|
70
|
+
const nearA = createMesh(createBoxMeshGeometry(), []);
|
|
71
|
+
const nearB = createMesh(createBoxMeshGeometry(), []);
|
|
72
|
+
nearB.position.x = 0.1;
|
|
73
|
+
const far = createMesh(createBoxMeshGeometry(), []);
|
|
74
|
+
far.position.x = 8;
|
|
75
|
+
addNodeChild(scene, nearA);
|
|
76
|
+
addNodeChild(scene, nearB);
|
|
77
|
+
addNodeChild(scene, far);
|
|
78
|
+
const lights = clusteredLights();
|
|
79
|
+
|
|
80
|
+
const renderList = prepareScene3DRender(state, scene, camera(), lights);
|
|
81
|
+
const selected = prepareGlScene3DForwardLights(state, renderList, lights);
|
|
82
|
+
|
|
83
|
+
expect(selected.meshCount).toBe(3);
|
|
84
|
+
expect(selected.meshLightBlocks[0]).toBe(selected.meshLightBlocks[1]);
|
|
85
|
+
expect(selected.meshLightBlocks[0]).not.toBe(selected.meshLightBlocks[2]);
|
|
86
|
+
expect(selected.meshLightBlocks[0].pointCount).toBe(4);
|
|
87
|
+
expect(selected.meshLightBlocks[0].data[SCENE_LIGHT_POINT_OFFSET]).toBe(0);
|
|
88
|
+
expect(selected.meshLightBlocks[2].data[SCENE_LIGHT_POINT_OFFSET]).toBe(8);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('reuses its list, mesh array, and packed blocks across frames', () => {
|
|
92
|
+
const { state } = makeGlScene3DState();
|
|
93
|
+
const scene = createNode3D(Node3DKind);
|
|
94
|
+
addNodeChild(scene, createMesh(createBoxMeshGeometry(), []));
|
|
95
|
+
const lights = clusteredLights();
|
|
96
|
+
const renderList = prepareScene3DRender(state, scene, camera(), lights);
|
|
97
|
+
const first = prepareGlScene3DForwardLights(state, renderList, lights);
|
|
98
|
+
const meshBlocks = first.meshLightBlocks;
|
|
99
|
+
const block = first.meshLightBlocks[0];
|
|
100
|
+
|
|
101
|
+
const second = prepareGlScene3DForwardLights(state, renderList, lights);
|
|
102
|
+
expect(second).toBe(first);
|
|
103
|
+
expect(second.meshLightBlocks).toBe(meshBlocks);
|
|
104
|
+
expect(second.meshLightBlocks[0]).toBe(block);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
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 { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
6
|
+
import { addNodeChild } from '@flighthq/node';
|
|
7
|
+
import { createMesh, createNode3D, Node3DKind } from '@flighthq/scene3d';
|
|
8
|
+
import type { Camera3D, GlRenderTarget, Scene3DLightsLike } from '@flighthq/types';
|
|
9
|
+
|
|
10
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
11
|
+
import { presentGlScene3D } from './presentGlScene3D';
|
|
12
|
+
import { registerStandardPbrGlMaterial } from './registerStandardPbrGlMaterial';
|
|
13
|
+
|
|
14
|
+
function makeCamera(): Camera3D {
|
|
15
|
+
const camera = createCamera3D({
|
|
16
|
+
far: 100,
|
|
17
|
+
near: 0.1,
|
|
18
|
+
projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' },
|
|
19
|
+
});
|
|
20
|
+
setCamera3DViewMatrix4FromLookAt(camera, { x: 0, y: 0, z: 5 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 1, z: 0 });
|
|
21
|
+
return camera;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function makeTarget(): GlRenderTarget {
|
|
25
|
+
return {
|
|
26
|
+
width: 256,
|
|
27
|
+
height: 256,
|
|
28
|
+
format: 'rgba16f',
|
|
29
|
+
colorSpace: 'linear',
|
|
30
|
+
clearColors: [],
|
|
31
|
+
clearDepth: 1,
|
|
32
|
+
sampleCount: 1,
|
|
33
|
+
framebuffer: { id: 'sceneFb' } as unknown as WebGLFramebuffer,
|
|
34
|
+
resolveFramebuffer: null,
|
|
35
|
+
textures: [{ id: 'sceneTex' } as unknown as WebGLTexture],
|
|
36
|
+
texture: { id: 'sceneTex' } as unknown as WebGLTexture,
|
|
37
|
+
depthTexture: null,
|
|
38
|
+
colorRenderbuffers: [],
|
|
39
|
+
depthStencilRenderbuffer: { id: 'sceneDepth' } as unknown as WebGLRenderbuffer,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const LIGHTS: Scene3DLightsLike = {
|
|
44
|
+
ambient: createAmbientLight({ color: 0xffffffff, intensity: 0.2 }),
|
|
45
|
+
directional: createDirectionalLight({ color: 0xffffffff, direction: createVector3(0, -1, -1), intensity: 1 }),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
describe('presentGlScene3D', () => {
|
|
49
|
+
it('renders into the target then presents to the canvas (default framebuffer)', () => {
|
|
50
|
+
const { state, gl } = makeGlScene3DState();
|
|
51
|
+
registerStandardPbrGlMaterial(state);
|
|
52
|
+
const scene = createNode3D(Node3DKind);
|
|
53
|
+
addNodeChild(scene, createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]));
|
|
54
|
+
const target = makeTarget();
|
|
55
|
+
|
|
56
|
+
presentGlScene3D(state, target, scene, makeCamera(), LIGHTS);
|
|
57
|
+
|
|
58
|
+
const framebufferBinds = gl.calls.filter((c) => c.name === 'bindFramebuffer');
|
|
59
|
+
// The scene is drawn into the target's framebuffer...
|
|
60
|
+
expect(framebufferBinds.some((c) => c.args[1] === target.framebuffer)).toBe(true);
|
|
61
|
+
// ...and the linear->sRGB present writes to the canvas (default framebuffer = null).
|
|
62
|
+
expect(framebufferBinds.some((c) => c.args[1] === null)).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('clears the background color and the depth buffer before drawing', () => {
|
|
66
|
+
const { state, gl } = makeGlScene3DState();
|
|
67
|
+
registerStandardPbrGlMaterial(state);
|
|
68
|
+
const scene = createNode3D(Node3DKind);
|
|
69
|
+
addNodeChild(scene, createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]));
|
|
70
|
+
|
|
71
|
+
presentGlScene3D(state, makeTarget(), scene, makeCamera(), LIGHTS);
|
|
72
|
+
|
|
73
|
+
// beginGlRenderPass clears per-attachment (clearBufferfv for color location 0) and the depth/stencil
|
|
74
|
+
// buffer (clearBufferfi), the WebGL2 path that lets each MRT attachment carry its own clear value.
|
|
75
|
+
const colorClears = gl.calls.filter((c) => c.name === 'clearBufferfv' && c.args[0] === gl.COLOR);
|
|
76
|
+
expect(colorClears.some((c) => c.args[1] === 0)).toBe(true);
|
|
77
|
+
const depthClears = gl.calls.filter((c) => c.name === 'clearBufferfi' && c.args[0] === gl.DEPTH_STENCIL);
|
|
78
|
+
expect(depthClears.length).toBeGreaterThan(0);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('presents to the canvas after the scene draw, so the encode reads the rendered target', () => {
|
|
82
|
+
const { state, gl } = makeGlScene3DState();
|
|
83
|
+
registerStandardPbrGlMaterial(state);
|
|
84
|
+
const scene = createNode3D(Node3DKind);
|
|
85
|
+
addNodeChild(scene, createMesh(createBoxMeshGeometry(), [createStandardPbrMaterial()]));
|
|
86
|
+
const target = makeTarget();
|
|
87
|
+
|
|
88
|
+
presentGlScene3D(state, target, scene, makeCamera(), LIGHTS);
|
|
89
|
+
|
|
90
|
+
const names = gl.calls.map((c) => c.name);
|
|
91
|
+
const lastTargetBind = names.lastIndexOf('bindTexture');
|
|
92
|
+
const lastDraw = names.lastIndexOf('drawElements');
|
|
93
|
+
// The present pass binds the scene texture and draws the fullscreen quad as the final operations.
|
|
94
|
+
expect(lastTargetBind).toBeGreaterThan(0);
|
|
95
|
+
expect(lastDraw).toBeGreaterThan(0);
|
|
96
|
+
expect(gl.calls.some((c) => c.name === 'bindTexture' && c.args[1] === target.texture)).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { StandardPbrMaterialKind } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import { getGlMeshMaterialRenderer } from './glMeshMaterialRegistry';
|
|
4
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
5
|
+
import { registerStandardPbrGlMaterial } from './registerStandardPbrGlMaterial';
|
|
6
|
+
import { standardPbrGlMeshMaterialRenderer } from './standardPbrGlMeshMaterialRenderer';
|
|
7
|
+
|
|
8
|
+
describe('registerStandardPbrGlMaterial', () => {
|
|
9
|
+
it('registers the StandardPbr renderer for StandardPbrMaterialKind', () => {
|
|
10
|
+
const { state } = makeGlScene3DState();
|
|
11
|
+
registerStandardPbrGlMaterial(state);
|
|
12
|
+
expect(getGlMeshMaterialRenderer(state, StandardPbrMaterialKind)).toBe(standardPbrGlMeshMaterialRenderer);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { createCamera3D } from '@flighthq/camera';
|
|
2
|
+
import { createMatrix3, createMatrix4 } from '@flighthq/geometry';
|
|
3
|
+
import { createBoxMeshGeometry } from '@flighthq/mesh';
|
|
4
|
+
import { createEmissiveModifier, createRimModifier, createShadedMaterial } from '@flighthq/shading';
|
|
5
|
+
import type { Camera3D, Scene3DLightBlock, Scene3DRenderProxy } from '@flighthq/types';
|
|
6
|
+
import { ShadedMaterialKind } from '@flighthq/types';
|
|
7
|
+
|
|
8
|
+
import { getGlMeshMaterialRenderer } from './glMeshMaterialRegistry';
|
|
9
|
+
import { getGlScene3DRuntime } from './glScene3DRuntime';
|
|
10
|
+
import { makeGlScene3DState } from './glScene3DTestHelper';
|
|
11
|
+
import { setGlScene3DTime } from './glScene3DTime';
|
|
12
|
+
import { registerBuiltInGlModifierSnippets } from './glShadedBuiltInModifiers';
|
|
13
|
+
import { registerShadedGlMaterial, shadedGlMeshMaterialRenderer } from './shadedGlMeshMaterialRenderer';
|
|
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: createShadedMaterial(),
|
|
35
|
+
normalMatrix: createMatrix3(),
|
|
36
|
+
subset: geometry.subsets[0],
|
|
37
|
+
worldMatrix: createMatrix4(),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('registerShadedGlMaterial', () => {
|
|
42
|
+
it('installs the renderer for ShadedMaterialKind', () => {
|
|
43
|
+
const { state } = makeGlScene3DState();
|
|
44
|
+
registerShadedGlMaterial(state);
|
|
45
|
+
expect(getGlMeshMaterialRenderer(state, ShadedMaterialKind)).toBe(shadedGlMeshMaterialRenderer);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('shadedGlMeshMaterialRenderer', () => {
|
|
50
|
+
it('bind selects a program and uploads camera + light block + base colors + time', () => {
|
|
51
|
+
const { state, gl } = makeGlScene3DState();
|
|
52
|
+
shadedGlMeshMaterialRenderer.bind(state, createShadedMaterial(), makeLights(), makeCamera());
|
|
53
|
+
|
|
54
|
+
expect(gl.calls.some((c) => c.name === 'useProgram')).toBe(true);
|
|
55
|
+
expect(gl.calls.some((c) => c.name === 'enable' && c.args[0] === gl.DEPTH_TEST)).toBe(true);
|
|
56
|
+
// Light block (2 vec4) + diffuse + specular colors → at least 4 uniform4f.
|
|
57
|
+
expect(gl.calls.filter((c) => c.name === 'uniform4f').length).toBeGreaterThanOrEqual(4);
|
|
58
|
+
// u_time is a uniform1f uploaded every bind.
|
|
59
|
+
expect(gl.calls.some((c) => c.name === 'uniform1f')).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('bind caches the program under the shaded: namespace', () => {
|
|
63
|
+
const { state } = makeGlScene3DState();
|
|
64
|
+
shadedGlMeshMaterialRenderer.bind(state, createShadedMaterial(), makeLights(), makeCamera());
|
|
65
|
+
const keys = [...getGlScene3DRuntime(state).programCache.keys()];
|
|
66
|
+
expect(keys.some((k) => k.startsWith('shaded:'))).toBe(true);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('a plain ShadedMaterial (empty stack) compiles the lean base variant', () => {
|
|
70
|
+
const { state } = makeGlScene3DState();
|
|
71
|
+
shadedGlMeshMaterialRenderer.bind(state, createShadedMaterial(), makeLights(), makeCamera());
|
|
72
|
+
const keys = [...getGlScene3DRuntime(state).programCache.keys()];
|
|
73
|
+
// Empty modifier feature-set → the define-key trails with an empty modifier segment. The last two
|
|
74
|
+
// base flags are the (unset) uv-transform and skin flags.
|
|
75
|
+
expect(keys).toContain('shaded:------|');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('binds modifier uniforms for a material carrying a modifier stack', () => {
|
|
79
|
+
const { state, gl } = makeGlScene3DState();
|
|
80
|
+
registerBuiltInGlModifierSnippets(state);
|
|
81
|
+
setGlScene3DTime(state, 1.5);
|
|
82
|
+
const material = createShadedMaterial({
|
|
83
|
+
modifiers: [createEmissiveModifier({ color: 0xffcc88ff }), createRimModifier({ color: 0x88ccffff })],
|
|
84
|
+
});
|
|
85
|
+
shadedGlMeshMaterialRenderer.bind(state, material, makeLights(), makeCamera());
|
|
86
|
+
// Emissive color + rim color each upload a uniform3f (vec3), on top of camera/ambient vec3s.
|
|
87
|
+
expect(gl.calls.filter((c) => c.name === 'uniform3f').length).toBeGreaterThanOrEqual(4);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('draw uploads geometry and issues an indexed draw over the subset range', () => {
|
|
91
|
+
const { state, gl } = makeGlScene3DState();
|
|
92
|
+
const proxy = makeProxy();
|
|
93
|
+
shadedGlMeshMaterialRenderer.bind(state, proxy.material, makeLights(), makeCamera());
|
|
94
|
+
shadedGlMeshMaterialRenderer.draw(state, proxy, createBoxMeshGeometry());
|
|
95
|
+
|
|
96
|
+
const drawCall = gl.calls.find((c) => c.name === 'drawElements');
|
|
97
|
+
expect(drawCall).toBeDefined();
|
|
98
|
+
expect(drawCall!.args[1]).toBe(proxy.subset.indexCount);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('draw is a no-op when bind has not selected a program', () => {
|
|
102
|
+
const { state, gl } = makeGlScene3DState();
|
|
103
|
+
shadedGlMeshMaterialRenderer.draw(state, makeProxy(), createBoxMeshGeometry());
|
|
104
|
+
expect(gl.calls.some((c) => c.name === 'drawElements')).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
});
|