emeraldengine 2.2.1 → 3.1.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 +2198 -968
- package/dist/types/index.d.ts +75 -32
- package/dist/types/src/Animator.d.ts +50 -0
- package/dist/types/{BitmapText.d.ts → src/BitmapText.d.ts} +19 -21
- package/dist/types/src/Camera.d.ts +122 -0
- package/dist/types/src/CameraController.d.ts +107 -0
- package/dist/types/src/CanvasText.d.ts +91 -0
- package/dist/types/src/CollisionLayers.d.ts +58 -0
- package/dist/types/{Color.d.ts → src/Color.d.ts} +6 -6
- package/dist/types/src/Coroutine.d.ts +111 -0
- package/dist/types/src/DebugOverlay.d.ts +86 -0
- package/dist/types/src/Drawable.d.ts +271 -0
- package/dist/types/src/Easing.d.ts +22 -0
- package/dist/types/src/Emerald.d.ts +420 -0
- package/dist/types/src/EmeraldDB.d.ts +159 -0
- package/dist/types/{FPSCounter.d.ts → src/FPSCounter.d.ts} +1 -3
- package/dist/types/src/GLUtils.d.ts +4 -0
- package/dist/types/{Instance.d.ts → src/Instance.d.ts} +37 -15
- package/dist/types/{InstancedTexture.d.ts → src/InstancedTexture.d.ts} +75 -23
- package/dist/types/src/Interpolator.d.ts +66 -0
- package/dist/types/src/Material.d.ts +87 -0
- package/dist/types/src/MathUtils.d.ts +81 -0
- package/dist/types/src/ParticleEmitter.d.ts +131 -0
- package/dist/types/src/Physics.d.ts +337 -0
- package/dist/types/src/Pool.d.ts +53 -0
- package/dist/types/src/PostEffects.d.ts +68 -0
- package/dist/types/src/PostProcessor.d.ts +124 -0
- package/dist/types/src/RenderTarget.d.ts +56 -0
- package/dist/types/src/Scene.d.ts +62 -0
- package/dist/types/src/ScreenEffects.d.ts +111 -0
- package/dist/types/src/Serializer.d.ts +86 -0
- package/dist/types/src/Shaders.d.ts +2 -0
- package/dist/types/{Shapes.d.ts → src/Shapes.d.ts} +4 -6
- package/dist/types/src/SpatialGrid.d.ts +50 -0
- package/dist/types/src/SpriteBatch.d.ts +89 -0
- package/dist/types/src/StateMachine.d.ts +59 -0
- package/dist/types/src/Storage.d.ts +89 -0
- package/dist/types/{Texture.d.ts → src/Texture.d.ts} +3 -4
- package/dist/types/src/TextureAtlas.d.ts +55 -0
- package/dist/types/src/Tilemap.d.ts +91 -0
- package/dist/types/src/Time.d.ts +53 -0
- package/dist/types/src/Timer.d.ts +54 -0
- package/dist/types/src/Transform.d.ts +94 -0
- package/dist/types/src/Tween.d.ts +81 -0
- package/dist/types/src/UI.d.ts +121 -0
- package/dist/types/src/components/Behaviour.d.ts +71 -0
- package/dist/types/{components → src/components}/BoxCollider.d.ts +14 -14
- package/dist/types/src/components/BoxColliderDebug.d.ts +15 -0
- package/dist/types/{components → src/components}/CircleCollider.d.ts +14 -12
- package/dist/types/src/components/CircleColliderDebug.d.ts +15 -0
- package/dist/types/src/components/Collider.d.ts +102 -0
- package/dist/types/src/components/GameObject.d.ts +135 -0
- package/dist/types/src/components/PolygonCollider.d.ts +33 -0
- package/dist/types/src/components/RigidBody.d.ts +456 -0
- package/dist/types/src/importers/Aseprite.d.ts +79 -0
- package/dist/types/src/importers/ForgeLevel.d.ts +97 -0
- package/dist/types/src/importers/TiledMap.d.ts +62 -0
- package/dist/types/{lights → src/lights}/DirectionalLight.d.ts +7 -9
- package/dist/types/{lights → src/lights}/PointLight.d.ts +6 -9
- package/dist/types/src/managers/AssetManager.d.ts +116 -0
- package/dist/types/src/managers/AudioManager.d.ts +259 -0
- package/dist/types/{managers → src/managers}/CameraManager.d.ts +8 -8
- package/dist/types/{managers → src/managers}/EventManager.d.ts +46 -32
- package/dist/types/src/managers/GLManager.d.ts +84 -0
- package/dist/types/src/managers/GLState.d.ts +37 -0
- package/dist/types/src/managers/Gamepad.d.ts +102 -0
- package/dist/types/src/managers/IDManager.d.ts +31 -0
- package/dist/types/src/managers/InputManager.d.ts +381 -0
- package/dist/types/src/managers/NetworkManager.d.ts +93 -0
- package/dist/types/src/managers/RenderStats.d.ts +34 -0
- package/dist/types/src/managers/SceneManager.d.ts +44 -0
- package/dist/types/src/managers/ShaderManager.d.ts +55 -0
- package/dist/types/src/managers/TextureManager.d.ts +102 -0
- package/dist/types/src/particlesystem/Particle.d.ts +64 -0
- package/dist/types/{particlesystem → src/particlesystem}/ParticleSettings.d.ts +32 -24
- package/dist/types/{particlesystem → src/particlesystem}/Particles.d.ts +20 -12
- package/dist/types/src/physics/AABB.d.ts +92 -0
- package/dist/types/src/physics/Body.d.ts +435 -0
- package/dist/types/src/physics/BodyType.d.ts +6 -0
- package/dist/types/src/physics/BroadPhase.d.ts +210 -0
- package/dist/types/src/physics/Collision.d.ts +102 -0
- package/dist/types/src/physics/Contact.d.ts +206 -0
- package/dist/types/src/physics/ContactSolver.d.ts +108 -0
- package/dist/types/src/physics/Distance.d.ts +54 -0
- package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
- package/dist/types/src/physics/Fixture.d.ts +221 -0
- package/dist/types/src/physics/Island.d.ts +52 -0
- package/dist/types/src/physics/Joint.d.ts +59 -0
- package/dist/types/src/physics/Math2D.d.ts +371 -0
- package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
- package/dist/types/src/physics/Settings.d.ts +22 -0
- package/dist/types/src/physics/Shapes.d.ts +207 -0
- package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
- package/dist/types/src/physics/World.d.ts +274 -0
- package/dist/types/src/physics/index.d.ts +34 -0
- package/index.js +78 -0
- package/package.json +73 -60
- package/src/Animator.js +95 -0
- package/src/BitmapText.js +6 -5
- package/src/Camera.js +183 -0
- package/src/CameraController.js +192 -0
- package/src/CanvasText.js +281 -0
- package/src/CollisionLayers.js +86 -0
- package/src/Color.js +26 -18
- package/src/Coroutine.js +259 -0
- package/src/DebugOverlay.js +246 -0
- package/src/Drawable.js +842 -582
- package/src/Easing.js +57 -0
- package/src/Emerald.js +1150 -459
- package/src/EmeraldDB.js +328 -0
- package/src/FPSCounter.js +43 -43
- package/src/GLUtils.js +60 -67
- package/src/Instance.js +41 -5
- package/src/InstancedTexture.js +308 -127
- package/src/Interpolator.js +124 -0
- package/src/Material.js +202 -0
- package/src/MathUtils.js +134 -0
- package/src/ParticleEmitter.js +284 -0
- package/src/Physics.js +431 -40
- package/src/Pool.js +85 -0
- package/src/PostEffects.js +296 -0
- package/src/PostProcessor.js +304 -0
- package/src/RenderTarget.js +134 -0
- package/src/Scene.js +115 -83
- package/src/ScreenEffects.js +266 -0
- package/src/Serializer.js +131 -0
- package/src/Shaders.js +150 -165
- package/src/Shapes.js +118 -129
- package/src/SpatialGrid.js +111 -0
- package/src/SpriteBatch.js +299 -0
- package/src/StateMachine.js +82 -0
- package/src/Storage.js +175 -47
- package/src/Texture.js +58 -67
- package/src/TextureAtlas.js +96 -0
- package/src/Tilemap.js +274 -0
- package/src/Time.js +51 -6
- package/src/Timer.js +99 -0
- package/src/Transform.js +100 -7
- package/src/Tween.js +160 -0
- package/src/UI.js +394 -0
- package/src/components/Behaviour.js +90 -0
- package/src/components/BoxCollider.js +29 -12
- package/src/components/BoxColliderDebug.js +3 -4
- package/src/components/CircleCollider.js +26 -12
- package/src/components/CircleColliderDebug.js +25 -24
- package/src/components/Collider.js +150 -34
- package/src/components/GameObject.js +130 -21
- package/src/components/PolygonCollider.js +55 -0
- package/src/components/RigidBody.js +556 -8
- package/src/importers/Aseprite.js +142 -0
- package/src/importers/ForgeLevel.js +581 -0
- package/src/importers/TiledMap.js +158 -0
- package/src/lights/DirectionalLight.js +6 -15
- package/src/lights/PointLight.js +4 -4
- package/src/managers/AssetManager.js +239 -0
- package/src/managers/AudioManager.js +565 -146
- package/src/managers/EventManager.js +488 -477
- package/src/managers/GLManager.js +57 -0
- package/src/managers/GLState.js +70 -0
- package/src/managers/Gamepad.js +126 -0
- package/src/managers/IDManager.js +24 -2
- package/src/managers/InputManager.js +905 -0
- package/src/managers/NetworkManager.js +178 -0
- package/src/managers/RenderStats.js +34 -0
- package/src/managers/SceneManager.js +30 -0
- package/src/managers/ShaderManager.js +0 -2
- package/src/managers/TextureManager.js +218 -0
- package/src/particlesystem/Particle.js +82 -7
- package/src/particlesystem/ParticleSettings.js +21 -3
- package/src/particlesystem/Particles.js +80 -31
- package/src/physics/AABB.js +207 -0
- package/src/physics/Body.js +862 -0
- package/src/physics/BodyType.js +16 -0
- package/src/physics/BroadPhase.js +641 -0
- package/src/physics/Collision.js +534 -0
- package/src/physics/Contact.js +500 -0
- package/src/physics/ContactSolver.js +526 -0
- package/src/physics/Distance.js +403 -0
- package/src/physics/DistanceJoint.js +227 -0
- package/src/physics/Fixture.js +346 -0
- package/src/physics/Island.js +203 -0
- package/src/physics/Joint.js +78 -0
- package/src/physics/Math2D.js +573 -0
- package/src/physics/RevoluteJoint.js +278 -0
- package/src/physics/Settings.js +78 -0
- package/src/physics/Shapes.js +549 -0
- package/src/physics/TimeOfImpact.js +87 -0
- package/src/physics/World.js +731 -0
- package/src/physics/index.js +79 -0
- package/dist/types/Drawable.d.ts +0 -157
- package/dist/types/Emerald.d.ts +0 -73
- package/dist/types/GLUtils.d.ts +0 -6
- package/dist/types/Physics.d.ts +0 -139
- package/dist/types/Scene.d.ts +0 -39
- package/dist/types/Shaders.d.ts +0 -4
- package/dist/types/Storage.d.ts +0 -46
- package/dist/types/Time.d.ts +0 -22
- package/dist/types/Transform.d.ts +0 -41
- package/dist/types/components/BoxColliderDebug.d.ts +0 -19
- package/dist/types/components/CircleColliderDebug.d.ts +0 -19
- package/dist/types/components/Collider.d.ts +0 -53
- package/dist/types/components/GameObject.d.ts +0 -72
- package/dist/types/components/RigidBody.d.ts +0 -104
- package/dist/types/managers/AudioManager.d.ts +0 -60
- package/dist/types/managers/GLManager.d.ts +0 -47
- package/dist/types/managers/IDManager.d.ts +0 -21
- package/dist/types/managers/SceneManager.d.ts +0 -22
- package/dist/types/particlesystem/Particle.d.ts +0 -42
package/src/Emerald.js
CHANGED
|
@@ -1,459 +1,1150 @@
|
|
|
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
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
this.gl
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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 Camera from "./Camera.js";
|
|
7
|
+
import { Vector2, Vector3 } from "./Physics.js";
|
|
8
|
+
import Time from "./Time.js";
|
|
9
|
+
import GLManager from "./managers/GLManager.js";
|
|
10
|
+
import GLState from "./managers/GLState.js";
|
|
11
|
+
import TextureManager from "./managers/TextureManager.js";
|
|
12
|
+
import RenderStats from "./managers/RenderStats.js";
|
|
13
|
+
import CameraManager from "./managers/CameraManager.js";
|
|
14
|
+
import PointLight from "./lights/PointLight.js";
|
|
15
|
+
import DirectionalLight from "./lights/DirectionalLight.js";
|
|
16
|
+
import InstancedTexture from "./InstancedTexture.js";
|
|
17
|
+
import Tween from "./Tween.js";
|
|
18
|
+
import Timer from "./Timer.js";
|
|
19
|
+
import Coroutine from "./Coroutine.js";
|
|
20
|
+
import PostProcessor from "./PostProcessor.js";
|
|
21
|
+
import RenderTarget from "./RenderTarget.js";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @class Emerald
|
|
25
|
+
* @description A class that represents the Emerald engine
|
|
26
|
+
* @param {HTMLCanvasElement} canvas - The canvas element
|
|
27
|
+
*/
|
|
28
|
+
class Emerald {
|
|
29
|
+
constructor(canvas) {
|
|
30
|
+
const gl = canvas.getContext("webgl2", {
|
|
31
|
+
antialias: true,
|
|
32
|
+
powerPreference: "high-performance",
|
|
33
|
+
desynchronized: false,
|
|
34
|
+
});
|
|
35
|
+
if (!gl) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
"[Emerald.js] > Could not acquire a WebGL2 context. Ensure the canvas is valid and WebGL2 is supported."
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.gl = gl;
|
|
42
|
+
this._initProgram(gl);
|
|
43
|
+
GLManager.setGL(gl);
|
|
44
|
+
GLManager.setProgramInfo(this.programInfo);
|
|
45
|
+
this._bindContextGuards(canvas);
|
|
46
|
+
this.camera = new Camera();
|
|
47
|
+
this.cameras = [this.camera];
|
|
48
|
+
|
|
49
|
+
CameraManager.setCamera(this.camera);
|
|
50
|
+
|
|
51
|
+
this.ambientLight = new Vector3(1.0, 1.0, 1.0);
|
|
52
|
+
this.pointLights = [];
|
|
53
|
+
this.directionalLights = [];
|
|
54
|
+
this.debugLogged = false;
|
|
55
|
+
|
|
56
|
+
this.cullingEnabled = true;
|
|
57
|
+
|
|
58
|
+
/** @private */
|
|
59
|
+
this._drawOrder = [];
|
|
60
|
+
/** @private */
|
|
61
|
+
this._projection = mat4.create();
|
|
62
|
+
/** @private */
|
|
63
|
+
this._view = mat4.create();
|
|
64
|
+
/** @private */
|
|
65
|
+
this._identityView = mat4.create();
|
|
66
|
+
/** @private */
|
|
67
|
+
this._ambient = new Float32Array(3);
|
|
68
|
+
/** @private */
|
|
69
|
+
this._lightPositions = new Float32Array(4 * 2);
|
|
70
|
+
/** @private */
|
|
71
|
+
this._lightColors = new Float32Array(4 * 3);
|
|
72
|
+
/** @private */
|
|
73
|
+
this._lightIntensities = new Float32Array(4);
|
|
74
|
+
/** @private */
|
|
75
|
+
this._lightRadii = new Float32Array(4);
|
|
76
|
+
/** @private */
|
|
77
|
+
this._dirLightPositions = new Float32Array(4 * 2);
|
|
78
|
+
/** @private */
|
|
79
|
+
this._dirLightDirections = new Float32Array(4 * 2);
|
|
80
|
+
/** @private */
|
|
81
|
+
this._dirLightColors = new Float32Array(4 * 3);
|
|
82
|
+
/** @private */
|
|
83
|
+
this._dirLightIntensities = new Float32Array(4);
|
|
84
|
+
/** @private */
|
|
85
|
+
this._dirLightWidths = new Float32Array(4);
|
|
86
|
+
this.backgroundColor = {
|
|
87
|
+
r: 0.0,
|
|
88
|
+
g: 0.0,
|
|
89
|
+
b: 0.0,
|
|
90
|
+
a: 1.0,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
this.postProcessor = null;
|
|
94
|
+
/** @private */
|
|
95
|
+
this._sceneRT = null;
|
|
96
|
+
|
|
97
|
+
this.designResolution = null;
|
|
98
|
+
/** @private */
|
|
99
|
+
this._paused = false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @method _initProgram
|
|
104
|
+
* @description Compiles the standard shader program and caches every attrib
|
|
105
|
+
* and uniform location. Called from the constructor and again after a WebGL
|
|
106
|
+
* context loss is restored (all programs die with the context).
|
|
107
|
+
* @private
|
|
108
|
+
*/
|
|
109
|
+
_initProgram(gl) {
|
|
110
|
+
const shaderProgram = initShaderProgram(
|
|
111
|
+
gl,
|
|
112
|
+
STANDARD_VERTEX_SHADER,
|
|
113
|
+
STANDARD_FRAGMENT_SHADER
|
|
114
|
+
);
|
|
115
|
+
this.shaderProgram = shaderProgram;
|
|
116
|
+
this.programInfo = {
|
|
117
|
+
program: shaderProgram,
|
|
118
|
+
attribLocations: {
|
|
119
|
+
vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
|
|
120
|
+
aTexCoord: gl.getAttribLocation(shaderProgram, "aTextureCoord"),
|
|
121
|
+
instanceMatrix: gl.getAttribLocation(shaderProgram, "aInstanceMatrix0"),
|
|
122
|
+
instanceTexCoord0: gl.getAttribLocation(
|
|
123
|
+
shaderProgram,
|
|
124
|
+
"aInstanceTexCoord0"
|
|
125
|
+
),
|
|
126
|
+
instanceTexCoord1: gl.getAttribLocation(
|
|
127
|
+
shaderProgram,
|
|
128
|
+
"aInstanceTexCoord1"
|
|
129
|
+
),
|
|
130
|
+
instanceTexCoord2: gl.getAttribLocation(
|
|
131
|
+
shaderProgram,
|
|
132
|
+
"aInstanceTexCoord2"
|
|
133
|
+
),
|
|
134
|
+
instanceTexCoord3: gl.getAttribLocation(
|
|
135
|
+
shaderProgram,
|
|
136
|
+
"aInstanceTexCoord3"
|
|
137
|
+
),
|
|
138
|
+
instanceColor: gl.getAttribLocation(shaderProgram, "aInstanceColor"),
|
|
139
|
+
},
|
|
140
|
+
uniformLocations: {
|
|
141
|
+
projectionMatrix: gl.getUniformLocation(
|
|
142
|
+
shaderProgram,
|
|
143
|
+
"uProjectionMatrix"
|
|
144
|
+
),
|
|
145
|
+
globalViewMatrix: gl.getUniformLocation(
|
|
146
|
+
shaderProgram,
|
|
147
|
+
"uModelViewMatrix"
|
|
148
|
+
),
|
|
149
|
+
instancedModelViewMatrix: gl.getUniformLocation(
|
|
150
|
+
shaderProgram,
|
|
151
|
+
"uInstancedModelViewMatrix"
|
|
152
|
+
),
|
|
153
|
+
uModelMatrix: gl.getUniformLocation(shaderProgram, "uModelMatrix"),
|
|
154
|
+
uInstancedModelMatrix: gl.getUniformLocation(
|
|
155
|
+
shaderProgram,
|
|
156
|
+
"uInstancedModelMatrix"
|
|
157
|
+
),
|
|
158
|
+
uAmbientLightValues: gl.getUniformLocation(
|
|
159
|
+
shaderProgram,
|
|
160
|
+
"uAmbientLightValues"
|
|
161
|
+
),
|
|
162
|
+
uSampler: gl.getUniformLocation(shaderProgram, "uSampler"),
|
|
163
|
+
color: gl.getUniformLocation(shaderProgram, "uColor"),
|
|
164
|
+
useTexture: gl.getUniformLocation(shaderProgram, "useTexture"),
|
|
165
|
+
useInstances: gl.getUniformLocation(shaderProgram, "useInstances"),
|
|
166
|
+
useText: gl.getUniformLocation(shaderProgram, "useText"),
|
|
167
|
+
useLighting: gl.getUniformLocation(shaderProgram, "uUseLighting"),
|
|
168
|
+
uOpacity: gl.getUniformLocation(shaderProgram, "uOpacity"),
|
|
169
|
+
uLightPosition: gl.getUniformLocation(shaderProgram, "uLightPosition"),
|
|
170
|
+
uLightColor: gl.getUniformLocation(shaderProgram, "uLightColor"),
|
|
171
|
+
uLightIntensity: gl.getUniformLocation(
|
|
172
|
+
shaderProgram,
|
|
173
|
+
"uLightIntensity"
|
|
174
|
+
),
|
|
175
|
+
uLightRadius: gl.getUniformLocation(shaderProgram, "uLightRadius"),
|
|
176
|
+
uActiveLights: gl.getUniformLocation(shaderProgram, "uActiveLights"),
|
|
177
|
+
uDirLightPosition: gl.getUniformLocation(
|
|
178
|
+
shaderProgram,
|
|
179
|
+
"uDirLightPosition"
|
|
180
|
+
),
|
|
181
|
+
uDirLightDirection: gl.getUniformLocation(
|
|
182
|
+
shaderProgram,
|
|
183
|
+
"uDirLightDirection"
|
|
184
|
+
),
|
|
185
|
+
uDirLightColor: gl.getUniformLocation(shaderProgram, "uDirLightColor"),
|
|
186
|
+
uDirLightIntensity: gl.getUniformLocation(
|
|
187
|
+
shaderProgram,
|
|
188
|
+
"uDirLightIntensity"
|
|
189
|
+
),
|
|
190
|
+
uDirLightWidth: gl.getUniformLocation(shaderProgram, "uDirLightWidth"),
|
|
191
|
+
uActiveDirLights: gl.getUniformLocation(
|
|
192
|
+
shaderProgram,
|
|
193
|
+
"uActiveDirLights"
|
|
194
|
+
),
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @method _bindContextGuards
|
|
201
|
+
* @description Installs webglcontextlost/restored handlers: on loss the
|
|
202
|
+
* default (context is gone for good) is prevented and rendering pauses; on
|
|
203
|
+
* restore every GPU resource is rebuilt and rendering resumes.
|
|
204
|
+
* @private
|
|
205
|
+
*/
|
|
206
|
+
_bindContextGuards(canvas) {
|
|
207
|
+
/** @private */
|
|
208
|
+
this._contextLost = false;
|
|
209
|
+
/** @private */
|
|
210
|
+
this._contextLostHandlers = [];
|
|
211
|
+
/** @private */
|
|
212
|
+
this._contextRestoredHandlers = [];
|
|
213
|
+
/** @private */
|
|
214
|
+
this._onCtxLost = (e) => {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
this._contextLost = true;
|
|
217
|
+
console.warn(
|
|
218
|
+
"[Emerald] > WebGL context lost, rendering paused until restore."
|
|
219
|
+
);
|
|
220
|
+
for (const h of this._contextLostHandlers) h();
|
|
221
|
+
};
|
|
222
|
+
/** @private */
|
|
223
|
+
this._onCtxRestored = () => {
|
|
224
|
+
try {
|
|
225
|
+
this._restoreContext();
|
|
226
|
+
console.info("[Emerald] > WebGL context restored.");
|
|
227
|
+
} catch (err) {
|
|
228
|
+
console.error("[Emerald] > Context restore failed:", err);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
canvas.addEventListener("webglcontextlost", this._onCtxLost, false);
|
|
232
|
+
canvas.addEventListener("webglcontextrestored", this._onCtxRestored, false);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* @method getRenderStats
|
|
237
|
+
* @description Render counters for the most recent completed frame:
|
|
238
|
+
* { drawCalls, quads, textureBinds }. DebugOverlay shows these automatically.
|
|
239
|
+
* @returns {{drawCalls:number, quads:number, textureBinds:number}}
|
|
240
|
+
*/
|
|
241
|
+
getRenderStats() {
|
|
242
|
+
return RenderStats.frame;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @method onContextLost
|
|
247
|
+
* @description Registers a callback fired when the WebGL context is lost
|
|
248
|
+
* (e.g. to show a "please wait" overlay).
|
|
249
|
+
* @param {Function} cb
|
|
250
|
+
* @returns {Emerald} - this
|
|
251
|
+
*/
|
|
252
|
+
onContextLost(cb) {
|
|
253
|
+
if (typeof cb === "function") this._contextLostHandlers.push(cb);
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @method onContextRestored
|
|
259
|
+
* @description Registers a callback fired after the context and all GPU
|
|
260
|
+
* resources have been rebuilt.
|
|
261
|
+
* @param {Function} cb
|
|
262
|
+
* @returns {Emerald} - this
|
|
263
|
+
*/
|
|
264
|
+
onContextRestored(cb) {
|
|
265
|
+
if (typeof cb === "function") this._contextRestoredHandlers.push(cb);
|
|
266
|
+
return this;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @method _restoreContext
|
|
271
|
+
* @description Rebuilds everything the GPU forgot: the standard program,
|
|
272
|
+
* cached textures (from the surviving image cache), every registered
|
|
273
|
+
* drawable's buffers, custom Materials, post-processing programs, and the
|
|
274
|
+
* scene render target. Runs on the webglcontextrestored event.
|
|
275
|
+
* @private
|
|
276
|
+
*/
|
|
277
|
+
_restoreContext() {
|
|
278
|
+
const gl = this.gl;
|
|
279
|
+
this._initProgram(gl);
|
|
280
|
+
GLManager.setGL(gl);
|
|
281
|
+
GLManager.setProgramInfo(this.programInfo);
|
|
282
|
+
GLState.reset();
|
|
283
|
+
TextureManager.restoreAll();
|
|
284
|
+
GLManager.restoreAll();
|
|
285
|
+
if (this._sceneRT) {
|
|
286
|
+
const canvas = gl.canvas;
|
|
287
|
+
this._sceneRT = new RenderTarget(
|
|
288
|
+
Math.max(2, canvas.width),
|
|
289
|
+
Math.max(2, canvas.height)
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
this._contextLost = false;
|
|
293
|
+
for (const h of this._contextRestoredHandlers) h();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* @method setDesignResolution
|
|
298
|
+
* @description Makes the world render at a fixed design resolution that is
|
|
299
|
+
* scaled to fit any screen, so the game looks consistent across devices and
|
|
300
|
+
* aspect ratios. Modes:
|
|
301
|
+
* - "fit" letterbox: preserve aspect, whole design visible, bars on the sides
|
|
302
|
+
* - "fill" cover: preserve aspect, fill the screen, crop the overflow
|
|
303
|
+
* - "stretch" ignore aspect, stretch the design to the screen
|
|
304
|
+
* - "pixel" like "fit" but snapped to an integer scale for crisp pixel art
|
|
305
|
+
* Call this once (and again on orientation change if desired); it composes with
|
|
306
|
+
* `resize`. Use `screenToWorld` for correct picking under any mode.
|
|
307
|
+
* @param {number} width - Design width in world units
|
|
308
|
+
* @param {number} height - Design height in world units
|
|
309
|
+
* @param {string} [mode="fit"] - "fit" | "fill" | "stretch" | "pixel"
|
|
310
|
+
*/
|
|
311
|
+
setDesignResolution(width, height, mode = "fit") {
|
|
312
|
+
this.designResolution = { width, height, mode };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* @method clearDesignResolution
|
|
317
|
+
* @description Disables design-resolution scaling; the world reverts to using
|
|
318
|
+
* the canvas's CSS pixel size as world units.
|
|
319
|
+
*/
|
|
320
|
+
clearDesignResolution() {
|
|
321
|
+
this.designResolution = null;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* @method _viewportFor
|
|
326
|
+
* @description Computes the device-pixel GL viewport rect and the world-space
|
|
327
|
+
* extents (`worldW` x `worldH`) for a camera, honoring the active design
|
|
328
|
+
* resolution + fit mode. Without a design resolution it returns the camera's
|
|
329
|
+
* screen sub-rect at the canvas CSS size (legacy behavior).
|
|
330
|
+
* @private
|
|
331
|
+
*/
|
|
332
|
+
_viewportFor(camera) {
|
|
333
|
+
const canvas = this.gl.canvas;
|
|
334
|
+
const vp = camera.viewport;
|
|
335
|
+
const rx = vp.x * canvas.width;
|
|
336
|
+
const ry = vp.y * canvas.height;
|
|
337
|
+
const rw = vp.width * canvas.width;
|
|
338
|
+
const rh = vp.height * canvas.height;
|
|
339
|
+
|
|
340
|
+
const design = this.designResolution;
|
|
341
|
+
if (!design) {
|
|
342
|
+
return {
|
|
343
|
+
px: Math.floor(rx),
|
|
344
|
+
py: Math.floor(ry),
|
|
345
|
+
pw: Math.floor(rw),
|
|
346
|
+
ph: Math.floor(rh),
|
|
347
|
+
worldW: canvas.clientWidth * vp.width,
|
|
348
|
+
worldH: canvas.clientHeight * vp.height,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const dw = design.width;
|
|
353
|
+
const dh = design.height;
|
|
354
|
+
const mode = design.mode;
|
|
355
|
+
|
|
356
|
+
if (mode === "stretch") {
|
|
357
|
+
return {
|
|
358
|
+
px: Math.floor(rx),
|
|
359
|
+
py: Math.floor(ry),
|
|
360
|
+
pw: Math.floor(rw),
|
|
361
|
+
ph: Math.floor(rh),
|
|
362
|
+
worldW: dw,
|
|
363
|
+
worldH: dh,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
if (mode === "fill") {
|
|
367
|
+
const scale = Math.max(rw / dw, rh / dh);
|
|
368
|
+
return {
|
|
369
|
+
px: Math.floor(rx),
|
|
370
|
+
py: Math.floor(ry),
|
|
371
|
+
pw: Math.floor(rw),
|
|
372
|
+
ph: Math.floor(rh),
|
|
373
|
+
worldW: rw / scale,
|
|
374
|
+
worldH: rh / scale,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
let scale = Math.min(rw / dw, rh / dh);
|
|
379
|
+
if (mode === "pixel") scale = Math.max(1, Math.floor(scale));
|
|
380
|
+
const vw = dw * scale;
|
|
381
|
+
const vh = dh * scale;
|
|
382
|
+
return {
|
|
383
|
+
px: Math.floor(rx + (rw - vw) / 2),
|
|
384
|
+
py: Math.floor(ry + (rh - vh) / 2),
|
|
385
|
+
pw: Math.floor(vw),
|
|
386
|
+
ph: Math.floor(vh),
|
|
387
|
+
worldW: dw,
|
|
388
|
+
worldH: dh,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* @method _clientToView
|
|
394
|
+
* @description Maps a client/CSS pixel coordinate to pre-camera view space
|
|
395
|
+
* (origin center, +Y up), honoring DPR, design resolution and fit mode
|
|
396
|
+
* (including letterbox offset). This is also the screen-space coordinate used
|
|
397
|
+
* for `screenSpace` objects. Camera transform is applied separately by
|
|
398
|
+
* `screenToWorld`.
|
|
399
|
+
* @private
|
|
400
|
+
* @returns {{x:number, y:number}}
|
|
401
|
+
*/
|
|
402
|
+
_clientToView(clientX, clientY, camera = this.camera) {
|
|
403
|
+
const canvas = this.gl.canvas;
|
|
404
|
+
const rect =
|
|
405
|
+
typeof canvas.getBoundingClientRect === "function"
|
|
406
|
+
? canvas.getBoundingClientRect()
|
|
407
|
+
: {
|
|
408
|
+
left: 0,
|
|
409
|
+
top: 0,
|
|
410
|
+
width: canvas.clientWidth,
|
|
411
|
+
height: canvas.clientHeight,
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const sx = (clientX - rect.left) * (canvas.width / (rect.width || 1));
|
|
415
|
+
const sy = (clientY - rect.top) * (canvas.height / (rect.height || 1));
|
|
416
|
+
const gy = canvas.height - sy;
|
|
417
|
+
|
|
418
|
+
const { px, py, pw, ph, worldW, worldH } = this._viewportFor(camera);
|
|
419
|
+
const nx = (sx - px) / (pw || 1);
|
|
420
|
+
const ny = (gy - py) / (ph || 1);
|
|
421
|
+
|
|
422
|
+
return { x: (nx - 0.5) * worldW, y: (ny - 0.5) * worldH };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* @method screenToWorld
|
|
427
|
+
* @description Converts a client/CSS pixel coordinate (e.g. `event.clientX/Y`)
|
|
428
|
+
* to world space for a camera, accounting for device pixel ratio, the active
|
|
429
|
+
* design resolution + fit mode (including letterbox offset), and the camera's
|
|
430
|
+
* position / zoom / rotation. Use this for mouse picking instead of ad-hoc math.
|
|
431
|
+
* @param {number} clientX - Client X (relative to the viewport)
|
|
432
|
+
* @param {number} clientY - Client Y (relative to the viewport)
|
|
433
|
+
* @param {Camera} [camera] - The camera to unproject through (default primary)
|
|
434
|
+
* @returns {Vector2} - World-space position
|
|
435
|
+
*/
|
|
436
|
+
screenToWorld(clientX, clientY, camera = this.camera) {
|
|
437
|
+
const { x: vx, y: vy } = this._clientToView(clientX, clientY, camera);
|
|
438
|
+
|
|
439
|
+
const zoomX = camera.transform.scale.x || 1;
|
|
440
|
+
const zoomY = camera.transform.scale.y || 1;
|
|
441
|
+
const ux = vx / zoomX;
|
|
442
|
+
const uy = vy / zoomY;
|
|
443
|
+
|
|
444
|
+
const rot = -camera.transform.rotation;
|
|
445
|
+
const c = Math.cos(rot);
|
|
446
|
+
const s = Math.sin(rot);
|
|
447
|
+
const wx = ux * c - uy * s - camera.transform.position.x;
|
|
448
|
+
const wy = ux * s + uy * c - camera.transform.position.y;
|
|
449
|
+
return new Vector2(wx, wy);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* @method enablePostProcessing
|
|
454
|
+
* @description Turns on the post-processing pipeline (lazily creating the
|
|
455
|
+
* scene render target and processor). Add effects with addPostEffect.
|
|
456
|
+
* @returns {PostProcessor} - The processor (to add/remove effects)
|
|
457
|
+
*/
|
|
458
|
+
enablePostProcessing() {
|
|
459
|
+
if (!this.postProcessor) {
|
|
460
|
+
const canvas = this.gl.canvas;
|
|
461
|
+
this._sceneRT = new RenderTarget(canvas.width, canvas.height);
|
|
462
|
+
this.postProcessor = new PostProcessor();
|
|
463
|
+
}
|
|
464
|
+
this.postProcessor.enabled = true;
|
|
465
|
+
return this.postProcessor;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* @method disablePostProcessing
|
|
470
|
+
* @description Disables the pipeline (scene renders straight to the canvas).
|
|
471
|
+
*/
|
|
472
|
+
disablePostProcessing() {
|
|
473
|
+
if (this.postProcessor) this.postProcessor.enabled = false;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* @method addPostEffect
|
|
478
|
+
* @description Appends a post-processing effect (enabling the pipeline if
|
|
479
|
+
* needed). See PostEffects for the built-ins.
|
|
480
|
+
* @param {PostEffect} effect
|
|
481
|
+
* @returns {Emerald} - this
|
|
482
|
+
*/
|
|
483
|
+
addPostEffect(effect) {
|
|
484
|
+
this.enablePostProcessing();
|
|
485
|
+
this.postProcessor.addEffect(effect);
|
|
486
|
+
return this;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* @method removePostEffect
|
|
491
|
+
* @description Removes a previously added post-processing effect.
|
|
492
|
+
* @param {PostEffect} effect
|
|
493
|
+
* @returns {Emerald} - this
|
|
494
|
+
*/
|
|
495
|
+
removePostEffect(effect) {
|
|
496
|
+
if (this.postProcessor) this.postProcessor.removeEffect(effect);
|
|
497
|
+
return this;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* @method setAmbientLight
|
|
502
|
+
* @description Sets the ambient light for the scene
|
|
503
|
+
* @param {Vector3} vector3 - The ambient light color as a Vector3 instance
|
|
504
|
+
*/
|
|
505
|
+
setAmbientLight(vector3) {
|
|
506
|
+
if (
|
|
507
|
+
vector3 &&
|
|
508
|
+
typeof vector3.x === "number" &&
|
|
509
|
+
typeof vector3.y === "number" &&
|
|
510
|
+
typeof vector3.z === "number"
|
|
511
|
+
) {
|
|
512
|
+
this.ambientLight = vector3;
|
|
513
|
+
} else {
|
|
514
|
+
console.error(
|
|
515
|
+
"[Emerald.js] > vector3 is not an instance of Vector3 class."
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* @method resize
|
|
522
|
+
* @description Resizes the canvas
|
|
523
|
+
* @param {number} width - The width of the canvas
|
|
524
|
+
* @param {number} height - The height of the canvas
|
|
525
|
+
* @param {number} [dpr] - Device pixel ratio override; defaults to
|
|
526
|
+
* `window.devicePixelRatio` (or 1 outside a browser)
|
|
527
|
+
*/
|
|
528
|
+
resize(width, height, dpr) {
|
|
529
|
+
const ratio =
|
|
530
|
+
dpr || (typeof window !== "undefined" && window.devicePixelRatio) || 1;
|
|
531
|
+
const canvas = this.gl.canvas;
|
|
532
|
+
|
|
533
|
+
if (canvas.style) {
|
|
534
|
+
canvas.style.width = width + "px";
|
|
535
|
+
canvas.style.height = height + "px";
|
|
536
|
+
}
|
|
537
|
+
canvas.width = Math.round(width * ratio);
|
|
538
|
+
canvas.height = Math.round(height * ratio);
|
|
539
|
+
|
|
540
|
+
this.gl.viewport(0, 0, canvas.width, canvas.height);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* @method addPointLight
|
|
545
|
+
* @description Adds a point light to the scene
|
|
546
|
+
* @param {PointLight} pointLight - The point light to add
|
|
547
|
+
*/
|
|
548
|
+
addPointLight(pointLight) {
|
|
549
|
+
if (!(pointLight instanceof PointLight)) {
|
|
550
|
+
console.error(
|
|
551
|
+
"[Emerald.js] > Passed argument is not an instance of PointLight class."
|
|
552
|
+
);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (this.pointLights.length < 4) {
|
|
556
|
+
this.pointLights.push(pointLight);
|
|
557
|
+
} else {
|
|
558
|
+
console.warn("Maximum number of point lights reached (4)");
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* @method addDirectionalLight
|
|
564
|
+
* @description Adds a directional light to the scene
|
|
565
|
+
* @param {DirectionalLight} directionalLight - The directional light to add
|
|
566
|
+
*/
|
|
567
|
+
addDirectionalLight(directionalLight) {
|
|
568
|
+
if (!(directionalLight instanceof DirectionalLight)) {
|
|
569
|
+
console.error(
|
|
570
|
+
"[Emerald.js] > Passed argument is not an instance of DirectionalLight class."
|
|
571
|
+
);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
if (this.directionalLights.length < 4) {
|
|
575
|
+
this.directionalLights.push(directionalLight);
|
|
576
|
+
} else {
|
|
577
|
+
console.warn("Maximum number of directional lights reached (4)");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* @method removeDirectionalLight
|
|
583
|
+
* @description Removes a directional light from the scene
|
|
584
|
+
* @param {DirectionalLight} directionalLight - The directional light to remove
|
|
585
|
+
*/
|
|
586
|
+
removeDirectionalLight(directionalLight) {
|
|
587
|
+
const index = this.directionalLights.indexOf(directionalLight);
|
|
588
|
+
if (index > -1) {
|
|
589
|
+
this.directionalLights.splice(index, 1);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* @method setBackgroundColor
|
|
595
|
+
* @description Sets the background color of the scene
|
|
596
|
+
* @param {Color} color - The background color
|
|
597
|
+
*/
|
|
598
|
+
setBackgroundColor(color) {
|
|
599
|
+
if (color instanceof Color) {
|
|
600
|
+
this.backgroundColor = {
|
|
601
|
+
r: color.r / 255,
|
|
602
|
+
g: color.g / 255,
|
|
603
|
+
b: color.b / 255,
|
|
604
|
+
a: color.a / 255,
|
|
605
|
+
};
|
|
606
|
+
} else {
|
|
607
|
+
console.error("[Emerald.js] > color is not an instance of Color class.");
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* @method run
|
|
613
|
+
* @description Starts an engine-owned game loop on requestAnimationFrame. Each
|
|
614
|
+
* frame it computes a delta time (clamped so a tab-switch stall can't produce
|
|
615
|
+
* a huge jump), optionally advances a fixed-timestep simulation, and calls
|
|
616
|
+
* your `update(dt, alpha)`. `alpha` is the 0..1 interpolation factor between
|
|
617
|
+
* the last and next fixed step (1 when no fixed step is configured), so you
|
|
618
|
+
* can render smoothly between simulation ticks.
|
|
619
|
+
*
|
|
620
|
+
* @param {Function} update - `(dt, alpha) => void`, called once per frame
|
|
621
|
+
* @param {Object} [options]
|
|
622
|
+
* @param {number} [options.maxDelta=0.25] - Max dt (seconds) per frame
|
|
623
|
+
* @param {number} [options.fixedStep=0] - Fixed simulation step in seconds
|
|
624
|
+
* (e.g. 1/60). 0 disables the fixed loop.
|
|
625
|
+
* @param {Function} [options.fixedUpdate] - `(step) => void`, called 0..N
|
|
626
|
+
* times per frame to advance the fixed-timestep simulation
|
|
627
|
+
* @param {number} [options.maxSubSteps=5] - Cap on fixed steps per frame
|
|
628
|
+
* @param {boolean} [options.pauseOnBlur=true] - Auto-pause when the tab is
|
|
629
|
+
* hidden/minimized (and on window blur if `pauseOnWindowBlur` is set)
|
|
630
|
+
* @param {boolean} [options.pauseOnWindowBlur=false] - Also pause on window blur
|
|
631
|
+
* @param {Function} [options.onPause] - Called when the loop pauses
|
|
632
|
+
* @param {Function} [options.onResume] - Called when the loop resumes
|
|
633
|
+
* @returns {Function} - A stop function (same as calling `stop()`)
|
|
634
|
+
*/
|
|
635
|
+
run(update, options = {}) {
|
|
636
|
+
const raf =
|
|
637
|
+
typeof globalThis !== "undefined" && globalThis.requestAnimationFrame
|
|
638
|
+
? globalThis.requestAnimationFrame.bind(globalThis)
|
|
639
|
+
: null;
|
|
640
|
+
if (!raf) {
|
|
641
|
+
throw new Error(
|
|
642
|
+
"[Emerald.js] > run() requires requestAnimationFrame (browser environment)."
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const maxDelta = options.maxDelta ?? 0.25;
|
|
647
|
+
const fixedStep = options.fixedStep ?? 0;
|
|
648
|
+
const fixedUpdate = options.fixedUpdate || null;
|
|
649
|
+
const maxSubSteps = options.maxSubSteps ?? 5;
|
|
650
|
+
|
|
651
|
+
/** @private */
|
|
652
|
+
this._running = true;
|
|
653
|
+
this._paused = false;
|
|
654
|
+
/** @private */
|
|
655
|
+
this._loopLastTime = 0;
|
|
656
|
+
/** @private */
|
|
657
|
+
this._fixedAccumulator = 0;
|
|
658
|
+
/** @private */
|
|
659
|
+
this._onPause = options.onPause || null;
|
|
660
|
+
/** @private */
|
|
661
|
+
this._onResume = options.onResume || null;
|
|
662
|
+
this._bindLifecycle(options);
|
|
663
|
+
|
|
664
|
+
const frame = (now) => {
|
|
665
|
+
if (!this._running) return;
|
|
666
|
+
if (this._paused || this._contextLost) {
|
|
667
|
+
this._loopLastTime = now;
|
|
668
|
+
/** @private */
|
|
669
|
+
this._rafId = raf(frame);
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
if (this._loopLastTime === 0) this._loopLastTime = now;
|
|
673
|
+
let dt = (now - this._loopLastTime) / 1000;
|
|
674
|
+
this._loopLastTime = now;
|
|
675
|
+
if (!isFinite(dt) || dt < 0) dt = 0;
|
|
676
|
+
if (dt > maxDelta) dt = maxDelta;
|
|
677
|
+
|
|
678
|
+
let alpha = 1;
|
|
679
|
+
if (fixedStep > 0 && fixedUpdate) {
|
|
680
|
+
this._fixedAccumulator += dt;
|
|
681
|
+
let steps = 0;
|
|
682
|
+
while (this._fixedAccumulator >= fixedStep && steps < maxSubSteps) {
|
|
683
|
+
fixedUpdate(fixedStep);
|
|
684
|
+
this._fixedAccumulator -= fixedStep;
|
|
685
|
+
steps++;
|
|
686
|
+
}
|
|
687
|
+
if (steps === maxSubSteps) this._fixedAccumulator = 0;
|
|
688
|
+
alpha = this._fixedAccumulator / fixedStep;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
update(dt, alpha);
|
|
692
|
+
this._rafId = raf(frame);
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
this._rafId = raf(frame);
|
|
696
|
+
return () => this.stop();
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* @method stop
|
|
701
|
+
* @description Stops a loop previously started with run().
|
|
702
|
+
*/
|
|
703
|
+
stop() {
|
|
704
|
+
this._running = false;
|
|
705
|
+
this._unbindLifecycle();
|
|
706
|
+
if (
|
|
707
|
+
this._rafId != null &&
|
|
708
|
+
typeof globalThis !== "undefined" &&
|
|
709
|
+
globalThis.cancelAnimationFrame
|
|
710
|
+
) {
|
|
711
|
+
globalThis.cancelAnimationFrame(this._rafId);
|
|
712
|
+
}
|
|
713
|
+
this._rafId = null;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* @method pause
|
|
718
|
+
* @description Pauses the loop: `update`/`fixedUpdate` stop being called while
|
|
719
|
+
* the RAF keeps spinning. Time doesn't accumulate, so resuming produces no dt
|
|
720
|
+
* spike. Fires the `onPause` callback once.
|
|
721
|
+
*/
|
|
722
|
+
pause() {
|
|
723
|
+
if (this._paused) return;
|
|
724
|
+
this._paused = true;
|
|
725
|
+
if (this._onPause) this._onPause();
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* @method resume
|
|
730
|
+
* @description Resumes a paused loop and resyncs the clock so the first frame
|
|
731
|
+
* after resuming has ~0 dt. Fires the `onResume` callback once.
|
|
732
|
+
*/
|
|
733
|
+
resume() {
|
|
734
|
+
if (!this._paused) return;
|
|
735
|
+
this._paused = false;
|
|
736
|
+
this._loopLastTime = 0;
|
|
737
|
+
if (this._onResume) this._onResume();
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* @method _bindLifecycle
|
|
742
|
+
* @description Wires visibility/blur listeners that auto-pause the loop when
|
|
743
|
+
* the page is backgrounded, per the `run` options.
|
|
744
|
+
* @private
|
|
745
|
+
*/
|
|
746
|
+
_bindLifecycle(options) {
|
|
747
|
+
this._unbindLifecycle();
|
|
748
|
+
const pauseOnBlur = options.pauseOnBlur ?? true;
|
|
749
|
+
if (!pauseOnBlur) return;
|
|
750
|
+
|
|
751
|
+
if (typeof document !== "undefined" && document.addEventListener) {
|
|
752
|
+
/** @private */
|
|
753
|
+
this._onVisibility = () => {
|
|
754
|
+
if (document.hidden) this.pause();
|
|
755
|
+
else this.resume();
|
|
756
|
+
};
|
|
757
|
+
document.addEventListener("visibilitychange", this._onVisibility);
|
|
758
|
+
}
|
|
759
|
+
if (
|
|
760
|
+
options.pauseOnWindowBlur &&
|
|
761
|
+
typeof window !== "undefined" &&
|
|
762
|
+
window.addEventListener
|
|
763
|
+
) {
|
|
764
|
+
/** @private */
|
|
765
|
+
this._onWinBlur = () => this.pause();
|
|
766
|
+
/** @private */
|
|
767
|
+
this._onWinFocus = () => this.resume();
|
|
768
|
+
window.addEventListener("blur", this._onWinBlur);
|
|
769
|
+
window.addEventListener("focus", this._onWinFocus);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* @method _unbindLifecycle
|
|
775
|
+
* @description Removes any listeners registered by `_bindLifecycle`.
|
|
776
|
+
* @private
|
|
777
|
+
*/
|
|
778
|
+
_unbindLifecycle() {
|
|
779
|
+
if (this._onVisibility && typeof document !== "undefined") {
|
|
780
|
+
document.removeEventListener("visibilitychange", this._onVisibility);
|
|
781
|
+
this._onVisibility = null;
|
|
782
|
+
}
|
|
783
|
+
if (this._onWinBlur && typeof window !== "undefined") {
|
|
784
|
+
window.removeEventListener("blur", this._onWinBlur);
|
|
785
|
+
window.removeEventListener("focus", this._onWinFocus);
|
|
786
|
+
this._onWinBlur = null;
|
|
787
|
+
this._onWinFocus = null;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* @method drawScene
|
|
793
|
+
* @description Draws the scene
|
|
794
|
+
* @param {Scene} scene - The scene to draw
|
|
795
|
+
* @param {number} deltaTime - The delta time
|
|
796
|
+
*/
|
|
797
|
+
drawScene(scene, deltaTime) {
|
|
798
|
+
if (this._contextLost) return;
|
|
799
|
+
RenderStats.beginFrame();
|
|
800
|
+
Time.setDeltaTime(deltaTime);
|
|
801
|
+
|
|
802
|
+
const scaledDelta = Time.getDeltaTime();
|
|
803
|
+
Tween.update(scaledDelta);
|
|
804
|
+
Timer.update(scaledDelta);
|
|
805
|
+
Coroutine.update(scaledDelta);
|
|
806
|
+
|
|
807
|
+
for (let i = 0; i < scene.objects.length; i++) {
|
|
808
|
+
const object = scene.objects[i];
|
|
809
|
+
if (object && typeof object.syncPhysics === "function") {
|
|
810
|
+
object.syncPhysics();
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
const gl = this.gl;
|
|
815
|
+
const canvas = gl.canvas;
|
|
816
|
+
|
|
817
|
+
const usePost = this.postProcessor && this.postProcessor.hasEffects();
|
|
818
|
+
if (usePost) {
|
|
819
|
+
this._sceneRT.resize(canvas.width, canvas.height);
|
|
820
|
+
this._sceneRT.bind();
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
gl.clearDepth(1.0);
|
|
824
|
+
gl.disable(gl.DEPTH_TEST);
|
|
825
|
+
gl.enable(gl.BLEND);
|
|
826
|
+
gl.blendFuncSeparate(
|
|
827
|
+
gl.SRC_ALPHA,
|
|
828
|
+
gl.ONE_MINUS_SRC_ALPHA,
|
|
829
|
+
gl.ONE,
|
|
830
|
+
gl.ONE_MINUS_SRC_ALPHA
|
|
831
|
+
);
|
|
832
|
+
|
|
833
|
+
const bg = this.backgroundColor || { r: 0, g: 0, b: 0, a: 1 };
|
|
834
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
835
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
836
|
+
gl.clearColor(bg.r, bg.g, bg.b, bg.a);
|
|
837
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
838
|
+
|
|
839
|
+
gl.useProgram(this.programInfo.program);
|
|
840
|
+
GLState.reset();
|
|
841
|
+
|
|
842
|
+
const order = this._drawOrder;
|
|
843
|
+
order.length = scene.objects.length;
|
|
844
|
+
for (let i = 0; i < scene.objects.length; i++) {
|
|
845
|
+
order[i] = scene.objects[i];
|
|
846
|
+
}
|
|
847
|
+
order.sort(
|
|
848
|
+
(a, b) =>
|
|
849
|
+
(a.layer || 0) - (b.layer || 0) ||
|
|
850
|
+
a.transform.position.z - b.transform.position.z
|
|
851
|
+
);
|
|
852
|
+
|
|
853
|
+
const now = performance.now();
|
|
854
|
+
|
|
855
|
+
const postExcluded = [];
|
|
856
|
+
for (const camera of this.cameras) {
|
|
857
|
+
if (!camera || !camera.active) continue;
|
|
858
|
+
if (usePost && camera.excludeFromPost) {
|
|
859
|
+
postExcluded.push(camera);
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
this._renderCamera(camera, order, now);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
866
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
867
|
+
|
|
868
|
+
if (usePost) {
|
|
869
|
+
this._sceneRT.unbind();
|
|
870
|
+
this.postProcessor.process(
|
|
871
|
+
this._sceneRT.texture,
|
|
872
|
+
canvas.width,
|
|
873
|
+
canvas.height,
|
|
874
|
+
now / 1000
|
|
875
|
+
);
|
|
876
|
+
|
|
877
|
+
if (postExcluded.length) {
|
|
878
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
879
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
880
|
+
gl.disable(gl.DEPTH_TEST);
|
|
881
|
+
gl.enable(gl.BLEND);
|
|
882
|
+
gl.blendFuncSeparate(
|
|
883
|
+
gl.SRC_ALPHA,
|
|
884
|
+
gl.ONE_MINUS_SRC_ALPHA,
|
|
885
|
+
gl.ONE,
|
|
886
|
+
gl.ONE_MINUS_SRC_ALPHA
|
|
887
|
+
);
|
|
888
|
+
gl.useProgram(this.programInfo.program);
|
|
889
|
+
GLState.reset();
|
|
890
|
+
for (const camera of postExcluded) {
|
|
891
|
+
this._renderCamera(camera, order, now);
|
|
892
|
+
}
|
|
893
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
894
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* @method _renderCamera
|
|
901
|
+
* @description Renders the sorted draw order through a single camera into its
|
|
902
|
+
* viewport rectangle.
|
|
903
|
+
* @private
|
|
904
|
+
*/
|
|
905
|
+
_renderCamera(camera, order, now) {
|
|
906
|
+
const gl = this.gl;
|
|
907
|
+
const canvas = gl.canvas;
|
|
908
|
+
|
|
909
|
+
const { px, py, pw, ph, worldW, worldH } = this._viewportFor(camera);
|
|
910
|
+
gl.viewport(px, py, pw, ph);
|
|
911
|
+
gl.enable(gl.SCISSOR_TEST);
|
|
912
|
+
gl.scissor(px, py, pw, ph);
|
|
913
|
+
|
|
914
|
+
if (camera.clearColor) {
|
|
915
|
+
const c = camera.clearColor;
|
|
916
|
+
gl.clearColor(c.r / 255, c.g / 255, c.b / 255, c.a / 255);
|
|
917
|
+
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
const cssW = worldW;
|
|
921
|
+
const cssH = worldH;
|
|
922
|
+
const projectionMatrix = this._projection;
|
|
923
|
+
mat4.ortho(
|
|
924
|
+
projectionMatrix,
|
|
925
|
+
-cssW / 2,
|
|
926
|
+
cssW / 2,
|
|
927
|
+
-cssH / 2,
|
|
928
|
+
cssH / 2,
|
|
929
|
+
-100,
|
|
930
|
+
100
|
|
931
|
+
);
|
|
932
|
+
GLManager.setProjection(projectionMatrix);
|
|
933
|
+
|
|
934
|
+
const view = this._view;
|
|
935
|
+
mat4.identity(view);
|
|
936
|
+
mat4.scale(view, view, [
|
|
937
|
+
camera.transform.scale.x,
|
|
938
|
+
camera.transform.scale.y,
|
|
939
|
+
1,
|
|
940
|
+
]);
|
|
941
|
+
mat4.rotate(view, view, camera.transform.rotation, [0, 0, 1]);
|
|
942
|
+
let camTX = camera.transform.position.x;
|
|
943
|
+
let camTY = camera.transform.position.y;
|
|
944
|
+
if (camera.pixelSnap && camera.transform.rotation === 0) {
|
|
945
|
+
const zx = camera.transform.scale.x || 1;
|
|
946
|
+
const zy = camera.transform.scale.y || 1;
|
|
947
|
+
camTX = Math.round(camTX * zx) / zx;
|
|
948
|
+
camTY = Math.round(camTY * zy) / zy;
|
|
949
|
+
}
|
|
950
|
+
mat4.translate(view, view, [camTX, camTY, camera.transform.position.z]);
|
|
951
|
+
|
|
952
|
+
this._ambient[0] = this.ambientLight.x;
|
|
953
|
+
this._ambient[1] = this.ambientLight.y;
|
|
954
|
+
this._ambient[2] = this.ambientLight.z;
|
|
955
|
+
gl.uniform3fv(
|
|
956
|
+
this.programInfo.uniformLocations.uAmbientLightValues,
|
|
957
|
+
this._ambient
|
|
958
|
+
);
|
|
959
|
+
gl.uniformMatrix4fv(
|
|
960
|
+
this.programInfo.uniformLocations.projectionMatrix,
|
|
961
|
+
false,
|
|
962
|
+
projectionMatrix
|
|
963
|
+
);
|
|
964
|
+
gl.uniformMatrix4fv(
|
|
965
|
+
this.programInfo.uniformLocations.globalViewMatrix,
|
|
966
|
+
false,
|
|
967
|
+
view
|
|
968
|
+
);
|
|
969
|
+
gl.uniformMatrix4fv(
|
|
970
|
+
this.programInfo.uniformLocations.instancedModelViewMatrix,
|
|
971
|
+
false,
|
|
972
|
+
view
|
|
973
|
+
);
|
|
974
|
+
|
|
975
|
+
const camX = camera.transform.position.x;
|
|
976
|
+
const camY = camera.transform.position.y;
|
|
977
|
+
const zoom = camera.transform.scale.x || 1;
|
|
978
|
+
|
|
979
|
+
const pointCount = Math.min(this.pointLights.length, 4);
|
|
980
|
+
for (let i = 0; i < pointCount; i++) {
|
|
981
|
+
const light = this.pointLights[i];
|
|
982
|
+
this._lightPositions[i * 2] = (light.position.x + camX) * zoom;
|
|
983
|
+
this._lightPositions[i * 2 + 1] = (light.position.y + camY) * zoom;
|
|
984
|
+
this._lightColors[i * 3] = light.color.r / 255;
|
|
985
|
+
this._lightColors[i * 3 + 1] = light.color.g / 255;
|
|
986
|
+
this._lightColors[i * 3 + 2] = light.color.b / 255;
|
|
987
|
+
this._lightIntensities[i] = light.intensity;
|
|
988
|
+
this._lightRadii[i] = light.radius * zoom;
|
|
989
|
+
}
|
|
990
|
+
if (pointCount > 0) {
|
|
991
|
+
gl.uniform2fv(
|
|
992
|
+
this.programInfo.uniformLocations.uLightPosition,
|
|
993
|
+
this._lightPositions
|
|
994
|
+
);
|
|
995
|
+
gl.uniform3fv(
|
|
996
|
+
this.programInfo.uniformLocations.uLightColor,
|
|
997
|
+
this._lightColors
|
|
998
|
+
);
|
|
999
|
+
gl.uniform1fv(
|
|
1000
|
+
this.programInfo.uniformLocations.uLightIntensity,
|
|
1001
|
+
this._lightIntensities
|
|
1002
|
+
);
|
|
1003
|
+
gl.uniform1fv(
|
|
1004
|
+
this.programInfo.uniformLocations.uLightRadius,
|
|
1005
|
+
this._lightRadii
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
gl.uniform1i(this.programInfo.uniformLocations.uActiveLights, pointCount);
|
|
1009
|
+
|
|
1010
|
+
const dirCount = Math.min(this.directionalLights.length, 4);
|
|
1011
|
+
for (let i = 0; i < dirCount; i++) {
|
|
1012
|
+
const light = this.directionalLights[i];
|
|
1013
|
+
this._dirLightPositions[i * 2] = (light.position.x + camX) * zoom;
|
|
1014
|
+
this._dirLightPositions[i * 2 + 1] = (light.position.y + camY) * zoom;
|
|
1015
|
+
this._dirLightDirections[i * 2] = light.direction.x;
|
|
1016
|
+
this._dirLightDirections[i * 2 + 1] = light.direction.y;
|
|
1017
|
+
this._dirLightColors[i * 3] = light.color.r / 255;
|
|
1018
|
+
this._dirLightColors[i * 3 + 1] = light.color.g / 255;
|
|
1019
|
+
this._dirLightColors[i * 3 + 2] = light.color.b / 255;
|
|
1020
|
+
this._dirLightIntensities[i] = light.intensity;
|
|
1021
|
+
this._dirLightWidths[i] = light.width * zoom;
|
|
1022
|
+
}
|
|
1023
|
+
if (dirCount > 0) {
|
|
1024
|
+
gl.uniform2fv(
|
|
1025
|
+
this.programInfo.uniformLocations.uDirLightPosition,
|
|
1026
|
+
this._dirLightPositions
|
|
1027
|
+
);
|
|
1028
|
+
gl.uniform2fv(
|
|
1029
|
+
this.programInfo.uniformLocations.uDirLightDirection,
|
|
1030
|
+
this._dirLightDirections
|
|
1031
|
+
);
|
|
1032
|
+
gl.uniform3fv(
|
|
1033
|
+
this.programInfo.uniformLocations.uDirLightColor,
|
|
1034
|
+
this._dirLightColors
|
|
1035
|
+
);
|
|
1036
|
+
gl.uniform1fv(
|
|
1037
|
+
this.programInfo.uniformLocations.uDirLightIntensity,
|
|
1038
|
+
this._dirLightIntensities
|
|
1039
|
+
);
|
|
1040
|
+
gl.uniform1fv(
|
|
1041
|
+
this.programInfo.uniformLocations.uDirLightWidth,
|
|
1042
|
+
this._dirLightWidths
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
gl.uniform1i(this.programInfo.uniformLocations.uActiveDirLights, dirCount);
|
|
1046
|
+
|
|
1047
|
+
const halfW = cssW / 2 / zoom;
|
|
1048
|
+
const halfH = cssH / 2 / zoom;
|
|
1049
|
+
const viewLeft = -camX - halfW;
|
|
1050
|
+
const viewRight = -camX + halfW;
|
|
1051
|
+
const viewBottom = -camY - halfH;
|
|
1052
|
+
const viewTop = -camY + halfH;
|
|
1053
|
+
|
|
1054
|
+
const ignoreLayers = camera.ignoreLayers;
|
|
1055
|
+
const onlyLayers = camera.onlyLayers;
|
|
1056
|
+
for (const object of order) {
|
|
1057
|
+
const objLayer = object.layer || 0;
|
|
1058
|
+
if (onlyLayers && !onlyLayers.has(objLayer)) {
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
if (ignoreLayers && ignoreLayers.size && ignoreLayers.has(objLayer)) {
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
if (
|
|
1065
|
+
this.cullingEnabled &&
|
|
1066
|
+
!object.screenSpace &&
|
|
1067
|
+
!this._isVisible(object, viewLeft, viewRight, viewBottom, viewTop)
|
|
1068
|
+
) {
|
|
1069
|
+
continue;
|
|
1070
|
+
}
|
|
1071
|
+
const objView = object.screenSpace ? this._identityView : view;
|
|
1072
|
+
object.draw(
|
|
1073
|
+
objView,
|
|
1074
|
+
this.programInfo.uniformLocations.globalViewMatrix,
|
|
1075
|
+
now
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* @method addCamera
|
|
1082
|
+
* @description Adds a camera to the render list (for split-screen, etc.).
|
|
1083
|
+
* @param {Camera} camera - The camera to add
|
|
1084
|
+
*/
|
|
1085
|
+
addCamera(camera) {
|
|
1086
|
+
if (this.cameras.indexOf(camera) === -1) this.cameras.push(camera);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* @method removeCamera
|
|
1091
|
+
* @description Removes a camera from the render list.
|
|
1092
|
+
* @param {Camera} camera - The camera to remove
|
|
1093
|
+
*/
|
|
1094
|
+
removeCamera(camera) {
|
|
1095
|
+
const idx = this.cameras.indexOf(camera);
|
|
1096
|
+
if (idx !== -1) this.cameras.splice(idx, 1);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* @method setCameras
|
|
1101
|
+
* @description Replaces the camera list. The first camera becomes the primary
|
|
1102
|
+
* `this.camera`.
|
|
1103
|
+
* @param {Camera[]} cameras - The cameras to render
|
|
1104
|
+
*/
|
|
1105
|
+
setCameras(cameras) {
|
|
1106
|
+
this.cameras = cameras.slice();
|
|
1107
|
+
if (this.cameras.length > 0) {
|
|
1108
|
+
this.camera = this.cameras[0];
|
|
1109
|
+
CameraManager.setCamera(this.camera);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* @method setCullingEnabled
|
|
1115
|
+
* @description Enables or disables off-screen object culling.
|
|
1116
|
+
* @param {boolean} enabled - Whether culling is enabled
|
|
1117
|
+
*/
|
|
1118
|
+
setCullingEnabled(enabled) {
|
|
1119
|
+
this.cullingEnabled = enabled;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* @method _isVisible
|
|
1124
|
+
* @description Conservative AABB visibility test against the view rectangle.
|
|
1125
|
+
* Instanced objects (whose instances spread beyond the owner transform) and
|
|
1126
|
+
* objects flagged `alwaysVisible` are never culled.
|
|
1127
|
+
* @private
|
|
1128
|
+
*/
|
|
1129
|
+
_isVisible(object, left, right, bottom, top) {
|
|
1130
|
+
if (!object || !object.transform) return true;
|
|
1131
|
+
if (object.alwaysVisible) return true;
|
|
1132
|
+
if (object.transform.parent) return true;
|
|
1133
|
+
if (object.getComponent && object.getComponent(InstancedTexture)) {
|
|
1134
|
+
return true;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
const pos = object.transform.position;
|
|
1138
|
+
const scale = object.transform.scale;
|
|
1139
|
+
const half = Math.max(Math.abs(scale.x), Math.abs(scale.y)) * 1.5;
|
|
1140
|
+
|
|
1141
|
+
return !(
|
|
1142
|
+
pos.x + half < left ||
|
|
1143
|
+
pos.x - half > right ||
|
|
1144
|
+
pos.y + half < bottom ||
|
|
1145
|
+
pos.y - half > top
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
export default Emerald;
|