@vib3code/sdk 2.0.1

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 (258) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/DOCS/BLUEPRINT_EXECUTION_PLAN_2026-01-07.md +34 -0
  3. package/DOCS/CI_TESTING.md +38 -0
  4. package/DOCS/CLI_ONBOARDING.md +75 -0
  5. package/DOCS/CONTROL_REFERENCE.md +64 -0
  6. package/DOCS/DEV_TRACK_ANALYSIS.md +77 -0
  7. package/DOCS/DEV_TRACK_PLAN_2026-01-07.md +42 -0
  8. package/DOCS/DEV_TRACK_SESSION_2026-01-31.md +220 -0
  9. package/DOCS/ENV_SETUP.md +189 -0
  10. package/DOCS/EXPORT_FORMATS.md +417 -0
  11. package/DOCS/GPU_DISPOSAL_GUIDE.md +21 -0
  12. package/DOCS/LICENSING_TIERS.md +275 -0
  13. package/DOCS/MASTER_PLAN_2026-01-31.md +570 -0
  14. package/DOCS/OBS_SETUP_GUIDE.md +98 -0
  15. package/DOCS/PROJECT_SETUP.md +66 -0
  16. package/DOCS/RENDERER_LIFECYCLE.md +40 -0
  17. package/DOCS/REPO_MANIFEST.md +121 -0
  18. package/DOCS/SESSION_014_PLAN.md +195 -0
  19. package/DOCS/SESSION_LOG_2026-01-07.md +56 -0
  20. package/DOCS/STRATEGIC_BLUEPRINT_2026-01-07.md +72 -0
  21. package/DOCS/SYSTEM_AUDIT_2026-01-30.md +738 -0
  22. package/DOCS/SYSTEM_INVENTORY.md +520 -0
  23. package/DOCS/TELEMETRY_EXPORTS.md +34 -0
  24. package/DOCS/WEBGPU_STATUS.md +38 -0
  25. package/DOCS/XR_BENCHMARKS.md +608 -0
  26. package/LICENSE +21 -0
  27. package/README.md +426 -0
  28. package/docs/.nojekyll +0 -0
  29. package/docs/01-dissolution_of_euclidean_hegemony.html +346 -0
  30. package/docs/02-hyperspatial_ego_death.html +346 -0
  31. package/docs/03-post_cartesian_sublime.html +346 -0
  32. package/docs/04-crystalline_void_meditation.html +346 -0
  33. package/docs/05-quantum_decoherence_ballet.html +346 -0
  34. package/docs/06-dissolution_of_euclidean_hegemony.html +346 -0
  35. package/docs/07-hyperspatial_ego_death.html +346 -0
  36. package/docs/08-post_cartesian_sublime.html +346 -0
  37. package/docs/09-crystalline_void_meditation.html +346 -0
  38. package/docs/10-quantum_decoherence_ballet.html +346 -0
  39. package/docs/11-dissolution_of_euclidean_hegemony.html +346 -0
  40. package/docs/12-hyperspatial_ego_death.html +346 -0
  41. package/docs/13-post_cartesian_sublime.html +346 -0
  42. package/docs/index.html +794 -0
  43. package/docs/test-hub.html +441 -0
  44. package/docs/url-state.js +102 -0
  45. package/docs/vib3-exports/01-quantum-quantum-tetrahedron-lattice.html +489 -0
  46. package/docs/vib3-exports/02-quantum-quantum-hypersphere-matrix.html +489 -0
  47. package/docs/vib3-exports/03-quantum-quantum-hypertetra-fractal.html +489 -0
  48. package/docs/vib3-exports/04-faceted-faceted-crystal-structure.html +407 -0
  49. package/docs/vib3-exports/05-faceted-faceted-klein-bottle.html +407 -0
  50. package/docs/vib3-exports/06-faceted-faceted-hypertetra-torus.html +407 -0
  51. package/docs/vib3-exports/07-holographic-holographic-wave-field.html +457 -0
  52. package/docs/vib3-exports/08-holographic-holographic-hypersphere-sphere.html +457 -0
  53. package/docs/vib3-exports/09-holographic-holographic-hypertetra-crystal.html +457 -0
  54. package/docs/vib3-exports/index.html +238 -0
  55. package/docs/webgpu-live.html +702 -0
  56. package/package.json +367 -0
  57. package/src/advanced/AIPresetGenerator.js +777 -0
  58. package/src/advanced/MIDIController.js +703 -0
  59. package/src/advanced/OffscreenWorker.js +1051 -0
  60. package/src/advanced/WebGPUCompute.js +1051 -0
  61. package/src/advanced/WebXRRenderer.js +680 -0
  62. package/src/agent/cli/AgentCLI.js +615 -0
  63. package/src/agent/cli/index.js +14 -0
  64. package/src/agent/index.js +73 -0
  65. package/src/agent/mcp/MCPServer.js +950 -0
  66. package/src/agent/mcp/index.js +9 -0
  67. package/src/agent/mcp/tools.js +548 -0
  68. package/src/agent/telemetry/EventStream.js +669 -0
  69. package/src/agent/telemetry/Instrumentation.js +618 -0
  70. package/src/agent/telemetry/TelemetryExporters.js +427 -0
  71. package/src/agent/telemetry/TelemetryService.js +464 -0
  72. package/src/agent/telemetry/index.js +52 -0
  73. package/src/benchmarks/BenchmarkRunner.js +381 -0
  74. package/src/benchmarks/MetricsCollector.js +299 -0
  75. package/src/benchmarks/index.js +9 -0
  76. package/src/benchmarks/scenes.js +259 -0
  77. package/src/cli/index.js +675 -0
  78. package/src/config/ApiConfig.js +88 -0
  79. package/src/core/CanvasManager.js +217 -0
  80. package/src/core/ErrorReporter.js +117 -0
  81. package/src/core/ParameterMapper.js +333 -0
  82. package/src/core/Parameters.js +396 -0
  83. package/src/core/RendererContracts.js +200 -0
  84. package/src/core/UnifiedResourceManager.js +370 -0
  85. package/src/core/VIB3Engine.js +636 -0
  86. package/src/core/renderers/FacetedRendererAdapter.js +32 -0
  87. package/src/core/renderers/HolographicRendererAdapter.js +29 -0
  88. package/src/core/renderers/QuantumRendererAdapter.js +29 -0
  89. package/src/core/renderers/RendererLifecycleManager.js +63 -0
  90. package/src/creative/ColorPresetsSystem.js +980 -0
  91. package/src/creative/ParameterTimeline.js +1061 -0
  92. package/src/creative/PostProcessingPipeline.js +1113 -0
  93. package/src/creative/TransitionAnimator.js +683 -0
  94. package/src/export/CSSExporter.js +226 -0
  95. package/src/export/CardGeneratorBase.js +279 -0
  96. package/src/export/ExportManager.js +580 -0
  97. package/src/export/FacetedCardGenerator.js +279 -0
  98. package/src/export/HolographicCardGenerator.js +543 -0
  99. package/src/export/LottieExporter.js +552 -0
  100. package/src/export/QuantumCardGenerator.js +315 -0
  101. package/src/export/SVGExporter.js +519 -0
  102. package/src/export/ShaderExporter.js +903 -0
  103. package/src/export/TradingCardGenerator.js +3055 -0
  104. package/src/export/TradingCardManager.js +181 -0
  105. package/src/export/VIB3PackageExporter.js +559 -0
  106. package/src/export/index.js +14 -0
  107. package/src/export/systems/TradingCardSystemFaceted.js +494 -0
  108. package/src/export/systems/TradingCardSystemHolographic.js +452 -0
  109. package/src/export/systems/TradingCardSystemQuantum.js +411 -0
  110. package/src/faceted/FacetedSystem.js +963 -0
  111. package/src/features/CollectionManager.js +433 -0
  112. package/src/gallery/CollectionManager.js +240 -0
  113. package/src/gallery/GallerySystem.js +485 -0
  114. package/src/geometry/GeometryFactory.js +314 -0
  115. package/src/geometry/GeometryLibrary.js +72 -0
  116. package/src/geometry/buffers/BufferBuilder.js +338 -0
  117. package/src/geometry/buffers/index.js +18 -0
  118. package/src/geometry/generators/Crystal.js +420 -0
  119. package/src/geometry/generators/Fractal.js +298 -0
  120. package/src/geometry/generators/KleinBottle.js +197 -0
  121. package/src/geometry/generators/Sphere.js +192 -0
  122. package/src/geometry/generators/Tesseract.js +160 -0
  123. package/src/geometry/generators/Tetrahedron.js +225 -0
  124. package/src/geometry/generators/Torus.js +304 -0
  125. package/src/geometry/generators/Wave.js +341 -0
  126. package/src/geometry/index.js +142 -0
  127. package/src/geometry/warp/HypersphereCore.js +211 -0
  128. package/src/geometry/warp/HypertetraCore.js +386 -0
  129. package/src/geometry/warp/index.js +57 -0
  130. package/src/holograms/HolographicVisualizer.js +1073 -0
  131. package/src/holograms/RealHolographicSystem.js +966 -0
  132. package/src/holograms/variantRegistry.js +69 -0
  133. package/src/integrations/FigmaPlugin.js +854 -0
  134. package/src/integrations/OBSMode.js +754 -0
  135. package/src/integrations/ThreeJsPackage.js +660 -0
  136. package/src/integrations/TouchDesignerExport.js +552 -0
  137. package/src/integrations/frameworks/Vib3React.js +591 -0
  138. package/src/integrations/frameworks/Vib3Svelte.js +654 -0
  139. package/src/integrations/frameworks/Vib3Vue.js +628 -0
  140. package/src/llm/LLMParameterInterface.js +240 -0
  141. package/src/llm/LLMParameterUI.js +577 -0
  142. package/src/math/Mat4x4.js +708 -0
  143. package/src/math/Projection.js +341 -0
  144. package/src/math/Rotor4D.js +637 -0
  145. package/src/math/Vec4.js +476 -0
  146. package/src/math/constants.js +164 -0
  147. package/src/math/index.js +68 -0
  148. package/src/math/projections.js +54 -0
  149. package/src/math/rotations.js +196 -0
  150. package/src/quantum/QuantumEngine.js +906 -0
  151. package/src/quantum/QuantumVisualizer.js +1103 -0
  152. package/src/reactivity/ReactivityConfig.js +499 -0
  153. package/src/reactivity/ReactivityManager.js +586 -0
  154. package/src/reactivity/SpatialInputSystem.js +1783 -0
  155. package/src/reactivity/index.js +93 -0
  156. package/src/render/CommandBuffer.js +465 -0
  157. package/src/render/MultiCanvasBridge.js +340 -0
  158. package/src/render/RenderCommand.js +514 -0
  159. package/src/render/RenderResourceRegistry.js +523 -0
  160. package/src/render/RenderState.js +552 -0
  161. package/src/render/RenderTarget.js +512 -0
  162. package/src/render/ShaderLoader.js +253 -0
  163. package/src/render/ShaderProgram.js +599 -0
  164. package/src/render/UnifiedRenderBridge.js +496 -0
  165. package/src/render/backends/WebGLBackend.js +1108 -0
  166. package/src/render/backends/WebGPUBackend.js +1409 -0
  167. package/src/render/commands/CommandBufferExecutor.js +607 -0
  168. package/src/render/commands/RenderCommandBuffer.js +661 -0
  169. package/src/render/commands/index.js +17 -0
  170. package/src/render/index.js +367 -0
  171. package/src/scene/Disposable.js +498 -0
  172. package/src/scene/MemoryPool.js +618 -0
  173. package/src/scene/Node4D.js +697 -0
  174. package/src/scene/ResourceManager.js +599 -0
  175. package/src/scene/Scene4D.js +540 -0
  176. package/src/scene/index.js +98 -0
  177. package/src/schemas/error.schema.json +84 -0
  178. package/src/schemas/extension.schema.json +88 -0
  179. package/src/schemas/index.js +214 -0
  180. package/src/schemas/parameters.schema.json +142 -0
  181. package/src/schemas/tool-pack.schema.json +44 -0
  182. package/src/schemas/tool-response.schema.json +127 -0
  183. package/src/shaders/common/fullscreen.vert.glsl +5 -0
  184. package/src/shaders/common/fullscreen.vert.wgsl +17 -0
  185. package/src/shaders/common/geometry24.glsl +65 -0
  186. package/src/shaders/common/geometry24.wgsl +54 -0
  187. package/src/shaders/common/rotation4d.glsl +85 -0
  188. package/src/shaders/common/rotation4d.wgsl +86 -0
  189. package/src/shaders/common/uniforms.glsl +44 -0
  190. package/src/shaders/common/uniforms.wgsl +48 -0
  191. package/src/shaders/faceted/faceted.frag.glsl +129 -0
  192. package/src/shaders/faceted/faceted.frag.wgsl +164 -0
  193. package/src/shaders/holographic/holographic.frag.glsl +406 -0
  194. package/src/shaders/holographic/holographic.frag.wgsl +185 -0
  195. package/src/shaders/quantum/quantum.frag.glsl +513 -0
  196. package/src/shaders/quantum/quantum.frag.wgsl +361 -0
  197. package/src/testing/ParallelTestFramework.js +519 -0
  198. package/src/testing/__snapshots__/exportFormats.test.js.snap +24 -0
  199. package/src/testing/exportFormats.test.js +8 -0
  200. package/src/testing/projections.test.js +14 -0
  201. package/src/testing/rotations.test.js +37 -0
  202. package/src/ui/InteractivityMenu.js +516 -0
  203. package/src/ui/StatusManager.js +96 -0
  204. package/src/ui/adaptive/renderers/webgpu/BufferLayout.ts +252 -0
  205. package/src/ui/adaptive/renderers/webgpu/PolytopeInstanceBuffer.ts +144 -0
  206. package/src/ui/adaptive/renderers/webgpu/TripleBufferedUniform.ts +170 -0
  207. package/src/ui/adaptive/renderers/webgpu/WebGPURenderer.ts +735 -0
  208. package/src/ui/adaptive/renderers/webgpu/index.ts +112 -0
  209. package/src/variations/VariationManager.js +431 -0
  210. package/src/viewer/AudioReactivity.js +505 -0
  211. package/src/viewer/CardBending.js +481 -0
  212. package/src/viewer/GalleryUI.js +832 -0
  213. package/src/viewer/ReactivityManager.js +590 -0
  214. package/src/viewer/TradingCardExporter.js +600 -0
  215. package/src/viewer/ViewerPortal.js +374 -0
  216. package/src/viewer/index.js +12 -0
  217. package/src/wasm/WasmLoader.js +296 -0
  218. package/src/wasm/index.js +132 -0
  219. package/tools/agentic/mcpTools.js +88 -0
  220. package/tools/cli/agent-cli.js +92 -0
  221. package/tools/export/formats.js +24 -0
  222. package/tools/math/rotation-baseline.mjs +64 -0
  223. package/tools/shader-sync-verify.js +937 -0
  224. package/tools/telemetry/manifestPipeline.js +141 -0
  225. package/tools/telemetry/telemetryEvents.js +35 -0
  226. package/types/adaptive-sdk.d.ts +185 -0
  227. package/types/advanced/AIPresetGenerator.d.ts +81 -0
  228. package/types/advanced/MIDIController.d.ts +100 -0
  229. package/types/advanced/OffscreenWorker.d.ts +82 -0
  230. package/types/advanced/WebGPUCompute.d.ts +52 -0
  231. package/types/advanced/WebXRRenderer.d.ts +77 -0
  232. package/types/advanced/index.d.ts +46 -0
  233. package/types/core/ErrorReporter.d.ts +50 -0
  234. package/types/core/VIB3Engine.d.ts +204 -0
  235. package/types/creative/ColorPresetsSystem.d.ts +91 -0
  236. package/types/creative/ParameterTimeline.d.ts +74 -0
  237. package/types/creative/PostProcessingPipeline.d.ts +109 -0
  238. package/types/creative/TransitionAnimator.d.ts +71 -0
  239. package/types/creative/index.d.ts +35 -0
  240. package/types/integrations/FigmaPlugin.d.ts +46 -0
  241. package/types/integrations/OBSMode.d.ts +74 -0
  242. package/types/integrations/ThreeJsPackage.d.ts +62 -0
  243. package/types/integrations/TouchDesignerExport.d.ts +36 -0
  244. package/types/integrations/Vib3React.d.ts +74 -0
  245. package/types/integrations/Vib3Svelte.d.ts +63 -0
  246. package/types/integrations/Vib3Vue.d.ts +55 -0
  247. package/types/integrations/index.d.ts +52 -0
  248. package/types/reactivity/SpatialInputSystem.d.ts +173 -0
  249. package/types/reactivity/index.d.ts +394 -0
  250. package/types/render/CommandBuffer.d.ts +169 -0
  251. package/types/render/RenderCommand.d.ts +312 -0
  252. package/types/render/RenderState.d.ts +279 -0
  253. package/types/render/RenderTarget.d.ts +254 -0
  254. package/types/render/ShaderProgram.d.ts +277 -0
  255. package/types/render/UnifiedRenderBridge.d.ts +143 -0
  256. package/types/render/WebGLBackend.d.ts +168 -0
  257. package/types/render/WebGPUBackend.d.ts +186 -0
  258. package/types/render/index.d.ts +141 -0
