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,75 @@
1
+ // actionengine/display/canvasmanager.js
2
+ class CanvasManager {
3
+ static get WIDTH() { return 800; }
4
+ static get HEIGHT() { return 600; }
5
+
6
+ constructor() {
7
+ this.container = document.getElementById("appContainer");
8
+
9
+ // Create the three canvases with explicit roles
10
+ this.gameCanvas = this.createCanvas("gameCanvas"); // Pure game rendering
11
+ this.guiCanvas = this.createCanvas("guiCanvas"); // All UI/interactive elements
12
+ this.debugCanvas = this.createCanvas("debugCanvas"); // Debug overlay
13
+
14
+ // Get 2D contexts
15
+ this.guiCtx = this.guiCanvas.getContext("2d");
16
+ this.debugCtx = this.debugCanvas.getContext("2d");
17
+
18
+ // Note: We don't get game context here as it might be 2D or WebGL
19
+
20
+ this.setupCanvasStyles();
21
+ this.setupResizeHandler();
22
+ this.resizeCanvases();
23
+ }
24
+
25
+ createCanvas(id) {
26
+ const canvas = document.createElement("canvas");
27
+ canvas.id = id;
28
+ canvas.width = CanvasManager.WIDTH;
29
+ canvas.height = CanvasManager.HEIGHT;
30
+ this.container.appendChild(canvas);
31
+ return canvas;
32
+ }
33
+
34
+ setupCanvasStyles() {
35
+ // Game canvas is base layer
36
+ this.gameCanvas.style.zIndex = "1";
37
+
38
+ // GUI canvas gets events and overlays game
39
+ this.guiCanvas.style.zIndex = "2";
40
+
41
+ // Debug on top, no events
42
+ this.debugCanvas.style.zIndex = "3";
43
+ }
44
+
45
+ setupResizeHandler() {
46
+ window.addEventListener("resize", () => this.resizeCanvases());
47
+ window.addEventListener("orientationchange", () => this.resizeCanvases());
48
+ }
49
+
50
+ resizeCanvases() {
51
+ const containerWidth = this.container.clientWidth;
52
+ const containerHeight = this.container.clientHeight;
53
+
54
+ const scale = Math.min(containerWidth / CanvasManager.WIDTH, containerHeight / CanvasManager.HEIGHT);
55
+
56
+ const scaledWidth = CanvasManager.WIDTH * scale;
57
+ const scaledHeight = CanvasManager.HEIGHT * scale;
58
+
59
+ // Apply scaling to all canvases
60
+ [this.gameCanvas, this.guiCanvas, this.debugCanvas].forEach((canvas) => {
61
+ canvas.style.width = `${scaledWidth}px`;
62
+ canvas.style.height = `${scaledHeight}px`;
63
+ });
64
+ }
65
+
66
+ getCanvases() {
67
+ return {
68
+ gameCanvas: this.gameCanvas,
69
+ guiCanvas: this.guiCanvas,
70
+ debugCanvas: this.debugCanvas,
71
+ guiCtx: this.guiCtx,
72
+ debugCtx: this.debugCtx
73
+ };
74
+ }
75
+ }
@@ -0,0 +1,570 @@
1
+ // actionengine/display/gl/programmanager.js
2
+ class ProgramManager {
3
+ constructor(gl, isWebGL2) {
4
+ this.gl = gl;
5
+ this.isWebGL2 = isWebGL2;
6
+
7
+ // Current active variant
8
+ this.currentVariant = "default";
9
+
10
+ // Store shader programs
11
+ this.objectProgram = null;
12
+ this.objectLocations = {};
13
+ this.particleProgram = null;
14
+ this.waterProgram = null;
15
+ this.lineProgram = null;
16
+
17
+ // Store locations for different shader programs
18
+ this.particleLocations = {};
19
+ this.waterLocations = {};
20
+ this.lineLocations = {};
21
+
22
+ // Shader instances
23
+ this.objectShader = null;
24
+ this.lineShader = null;
25
+
26
+ // Debug visualization buffers
27
+ this.debugQuadBuffer = null;
28
+ this.debugBackgroundBuffer = null;
29
+
30
+ // Attribute and uniform name mappings
31
+ this.attributeNames = {
32
+ position: 'aPosition',
33
+ normal: 'aNormal',
34
+ color: 'aColor',
35
+ texCoord: 'aTexCoord',
36
+ textureIndex: 'aTextureIndex',
37
+ useTexture: 'aUseTexture'
38
+ };
39
+
40
+ this.uniformNames = {
41
+ projectionMatrix: 'uProjectionMatrix',
42
+ viewMatrix: 'uViewMatrix',
43
+ modelMatrix: 'uModelMatrix',
44
+ lightPos: 'uLightPos',
45
+ lightDir: 'uLightDir',
46
+ lightIntensity: 'uLightIntensity',
47
+ pointLightIntensity: 'uPointLightIntensity',
48
+ roughness: 'uRoughness',
49
+ metallic: 'uMetallic',
50
+ baseReflectivity: 'uBaseReflectivity',
51
+ cameraPos: 'uCameraPos',
52
+ time: 'uTime',
53
+ lightSpaceMatrix: 'uLightSpaceMatrix',
54
+ shadowMap: 'uShadowMap',
55
+ shadowsEnabled: 'uShadowsEnabled',
56
+ intensityFactor: 'uIntensityFactor'
57
+ };
58
+
59
+ this.textureUniforms = {
60
+ standard: this.isWebGL2 ? 'uTextureArray' : 'uTexture',
61
+ pbr: 'uPBRTextureArray',
62
+ shadowMap: 'uShadowMap',
63
+ materialProps: 'uMaterialPropertiesTexture'
64
+ };
65
+
66
+ // Initialize all shaders
67
+ this.initializeSpecialShaders();
68
+ this.initializeObjectShader();
69
+ }
70
+
71
+ /**
72
+ * Create a shader program
73
+ */
74
+ createShaderProgram(vsSource, fsSource, shaderName = 'unknown') {
75
+ try {
76
+ // Try to compile the vertex shader
77
+ const vertexShader = this.compileShader(this.gl.VERTEX_SHADER, vsSource, `${shaderName} vertex`);
78
+
79
+ // Try to compile the fragment shader
80
+ const fragmentShader = this.compileShader(this.gl.FRAGMENT_SHADER, fsSource, `${shaderName} fragment`);
81
+
82
+ const program = this.gl.createProgram();
83
+ this.gl.attachShader(program, vertexShader);
84
+ this.gl.attachShader(program, fragmentShader);
85
+ this.gl.linkProgram(program);
86
+
87
+ // After successful linking:
88
+ if (program) {
89
+ // Set explicit texture sampler bindings to prevent location conflicts
90
+ this.assignExplicitSamplerBindings(program);
91
+ }
92
+
93
+ if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
94
+ const info = this.gl.getProgramInfoLog(program);
95
+ console.error(`==== SHADER LINK ERROR FOR '${shaderName}' ====`);
96
+ console.error(info);
97
+ console.error('==== VERTEX SHADER SOURCE ====');
98
+ console.error(vsSource);
99
+ console.error('==== FRAGMENT SHADER SOURCE ====');
100
+ console.error(fsSource);
101
+ throw new Error(`Shader program '${shaderName}' failed to link: ${info}`);
102
+ }
103
+
104
+ // Clean up shaders after linking
105
+ this.gl.deleteShader(vertexShader);
106
+ this.gl.deleteShader(fragmentShader);
107
+
108
+ return program;
109
+ } catch (error) {
110
+ console.error(`Error creating shader program '${shaderName}': ${error.message}`);
111
+ throw error;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Assigns explicit texture units to sampler uniforms to prevent WebGL sampler location conflicts.
117
+ *
118
+ * BACKGROUND: WebGL has a critical requirement where different sampler types
119
+ * (sampler2D, samplerCube, sampler2DArray) cannot share the same texture unit
120
+ * if they're used in the same shader program during a draw call. If not handled
121
+ * properly, it causes the error:
122
+ * "GL_INVALID_OPERATION: Two textures of different types use the same sampler location"
123
+ *
124
+ * WHY THIS HAPPENS: When WebGL compiles a shader program, it assigns internal memory
125
+ * locations to samplers. Sometimes the compiler optimizes by sharing locations, which
126
+ * can cause conflicts between DIFFERENT sampler TYPES.......
127
+ *
128
+ * KEY POINTS:
129
+ * - This only becomes an issue when mixing different sampler types
130
+ * - You MUST call this after program linking - calling earlier has no effect
131
+ * - Only texture samplers need this explicit assignment, not regular uniforms
132
+ * - The texture units used (0, 1, etc.) aren't as important as using different ones
133
+ * for different sampler types, but it's advised to use the same units, and unit 0
134
+ * will be selected when webgl handles automatic texture unit assignment
135
+ * - This hurt brain a lot
136
+ *
137
+ * @param {WebGLProgram} program - The linked shader program
138
+ */
139
+ assignExplicitSamplerBindings(program) {
140
+ this.gl.useProgram(program);
141
+
142
+ // WEBGL SAMPLER CONFLICT RESOLUTION:
143
+ // 1. Group samplers by type (2D, Cube, Array)
144
+ // 2. Assign consecutive units within each type group
145
+ // 3. Ensure large gaps between different sampler types
146
+
147
+ // Define dedicated texture units for each sampler type
148
+ const samplerUniforms = [
149
+ // GROUP 1: 2D TEXTURES (units 0-7)
150
+ // Regular 2D textures - TEXTURE_2D type
151
+ {name: "uMaterialPropertiesTexture", unit: 0}, // Material properties
152
+ {name: "uDirectionalLightData", unit: 1}, // Directional light data
153
+ {name: "uPointLightData", unit: 2}, // Point light data
154
+ {name: "uShadowMap", unit: 3}, // Directional shadow map
155
+
156
+ // GROUP 2: CUBEMAP TEXTURES (units 10-19) - Large gap to prevent conflicts
157
+ // Cubemap texture samplers - TEXTURE_CUBE_MAP type
158
+ {name: "uPointShadowMap", unit: 10}, // First point shadow map
159
+ {name: "uPointShadowMap1", unit: 11}, // Second point shadow map
160
+ {name: "uPointShadowMap2", unit: 12}, // Third point shadow map
161
+ {name: "uPointShadowMap3", unit: 13}, // Fourth point shadow map
162
+
163
+ // GROUP 3: TEXTURE ARRAYS (units 20-29) - Large gap to prevent conflicts
164
+ // Texture array samplers - TEXTURE_2D_ARRAY type
165
+ {name: "uTextureArray", unit: 20}, // Standard texture array
166
+ {name: "uPBRTextureArray", unit: 21}, // PBR texture array
167
+ ];
168
+
169
+ // Assign each sampler to its dedicated texture unit
170
+ // This forces WebGL to use separate internal locations for different sampler types
171
+ for (const {name, unit} of samplerUniforms) {
172
+ const loc = this.gl.getUniformLocation(program, name);
173
+ if (loc !== null) {
174
+ this.gl.uniform1i(loc, unit);
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Compile a shader
181
+ */
182
+ compileShader(type, source, shaderLabel = 'unknown') {
183
+ const shader = this.gl.createShader(type);
184
+ this.gl.shaderSource(shader, source);
185
+ this.gl.compileShader(shader);
186
+
187
+ if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
188
+ const info = this.gl.getShaderInfoLog(shader);
189
+ this.gl.deleteShader(shader);
190
+
191
+ // Print detailed error information
192
+ console.error(`==== SHADER COMPILE ERROR FOR '${shaderLabel}' ====`);
193
+ console.error(info);
194
+
195
+ // Print the source code with line numbers
196
+ const sourceLines = source.split('\n');
197
+ console.error('==== SHADER SOURCE ====');
198
+ sourceLines.forEach((line, index) => {
199
+ console.error(`${index + 1}: ${line}`);
200
+ });
201
+
202
+ // Analyze error message for line numbers
203
+ let lineNumber = -1;
204
+ const lineMatch = info.match(/\d+:(\d+)/);
205
+ if (lineMatch && lineMatch[1]) {
206
+ lineNumber = parseInt(lineMatch[1]);
207
+ if (lineNumber > 0 && lineNumber <= sourceLines.length) {
208
+ console.error('==== PROBLEMATIC LINE ====');
209
+ console.error(`${lineNumber}: ${sourceLines[lineNumber - 1]}`);
210
+
211
+ // Show context (lines before and after)
212
+ console.error('==== CONTEXT ====');
213
+ const startLine = Math.max(0, lineNumber - 3);
214
+ const endLine = Math.min(sourceLines.length, lineNumber + 2);
215
+ for (let i = startLine; i < endLine; i++) {
216
+ const prefix = i === lineNumber - 1 ? '> ' : ' ';
217
+ console.error(`${prefix}${i + 1}: ${sourceLines[i]}`);
218
+ }
219
+ }
220
+ }
221
+
222
+ throw new Error(`Shader compile error in '${shaderLabel}': ${info}`);
223
+ }
224
+
225
+ return shader;
226
+ }
227
+
228
+ /**
229
+ * Initialize the object shader
230
+ */
231
+ initializeObjectShader() {
232
+ console.log("[ProgramManager] Initializing object shader");
233
+
234
+ try {
235
+ // Create a new ObjectShader instance
236
+ this.objectShader = new ObjectShader();
237
+
238
+ // Set initial variant
239
+ this.setObjectShaderVariant("default");
240
+
241
+ console.log("[ProgramManager] Object shader initialized successfully");
242
+ } catch (e) {
243
+ console.error(`[ProgramManager] Error initializing object shader: ${e.message}`);
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Set the current object shader variant and recompile
249
+ * @param {string} variant - The variant to use ('default', 'pbr', 'virtualboy')
250
+ */
251
+ setObjectShaderVariant(variant) {
252
+ if (!this.objectShader) {
253
+ console.warn("[ProgramManager] Object shader not initialized");
254
+ return;
255
+ }
256
+
257
+ try {
258
+ // Remember current variant
259
+ this.currentVariant = variant;
260
+
261
+ // Update the object shader variant
262
+ this.objectShader.setVariant(variant);
263
+
264
+ // Compile shader program with the current variant
265
+ const program = this.createShaderProgram(
266
+ this.objectShader.getVertexShader(this.isWebGL2),
267
+ this.objectShader.getFragmentShader(this.isWebGL2),
268
+ `object_shader_${variant}`
269
+ );
270
+
271
+ // Get locations for uniforms and attributes
272
+ const isPBR = variant === 'pbr';
273
+ const locations = this.getStandardShaderLocations(program, isPBR);
274
+
275
+ // Update stored program and locations
276
+ this.objectProgram = program;
277
+ this.objectLocations = locations;
278
+
279
+ console.log(`[ProgramManager] Object shader variant changed to: ${variant}`);
280
+
281
+ // Update line shader to match
282
+ this.handleVariantChange(variant);
283
+
284
+ return variant;
285
+ } catch (e) {
286
+ console.error(`[ProgramManager] Error setting object shader variant: ${e.message}`);
287
+ return this.currentVariant; // Return previous variant on error
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Cycle to the next shader variant
293
+ * @param {function} callback - Optional callback for when variant changes
294
+ * @returns {string} - Name of the new shader variant
295
+ */
296
+ cycleVariants(callback) {
297
+ const variants = ["default", "pbr", "virtualboy"];
298
+ const currentIndex = variants.indexOf(this.currentVariant);
299
+ const nextIndex = (currentIndex + 1) % variants.length;
300
+ const newVariant = variants[nextIndex];
301
+
302
+ // Set the new variant
303
+ this.setObjectShaderVariant(newVariant);
304
+
305
+ // Call the callback if provided
306
+ if (callback && typeof callback === 'function') {
307
+ callback(newVariant);
308
+ }
309
+
310
+ return newVariant;
311
+ }
312
+
313
+ /**
314
+ * Get the current shader variant
315
+ * @returns {string} - Current variant name
316
+ */
317
+ getCurrentVariant() {
318
+ return this.currentVariant;
319
+ }
320
+
321
+ /**
322
+ * Get the current object shader program
323
+ * @returns {WebGLProgram} - WebGL program for the current object shader
324
+ */
325
+ getObjectProgram() {
326
+ return this.objectProgram;
327
+ }
328
+
329
+ /**
330
+ * Get locations for the current object shader
331
+ * @returns {Object} - Locations for attributes and uniforms
332
+ */
333
+ getObjectLocations() {
334
+ return this.objectLocations;
335
+ }
336
+
337
+ /**
338
+ * Initialize special-case shaders (particles, water, line)
339
+ */
340
+ initializeSpecialShaders() {
341
+ this.initializeParticleShader();
342
+ this.initializeWaterShader();
343
+ this.initializeLineShader();
344
+ }
345
+
346
+ initializeParticleShader() {
347
+ const particleShader = new ParticleShader();
348
+ this.particleProgram = this.createShaderProgram(
349
+ particleShader.getParticleVertexShader(this.isWebGL2),
350
+ particleShader.getParticleFragmentShader(this.isWebGL2),
351
+ 'particle_shader'
352
+ );
353
+
354
+ this.particleLocations = {
355
+ position: this.gl.getAttribLocation(this.particleProgram, "aPosition"),
356
+ size: this.gl.getAttribLocation(this.particleProgram, "aSize"),
357
+ color: this.gl.getAttribLocation(this.particleProgram, "aColor"),
358
+ projectionMatrix: this.gl.getUniformLocation(this.particleProgram, "uProjectionMatrix"),
359
+ viewMatrix: this.gl.getUniformLocation(this.particleProgram, "uViewMatrix")
360
+ };
361
+ }
362
+
363
+ initializeWaterShader() {
364
+ const waterShader = new WaterShader();
365
+ this.waterProgram = this.createShaderProgram(
366
+ waterShader.getWaterVertexShader(this.isWebGL2),
367
+ waterShader.getWaterFragmentShader(this.isWebGL2),
368
+ 'water_shader'
369
+ );
370
+
371
+ // Add null checks
372
+ if (!this.waterProgram) {
373
+ console.error("Failed to create water program");
374
+ return;
375
+ }
376
+
377
+ this.waterLocations = {
378
+ position: this.gl.getAttribLocation(this.waterProgram, "aPosition"),
379
+ normal: this.gl.getAttribLocation(this.waterProgram, "aNormal"),
380
+ texCoord: this.gl.getAttribLocation(this.waterProgram, "aTexCoord"),
381
+ projectionMatrix: this.gl.getUniformLocation(this.waterProgram, "uProjectionMatrix"),
382
+ viewMatrix: this.gl.getUniformLocation(this.waterProgram, "uViewMatrix"),
383
+ modelMatrix: this.gl.getUniformLocation(this.waterProgram, "uModelMatrix"),
384
+ time: this.gl.getUniformLocation(this.waterProgram, "uTime"),
385
+ cameraPos: this.gl.getUniformLocation(this.waterProgram, "uCameraPos"),
386
+ lightDir: this.gl.getUniformLocation(this.waterProgram, "uLightDir")
387
+ };
388
+ }
389
+
390
+ initializeLineShader() {
391
+ // Create a new LineShader instance
392
+ this.lineShader = new LineShader();
393
+
394
+ // Create shader program for the default line shader
395
+ const lineProgram = this.createShaderProgram(
396
+ this.lineShader.getVertexShader(this.isWebGL2),
397
+ this.lineShader.getFragmentShader(this.isWebGL2),
398
+ 'line_shader'
399
+ );
400
+
401
+ // Get and store shader locations
402
+ this.lineLocations = {
403
+ position: this.gl.getAttribLocation(lineProgram, "aPosition"),
404
+ projectionMatrix: this.gl.getUniformLocation(lineProgram, "uProjectionMatrix"),
405
+ viewMatrix: this.gl.getUniformLocation(lineProgram, "uViewMatrix"),
406
+ color: this.gl.getUniformLocation(lineProgram, "uColor"),
407
+ time: this.gl.getUniformLocation(lineProgram, "uTime")
408
+ };
409
+
410
+ // Store the program for later use
411
+ this.lineProgram = lineProgram;
412
+
413
+ console.log("[ProgramManager] Line shader initialized");
414
+ }
415
+
416
+ /**
417
+ * Get locations for a standard shader
418
+ * @param {WebGLProgram} program - The WebGL program
419
+ * @param {boolean} isPBR - Whether this is a PBR shader
420
+ * @returns {Object} - Object containing all shader locations
421
+ */
422
+ getStandardShaderLocations(program, isPBR = false) {
423
+ const gl = this.gl;
424
+ const attr = this.attributeNames;
425
+ const unif = this.uniformNames;
426
+ const tex = this.textureUniforms;
427
+
428
+ // Get all attribute and uniform locations
429
+ return {
430
+ // Attributes
431
+ position: gl.getAttribLocation(program, attr.position),
432
+ normal: gl.getAttribLocation(program, attr.normal),
433
+ color: gl.getAttribLocation(program, attr.color),
434
+ texCoord: gl.getAttribLocation(program, attr.texCoord),
435
+ textureIndex: gl.getAttribLocation(program, attr.textureIndex),
436
+ useTexture: gl.getAttribLocation(program, attr.useTexture),
437
+
438
+ // Uniforms
439
+ projectionMatrix: gl.getUniformLocation(program, unif.projectionMatrix),
440
+ viewMatrix: gl.getUniformLocation(program, unif.viewMatrix),
441
+ modelMatrix: gl.getUniformLocation(program, unif.modelMatrix),
442
+ lightPos: gl.getUniformLocation(program, unif.lightPos),
443
+ lightDir: gl.getUniformLocation(program, unif.lightDir),
444
+ lightIntensity: gl.getUniformLocation(program, unif.lightIntensity),
445
+ pointLightIntensity: gl.getUniformLocation(program, unif.pointLightIntensity),
446
+ roughness: gl.getUniformLocation(program, unif.roughness),
447
+ metallic: gl.getUniformLocation(program, unif.metallic),
448
+ baseReflectivity: gl.getUniformLocation(program, unif.baseReflectivity),
449
+ usePerTextureMaterials: gl.getUniformLocation(program, 'uUsePerTextureMaterials'),
450
+ materialPropertiesTexture: gl.getUniformLocation(program, tex.materialProps),
451
+ cameraPos: gl.getUniformLocation(program, unif.cameraPos),
452
+ time: gl.getUniformLocation(program, unif.time),
453
+ intensityFactor: gl.getUniformLocation(program, unif.intensityFactor),
454
+
455
+ // Shadow mapping uniforms
456
+ lightSpaceMatrix: gl.getUniformLocation(program, unif.lightSpaceMatrix),
457
+ shadowMap: gl.getUniformLocation(program, unif.shadowMap),
458
+ shadowsEnabled: gl.getUniformLocation(program, unif.shadowsEnabled),
459
+
460
+ // Light counts
461
+ directionalLightCount: gl.getUniformLocation(program, "uDirectionalLightCount"),
462
+ pointLightCount: gl.getUniformLocation(program, "uPointLightCount"),
463
+ spotLightCount: gl.getUniformLocation(program, "uSpotLightCount"),
464
+
465
+ // Light data textures
466
+ directionalLightData: gl.getUniformLocation(program, "uDirectionalLightData"),
467
+ directionalLightTextureSize: gl.getUniformLocation(program, "uDirectionalLightTextureSize"),
468
+ pointLightData: gl.getUniformLocation(program, "uPointLightData"),
469
+ pointLightTextureSize: gl.getUniformLocation(program, "uPointLightTextureSize"),
470
+
471
+ farPlane: gl.getUniformLocation(program, "uFarPlane"),
472
+
473
+ // Legacy light uniforms
474
+ pointLightPos: gl.getUniformLocation(program, "uPointLightPos"),
475
+ pointLightColor: gl.getUniformLocation(program, "uPointLightColor"),
476
+ pointLightRadius: gl.getUniformLocation(program, "uLightRadius"),
477
+ pointShadowsEnabled: gl.getUniformLocation(program, "uPointShadowsEnabled"),
478
+ pointShadowMap: gl.getUniformLocation(program, "uPointShadowMap"),
479
+
480
+ // Additional point light uniforms (2-4)
481
+ pointLightPos1: gl.getUniformLocation(program, "uPointLightPos1"),
482
+ pointLightColor1: gl.getUniformLocation(program, "uPointLightColor1"),
483
+ pointLightRadius1: gl.getUniformLocation(program, "uPointLightRadius1"),
484
+ pointShadowsEnabled1: gl.getUniformLocation(program, "uPointShadowsEnabled1"),
485
+ pointShadowMap1: gl.getUniformLocation(program, "uPointShadowMap1"),
486
+ pointLightIntensity1: gl.getUniformLocation(program, "uPointLightIntensity1"),
487
+
488
+ // Third point light uniforms
489
+ pointShadowsEnabled2: gl.getUniformLocation(program, "uPointShadowsEnabled2"),
490
+ pointShadowMap2: gl.getUniformLocation(program, "uPointShadowMap2"),
491
+
492
+ // Fourth point light uniforms
493
+ pointShadowsEnabled3: gl.getUniformLocation(program, "uPointShadowsEnabled3"),
494
+ pointShadowMap3: gl.getUniformLocation(program, "uPointShadowMap3"),
495
+
496
+ // Texture uniform
497
+ textureArray: gl.getUniformLocation(program, isPBR ? tex.pbr : tex.standard)
498
+ };
499
+ }
500
+
501
+ /**
502
+ * Set the current line shader variant
503
+ * @param {string} variant - The shader variant to use ('default', 'virtualboy', etc.)
504
+ */
505
+ setLineShaderVariant(variant) {
506
+ if (!this.lineShader) {
507
+ console.warn("[ProgramManager] Line shader not initialized");
508
+ return;
509
+ }
510
+
511
+ // Update the line shader variant
512
+ this.lineShader.setVariant(variant);
513
+
514
+ // Reinitialize the line shader program
515
+ const newLineProgram = this.createShaderProgram(
516
+ this.lineShader.getVertexShader(this.isWebGL2),
517
+ this.lineShader.getFragmentShader(this.isWebGL2),
518
+ `line_shader_${variant}`
519
+ );
520
+
521
+ // Update the program and locations
522
+ this.lineProgram = newLineProgram;
523
+ this.lineLocations = {
524
+ position: this.gl.getAttribLocation(newLineProgram, "aPosition"),
525
+ projectionMatrix: this.gl.getUniformLocation(newLineProgram, "uProjectionMatrix"),
526
+ viewMatrix: this.gl.getUniformLocation(newLineProgram, "uViewMatrix"),
527
+ color: this.gl.getUniformLocation(newLineProgram, "uColor"),
528
+ time: this.gl.getUniformLocation(newLineProgram, "uTime")
529
+ };
530
+
531
+ console.log(`[ProgramManager] Line shader variant changed to: ${variant}`);
532
+ }
533
+
534
+ /**
535
+ * Update shaders when variant changes
536
+ * @param {string} variant - The name of the new variant
537
+ */
538
+ handleVariantChange(variant) {
539
+ if (variant === "virtualboy") {
540
+ this.setLineShaderVariant("virtualboy");
541
+ } else {
542
+ this.setLineShaderVariant("default");
543
+ }
544
+ }
545
+
546
+ // Accessor methods
547
+ getParticleProgram() {
548
+ return this.particleProgram;
549
+ }
550
+
551
+ getParticleLocations() {
552
+ return this.particleLocations;
553
+ }
554
+
555
+ getWaterProgram() {
556
+ return this.waterProgram;
557
+ }
558
+
559
+ getWaterLocations() {
560
+ return this.waterLocations;
561
+ }
562
+
563
+ getLineProgram() {
564
+ return this.lineProgram;
565
+ }
566
+
567
+ getLineLocations() {
568
+ return this.lineLocations;
569
+ }
570
+ }