action-engine-js 1.0.0

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 (93) hide show
  1. package/LICENSE +45 -0
  2. package/README.md +348 -0
  3. package/actionengine/3rdparty/goblin/goblin.js +9609 -0
  4. package/actionengine/3rdparty/goblin/goblin.min.js +5 -0
  5. package/actionengine/camera/actioncamera.js +90 -0
  6. package/actionengine/camera/cameracollisionhandler.js +69 -0
  7. package/actionengine/character/actioncharacter.js +360 -0
  8. package/actionengine/character/actioncharacter3D.js +61 -0
  9. package/actionengine/core/app.js +430 -0
  10. package/actionengine/debug/basedebugpanel.js +858 -0
  11. package/actionengine/display/canvasmanager.js +75 -0
  12. package/actionengine/display/gl/programmanager.js +570 -0
  13. package/actionengine/display/gl/shaders/lineshader.js +118 -0
  14. package/actionengine/display/gl/shaders/objectshader.js +1756 -0
  15. package/actionengine/display/gl/shaders/particleshader.js +43 -0
  16. package/actionengine/display/gl/shaders/shadowshader.js +319 -0
  17. package/actionengine/display/gl/shaders/spriteshader.js +100 -0
  18. package/actionengine/display/gl/shaders/watershader.js +67 -0
  19. package/actionengine/display/graphics/actionmodel3D.js +191 -0
  20. package/actionengine/display/graphics/actionsprite3D.js +230 -0
  21. package/actionengine/display/graphics/lighting/actiondirectionalshadowlight.js +864 -0
  22. package/actionengine/display/graphics/lighting/actionlight.js +211 -0
  23. package/actionengine/display/graphics/lighting/actionomnidirectionalshadowlight.js +862 -0
  24. package/actionengine/display/graphics/lighting/lightingconstants.js +263 -0
  25. package/actionengine/display/graphics/lighting/lightmanager.js +789 -0
  26. package/actionengine/display/graphics/renderableobject.js +44 -0
  27. package/actionengine/display/graphics/renderers/actionrenderer2D.js +341 -0
  28. package/actionengine/display/graphics/renderers/actionrenderer3D/actionrenderer3D.js +655 -0
  29. package/actionengine/display/graphics/renderers/actionrenderer3D/canvasmanager3D.js +82 -0
  30. package/actionengine/display/graphics/renderers/actionrenderer3D/debugrenderer3D.js +493 -0
  31. package/actionengine/display/graphics/renderers/actionrenderer3D/objectrenderer3D.js +790 -0
  32. package/actionengine/display/graphics/renderers/actionrenderer3D/spriteRenderer3D.js +266 -0
  33. package/actionengine/display/graphics/renderers/actionrenderer3D/sunrenderer3D.js +140 -0
  34. package/actionengine/display/graphics/renderers/actionrenderer3D/waterrenderer3D.js +173 -0
  35. package/actionengine/display/graphics/renderers/actionrenderer3D/weatherrenderer3D.js +87 -0
  36. package/actionengine/display/graphics/texture/proceduraltexture.js +192 -0
  37. package/actionengine/display/graphics/texture/texturemanager.js +242 -0
  38. package/actionengine/display/graphics/texture/textureregistry.js +177 -0
  39. package/actionengine/input/actionscrollablearea.js +1405 -0
  40. package/actionengine/input/inputhandler.js +1647 -0
  41. package/actionengine/math/geometry/geometrybuilder.js +161 -0
  42. package/actionengine/math/geometry/glbexporter.js +364 -0
  43. package/actionengine/math/geometry/glbloader.js +722 -0
  44. package/actionengine/math/geometry/modelcodegenerator.js +97 -0
  45. package/actionengine/math/geometry/triangle.js +33 -0
  46. package/actionengine/math/geometry/triangleutils.js +34 -0
  47. package/actionengine/math/mathutils.js +25 -0
  48. package/actionengine/math/matrix4.js +785 -0
  49. package/actionengine/math/physics/actionphysics.js +108 -0
  50. package/actionengine/math/physics/actionphysicsobject3D.js +164 -0
  51. package/actionengine/math/physics/actionphysicsworld3D.js +238 -0
  52. package/actionengine/math/physics/actionraycast.js +129 -0
  53. package/actionengine/math/physics/shapes/actionphysicsbox3D.js +158 -0
  54. package/actionengine/math/physics/shapes/actionphysicscapsule3D.js +200 -0
  55. package/actionengine/math/physics/shapes/actionphysicscompoundshape3D.js +147 -0
  56. package/actionengine/math/physics/shapes/actionphysicscone3D.js +126 -0
  57. package/actionengine/math/physics/shapes/actionphysicsconvexshape3D.js +72 -0
  58. package/actionengine/math/physics/shapes/actionphysicscylinder3D.js +117 -0
  59. package/actionengine/math/physics/shapes/actionphysicsmesh3D.js +74 -0
  60. package/actionengine/math/physics/shapes/actionphysicsplane3D.js +100 -0
  61. package/actionengine/math/physics/shapes/actionphysicssphere3D.js +95 -0
  62. package/actionengine/math/quaternion.js +61 -0
  63. package/actionengine/math/vector2.js +277 -0
  64. package/actionengine/math/vector3.js +318 -0
  65. package/actionengine/math/viewfrustum.js +136 -0
  66. package/actionengine/network/ACTIONNETREADME.md +810 -0
  67. package/actionengine/network/client/ActionNetManager.js +802 -0
  68. package/actionengine/network/client/ActionNetManagerGUI.js +1709 -0
  69. package/actionengine/network/client/ActionNetManagerP2P.js +1537 -0
  70. package/actionengine/network/client/SyncSystem.js +422 -0
  71. package/actionengine/network/p2p/ActionNetPeer.js +142 -0
  72. package/actionengine/network/p2p/ActionNetTrackerClient.js +623 -0
  73. package/actionengine/network/p2p/DataConnection.js +282 -0
  74. package/actionengine/network/p2p/README.md +510 -0
  75. package/actionengine/network/p2p/example.html +502 -0
  76. package/actionengine/network/server/ActionNetServer.js +577 -0
  77. package/actionengine/network/server/ActionNetServerSSL.js +579 -0
  78. package/actionengine/network/server/ActionNetServerUtils.js +458 -0
  79. package/actionengine/network/server/SERVERREADME.md +314 -0
  80. package/actionengine/network/server/package-lock.json +35 -0
  81. package/actionengine/network/server/package.json +13 -0
  82. package/actionengine/network/server/start.bat +27 -0
  83. package/actionengine/network/server/start.sh +25 -0
  84. package/actionengine/network/server/startwss.bat +27 -0
  85. package/actionengine/sound/audiomanager.js +1589 -0
  86. package/actionengine/sound/soundfont/ACTIONSOUNDFONT_README.md +205 -0
  87. package/actionengine/sound/soundfont/actionparser.js +718 -0
  88. package/actionengine/sound/soundfont/actionreverb.js +252 -0
  89. package/actionengine/sound/soundfont/actionsoundfont.js +543 -0
  90. package/actionengine/sound/soundfont/sf2playerlicence.txt +29 -0
  91. package/actionengine/sound/soundfont/soundfont.js +2 -0
  92. package/dist/action-engine.min.js +328 -0
  93. package/package.json +35 -0
