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.
- package/LICENSE +45 -0
- package/README.md +348 -0
- package/actionengine/3rdparty/goblin/goblin.js +9609 -0
- package/actionengine/3rdparty/goblin/goblin.min.js +5 -0
- package/actionengine/camera/actioncamera.js +90 -0
- package/actionengine/camera/cameracollisionhandler.js +69 -0
- package/actionengine/character/actioncharacter.js +360 -0
- package/actionengine/character/actioncharacter3D.js +61 -0
- package/actionengine/core/app.js +430 -0
- package/actionengine/debug/basedebugpanel.js +858 -0
- package/actionengine/display/canvasmanager.js +75 -0
- package/actionengine/display/gl/programmanager.js +570 -0
- package/actionengine/display/gl/shaders/lineshader.js +118 -0
- package/actionengine/display/gl/shaders/objectshader.js +1756 -0
- package/actionengine/display/gl/shaders/particleshader.js +43 -0
- package/actionengine/display/gl/shaders/shadowshader.js +319 -0
- package/actionengine/display/gl/shaders/spriteshader.js +100 -0
- package/actionengine/display/gl/shaders/watershader.js +67 -0
- package/actionengine/display/graphics/actionmodel3D.js +191 -0
- package/actionengine/display/graphics/actionsprite3D.js +230 -0
- package/actionengine/display/graphics/lighting/actiondirectionalshadowlight.js +864 -0
- package/actionengine/display/graphics/lighting/actionlight.js +211 -0
- package/actionengine/display/graphics/lighting/actionomnidirectionalshadowlight.js +862 -0
- package/actionengine/display/graphics/lighting/lightingconstants.js +263 -0
- package/actionengine/display/graphics/lighting/lightmanager.js +789 -0
- package/actionengine/display/graphics/renderableobject.js +44 -0
- package/actionengine/display/graphics/renderers/actionrenderer2D.js +341 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/actionrenderer3D.js +655 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/canvasmanager3D.js +82 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/debugrenderer3D.js +493 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/objectrenderer3D.js +790 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/spriteRenderer3D.js +266 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/sunrenderer3D.js +140 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/waterrenderer3D.js +173 -0
- package/actionengine/display/graphics/renderers/actionrenderer3D/weatherrenderer3D.js +87 -0
- package/actionengine/display/graphics/texture/proceduraltexture.js +192 -0
- package/actionengine/display/graphics/texture/texturemanager.js +242 -0
- package/actionengine/display/graphics/texture/textureregistry.js +177 -0
- package/actionengine/input/actionscrollablearea.js +1405 -0
- package/actionengine/input/inputhandler.js +1647 -0
- package/actionengine/math/geometry/geometrybuilder.js +161 -0
- package/actionengine/math/geometry/glbexporter.js +364 -0
- package/actionengine/math/geometry/glbloader.js +722 -0
- package/actionengine/math/geometry/modelcodegenerator.js +97 -0
- package/actionengine/math/geometry/triangle.js +33 -0
- package/actionengine/math/geometry/triangleutils.js +34 -0
- package/actionengine/math/mathutils.js +25 -0
- package/actionengine/math/matrix4.js +785 -0
- package/actionengine/math/physics/actionphysics.js +108 -0
- package/actionengine/math/physics/actionphysicsobject3D.js +164 -0
- package/actionengine/math/physics/actionphysicsworld3D.js +238 -0
- package/actionengine/math/physics/actionraycast.js +129 -0
- package/actionengine/math/physics/shapes/actionphysicsbox3D.js +158 -0
- package/actionengine/math/physics/shapes/actionphysicscapsule3D.js +200 -0
- package/actionengine/math/physics/shapes/actionphysicscompoundshape3D.js +147 -0
- package/actionengine/math/physics/shapes/actionphysicscone3D.js +126 -0
- package/actionengine/math/physics/shapes/actionphysicsconvexshape3D.js +72 -0
- package/actionengine/math/physics/shapes/actionphysicscylinder3D.js +117 -0
- package/actionengine/math/physics/shapes/actionphysicsmesh3D.js +74 -0
- package/actionengine/math/physics/shapes/actionphysicsplane3D.js +100 -0
- package/actionengine/math/physics/shapes/actionphysicssphere3D.js +95 -0
- package/actionengine/math/quaternion.js +61 -0
- package/actionengine/math/vector2.js +277 -0
- package/actionengine/math/vector3.js +318 -0
- package/actionengine/math/viewfrustum.js +136 -0
- package/actionengine/network/ACTIONNETREADME.md +810 -0
- package/actionengine/network/client/ActionNetManager.js +802 -0
- package/actionengine/network/client/ActionNetManagerGUI.js +1709 -0
- package/actionengine/network/client/ActionNetManagerP2P.js +1537 -0
- package/actionengine/network/client/SyncSystem.js +422 -0
- package/actionengine/network/p2p/ActionNetPeer.js +142 -0
- package/actionengine/network/p2p/ActionNetTrackerClient.js +623 -0
- package/actionengine/network/p2p/DataConnection.js +282 -0
- package/actionengine/network/p2p/README.md +510 -0
- package/actionengine/network/p2p/example.html +502 -0
- package/actionengine/network/server/ActionNetServer.js +577 -0
- package/actionengine/network/server/ActionNetServerSSL.js +579 -0
- package/actionengine/network/server/ActionNetServerUtils.js +458 -0
- package/actionengine/network/server/SERVERREADME.md +314 -0
- package/actionengine/network/server/package-lock.json +35 -0
- package/actionengine/network/server/package.json +13 -0
- package/actionengine/network/server/start.bat +27 -0
- package/actionengine/network/server/start.sh +25 -0
- package/actionengine/network/server/startwss.bat +27 -0
- package/actionengine/sound/audiomanager.js +1589 -0
- package/actionengine/sound/soundfont/ACTIONSOUNDFONT_README.md +205 -0
- package/actionengine/sound/soundfont/actionparser.js +718 -0
- package/actionengine/sound/soundfont/actionreverb.js +252 -0
- package/actionengine/sound/soundfont/actionsoundfont.js +543 -0
- package/actionengine/sound/soundfont/sf2playerlicence.txt +29 -0
- package/actionengine/sound/soundfont/soundfont.js +2 -0
- package/dist/action-engine.min.js +328 -0
- 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
|
+
}
|