@@ -0,0 +1,1108 @@
1
+ /**
2
+ * WebGLBackend - WebGL 2.0 rendering backend
3
+ *
4
+ * Implements the rendering interface for WebGL 2.0:
5
+ * - State management with minimal redundant calls
6
+ * - Shader compilation and linking
7
+ * - Buffer and texture management
8
+ * - Draw call execution
9
+ */
10
+
11
+ import { BlendMode, DepthFunc, CullFace, StencilOp } from '../RenderState.js';
12
+ import { TextureFormat, FilterMode, WrapMode } from '../RenderTarget.js';
13
+ import { RenderResourceRegistry } from '../RenderResourceRegistry.js';
14
+
15
+ /**
16
+ * WebGL blend factor mapping
17
+ */
18
+ const BLEND_FACTORS = {
19
+ 'zero': 0,
20
+ 'one': 1,
21
+ 'src_color': 0x0300,
22
+ 'one_minus_src_color': 0x0301,
23
+ 'dst_color': 0x0306,
24
+ 'one_minus_dst_color': 0x0307,
25
+ 'src_alpha': 0x0302,
26
+ 'one_minus_src_alpha': 0x0303,
27
+ 'dst_alpha': 0x0304,
28
+ 'one_minus_dst_alpha': 0x0305,
29
+ 'constant_color': 0x8001,
30
+ 'one_minus_constant_color': 0x8002,
31
+ 'constant_alpha': 0x8003,
32
+ 'one_minus_constant_alpha': 0x8004,
33
+ 'src_alpha_saturate': 0x0308
34
+ };
35
+
36
+ /**
37
+ * WebGL depth function mapping
38
+ */
39
+ const DEPTH_FUNCS = {
40
+ [DepthFunc.NEVER]: 0x0200,
41
+ [DepthFunc.LESS]: 0x0201,
42
+ [DepthFunc.EQUAL]: 0x0202,
43
+ [DepthFunc.LEQUAL]: 0x0203,
44
+ [DepthFunc.GREATER]: 0x0204,
45
+ [DepthFunc.NOTEQUAL]: 0x0205,
46
+ [DepthFunc.GEQUAL]: 0x0206,
47
+ [DepthFunc.ALWAYS]: 0x0207
48
+ };
49
+
50
+ /**
51
+ * WebGL stencil operation mapping
52
+ */
53
+ const STENCIL_OPS = {
54
+ [StencilOp.KEEP]: 0x1E00,
55
+ [StencilOp.ZERO]: 0,
56
+ [StencilOp.REPLACE]: 0x1E01,
57
+ [StencilOp.INCR]: 0x1E02,
58
+ [StencilOp.INCR_WRAP]: 0x8507,
59
+ [StencilOp.DECR]: 0x1E03,
60
+ [StencilOp.DECR_WRAP]: 0x8508,
61
+ [StencilOp.INVERT]: 0x150A
62
+ };
63
+
64
+ /**
65
+ * Primitive type mapping
66
+ */
67
+ const PRIMITIVE_TYPES = {
68
+ 'points': 0,
69
+ 'lines': 1,
70
+ 'line_loop': 2,
71
+ 'line_strip': 3,
72
+ 'triangles': 4,
73
+ 'triangle_strip': 5,
74
+ 'triangle_fan': 6
75
+ };
76
+
77
+ /**
78
+ * WebGLBackend class
79
+ */
80
+ export class WebGLBackend {
81
+ /**
82
+ * @param {WebGL2RenderingContext} gl
83
+ * @param {object} [options]
84
+ */
85
+ constructor(gl, options = {}) {
86
+ /** @type {WebGL2RenderingContext} */
87
+ this.gl = gl;
88
+
89
+ /** @type {boolean} */
90
+ this.debug = options.debug || false;
91
+
92
+ // Current state tracking for minimal state changes
93
+ /** @type {object|null} */
94
+ this._currentState = null;
95
+
96
+ /** @type {object|null} */
97
+ this._currentShader = null;
98
+
99
+ /** @type {object|null} */
100
+ this._currentVAO = null;
101
+
102
+ /** @type {object|null} */
103
+ this._currentRenderTarget = null;
104
+
105
+ /** @type {Map<number, object>} */
106
+ this._boundTextures = new Map();
107
+
108
+ // Caches
109
+ /** @type {Map<object, WebGLProgram>} */
110
+ this._shaderCache = new Map();
111
+
112
+ /** @type {Map<object, WebGLFramebuffer>} */
113
+ this._framebufferCache = new Map();
114
+
115
+ /** @type {Map<object, WebGLVertexArrayObject>} */
116
+ this._vaoCache = new Map();
117
+
118
+ /** @type {RenderResourceRegistry} */
119
+ this._resources = options.resourceRegistry || new RenderResourceRegistry();
120
+
121
+ // WebGL constants
122
+ this._initConstants();
123
+
124
+ // Extensions
125
+ this._initExtensions();
126
+
127
+ // Statistics
128
+ this._stats = {
129
+ drawCalls: 0,
130
+ stateChanges: 0,
131
+ shaderSwitches: 0,
132
+ textureBinds: 0
133
+ };
134
+ }
135
+
136
+ /**
137
+ * Initialize WebGL constants
138
+ * @private
139
+ */
140
+ _initConstants() {
141
+ const gl = this.gl;
142
+
143
+ this.BLEND_FACTORS = {
144
+ 'zero': gl.ZERO,
145
+ 'one': gl.ONE,
146
+ 'src_color': gl.SRC_COLOR,
147
+ 'one_minus_src_color': gl.ONE_MINUS_SRC_COLOR,
148
+ 'dst_color': gl.DST_COLOR,
149
+ 'one_minus_dst_color': gl.ONE_MINUS_DST_COLOR,
150
+ 'src_alpha': gl.SRC_ALPHA,
151
+ 'one_minus_src_alpha': gl.ONE_MINUS_SRC_ALPHA,
152
+ 'dst_alpha': gl.DST_ALPHA,
153
+ 'one_minus_dst_alpha': gl.ONE_MINUS_DST_ALPHA,
154
+ 'constant_color': gl.CONSTANT_COLOR,
155
+ 'one_minus_constant_color': gl.ONE_MINUS_CONSTANT_COLOR,
156
+ 'constant_alpha': gl.CONSTANT_ALPHA,
157
+ 'one_minus_constant_alpha': gl.ONE_MINUS_CONSTANT_ALPHA,
158
+ 'src_alpha_saturate': gl.SRC_ALPHA_SATURATE
159
+ };
160
+
161
+ this.DEPTH_FUNCS = {
162
+ [DepthFunc.NEVER]: gl.NEVER,
163
+ [DepthFunc.LESS]: gl.LESS,
164
+ [DepthFunc.EQUAL]: gl.EQUAL,
165
+ [DepthFunc.LEQUAL]: gl.LEQUAL,
166
+ [DepthFunc.GREATER]: gl.GREATER,
167
+ [DepthFunc.NOTEQUAL]: gl.NOTEQUAL,
168
+ [DepthFunc.GEQUAL]: gl.GEQUAL,
169
+ [DepthFunc.ALWAYS]: gl.ALWAYS
170
+ };
171
+
172
+ this.STENCIL_OPS = {
173
+ [StencilOp.KEEP]: gl.KEEP,
174
+ [StencilOp.ZERO]: gl.ZERO,
175
+ [StencilOp.REPLACE]: gl.REPLACE,
176
+ [StencilOp.INCR]: gl.INCR,
177
+ [StencilOp.INCR_WRAP]: gl.INCR_WRAP,
178
+ [StencilOp.DECR]: gl.DECR,
179
+ [StencilOp.DECR_WRAP]: gl.DECR_WRAP,
180
+ [StencilOp.INVERT]: gl.INVERT
181
+ };
182
+
183
+ this.PRIMITIVE_TYPES = {
184
+ 'points': gl.POINTS,
185
+ 'lines': gl.LINES,
186
+ 'line_loop': gl.LINE_LOOP,
187
+ 'line_strip': gl.LINE_STRIP,
188
+ 'triangles': gl.TRIANGLES,
189
+ 'triangle_strip': gl.TRIANGLE_STRIP,
190
+ 'triangle_fan': gl.TRIANGLE_FAN
191
+ };
192
+
193
+ this.TEXTURE_FORMATS = {
194
+ [TextureFormat.RGBA8]: { internalFormat: gl.RGBA8, format: gl.RGBA, type: gl.UNSIGNED_BYTE },
195
+ [TextureFormat.RGBA16F]: { internalFormat: gl.RGBA16F, format: gl.RGBA, type: gl.HALF_FLOAT },
196
+ [TextureFormat.RGBA32F]: { internalFormat: gl.RGBA32F, format: gl.RGBA, type: gl.FLOAT },
197
+ [TextureFormat.RGB8]: { internalFormat: gl.RGB8, format: gl.RGB, type: gl.UNSIGNED_BYTE },
198
+ [TextureFormat.RG8]: { internalFormat: gl.RG8, format: gl.RG, type: gl.UNSIGNED_BYTE },
199
+ [TextureFormat.R8]: { internalFormat: gl.R8, format: gl.RED, type: gl.UNSIGNED_BYTE },
200
+ [TextureFormat.DEPTH16]: { internalFormat: gl.DEPTH_COMPONENT16, format: gl.DEPTH_COMPONENT, type: gl.UNSIGNED_SHORT },
201
+ [TextureFormat.DEPTH24]: { internalFormat: gl.DEPTH_COMPONENT24, format: gl.DEPTH_COMPONENT, type: gl.UNSIGNED_INT },
202
+ [TextureFormat.DEPTH32F]: { internalFormat: gl.DEPTH_COMPONENT32F, format: gl.DEPTH_COMPONENT, type: gl.FLOAT },
203
+ [TextureFormat.DEPTH24_STENCIL8]: { internalFormat: gl.DEPTH24_STENCIL8, format: gl.DEPTH_STENCIL, type: gl.UNSIGNED_INT_24_8 },
204
+ [TextureFormat.DEPTH32F_STENCIL8]: { internalFormat: gl.DEPTH32F_STENCIL8, format: gl.DEPTH_STENCIL, type: gl.FLOAT_32_UNSIGNED_INT_24_8_REV }
205
+ };
206
+
207
+ this.FILTER_MODES = {
208
+ [FilterMode.NEAREST]: gl.NEAREST,
209
+ [FilterMode.LINEAR]: gl.LINEAR,
210
+ [FilterMode.NEAREST_MIPMAP_NEAREST]: gl.NEAREST_MIPMAP_NEAREST,
211
+ [FilterMode.LINEAR_MIPMAP_NEAREST]: gl.LINEAR_MIPMAP_NEAREST,
212
+ [FilterMode.NEAREST_MIPMAP_LINEAR]: gl.NEAREST_MIPMAP_LINEAR,
213
+ [FilterMode.LINEAR_MIPMAP_LINEAR]: gl.LINEAR_MIPMAP_LINEAR
214
+ };
215
+
216
+ this.WRAP_MODES = {
217
+ [WrapMode.REPEAT]: gl.REPEAT,
218
+ [WrapMode.CLAMP_TO_EDGE]: gl.CLAMP_TO_EDGE,
219
+ [WrapMode.MIRRORED_REPEAT]: gl.MIRRORED_REPEAT
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Initialize WebGL extensions
225
+ * @private
226
+ */
227
+ _initExtensions() {
228
+ const gl = this.gl;
229
+
230
+ this.extensions = {
231
+ colorBufferFloat: gl.getExtension('EXT_color_buffer_float'),
232
+ floatBlend: gl.getExtension('EXT_float_blend'),
233
+ textureFilterAnisotropic: gl.getExtension('EXT_texture_filter_anisotropic'),
234
+ debugRendererInfo: gl.getExtension('WEBGL_debug_renderer_info'),
235
+ loseContext: gl.getExtension('WEBGL_lose_context'),
236
+ parallelShaderCompile: gl.getExtension('KHR_parallel_shader_compile')
237
+ };
238
+
239
+ if (this.debug && this.extensions.debugRendererInfo) {
240
+ console.log('WebGL Renderer:', gl.getParameter(this.extensions.debugRendererInfo.UNMASKED_RENDERER_WEBGL));
241
+ console.log('WebGL Vendor:', gl.getParameter(this.extensions.debugRendererInfo.UNMASKED_VENDOR_WEBGL));
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Clear framebuffer
247
+ * @param {object} command - ClearCommand
248
+ */
249
+ clear(command) {
250
+ const gl = this.gl;
251
+ let mask = 0;
252
+
253
+ if (command.clearColor) {
254
+ gl.clearColor(...command.colorValue);
255
+ mask |= gl.COLOR_BUFFER_BIT;
256
+ }
257
+
258
+ if (command.clearDepth) {
259
+ gl.clearDepth(command.depthValue);
260
+ mask |= gl.DEPTH_BUFFER_BIT;
261
+ }
262
+
263
+ if (command.clearStencil) {
264
+ gl.clearStencil(command.stencilValue);
265
+ mask |= gl.STENCIL_BUFFER_BIT;
266
+ }
267
+
268
+ if (mask) {
269
+ gl.clear(mask);
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Set render state
275
+ * @param {object} state - RenderState
276
+ */
277
+ setState(state) {
278
+ const gl = this.gl;
279
+ this._stats.stateChanges++;
280
+
281
+ // Blend state
282
+ this._setBlendState(state.blend);
283
+
284
+ // Depth state
285
+ this._setDepthState(state.depth);
286
+
287
+ // Stencil state
288
+ this._setStencilState(state.stencil);
289
+
290
+ // Rasterizer state
291
+ this._setRasterizerState(state.rasterizer);
292
+
293
+ // Color mask
294
+ gl.colorMask(...state.colorMask);
295
+
296
+ this._currentState = state;
297
+ }
298
+
299
+ /**
300
+ * Set blend state
301
+ * @private
302
+ */
303
+ _setBlendState(blend) {
304
+ const gl = this.gl;
305
+
306
+ if (blend.enabled) {
307
+ gl.enable(gl.BLEND);
308
+ gl.blendFuncSeparate(
309
+ this.BLEND_FACTORS[blend.srcRGB],
310
+ this.BLEND_FACTORS[blend.dstRGB],
311
+ this.BLEND_FACTORS[blend.srcAlpha],
312
+ this.BLEND_FACTORS[blend.dstAlpha]
313
+ );
314
+ if (blend.color.some(c => c !== 0)) {
315
+ gl.blendColor(...blend.color);
316
+ }
317
+ } else {
318
+ gl.disable(gl.BLEND);
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Set depth state
324
+ * @private
325
+ */
326
+ _setDepthState(depth) {
327
+ const gl = this.gl;
328
+
329
+ if (depth.testEnabled) {
330
+ gl.enable(gl.DEPTH_TEST);
331
+ gl.depthFunc(this.DEPTH_FUNCS[depth.func]);
332
+ } else {
333
+ gl.disable(gl.DEPTH_TEST);
334
+ }
335
+
336
+ gl.depthMask(depth.writeEnabled);
337
+ gl.depthRange(depth.near, depth.far);
338
+ }
339
+
340
+ /**
341
+ * Set stencil state
342
+ * @private
343
+ */
344
+ _setStencilState(stencil) {
345
+ const gl = this.gl;
346
+
347
+ if (stencil.enabled) {
348
+ gl.enable(gl.STENCIL_TEST);
349
+ gl.stencilFunc(
350
+ this.DEPTH_FUNCS[stencil.func] || gl.ALWAYS,
351
+ stencil.ref,
352
+ stencil.mask
353
+ );
354
+ gl.stencilOp(
355
+ this.STENCIL_OPS[stencil.failOp],
356
+ this.STENCIL_OPS[stencil.depthFailOp],
357
+ this.STENCIL_OPS[stencil.passOp]
358
+ );
359
+ } else {
360
+ gl.disable(gl.STENCIL_TEST);
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Set rasterizer state
366
+ * @private
367
+ */
368
+ _setRasterizerState(rasterizer) {
369
+ const gl = this.gl;
370
+
371
+ // Culling
372
+ if (rasterizer.cullFace === CullFace.NONE) {
373
+ gl.disable(gl.CULL_FACE);
374
+ } else {
375
+ gl.enable(gl.CULL_FACE);
376
+ switch (rasterizer.cullFace) {
377
+ case CullFace.FRONT:
378
+ gl.cullFace(gl.FRONT);
379
+ break;
380
+ case CullFace.BACK:
381
+ gl.cullFace(gl.BACK);
382
+ break;
383
+ case CullFace.FRONT_AND_BACK:
384
+ gl.cullFace(gl.FRONT_AND_BACK);
385
+ break;
386
+ }
387
+ }
388
+
389
+ // Front face
390
+ gl.frontFace(rasterizer.frontFaceCCW ? gl.CCW : gl.CW);
391
+
392
+ // Scissor
393
+ if (rasterizer.scissorEnabled) {
394
+ gl.enable(gl.SCISSOR_TEST);
395
+ gl.scissor(...rasterizer.scissorRect);
396
+ } else {
397
+ gl.disable(gl.SCISSOR_TEST);
398
+ }
399
+
400
+ // Line width (clamped by implementation)
401
+ gl.lineWidth(Math.min(rasterizer.lineWidth, gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE)[1]));
402
+
403
+ // Polygon offset for depth bias
404
+ if (rasterizer.depthBiasEnabled) {
405
+ gl.enable(gl.POLYGON_OFFSET_FILL);
406
+ gl.polygonOffset(rasterizer.depthBiasFactor, rasterizer.depthBiasUnits);
407
+ } else {
408
+ gl.disable(gl.POLYGON_OFFSET_FILL);
409
+ }
410
+ }
411
+
412
+ /**
413
+ * Set viewport
414
+ * @param {number} x
415
+ * @param {number} y
416
+ * @param {number} width
417
+ * @param {number} height
418
+ */
419
+ setViewport(x, y, width, height) {
420
+ this.gl.viewport(x, y, width, height);
421
+ }
422
+
423
+ /**
424
+ * Bind shader program
425
+ * @param {object} shader - ShaderProgram
426
+ */
427
+ bindShader(shader) {
428
+ if (this._currentShader === shader) return;
429
+
430
+ const gl = this.gl;
431
+ let program = this._shaderCache.get(shader);
432
+
433
+ if (!program) {
434
+ program = this._compileShader(shader);
435
+ if (program) {
436
+ this._shaderCache.set(shader, program);
437
+ shader.setHandle(program);
438
+ }
439
+ }
440
+
441
+ if (program) {
442
+ gl.useProgram(program);
443
+ this._currentShader = shader;
444
+ this._stats.shaderSwitches++;
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Compile shader program
450
+ * @private
451
+ */
452
+ _compileShader(shader) {
453
+ const gl = this.gl;
454
+
455
+ // Compile vertex shader
456
+ const vertexShader = gl.createShader(gl.VERTEX_SHADER);
457
+ gl.shaderSource(vertexShader, shader.vertexSource.getProcessedCode());
458
+ gl.compileShader(vertexShader);
459
+
460
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
461
+ const error = gl.getShaderInfoLog(vertexShader);
462
+ shader.setError(`Vertex shader error: ${error}`);
463
+ gl.deleteShader(vertexShader);
464
+ return null;
465
+ }
466
+
467
+ // Compile fragment shader
468
+ const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
469
+ gl.shaderSource(fragmentShader, shader.fragmentSource.getProcessedCode());
470
+ gl.compileShader(fragmentShader);
471
+
472
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
473
+ const error = gl.getShaderInfoLog(fragmentShader);
474
+ shader.setError(`Fragment shader error: ${error}`);
475
+ gl.deleteShader(vertexShader);
476
+ gl.deleteShader(fragmentShader);
477
+ return null;
478
+ }
479
+
480
+ // Link program
481
+ const program = gl.createProgram();
482
+ gl.attachShader(program, vertexShader);
483
+ gl.attachShader(program, fragmentShader);
484
+
485
+ // Bind attribute locations
486
+ for (const attr of shader.attributes) {
487
+ gl.bindAttribLocation(program, attr.location, attr.name);
488
+ }
489
+
490
+ gl.linkProgram(program);
491
+
492
+ // Clean up shaders (attached to program, can be deleted)
493
+ gl.deleteShader(vertexShader);
494
+ gl.deleteShader(fragmentShader);
495
+
496
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
497
+ const error = gl.getProgramInfoLog(program);
498
+ shader.setError(`Link error: ${error}`);
499
+ gl.deleteProgram(program);
500
+ return null;
501
+ }
502
+
503
+ this._resources.register('program', program, () => gl.deleteProgram(program));
504
+
505
+ // Cache uniform locations
506
+ for (const uniform of shader.uniforms) {
507
+ uniform.location = gl.getUniformLocation(program, uniform.name);
508
+ }
509
+
510
+ return program;
511
+ }
512
+
513
+ /**
514
+ * Set shader uniform
515
+ * @param {string} name
516
+ * @param {any} value
517
+ * @param {string} [type]
518
+ */
519
+ setUniform(name, value, type = null) {
520
+ const gl = this.gl;
521
+
522
+ if (!this._currentShader) return;
523
+
524
+ const uniform = this._currentShader.getUniform(name);
525
+ const location = uniform?.location ?? gl.getUniformLocation(
526
+ this._shaderCache.get(this._currentShader),
527
+ name
528
+ );
529
+
530
+ if (location === null) return;
531
+
532
+ // Check cache
533
+ if (!this._currentShader.setUniformValue(name, value)) {
534
+ return; // Value unchanged
535
+ }
536
+
537
+ const uniformType = type || uniform?.type || this._inferUniformType(value);
538
+
539
+ switch (uniformType) {
540
+ case 'float':
541
+ gl.uniform1f(location, value);
542
+ break;
543
+ case 'vec2':
544
+ gl.uniform2fv(location, value);
545
+ break;
546
+ case 'vec3':
547
+ gl.uniform3fv(location, value);
548
+ break;
549
+ case 'vec4':
550
+ gl.uniform4fv(location, value);
551
+ break;
552
+ case 'int':
553
+ case 'sampler2D':
554
+ case 'samplerCube':
555
+ case 'sampler3D':
556
+ gl.uniform1i(location, value);
557
+ break;
558
+ case 'ivec2':
559
+ gl.uniform2iv(location, value);
560
+ break;
561
+ case 'ivec3':
562
+ gl.uniform3iv(location, value);
563
+ break;
564
+ case 'ivec4':
565
+ gl.uniform4iv(location, value);
566
+ break;
567
+ case 'bool':
568
+ gl.uniform1i(location, value ? 1 : 0);
569
+ break;
570
+ case 'mat2':
571
+ gl.uniformMatrix2fv(location, false, value);
572
+ break;
573
+ case 'mat3':
574
+ gl.uniformMatrix3fv(location, false, value);
575
+ break;
576
+ case 'mat4':
577
+ gl.uniformMatrix4fv(location, false, value);
578
+ break;
579
+ default:
580
+ console.warn(`Unknown uniform type: ${uniformType}`);
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Infer uniform type from value
586
+ * @private
587
+ */
588
+ _inferUniformType(value) {
589
+ if (typeof value === 'number') {
590
+ return Number.isInteger(value) ? 'int' : 'float';
591
+ }
592
+ if (typeof value === 'boolean') {
593
+ return 'bool';
594
+ }
595
+ if (Array.isArray(value) || ArrayBuffer.isView(value)) {
596
+ const length = value.length;
597
+ if (length === 2) return 'vec2';
598
+ if (length === 3) return 'vec3';
599
+ if (length === 4) return 'vec4';
600
+ if (length === 9) return 'mat3';
601
+ if (length === 16) return 'mat4';
602
+ }
603
+ return 'float';
604
+ }
605
+
606
+ /**
607
+ * Bind texture to slot
608
+ * @param {object} texture
609
+ * @param {number} slot
610
+ */
611
+ bindTexture(texture, slot = 0) {
612
+ const gl = this.gl;
613
+
614
+ gl.activeTexture(gl.TEXTURE0 + slot);
615
+
616
+ if (!texture) {
617
+ gl.bindTexture(gl.TEXTURE_2D, null);
618
+ this._boundTextures.delete(slot);
619
+ return;
620
+ }
621
+
622
+ let glTexture = texture._handle;
623
+
624
+ if (!glTexture) {
625
+ glTexture = this._createTexture(texture);
626
+ texture._handle = glTexture;
627
+ }
628
+
629
+ gl.bindTexture(texture._target || gl.TEXTURE_2D, glTexture);
630
+ this._boundTextures.set(slot, texture);
631
+ this._stats.textureBinds++;
632
+ }
633
+
634
+ /**
635
+ * Create WebGL texture
636
+ * @private
637
+ */
638
+ _createTexture(texture) {
639
+ const gl = this.gl;
640
+ const glTexture = gl.createTexture();
641
+ const target = texture._target || gl.TEXTURE_2D;
642
+
643
+ this._resources.register('texture', glTexture, () => gl.deleteTexture(glTexture));
644
+
645
+ gl.bindTexture(target, glTexture);
646
+
647
+ // Set parameters
648
+ gl.texParameteri(target, gl.TEXTURE_MIN_FILTER,
649
+ this.FILTER_MODES[texture.minFilter] || gl.LINEAR);
650
+ gl.texParameteri(target, gl.TEXTURE_MAG_FILTER,
651
+ this.FILTER_MODES[texture.magFilter] || gl.LINEAR);
652
+ gl.texParameteri(target, gl.TEXTURE_WRAP_S,
653
+ this.WRAP_MODES[texture.wrapS] || gl.CLAMP_TO_EDGE);
654
+ gl.texParameteri(target, gl.TEXTURE_WRAP_T,
655
+ this.WRAP_MODES[texture.wrapT] || gl.CLAMP_TO_EDGE);
656
+
657
+ return glTexture;
658
+ }
659
+
660
+ /**
661
+ * Bind vertex array object
662
+ * @param {object} vao
663
+ */
664
+ bindVertexArray(vao) {
665
+ if (this._currentVAO === vao) return;
666
+
667
+ const gl = this.gl;
668
+ let glVAO = this._vaoCache.get(vao);
669
+
670
+ if (!glVAO && vao) {
671
+ glVAO = this._createVAO(vao);
672
+ this._vaoCache.set(vao, glVAO);
673
+ vao._handle = glVAO;
674
+ }
675
+
676
+ gl.bindVertexArray(glVAO || null);
677
+ this._currentVAO = vao;
678
+ }
679
+
680
+ /**
681
+ * Create vertex array object
682
+ * @private
683
+ */
684
+ _createVAO(vao) {
685
+ const gl = this.gl;
686
+ const glVAO = gl.createVertexArray();
687
+ gl.bindVertexArray(glVAO);
688
+
689
+ this._resources.register('vao', glVAO, () => gl.deleteVertexArray(glVAO));
690
+
691
+ // Set up attributes from VAO descriptor
692
+ for (const attr of vao.attributes || []) {
693
+ gl.enableVertexAttribArray(attr.location);
694
+
695
+ // Bind the attribute's buffer
696
+ if (attr.buffer?._handle) {
697
+ gl.bindBuffer(gl.ARRAY_BUFFER, attr.buffer._handle);
698
+ }
699
+
700
+ // Set vertex attribute pointer
701
+ gl.vertexAttribPointer(
702
+ attr.location,
703
+ attr.size || 4,
704
+ attr.type || gl.FLOAT,
705
+ attr.normalized || false,
706
+ attr.stride || 0,
707
+ attr.offset || 0
708
+ );
709
+
710
+ // Instance divisor for instanced rendering
711
+ if (attr.divisor) {
712
+ gl.vertexAttribDivisor(attr.location, attr.divisor);
713
+ }
714
+ }
715
+
716
+ // Bind index buffer if present
717
+ if (vao.indexBuffer?._handle) {
718
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vao.indexBuffer._handle);
719
+ }
720
+
721
+ gl.bindVertexArray(null);
722
+ return glVAO;
723
+ }
724
+
725
+ /**
726
+ * Bind render target (framebuffer)
727
+ * @param {object|null} target
728
+ */
729
+ bindRenderTarget(target) {
730
+ const gl = this.gl;
731
+
732
+ if (target === null) {
733
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
734
+ this._currentRenderTarget = null;
735
+ return;
736
+ }
737
+
738
+ let framebuffer = this._framebufferCache.get(target);
739
+
740
+ if (!framebuffer || target._needsReallocation) {
741
+ framebuffer = this._createFramebuffer(target);
742
+ this._framebufferCache.set(target, framebuffer);
743
+ target.setHandle(framebuffer);
744
+ target._needsReallocation = false;
745
+ }
746
+
747
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
748
+ this._currentRenderTarget = target;
749
+
750
+ // Set viewport to match target size
751
+ gl.viewport(0, 0, target.width, target.height);
752
+ }
753
+
754
+ /**
755
+ * Create framebuffer
756
+ * @private
757
+ */
758
+ _createFramebuffer(target) {
759
+ const gl = this.gl;
760
+ const framebuffer = gl.createFramebuffer();
761
+
762
+ this._resources.register('framebuffer', framebuffer, () => gl.deleteFramebuffer(framebuffer));
763
+
764
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
765
+
766
+ // Color attachments
767
+ const drawBuffers = [];
768
+ target._colorTextures = [];
769
+
770
+ for (let i = 0; i < target.colorAttachments.length; i++) {
771
+ const attachment = target.colorAttachments[i];
772
+ const formatInfo = this.TEXTURE_FORMATS[attachment.format];
773
+
774
+ if (attachment.useTexture) {
775
+ const texture = gl.createTexture();
776
+ this._resources.register('texture', texture, () => gl.deleteTexture(texture));
777
+ gl.bindTexture(gl.TEXTURE_2D, texture);
778
+ gl.texImage2D(
779
+ gl.TEXTURE_2D, 0,
780
+ formatInfo.internalFormat,
781
+ target.width, target.height, 0,
782
+ formatInfo.format,
783
+ formatInfo.type,
784
+ null
785
+ );
786
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
787
+ this.FILTER_MODES[attachment.minFilter]);
788
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,
789
+ this.FILTER_MODES[attachment.magFilter]);
790
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S,
791
+ this.WRAP_MODES[attachment.wrapS]);
792
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T,
793
+ this.WRAP_MODES[attachment.wrapT]);
794
+
795
+ gl.framebufferTexture2D(
796
+ gl.FRAMEBUFFER,
797
+ gl.COLOR_ATTACHMENT0 + i,
798
+ gl.TEXTURE_2D,
799
+ texture, 0
800
+ );
801
+
802
+ target._colorTextures.push({ _handle: texture, _target: gl.TEXTURE_2D });
803
+ } else {
804
+ const renderbuffer = gl.createRenderbuffer();
805
+ this._resources.register('renderbuffer', renderbuffer, () => gl.deleteRenderbuffer(renderbuffer));
806
+ gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
807
+
808
+ if (attachment.samples > 1) {
809
+ gl.renderbufferStorageMultisample(
810
+ gl.RENDERBUFFER,
811
+ attachment.samples,
812
+ formatInfo.internalFormat,
813
+ target.width, target.height
814
+ );
815
+ } else {
816
+ gl.renderbufferStorage(
817
+ gl.RENDERBUFFER,
818
+ formatInfo.internalFormat,
819
+ target.width, target.height
820
+ );
821
+ }
822
+
823
+ gl.framebufferRenderbuffer(
824
+ gl.FRAMEBUFFER,
825
+ gl.COLOR_ATTACHMENT0 + i,
826
+ gl.RENDERBUFFER,
827
+ renderbuffer
828
+ );
829
+ }
830
+
831
+ drawBuffers.push(gl.COLOR_ATTACHMENT0 + i);
832
+ }
833
+
834
+ // Set draw buffers for MRT
835
+ if (drawBuffers.length > 0) {
836
+ gl.drawBuffers(drawBuffers);
837
+ }
838
+
839
+ // Depth attachment
840
+ if (target.depthAttachment) {
841
+ const depthAttachment = target.depthAttachment;
842
+ const formatInfo = this.TEXTURE_FORMATS[depthAttachment.format];
843
+ const isDepthStencil = depthAttachment.format.includes('stencil');
844
+
845
+ if (depthAttachment.useTexture) {
846
+ const texture = gl.createTexture();
847
+ this._resources.register('texture', texture, () => gl.deleteTexture(texture));
848
+ gl.bindTexture(gl.TEXTURE_2D, texture);
849
+ gl.texImage2D(
850
+ gl.TEXTURE_2D, 0,
851
+ formatInfo.internalFormat,
852
+ target.width, target.height, 0,
853
+ formatInfo.format,
854
+ formatInfo.type,
855
+ null
856
+ );
857
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
858
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
859
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
860
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
861
+
862
+ gl.framebufferTexture2D(
863
+ gl.FRAMEBUFFER,
864
+ isDepthStencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT,
865
+ gl.TEXTURE_2D,
866
+ texture, 0
867
+ );
868
+
869
+ target._depthTexture = { _handle: texture, _target: gl.TEXTURE_2D };
870
+ } else {
871
+ const renderbuffer = gl.createRenderbuffer();
872
+ this._resources.register('renderbuffer', renderbuffer, () => gl.deleteRenderbuffer(renderbuffer));
873
+ gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
874
+
875
+ if (depthAttachment.samples > 1) {
876
+ gl.renderbufferStorageMultisample(
877
+ gl.RENDERBUFFER,
878
+ depthAttachment.samples,
879
+ formatInfo.internalFormat,
880
+ target.width, target.height
881
+ );
882
+ } else {
883
+ gl.renderbufferStorage(
884
+ gl.RENDERBUFFER,
885
+ formatInfo.internalFormat,
886
+ target.width, target.height
887
+ );
888
+ }
889
+
890
+ gl.framebufferRenderbuffer(
891
+ gl.FRAMEBUFFER,
892
+ isDepthStencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT,
893
+ gl.RENDERBUFFER,
894
+ renderbuffer
895
+ );
896
+ }
897
+ }
898
+
899
+ // Check completeness
900
+ const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
901
+ if (status !== gl.FRAMEBUFFER_COMPLETE) {
902
+ console.error('Framebuffer incomplete:', this._getFramebufferStatusName(status));
903
+ }
904
+
905
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
906
+ return framebuffer;
907
+ }
908
+
909
+ /**
910
+ * Get framebuffer status name for debugging
911
+ * @private
912
+ */
913
+ _getFramebufferStatusName(status) {
914
+ const gl = this.gl;
915
+ const names = {
916
+ [gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT]: 'INCOMPLETE_ATTACHMENT',
917
+ [gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT]: 'INCOMPLETE_MISSING_ATTACHMENT',
918
+ [gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS]: 'INCOMPLETE_DIMENSIONS',
919
+ [gl.FRAMEBUFFER_UNSUPPORTED]: 'UNSUPPORTED',
920
+ [gl.FRAMEBUFFER_INCOMPLETE_MULTISAMPLE]: 'INCOMPLETE_MULTISAMPLE'
921
+ };
922
+ return names[status] || `UNKNOWN (${status})`;
923
+ }
924
+
925
+ /**
926
+ * Draw non-indexed primitives
927
+ * @param {object} command - DrawCommand
928
+ */
929
+ draw(command) {
930
+ const gl = this.gl;
931
+ const primitive = this.PRIMITIVE_TYPES[command.primitive] || gl.TRIANGLES;
932
+
933
+ gl.drawArrays(primitive, command.firstVertex, command.vertexCount);
934
+ this._stats.drawCalls++;
935
+ }
936
+
937
+ /**
938
+ * Draw indexed primitives
939
+ * @param {object} command - DrawIndexedCommand
940
+ */
941
+ drawIndexed(command) {
942
+ const gl = this.gl;
943
+ const primitive = this.PRIMITIVE_TYPES[command.primitive] || gl.TRIANGLES;
944
+ const indexType = command.indexType === 'uint32' ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT;
945
+ const byteOffset = command.firstIndex * (command.indexType === 'uint32' ? 4 : 2);
946
+
947
+ gl.drawElements(primitive, command.indexCount, indexType, byteOffset);
948
+ this._stats.drawCalls++;
949
+ }
950
+
951
+ /**
952
+ * Draw instanced primitives
953
+ * @param {object} command - DrawInstancedCommand
954
+ */
955
+ drawInstanced(command) {
956
+ const gl = this.gl;
957
+ const primitive = this.PRIMITIVE_TYPES[command.primitive] || gl.TRIANGLES;
958
+
959
+ gl.drawArraysInstanced(
960
+ primitive,
961
+ command.firstVertex,
962
+ command.vertexCount,
963
+ command.instanceCount
964
+ );
965
+ this._stats.drawCalls++;
966
+ }
967
+
968
+ /**
969
+ * Draw indexed instanced primitives
970
+ * @param {object} command - DrawIndexedInstancedCommand
971
+ */
972
+ drawIndexedInstanced(command) {
973
+ const gl = this.gl;
974
+ const primitive = this.PRIMITIVE_TYPES[command.primitive] || gl.TRIANGLES;
975
+ const indexType = command.indexType === 'uint32' ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT;
976
+ const byteOffset = command.firstIndex * (command.indexType === 'uint32' ? 4 : 2);
977
+
978
+ gl.drawElementsInstanced(
979
+ primitive,
980
+ command.indexCount,
981
+ indexType,
982
+ byteOffset,
983
+ command.instanceCount
984
+ );
985
+ this._stats.drawCalls++;
986
+ }
987
+
988
+ /**
989
+ * Create buffer
990
+ * @param {object} descriptor
991
+ * @returns {object}
992
+ */
993
+ createBuffer(descriptor) {
994
+ const gl = this.gl;
995
+ const buffer = gl.createBuffer();
996
+ const target = descriptor.usage === 'index' ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;
997
+ const usage = descriptor.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;
998
+
999
+ gl.bindBuffer(target, buffer);
1000
+
1001
+ if (descriptor.data) {
1002
+ gl.bufferData(target, descriptor.data, usage);
1003
+ } else if (descriptor.size) {
1004
+ gl.bufferData(target, descriptor.size, usage);
1005
+ }
1006
+
1007
+ this._resources.register('buffer', buffer, () => gl.deleteBuffer(buffer), {
1008
+ bytes: descriptor.data?.byteLength || descriptor.size || 0
1009
+ });
1010
+
1011
+ return {
1012
+ _handle: buffer,
1013
+ _target: target,
1014
+ size: descriptor.data?.byteLength || descriptor.size || 0,
1015
+ usage: descriptor.usage || 'vertex'
1016
+ };
1017
+ }
1018
+
1019
+ /**
1020
+ * Update buffer data
1021
+ * @param {object} buffer
1022
+ * @param {ArrayBufferView} data
1023
+ * @param {number} [offset]
1024
+ */
1025
+ updateBuffer(buffer, data, offset = 0) {
1026
+ const gl = this.gl;
1027
+ gl.bindBuffer(buffer._target, buffer._handle);
1028
+ gl.bufferSubData(buffer._target, offset, data);
1029
+ }
1030
+
1031
+ /**
1032
+ * Delete buffer
1033
+ * @param {object} buffer
1034
+ */
1035
+ deleteBuffer(buffer) {
1036
+ if (buffer?._handle) {
1037
+ this.gl.deleteBuffer(buffer._handle);
1038
+ this._resources.release('buffer', buffer._handle);
1039
+ buffer._handle = null;
1040
+ }
1041
+ }
1042
+
1043
+ /**
1044
+ * Get render statistics
1045
+ * @returns {object}
1046
+ */
1047
+ getStats() {
1048
+ return { ...this._stats, resources: this._resources.getStats() };
1049
+ }
1050
+
1051
+ /**
1052
+ * Reset statistics
1053
+ */
1054
+ resetStats() {
1055
+ this._stats = {
1056
+ drawCalls: 0,
1057
+ stateChanges: 0,
1058
+ shaderSwitches: 0,
1059
+ textureBinds: 0
1060
+ };
1061
+ }
1062
+
1063
+ /**
1064
+ * Dispose all resources
1065
+ */
1066
+ dispose() {
1067
+ this._resources.disposeAll();
1068
+ this._shaderCache.clear();
1069
+ this._framebufferCache.clear();
1070
+ this._vaoCache.clear();
1071
+
1072
+ this._currentState = null;
1073
+ this._currentShader = null;
1074
+ this._currentVAO = null;
1075
+ this._currentRenderTarget = null;
1076
+ this._boundTextures.clear();
1077
+ }
1078
+ }
1079
+
1080
+ /**
1081
+ * Create WebGL 2.0 backend from canvas
1082
+ * @param {HTMLCanvasElement} canvas
1083
+ * @param {object} [options]
1084
+ * @returns {WebGLBackend|null}
1085
+ */
1086
+ export function createWebGLBackend(canvas, options = {}) {
1087
+ const contextOptions = {
1088
+ alpha: options.alpha ?? true,
1089
+ depth: options.depth ?? true,
1090
+ stencil: options.stencil ?? false,
1091
+ antialias: options.antialias ?? true,
1092
+ premultipliedAlpha: options.premultipliedAlpha ?? true,
1093
+ preserveDrawingBuffer: options.preserveDrawingBuffer ?? false,
1094
+ powerPreference: options.powerPreference ?? 'high-performance',
1095
+ failIfMajorPerformanceCaveat: options.failIfMajorPerformanceCaveat ?? false
1096
+ };
1097
+
1098
+ const gl = canvas.getContext('webgl2', contextOptions);
1099
+
1100
+ if (!gl) {
1101
+ console.error('WebGL 2.0 not supported');
1102
+ return null;
1103
+ }
1104
+
1105
+ return new WebGLBackend(gl, options);
1106
+ }
1107
+
1108
+ export default WebGLBackend;