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,82 @@
1
+ // actionengine/display/graphics/renderers/actionrenderer3D/canvasmanager3D.js
2
+ class CanvasManager3D {
3
+ constructor(canvas) {
4
+ this.canvas = canvas;
5
+ this._clearColor = [0.529, 0.808, 0.922, 1.0]; // Default light blue
6
+ this.initializeContext();
7
+ this.initializeGLState();
8
+ }
9
+
10
+ initializeContext() {
11
+ this.gl = this.canvas.getContext("webgl2");
12
+ if (!this.gl) {
13
+ this.gl = this.canvas.getContext("webgl");
14
+ if (!this.gl) {
15
+ throw new Error("WebGL not supported");
16
+ }
17
+ this._isWebGL2 = false;
18
+ console.log("[CanvasManager3D] Using WebGL 1.0");
19
+ } else {
20
+ this._isWebGL2 = true;
21
+ console.log("[CanvasManager3D] Using WebGL 2.0");
22
+ }
23
+ }
24
+
25
+ initializeGLState() {
26
+ // Basic setup
27
+ this.gl.clearColor(...this._clearColor);
28
+ this.gl.viewport(0, 0, Game.WIDTH, Game.HEIGHT);
29
+
30
+ // Depth testing setup
31
+ this.gl.enable(this.gl.DEPTH_TEST);
32
+ this.gl.depthFunc(this.gl.LEQUAL);
33
+
34
+ // Face culling setup
35
+ this.gl.enable(this.gl.CULL_FACE);
36
+ this.gl.frontFace(this.gl.CCW);
37
+ this.gl.cullFace(this.gl.BACK);
38
+
39
+ // Disable blending by default
40
+ this.gl.disable(this.gl.BLEND);
41
+ }
42
+
43
+ getContext() {
44
+ return this.gl;
45
+ }
46
+
47
+ isWebGL2() {
48
+ return this._isWebGL2;
49
+ }
50
+
51
+ clear() {
52
+ this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
53
+ }
54
+
55
+ setViewport(width, height) {
56
+ this.gl.viewport(0, 0, width, height);
57
+ }
58
+
59
+ enableBlending() {
60
+ this.gl.enable(this.gl.BLEND);
61
+ this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
62
+ }
63
+
64
+ disableBlending() {
65
+ this.gl.disable(this.gl.BLEND);
66
+ }
67
+
68
+ bindFramebuffer(framebuffer) {
69
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
70
+ }
71
+
72
+ resetToDefaultFramebuffer() {
73
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
74
+ this.setViewport(Game.WIDTH, Game.HEIGHT);
75
+ this.gl.clearColor(...this._clearColor);
76
+ }
77
+
78
+ setClearColor(r, g, b, a = 1.0) {
79
+ this._clearColor = [r, g, b, a];
80
+ this.gl.clearColor(r, g, b, a);
81
+ }
82
+ }
@@ -0,0 +1,493 @@
1
+ // actionengine/display/graphics/renderers/actionrenderer3D/debugrenderer3D.js
2
+ class DebugRenderer3D {
3
+ constructor(gl, programManager, lightManager) {
4
+ this.gl = gl;
5
+ this.programManager = programManager;
6
+ this.lightManager = lightManager;
7
+
8
+ // Reference to lighting constants
9
+ this.constants = lightingConstants;
10
+
11
+ // Create buffer for direction indicators
12
+ this.directionBuffer = this.gl.createBuffer();
13
+
14
+ // Enable shadow map debug labels
15
+ this._shadowDebugLabels = true;
16
+
17
+ // Track shadow map visualization state
18
+ this._wasVisualizingShadowMap = false;
19
+
20
+ this._lastLineShaderVariant = null;
21
+
22
+ // Debug flag
23
+ this._debugFrustum = false;
24
+ }
25
+
26
+ drawDebugLines(camera, character, currentTime) {
27
+ // Only change variant if needed
28
+ const currentVariant = this.programManager.getCurrentVariant();
29
+ const targetVariant = currentVariant === "virtualboy" ? "virtualboy" : "default";
30
+
31
+ if (this._lastLineShaderVariant !== targetVariant) {
32
+ this.programManager.setLineShaderVariant(targetVariant);
33
+ this._lastLineShaderVariant = targetVariant;
34
+ }
35
+
36
+ // Get the line program and locations
37
+ const lineProgram = this.programManager.getLineProgram();
38
+ const lineLocations = this.programManager.getLineLocations();
39
+
40
+ // Draw character-related debug info if character exists
41
+ if (character) {
42
+ const currentTriangle = character.getCurrentTriangle();
43
+ if (currentTriangle) {
44
+ this.drawTriangleNormal(currentTriangle, camera, { program: lineProgram, locations: lineLocations }, currentTime);
45
+ }
46
+ this.drawDirectionIndicator(character, camera, { program: lineProgram, locations: lineLocations }, currentTime);
47
+ }
48
+
49
+ // Always try to draw light frustum - it will check for the DEBUG.VISUALIZE_FRUSTUM flag internally
50
+ this.drawLightFrustum(camera, { program: lineProgram, locations: lineLocations });
51
+
52
+ // Draw shadow map visualization if enabled
53
+ this.drawShadowMapDebug(camera);
54
+ }
55
+
56
+ drawTriangleNormal(triangle, camera, lineShader, currentTime) {
57
+ // Calculate triangle center
58
+ const v1 = triangle.vertices[0];
59
+ const v2 = triangle.vertices[1];
60
+ const v3 = triangle.vertices[2];
61
+ const center = [(v1.x + v2.x + v3.x) / 3, (v1.y + v2.y + v3.y) / 3, (v1.z + v2.z + v3.z) / 3];
62
+
63
+ // Create normal line
64
+ const normalLength = 10;
65
+ const end = [
66
+ center[0] + triangle.normal.x * normalLength,
67
+ center[1] + triangle.normal.y * normalLength,
68
+ center[2] + triangle.normal.z * normalLength
69
+ ];
70
+
71
+ this.drawLine(center, end, camera, lineShader, currentTime);
72
+ }
73
+
74
+ drawDirectionIndicator(character, camera, lineShader, currentTime) {
75
+ const center = character.position;
76
+ const directionEnd = new Vector3(
77
+ center.x + character.facingDirection.x * character.size * 2,
78
+ center.y,
79
+ center.z + character.facingDirection.z * character.size * 2
80
+ );
81
+
82
+ this.drawLine(center.toArray(), directionEnd.toArray(), camera, lineShader, currentTime);
83
+ }
84
+
85
+ /**
86
+ * Draw the light frustum for visualization
87
+ */
88
+ drawLightFrustum(camera, lineShader) {
89
+ if (!this.lightManager) {
90
+ console.log("No light manager available for frustum visualization");
91
+ return;
92
+ }
93
+
94
+ // Check if frustum visualization is enabled in constants
95
+ if (!this.constants.DEBUG.VISUALIZE_FRUSTUM) {
96
+ return;
97
+ }
98
+
99
+ //console.log("Drawing light frustum...");
100
+ this._debugFrustum = true;
101
+
102
+ // Get main directional light
103
+ const mainLight = this.lightManager.getMainDirectionalLight();
104
+ if (!mainLight) {
105
+ console.log("No main directional light for frustum visualization");
106
+ return;
107
+ }
108
+
109
+ // Get light position and direction
110
+ const lightPos = mainLight.getPosition();
111
+ const lightDir = mainLight.getDirection();
112
+
113
+ //console.log("Light position:", lightPos);
114
+ //console.log("Light direction:", lightDir);
115
+
116
+ // Get shadow projection parameters from constants
117
+ const projection = this.constants.SHADOW_PROJECTION;
118
+
119
+ // Frustum parameters - make sure these get the actual values, not just property names
120
+ const left = projection.LEFT.value;
121
+ const right = projection.RIGHT.value;
122
+ const bottom = projection.BOTTOM.value;
123
+ const top = projection.TOP.value;
124
+ const near = projection.NEAR.value;
125
+ const far = projection.FAR.value;
126
+
127
+ //console.log("Frustum bounds:", { left, right, bottom, top, near, far });
128
+
129
+ // Calculate frustum corners in light space
130
+ const corners = [
131
+ // Near plane (4 corners)
132
+ [left, bottom, -near],
133
+ [right, bottom, -near],
134
+ [right, top, -near],
135
+ [left, top, -near],
136
+
137
+ // Far plane (4 corners)
138
+ [left, bottom, -far],
139
+ [right, bottom, -far],
140
+ [right, top, -far],
141
+ [left, top, -far]
142
+ ];
143
+
144
+ // Create light view matrix
145
+ const lightViewMatrix = Matrix4.create();
146
+ const lightTarget = new Vector3(
147
+ lightPos.x + lightDir.x * 100,
148
+ lightPos.y + lightDir.y * 100,
149
+ lightPos.z + lightDir.z * 100
150
+ );
151
+
152
+ Matrix4.lookAt(
153
+ lightViewMatrix,
154
+ lightPos.toArray(),
155
+ lightTarget.toArray(),
156
+ [0, 1, 0] // Up vector
157
+ );
158
+
159
+ // Invert the light view matrix to transform frustum from light space to world space
160
+ const invLightViewMatrix = Matrix4.create();
161
+ Matrix4.invert(invLightViewMatrix, lightViewMatrix);
162
+
163
+ // Transform corners to world space
164
+ const worldCorners = corners.map((corner) => {
165
+ const worldCorner = [0, 0, 0, 1];
166
+ Matrix4.multiplyVector(worldCorner, invLightViewMatrix, [...corner, 1]);
167
+ return [worldCorner[0], worldCorner[1], worldCorner[2]];
168
+ });
169
+
170
+ // Draw lines connecting the corners (frustum edges)
171
+ // Near plane
172
+ this.drawLine(worldCorners[0], worldCorners[1], camera, lineShader, 0, [1.0, 1.0, 0.2]);
173
+ this.drawLine(worldCorners[1], worldCorners[2], camera, lineShader, 0, [1.0, 1.0, 0.2]);
174
+ this.drawLine(worldCorners[2], worldCorners[3], camera, lineShader, 0, [1.0, 1.0, 0.2]);
175
+ this.drawLine(worldCorners[3], worldCorners[0], camera, lineShader, 0, [1.0, 1.0, 0.2]);
176
+
177
+ // Far plane
178
+ this.drawLine(worldCorners[4], worldCorners[5], camera, lineShader, 0, [1.0, 1.0, 0.2]);
179
+ this.drawLine(worldCorners[5], worldCorners[6], camera, lineShader, 0, [1.0, 1.0, 0.2]);
180
+ this.drawLine(worldCorners[6], worldCorners[7], camera, lineShader, 0, [1.0, 1.0, 0.2]);
181
+ this.drawLine(worldCorners[7], worldCorners[4], camera, lineShader, 0, [1.0, 1.0, 0.2]);
182
+
183
+ // Connecting edges
184
+ this.drawLine(worldCorners[0], worldCorners[4], camera, lineShader, 0, [1.0, 1.0, 0.2]);
185
+ this.drawLine(worldCorners[1], worldCorners[5], camera, lineShader, 0, [1.0, 1.0, 0.2]);
186
+ this.drawLine(worldCorners[2], worldCorners[6], camera, lineShader, 0, [1.0, 1.0, 0.2]);
187
+ this.drawLine(worldCorners[3], worldCorners[7], camera, lineShader, 0, [1.0, 1.0, 0.2]);
188
+
189
+ // Draw light position and direction
190
+ const lightPosArray = [lightPos.x, lightPos.y, lightPos.z];
191
+ const lightDirEnd = [lightPos.x + lightDir.x * 500, lightPos.y + lightDir.y * 500, lightPos.z + lightDir.z * 500];
192
+
193
+ // Always draw the light direction line, even if frustum lines are disabled
194
+ this.drawLine(lightPosArray, lightDirEnd, camera, lineShader, 0, [1.0, 0.8, 0.2]);
195
+
196
+ //console.log("Light frustum visualization complete");
197
+ }
198
+
199
+ drawLine(start, end, camera, lineShader, currentTime, color = [0.2, 0.2, 1.0]) {
200
+ if (!lineShader || !lineShader.program) {
201
+ console.error("Line shader not available");
202
+ return;
203
+ }
204
+
205
+ try {
206
+ const lineVerts = new Float32Array([...start, ...end]);
207
+
208
+ this.gl.useProgram(lineShader.program);
209
+
210
+ // Set up matrices
211
+ const projection = Matrix4.perspective(Matrix4.create(), camera.fov, Game.WIDTH / Game.HEIGHT, 0.1, 10000.0);
212
+ const view = Matrix4.create();
213
+ Matrix4.lookAt(view, camera.position.toArray(), camera.target.toArray(), camera.up.toArray());
214
+
215
+ // Check if locations exist
216
+ if (!lineShader.locations) {
217
+ lineShader.locations = {
218
+ position: this.gl.getAttribLocation(lineShader.program, "aPosition"),
219
+ projectionMatrix: this.gl.getUniformLocation(lineShader.program, "uProjectionMatrix"),
220
+ viewMatrix: this.gl.getUniformLocation(lineShader.program, "uViewMatrix"),
221
+ color: this.gl.getUniformLocation(lineShader.program, "uColor")
222
+ };
223
+ }
224
+
225
+ // Set matrix uniforms
226
+ this.gl.uniformMatrix4fv(lineShader.locations.projectionMatrix, false, projection);
227
+ this.gl.uniformMatrix4fv(lineShader.locations.viewMatrix, false, view);
228
+
229
+ // Set the line color
230
+ const colorLocation = lineShader.locations.color || this.gl.getUniformLocation(lineShader.program, "uColor");
231
+ if (colorLocation) {
232
+ this.gl.uniform3fv(colorLocation, color);
233
+ }
234
+
235
+ // Buffer and draw the line
236
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.directionBuffer);
237
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, lineVerts, this.gl.STATIC_DRAW);
238
+
239
+ const positionLocation = lineShader.locations.position;
240
+ this.gl.vertexAttribPointer(positionLocation, 3, this.gl.FLOAT, false, 0, 0);
241
+ this.gl.enableVertexAttribArray(positionLocation);
242
+
243
+ this.gl.drawArrays(this.gl.LINES, 0, 2);
244
+ } catch (error) {
245
+ console.error("Error drawing line:", error);
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Render the shadow map to a quad on screen for debugging
251
+ * This is useful for visualizing the shadow depth map
252
+ */
253
+ drawShadowMapDebug(camera) {
254
+ // Handle toggling visualization on/off
255
+ if (!this.constants.DEBUG.VISUALIZE_SHADOW_MAP) {
256
+ // If visualization was on but is now off, clean up
257
+ if (this._wasVisualizingShadowMap) {
258
+ this._wasVisualizingShadowMap = false;
259
+ }
260
+ return;
261
+ }
262
+
263
+ // Force visualization frustum on when debug shadow map is on
264
+ // This ensures we can always see both
265
+ this.constants.DEBUG.VISUALIZE_FRUSTUM = true;
266
+
267
+ // Only render if we have a light manager and main directional light
268
+ if (!this.lightManager) return;
269
+
270
+ const mainLight = this.lightManager.getMainDirectionalLight();
271
+ if (!mainLight || !mainLight.shadowTexture) return;
272
+
273
+ // Track that we're visualizing the shadow map
274
+ this._wasVisualizingShadowMap = true;
275
+
276
+ const gl = this.gl;
277
+
278
+ // Create a program for rendering the depth texture if it doesn't exist
279
+ if (!this._shadowDebugProgram) {
280
+ // Vertex shader for rendering a simple quad
281
+ const quadVS = `
282
+ attribute vec2 aPosition;
283
+ attribute vec2 aTexCoord;
284
+ varying vec2 vTexCoord;
285
+
286
+ void main() {
287
+ gl_Position = vec4(aPosition, 0.0, 1.0);
288
+ vTexCoord = aTexCoord;
289
+ }
290
+ `;
291
+
292
+ // Fragment shader for rendering the depth texture
293
+ const depthFS = `
294
+ precision mediump float;
295
+ varying vec2 vTexCoord;
296
+ uniform sampler2D uShadowMap;
297
+ uniform int uVisualizeMode; // 0 = raw RGBA, 1 = raw grayscale
298
+
299
+ void main() {
300
+ // Get the raw texture value
301
+ vec4 rawValue = texture2D(uShadowMap, vTexCoord);
302
+
303
+ if (uVisualizeMode == 0) {
304
+ // Mode 0: Raw RGBA values exactly as stored
305
+ gl_FragColor = rawValue;
306
+ } else {
307
+ // Mode 1: Raw grayscale using just the red channel, no enhancements
308
+ float depth = rawValue.r;
309
+ gl_FragColor = vec4(depth, depth, depth, 1.0);
310
+ }
311
+
312
+ // Optional border
313
+ float border = 0.01;
314
+ if (vTexCoord.x < border || vTexCoord.x > 1.0 - border ||
315
+ vTexCoord.y < border || vTexCoord.y > 1.0 - border) {
316
+ gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
317
+ }
318
+ }
319
+ `;
320
+
321
+ // Create the program
322
+ this._shadowDebugProgram = this.programManager.createShaderProgram(quadVS, depthFS, "shadow_debug");
323
+
324
+ // Get attribute locations
325
+ this._shadowDebugLocations = {
326
+ position: gl.getAttribLocation(this._shadowDebugProgram, "aPosition"),
327
+ texCoord: gl.getAttribLocation(this._shadowDebugProgram, "aTexCoord"),
328
+ shadowMap: gl.getUniformLocation(this._shadowDebugProgram, "uShadowMap"),
329
+ visualizeMode: gl.getUniformLocation(this._shadowDebugProgram, "uVisualizeMode")
330
+ };
331
+
332
+ // Default to visualization mode 0
333
+ this._shadowVisualizationMode = 0;
334
+
335
+ // Create buffers for quad
336
+ this._quadPositionBuffer = gl.createBuffer();
337
+ this._quadTexCoordBuffer = gl.createBuffer();
338
+
339
+ // Create a square visualization quad adjusted for screen aspect ratio
340
+ // Get the canvas dimensions to calculate aspect ratio
341
+ const canvasWidth = gl.canvas.width;
342
+ const canvasHeight = gl.canvas.height;
343
+ const aspectRatio = canvasWidth / canvasHeight;
344
+
345
+ // Size of the square as a percentage of screen height
346
+ const quadSizeY = 0.3; // 30% of screen height
347
+ // Adjust width based on aspect ratio to maintain square shape
348
+ const quadSizeX = quadSizeY / aspectRatio;
349
+
350
+ // Position in bottom right corner
351
+ const quadX = 1.0 - quadSizeX * 2.0; // Right side
352
+ const quadY = -1.0 + quadSizeY * 0; // Bottom side
353
+
354
+ console.log(
355
+ `Creating shadow map visualization quad: ${quadSizeX}x${quadSizeY}, aspect ratio: ${aspectRatio}`
356
+ );
357
+
358
+ const quadPositions = new Float32Array([
359
+ quadX,
360
+ quadY, // Bottom-left
361
+ quadX + quadSizeX * 2.0,
362
+ quadY, // Bottom-right
363
+ quadX,
364
+ quadY + quadSizeY * 2.0, // Top-left
365
+ quadX + quadSizeX * 2.0,
366
+ quadY + quadSizeY * 2.0 // Top-right
367
+ ]);
368
+
369
+ const quadTexCoords = new Float32Array([
370
+ 0.0,
371
+ 0.0, // Bottom-left
372
+ 1.0,
373
+ 0.0, // Bottom-right
374
+ 0.0,
375
+ 1.0, // Top-left
376
+ 1.0,
377
+ 1.0 // Top-right
378
+ ]);
379
+
380
+ // Upload quad data to buffers
381
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._quadPositionBuffer);
382
+ gl.bufferData(gl.ARRAY_BUFFER, quadPositions, gl.STATIC_DRAW);
383
+
384
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._quadTexCoordBuffer);
385
+ gl.bufferData(gl.ARRAY_BUFFER, quadTexCoords, gl.STATIC_DRAW);
386
+ }
387
+
388
+ // Save WebGL state before rendering
389
+ const previousProgram = gl.getParameter(gl.CURRENT_PROGRAM);
390
+
391
+ // Use the debug shader program
392
+ gl.useProgram(this._shadowDebugProgram);
393
+
394
+ // Unbind any existing texture first to clear state
395
+ gl.activeTexture(gl.TEXTURE0);
396
+ gl.bindTexture(gl.TEXTURE_2D, null);
397
+
398
+ // Now bind the shadow map texture
399
+ gl.bindTexture(gl.TEXTURE_2D, mainLight.shadowTexture);
400
+ gl.uniform1i(this._shadowDebugLocations.shadowMap, 0);
401
+
402
+ // Set visualization mode
403
+ if (this._shadowDebugLocations.visualizeMode !== null) {
404
+ gl.uniform1i(this._shadowDebugLocations.visualizeMode, this._shadowVisualizationMode);
405
+ }
406
+
407
+ // Cycle visualization mode when shadow map visualization is enabled
408
+ // This gives us multiple ways to view the shadow map
409
+ // Cycle visualization mode every 2 seconds
410
+ if (!this._lastVisualizationTime || performance.now() - this._lastVisualizationTime > 2000) {
411
+ this._shadowVisualizationMode = (this._shadowVisualizationMode + 1) % 2; // Just 2 modes now
412
+ this._lastVisualizationTime = performance.now();
413
+ console.log(
414
+ `Shadow map visualization mode: ${this._shadowVisualizationMode ? "Decoded Grayscale" : "Raw RGBA"}`
415
+ );
416
+ }
417
+
418
+ // Draw the quad
419
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._quadPositionBuffer);
420
+ gl.vertexAttribPointer(this._shadowDebugLocations.position, 2, gl.FLOAT, false, 0, 0);
421
+ gl.enableVertexAttribArray(this._shadowDebugLocations.position);
422
+
423
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._quadTexCoordBuffer);
424
+ gl.vertexAttribPointer(this._shadowDebugLocations.texCoord, 2, gl.FLOAT, false, 0, 0);
425
+ gl.enableVertexAttribArray(this._shadowDebugLocations.texCoord);
426
+
427
+ // Disable depth testing so the quad is always visible
428
+ const depthTestEnabled = gl.isEnabled(gl.DEPTH_TEST);
429
+ if (depthTestEnabled) {
430
+ gl.disable(gl.DEPTH_TEST);
431
+ }
432
+
433
+ // Draw the quad
434
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
435
+
436
+ // Restore WebGL state
437
+ if (depthTestEnabled) {
438
+ gl.enable(gl.DEPTH_TEST);
439
+ }
440
+
441
+ // Draw information text next to the shadow map visualization
442
+ if (this._shadowDebugLabels) {
443
+ // If we have a canvas 2D context available, draw text
444
+ if (!this._canvas2d) {
445
+ // Try to get the canvas element and create a 2D context
446
+ const canvas = this.gl.canvas;
447
+ if (canvas && canvas.getContext) {
448
+ this._canvas2d = canvas.getContext("2d");
449
+ }
450
+ }
451
+
452
+ if (this._canvas2d) {
453
+ const ctx = this._canvas2d;
454
+ const canvas = this.gl.canvas;
455
+ const width = canvas.width;
456
+ const height = canvas.height;
457
+
458
+ // Size of the shadow map display
459
+ const quadSize = 0.3; // Same as defined for the quad
460
+ const displayWidth = width * quadSize;
461
+ const displayHeight = height * quadSize;
462
+
463
+ // Position (bottom right of screen)
464
+ const displayX = width - displayWidth - 10;
465
+ const displayY = height - displayHeight - 10;
466
+
467
+ // Draw visualization mode info
468
+ ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
469
+ ctx.fillRect(displayX, displayY - 25, displayWidth, 25);
470
+
471
+ ctx.fillStyle = "white";
472
+ ctx.font = "12px Arial";
473
+ ctx.textAlign = "left";
474
+ ctx.textBaseline = "middle";
475
+
476
+ let modeText = "";
477
+ switch (this._shadowVisualizationMode) {
478
+ case 0:
479
+ modeText = "Mode: Raw RGBA (Encoded)";
480
+ break;
481
+ case 1:
482
+ modeText = "Mode: Decoded Grayscale";
483
+ break;
484
+ }
485
+
486
+ ctx.fillText(`Shadow Map - ${modeText}`, displayX + 5, displayY - 12);
487
+ }
488
+ }
489
+
490
+ // Restore previous program
491
+ gl.useProgram(previousProgram);
492
+ }
493
+ }