emeraldengine 2.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/README.md +968 -0
- package/dist/types/imports/BitmapText.d.ts +79 -0
- package/dist/types/imports/Color.d.ts +17 -0
- package/dist/types/imports/Drawable.d.ts +157 -0
- package/dist/types/imports/Emerald.d.ts +73 -0
- package/dist/types/imports/FPSCounter.d.ts +19 -0
- package/dist/types/imports/GLUtils.d.ts +6 -0
- package/dist/types/imports/Instance.d.ts +87 -0
- package/dist/types/imports/InstancedTexture.d.ts +230 -0
- package/dist/types/imports/Physics.d.ts +139 -0
- package/dist/types/imports/Scene.d.ts +39 -0
- package/dist/types/imports/Shaders.d.ts +4 -0
- package/dist/types/imports/Shapes.d.ts +25 -0
- package/dist/types/imports/Storage.d.ts +46 -0
- package/dist/types/imports/Texture.d.ts +19 -0
- package/dist/types/imports/Time.d.ts +22 -0
- package/dist/types/imports/Transform.d.ts +41 -0
- package/dist/types/imports/components/BoxCollider.d.ts +41 -0
- package/dist/types/imports/components/BoxColliderDebug.d.ts +19 -0
- package/dist/types/imports/components/CircleCollider.d.ts +39 -0
- package/dist/types/imports/components/CircleColliderDebug.d.ts +19 -0
- package/dist/types/imports/components/Collider.d.ts +53 -0
- package/dist/types/imports/components/GameObject.d.ts +72 -0
- package/dist/types/imports/components/RigidBody.d.ts +104 -0
- package/dist/types/imports/lights/DirectionalLight.d.ts +26 -0
- package/dist/types/imports/lights/PointLight.d.ts +18 -0
- package/dist/types/imports/managers/AudioManager.d.ts +60 -0
- package/dist/types/imports/managers/CameraManager.d.ts +35 -0
- package/dist/types/imports/managers/EventManager.d.ts +187 -0
- package/dist/types/imports/managers/GLManager.d.ts +47 -0
- package/dist/types/imports/managers/IDManager.d.ts +21 -0
- package/dist/types/imports/managers/SceneManager.d.ts +22 -0
- package/dist/types/imports/particlesystem/Particle.d.ts +42 -0
- package/dist/types/imports/particlesystem/ParticleSettings.d.ts +49 -0
- package/dist/types/imports/particlesystem/Particles.d.ts +71 -0
- package/dist/types/index.d.ts +32 -0
- package/imports/BitmapText.js +245 -0
- package/imports/Color.js +18 -0
- package/imports/Drawable.js +550 -0
- package/imports/Emerald.js +459 -0
- package/imports/FPSCounter.js +43 -0
- package/imports/GLUtils.js +67 -0
- package/imports/Instance.js +187 -0
- package/imports/InstancedTexture.js +856 -0
- package/imports/Physics.js +242 -0
- package/imports/Scene.js +79 -0
- package/imports/Shaders.js +165 -0
- package/imports/Shapes.js +129 -0
- package/imports/Storage.js +63 -0
- package/imports/Texture.js +67 -0
- package/imports/Time.js +28 -0
- package/imports/Transform.js +59 -0
- package/imports/components/BoxCollider.js +67 -0
- package/imports/components/BoxColliderDebug.js +38 -0
- package/imports/components/CircleCollider.js +67 -0
- package/imports/components/CircleColliderDebug.js +36 -0
- package/imports/components/Collider.js +51 -0
- package/imports/components/GameObject.js +160 -0
- package/imports/components/RigidBody.js +169 -0
- package/imports/lights/DirectionalLight.js +68 -0
- package/imports/lights/PointLight.js +17 -0
- package/imports/managers/AudioManager.js +146 -0
- package/imports/managers/CameraManager.js +60 -0
- package/imports/managers/EventManager.js +479 -0
- package/imports/managers/GLManager.js +65 -0
- package/imports/managers/IDManager.js +32 -0
- package/imports/managers/SceneManager.js +27 -0
- package/imports/managers/ShaderManager.js +133 -0
- package/imports/particlesystem/Particle.js +75 -0
- package/imports/particlesystem/ParticleSettings.js +49 -0
- package/imports/particlesystem/Particles.js +226 -0
- package/index.js +67 -0
- package/package.json +38 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { STANDARD_FRAGMENT_SHADER, STANDARD_VERTEX_SHADER } from "./Shaders.js";
|
|
2
|
+
import { initShaderProgram } from "./GLUtils.js";
|
|
3
|
+
import { mat4 } from "gl-matrix";
|
|
4
|
+
import Color from "./Color.js";
|
|
5
|
+
import Transform from "./Transform.js";
|
|
6
|
+
import { Vector2, Vector3 } from "./Physics.js";
|
|
7
|
+
import Time from "./Time.js";
|
|
8
|
+
import GLManager from "./managers/GLManager.js";
|
|
9
|
+
import CameraManager from "./managers/CameraManager.js";
|
|
10
|
+
import PointLight from "./lights/PointLight.js";
|
|
11
|
+
import DirectionalLight from "./lights/DirectionalLight.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @class Emerald
|
|
15
|
+
* @description A class that represents the Emerald engine
|
|
16
|
+
* @param {HTMLCanvasElement} canvas - The canvas element
|
|
17
|
+
*/
|
|
18
|
+
class Emerald {
|
|
19
|
+
constructor(canvas) {
|
|
20
|
+
const gl = canvas.getContext("webgl2", {
|
|
21
|
+
antialias: true,
|
|
22
|
+
powerPreference: "high-performance",
|
|
23
|
+
desynchronized: false,
|
|
24
|
+
});
|
|
25
|
+
if (!gl) {
|
|
26
|
+
console.error("[Emerald.js] > No WebGL context found");
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const shaderProgram = initShaderProgram(
|
|
31
|
+
gl,
|
|
32
|
+
STANDARD_VERTEX_SHADER,
|
|
33
|
+
STANDARD_FRAGMENT_SHADER
|
|
34
|
+
);
|
|
35
|
+
this.shaderProgram = shaderProgram;
|
|
36
|
+
this.gl = gl;
|
|
37
|
+
this.programInfo = {
|
|
38
|
+
program: shaderProgram,
|
|
39
|
+
attribLocations: {
|
|
40
|
+
vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
|
|
41
|
+
aTexCoord: gl.getAttribLocation(shaderProgram, "aTextureCoord"),
|
|
42
|
+
instanceMatrix: gl.getAttribLocation(shaderProgram, "aInstanceMatrix0"),
|
|
43
|
+
instanceTexCoord0: gl.getAttribLocation(
|
|
44
|
+
shaderProgram,
|
|
45
|
+
"aInstanceTexCoord0"
|
|
46
|
+
),
|
|
47
|
+
instanceTexCoord1: gl.getAttribLocation(
|
|
48
|
+
shaderProgram,
|
|
49
|
+
"aInstanceTexCoord1"
|
|
50
|
+
),
|
|
51
|
+
instanceTexCoord2: gl.getAttribLocation(
|
|
52
|
+
shaderProgram,
|
|
53
|
+
"aInstanceTexCoord2"
|
|
54
|
+
),
|
|
55
|
+
instanceTexCoord3: gl.getAttribLocation(
|
|
56
|
+
shaderProgram,
|
|
57
|
+
"aInstanceTexCoord3"
|
|
58
|
+
),
|
|
59
|
+
},
|
|
60
|
+
uniformLocations: {
|
|
61
|
+
projectionMatrix: gl.getUniformLocation(
|
|
62
|
+
shaderProgram,
|
|
63
|
+
"uProjectionMatrix"
|
|
64
|
+
),
|
|
65
|
+
globalViewMatrix: gl.getUniformLocation(
|
|
66
|
+
shaderProgram,
|
|
67
|
+
"uModelViewMatrix"
|
|
68
|
+
),
|
|
69
|
+
instancedModelViewMatrix: gl.getUniformLocation(
|
|
70
|
+
shaderProgram,
|
|
71
|
+
"uInstancedModelViewMatrix"
|
|
72
|
+
),
|
|
73
|
+
uModelMatrix: gl.getUniformLocation(shaderProgram, "uModelMatrix"),
|
|
74
|
+
uInstancedModelMatrix: gl.getUniformLocation(
|
|
75
|
+
shaderProgram,
|
|
76
|
+
"uInstancedModelMatrix"
|
|
77
|
+
),
|
|
78
|
+
uAmbientLightValues: gl.getUniformLocation(
|
|
79
|
+
shaderProgram,
|
|
80
|
+
"uAmbientLightValues"
|
|
81
|
+
),
|
|
82
|
+
uSampler: gl.getUniformLocation(shaderProgram, "uSampler"),
|
|
83
|
+
color: gl.getUniformLocation(shaderProgram, "uColor"),
|
|
84
|
+
useTexture: gl.getUniformLocation(shaderProgram, "useTexture"),
|
|
85
|
+
useInstances: gl.getUniformLocation(shaderProgram, "useInstances"),
|
|
86
|
+
useText: gl.getUniformLocation(shaderProgram, "useText"),
|
|
87
|
+
useLighting: gl.getUniformLocation(shaderProgram, "uUseLighting"),
|
|
88
|
+
uOpacity: gl.getUniformLocation(shaderProgram, "uOpacity"),
|
|
89
|
+
// Point light uniforms
|
|
90
|
+
uLightPosition: gl.getUniformLocation(shaderProgram, "uLightPosition"),
|
|
91
|
+
uLightColor: gl.getUniformLocation(shaderProgram, "uLightColor"),
|
|
92
|
+
uLightIntensity: gl.getUniformLocation(
|
|
93
|
+
shaderProgram,
|
|
94
|
+
"uLightIntensity"
|
|
95
|
+
),
|
|
96
|
+
uLightRadius: gl.getUniformLocation(shaderProgram, "uLightRadius"),
|
|
97
|
+
uActiveLights: gl.getUniformLocation(shaderProgram, "uActiveLights"),
|
|
98
|
+
// Directional light uniforms
|
|
99
|
+
uDirLightPosition: gl.getUniformLocation(
|
|
100
|
+
shaderProgram,
|
|
101
|
+
"uDirLightPosition"
|
|
102
|
+
),
|
|
103
|
+
uDirLightDirection: gl.getUniformLocation(
|
|
104
|
+
shaderProgram,
|
|
105
|
+
"uDirLightDirection"
|
|
106
|
+
),
|
|
107
|
+
uDirLightColor: gl.getUniformLocation(shaderProgram, "uDirLightColor"),
|
|
108
|
+
uDirLightIntensity: gl.getUniformLocation(
|
|
109
|
+
shaderProgram,
|
|
110
|
+
"uDirLightIntensity"
|
|
111
|
+
),
|
|
112
|
+
uDirLightWidth: gl.getUniformLocation(shaderProgram, "uDirLightWidth"),
|
|
113
|
+
uActiveDirLights: gl.getUniformLocation(
|
|
114
|
+
shaderProgram,
|
|
115
|
+
"uActiveDirLights"
|
|
116
|
+
),
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
GLManager.setGL(gl);
|
|
120
|
+
GLManager.setProgramInfo(this.programInfo);
|
|
121
|
+
this.camera = {
|
|
122
|
+
transform: new Transform(new Vector3(0, 0, 0), 0, new Vector2(1, 1)),
|
|
123
|
+
setPosition(x, y, z) {
|
|
124
|
+
this.transform.position.x = -x;
|
|
125
|
+
this.transform.position.y = -y;
|
|
126
|
+
this.transform.position.z = -z;
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
CameraManager.setCamera(this.camera);
|
|
131
|
+
|
|
132
|
+
this.ambientLight = new Vector3(1.0, 1.0, 1.0);
|
|
133
|
+
this.pointLights = [];
|
|
134
|
+
this.directionalLights = [];
|
|
135
|
+
this.debugLogged = false; // Flag to prevent spam in console
|
|
136
|
+
this.backgroundColor = {
|
|
137
|
+
r: 0.0,
|
|
138
|
+
g: 0.0,
|
|
139
|
+
b: 0.0,
|
|
140
|
+
a: 1.0,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @method setAmbientLight
|
|
146
|
+
* @description Sets the ambient light for the scene
|
|
147
|
+
* @param {Vector3} vector3 - The ambient light color as a Vector3 instance
|
|
148
|
+
*/
|
|
149
|
+
setAmbientLight(vector3) {
|
|
150
|
+
if (vector3.x && vector3.y && vector3.z) {
|
|
151
|
+
this.ambientLight = vector3;
|
|
152
|
+
} else {
|
|
153
|
+
console.error(
|
|
154
|
+
"[Emerald.js] > vector3 is not an instance of Vector3 class."
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @method resize
|
|
161
|
+
* @description Resizes the canvas
|
|
162
|
+
* @param {number} width - The width of the canvas
|
|
163
|
+
* @param {number} height - The height of the canvas
|
|
164
|
+
*/
|
|
165
|
+
resize(width, height) {
|
|
166
|
+
// Update canvas dimensions
|
|
167
|
+
this.gl.canvas.width = width;
|
|
168
|
+
this.gl.canvas.height = height;
|
|
169
|
+
|
|
170
|
+
// Update WebGL viewport
|
|
171
|
+
this.gl.viewport(0, 0, width, height);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @method addPointLight
|
|
176
|
+
* @description Adds a point light to the scene
|
|
177
|
+
* @param {PointLight} pointLight - The point light to add
|
|
178
|
+
*/
|
|
179
|
+
addPointLight(pointLight) {
|
|
180
|
+
if (!(pointLight instanceof PointLight)) {
|
|
181
|
+
console.error(
|
|
182
|
+
"[Emerald.js] > Passed argument is not an instance of PointLight class."
|
|
183
|
+
);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (this.pointLights.length < 4) {
|
|
187
|
+
// Maximum 4 lights as defined in shader
|
|
188
|
+
this.pointLights.push(pointLight);
|
|
189
|
+
} else {
|
|
190
|
+
console.warn("Maximum number of point lights reached (4)");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @method addDirectionalLight
|
|
196
|
+
* @description Adds a directional light to the scene
|
|
197
|
+
* @param {DirectionalLight} directionalLight - The directional light to add
|
|
198
|
+
*/
|
|
199
|
+
addDirectionalLight(directionalLight) {
|
|
200
|
+
if (!(directionalLight instanceof DirectionalLight)) {
|
|
201
|
+
console.error(
|
|
202
|
+
"[Emerald.js] > Passed argument is not an instance of DirectionalLight class."
|
|
203
|
+
);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (this.directionalLights.length < 4) {
|
|
207
|
+
// Maximum 4 directional lights as defined in shader
|
|
208
|
+
this.directionalLights.push(directionalLight);
|
|
209
|
+
} else {
|
|
210
|
+
console.warn("Maximum number of directional lights reached (4)");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @method removeDirectionalLight
|
|
216
|
+
* @description Removes a directional light from the scene
|
|
217
|
+
* @param {DirectionalLight} directionalLight - The directional light to remove
|
|
218
|
+
*/
|
|
219
|
+
removeDirectionalLight(directionalLight) {
|
|
220
|
+
const index = this.directionalLights.indexOf(directionalLight);
|
|
221
|
+
if (index > -1) {
|
|
222
|
+
this.directionalLights.splice(index, 1);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @method setBackgroundColor
|
|
228
|
+
* @description Sets the background color of the scene
|
|
229
|
+
* @param {Color} color - The background color
|
|
230
|
+
*/
|
|
231
|
+
setBackgroundColor(color) {
|
|
232
|
+
if (color instanceof Color) {
|
|
233
|
+
this.backgroundColor = {
|
|
234
|
+
r: color.r / 255,
|
|
235
|
+
g: color.g / 255,
|
|
236
|
+
b: color.b / 255,
|
|
237
|
+
a: color.a / 255,
|
|
238
|
+
};
|
|
239
|
+
} else {
|
|
240
|
+
console.error("[Emerald.js] > color is not an instance of Color class.");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Buggy (events don't work correctly when zoom changes from 1)
|
|
244
|
+
// setZoom(float) {
|
|
245
|
+
// vec3.set(this.camera.scale, float, float, float);
|
|
246
|
+
// }
|
|
247
|
+
|
|
248
|
+
// getZoom() {
|
|
249
|
+
// return this.camera.scale[0];
|
|
250
|
+
// }
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @method drawScene
|
|
254
|
+
* @description Draws the scene
|
|
255
|
+
* @param {Scene} scene - The scene to draw
|
|
256
|
+
* @param {number} deltaTime - The delta time
|
|
257
|
+
*/
|
|
258
|
+
drawScene(scene, deltaTime) {
|
|
259
|
+
Time.setDeltaTime(deltaTime);
|
|
260
|
+
if (this.backgroundColor) {
|
|
261
|
+
this.gl.clearColor(
|
|
262
|
+
this.backgroundColor.r,
|
|
263
|
+
this.backgroundColor.g,
|
|
264
|
+
this.backgroundColor.b,
|
|
265
|
+
this.backgroundColor.a
|
|
266
|
+
);
|
|
267
|
+
} else {
|
|
268
|
+
this.gl.clearColor(0.0, 0.0, 0.0, 1.0); // Default to black if no background color is set
|
|
269
|
+
}
|
|
270
|
+
// 2D rendering: disable depth testing to allow proper alpha blending with back-to-front draw order
|
|
271
|
+
this.gl.clearDepth(1.0);
|
|
272
|
+
this.gl.disable(this.gl.DEPTH_TEST);
|
|
273
|
+
|
|
274
|
+
this.gl.enable(this.gl.BLEND);
|
|
275
|
+
// Keep framebuffer alpha at 1.0 to avoid page compositing artifacts while blending RGB correctly
|
|
276
|
+
// newRGB = srcRGB * srcA + dstRGB * (1 - srcA)
|
|
277
|
+
// newA = 1 * srcA + (1 - srcA) * 1 = 1
|
|
278
|
+
this.gl.blendFuncSeparate(
|
|
279
|
+
this.gl.SRC_ALPHA,
|
|
280
|
+
this.gl.ONE_MINUS_SRC_ALPHA,
|
|
281
|
+
this.gl.ONE,
|
|
282
|
+
this.gl.ONE_MINUS_SRC_ALPHA
|
|
283
|
+
);
|
|
284
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
|
285
|
+
|
|
286
|
+
const projectionMatrix = mat4.create();
|
|
287
|
+
const left = -this.gl.canvas.clientWidth / 2;
|
|
288
|
+
const right = this.gl.canvas.clientWidth / 2;
|
|
289
|
+
const bottom = -this.gl.canvas.clientHeight / 2;
|
|
290
|
+
const top = this.gl.canvas.clientHeight / 2;
|
|
291
|
+
const near = -100;
|
|
292
|
+
const far = 100;
|
|
293
|
+
mat4.ortho(projectionMatrix, left, right, bottom, top, near, far);
|
|
294
|
+
|
|
295
|
+
const globalViewMatrix = mat4.create();
|
|
296
|
+
mat4.translate(globalViewMatrix, globalViewMatrix, [
|
|
297
|
+
this.camera.transform.position.x,
|
|
298
|
+
this.camera.transform.position.y,
|
|
299
|
+
this.camera.transform.position.z,
|
|
300
|
+
]); // Translation
|
|
301
|
+
mat4.rotate(globalViewMatrix, globalViewMatrix, 0, [1, 0, 0]); // X-axis rotation
|
|
302
|
+
mat4.rotate(globalViewMatrix, globalViewMatrix, 0, [0, 1, 0]); // Y-axis rotation
|
|
303
|
+
mat4.rotate(
|
|
304
|
+
globalViewMatrix,
|
|
305
|
+
globalViewMatrix,
|
|
306
|
+
this.camera.transform.rotation,
|
|
307
|
+
[0, 0, 1]
|
|
308
|
+
); // Z-axis rotation
|
|
309
|
+
mat4.scale(globalViewMatrix, globalViewMatrix, [
|
|
310
|
+
this.camera.transform.scale.x,
|
|
311
|
+
this.camera.transform.scale.y,
|
|
312
|
+
1,
|
|
313
|
+
]); // Scale
|
|
314
|
+
|
|
315
|
+
this.gl.useProgram(this.programInfo.program);
|
|
316
|
+
|
|
317
|
+
this.gl.uniform3fv(
|
|
318
|
+
this.programInfo.uniformLocations.uAmbientLightValues,
|
|
319
|
+
new Float32Array([
|
|
320
|
+
this.ambientLight.x,
|
|
321
|
+
this.ambientLight.y,
|
|
322
|
+
this.ambientLight.z,
|
|
323
|
+
])
|
|
324
|
+
);
|
|
325
|
+
this.gl.uniformMatrix4fv(
|
|
326
|
+
this.programInfo.uniformLocations.projectionMatrix,
|
|
327
|
+
false,
|
|
328
|
+
projectionMatrix
|
|
329
|
+
);
|
|
330
|
+
this.gl.uniformMatrix4fv(
|
|
331
|
+
this.programInfo.uniformLocations.globalViewMatrix,
|
|
332
|
+
false,
|
|
333
|
+
globalViewMatrix
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
this.gl.uniformMatrix4fv(
|
|
337
|
+
this.programInfo.uniformLocations.instancedModelViewMatrix,
|
|
338
|
+
false,
|
|
339
|
+
globalViewMatrix
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
const lightPositions = [];
|
|
343
|
+
const lightColors = [];
|
|
344
|
+
const lightIntensities = [];
|
|
345
|
+
const lightRadii = [];
|
|
346
|
+
|
|
347
|
+
this.pointLights.forEach((light) => {
|
|
348
|
+
// Get original world position
|
|
349
|
+
const worldPos = new Vector2(light.position.x, light.position.y);
|
|
350
|
+
|
|
351
|
+
// This transforms the world position to account for camera movement
|
|
352
|
+
const adjustedPos = new Vector2(
|
|
353
|
+
worldPos.x + this.camera.transform.position.x,
|
|
354
|
+
worldPos.y + this.camera.transform.position.y
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
// Push the adjusted position to the array
|
|
358
|
+
lightPositions.push(adjustedPos.x, adjustedPos.y);
|
|
359
|
+
lightColors.push(
|
|
360
|
+
light.color.r / 255,
|
|
361
|
+
light.color.g / 255,
|
|
362
|
+
light.color.b / 255
|
|
363
|
+
);
|
|
364
|
+
lightIntensities.push(light.intensity);
|
|
365
|
+
lightRadii.push(light.radius);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Get uniform locations
|
|
369
|
+
const uLightPositionLoc = this.programInfo.uniformLocations.uLightPosition;
|
|
370
|
+
const uLightColorLoc = this.programInfo.uniformLocations.uLightColor;
|
|
371
|
+
const uLightIntensityLoc =
|
|
372
|
+
this.programInfo.uniformLocations.uLightIntensity;
|
|
373
|
+
const uLightRadiusLoc = this.programInfo.uniformLocations.uLightRadius;
|
|
374
|
+
const uActiveLightsLoc = this.programInfo.uniformLocations.uActiveLights;
|
|
375
|
+
|
|
376
|
+
// Set uniform values
|
|
377
|
+
if (lightPositions.length > 0) {
|
|
378
|
+
this.gl.uniform2fv(uLightPositionLoc, new Float32Array(lightPositions));
|
|
379
|
+
this.gl.uniform3fv(uLightColorLoc, new Float32Array(lightColors));
|
|
380
|
+
this.gl.uniform1fv(
|
|
381
|
+
uLightIntensityLoc,
|
|
382
|
+
new Float32Array(lightIntensities)
|
|
383
|
+
);
|
|
384
|
+
this.gl.uniform1fv(uLightRadiusLoc, new Float32Array(lightRadii));
|
|
385
|
+
}
|
|
386
|
+
this.gl.uniform1i(uActiveLightsLoc, this.pointLights.length);
|
|
387
|
+
|
|
388
|
+
// Prepare directional light data
|
|
389
|
+
const dirLightPositions = [];
|
|
390
|
+
const dirLightDirections = [];
|
|
391
|
+
const dirLightColors = [];
|
|
392
|
+
const dirLightIntensities = [];
|
|
393
|
+
const dirLightWidths = [];
|
|
394
|
+
|
|
395
|
+
this.directionalLights.forEach((light) => {
|
|
396
|
+
// Apply camera transformation to directional light position
|
|
397
|
+
const worldPos = new Vector2(light.position.x, light.position.y);
|
|
398
|
+
const adjustedPos = new Vector2(
|
|
399
|
+
worldPos.x + this.camera.transform.position.x,
|
|
400
|
+
worldPos.y + this.camera.transform.position.y
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
dirLightPositions.push(adjustedPos.x, adjustedPos.y);
|
|
404
|
+
dirLightDirections.push(light.direction.x, light.direction.y);
|
|
405
|
+
dirLightColors.push(
|
|
406
|
+
light.color.r / 255,
|
|
407
|
+
light.color.g / 255,
|
|
408
|
+
light.color.b / 255
|
|
409
|
+
);
|
|
410
|
+
dirLightIntensities.push(light.intensity);
|
|
411
|
+
dirLightWidths.push(light.width);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Get directional light uniform locations
|
|
415
|
+
const uDirLightPositionLoc =
|
|
416
|
+
this.programInfo.uniformLocations.uDirLightPosition;
|
|
417
|
+
const uDirLightDirectionLoc =
|
|
418
|
+
this.programInfo.uniformLocations.uDirLightDirection;
|
|
419
|
+
const uDirLightColorLoc = this.programInfo.uniformLocations.uDirLightColor;
|
|
420
|
+
const uDirLightIntensityLoc =
|
|
421
|
+
this.programInfo.uniformLocations.uDirLightIntensity;
|
|
422
|
+
const uDirLightWidthLoc = this.programInfo.uniformLocations.uDirLightWidth;
|
|
423
|
+
const uActiveDirLightsLoc =
|
|
424
|
+
this.programInfo.uniformLocations.uActiveDirLights;
|
|
425
|
+
|
|
426
|
+
// Set directional light uniform values
|
|
427
|
+
if (dirLightPositions.length > 0) {
|
|
428
|
+
this.gl.uniform2fv(
|
|
429
|
+
uDirLightPositionLoc,
|
|
430
|
+
new Float32Array(dirLightPositions)
|
|
431
|
+
);
|
|
432
|
+
this.gl.uniform2fv(
|
|
433
|
+
uDirLightDirectionLoc,
|
|
434
|
+
new Float32Array(dirLightDirections)
|
|
435
|
+
);
|
|
436
|
+
this.gl.uniform3fv(uDirLightColorLoc, new Float32Array(dirLightColors));
|
|
437
|
+
this.gl.uniform1fv(
|
|
438
|
+
uDirLightIntensityLoc,
|
|
439
|
+
new Float32Array(dirLightIntensities)
|
|
440
|
+
);
|
|
441
|
+
this.gl.uniform1fv(uDirLightWidthLoc, new Float32Array(dirLightWidths));
|
|
442
|
+
}
|
|
443
|
+
this.gl.uniform1i(uActiveDirLightsLoc, this.directionalLights.length);
|
|
444
|
+
|
|
445
|
+
// Draw objects back-to-front based on their z to ensure correct transparency blending
|
|
446
|
+
const sortedObjects = [...scene.objects].sort(
|
|
447
|
+
(a, b) => a.transform.position.z - b.transform.position.z
|
|
448
|
+
);
|
|
449
|
+
for (const object of sortedObjects) {
|
|
450
|
+
object.draw(
|
|
451
|
+
globalViewMatrix,
|
|
452
|
+
this.programInfo.uniformLocations.globalViewMatrix,
|
|
453
|
+
performance.now()
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export default Emerald;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @class FPSCounter
|
|
3
|
+
* @description Represents a FPS counter
|
|
4
|
+
*/
|
|
5
|
+
class FPSCounter {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.lastFrameTime = performance.now();
|
|
8
|
+
this.frameCount = 0;
|
|
9
|
+
this.fps = 0;
|
|
10
|
+
this.fpsElement = document.createElement("div");
|
|
11
|
+
this.fpsElement.style.position = "absolute";
|
|
12
|
+
this.fpsElement.style.top = "10px";
|
|
13
|
+
this.fpsElement.style.left = "10px";
|
|
14
|
+
this.fpsElement.style.color = "white";
|
|
15
|
+
this.fpsElement.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
|
|
16
|
+
this.fpsElement.style.padding = "5px";
|
|
17
|
+
this.fpsElement.style.fontFamily = "Arial";
|
|
18
|
+
this.fpsElement.style.fontSize = "14px";
|
|
19
|
+
document.body.appendChild(this.fpsElement);
|
|
20
|
+
this.accumulatedTime = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @method update
|
|
25
|
+
* @description Updates the FPS counter
|
|
26
|
+
*/
|
|
27
|
+
update() {
|
|
28
|
+
const now = performance.now();
|
|
29
|
+
const delta = now - this.lastFrameTime;
|
|
30
|
+
this.lastFrameTime = now;
|
|
31
|
+
this.accumulatedTime += delta;
|
|
32
|
+
this.frameCount++;
|
|
33
|
+
|
|
34
|
+
if (this.accumulatedTime >= 1000) {
|
|
35
|
+
this.fps = this.frameCount;
|
|
36
|
+
this.frameCount = 0;
|
|
37
|
+
this.accumulatedTime = 0;
|
|
38
|
+
this.fpsElement.textContent = `FPS: ${this.fps}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default FPSCounter;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Initializing a shader program
|
|
2
|
+
|
|
3
|
+
function initShaderProgram(gl, vShader, fShader) {
|
|
4
|
+
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vShader);
|
|
5
|
+
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fShader);
|
|
6
|
+
|
|
7
|
+
// Create the shader program
|
|
8
|
+
const shaderProgram = gl.createProgram();
|
|
9
|
+
gl.attachShader(shaderProgram, vertexShader);
|
|
10
|
+
gl.attachShader(shaderProgram, fragmentShader);
|
|
11
|
+
gl.linkProgram(shaderProgram);
|
|
12
|
+
|
|
13
|
+
// Error handling
|
|
14
|
+
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
|
|
15
|
+
alert(
|
|
16
|
+
`[GLUtils.js] > Unable to initialize the shader program: ${gl.getProgramInfoLog(
|
|
17
|
+
shaderProgram
|
|
18
|
+
)}`
|
|
19
|
+
);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return shaderProgram;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Loading a shader
|
|
26
|
+
|
|
27
|
+
function loadShader(gl, type, source) {
|
|
28
|
+
const shader = gl.createShader(type);
|
|
29
|
+
|
|
30
|
+
// Send the source to the shader object
|
|
31
|
+
gl.shaderSource(shader, source);
|
|
32
|
+
|
|
33
|
+
// Compile the shader program
|
|
34
|
+
gl.compileShader(shader);
|
|
35
|
+
|
|
36
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
37
|
+
alert(
|
|
38
|
+
`[GLUtils.js] An error occurred compiling the shaders: ${gl.getShaderInfoLog(
|
|
39
|
+
shader
|
|
40
|
+
)}`
|
|
41
|
+
);
|
|
42
|
+
gl.deleteShader(shader);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return shader;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const initVertexBuffer = (gl, vertices) => {
|
|
50
|
+
const floatArray = new Float32Array(vertices);
|
|
51
|
+
const buffer = gl.createBuffer();
|
|
52
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
53
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
|
|
54
|
+
buffer._bufferSize = floatArray.byteLength;
|
|
55
|
+
return buffer;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function initInstancedBuffer(gl, data, itemSize) {
|
|
59
|
+
const buffer = gl.createBuffer();
|
|
60
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
61
|
+
gl.bufferData(gl.ARRAY_BUFFER, data, gl.DYNAMIC_DRAW);
|
|
62
|
+
buffer.itemSize = itemSize;
|
|
63
|
+
buffer.numItems = data.length / itemSize;
|
|
64
|
+
return buffer;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { initShaderProgram, loadShader, initVertexBuffer, initInstancedBuffer };
|