@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,219 @@
1
+ import { createCamera3D, createOrthographicProjection, setCamera3DViewMatrix4FromLookAt } from '@flighthq/camera';
2
+ import { createVector3 } from '@flighthq/geometry';
3
+ import { createMeshGeometry, updateMeshMorph } from '@flighthq/mesh';
4
+ import { addNodeChild } from '@flighthq/node';
5
+ import { createMesh, createNode3D, Node3DKind } from '@flighthq/scene3d';
6
+ import type { Skin, VertexAttributeLayout } from '@flighthq/types';
7
+ import { EntityRuntimeKey } from '@flighthq/types';
8
+
9
+ import { getGlScene3DRuntime } from './glScene3DRuntime';
10
+ import { makeGlScene3DState } from './glScene3DTestHelper';
11
+ import { drawGlScene3DShadowMap } from './glShadowMap';
12
+
13
+ const POSITION_LAYOUT: VertexAttributeLayout = {
14
+ attributes: [{ byteOffset: 0, format: 'float32x3', semantic: 'position' }],
15
+ stride: 12,
16
+ };
17
+
18
+ // A layout carrying joints0 (what hasMeshGeometrySkin keys off) so a mesh with a skin GPU-skins.
19
+ const SKINNED_LAYOUT: VertexAttributeLayout = {
20
+ attributes: [
21
+ { byteOffset: 0, format: 'float32x3', semantic: 'position' },
22
+ { byteOffset: 12, format: 'float32x4', semantic: 'joints0' },
23
+ { byteOffset: 28, format: 'float32x4', semantic: 'weights0' },
24
+ ],
25
+ stride: 44,
26
+ };
27
+
28
+ function lastUploadedVertices(calls: readonly { name: string; args: readonly unknown[] }[]): Float32Array {
29
+ const data = calls
30
+ .filter((c) => c.name === 'bufferData')
31
+ .map((c) => c.args[1])
32
+ .filter((d): d is Float32Array => d instanceof Float32Array);
33
+ return data[data.length - 1]!;
34
+ }
35
+
36
+ function makeShadowState() {
37
+ const { state, gl } = makeGlScene3DState();
38
+
39
+ const calls = gl.calls;
40
+ const record =
41
+ (name: string, result?: unknown) =>
42
+ (...args: unknown[]): unknown => {
43
+ calls.push({ name, args });
44
+ return result;
45
+ };
46
+
47
+ Object.assign(gl as unknown as Record<string, unknown>, {
48
+ FRAMEBUFFER: 0x8d40,
49
+ FRAMEBUFFER_BINDING: 0x8ca6,
50
+ VIEWPORT: 0x0ba2,
51
+ DEPTH_BUFFER_BIT: 0x100,
52
+ COLOR_BUFFER_BIT: 0x4000,
53
+ FRONT: 0x0404,
54
+ MAX_SAMPLES: 0x8d57,
55
+ LINEAR: 0x2601,
56
+ NEAREST: 0x2600,
57
+ TEXTURE_MIN_FILTER: 0x2801,
58
+ TEXTURE_MAG_FILTER: 0x2800,
59
+ TEXTURE_WRAP_S: 0x2802,
60
+ TEXTURE_WRAP_T: 0x2803,
61
+ CLAMP_TO_EDGE: 0x812f,
62
+ COLOR_ATTACHMENT0: 0x8ce0,
63
+ DEPTH_STENCIL_ATTACHMENT: 0x821a,
64
+ DEPTH24_STENCIL8: 0x88f0,
65
+ DEPTH_STENCIL: 0x84f9,
66
+ UNSIGNED_INT_24_8: 0x84fa,
67
+ RENDERBUFFER: 0x8d41,
68
+ createFramebuffer: () => ({}),
69
+ deleteFramebuffer: () => {},
70
+ bindFramebuffer: record('bindFramebuffer'),
71
+ viewport: record('viewport'),
72
+ getParameter: (param: number) => {
73
+ const gl2 = gl as unknown as Record<string, number>;
74
+ if (param === gl2['MAX_SAMPLES']) return 4;
75
+ if (param === gl2['FRAMEBUFFER_BINDING']) return null;
76
+ if (param === gl2['VIEWPORT']) return new Int32Array([0, 0, 256, 256]);
77
+ return null;
78
+ },
79
+ clear: record('clear'),
80
+ createRenderbuffer: () => ({}),
81
+ bindRenderbuffer: () => {},
82
+ deleteRenderbuffer: () => {},
83
+ framebufferTexture2D: () => {},
84
+ framebufferRenderbuffer: () => {},
85
+ getExtension: () => ({}),
86
+ });
87
+
88
+ (state[EntityRuntimeKey] as unknown as Record<string, unknown>).currentFramebuffer = null;
89
+
90
+ return { state, gl };
91
+ }
92
+
93
+ function makeShadowCamera() {
94
+ const camera = createCamera3D({
95
+ far: 100,
96
+ near: 0.1,
97
+ projection: createOrthographicProjection({ halfHeight: 10, halfWidth: 10 }),
98
+ });
99
+ setCamera3DViewMatrix4FromLookAt(camera, createVector3(0, 10, 10), createVector3(0, 0, 0), createVector3(0, 1, 0));
100
+ return camera;
101
+ }
102
+
103
+ describe('drawGlScene3DShadowMap', () => {
104
+ it('lazily creates the shadow target on the first call', () => {
105
+ const { state } = makeShadowState();
106
+ const scene = createNode3D(Node3DKind);
107
+ const camera = makeShadowCamera();
108
+
109
+ drawGlScene3DShadowMap(state, scene, camera);
110
+
111
+ expect(getGlScene3DRuntime(state).shadowTarget).not.toBeNull();
112
+ });
113
+
114
+ it('reuses the same shadow target on subsequent calls', () => {
115
+ const { state } = makeShadowState();
116
+ const scene = createNode3D(Node3DKind);
117
+ const camera = makeShadowCamera();
118
+
119
+ drawGlScene3DShadowMap(state, scene, camera);
120
+ const firstTarget = getGlScene3DRuntime(state).shadowTarget;
121
+
122
+ drawGlScene3DShadowMap(state, scene, camera);
123
+ const secondTarget = getGlScene3DRuntime(state).shadowTarget;
124
+
125
+ expect(secondTarget).toBe(firstTarget);
126
+ });
127
+
128
+ it('records the light-space matrix on the runtime', () => {
129
+ const { state } = makeShadowState();
130
+ const scene = createNode3D(Node3DKind);
131
+ const camera = makeShadowCamera();
132
+
133
+ drawGlScene3DShadowMap(state, scene, camera);
134
+
135
+ const shadow = getGlScene3DRuntime(state).shadow;
136
+ expect(shadow).not.toBeNull();
137
+ expect(shadow!.matrix).not.toBeNull();
138
+ });
139
+
140
+ it('sets up the depth pass with front-face culling', () => {
141
+ const { state, gl } = makeShadowState();
142
+ const scene = createNode3D(Node3DKind);
143
+ const camera = makeShadowCamera();
144
+
145
+ drawGlScene3DShadowMap(state, scene, camera);
146
+
147
+ const frontValue = (gl as unknown as Record<string, number>)['FRONT'];
148
+ const cullFaceCall = gl.calls.find((c) => c.name === 'cullFace' && c.args[0] === frontValue);
149
+ expect(cullFaceCall).toBeDefined();
150
+
151
+ const cullFaceConstant = (gl as unknown as Record<string, number>)['CULL_FACE'];
152
+ const enableCullFaceCall = gl.calls.find((c) => c.name === 'enable' && c.args[0] === cullFaceConstant);
153
+ expect(enableCullFaceCall).toBeDefined();
154
+ });
155
+
156
+ it('records the caster at the pose the app blended before the pass', () => {
157
+ const { state, gl } = makeShadowState();
158
+ const scene = createNode3D(Node3DKind);
159
+ const geometry = createMeshGeometry({ layout: POSITION_LAYOUT, vertices: new Float32Array([0, 0, 0, 1, 0, 0]) });
160
+ const mesh = createMesh(geometry, []);
161
+ // Weight 1 on a target that raises y by 5. The morph is blended by the app's prepareScene3DMorph
162
+ // before any draw (here updateMeshMorph stands in for it); the depth pass just uploads that pose,
163
+ // it no longer re-blends internally — otherwise the cull would have already lagged a frame.
164
+ mesh.morph = {
165
+ targets: [{ normalDeltas: null, positionDeltas: new Float32Array([0, 5, 0, 0, 5, 0]), tangentDeltas: null }],
166
+ weights: new Float32Array([1]),
167
+ };
168
+ addNodeChild(scene, mesh);
169
+ updateMeshMorph(mesh);
170
+
171
+ drawGlScene3DShadowMap(state, scene, makeShadowCamera());
172
+
173
+ const uploaded = lastUploadedVertices(gl.calls);
174
+ expect(uploaded[1]).toBe(5);
175
+ expect(uploaded[4]).toBe(5);
176
+ });
177
+
178
+ it('draws a GPU-skinned caster through the HAS_SKIN depth variant', () => {
179
+ const { state } = makeShadowState();
180
+ const scene = createNode3D(Node3DKind);
181
+ const geometry = createMeshGeometry({
182
+ layout: SKINNED_LAYOUT,
183
+ vertices: new Float32Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]),
184
+ });
185
+ const mesh = createMesh(geometry, []);
186
+ const skin: Skin = {
187
+ skeleton: {
188
+ [EntityRuntimeKey]: undefined,
189
+ inverseBindMatrices: new Float32Array(16),
190
+ jointMatrices: new Float32Array(16),
191
+ joints: [],
192
+ names: null,
193
+ },
194
+ };
195
+ mesh.skin = skin;
196
+ addNodeChild(scene, mesh);
197
+
198
+ drawGlScene3DShadowMap(state, scene, makeShadowCamera());
199
+
200
+ // A skinned caster compiles + uses the dedicated HAS_SKIN depth program rather than the rigid one.
201
+ expect([...getGlScene3DRuntime(state).programCache.keys()]).toContain('shadow:depth:skin');
202
+ });
203
+
204
+ it('restores the previous framebuffer after the depth pass', () => {
205
+ const { state, gl } = makeShadowState();
206
+ const scene = createNode3D(Node3DKind);
207
+ const camera = makeShadowCamera();
208
+
209
+ drawGlScene3DShadowMap(state, scene, camera);
210
+
211
+ const framebufferConstant = (gl as unknown as Record<string, number>)['FRAMEBUFFER'];
212
+ const bindFramebufferCalls = gl.calls.filter((c) => c.name === 'bindFramebuffer');
213
+ const lastBindFramebuffer = bindFramebufferCalls[bindFramebufferCalls.length - 1];
214
+
215
+ expect(lastBindFramebuffer).toBeDefined();
216
+ expect(lastBindFramebuffer.args[0]).toBe(framebufferConstant);
217
+ expect(lastBindFramebuffer.args[1]).toBeNull();
218
+ });
219
+ });
@@ -0,0 +1,118 @@
1
+ import type { GlToonDefineKey } from '@flighthq/types';
2
+
3
+ import { getGlScene3DRuntime } from './glScene3DRuntime';
4
+ import { makeFakeGl2, makeGlScene3DState } from './glScene3DTestHelper';
5
+ import {
6
+ buildGlToonDefineKey,
7
+ compileGlToonProgram,
8
+ ensureGlToonProgram,
9
+ getGlToonFragmentSourceForKey,
10
+ getGlToonVertexSourceForKey,
11
+ } from './glToonPrelude';
12
+
13
+ const FLAT: GlToonDefineKey = {
14
+ alphaMaskEnabled: false,
15
+ hasBaseColorMap: false,
16
+ hasRamp: false,
17
+ hasUvTransform: false,
18
+ };
19
+
20
+ describe('buildGlToonDefineKey', () => {
21
+ it('produces distinct stable strings per flag set', () => {
22
+ expect(buildGlToonDefineKey(FLAT)).toBe('-----');
23
+ expect(
24
+ buildGlToonDefineKey({ alphaMaskEnabled: true, hasBaseColorMap: true, hasRamp: true, hasUvTransform: true }),
25
+ ).toBe('mbru-');
26
+ expect(buildGlToonDefineKey({ ...FLAT, hasRamp: true })).toBe('--r--');
27
+ });
28
+
29
+ it('encodes a non-identity uv transform in the u slot ahead of skin', () => {
30
+ expect(buildGlToonDefineKey({ ...FLAT, hasUvTransform: true })).toBe('---u-');
31
+ expect(buildGlToonDefineKey({ ...FLAT, hasUvTransform: true })).not.toBe(buildGlToonDefineKey(FLAT));
32
+ });
33
+
34
+ it('appends a skinned flag that differs from the rigid key', () => {
35
+ expect(buildGlToonDefineKey({ ...FLAT, hasSkin: true })).toBe('----k');
36
+ expect(buildGlToonDefineKey({ ...FLAT, hasSkin: true })).not.toBe(buildGlToonDefineKey(FLAT));
37
+ });
38
+ });
39
+
40
+ describe('compileGlToonProgram', () => {
41
+ it('compiles, links, and resolves the lit + toon uniform locations', () => {
42
+ const gl = makeFakeGl2();
43
+ const program = compileGlToonProgram(gl, FLAT);
44
+ // Lit base locations (spread from resolveGlLitLocations).
45
+ expect(program.locDirectional).not.toBeNull();
46
+ expect(program.locAmbientRadiance).not.toBeNull();
47
+ expect(program.locCameraPosition).not.toBeNull();
48
+ // Toon material locations.
49
+ expect(program.locBaseColor).not.toBeNull();
50
+ expect(program.locSteps).not.toBeNull();
51
+ expect(program.locRamp).not.toBeNull();
52
+ expect(program.locBaseColorMap).not.toBeNull();
53
+ expect(program.locAlphaCutoff).not.toBeNull();
54
+ // Shared vertex transforms.
55
+ expect(program.locModel).not.toBeNull();
56
+ expect(program.locNormalMatrix).not.toBeNull();
57
+ expect(program.locViewProjection).not.toBeNull();
58
+ expect(gl.calls.some((c) => c.name === 'linkProgram')).toBe(true);
59
+ });
60
+ });
61
+
62
+ describe('ensureGlToonProgram', () => {
63
+ it('caches a variant under the toon namespace and reuses it', () => {
64
+ const { state, gl } = makeGlScene3DState();
65
+ const first = ensureGlToonProgram(state, FLAT);
66
+ const links = gl.calls.filter((c) => c.name === 'linkProgram').length;
67
+ const second = ensureGlToonProgram(state, FLAT);
68
+ expect(second).toBe(first);
69
+ expect(gl.calls.filter((c) => c.name === 'linkProgram').length).toBe(links);
70
+ expect([...getGlScene3DRuntime(state).programCache.keys()].some((k) => k.startsWith('toon:'))).toBe(true);
71
+ });
72
+ });
73
+
74
+ describe('getGlToonFragmentSourceForKey', () => {
75
+ it('includes the light block and gates feature defines on their flag', () => {
76
+ expect(getGlToonFragmentSourceForKey(FLAT)).toContain('u_directional');
77
+ expect(getGlToonFragmentSourceForKey(FLAT)).toContain('#define MAX_FORWARD_LIGHTS');
78
+ expect(getGlToonFragmentSourceForKey(FLAT)).not.toContain('#define HAS_BASE_COLOR_MAP');
79
+ expect(getGlToonFragmentSourceForKey(FLAT)).not.toContain('#define HAS_RAMP');
80
+ expect(getGlToonFragmentSourceForKey({ ...FLAT, hasBaseColorMap: true })).toContain('#define HAS_BASE_COLOR_MAP');
81
+ expect(getGlToonFragmentSourceForKey({ ...FLAT, hasRamp: true })).toContain('#define HAS_RAMP');
82
+ expect(getGlToonFragmentSourceForKey({ ...FLAT, alphaMaskEnabled: true })).toContain('#define ALPHA_MASK');
83
+ });
84
+
85
+ it('shadow-maps the banded directional term (and only the directional term)', () => {
86
+ // The banded directional contribution is multiplied by the shared sampleDirectionalShadow factor,
87
+ // matching the classic/PBR families; ambient stays unshadowed, so the helper appears once.
88
+ for (const key of [FLAT, { ...FLAT, hasRamp: true }]) {
89
+ const source = getGlToonFragmentSourceForKey(key);
90
+ expect(source).toContain('sampleDirectionalShadow(v_worldPosition)');
91
+ expect(source.match(/sampleDirectionalShadow\(v_worldPosition\)/g)).toHaveLength(1);
92
+ }
93
+ });
94
+ });
95
+
96
+ describe('getGlToonVertexSourceForKey', () => {
97
+ it('passes world position and normal varyings through the shared vertex transforms', () => {
98
+ const source = getGlToonVertexSourceForKey(FLAT);
99
+ expect(source).toContain('#version 300 es');
100
+ expect(source).toContain('v_worldPosition');
101
+ expect(source).toContain('v_normal');
102
+ expect(source).toContain('u_viewProjection');
103
+ expect(source).toContain('u_normalMatrix');
104
+ });
105
+
106
+ it('injects the skin declarations only for the skinned variant', () => {
107
+ const skinned = getGlToonVertexSourceForKey({ ...FLAT, hasSkin: true });
108
+ expect(skinned).toContain('#define HAS_SKIN');
109
+ expect(skinned).not.toContain('#define MAX_JOINTS');
110
+ expect(skinned).toContain('sampler2D u_jointTexture');
111
+ expect(skinned).toContain('texelFetch');
112
+ expect(skinned).toContain('mat4 skinMatrix()');
113
+ expect(skinned).toContain('a_joints0');
114
+ const rigid = getGlToonVertexSourceForKey(FLAT);
115
+ expect(rigid).not.toContain('#define HAS_SKIN');
116
+ expect(rigid).not.toContain('a_joints0');
117
+ });
118
+ });
@@ -0,0 +1,156 @@
1
+ import type { LinearColor, VideoTexture, GlUnlitDefineKey } from '@flighthq/types';
2
+
3
+ import { getGlScene3DRuntime } from './glScene3DRuntime';
4
+ import { makeFakeGl2, makeGlScene3DState } from './glScene3DTestHelper';
5
+ import {
6
+ bindGlUnlitSurface,
7
+ bindGlUnlitVideoSurface,
8
+ buildGlUnlitDefineKey,
9
+ compileGlUnlitProgram,
10
+ ensureGlUnlitProgram,
11
+ getGlUnlitFragmentSourceForKey,
12
+ getGlUnlitVertexSourceForKey,
13
+ } from './glUnlitPrelude';
14
+
15
+ function makeReadyVideoTexture(frameId: number): VideoTexture {
16
+ return {
17
+ frameId,
18
+ sampler: null,
19
+ source: { element: { readyState: 4, videoWidth: 320, videoHeight: 240 } as unknown as HTMLVideoElement },
20
+ } as unknown as VideoTexture;
21
+ }
22
+
23
+ const FLAT: GlUnlitDefineKey = {
24
+ alphaMaskEnabled: false,
25
+ hasColorMap: false,
26
+ hasUvTransform: false,
27
+ vertexColor: false,
28
+ };
29
+ const COLOR: LinearColor = [0.5, 0.25, 0.1, 1];
30
+
31
+ describe('bindGlUnlitSurface', () => {
32
+ it('uploads the color, intensity, and alpha cutoff', () => {
33
+ const { state, gl } = makeGlScene3DState();
34
+ const program = compileGlUnlitProgram(gl, FLAT);
35
+ bindGlUnlitSurface(state, program, COLOR, 2, null, 0.5);
36
+ expect(gl.calls.some((c) => c.name === 'uniform4f')).toBe(true);
37
+ expect(gl.calls.filter((c) => c.name === 'uniform1f').length).toBeGreaterThanOrEqual(2);
38
+ // No color map → no texture bind.
39
+ expect(gl.calls.some((c) => c.name === 'bindTexture')).toBe(false);
40
+ });
41
+ });
42
+
43
+ describe('bindGlUnlitVideoSurface', () => {
44
+ it('uploads the color/intensity/cutoff and binds the video map on unit 0', () => {
45
+ const { state, gl } = makeGlScene3DState();
46
+ const program = compileGlUnlitProgram(gl, { ...FLAT, hasColorMap: true });
47
+ bindGlUnlitVideoSurface(state, program, COLOR, 1, makeReadyVideoTexture(2), 0.5);
48
+ expect(gl.calls.some((c) => c.name === 'uniform4f')).toBe(true);
49
+ // Live frame uploaded and the map sampler set to unit 0.
50
+ expect(gl.calls.some((c) => c.name === 'texImage2D')).toBe(true);
51
+ expect(gl.calls.some((c) => c.name === 'bindTexture')).toBe(true);
52
+ expect(gl.calls.some((c) => c.name === 'uniform1i')).toBe(true);
53
+ });
54
+
55
+ it('does not upload a frame that has not advanced (the dirty-gate)', () => {
56
+ const { state, gl } = makeGlScene3DState();
57
+ const program = compileGlUnlitProgram(gl, { ...FLAT, hasColorMap: true });
58
+ const videoMap = makeReadyVideoTexture(3);
59
+ bindGlUnlitVideoSurface(state, program, COLOR, 1, videoMap, 0.5);
60
+ const uploads = gl.calls.filter((c) => c.name === 'texImage2D').length;
61
+ bindGlUnlitVideoSurface(state, program, COLOR, 1, videoMap, 0.5);
62
+ expect(gl.calls.filter((c) => c.name === 'texImage2D').length).toBe(uploads);
63
+ });
64
+ });
65
+
66
+ describe('buildGlUnlitDefineKey', () => {
67
+ it('produces distinct stable strings per flag set', () => {
68
+ expect(buildGlUnlitDefineKey(FLAT)).toBe('-----');
69
+ expect(
70
+ buildGlUnlitDefineKey({ alphaMaskEnabled: true, hasColorMap: true, hasUvTransform: true, vertexColor: true }),
71
+ ).toBe('mcvu-');
72
+ expect(buildGlUnlitDefineKey({ ...FLAT, vertexColor: true })).toBe('--v--');
73
+ });
74
+
75
+ it('encodes a non-identity uv transform with a u slot and keys it distinctly', () => {
76
+ expect(buildGlUnlitDefineKey({ ...FLAT, hasUvTransform: true })).toBe('---u-');
77
+ expect(buildGlUnlitDefineKey({ ...FLAT, hasUvTransform: true })).not.toBe(buildGlUnlitDefineKey(FLAT));
78
+ });
79
+
80
+ it('encodes the skinned variant with a trailing k and keys it distinctly', () => {
81
+ expect(buildGlUnlitDefineKey({ ...FLAT, hasSkin: true }).endsWith('k')).toBe(true);
82
+ expect(buildGlUnlitDefineKey({ ...FLAT, hasSkin: true })).not.toBe(buildGlUnlitDefineKey(FLAT));
83
+ });
84
+ });
85
+
86
+ describe('compileGlUnlitProgram', () => {
87
+ it('compiles, links, and resolves the unlit uniforms with a null normal matrix', () => {
88
+ const gl = makeFakeGl2();
89
+ const program = compileGlUnlitProgram(gl, FLAT);
90
+ expect(program.locColor).not.toBeNull();
91
+ expect(program.locIntensity).not.toBeNull();
92
+ expect(program.locNormalMatrix).toBeNull();
93
+ expect(gl.calls.some((c) => c.name === 'linkProgram')).toBe(true);
94
+ });
95
+ });
96
+
97
+ describe('ensureGlUnlitProgram', () => {
98
+ it('caches a variant under the unlit namespace and reuses it', () => {
99
+ const { state, gl } = makeGlScene3DState();
100
+ const first = ensureGlUnlitProgram(state, FLAT);
101
+ const links = gl.calls.filter((c) => c.name === 'linkProgram').length;
102
+ const second = ensureGlUnlitProgram(state, FLAT);
103
+ expect(second).toBe(first);
104
+ expect(gl.calls.filter((c) => c.name === 'linkProgram').length).toBe(links);
105
+ expect([...getGlScene3DRuntime(state).programCache.keys()].some((k) => k.startsWith('unlit:'))).toBe(true);
106
+ });
107
+
108
+ it('compiles the skinned variant as a distinct cache entry', () => {
109
+ const { state } = makeGlScene3DState();
110
+ ensureGlUnlitProgram(state, FLAT);
111
+ getGlScene3DRuntime(state).activeSkinnedRun = true;
112
+ ensureGlUnlitProgram(state, FLAT);
113
+ const keys = [...getGlScene3DRuntime(state).programCache.keys()];
114
+ expect(keys.some((k) => k.startsWith('unlit:') && k.endsWith('k'))).toBe(true);
115
+ expect(keys.filter((k) => k.startsWith('unlit:')).length).toBe(2);
116
+ });
117
+ });
118
+
119
+ describe('getGlUnlitFragmentSourceForKey', () => {
120
+ it('includes feature defines only when their flag is set', () => {
121
+ expect(getGlUnlitFragmentSourceForKey(FLAT)).not.toContain('#define HAS_COLOR_MAP');
122
+ expect(getGlUnlitFragmentSourceForKey({ ...FLAT, hasColorMap: true })).toContain('#define HAS_COLOR_MAP');
123
+ expect(getGlUnlitFragmentSourceForKey({ ...FLAT, alphaMaskEnabled: true })).toContain('#define ALPHA_MASK');
124
+ });
125
+ });
126
+
127
+ describe('getGlUnlitVertexSourceForKey', () => {
128
+ it('defines VERTEX_COLOR only in the vertex-color variant', () => {
129
+ expect(getGlUnlitVertexSourceForKey(FLAT)).not.toContain('#define VERTEX_COLOR');
130
+ expect(getGlUnlitVertexSourceForKey({ ...FLAT, vertexColor: true })).toContain('#define VERTEX_COLOR');
131
+ // The color0 attribute lives behind the #ifdef, so it is present in the body string either way.
132
+ expect(getGlUnlitVertexSourceForKey({ ...FLAT, vertexColor: true })).toContain('a_color0');
133
+ });
134
+
135
+ it('defines HAS_UV_TRANSFORM and applies the transform only in the uv-transform variant', () => {
136
+ // applyUvTransform is always in the body (identity when undefined); the define + uniform are gated.
137
+ expect(getGlUnlitVertexSourceForKey(FLAT)).not.toContain('#define HAS_UV_TRANSFORM');
138
+ const transformed = getGlUnlitVertexSourceForKey({ ...FLAT, hasColorMap: true, hasUvTransform: true });
139
+ expect(transformed).toContain('#define HAS_UV_TRANSFORM');
140
+ expect(transformed).toContain('uniform mat3 u_uvTransform');
141
+ expect(transformed).toContain('v_uv0 = applyUvTransform(a_uv0)');
142
+ });
143
+
144
+ it('splices the skin declarations and HAS_SKIN defines only in the skinned variant', () => {
145
+ const skinned = getGlUnlitVertexSourceForKey({ ...FLAT, hasSkin: true });
146
+ expect(skinned).toContain('#define HAS_SKIN');
147
+ expect(skinned).not.toContain('#define MAX_JOINTS');
148
+ expect(skinned).toContain('sampler2D u_jointTexture');
149
+ expect(skinned).toContain('texelFetch');
150
+ expect(skinned).toContain('mat4 skinMatrix()');
151
+ expect(skinned).toContain('a_joints0');
152
+ const flat = getGlUnlitVertexSourceForKey(FLAT);
153
+ expect(flat).not.toContain('#define HAS_SKIN');
154
+ expect(flat).not.toContain('a_joints0');
155
+ });
156
+ });
@@ -0,0 +1,46 @@
1
+ import { getGlScene3DRuntime } from './glScene3DRuntime';
2
+ import { makeFakeGl2, makeGlScene3DState } from './glScene3DTestHelper';
3
+ import {
4
+ compileGlWireframeProgram,
5
+ ensureGlWireframeProgram,
6
+ getGlWireframeFragmentSource,
7
+ getGlWireframeVertexSource,
8
+ } from './glWireframePrelude';
9
+
10
+ describe('compileGlWireframeProgram', () => {
11
+ it('compiles, links, and resolves the color + transform locations with a null normal matrix', () => {
12
+ const gl = makeFakeGl2();
13
+ const program = compileGlWireframeProgram(gl);
14
+ expect(program.locColor).not.toBeNull();
15
+ expect(program.locViewProjection).not.toBeNull();
16
+ expect(program.locNormalMatrix).toBeNull();
17
+ expect(gl.calls.some((c) => c.name === 'linkProgram')).toBe(true);
18
+ });
19
+ });
20
+
21
+ describe('ensureGlWireframeProgram', () => {
22
+ it('caches the single wireframe program under the wireframe namespace', () => {
23
+ const { state, gl } = makeGlScene3DState();
24
+ const first = ensureGlWireframeProgram(state);
25
+ const links = gl.calls.filter((c) => c.name === 'linkProgram').length;
26
+ const second = ensureGlWireframeProgram(state);
27
+ expect(second).toBe(first);
28
+ expect(gl.calls.filter((c) => c.name === 'linkProgram').length).toBe(links);
29
+ expect([...getGlScene3DRuntime(state).programCache.keys()].some((k) => k.startsWith('wireframe:'))).toBe(true);
30
+ });
31
+ });
32
+
33
+ describe('getGlWireframeFragmentSource', () => {
34
+ it('outputs the flat color uniform', () => {
35
+ expect(getGlWireframeFragmentSource()).toContain('u_color');
36
+ });
37
+ });
38
+
39
+ describe('getGlWireframeVertexSource', () => {
40
+ it('transforms position by model and view-projection', () => {
41
+ const source = getGlWireframeVertexSource();
42
+ expect(source).toContain('a_position');
43
+ expect(source).toContain('u_viewProjection');
44
+ expect(source).toContain('u_model');
45
+ });
46
+ });
@@ -0,0 +1,50 @@
1
+ import { createBoxMeshGeometry } from '@flighthq/mesh';
2
+
3
+ import { makeGlScene3DState } from './glScene3DTestHelper';
4
+ import { destroyGlWireframeUpload, ensureGlWireframeUpload } from './glWireframeUpload';
5
+
6
+ describe('destroyGlWireframeUpload', () => {
7
+ it('deletes the VAO and the line-index buffer, but not the shared vertex buffer', () => {
8
+ const { state, gl } = makeGlScene3DState();
9
+ const upload = ensureGlWireframeUpload(state, createBoxMeshGeometry());
10
+ const deletesBefore = gl.calls.filter((c) => c.name === 'deleteBuffer').length;
11
+ destroyGlWireframeUpload(state, upload);
12
+ // Exactly the line-index buffer is freed here; the interleaved vertex buffer belongs to the
13
+ // triangle mesh upload and is freed by destroyGlMeshUpload for that geometry.
14
+ expect(gl.calls.filter((c) => c.name === 'deleteBuffer').length).toBe(deletesBefore + 1);
15
+ expect(gl.calls.filter((c) => c.name === 'deleteVertexArray').length).toBe(1);
16
+ const deletedBuffer = gl.calls.find((c) => c.name === 'deleteBuffer');
17
+ expect(deletedBuffer!.args[0]).toBe(upload.lineIndexBuffer);
18
+ });
19
+ });
20
+
21
+ describe('ensureGlWireframeUpload', () => {
22
+ it('builds a line-index VAO and uploads the derived line buffer', () => {
23
+ const { state, gl } = makeGlScene3DState();
24
+ const geometry = createBoxMeshGeometry();
25
+ const upload = ensureGlWireframeUpload(state, geometry);
26
+ expect(upload.vao).toBeDefined();
27
+ expect(upload.lineIndexBuffer).toBeDefined();
28
+ // The element-array upload holds the derived line list: two indices per triangle edge, six per
29
+ // triangle (independent of the Uint16/Uint32 index width).
30
+ const triangleCount = geometry.indices!.length / 3;
31
+ const lineUpload = gl.calls.find(
32
+ (c) =>
33
+ c.name === 'bufferData' &&
34
+ c.args[0] === gl.ELEMENT_ARRAY_BUFFER &&
35
+ ArrayBuffer.isView(c.args[1] as ArrayBufferView) &&
36
+ (c.args[1] as Uint16Array | Uint32Array).length === triangleCount * 6,
37
+ );
38
+ expect(lineUpload).toBeDefined();
39
+ });
40
+
41
+ it('re-binds the cached VAO without rebuilding when the version is unchanged', () => {
42
+ const { state, gl } = makeGlScene3DState();
43
+ const geometry = createBoxMeshGeometry();
44
+ const first = ensureGlWireframeUpload(state, geometry);
45
+ const builds = gl.calls.filter((c) => c.name === 'createVertexArray').length;
46
+ const second = ensureGlWireframeUpload(state, geometry);
47
+ expect(second).toBe(first);
48
+ expect(gl.calls.filter((c) => c.name === 'createVertexArray').length).toBe(builds);
49
+ });
50
+ });
@@ -0,0 +1,86 @@
1
+ import { createCamera3D } from '@flighthq/camera';
2
+ import { createMatrix3, createMatrix4 } from '@flighthq/geometry';
3
+ import { createIridescencePbrMaterial } from '@flighthq/materials';
4
+ import { createBoxMeshGeometry } from '@flighthq/mesh';
5
+ import type { Camera3D, Matrix3, Matrix4, Scene3DLightBlock, Scene3DRenderProxy } from '@flighthq/types';
6
+ import { IridescencePbrMaterialKind } from '@flighthq/types';
7
+
8
+ import { getGlMeshMaterialRenderer } from './glMeshMaterialRegistry';
9
+ import { getGlScene3DRuntime } from './glScene3DRuntime';
10
+ import { makeGlScene3DState } from './glScene3DTestHelper';
11
+ import {
12
+ iridescencePbrGlMeshMaterialRenderer,
13
+ registerIridescencePbrGlMaterial,
14
+ } from './iridescencePbrGlMeshMaterialRenderer';
15
+
16
+ function makeCamera(): Camera3D {
17
+ return createCamera3D({ far: 100, near: 0.1, projection: { aspect: 1, fovY: Math.PI / 3, kind: 'perspective' } });
18
+ }
19
+
20
+ function makeLights(): Scene3DLightBlock {
21
+ const data = new Float32Array(12);
22
+ data[1] = -1;
23
+ data[4] = 1;
24
+ data[5] = 1;
25
+ data[6] = 1;
26
+ data[8] = 0.1;
27
+ data[9] = 0.1;
28
+ data[10] = 0.1;
29
+ return { ambientCount: 1, data, directionalCount: 1, hemisphereCount: 0, pointCount: 0, spotCount: 0, version: 1 };
30
+ }
31
+
32
+ function makeProxy(): Scene3DRenderProxy {
33
+ const geometry = createBoxMeshGeometry();
34
+ return {
35
+ material: createIridescencePbrMaterial(),
36
+ normalMatrix: createMatrix3() as Matrix3,
37
+ subset: geometry.subsets[0],
38
+ worldMatrix: createMatrix4() as Matrix4,
39
+ };
40
+ }
41
+
42
+ describe('iridescencePbrGlMeshMaterialRenderer', () => {
43
+ it('bind uploads the light block, standard block, and iridescence uniforms', () => {
44
+ const { state, gl } = makeGlScene3DState();
45
+ iridescencePbrGlMeshMaterialRenderer.bind(
46
+ state,
47
+ createIridescencePbrMaterial({ iridescence: 1 }),
48
+ makeLights(),
49
+ makeCamera(),
50
+ );
51
+ expect(gl.calls.some((c) => c.name === 'useProgram')).toBe(true);
52
+ expect(gl.calls.some((c) => c.name === 'uniform4f')).toBe(true);
53
+ expect(gl.calls.some((c) => c.name === 'uniformMatrix4fv')).toBe(true);
54
+ expect(gl.calls.filter((c) => c.name === 'uniform1f').length).toBeGreaterThanOrEqual(3);
55
+ });
56
+
57
+ it('caches the iridescence program under the pbr: namespace', () => {
58
+ const { state } = makeGlScene3DState();
59
+ iridescencePbrGlMeshMaterialRenderer.bind(state, createIridescencePbrMaterial(), makeLights(), makeCamera());
60
+ const cache = getGlScene3DRuntime(state).programCache;
61
+ expect(cache.size).toBe(1);
62
+ expect([...cache.keys()][0].startsWith('pbr:')).toBe(true);
63
+ });
64
+
65
+ it('draw issues an indexed draw after bind', () => {
66
+ const { state, gl } = makeGlScene3DState();
67
+ const proxy = makeProxy();
68
+ iridescencePbrGlMeshMaterialRenderer.bind(state, proxy.material, makeLights(), makeCamera());
69
+ iridescencePbrGlMeshMaterialRenderer.draw(state, proxy, createBoxMeshGeometry());
70
+ expect(gl.calls.some((c) => c.name === 'drawElements')).toBe(true);
71
+ });
72
+
73
+ it('draw is a no-op before bind', () => {
74
+ const { state, gl } = makeGlScene3DState();
75
+ iridescencePbrGlMeshMaterialRenderer.draw(state, makeProxy(), createBoxMeshGeometry());
76
+ expect(gl.calls.some((c) => c.name === 'drawElements')).toBe(false);
77
+ });
78
+ });
79
+
80
+ describe('registerIridescencePbrGlMaterial', () => {
81
+ it('installs the renderer for IridescencePbrMaterialKind', () => {
82
+ const { state } = makeGlScene3DState();
83
+ registerIridescencePbrGlMaterial(state);
84
+ expect(getGlMeshMaterialRenderer(state, IridescencePbrMaterialKind)).toBe(iridescencePbrGlMeshMaterialRenderer);
85
+ });
86
+ });