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,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
|
+
}
|