@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.
Files changed (284) hide show
  1. package/dist/anisotropyPbrGlMeshMaterialRenderer.d.ts +4 -0
  2. package/dist/anisotropyPbrGlMeshMaterialRenderer.d.ts.map +1 -0
  3. package/dist/anisotropyPbrGlMeshMaterialRenderer.js +45 -0
  4. package/dist/anisotropyPbrGlMeshMaterialRenderer.js.map +1 -0
  5. package/dist/blinnPhongGlMeshMaterialRenderer.d.ts +4 -0
  6. package/dist/blinnPhongGlMeshMaterialRenderer.d.ts.map +1 -0
  7. package/dist/blinnPhongGlMeshMaterialRenderer.js +102 -0
  8. package/dist/blinnPhongGlMeshMaterialRenderer.js.map +1 -0
  9. package/dist/clearcoatPbrGlMeshMaterialRenderer.d.ts +4 -0
  10. package/dist/clearcoatPbrGlMeshMaterialRenderer.d.ts.map +1 -0
  11. package/dist/clearcoatPbrGlMeshMaterialRenderer.js +45 -0
  12. package/dist/clearcoatPbrGlMeshMaterialRenderer.js.map +1 -0
  13. package/dist/customShaderGlMeshMaterialRenderer.d.ts +6 -0
  14. package/dist/customShaderGlMeshMaterialRenderer.d.ts.map +1 -0
  15. package/dist/customShaderGlMeshMaterialRenderer.js +136 -0
  16. package/dist/customShaderGlMeshMaterialRenderer.js.map +1 -0
  17. package/dist/depthGlMeshMaterialRenderer.d.ts +4 -0
  18. package/dist/depthGlMeshMaterialRenderer.d.ts.map +1 -0
  19. package/dist/depthGlMeshMaterialRenderer.js +36 -0
  20. package/dist/depthGlMeshMaterialRenderer.js.map +1 -0
  21. package/dist/drawGlScene3D.d.ts +3 -0
  22. package/dist/drawGlScene3D.d.ts.map +1 -0
  23. package/dist/drawGlScene3D.js +251 -0
  24. package/dist/drawGlScene3D.js.map +1 -0
  25. package/dist/emissiveGlMeshMaterialRenderer.d.ts +4 -0
  26. package/dist/emissiveGlMeshMaterialRenderer.d.ts.map +1 -0
  27. package/dist/emissiveGlMeshMaterialRenderer.js +49 -0
  28. package/dist/emissiveGlMeshMaterialRenderer.js.map +1 -0
  29. package/dist/enableGlScene3DColorSpaceGuards.d.ts +4 -0
  30. package/dist/enableGlScene3DColorSpaceGuards.d.ts.map +1 -0
  31. package/dist/enableGlScene3DColorSpaceGuards.js +22 -0
  32. package/dist/enableGlScene3DColorSpaceGuards.js.map +1 -0
  33. package/dist/enableGlScene3DCustomShaderGuards.d.ts +4 -0
  34. package/dist/enableGlScene3DCustomShaderGuards.d.ts.map +1 -0
  35. package/dist/enableGlScene3DCustomShaderGuards.js +71 -0
  36. package/dist/enableGlScene3DCustomShaderGuards.js.map +1 -0
  37. package/dist/enableGlScene3DDeformGuards.d.ts +4 -0
  38. package/dist/enableGlScene3DDeformGuards.d.ts.map +1 -0
  39. package/dist/enableGlScene3DDeformGuards.js +42 -0
  40. package/dist/enableGlScene3DDeformGuards.js.map +1 -0
  41. package/dist/enableGlScene3DForwardLightSelectionGuards.d.ts +4 -0
  42. package/dist/enableGlScene3DForwardLightSelectionGuards.d.ts.map +1 -0
  43. package/dist/enableGlScene3DForwardLightSelectionGuards.js +21 -0
  44. package/dist/enableGlScene3DForwardLightSelectionGuards.js.map +1 -0
  45. package/dist/explainGlScene3DForwardLightSelection.d.ts +3 -0
  46. package/dist/explainGlScene3DForwardLightSelection.d.ts.map +1 -0
  47. package/dist/explainGlScene3DForwardLightSelection.js +19 -0
  48. package/dist/explainGlScene3DForwardLightSelection.js.map +1 -0
  49. package/dist/glClassicPrelude.d.ts +9 -0
  50. package/dist/glClassicPrelude.d.ts.map +1 -0
  51. package/dist/glClassicPrelude.js +288 -0
  52. package/dist/glClassicPrelude.js.map +1 -0
  53. package/dist/glDebugPrelude.d.ts +9 -0
  54. package/dist/glDebugPrelude.d.ts.map +1 -0
  55. package/dist/glDebugPrelude.js +143 -0
  56. package/dist/glDebugPrelude.js.map +1 -0
  57. package/dist/glEnvironmentCube.d.ts +5 -0
  58. package/dist/glEnvironmentCube.d.ts.map +1 -0
  59. package/dist/glEnvironmentCube.js +65 -0
  60. package/dist/glEnvironmentCube.js.map +1 -0
  61. package/dist/glEnvironmentIblBake.d.ts +4 -0
  62. package/dist/glEnvironmentIblBake.d.ts.map +1 -0
  63. package/dist/glEnvironmentIblBake.js +365 -0
  64. package/dist/glEnvironmentIblBake.js.map +1 -0
  65. package/dist/glEnvironmentSkybox.d.ts +3 -0
  66. package/dist/glEnvironmentSkybox.d.ts.map +1 -0
  67. package/dist/glEnvironmentSkybox.js +95 -0
  68. package/dist/glEnvironmentSkybox.js.map +1 -0
  69. package/dist/glLitProgram.d.ts +5 -0
  70. package/dist/glLitProgram.d.ts.map +1 -0
  71. package/dist/glLitProgram.js +211 -0
  72. package/dist/glLitProgram.js.map +1 -0
  73. package/dist/glMatcapPrelude.d.ts +8 -0
  74. package/dist/glMatcapPrelude.d.ts.map +1 -0
  75. package/dist/glMatcapPrelude.js +118 -0
  76. package/dist/glMatcapPrelude.js.map +1 -0
  77. package/dist/glMeshMaterialRegistry.d.ts +5 -0
  78. package/dist/glMeshMaterialRegistry.d.ts.map +1 -0
  79. package/dist/glMeshMaterialRegistry.js +29 -0
  80. package/dist/glMeshMaterialRegistry.js.map +1 -0
  81. package/dist/glMeshProgram.d.ts +14 -0
  82. package/dist/glMeshProgram.d.ts.map +1 -0
  83. package/dist/glMeshProgram.js +193 -0
  84. package/dist/glMeshProgram.js.map +1 -0
  85. package/dist/glMeshUpload.d.ts +4 -0
  86. package/dist/glMeshUpload.d.ts.map +1 -0
  87. package/dist/glMeshUpload.js +190 -0
  88. package/dist/glMeshUpload.js.map +1 -0
  89. package/dist/glParticleEmitter3D.d.ts +4 -0
  90. package/dist/glParticleEmitter3D.d.ts.map +1 -0
  91. package/dist/glParticleEmitter3D.js +342 -0
  92. package/dist/glParticleEmitter3D.js.map +1 -0
  93. package/dist/glPbrPrelude.d.ts +8 -0
  94. package/dist/glPbrPrelude.d.ts.map +1 -0
  95. package/dist/glPbrPrelude.js +569 -0
  96. package/dist/glPbrPrelude.js.map +1 -0
  97. package/dist/glPbrProgramCache.d.ts +4 -0
  98. package/dist/glPbrProgramCache.d.ts.map +1 -0
  99. package/dist/glPbrProgramCache.js +63 -0
  100. package/dist/glPbrProgramCache.js.map +1 -0
  101. package/dist/glPbrStandardBlock.d.ts +13 -0
  102. package/dist/glPbrStandardBlock.d.ts.map +1 -0
  103. package/dist/glPbrStandardBlock.js +107 -0
  104. package/dist/glPbrStandardBlock.js.map +1 -0
  105. package/dist/glScene3DRuntime.d.ts +5 -0
  106. package/dist/glScene3DRuntime.d.ts.map +1 -0
  107. package/dist/glScene3DRuntime.js +99 -0
  108. package/dist/glScene3DRuntime.js.map +1 -0
  109. package/dist/glScene3DTestHelper.d.ts +20 -0
  110. package/dist/glScene3DTestHelper.d.ts.map +1 -0
  111. package/dist/glScene3DTestHelper.js +178 -0
  112. package/dist/glScene3DTestHelper.js.map +1 -0
  113. package/dist/glScene3DTime.d.ts +4 -0
  114. package/dist/glScene3DTime.d.ts.map +1 -0
  115. package/dist/glScene3DTime.js +18 -0
  116. package/dist/glScene3DTime.js.map +1 -0
  117. package/dist/glShadedBuiltInModifiers.d.ts +11 -0
  118. package/dist/glShadedBuiltInModifiers.d.ts.map +1 -0
  119. package/dist/glShadedBuiltInModifiers.js +386 -0
  120. package/dist/glShadedBuiltInModifiers.js.map +1 -0
  121. package/dist/glShadedModifierSnippet.d.ts +4 -0
  122. package/dist/glShadedModifierSnippet.d.ts.map +1 -0
  123. package/dist/glShadedModifierSnippet.js +24 -0
  124. package/dist/glShadedModifierSnippet.js.map +1 -0
  125. package/dist/glShadedPrelude.d.ts +5 -0
  126. package/dist/glShadedPrelude.d.ts.map +1 -0
  127. package/dist/glShadedPrelude.js +393 -0
  128. package/dist/glShadedPrelude.js.map +1 -0
  129. package/dist/glShadowMap.d.ts +3 -0
  130. package/dist/glShadowMap.d.ts.map +1 -0
  131. package/dist/glShadowMap.js +150 -0
  132. package/dist/glShadowMap.js.map +1 -0
  133. package/dist/glToonPrelude.d.ts +7 -0
  134. package/dist/glToonPrelude.d.ts.map +1 -0
  135. package/dist/glToonPrelude.js +172 -0
  136. package/dist/glToonPrelude.js.map +1 -0
  137. package/dist/glUnlitPrelude.d.ts +9 -0
  138. package/dist/glUnlitPrelude.d.ts.map +1 -0
  139. package/dist/glUnlitPrelude.js +160 -0
  140. package/dist/glUnlitPrelude.js.map +1 -0
  141. package/dist/glWireframePrelude.d.ts +6 -0
  142. package/dist/glWireframePrelude.d.ts.map +1 -0
  143. package/dist/glWireframePrelude.js +56 -0
  144. package/dist/glWireframePrelude.js.map +1 -0
  145. package/dist/glWireframeUpload.d.ts +4 -0
  146. package/dist/glWireframeUpload.d.ts.map +1 -0
  147. package/dist/glWireframeUpload.js +85 -0
  148. package/dist/glWireframeUpload.js.map +1 -0
  149. package/dist/index.d.ts +56 -0
  150. package/dist/index.d.ts.map +1 -0
  151. package/dist/index.js +56 -0
  152. package/dist/index.js.map +1 -0
  153. package/dist/iridescencePbrGlMeshMaterialRenderer.d.ts +4 -0
  154. package/dist/iridescencePbrGlMeshMaterialRenderer.d.ts.map +1 -0
  155. package/dist/iridescencePbrGlMeshMaterialRenderer.js +54 -0
  156. package/dist/iridescencePbrGlMeshMaterialRenderer.js.map +1 -0
  157. package/dist/lambertGlMeshMaterialRenderer.d.ts +4 -0
  158. package/dist/lambertGlMeshMaterialRenderer.d.ts.map +1 -0
  159. package/dist/lambertGlMeshMaterialRenderer.js +70 -0
  160. package/dist/lambertGlMeshMaterialRenderer.js.map +1 -0
  161. package/dist/matcapGlMeshMaterialRenderer.d.ts +4 -0
  162. package/dist/matcapGlMeshMaterialRenderer.d.ts.map +1 -0
  163. package/dist/matcapGlMeshMaterialRenderer.js +49 -0
  164. package/dist/matcapGlMeshMaterialRenderer.js.map +1 -0
  165. package/dist/normalGlMeshMaterialRenderer.d.ts +4 -0
  166. package/dist/normalGlMeshMaterialRenderer.d.ts.map +1 -0
  167. package/dist/normalGlMeshMaterialRenderer.js +39 -0
  168. package/dist/normalGlMeshMaterialRenderer.js.map +1 -0
  169. package/dist/phongGlMeshMaterialRenderer.d.ts +4 -0
  170. package/dist/phongGlMeshMaterialRenderer.d.ts.map +1 -0
  171. package/dist/phongGlMeshMaterialRenderer.js +90 -0
  172. package/dist/phongGlMeshMaterialRenderer.js.map +1 -0
  173. package/dist/prepareGlScene3DForwardLights.d.ts +3 -0
  174. package/dist/prepareGlScene3DForwardLights.d.ts.map +1 -0
  175. package/dist/prepareGlScene3DForwardLights.js +117 -0
  176. package/dist/prepareGlScene3DForwardLights.js.map +1 -0
  177. package/dist/presentGlScene3D.d.ts +3 -0
  178. package/dist/presentGlScene3D.d.ts.map +1 -0
  179. package/dist/presentGlScene3D.js +21 -0
  180. package/dist/presentGlScene3D.js.map +1 -0
  181. package/dist/registerStandardPbrGlMaterial.d.ts +3 -0
  182. package/dist/registerStandardPbrGlMaterial.d.ts.map +1 -0
  183. package/dist/registerStandardPbrGlMaterial.js +11 -0
  184. package/dist/registerStandardPbrGlMaterial.js.map +1 -0
  185. package/dist/shadedGlMeshMaterialRenderer.d.ts +4 -0
  186. package/dist/shadedGlMeshMaterialRenderer.d.ts.map +1 -0
  187. package/dist/shadedGlMeshMaterialRenderer.js +135 -0
  188. package/dist/shadedGlMeshMaterialRenderer.js.map +1 -0
  189. package/dist/sheenPbrGlMeshMaterialRenderer.d.ts +4 -0
  190. package/dist/sheenPbrGlMeshMaterialRenderer.d.ts.map +1 -0
  191. package/dist/sheenPbrGlMeshMaterialRenderer.js +54 -0
  192. package/dist/sheenPbrGlMeshMaterialRenderer.js.map +1 -0
  193. package/dist/specularGlossinessPbrGlMeshMaterialRenderer.d.ts +4 -0
  194. package/dist/specularGlossinessPbrGlMeshMaterialRenderer.d.ts.map +1 -0
  195. package/dist/specularGlossinessPbrGlMeshMaterialRenderer.js +121 -0
  196. package/dist/specularGlossinessPbrGlMeshMaterialRenderer.js.map +1 -0
  197. package/dist/specularPbrGlMeshMaterialRenderer.d.ts +4 -0
  198. package/dist/specularPbrGlMeshMaterialRenderer.d.ts.map +1 -0
  199. package/dist/specularPbrGlMeshMaterialRenderer.js +55 -0
  200. package/dist/specularPbrGlMeshMaterialRenderer.js.map +1 -0
  201. package/dist/standardPbrGlMeshMaterialRenderer.d.ts +3 -0
  202. package/dist/standardPbrGlMeshMaterialRenderer.d.ts.map +1 -0
  203. package/dist/standardPbrGlMeshMaterialRenderer.js +35 -0
  204. package/dist/standardPbrGlMeshMaterialRenderer.js.map +1 -0
  205. package/dist/subsurfacePbrGlMeshMaterialRenderer.d.ts +4 -0
  206. package/dist/subsurfacePbrGlMeshMaterialRenderer.d.ts.map +1 -0
  207. package/dist/subsurfacePbrGlMeshMaterialRenderer.js +57 -0
  208. package/dist/subsurfacePbrGlMeshMaterialRenderer.js.map +1 -0
  209. package/dist/toonGlMeshMaterialRenderer.d.ts +4 -0
  210. package/dist/toonGlMeshMaterialRenderer.d.ts.map +1 -0
  211. package/dist/toonGlMeshMaterialRenderer.js +81 -0
  212. package/dist/toonGlMeshMaterialRenderer.js.map +1 -0
  213. package/dist/transmissionVolumePbrGlMeshMaterialRenderer.d.ts +4 -0
  214. package/dist/transmissionVolumePbrGlMeshMaterialRenderer.d.ts.map +1 -0
  215. package/dist/transmissionVolumePbrGlMeshMaterialRenderer.js +62 -0
  216. package/dist/transmissionVolumePbrGlMeshMaterialRenderer.js.map +1 -0
  217. package/dist/unlitGlMeshMaterialRenderer.d.ts +4 -0
  218. package/dist/unlitGlMeshMaterialRenderer.d.ts.map +1 -0
  219. package/dist/unlitGlMeshMaterialRenderer.js +61 -0
  220. package/dist/unlitGlMeshMaterialRenderer.js.map +1 -0
  221. package/dist/vertexColorGlMeshMaterialRenderer.d.ts +4 -0
  222. package/dist/vertexColorGlMeshMaterialRenderer.d.ts.map +1 -0
  223. package/dist/vertexColorGlMeshMaterialRenderer.js +54 -0
  224. package/dist/vertexColorGlMeshMaterialRenderer.js.map +1 -0
  225. package/dist/wireframeGlMeshMaterialRenderer.d.ts +4 -0
  226. package/dist/wireframeGlMeshMaterialRenderer.d.ts.map +1 -0
  227. package/dist/wireframeGlMeshMaterialRenderer.js +50 -0
  228. package/dist/wireframeGlMeshMaterialRenderer.js.map +1 -0
  229. package/package.json +58 -0
  230. package/src/anisotropyPbrGlMeshMaterialRenderer.test.ts +86 -0
  231. package/src/blinnPhongGlMeshMaterialRenderer.test.ts +94 -0
  232. package/src/clearcoatPbrGlMeshMaterialRenderer.test.ts +81 -0
  233. package/src/customShaderGlMeshMaterialRenderer.test.ts +169 -0
  234. package/src/depthGlMeshMaterialRenderer.test.ts +69 -0
  235. package/src/drawGlScene3D.test.ts +304 -0
  236. package/src/emissiveGlMeshMaterialRenderer.test.ts +60 -0
  237. package/src/enableGlScene3DColorSpaceGuards.test.ts +63 -0
  238. package/src/enableGlScene3DCustomShaderGuards.test.ts +85 -0
  239. package/src/enableGlScene3DDeformGuards.test.ts +98 -0
  240. package/src/enableGlScene3DForwardLightSelectionGuards.test.ts +84 -0
  241. package/src/explainGlScene3DForwardLightSelection.test.ts +32 -0
  242. package/src/glClassicPrelude.test.ts +214 -0
  243. package/src/glDebugPrelude.test.ts +94 -0
  244. package/src/glEnvironmentCube.test.ts +71 -0
  245. package/src/glEnvironmentIblBake.test.ts +31 -0
  246. package/src/glEnvironmentSkybox.test.ts +24 -0
  247. package/src/glLitProgram.test.ts +181 -0
  248. package/src/glMatcapPrelude.test.ts +74 -0
  249. package/src/glMeshMaterialRegistry.test.ts +52 -0
  250. package/src/glMeshProgram.test.ts +406 -0
  251. package/src/glMeshUpload.test.ts +311 -0
  252. package/src/glParticleEmitter3D.test.ts +280 -0
  253. package/src/glPbrPrelude.test.ts +178 -0
  254. package/src/glPbrProgramCache.test.ts +116 -0
  255. package/src/glPbrStandardBlock.test.ts +85 -0
  256. package/src/glScene3DRuntime.test.ts +108 -0
  257. package/src/glScene3DTime.test.ts +31 -0
  258. package/src/glShadedBuiltInModifiers.test.ts +377 -0
  259. package/src/glShadedModifierSnippet.test.ts +46 -0
  260. package/src/glShadedPrelude.test.ts +177 -0
  261. package/src/glShadowMap.test.ts +219 -0
  262. package/src/glToonPrelude.test.ts +118 -0
  263. package/src/glUnlitPrelude.test.ts +156 -0
  264. package/src/glWireframePrelude.test.ts +46 -0
  265. package/src/glWireframeUpload.test.ts +50 -0
  266. package/src/iridescencePbrGlMeshMaterialRenderer.test.ts +86 -0
  267. package/src/lambertGlMeshMaterialRenderer.test.ts +92 -0
  268. package/src/matcapGlMeshMaterialRenderer.test.ts +70 -0
  269. package/src/normalGlMeshMaterialRenderer.test.ts +69 -0
  270. package/src/phongGlMeshMaterialRenderer.test.ts +94 -0
  271. package/src/prepareGlScene3DForwardLights.test.ts +106 -0
  272. package/src/presentGlScene3D.test.ts +98 -0
  273. package/src/registerStandardPbrGlMaterial.test.ts +14 -0
  274. package/src/shadedGlMeshMaterialRenderer.test.ts +106 -0
  275. package/src/sheenPbrGlMeshMaterialRenderer.test.ts +83 -0
  276. package/src/specularGlossinessPbrGlMeshMaterialRenderer.test.ts +96 -0
  277. package/src/specularPbrGlMeshMaterialRenderer.test.ts +83 -0
  278. package/src/standardPbrGlMeshMaterialRenderer.test.ts +88 -0
  279. package/src/subsurfacePbrGlMeshMaterialRenderer.test.ts +86 -0
  280. package/src/toonGlMeshMaterialRenderer.test.ts +92 -0
  281. package/src/transmissionVolumePbrGlMeshMaterialRenderer.test.ts +93 -0
  282. package/src/unlitGlMeshMaterialRenderer.test.ts +88 -0
  283. package/src/vertexColorGlMeshMaterialRenderer.test.ts +60 -0
  284. 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
+ });