@@ -0,0 +1,655 @@
1
+ // actionengine/display/graphics/renderers/actionrenderer3D/actionrenderer3D.js
2
+ class ActionRenderer3D {
3
+ constructor(canvas) {
4
+ // Initialize canvas manager
5
+ this.canvasManager = new CanvasManager3D(canvas);
6
+
7
+ // Get GL context from canvas manager
8
+ this.gl = this.canvasManager.getContext();
9
+
10
+ // Initialize all managers and renderers
11
+ this.programManager = new ProgramManager(this.gl, this.canvasManager.isWebGL2());
12
+
13
+ // Use the new LightManager instead of separate lighting and shadow managers
14
+ this.lightManager = new LightManager(this.gl, this.canvasManager.isWebGL2(), this.programManager);
15
+
16
+ this.debugRenderer = new DebugRenderer3D(this.gl, this.programManager, this.lightManager);
17
+ this.weatherRenderer = new WeatherRenderer3D(this.gl, this.programManager);
18
+ this.sunRenderer = new SunRenderer3D(this.gl, this.programManager);
19
+ // Create texture manager and texture array before other renderers
20
+ this.textureManager = new TextureManager(this.gl);
21
+ this.textureArray = this.textureManager.textureArray;
22
+
23
+ this.objectRenderer = new ObjectRenderer3D(this, this.gl, this.programManager, this.lightManager);
24
+ this.waterRenderer = new WaterRenderer3D(this.gl, this.programManager);
25
+ this.spriteRenderer = new SpriteRenderer3D(this.gl, this.programManager, this.canvasManager.isWebGL2());
26
+
27
+ // Time tracking
28
+ this.startTime = performance.now();
29
+ this.currentTime = 0;
30
+
31
+ // Shadow settings
32
+ this.shadowsEnabled = true; // Enable shadows by default
33
+ }
34
+
35
+ render(renderData) {
36
+ const {
37
+ camera,
38
+ renderableObjects,
39
+ showDebugPanel,
40
+ weatherSystem
41
+ } = renderData;
42
+
43
+ // Initialize the shadow textures before first use
44
+ if (!this._initializedShadows) {
45
+ try {
46
+ // Initialize shadows for all shader types
47
+ this._initShadowsForAllShaders();
48
+ this._initializedShadows = true;
49
+ } catch (error) {
50
+ console.error('Error initializing shadows:', error);
51
+ }
52
+ }
53
+
54
+ // Update lights through the light manager
55
+ const lightingChanged = this.lightManager.update();
56
+
57
+ // If lighting changed, we need to reinitialize shadows to ensure textures are properly bound
58
+ if (lightingChanged) {
59
+ try {
60
+ this._initShadowsForAllShaders();
61
+ } catch (error) {
62
+ console.error('Error reinitializing shadows after lighting change:', error);
63
+ }
64
+ }
65
+
66
+ // No need to update shadow mapping separately - it's now handled by the light manager
67
+
68
+ this.currentTime = (performance.now() - this.startTime) / 1000.0;
69
+
70
+ // Cache current variant name
71
+ if (!this._cachedVariant) {
72
+ this._cachedVariant = this.programManager.getCurrentVariant();
73
+
74
+ // Set clear color based on current variant
75
+ if (this._cachedVariant === "virtualboy") {
76
+ this.canvasManager.setClearColor(0.0, 0.0, 0.0, 1.0); // Black
77
+ } else {
78
+ this.canvasManager.setClearColor(0.529, 0.808, 0.922, 1.0); // Original blue
79
+ }
80
+ }
81
+
82
+ // Check if shader variant changed
83
+ const currentVariant = this.programManager.getCurrentVariant();
84
+ if (currentVariant !== this._cachedVariant) {
85
+ this._cachedVariant = currentVariant;
86
+
87
+ // Update clear color if variant changed
88
+ if (this._cachedVariant === "virtualboy") {
89
+ this.canvasManager.setClearColor(0.0, 0.0, 0.0, 1.0); // Black
90
+ } else {
91
+ this.canvasManager.setClearColor(0.529, 0.808, 0.922, 1.0); // Original blue
92
+ }
93
+ }
94
+
95
+ // Create empty arrays for different object types
96
+ let waterObjects = [];
97
+ let spriteObjects = [];
98
+ let nonWaterObjects = [];
99
+
100
+ // Fast pre-sorting of objects for better performance
101
+ if (renderableObjects?.length) {
102
+ for (const object of renderableObjects) {
103
+ if (typeof Ocean !== 'undefined' && object instanceof Ocean) {
104
+ waterObjects.push(object);
105
+ } else if (object && object.constructor.name === 'ActionSprite3D') {
106
+ // All ActionSprite3D objects go to sprite renderer (billboard and non-billboard)
107
+ spriteObjects.push(object);
108
+ } else if (object) {
109
+ nonWaterObjects.push(object);
110
+ }
111
+ }
112
+ }
113
+
114
+ // MAIN RENDER PASS
115
+ this.canvasManager.resetToDefaultFramebuffer();
116
+ this.canvasManager.clear();
117
+
118
+ // Collect all objects into batch first
119
+ // This will call updateVisual() on each object, ensuring triangles are up-to-date
120
+ for (const object of nonWaterObjects) {
121
+ this.objectRenderer.queue(object, camera, this.currentTime);
122
+ }
123
+
124
+ // SHADOW MAP PASS (only if shadows are enabled)
125
+ // Now that objects have been queued and their triangles updated,
126
+ // we can render accurate shadows
127
+ if (this.shadowsEnabled && nonWaterObjects.length > 0) {
128
+ // Render all objects to shadow maps for all lights
129
+ this.lightManager.renderShadowMaps(nonWaterObjects);
130
+
131
+ // Ensure we're back to the default framebuffer after shadow rendering
132
+ this.canvasManager.resetToDefaultFramebuffer();
133
+ }
134
+
135
+ // Prepare for main rendering with shadows
136
+ if (this.shadowsEnabled) {
137
+ try {
138
+ // Get the current shader program
139
+ const program = this.programManager.getObjectProgram();
140
+ if (!program) {
141
+ console.warn('Cannot setup shadows: shader program not available');
142
+ return;
143
+ }
144
+
145
+ // Use the shader program
146
+ this.gl.useProgram(program);
147
+
148
+ // Apply all lights to the shader with texture binding persistence
149
+ // First, make sure we re-bind the shadow textures to their units
150
+ const SHADOW_MAP_TEXTURE_UNIT = 4;
151
+ const POINT_SHADOW_TEXTURE_UNIT = 3;
152
+
153
+ // Get main directional light and point light
154
+ const mainLight = this.lightManager.getMainDirectionalLight();
155
+ const pointLight = this.lightManager.pointLights.length > 0 ? this.lightManager.pointLights[0] : null;
156
+
157
+ // Ensure directional shadow map is bound
158
+ if (mainLight && mainLight.shadowTexture) {
159
+ this.gl.activeTexture(this.gl.TEXTURE0 + SHADOW_MAP_TEXTURE_UNIT);
160
+ this.gl.bindTexture(this.gl.TEXTURE_2D, mainLight.shadowTexture);
161
+ }
162
+
163
+ // Ensure point light shadow map is bound
164
+ if (pointLight && pointLight.shadowTexture) {
165
+ this.gl.activeTexture(this.gl.TEXTURE0 + POINT_SHADOW_TEXTURE_UNIT);
166
+ if (this.canvasManager.isWebGL2()) {
167
+ this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, pointLight.shadowTexture);
168
+ } else if (pointLight.shadowTextures && pointLight.shadowTextures.length > 0) {
169
+ this.gl.bindTexture(this.gl.TEXTURE_2D, pointLight.shadowTextures[0]);
170
+ }
171
+ }
172
+
173
+ // Now apply all lights' uniforms to the shader
174
+ this.lightManager.applyLightsToShader(program);
175
+
176
+ // Get shadow-specific uniform locations
177
+ const uniformShadowSoftness = this.gl.getUniformLocation(program, 'uShadowSoftness');
178
+ const uniformPCFSize = this.gl.getUniformLocation(program, 'uPCFSize');
179
+ const uniformPCFEnabled = this.gl.getUniformLocation(program, 'uPCFEnabled');
180
+
181
+ // Set shadow softness uniform
182
+ if (uniformShadowSoftness !== null) {
183
+ const softness = this.lightManager.constants.SHADOW_FILTERING.SOFTNESS.value;
184
+ this.gl.uniform1f(uniformShadowSoftness, softness);
185
+ }
186
+
187
+ // Set PCF size uniform
188
+ if (uniformPCFSize !== null) {
189
+ const pcfSize = this.lightManager.constants.SHADOW_FILTERING.PCF.SIZE.value;
190
+ this.gl.uniform1i(uniformPCFSize, pcfSize);
191
+ }
192
+
193
+ // Set PCF enabled uniform
194
+ if (uniformPCFEnabled !== null) {
195
+ const pcfEnabled = this.lightManager.constants.SHADOW_FILTERING.PCF.ENABLED ? 1 : 0;
196
+ this.gl.uniform1i(uniformPCFEnabled, pcfEnabled);
197
+ }
198
+ } catch (error) {
199
+ console.error('Error setting up shadows:', error);
200
+ }
201
+ }
202
+
203
+ // Then render everything in one batch
204
+ this.objectRenderer.render();
205
+
206
+ // Then render water objects last
207
+ for (const object of waterObjects) {
208
+ this.waterRenderer.render(camera, this.currentTime, object);
209
+ }
210
+
211
+ // Render weather if it exists
212
+ if (weatherSystem) {
213
+ this.weatherRenderer.render(weatherSystem, camera);
214
+ }
215
+
216
+ // Draw the sun (only if directional light is enabled)
217
+ if (this.lightManager.isMainDirectionalLightEnabled()) {
218
+ const mainLight = this.lightManager.getMainDirectionalLight();
219
+ const lightPos = mainLight ? mainLight.getPosition() : new Vector3(0, 5000, 0);
220
+ const isVirtualBoyMode = (this._cachedVariant === "virtualboy");
221
+ this.sunRenderer.render(camera, lightPos, isVirtualBoyMode);
222
+ }
223
+
224
+ // Render sprites (ActionSprite3D objects - both billboard and non-billboard)
225
+ if (spriteObjects.length > 0) {
226
+ // Get matrices for sprite rendering
227
+ const projectionMatrix = Matrix4.create();
228
+ const viewMatrix = Matrix4.create();
229
+
230
+ // Create projection matrix using same parameters as ObjectRenderer3D for consistent depth values
231
+ const aspectRatio = Game.WIDTH / Game.HEIGHT;
232
+ Matrix4.perspective(
233
+ projectionMatrix,
234
+ camera.fov,
235
+ aspectRatio,
236
+ 0.1, // Same near plane as ObjectRenderer3D
237
+ 10000.0 // Same far plane as ObjectRenderer3D
238
+ );
239
+
240
+ // Create view matrix
241
+ Matrix4.lookAt(
242
+ viewMatrix,
243
+ camera.position.toArray(),
244
+ camera.target.toArray(),
245
+ camera.up.toArray()
246
+ );
247
+
248
+ // Render sprites
249
+ this.spriteRenderer.render(spriteObjects, camera, projectionMatrix, viewMatrix);
250
+ }
251
+
252
+ // Debug visualization if enabled
253
+ if (showDebugPanel && camera) {
254
+ // Find character in renderableObjects for debug visualization
255
+ const character = renderableObjects?.find(obj =>
256
+ obj.constructor.name === 'ThirdPersonActionCharacter' ||
257
+ obj.constructor.name === 'ActionCharacter'
258
+ );
259
+ this.debugRenderer.drawDebugLines(camera, character, this.currentTime);
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Toggle directional light on or off
265
+ * @param {boolean} [enabled] - If provided, explicitly sets directional light on/off
266
+ * @returns {boolean} - Current state of directional light
267
+ */
268
+ toggleDirectionalLight(enabled) {
269
+ // If enabled parameter is provided, use it, otherwise toggle
270
+ if (enabled !== undefined) {
271
+ this.lightManager.setMainDirectionalLightEnabled(enabled);
272
+ } else {
273
+ const currentState = this.lightManager.isMainDirectionalLightEnabled();
274
+ this.lightManager.setMainDirectionalLightEnabled(!currentState);
275
+ }
276
+
277
+ // The state may have changed, so ensure shader is updated
278
+ const program = this.programManager.getObjectProgram();
279
+ if (program) {
280
+ // Get current directional light state
281
+ const isEnabled = this.lightManager.isMainDirectionalLightEnabled();
282
+ const hasLight = this.lightManager.getMainDirectionalLight() !== null;
283
+
284
+ // Use the shader program
285
+ this.gl.useProgram(program);
286
+
287
+ // Set shadows enabled flag based on light status
288
+ const shadowsEnabledLoc = this.gl.getUniformLocation(program, 'uShadowsEnabled');
289
+ if (shadowsEnabledLoc !== null) {
290
+ this.gl.uniform1i(shadowsEnabledLoc, (isEnabled && hasLight) ? 1 : 0);
291
+ }
292
+
293
+ // If directional light was just enabled, initialize all shadow-related uniforms
294
+ if (isEnabled && hasLight) {
295
+ this._initShadowsForAllShaders();
296
+ }
297
+ }
298
+
299
+ // Return the new state
300
+ return this.lightManager.isMainDirectionalLightEnabled();
301
+ }
302
+
303
+ /**
304
+ * Toggle shadows on or off
305
+ */
306
+ toggleShadows() {
307
+ this.shadowsEnabled = !this.shadowsEnabled;
308
+ console.log(`Shadows ${this.shadowsEnabled ? 'enabled' : 'disabled'}`);
309
+
310
+ // Update the current shader program with the new shadow state
311
+ const program = this.programManager.getObjectProgram();
312
+ const variant = this.programManager.getCurrentVariant();
313
+
314
+ if (program) {
315
+ // Use the shader program
316
+ this.gl.useProgram(program);
317
+
318
+ // Set shadows enabled flag based on current state
319
+ const shadowEnabledLoc = this.gl.getUniformLocation(program, 'uShadowsEnabled');
320
+ if (shadowEnabledLoc !== null) {
321
+ this.gl.uniform1i(shadowEnabledLoc, this.shadowsEnabled ? 1 : 0);
322
+ console.log(`Set uShadowsEnabled=${this.shadowsEnabled ? 1 : 0} for ${variant} shader variant`);
323
+ }
324
+ }
325
+
326
+ // If re-enabling shadows, make sure the settings are properly reinitialized
327
+ if (this.shadowsEnabled) {
328
+ this._initShadowsForAllShaders();
329
+ }
330
+
331
+ return this.shadowsEnabled;
332
+ }
333
+
334
+ /**
335
+ * Set shadow quality using presets from constants
336
+ * @param {number} quality - Shadow quality preset index (0-3: low, medium, high, ultra)
337
+ */
338
+ setShadowQuality(quality) {
339
+ const maxPreset = lightingConstants.SHADOW_QUALITY_PRESETS.length - 1;
340
+ if (quality < 0 || quality > maxPreset) {
341
+ console.warn(`Shadow quality must be between 0 and ${maxPreset}`);
342
+ return;
343
+ }
344
+
345
+ // Apply the quality preset through the light manager
346
+ this.lightManager.setShadowQuality(quality);
347
+
348
+ const presetName = lightingConstants.SHADOW_QUALITY_PRESETS[quality].name;
349
+ console.log(`Shadow quality set to ${presetName}`);
350
+ }
351
+
352
+ /**
353
+ * Initialize shadow maps for all shader types
354
+ * This ensures both default and PBR shaders can render shadows
355
+ */
356
+ _initShadowsForAllShaders() {
357
+ console.log("_initShadowsForAllShaders called");
358
+ // Constant for shadow texture unit
359
+ const SHADOW_MAP_TEXTURE_UNIT = 4;
360
+ const POINT_SHADOW_TEXTURE_UNIT = 3; // Point shadows use texture unit 3
361
+
362
+ // Get main directional light
363
+ const mainLight = this.lightManager.getMainDirectionalLight();
364
+
365
+ // Always check for point lights regardless of directional light existence
366
+ const pointLight = this.lightManager.pointLights.length > 0 ? this.lightManager.pointLights[0] : null;
367
+
368
+ // Only proceed with directional light stuff if it exists
369
+ if (mainLight) {
370
+ // Verify that the light and its texture are properly initialized
371
+ if (!mainLight.shadowTexture) {
372
+ console.log("Reinitializing shadow map for directional light");
373
+ mainLight.setupShadowMap();
374
+ }
375
+
376
+ // Pre-bind the directional shadow map texture to unit 4
377
+ this.gl.activeTexture(this.gl.TEXTURE0 + SHADOW_MAP_TEXTURE_UNIT);
378
+ // Force unbind texture first to ensure complete refresh
379
+ this.gl.bindTexture(this.gl.TEXTURE_2D, null);
380
+ // Now bind the shadow texture - this ensures proper rebinding when toggling
381
+ this.gl.bindTexture(this.gl.TEXTURE_2D, mainLight.shadowTexture);
382
+ console.log("Bound directional light shadow texture to unit 4");
383
+ }
384
+
385
+ // Now set up point light shadow if available
386
+ if (pointLight) {
387
+ // Verify the point light texture is present
388
+ if (!pointLight.shadowTexture) {
389
+ console.log("Initializing shadow map for point light");
390
+ pointLight.setupShadowMap();
391
+ }
392
+
393
+ // Pre-bind the point light shadow cubemap texture to unit 3
394
+ this.gl.activeTexture(this.gl.TEXTURE0 + POINT_SHADOW_TEXTURE_UNIT);
395
+
396
+ // Force unbind to ensure complete refresh
397
+ if (this.isWebGL2) {
398
+ this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, null);
399
+ // For WebGL2, bind as CUBE_MAP
400
+ this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, pointLight.shadowTexture);
401
+ console.log("Bound point light shadow cubemap texture to unit 3");
402
+ } else {
403
+ // For WebGL1, we have individual textures
404
+ this.gl.bindTexture(this.gl.TEXTURE_2D, null);
405
+ if (pointLight.shadowTextures && pointLight.shadowTextures.length > 0) {
406
+ this.gl.bindTexture(this.gl.TEXTURE_2D, pointLight.shadowTextures[0]);
407
+ console.log("Bound point light shadow texture (face 0) to unit 3 (WebGL1)");
408
+ }
409
+ }
410
+ }
411
+
412
+ // Previous code already bound the point light texture if available
413
+
414
+ // For now, we just need to initialize the current shader variant
415
+ const currentProgram = this.programManager.getObjectProgram();
416
+
417
+ // Initialize shadows for current program
418
+ if (currentProgram) {
419
+ // Use this shader program
420
+ this.gl.useProgram(currentProgram);
421
+
422
+ // === DIRECTIONAL LIGHT UNIFORMS ===
423
+ if (mainLight) {
424
+ // Get shadow uniforms for directional light
425
+ const shadowMapLoc = this.gl.getUniformLocation(currentProgram, 'uShadowMap');
426
+ const shadowEnabledLoc = this.gl.getUniformLocation(currentProgram, 'uShadowsEnabled');
427
+ const lightSpaceMatrixLoc = this.gl.getUniformLocation(currentProgram, 'uLightSpaceMatrix');
428
+
429
+ // Set shadow map texture uniform
430
+ if (shadowMapLoc !== null) {
431
+ this.gl.uniform1i(shadowMapLoc, SHADOW_MAP_TEXTURE_UNIT);
432
+ console.log("Set uShadowMap to texture unit", SHADOW_MAP_TEXTURE_UNIT);
433
+ }
434
+
435
+ // Set shadow enabled flag
436
+ if (shadowEnabledLoc !== null) {
437
+ this.gl.uniform1i(shadowEnabledLoc, 1);
438
+ console.log("Set uShadowsEnabled to 1");
439
+ }
440
+
441
+ // Set initial light space matrix if available
442
+ if (lightSpaceMatrixLoc !== null) {
443
+ const lightSpaceMatrix = this.lightManager.getLightSpaceMatrix();
444
+ if (lightSpaceMatrix) {
445
+ this.gl.uniformMatrix4fv(lightSpaceMatrixLoc, false, lightSpaceMatrix);
446
+ }
447
+ }
448
+ } else {
449
+ // If no directional light, explicitly disable shadows
450
+ const shadowEnabledLoc = this.gl.getUniformLocation(currentProgram, 'uShadowsEnabled');
451
+ if (shadowEnabledLoc !== null) {
452
+ this.gl.uniform1i(shadowEnabledLoc, 0);
453
+ console.log("Set uShadowsEnabled to 0 - no directional light");
454
+ }
455
+ }
456
+
457
+ // === POINT LIGHT UNIFORMS ===
458
+ if (pointLight) {
459
+ // Get point light shadow uniforms
460
+ const pointShadowMapLoc = this.gl.getUniformLocation(currentProgram, 'uPointShadowMap');
461
+ const pointShadowsEnabledLoc = this.gl.getUniformLocation(currentProgram, 'uPointShadowsEnabled');
462
+ const pointLightCountLoc = this.gl.getUniformLocation(currentProgram, 'uPointLightCount');
463
+ const farPlaneLoc = this.gl.getUniformLocation(currentProgram, 'uFarPlane');
464
+
465
+ // Set point light count
466
+ if (pointLightCountLoc !== null) {
467
+ this.gl.uniform1i(pointLightCountLoc, 1); // We have one point light
468
+ console.log("Set uPointLightCount to 1");
469
+ }
470
+
471
+ // Set point shadow map texture uniform
472
+ if (pointShadowMapLoc !== null) {
473
+ this.gl.uniform1i(pointShadowMapLoc, POINT_SHADOW_TEXTURE_UNIT);
474
+ console.log("Set uPointShadowMap to texture unit", POINT_SHADOW_TEXTURE_UNIT);
475
+ }
476
+
477
+ // Set point shadows enabled flag
478
+ if (pointShadowsEnabledLoc !== null) {
479
+ this.gl.uniform1i(pointShadowsEnabledLoc, 1); // Enable point light shadows
480
+ console.log("Set uPointShadowsEnabled to 1");
481
+ }
482
+
483
+ // Set far plane for point light shadow mapping
484
+ if (farPlaneLoc !== null) {
485
+ this.gl.uniform1f(farPlaneLoc, 500.0); // Match the value in pointLight.applyToShader
486
+ console.log("Set uFarPlane to 500.0");
487
+ }
488
+ } else {
489
+ // No point light, set count to 0
490
+ const pointLightCountLoc = this.gl.getUniformLocation(currentProgram, 'uPointLightCount');
491
+ if (pointLightCountLoc !== null) {
492
+ this.gl.uniform1i(pointLightCountLoc, 0);
493
+ console.log("Set uPointLightCount to 0 - no point light");
494
+ }
495
+ }
496
+
497
+ // === COMMON SHADOW PARAMETERS ===
498
+ // These apply to both directional and point lights
499
+ const shadowBiasLoc = this.gl.getUniformLocation(currentProgram, 'uShadowBias');
500
+ const shadowMapSizeLoc = this.gl.getUniformLocation(currentProgram, 'uShadowMapSize');
501
+ const shadowSoftnessLoc = this.gl.getUniformLocation(currentProgram, 'uShadowSoftness');
502
+ const pcfSizeLoc = this.gl.getUniformLocation(currentProgram, 'uPCFSize');
503
+ const pcfEnabledLoc = this.gl.getUniformLocation(currentProgram, 'uPCFEnabled');
504
+
505
+ // Set shadow bias
506
+ if (shadowBiasLoc !== null) {
507
+ this.gl.uniform1f(shadowBiasLoc, this.lightManager.getShadowBias());
508
+ }
509
+
510
+ // Set shadow map size
511
+ if (shadowMapSizeLoc !== null) {
512
+ this.gl.uniform1f(shadowMapSizeLoc, this.lightManager.getShadowMapSize());
513
+ }
514
+
515
+ // Set shadow softness
516
+ if (shadowSoftnessLoc !== null) {
517
+ const softness = this.lightManager.constants.SHADOW_FILTERING.SOFTNESS.value;
518
+ this.gl.uniform1f(shadowSoftnessLoc, softness);
519
+ }
520
+
521
+ // Set PCF size
522
+ if (pcfSizeLoc !== null) {
523
+ const pcfSize = this.lightManager.constants.SHADOW_FILTERING.PCF.SIZE.value;
524
+ this.gl.uniform1i(pcfSizeLoc, pcfSize);
525
+ }
526
+
527
+ // Set PCF enabled
528
+ if (pcfEnabledLoc !== null) {
529
+ const pcfEnabled = this.lightManager.constants.SHADOW_FILTERING.PCF.ENABLED ? 1 : 0;
530
+ this.gl.uniform1i(pcfEnabledLoc, pcfEnabled);
531
+ }
532
+ }
533
+ }
534
+
535
+
536
+ /**
537
+ * Debug shadow uniform locations in all shaders
538
+ */
539
+ debugShadowUniforms() {
540
+ // Make sure GL context exists
541
+ if (!this.gl) {
542
+ console.warn('GL context not available for shadow uniform debugging');
543
+ return;
544
+ }
545
+ const gl = this.gl;
546
+
547
+ // Get current object shader program
548
+ const program = this.programManager.getObjectProgram();
549
+ const variant = this.programManager.getCurrentVariant();
550
+
551
+ if (!program) {
552
+ console.warn('Object shader program not available for debugging');
553
+ return;
554
+ }
555
+
556
+ // Check current shader program
557
+ try {
558
+ console.log(`\nChecking shadow uniforms for current shader variant '${variant}':\n`);
559
+
560
+ if (program) {
561
+ // Check uniform locations directly
562
+ const shadowMapLoc = gl.getUniformLocation(program, 'uShadowMap');
563
+ const lightSpaceMatrixLoc = gl.getUniformLocation(program, 'uLightSpaceMatrix');
564
+ const shadowsEnabledLoc = gl.getUniformLocation(program, 'uShadowsEnabled');
565
+ const shadowBiasLoc = gl.getUniformLocation(program, 'uShadowBias');
566
+ const shadowMapSizeLoc = gl.getUniformLocation(program, 'uShadowMapSize');
567
+ const shadowSoftnessLoc = gl.getUniformLocation(program, 'uShadowSoftness');
568
+ const pcfSizeLoc = gl.getUniformLocation(program, 'uPCFSize');
569
+ const pcfEnabledLoc = gl.getUniformLocation(program, 'uPCFEnabled');
570
+
571
+ console.log(`Direct check for shader variant '${variant}':\n`);
572
+ console.log('uShadowMap:', shadowMapLoc);
573
+ console.log('uLightSpaceMatrix:', lightSpaceMatrixLoc);
574
+ console.log('uShadowsEnabled:', shadowsEnabledLoc);
575
+ console.log('uShadowBias:', shadowBiasLoc);
576
+ console.log('uShadowMapSize:', shadowMapSizeLoc);
577
+ console.log('uShadowSoftness:', shadowSoftnessLoc);
578
+ console.log('uPCFSize:', pcfSizeLoc);
579
+ console.log('uPCFEnabled:', pcfEnabledLoc);
580
+
581
+ // Get active uniforms
582
+ const numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
583
+ console.log(`\nActive uniforms for shader variant '${variant}' (${numUniforms} total):\n`);
584
+
585
+ for (let i = 0; i < numUniforms; i++) {
586
+ const uniformInfo = gl.getActiveUniform(program, i);
587
+ console.log(`${i}: ${uniformInfo.name} (${this.getGLTypeString(uniformInfo.type)})`);
588
+ }
589
+ } else {
590
+ console.log(`Program not available for shader variant '${variant}'`);
591
+ }
592
+ } catch (error) {
593
+ console.error('Error in shadow uniform debugging:', error);
594
+ }
595
+ }
596
+
597
+ /**
598
+ * Helper to convert WebGL type enum to string
599
+ */
600
+ getGLTypeString(type) {
601
+ const gl = this.gl;
602
+ const types = {
603
+ [gl.FLOAT]: 'FLOAT',
604
+ [gl.FLOAT_VEC2]: 'FLOAT_VEC2',
605
+ [gl.FLOAT_VEC3]: 'FLOAT_VEC3',
606
+ [gl.FLOAT_VEC4]: 'FLOAT_VEC4',
607
+ [gl.INT]: 'INT',
608
+ [gl.INT_VEC2]: 'INT_VEC2',
609
+ [gl.INT_VEC3]: 'INT_VEC3',
610
+ [gl.INT_VEC4]: 'INT_VEC4',
611
+ [gl.BOOL]: 'BOOL',
612
+ [gl.BOOL_VEC2]: 'BOOL_VEC2',
613
+ [gl.BOOL_VEC3]: 'BOOL_VEC3',
614
+ [gl.BOOL_VEC4]: 'BOOL_VEC4',
615
+ [gl.FLOAT_MAT2]: 'FLOAT_MAT2',
616
+ [gl.FLOAT_MAT3]: 'FLOAT_MAT3',
617
+ [gl.FLOAT_MAT4]: 'FLOAT_MAT4',
618
+ [gl.SAMPLER_2D]: 'SAMPLER_2D',
619
+ [gl.SAMPLER_CUBE]: 'SAMPLER_CUBE'
620
+ };
621
+
622
+ return types[type] || `UNKNOWN_TYPE(${type})`;
623
+ }
624
+
625
+
626
+ /**
627
+ * Toggle shadow map visualization
628
+ * @param {boolean} [enable] - If provided, explicitly sets visualization on/off
629
+ * @returns {boolean} The new state of shadow map visualization
630
+ */
631
+ toggleShadowMapVisualization(enable) {
632
+ // If enable parameter is provided, use it, otherwise toggle
633
+ if (enable !== undefined) {
634
+ lightingConstants.DEBUG.VISUALIZE_SHADOW_MAP = enable;
635
+ } else {
636
+ lightingConstants.DEBUG.VISUALIZE_SHADOW_MAP = !lightingConstants.DEBUG.VISUALIZE_SHADOW_MAP;
637
+ }
638
+
639
+ // When shadow map visualization is enabled, also enable frustum visualization for clarity
640
+ if (lightingConstants.DEBUG.VISUALIZE_SHADOW_MAP) {
641
+ lightingConstants.DEBUG.VISUALIZE_FRUSTUM = true;
642
+ }
643
+
644
+ // Reset debug state when enabling shadow map visualization
645
+ if (lightingConstants.DEBUG.VISUALIZE_SHADOW_MAP && this.lightManager) {
646
+ const mainLight = this.lightManager.getMainDirectionalLight();
647
+ if (mainLight) {
648
+ // Reset debug state if needed
649
+ }
650
+ }
651
+
652
+ console.log(`Shadow map visualization ${lightingConstants.DEBUG.VISUALIZE_SHADOW_MAP ? 'enabled' : 'disabled'}`);
653
+ return lightingConstants.DEBUG.VISUALIZE_SHADOW_MAP;
654
+ }
655
+ }