emeraldengine 2.2.1 → 3.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 +2359 -968
- package/dist/types/index.d.ts +72 -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} +5 -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} +60 -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 +80 -0
- package/dist/types/src/ParticleEmitter.d.ts +131 -0
- package/dist/types/{Physics.d.ts → src/Physics.d.ts} +88 -20
- 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 +96 -0
- package/dist/types/src/components/GameObject.d.ts +135 -0
- package/dist/types/src/components/RigidBody.d.ts +183 -0
- package/dist/types/src/importers/Aseprite.d.ts +79 -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/IDManager.d.ts +31 -0
- package/dist/types/src/managers/InputManager.d.ts +290 -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/index.js +72 -0
- package/package.json +74 -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 +18 -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 +251 -118
- package/src/Interpolator.js +124 -0
- package/src/Material.js +202 -0
- package/src/MathUtils.js +133 -0
- package/src/ParticleEmitter.js +284 -0
- package/src/Physics.js +186 -5
- 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 +22 -3
- package/src/components/CircleCollider.js +19 -3
- package/src/components/CircleColliderDebug.js +24 -24
- package/src/components/Collider.js +140 -34
- package/src/components/GameObject.js +130 -21
- package/src/components/RigidBody.js +123 -2
- package/src/importers/Aseprite.js +142 -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/IDManager.js +24 -2
- package/src/managers/InputManager.js +779 -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/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/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/Shapes.js
CHANGED
|
@@ -1,129 +1,118 @@
|
|
|
1
|
-
import { initVertexBuffer } from "./GLUtils.js";
|
|
2
|
-
import Drawable from "./Drawable.js";
|
|
3
|
-
import GLManager from "./managers/GLManager.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @class Square2D
|
|
7
|
-
* @description Represents a square 2D shape
|
|
8
|
-
*/
|
|
9
|
-
class Square2D extends Drawable {
|
|
10
|
-
constructor() {
|
|
11
|
-
const vertices = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0];
|
|
12
|
-
const verticesBuffer = initVertexBuffer(GLManager.getGL(), vertices);
|
|
13
|
-
const textureCoordinates = [
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
0,
|
|
120
|
-
0,
|
|
121
|
-
0,
|
|
122
|
-
false,
|
|
123
|
-
true,
|
|
124
|
-
false
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export { Square2D, Triangle2D, Circle2D };
|
|
1
|
+
import { initVertexBuffer } from "./GLUtils.js";
|
|
2
|
+
import Drawable from "./Drawable.js";
|
|
3
|
+
import GLManager from "./managers/GLManager.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @class Square2D
|
|
7
|
+
* @description Represents a square 2D shape
|
|
8
|
+
*/
|
|
9
|
+
class Square2D extends Drawable {
|
|
10
|
+
constructor() {
|
|
11
|
+
const vertices = [1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0];
|
|
12
|
+
const verticesBuffer = initVertexBuffer(GLManager.getGL(), vertices);
|
|
13
|
+
const textureCoordinates = [1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0];
|
|
14
|
+
const texCoordBuffer = initVertexBuffer(
|
|
15
|
+
GLManager.getGL(),
|
|
16
|
+
textureCoordinates
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
super(
|
|
20
|
+
GLManager.getGL(),
|
|
21
|
+
GLManager.getProgramInfo(),
|
|
22
|
+
verticesBuffer,
|
|
23
|
+
texCoordBuffer,
|
|
24
|
+
vertices,
|
|
25
|
+
false,
|
|
26
|
+
"",
|
|
27
|
+
0,
|
|
28
|
+
0,
|
|
29
|
+
0,
|
|
30
|
+
0,
|
|
31
|
+
0,
|
|
32
|
+
false
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @class Triangle2D
|
|
39
|
+
* @description Represents a triangle 2D shape
|
|
40
|
+
*/
|
|
41
|
+
class Triangle2D extends Drawable {
|
|
42
|
+
constructor() {
|
|
43
|
+
const vertices = [0.0, 1.0, 1.0, -1.0, -1.0, -1.0];
|
|
44
|
+
const verticesBuffer = initVertexBuffer(GLManager.getGL(), vertices);
|
|
45
|
+
|
|
46
|
+
const textureCoordinates = [0.5, 1.0, 1.0, 0.0, 0.0, 0.0];
|
|
47
|
+
const texCoordBuffer = initVertexBuffer(
|
|
48
|
+
GLManager.getGL(),
|
|
49
|
+
textureCoordinates
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
super(
|
|
53
|
+
GLManager.getGL(),
|
|
54
|
+
GLManager.getProgramInfo(),
|
|
55
|
+
verticesBuffer,
|
|
56
|
+
texCoordBuffer,
|
|
57
|
+
vertices,
|
|
58
|
+
false,
|
|
59
|
+
"",
|
|
60
|
+
0,
|
|
61
|
+
0,
|
|
62
|
+
0,
|
|
63
|
+
0,
|
|
64
|
+
0,
|
|
65
|
+
false
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @class Circle2D
|
|
72
|
+
* @description Represents a circle 2D shape
|
|
73
|
+
* @param {number} segments - The number of segments to use to draw the circle
|
|
74
|
+
*/
|
|
75
|
+
class Circle2D extends Drawable {
|
|
76
|
+
constructor(segments = 32) {
|
|
77
|
+
const radius = 1.0;
|
|
78
|
+
const vertices = [];
|
|
79
|
+
const textureCoordinates = [];
|
|
80
|
+
|
|
81
|
+
vertices.push(0, 0);
|
|
82
|
+
textureCoordinates.push(0.5, 0.5);
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i <= segments; i++) {
|
|
85
|
+
const angle = (i * 2 * Math.PI) / segments;
|
|
86
|
+
const x = radius * Math.cos(angle);
|
|
87
|
+
const y = radius * Math.sin(angle);
|
|
88
|
+
vertices.push(x, y);
|
|
89
|
+
textureCoordinates.push((x / radius + 1) / 2, (y / radius + 1) / 2);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const verticesBuffer = initVertexBuffer(GLManager.getGL(), vertices);
|
|
93
|
+
const texCoordBuffer = initVertexBuffer(
|
|
94
|
+
GLManager.getGL(),
|
|
95
|
+
textureCoordinates
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
super(
|
|
99
|
+
GLManager.getGL(),
|
|
100
|
+
GLManager.getProgramInfo(),
|
|
101
|
+
verticesBuffer,
|
|
102
|
+
texCoordBuffer,
|
|
103
|
+
vertices,
|
|
104
|
+
false,
|
|
105
|
+
"",
|
|
106
|
+
0,
|
|
107
|
+
0,
|
|
108
|
+
0,
|
|
109
|
+
0,
|
|
110
|
+
0,
|
|
111
|
+
false,
|
|
112
|
+
true,
|
|
113
|
+
false
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { Square2D, Triangle2D, Circle2D };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @class SpatialGrid
|
|
3
|
+
* @description A uniform spatial hash grid for fast 2D neighbor queries (picking,
|
|
4
|
+
* proximity, broadphase). Rebuild it each frame (or as objects move) with
|
|
5
|
+
* clear()/insert(), then query by radius or rectangle.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const grid = new SpatialGrid(64);
|
|
9
|
+
* grid.clear();
|
|
10
|
+
* for (const e of enemies) grid.insert(e, e.transform.position.x, e.transform.position.y);
|
|
11
|
+
* const near = grid.queryRadius(player.x, player.y, 100);
|
|
12
|
+
*/
|
|
13
|
+
class SpatialGrid {
|
|
14
|
+
/**
|
|
15
|
+
* @param {number} cellSize - World units per cell
|
|
16
|
+
*/
|
|
17
|
+
constructor(cellSize = 64) {
|
|
18
|
+
this.cellSize = cellSize;
|
|
19
|
+
this.cells = new Map();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** @private */
|
|
23
|
+
_key(cx, cy) {
|
|
24
|
+
return cx + "," + cy;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** @private */
|
|
28
|
+
_cell(x, y) {
|
|
29
|
+
return [Math.floor(x / this.cellSize), Math.floor(y / this.cellSize)];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @method clear
|
|
34
|
+
* @description Empties the grid.
|
|
35
|
+
*/
|
|
36
|
+
clear() {
|
|
37
|
+
this.cells.clear();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @method insert
|
|
42
|
+
* @description Inserts an item at a world position.
|
|
43
|
+
* @param {*} item - The item to store
|
|
44
|
+
* @param {number} x - World x
|
|
45
|
+
* @param {number} y - World y
|
|
46
|
+
*/
|
|
47
|
+
insert(item, x, y) {
|
|
48
|
+
const [cx, cy] = this._cell(x, y);
|
|
49
|
+
const key = this._key(cx, cy);
|
|
50
|
+
let bucket = this.cells.get(key);
|
|
51
|
+
if (!bucket) {
|
|
52
|
+
bucket = [];
|
|
53
|
+
this.cells.set(key, bucket);
|
|
54
|
+
}
|
|
55
|
+
bucket.push({ item, x, y });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @method queryRect
|
|
60
|
+
* @description Returns items whose stored position lies within a rectangle.
|
|
61
|
+
* @returns {Array} - Matching items
|
|
62
|
+
*/
|
|
63
|
+
queryRect(minX, minY, maxX, maxY) {
|
|
64
|
+
const [cx0, cy0] = this._cell(minX, minY);
|
|
65
|
+
const [cx1, cy1] = this._cell(maxX, maxY);
|
|
66
|
+
const results = [];
|
|
67
|
+
for (let cx = cx0; cx <= cx1; cx++) {
|
|
68
|
+
for (let cy = cy0; cy <= cy1; cy++) {
|
|
69
|
+
const bucket = this.cells.get(this._key(cx, cy));
|
|
70
|
+
if (!bucket) continue;
|
|
71
|
+
for (const entry of bucket) {
|
|
72
|
+
if (
|
|
73
|
+
entry.x >= minX &&
|
|
74
|
+
entry.x <= maxX &&
|
|
75
|
+
entry.y >= minY &&
|
|
76
|
+
entry.y <= maxY
|
|
77
|
+
) {
|
|
78
|
+
results.push(entry.item);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @method queryRadius
|
|
88
|
+
* @description Returns items within a radius of a point.
|
|
89
|
+
* @returns {Array} - Matching items
|
|
90
|
+
*/
|
|
91
|
+
queryRadius(x, y, radius) {
|
|
92
|
+
const r2 = radius * radius;
|
|
93
|
+
const results = [];
|
|
94
|
+
const [cx0, cy0] = this._cell(x - radius, y - radius);
|
|
95
|
+
const [cx1, cy1] = this._cell(x + radius, y + radius);
|
|
96
|
+
for (let cx = cx0; cx <= cx1; cx++) {
|
|
97
|
+
for (let cy = cy0; cy <= cy1; cy++) {
|
|
98
|
+
const bucket = this.cells.get(this._key(cx, cy));
|
|
99
|
+
if (!bucket) continue;
|
|
100
|
+
for (const entry of bucket) {
|
|
101
|
+
const dx = entry.x - x;
|
|
102
|
+
const dy = entry.y - y;
|
|
103
|
+
if (dx * dx + dy * dy <= r2) results.push(entry.item);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default SpatialGrid;
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { initShaderProgram } from "./GLUtils.js";
|
|
2
|
+
import GLManager from "./managers/GLManager.js";
|
|
3
|
+
import RenderStats from "./managers/RenderStats.js";
|
|
4
|
+
|
|
5
|
+
const BATCH_VERTEX_SHADER = `
|
|
6
|
+
attribute vec2 aPos;
|
|
7
|
+
attribute vec2 aUV;
|
|
8
|
+
attribute vec4 aColor;
|
|
9
|
+
uniform mat4 uProjection;
|
|
10
|
+
uniform mat4 uView;
|
|
11
|
+
varying vec2 vUV;
|
|
12
|
+
varying vec4 vColor;
|
|
13
|
+
void main() {
|
|
14
|
+
gl_Position = uProjection * uView * vec4(aPos, 0.0, 1.0);
|
|
15
|
+
vUV = aUV;
|
|
16
|
+
vColor = aColor;
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const BATCH_FRAGMENT_SHADER = `
|
|
21
|
+
#ifdef GL_ES
|
|
22
|
+
precision highp float;
|
|
23
|
+
#endif
|
|
24
|
+
uniform sampler2D uSampler;
|
|
25
|
+
varying vec2 vUV;
|
|
26
|
+
varying vec4 vColor;
|
|
27
|
+
void main() {
|
|
28
|
+
vec4 tex = texture2D(uSampler, vUV);
|
|
29
|
+
if (tex.a < 0.01) discard;
|
|
30
|
+
gl_FragColor = tex * vColor;
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const FLOATS_PER_VERTEX = 8;
|
|
35
|
+
const VERTS_PER_QUAD = 4;
|
|
36
|
+
const INDICES_PER_QUAD = 6;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @class SpriteBatch
|
|
40
|
+
* @description A dynamic batched sprite renderer. Instead of one draw call per
|
|
41
|
+
* sprite (as ordinary Drawables incur), it accumulates many sprites that share a
|
|
42
|
+
* texture into a single interleaved buffer and submits them in one
|
|
43
|
+
* `drawElements` call. Batches are flushed automatically when the texture
|
|
44
|
+
* changes or the buffer fills, so for content that shares a texture/atlas this
|
|
45
|
+
* collapses hundreds of draw calls into a handful.
|
|
46
|
+
*
|
|
47
|
+
* It uses its own minimal shader program (position + uv + per-vertex color), so
|
|
48
|
+
* it is independent of the main render pipeline.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* const batch = new SpriteBatch();
|
|
52
|
+
* batch.begin(projectionMatrix, viewMatrix); // gl-matrix mat4s
|
|
53
|
+
* for (const e of entities) {
|
|
54
|
+
* batch.draw({ texture: atlasTex, x: e.x, y: e.y, w: 32, h: 32,
|
|
55
|
+
* u0: e.u0, v0: e.v0, u1: e.u1, v1: e.v1 });
|
|
56
|
+
* }
|
|
57
|
+
* batch.end();
|
|
58
|
+
*/
|
|
59
|
+
class SpriteBatch {
|
|
60
|
+
/**
|
|
61
|
+
* @param {Object} [options] - { maxQuads = 2000 }
|
|
62
|
+
*/
|
|
63
|
+
constructor(options = {}) {
|
|
64
|
+
this.gl = GLManager.getGL();
|
|
65
|
+
this.maxQuads = options.maxQuads ?? 2000;
|
|
66
|
+
|
|
67
|
+
const gl = this.gl;
|
|
68
|
+
this.program = initShaderProgram(
|
|
69
|
+
gl,
|
|
70
|
+
BATCH_VERTEX_SHADER,
|
|
71
|
+
BATCH_FRAGMENT_SHADER
|
|
72
|
+
);
|
|
73
|
+
this.attribs = {
|
|
74
|
+
pos: gl.getAttribLocation(this.program, "aPos"),
|
|
75
|
+
uv: gl.getAttribLocation(this.program, "aUV"),
|
|
76
|
+
color: gl.getAttribLocation(this.program, "aColor"),
|
|
77
|
+
};
|
|
78
|
+
this.uniforms = {
|
|
79
|
+
projection: gl.getUniformLocation(this.program, "uProjection"),
|
|
80
|
+
view: gl.getUniformLocation(this.program, "uView"),
|
|
81
|
+
sampler: gl.getUniformLocation(this.program, "uSampler"),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
this.vertexData = new Float32Array(
|
|
85
|
+
this.maxQuads * VERTS_PER_QUAD * FLOATS_PER_VERTEX
|
|
86
|
+
);
|
|
87
|
+
this.vertexBuffer = gl.createBuffer();
|
|
88
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
89
|
+
gl.bufferData(gl.ARRAY_BUFFER, this.vertexData.byteLength, gl.DYNAMIC_DRAW);
|
|
90
|
+
|
|
91
|
+
const indices = new Uint16Array(this.maxQuads * INDICES_PER_QUAD);
|
|
92
|
+
for (let q = 0; q < this.maxQuads; q++) {
|
|
93
|
+
const v = q * VERTS_PER_QUAD;
|
|
94
|
+
const i = q * INDICES_PER_QUAD;
|
|
95
|
+
indices[i] = v;
|
|
96
|
+
indices[i + 1] = v + 1;
|
|
97
|
+
indices[i + 2] = v + 2;
|
|
98
|
+
indices[i + 3] = v;
|
|
99
|
+
indices[i + 4] = v + 2;
|
|
100
|
+
indices[i + 5] = v + 3;
|
|
101
|
+
}
|
|
102
|
+
this.indexBuffer = gl.createBuffer();
|
|
103
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
104
|
+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
|
|
105
|
+
|
|
106
|
+
/** @private */
|
|
107
|
+
this._quadCount = 0;
|
|
108
|
+
/** @private */
|
|
109
|
+
this._currentTexture = null;
|
|
110
|
+
/** @private */
|
|
111
|
+
this._projection = null;
|
|
112
|
+
/** @private */
|
|
113
|
+
this._view = null;
|
|
114
|
+
this.drawCalls = 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @method begin
|
|
119
|
+
* @description Starts a batch with the given projection and view matrices
|
|
120
|
+
* (gl-matrix mat4 / Float32Array(16)).
|
|
121
|
+
*/
|
|
122
|
+
begin(projectionMatrix, viewMatrix) {
|
|
123
|
+
this._projection = projectionMatrix;
|
|
124
|
+
this._view = viewMatrix;
|
|
125
|
+
this._quadCount = 0;
|
|
126
|
+
this._currentTexture = null;
|
|
127
|
+
this.drawCalls = 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @method draw
|
|
132
|
+
* @description Queues one sprite. Flushes first if the texture changed or the
|
|
133
|
+
* batch is full.
|
|
134
|
+
* @param {Object} sprite - {
|
|
135
|
+
* texture, x, y, w, h,
|
|
136
|
+
* rotation = 0, originX = 0.5, originY = 0.5,
|
|
137
|
+
* u0 = 0, v0 = 0, u1 = 1, v1 = 1,
|
|
138
|
+
* r = 1, g = 1, b = 1, a = 1
|
|
139
|
+
* }
|
|
140
|
+
*/
|
|
141
|
+
draw(sprite) {
|
|
142
|
+
const tex = sprite.texture;
|
|
143
|
+
if (
|
|
144
|
+
(this._currentTexture && tex !== this._currentTexture) ||
|
|
145
|
+
this._quadCount >= this.maxQuads
|
|
146
|
+
) {
|
|
147
|
+
this.flush();
|
|
148
|
+
}
|
|
149
|
+
this._currentTexture = tex;
|
|
150
|
+
|
|
151
|
+
const {
|
|
152
|
+
x,
|
|
153
|
+
y,
|
|
154
|
+
w,
|
|
155
|
+
h,
|
|
156
|
+
rotation = 0,
|
|
157
|
+
originX = 0.5,
|
|
158
|
+
originY = 0.5,
|
|
159
|
+
u0 = 0,
|
|
160
|
+
v0 = 0,
|
|
161
|
+
u1 = 1,
|
|
162
|
+
v1 = 1,
|
|
163
|
+
r = 1,
|
|
164
|
+
g = 1,
|
|
165
|
+
b = 1,
|
|
166
|
+
a = 1,
|
|
167
|
+
} = sprite;
|
|
168
|
+
|
|
169
|
+
const cx = x + (0.5 - originX) * w;
|
|
170
|
+
const cy = y + (0.5 - originY) * h;
|
|
171
|
+
const offset = this._quadCount * VERTS_PER_QUAD * FLOATS_PER_VERTEX;
|
|
172
|
+
SpriteBatch._writeQuad(
|
|
173
|
+
this.vertexData,
|
|
174
|
+
offset,
|
|
175
|
+
cx,
|
|
176
|
+
cy,
|
|
177
|
+
w / 2,
|
|
178
|
+
h / 2,
|
|
179
|
+
rotation,
|
|
180
|
+
u0,
|
|
181
|
+
v0,
|
|
182
|
+
u1,
|
|
183
|
+
v1,
|
|
184
|
+
r,
|
|
185
|
+
g,
|
|
186
|
+
b,
|
|
187
|
+
a
|
|
188
|
+
);
|
|
189
|
+
this._quadCount++;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @method end
|
|
194
|
+
* @description Flushes any remaining sprites.
|
|
195
|
+
*/
|
|
196
|
+
end() {
|
|
197
|
+
this.flush();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @method flush
|
|
202
|
+
* @description Uploads and draws the currently staged quads.
|
|
203
|
+
*/
|
|
204
|
+
flush() {
|
|
205
|
+
if (this._quadCount === 0 || !this._currentTexture) return;
|
|
206
|
+
const gl = this.gl;
|
|
207
|
+
|
|
208
|
+
gl.useProgram(this.program);
|
|
209
|
+
gl.uniformMatrix4fv(this.uniforms.projection, false, this._projection);
|
|
210
|
+
gl.uniformMatrix4fv(this.uniforms.view, false, this._view);
|
|
211
|
+
|
|
212
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
213
|
+
gl.bindTexture(gl.TEXTURE_2D, this._currentTexture);
|
|
214
|
+
gl.uniform1i(this.uniforms.sampler, 0);
|
|
215
|
+
|
|
216
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
217
|
+
const used = this._quadCount * VERTS_PER_QUAD * FLOATS_PER_VERTEX;
|
|
218
|
+
gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexData.subarray(0, used));
|
|
219
|
+
|
|
220
|
+
const stride = FLOATS_PER_VERTEX * 4;
|
|
221
|
+
gl.enableVertexAttribArray(this.attribs.pos);
|
|
222
|
+
gl.vertexAttribPointer(this.attribs.pos, 2, gl.FLOAT, false, stride, 0);
|
|
223
|
+
gl.enableVertexAttribArray(this.attribs.uv);
|
|
224
|
+
gl.vertexAttribPointer(this.attribs.uv, 2, gl.FLOAT, false, stride, 2 * 4);
|
|
225
|
+
gl.enableVertexAttribArray(this.attribs.color);
|
|
226
|
+
gl.vertexAttribPointer(
|
|
227
|
+
this.attribs.color,
|
|
228
|
+
4,
|
|
229
|
+
gl.FLOAT,
|
|
230
|
+
false,
|
|
231
|
+
stride,
|
|
232
|
+
4 * 4
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
|
236
|
+
gl.drawElements(
|
|
237
|
+
gl.TRIANGLES,
|
|
238
|
+
this._quadCount * INDICES_PER_QUAD,
|
|
239
|
+
gl.UNSIGNED_SHORT,
|
|
240
|
+
0
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
this.drawCalls++;
|
|
244
|
+
RenderStats.drawCalls++;
|
|
245
|
+
RenderStats.quads += this._quadCount;
|
|
246
|
+
RenderStats.textureBinds++;
|
|
247
|
+
this._quadCount = 0;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* @method _writeQuad
|
|
252
|
+
* @description Pure helper that writes one quad's 4 interleaved vertices
|
|
253
|
+
* (position, uv, color) into `out` at `offset`. Positions are rotated around
|
|
254
|
+
* the quad center (cx, cy). Exposed (and side-effect free) for testing.
|
|
255
|
+
* @private
|
|
256
|
+
*/
|
|
257
|
+
static _writeQuad(
|
|
258
|
+
out,
|
|
259
|
+
offset,
|
|
260
|
+
cx,
|
|
261
|
+
cy,
|
|
262
|
+
hw,
|
|
263
|
+
hh,
|
|
264
|
+
rot,
|
|
265
|
+
u0,
|
|
266
|
+
v0,
|
|
267
|
+
u1,
|
|
268
|
+
v1,
|
|
269
|
+
r,
|
|
270
|
+
g,
|
|
271
|
+
b,
|
|
272
|
+
a
|
|
273
|
+
) {
|
|
274
|
+
const cos = Math.cos(rot);
|
|
275
|
+
const sin = Math.sin(rot);
|
|
276
|
+
const corners = [
|
|
277
|
+
[-hw, hh, u0, v0],
|
|
278
|
+
[hw, hh, u1, v0],
|
|
279
|
+
[hw, -hh, u1, v1],
|
|
280
|
+
[-hw, -hh, u0, v1],
|
|
281
|
+
];
|
|
282
|
+
let o = offset;
|
|
283
|
+
for (let i = 0; i < 4; i++) {
|
|
284
|
+
const lx = corners[i][0];
|
|
285
|
+
const ly = corners[i][1];
|
|
286
|
+
out[o++] = cx + lx * cos - ly * sin;
|
|
287
|
+
out[o++] = cy + lx * sin + ly * cos;
|
|
288
|
+
out[o++] = corners[i][2];
|
|
289
|
+
out[o++] = corners[i][3];
|
|
290
|
+
out[o++] = r;
|
|
291
|
+
out[o++] = g;
|
|
292
|
+
out[o++] = b;
|
|
293
|
+
out[o++] = a;
|
|
294
|
+
}
|
|
295
|
+
return o;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export default SpriteBatch;
|