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,44 @@
|
|
|
1
|
+
// actionengine/display/graphics/renderableobject.js
|
|
2
|
+
class RenderableObject {
|
|
3
|
+
constructor() {
|
|
4
|
+
// Add visual update tracking
|
|
5
|
+
this._visualDirty = true;
|
|
6
|
+
this._lastPosition = null;
|
|
7
|
+
this._lastRotation = null;
|
|
8
|
+
|
|
9
|
+
// Frustum culling properties
|
|
10
|
+
this.excludeFromFrustumCulling = false; // Objects can opt out if needed
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
markVisualDirty() {
|
|
14
|
+
this._visualDirty = true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
isVisualDirty() {
|
|
18
|
+
return this._visualDirty;
|
|
19
|
+
}
|
|
20
|
+
getModelMatrix() {
|
|
21
|
+
const matrix = Matrix4.create();
|
|
22
|
+
const rotationMatrix = Matrix4.create();
|
|
23
|
+
|
|
24
|
+
// Apply initial vertical offset
|
|
25
|
+
Matrix4.translate(matrix, matrix, [0, this.height / 8, 0]);
|
|
26
|
+
|
|
27
|
+
// Apply position
|
|
28
|
+
Matrix4.translate(matrix, matrix, this.position.toArray());
|
|
29
|
+
|
|
30
|
+
// Apply full rotation from physics body if it exists
|
|
31
|
+
if (this.body) {
|
|
32
|
+
Matrix4.fromQuat(rotationMatrix, this.body.rotation);
|
|
33
|
+
Matrix4.multiply(matrix, matrix, rotationMatrix);
|
|
34
|
+
} else {
|
|
35
|
+
// Fall back to simple Y rotation if no physics body
|
|
36
|
+
Matrix4.rotateY(matrix, matrix, this.rotation);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Apply scale
|
|
40
|
+
Matrix4.scale(matrix, matrix, [this.scale, this.scale, this.scale]);
|
|
41
|
+
|
|
42
|
+
return matrix;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
// actionengine/display/graphics/texture/texturemanager.js
|
|
2
|
+
class ActionRenderer2D {
|
|
3
|
+
constructor(canvas) {
|
|
4
|
+
this.ctx = canvas.getContext("2d");
|
|
5
|
+
this.width = Game.WIDTH;
|
|
6
|
+
this.height = Game.HEIGHT;
|
|
7
|
+
|
|
8
|
+
this.viewMatrix = Matrix4.create();
|
|
9
|
+
this.projMatrix = Matrix4.create();
|
|
10
|
+
|
|
11
|
+
this.imageData = this.ctx.createImageData(this.width, this.height);
|
|
12
|
+
this.zBuffer = new Float32Array(this.width * this.height);
|
|
13
|
+
|
|
14
|
+
// Configuration for depth handling
|
|
15
|
+
this.depthConfig = {
|
|
16
|
+
far: 10000.0,
|
|
17
|
+
transitionDistance: 250.0 // Where we switch to painter's algorithm
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Create procedural textures
|
|
21
|
+
this.grassTexture = new ProceduralTexture(256, 256);
|
|
22
|
+
this.grassTexture.generateGrass();
|
|
23
|
+
|
|
24
|
+
this.checkerTexture = new ProceduralTexture(256, 256);
|
|
25
|
+
this.checkerTexture.generateCheckerboard();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
render(camera, renderablePhysicsObjects, showDebugPanel, character) {
|
|
29
|
+
// Update visual representation of all renderable physics objects first
|
|
30
|
+
if (renderablePhysicsObjects) {
|
|
31
|
+
for (const object of renderablePhysicsObjects) {
|
|
32
|
+
if (object && typeof object.updateVisual === "function") {
|
|
33
|
+
object.updateVisual();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Get view matrix ONCE at the start
|
|
39
|
+
const view = camera.getViewMatrix();
|
|
40
|
+
// Calculate view and projection matrices ONCE
|
|
41
|
+
Matrix4.lookAt(
|
|
42
|
+
this.viewMatrix,
|
|
43
|
+
view.position.toArray(),
|
|
44
|
+
[view.position.x + view.forward.x, view.position.y + view.forward.y, view.position.z + view.forward.z],
|
|
45
|
+
view.up.toArray()
|
|
46
|
+
);
|
|
47
|
+
Matrix4.perspective(this.projMatrix, camera.fov, this.width / this.height, 0.1, 10000.0);
|
|
48
|
+
|
|
49
|
+
// Clear buffers
|
|
50
|
+
this.clearBuffers();
|
|
51
|
+
|
|
52
|
+
// Pass view to collectTriangles
|
|
53
|
+
const { nearTriangles, farTriangles } = this.collectTriangles(camera, renderablePhysicsObjects, view);
|
|
54
|
+
|
|
55
|
+
// Render far triangles first (back to front) WITHOUT depth testing
|
|
56
|
+
farTriangles.sort((a, b) => b.depth - a.depth);
|
|
57
|
+
for (const triangle of farTriangles) {
|
|
58
|
+
this.rasterizeTriangleNoDepth(triangle);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Render near triangles WITH depth testing
|
|
62
|
+
nearTriangles.sort((a, b) => b.depth - a.depth);
|
|
63
|
+
for (const triangle of nearTriangles) {
|
|
64
|
+
this.rasterizeTriangle(triangle);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Put final image to canvas
|
|
68
|
+
this.ctx.putImageData(this.imageData, 0, 0);
|
|
69
|
+
|
|
70
|
+
// Debug overlays if needed
|
|
71
|
+
if (showDebugPanel) {
|
|
72
|
+
this.renderDebugOverlays(character, camera, view); // Pass view here too
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
clearBuffers() {
|
|
77
|
+
const data = this.imageData.data;
|
|
78
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
79
|
+
data[i] = 135; // sky r
|
|
80
|
+
data[i + 1] = 206; // sky g
|
|
81
|
+
data[i + 2] = 235; // sky b
|
|
82
|
+
data[i + 3] = 255; // alpha
|
|
83
|
+
}
|
|
84
|
+
this.zBuffer.fill(Infinity);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
collectTriangles(camera, physicsObjects, view) {
|
|
88
|
+
const nearTriangles = [];
|
|
89
|
+
const farTriangles = [];
|
|
90
|
+
|
|
91
|
+
const processTriangle = (triangle) => {
|
|
92
|
+
// Calculate viewZ values once
|
|
93
|
+
const viewZs = triangle.vertices.map((vertex) => {
|
|
94
|
+
const viewSpace = vertex.sub(view.position);
|
|
95
|
+
return viewSpace.dot(view.forward);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// If ALL vertices are behind, skip it
|
|
99
|
+
if (viewZs.every((z) => z <= 0)) return;
|
|
100
|
+
// If ALL vertices are too far, skip it
|
|
101
|
+
if (viewZs.every((z) => z > this.depthConfig.far)) return;
|
|
102
|
+
// Back-face culling using viewspace positions
|
|
103
|
+
if (triangle.normal.dot(triangle.vertices[0].sub(view.position)) >= 0) return;
|
|
104
|
+
|
|
105
|
+
// Project using our cached viewZ values
|
|
106
|
+
const projectedVerts = triangle.vertices.map((v, i) => this.project(v, camera, view, viewZs[i]));
|
|
107
|
+
if (projectedVerts.some((v) => v === null)) return;
|
|
108
|
+
|
|
109
|
+
const lightDir = new Vector3(0.5, 1, 0.5).normalize();
|
|
110
|
+
const lighting = Math.max(0.3, Math.min(1.0, triangle.normal.dot(lightDir)));
|
|
111
|
+
|
|
112
|
+
const processedTriangle = {
|
|
113
|
+
points: projectedVerts,
|
|
114
|
+
color: triangle.color,
|
|
115
|
+
lighting: triangle.vertices[0].y === 0 ? 1.0 : lighting,
|
|
116
|
+
depth: (projectedVerts[0].z + projectedVerts[1].z + projectedVerts[2].z) / 3,
|
|
117
|
+
isWater: triangle.isWater || false,
|
|
118
|
+
uvs: triangle.uvs,
|
|
119
|
+
texture: triangle.texture
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Assign different textures based on distance
|
|
123
|
+
if (processedTriangle.depth <= this.depthConfig.transitionDistance) {
|
|
124
|
+
nearTriangles.push(processedTriangle);
|
|
125
|
+
} else {
|
|
126
|
+
farTriangles.push(processedTriangle);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Process physics object triangles
|
|
131
|
+
for (const physicsObject of physicsObjects) {
|
|
132
|
+
for (const triangle of physicsObject.triangles) {
|
|
133
|
+
processTriangle(triangle);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { nearTriangles, farTriangles };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
project(point, camera, view, cachedViewZ) {
|
|
141
|
+
const viewZ = cachedViewZ ?? point.sub(view.position).dot(view.forward);
|
|
142
|
+
|
|
143
|
+
const worldPoint = [point.x, point.y, point.z, 1];
|
|
144
|
+
const clipSpace = Matrix4.transformVector(worldPoint, this.viewMatrix, this.projMatrix);
|
|
145
|
+
|
|
146
|
+
const w = Math.max(0.1, clipSpace[3]);
|
|
147
|
+
const screenX = ((clipSpace[0] / w) * 0.5 + 0.5) * this.width;
|
|
148
|
+
const screenY = ((-clipSpace[1] / w) * 0.5 + 0.5) * this.height;
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
x: screenX,
|
|
152
|
+
y: screenY,
|
|
153
|
+
z: viewZ
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
rasterizeTriangleBase(triangle, useDepthTest = true) {
|
|
158
|
+
const points = triangle.points;
|
|
159
|
+
// Cache array access and bound calculations
|
|
160
|
+
const p0 = points[0],
|
|
161
|
+
p1 = points[1],
|
|
162
|
+
p2 = points[2];
|
|
163
|
+
const minX = Math.max(0, Math.floor(Math.min(p0.x, p1.x, p2.x)));
|
|
164
|
+
const maxX = Math.min(this.width - 1, Math.ceil(Math.max(p0.x, p1.x, p2.x)));
|
|
165
|
+
const minY = Math.max(0, Math.floor(Math.min(p0.y, p1.y, p2.y)));
|
|
166
|
+
const maxY = Math.min(this.height - 1, Math.ceil(Math.max(p0.y, p1.y, p2.y)));
|
|
167
|
+
|
|
168
|
+
// Pre-calculate color values once
|
|
169
|
+
const color = triangle.color;
|
|
170
|
+
const r = parseInt(color.substr(1, 2), 16);
|
|
171
|
+
const g = parseInt(color.substr(3, 2), 16);
|
|
172
|
+
const b = parseInt(color.substr(5, 2), 16);
|
|
173
|
+
const baseLighting = triangle.lighting;
|
|
174
|
+
|
|
175
|
+
// Cache texture-related values
|
|
176
|
+
const hasTexture = triangle.texture && triangle.uvs;
|
|
177
|
+
const imageData = this.imageData.data;
|
|
178
|
+
let oneOverW, uvOverW;
|
|
179
|
+
|
|
180
|
+
if (hasTexture) {
|
|
181
|
+
oneOverW = [1 / Math.max(0.1, p0.z), 1 / Math.max(0.1, p1.z), 1 / Math.max(0.1, p2.z)];
|
|
182
|
+
const uvs = triangle.uvs;
|
|
183
|
+
uvOverW = [
|
|
184
|
+
{ u: uvs[0].u * oneOverW[0], v: uvs[0].v * oneOverW[0] },
|
|
185
|
+
{ u: uvs[1].u * oneOverW[1], v: uvs[1].v * oneOverW[1] },
|
|
186
|
+
{ u: uvs[2].u * oneOverW[2], v: uvs[2].v * oneOverW[2] }
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Cache texture dimensions if available
|
|
191
|
+
const textureWidth = hasTexture ? triangle.texture.width : 0;
|
|
192
|
+
const textureHeight = hasTexture ? triangle.texture.height : 0;
|
|
193
|
+
|
|
194
|
+
const BLOCK_SIZE = 8;
|
|
195
|
+
const isWater = triangle.isWater;
|
|
196
|
+
const zBuffer = this.zBuffer;
|
|
197
|
+
|
|
198
|
+
// Pre-calculate block boundaries
|
|
199
|
+
const numBlocksX = Math.ceil((maxX - minX + 1) / BLOCK_SIZE);
|
|
200
|
+
const numBlocksY = Math.ceil((maxY - minY + 1) / BLOCK_SIZE);
|
|
201
|
+
|
|
202
|
+
for (let blockYIndex = 0; blockYIndex < numBlocksY; blockYIndex++) {
|
|
203
|
+
const blockY = minY + blockYIndex * BLOCK_SIZE;
|
|
204
|
+
const endY = Math.min(blockY + BLOCK_SIZE, maxY + 1);
|
|
205
|
+
|
|
206
|
+
for (let blockXIndex = 0; blockXIndex < numBlocksX; blockXIndex++) {
|
|
207
|
+
const blockX = minX + blockXIndex * BLOCK_SIZE;
|
|
208
|
+
const endX = Math.min(blockX + BLOCK_SIZE, maxX + 1);
|
|
209
|
+
|
|
210
|
+
for (let y = blockY; y < endY; y++) {
|
|
211
|
+
const rowOffset = y * this.width;
|
|
212
|
+
for (let x = blockX; x < endX; x++) {
|
|
213
|
+
if (!TriangleUtils.pointInTriangle({ x, y }, p0, p1, p2)) continue;
|
|
214
|
+
|
|
215
|
+
const index = rowOffset + x;
|
|
216
|
+
let currentLighting = baseLighting;
|
|
217
|
+
|
|
218
|
+
// Calculate barycentric coords once
|
|
219
|
+
const bary = TriangleUtils.getBarycentricCoords(x, y, p0, p1, p2);
|
|
220
|
+
|
|
221
|
+
// Z-buffer and water effects
|
|
222
|
+
if (isWater || useDepthTest) {
|
|
223
|
+
// Use bary coords instead of recalculating
|
|
224
|
+
const z = bary.w1 * p0.z + bary.w2 * p1.z + bary.w3 * p2.z;
|
|
225
|
+
|
|
226
|
+
if (isWater) {
|
|
227
|
+
currentLighting *= Math.sin(performance.now() / 1000 + z / 50) * 0.1 + 0.9;
|
|
228
|
+
}
|
|
229
|
+
if (useDepthTest && z >= zBuffer[index]) continue;
|
|
230
|
+
if (useDepthTest) zBuffer[index] = z;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const pixelIndex = index * 4;
|
|
234
|
+
|
|
235
|
+
if (hasTexture) {
|
|
236
|
+
let u, v;
|
|
237
|
+
if (useDepthTest) {
|
|
238
|
+
// Full perspective-correct texture mapping for near triangles
|
|
239
|
+
const interpolatedOneOverW =
|
|
240
|
+
bary.w1 * oneOverW[0] + bary.w2 * oneOverW[1] + bary.w3 * oneOverW[2];
|
|
241
|
+
const interpolatedUOverW =
|
|
242
|
+
bary.w1 * uvOverW[0].u + bary.w2 * uvOverW[1].u + bary.w3 * uvOverW[2].u;
|
|
243
|
+
const interpolatedVOverW =
|
|
244
|
+
bary.w1 * uvOverW[0].v + bary.w2 * uvOverW[1].v + bary.w3 * uvOverW[2].v;
|
|
245
|
+
u = interpolatedUOverW / interpolatedOneOverW;
|
|
246
|
+
v = interpolatedVOverW / interpolatedOneOverW;
|
|
247
|
+
} else {
|
|
248
|
+
// Simpler linear interpolation for far triangles
|
|
249
|
+
u =
|
|
250
|
+
bary.w1 * triangle.uvs[0].u +
|
|
251
|
+
bary.w2 * triangle.uvs[1].u +
|
|
252
|
+
bary.w3 * triangle.uvs[2].u;
|
|
253
|
+
v =
|
|
254
|
+
bary.w1 * triangle.uvs[0].v +
|
|
255
|
+
bary.w2 * triangle.uvs[1].v +
|
|
256
|
+
bary.w3 * triangle.uvs[2].v;
|
|
257
|
+
}
|
|
258
|
+
const texel = triangle.texture.getPixel(
|
|
259
|
+
Math.floor(u * textureWidth),
|
|
260
|
+
Math.floor(v * textureHeight)
|
|
261
|
+
);
|
|
262
|
+
imageData[pixelIndex] = texel.r * currentLighting;
|
|
263
|
+
imageData[pixelIndex + 1] = texel.g * currentLighting;
|
|
264
|
+
imageData[pixelIndex + 2] = texel.b * currentLighting;
|
|
265
|
+
imageData[pixelIndex + 3] = 255;
|
|
266
|
+
} else {
|
|
267
|
+
imageData[pixelIndex] = r * currentLighting;
|
|
268
|
+
imageData[pixelIndex + 1] = g * currentLighting;
|
|
269
|
+
imageData[pixelIndex + 2] = b * currentLighting;
|
|
270
|
+
imageData[pixelIndex + 3] = 255;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
rasterizeTriangle(triangle) {
|
|
279
|
+
this.rasterizeTriangleBase(triangle, true);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
rasterizeTriangleNoDepth(triangle) {
|
|
283
|
+
this.rasterizeTriangleBase(triangle, false);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
renderDebugOverlays(character, camera, view) {
|
|
287
|
+
const ctx = this.ctx;
|
|
288
|
+
// Add null check to prevent error when character is null
|
|
289
|
+
if (!character || !character.getCurrentTriangle) {
|
|
290
|
+
return; // Skip debug visualization if character is not valid
|
|
291
|
+
}
|
|
292
|
+
const currentTriangle = character.getCurrentTriangle();
|
|
293
|
+
if (currentTriangle) {
|
|
294
|
+
const center = {
|
|
295
|
+
x: (currentTriangle.vertices[0].x + currentTriangle.vertices[1].x + currentTriangle.vertices[2].x) / 3,
|
|
296
|
+
y: (currentTriangle.vertices[0].y + currentTriangle.vertices[1].y + currentTriangle.vertices[2].y) / 3,
|
|
297
|
+
z: (currentTriangle.vertices[0].z + currentTriangle.vertices[1].z + currentTriangle.vertices[2].z) / 3
|
|
298
|
+
};
|
|
299
|
+
const normalEnd = {
|
|
300
|
+
x: center.x + currentTriangle.normal.x * 10,
|
|
301
|
+
y: center.y + currentTriangle.normal.y * 10,
|
|
302
|
+
z: center.z + currentTriangle.normal.z * 10
|
|
303
|
+
};
|
|
304
|
+
const projectedCenter = this.project(new Vector3(center.x, center.y, center.z), camera, view);
|
|
305
|
+
const projectedEnd = this.project(new Vector3(normalEnd.x, normalEnd.y, normalEnd.z), camera, view);
|
|
306
|
+
if (projectedCenter && projectedEnd) {
|
|
307
|
+
ctx.strokeStyle = "#0000FF";
|
|
308
|
+
ctx.beginPath();
|
|
309
|
+
ctx.moveTo(projectedCenter.x, projectedCenter.y);
|
|
310
|
+
ctx.lineTo(projectedEnd.x, projectedEnd.y);
|
|
311
|
+
ctx.stroke();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
this.renderDirectionIndicator(character, camera, view);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
renderDirectionIndicator(character, camera, view) {
|
|
318
|
+
// Add null check to prevent error when character is null
|
|
319
|
+
if (!character || !character.position || !character.facingDirection) {
|
|
320
|
+
return; // Skip direction indicator if character is not valid
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const center = character.position;
|
|
324
|
+
const directionEnd = new Vector3(
|
|
325
|
+
center.x + character.facingDirection.x * (character.size || 5) * 2, // Default size if undefined
|
|
326
|
+
center.y,
|
|
327
|
+
center.z + character.facingDirection.z * (character.size || 5) * 2
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
const projectedCenter = this.project(center, camera, view);
|
|
331
|
+
const projectedEnd = this.project(directionEnd, camera, view);
|
|
332
|
+
|
|
333
|
+
if (projectedCenter && projectedEnd) {
|
|
334
|
+
this.ctx.strokeStyle = "#0000FF";
|
|
335
|
+
this.ctx.beginPath();
|
|
336
|
+
this.ctx.moveTo(projectedCenter.x, projectedCenter.y);
|
|
337
|
+
this.ctx.lineTo(projectedEnd.x, projectedEnd.y);
|
|
338
|
+
this.ctx.stroke();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|