@codexo/exojs 0.4.0 → 0.6.1
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/CHANGELOG.md +464 -163
- package/README.md +156 -141
- package/dist/esm/audio/AudioAnalyser.d.ts +0 -1
- package/dist/esm/audio/AudioAnalyser.js +0 -2
- package/dist/esm/audio/AudioAnalyser.js.map +1 -1
- package/dist/esm/core/Application.d.ts +4 -4
- package/dist/esm/core/Application.js +19 -19
- package/dist/esm/core/Application.js.map +1 -1
- package/dist/esm/core/Scene.d.ts +59 -24
- package/dist/esm/core/Scene.js +60 -18
- package/dist/esm/core/Scene.js.map +1 -1
- package/dist/esm/core/SceneManager.js +15 -9
- package/dist/esm/core/SceneManager.js.map +1 -1
- package/dist/esm/core/SceneNode.d.ts +45 -5
- package/dist/esm/core/SceneNode.js +136 -7
- package/dist/esm/core/SceneNode.js.map +1 -1
- package/dist/esm/index.js +3 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/math/index.d.ts +0 -1
- package/dist/esm/rendering/CallbackRenderPass.d.ts +3 -3
- package/dist/esm/rendering/CallbackRenderPass.js +2 -2
- package/dist/esm/rendering/CallbackRenderPass.js.map +1 -1
- package/dist/esm/rendering/Container.d.ts +10 -11
- package/dist/esm/rendering/Container.js +5 -5
- package/dist/esm/rendering/Container.js.map +1 -1
- package/dist/esm/rendering/Drawable.d.ts +2 -2
- package/dist/esm/rendering/Drawable.js +5 -5
- package/dist/esm/rendering/Drawable.js.map +1 -1
- package/dist/esm/rendering/{SceneRenderRuntime.d.ts → RenderBackend.d.ts} +21 -3
- package/dist/esm/rendering/RenderNode.d.ts +41 -5
- package/dist/esm/rendering/RenderNode.js +89 -24
- package/dist/esm/rendering/RenderNode.js.map +1 -1
- package/dist/esm/rendering/RenderPass.d.ts +2 -2
- package/dist/esm/rendering/RenderTargetPass.d.ts +3 -3
- package/dist/esm/rendering/RenderTargetPass.js +9 -9
- package/dist/esm/rendering/RenderTargetPass.js.map +1 -1
- package/dist/esm/rendering/Renderer.d.ts +3 -3
- package/dist/esm/rendering/RendererRegistry.d.ts +13 -7
- package/dist/esm/rendering/RendererRegistry.js +18 -10
- package/dist/esm/rendering/RendererRegistry.js.map +1 -1
- package/dist/esm/rendering/filters/BlurFilter.d.ts +2 -2
- package/dist/esm/rendering/filters/BlurFilter.js +5 -5
- package/dist/esm/rendering/filters/BlurFilter.js.map +1 -1
- package/dist/esm/rendering/filters/ColorFilter.d.ts +2 -2
- package/dist/esm/rendering/filters/ColorFilter.js +3 -3
- package/dist/esm/rendering/filters/ColorFilter.js.map +1 -1
- package/dist/esm/rendering/filters/Filter.d.ts +2 -2
- package/dist/esm/rendering/index.d.ts +6 -6
- package/dist/esm/rendering/primitives/Graphics.d.ts +3 -3
- package/dist/esm/rendering/primitives/Graphics.js.map +1 -1
- package/dist/esm/rendering/shader/Shader.d.ts +3 -3
- package/dist/esm/rendering/shader/Shader.js +10 -10
- package/dist/esm/rendering/text/Text.d.ts +2 -2
- package/dist/esm/rendering/text/Text.js +2 -2
- package/dist/esm/rendering/text/Text.js.map +1 -1
- package/dist/esm/rendering/texture/Sampler.d.ts +0 -3
- package/dist/esm/rendering/texture/Sampler.js +5 -7
- package/dist/esm/rendering/texture/Sampler.js.map +1 -1
- package/dist/esm/rendering/types.d.ts +4 -0
- package/dist/esm/rendering/types.js +4 -0
- package/dist/esm/rendering/types.js.map +1 -1
- package/dist/esm/rendering/video/Video.d.ts +2 -2
- package/dist/esm/rendering/video/Video.js +2 -2
- package/dist/esm/rendering/video/Video.js.map +1 -1
- package/dist/esm/rendering/webgl2/AbstractWebGl2BatchedRenderer.d.ts +2 -2
- package/dist/esm/rendering/webgl2/AbstractWebGl2BatchedRenderer.js +35 -11
- package/dist/esm/rendering/webgl2/AbstractWebGl2BatchedRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/AbstractWebGl2Renderer.d.ts +13 -13
- package/dist/esm/rendering/webgl2/AbstractWebGl2Renderer.js +20 -20
- package/dist/esm/rendering/webgl2/AbstractWebGl2Renderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/{WebGl2RenderManager.d.ts → WebGl2Backend.d.ts} +15 -12
- package/dist/esm/rendering/webgl2/{WebGl2RenderManager.js → WebGl2Backend.js} +60 -38
- package/dist/esm/rendering/webgl2/WebGl2Backend.js.map +1 -0
- package/dist/esm/rendering/webgl2/WebGl2MaskCompositor.d.ts +31 -0
- package/dist/esm/rendering/webgl2/WebGl2MaskCompositor.js +186 -0
- package/dist/esm/rendering/webgl2/WebGl2MaskCompositor.js.map +1 -0
- package/dist/esm/rendering/webgl2/WebGl2ParticleRenderer.d.ts +38 -7
- package/dist/esm/rendering/webgl2/WebGl2ParticleRenderer.js +281 -90
- package/dist/esm/rendering/webgl2/WebGl2ParticleRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2PrimitiveRenderer.d.ts +2 -2
- package/dist/esm/rendering/webgl2/WebGl2PrimitiveRenderer.js +15 -10
- package/dist/esm/rendering/webgl2/WebGl2PrimitiveRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2ShaderMappings.js +12 -0
- package/dist/esm/rendering/webgl2/WebGl2ShaderMappings.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2ShaderProgram.d.ts +2 -0
- package/dist/esm/rendering/webgl2/{WebGl2ShaderRuntime.js → WebGl2ShaderProgram.js} +58 -18
- package/dist/esm/rendering/webgl2/WebGl2ShaderProgram.js.map +1 -0
- package/dist/esm/rendering/webgl2/WebGl2SpriteRenderer.d.ts +26 -7
- package/dist/esm/rendering/webgl2/WebGl2SpriteRenderer.js +260 -62
- package/dist/esm/rendering/webgl2/WebGl2SpriteRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2VertexArrayObject.d.ts +24 -1
- package/dist/esm/rendering/webgl2/WebGl2VertexArrayObject.js +6 -2
- package/dist/esm/rendering/webgl2/WebGl2VertexArrayObject.js.map +1 -1
- package/dist/esm/rendering/webgl2/glsl/mask-compose.frag.js +4 -0
- package/dist/esm/rendering/webgl2/glsl/mask-compose.frag.js.map +1 -0
- package/dist/esm/rendering/webgl2/glsl/mask-compose.vert.js +4 -0
- package/dist/esm/rendering/webgl2/glsl/mask-compose.vert.js.map +1 -0
- package/dist/esm/rendering/webgl2/glsl/particle.vert.js +1 -1
- package/dist/esm/rendering/webgl2/glsl/sprite.frag.js +1 -1
- package/dist/esm/rendering/webgl2/glsl/sprite.vert.js +1 -1
- package/dist/esm/rendering/webgpu/AbstractWebGpuRenderer.d.ts +9 -9
- package/dist/esm/rendering/webgpu/AbstractWebGpuRenderer.js +18 -18
- package/dist/esm/rendering/webgpu/AbstractWebGpuRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/{WebGpuRenderManager.d.ts → WebGpuBackend.d.ts} +17 -14
- package/dist/esm/rendering/webgpu/{WebGpuRenderManager.js → WebGpuBackend.js} +74 -40
- package/dist/esm/rendering/webgpu/WebGpuBackend.js.map +1 -0
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.d.ts +37 -0
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js +279 -0
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js.map +1 -0
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.d.ts +2 -3
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js +65 -82
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.d.ts +2 -3
- package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.js +24 -25
- package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.d.ts +28 -13
- package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.js +410 -382
- package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.js.map +1 -1
- package/dist/esm/resources/Loader.js +5 -3
- package/dist/esm/resources/Loader.js.map +1 -1
- package/dist/exo.esm.js +2500 -1399
- package/dist/exo.esm.js.map +1 -1
- package/package.json +20 -11
- package/dist/esm/math/Transformable.d.ts +0 -47
- package/dist/esm/math/Transformable.js +0 -140
- package/dist/esm/math/Transformable.js.map +0 -1
- package/dist/esm/rendering/webgl2/WebGl2RenderManager.js.map +0 -1
- package/dist/esm/rendering/webgl2/WebGl2RendererRuntime.d.ts +0 -15
- package/dist/esm/rendering/webgl2/WebGl2ShaderRuntime.d.ts +0 -2
- package/dist/esm/rendering/webgl2/WebGl2ShaderRuntime.js.map +0 -1
- package/dist/esm/rendering/webgpu/WebGpuRenderManager.js.map +0 -1
- package/dist/esm/rendering/webgpu/WebGpuRendererRuntime.d.ts +0 -8
- package/dist/exo.esm.min.js +0 -2
- package/dist/exo.esm.min.js.map +0 -1
- package/dist/exo.global.js +0 -17328
- package/dist/exo.global.js.map +0 -1
- package/dist/exo.global.min.js +0 -2
- package/dist/exo.global.min.js.map +0 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Shader } from '../shader/Shader.js';
|
|
2
|
+
import { createWebGl2ShaderProgram } from './WebGl2ShaderProgram.js';
|
|
3
|
+
import { WebGl2VertexArrayObject } from './WebGl2VertexArrayObject.js';
|
|
4
|
+
import { WebGl2RenderBuffer } from './WebGl2RenderBuffer.js';
|
|
5
|
+
import { BufferTypes, BufferUsage } from '../types.js';
|
|
6
|
+
import vertexSource from './glsl/mask-compose.vert.js';
|
|
7
|
+
import fragmentSource from './glsl/mask-compose.frag.js';
|
|
8
|
+
|
|
9
|
+
// 4 floats per vertex: position(x, y) + texcoord(u, v).
|
|
10
|
+
const vertexStrideBytes = 16;
|
|
11
|
+
const quadIndices = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
12
|
+
/**
|
|
13
|
+
* Single-quad two-texture compositor used by `WebGl2Backend.composeWithAlphaMask`.
|
|
14
|
+
*
|
|
15
|
+
* Renders the content texture onto the active render target with each
|
|
16
|
+
* output texel's alpha multiplied by the mask texture's alpha at the
|
|
17
|
+
* same UV. Both textures are sampled with stretched-fit UVs over the
|
|
18
|
+
* destination rectangle.
|
|
19
|
+
*
|
|
20
|
+
* Intentionally not a {@link AbstractWebGl2Renderer} subclass: this
|
|
21
|
+
* compositor is invoked directly by the manager for non-Drawable
|
|
22
|
+
* compositing operations and never participates in the renderer
|
|
23
|
+
* registry dispatch path.
|
|
24
|
+
*/
|
|
25
|
+
class WebGl2MaskCompositor {
|
|
26
|
+
_shader = new Shader(vertexSource, fragmentSource);
|
|
27
|
+
_vertexData = new ArrayBuffer(4 * vertexStrideBytes);
|
|
28
|
+
_float32View = new Float32Array(this._vertexData);
|
|
29
|
+
_contentSamplerSlot = new Int32Array([0]);
|
|
30
|
+
_maskSamplerSlot = new Int32Array([1]);
|
|
31
|
+
_connection = null;
|
|
32
|
+
connect(backend) {
|
|
33
|
+
if (this._connection !== null) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const gl = backend.context;
|
|
37
|
+
const vaoHandle = gl.createVertexArray();
|
|
38
|
+
if (vaoHandle === null) {
|
|
39
|
+
throw new Error('WebGl2MaskCompositor: could not create vertex array object.');
|
|
40
|
+
}
|
|
41
|
+
this._shader.connect(createWebGl2ShaderProgram(gl));
|
|
42
|
+
const bufferHandles = new Map();
|
|
43
|
+
const indexBuffer = new WebGl2RenderBuffer(BufferTypes.ElementArrayBuffer, quadIndices, BufferUsage.StaticDraw)
|
|
44
|
+
.connect(this._createBufferRuntime(gl, bufferHandles));
|
|
45
|
+
const vertexBuffer = new WebGl2RenderBuffer(BufferTypes.ArrayBuffer, this._vertexData, BufferUsage.DynamicDraw)
|
|
46
|
+
.connect(this._createBufferRuntime(gl, bufferHandles));
|
|
47
|
+
// Force shader finalize so getAttribute() below sees a populated
|
|
48
|
+
// attribute table; the async-compile path defers extraction until
|
|
49
|
+
// the first sync() call.
|
|
50
|
+
this._shader.sync();
|
|
51
|
+
const vao = new WebGl2VertexArrayObject()
|
|
52
|
+
.addIndex(indexBuffer)
|
|
53
|
+
.addAttribute(vertexBuffer, this._shader.getAttribute('a_position'), gl.FLOAT, false, vertexStrideBytes, 0)
|
|
54
|
+
.addAttribute(vertexBuffer, this._shader.getAttribute('a_texcoord'), gl.FLOAT, false, vertexStrideBytes, 8)
|
|
55
|
+
.connect(this._createVaoRuntime(gl, vaoHandle));
|
|
56
|
+
this._connection = { gl, vaoHandle, vao, indexBuffer, vertexBuffer, bufferHandles };
|
|
57
|
+
}
|
|
58
|
+
disconnect() {
|
|
59
|
+
const connection = this._connection;
|
|
60
|
+
if (connection === null) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
connection.indexBuffer.destroy();
|
|
64
|
+
connection.vertexBuffer.destroy();
|
|
65
|
+
connection.vao.destroy();
|
|
66
|
+
this._shader.disconnect();
|
|
67
|
+
this._connection = null;
|
|
68
|
+
}
|
|
69
|
+
compose(backend, content, mask, x, y, width, height, blendMode) {
|
|
70
|
+
const connection = this._connection;
|
|
71
|
+
if (connection === null) {
|
|
72
|
+
throw new Error('WebGl2MaskCompositor: not connected.');
|
|
73
|
+
}
|
|
74
|
+
// Update the quad vertices for this destination rect. UVs are 0..1
|
|
75
|
+
// mapped over (left, top) → (right, bottom) with an explicit Y-flip
|
|
76
|
+
// for render-texture sampling: the mask path samples textures
|
|
77
|
+
// already authored as RGBA in render-texture orientation.
|
|
78
|
+
this._writeQuadVertices(x, y, x + width, y + height);
|
|
79
|
+
// Bind the compositor program. Setting projection + sampler uniforms
|
|
80
|
+
// each call because they need to match the current render target.
|
|
81
|
+
backend.bindShader(this._shader);
|
|
82
|
+
const view = backend.view;
|
|
83
|
+
const projection = view.getTransform().toArray(false);
|
|
84
|
+
this._shader.getUniform('u_projection').setValue(projection);
|
|
85
|
+
this._shader.getUniform('u_content').setValue(this._contentSamplerSlot);
|
|
86
|
+
this._shader.getUniform('u_mask').setValue(this._maskSamplerSlot);
|
|
87
|
+
this._shader.sync();
|
|
88
|
+
backend.bindTexture(content, 0);
|
|
89
|
+
backend.bindTexture(mask, 1);
|
|
90
|
+
backend.setBlendMode(blendMode);
|
|
91
|
+
backend.bindVertexArrayObject(connection.vao);
|
|
92
|
+
connection.vertexBuffer.upload(this._float32View);
|
|
93
|
+
connection.vao.draw(6, 0);
|
|
94
|
+
backend.stats.batches++;
|
|
95
|
+
backend.stats.drawCalls++;
|
|
96
|
+
// Reset the active texture unit to 0 to avoid leaking unit 1 into
|
|
97
|
+
// subsequent renderer state.
|
|
98
|
+
backend.bindTexture(null, 1);
|
|
99
|
+
}
|
|
100
|
+
_writeQuadVertices(left, top, right, bottom) {
|
|
101
|
+
const view = this._float32View;
|
|
102
|
+
// Vertex 0: top-left (UV 0, 0)
|
|
103
|
+
view[0] = left;
|
|
104
|
+
view[1] = top;
|
|
105
|
+
view[2] = 0;
|
|
106
|
+
view[3] = 0;
|
|
107
|
+
// Vertex 1: top-right (UV 1, 0)
|
|
108
|
+
view[4] = right;
|
|
109
|
+
view[5] = top;
|
|
110
|
+
view[6] = 1;
|
|
111
|
+
view[7] = 0;
|
|
112
|
+
// Vertex 2: bottom-right (UV 1, 1)
|
|
113
|
+
view[8] = right;
|
|
114
|
+
view[9] = bottom;
|
|
115
|
+
view[10] = 1;
|
|
116
|
+
view[11] = 1;
|
|
117
|
+
// Vertex 3: bottom-left (UV 0, 1)
|
|
118
|
+
view[12] = left;
|
|
119
|
+
view[13] = bottom;
|
|
120
|
+
view[14] = 0;
|
|
121
|
+
view[15] = 1;
|
|
122
|
+
}
|
|
123
|
+
_createBufferRuntime(gl, handles) {
|
|
124
|
+
const handle = gl.createBuffer();
|
|
125
|
+
if (handle === null) {
|
|
126
|
+
throw new Error('WebGl2MaskCompositor: could not create render buffer.');
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
bind: (buffer) => {
|
|
130
|
+
gl.bindBuffer(buffer.type, handle);
|
|
131
|
+
},
|
|
132
|
+
upload: (buffer) => {
|
|
133
|
+
const data = buffer.data;
|
|
134
|
+
gl.bindBuffer(buffer.type, handle);
|
|
135
|
+
gl.bufferData(buffer.type, data, buffer.usage);
|
|
136
|
+
handles.set(buffer, handle);
|
|
137
|
+
},
|
|
138
|
+
destroy: (buffer) => {
|
|
139
|
+
gl.deleteBuffer(handle);
|
|
140
|
+
handles.delete(buffer);
|
|
141
|
+
buffer.disconnect();
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
_createVaoRuntime(gl, vaoHandle) {
|
|
146
|
+
let appliedVersion = -1;
|
|
147
|
+
return {
|
|
148
|
+
bind: (vao) => {
|
|
149
|
+
gl.bindVertexArray(vaoHandle);
|
|
150
|
+
if (appliedVersion !== vao.version) {
|
|
151
|
+
let lastBuffer = null;
|
|
152
|
+
for (const attribute of vao.attributes) {
|
|
153
|
+
if (lastBuffer !== attribute.buffer) {
|
|
154
|
+
attribute.buffer.bind();
|
|
155
|
+
lastBuffer = attribute.buffer;
|
|
156
|
+
}
|
|
157
|
+
gl.vertexAttribPointer(attribute.location, attribute.size, attribute.type, attribute.normalized, attribute.stride, attribute.start);
|
|
158
|
+
gl.enableVertexAttribArray(attribute.location);
|
|
159
|
+
}
|
|
160
|
+
if (vao.indexBuffer) {
|
|
161
|
+
vao.indexBuffer.bind();
|
|
162
|
+
}
|
|
163
|
+
appliedVersion = vao.version;
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
unbind: () => {
|
|
167
|
+
gl.bindVertexArray(null);
|
|
168
|
+
},
|
|
169
|
+
draw: (vao, size, start, type) => {
|
|
170
|
+
if (vao.indexBuffer) {
|
|
171
|
+
gl.drawElements(type, size, gl.UNSIGNED_SHORT, start);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
gl.drawArrays(type, start, size);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
destroy: (vao) => {
|
|
178
|
+
gl.deleteVertexArray(vaoHandle);
|
|
179
|
+
vao.disconnect();
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export { WebGl2MaskCompositor };
|
|
186
|
+
//# sourceMappingURL=WebGl2MaskCompositor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebGl2MaskCompositor.js","sources":["../../../../../src/rendering/webgl2/WebGl2MaskCompositor.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,42 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { WebGl2VertexArrayObject } from './WebGl2VertexArrayObject';
|
|
1
|
+
import { AbstractWebGl2Renderer } from './AbstractWebGl2Renderer';
|
|
3
2
|
import type { ParticleSystem } from '@/particles/ParticleSystem';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import type { WebGl2Backend } from './WebGl2Backend';
|
|
4
|
+
export declare class WebGl2ParticleRenderer extends AbstractWebGl2Renderer<ParticleSystem> {
|
|
5
|
+
private readonly _shader;
|
|
6
|
+
private readonly _batchSize;
|
|
7
|
+
private readonly _instanceData;
|
|
8
|
+
private readonly _instanceFloat32;
|
|
9
|
+
private readonly _instanceUint32;
|
|
10
|
+
private _instanceCount;
|
|
11
|
+
private _currentTexture;
|
|
12
|
+
private _currentBlendMode;
|
|
13
|
+
private _currentView;
|
|
14
|
+
private _currentViewId;
|
|
15
|
+
private _instanceBuffer;
|
|
16
|
+
private _indexBuffer;
|
|
17
|
+
private _vao;
|
|
18
|
+
private _connection;
|
|
7
19
|
constructor(batchSize: number);
|
|
8
20
|
render(system: ParticleSystem): this;
|
|
9
|
-
|
|
10
|
-
protected
|
|
21
|
+
flush(): void;
|
|
22
|
+
protected onConnect(backend: WebGl2Backend): void;
|
|
23
|
+
protected onDisconnect(): void;
|
|
24
|
+
destroy(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Convert the system's per-corner packed-u32 texCoords into the
|
|
27
|
+
* (uMin, vMin, uMax, vMax) bounds the new vertex shader expects.
|
|
28
|
+
* Already accounts for flipY (the system's `texCoords` baked it in).
|
|
29
|
+
*
|
|
30
|
+
* The four packed u32s, by corner index, are:
|
|
31
|
+
* [0] TL = (uMin/uMax | vMin/vMax depending on flipY)
|
|
32
|
+
* [1] TR
|
|
33
|
+
* [2] BR
|
|
34
|
+
* [3] BL
|
|
35
|
+
* We need (uMin, vMin, uMax, vMax) — the corner-extreme values.
|
|
36
|
+
*/
|
|
37
|
+
private _uvBoundsScratch;
|
|
38
|
+
private _unpackUvBounds;
|
|
39
|
+
private _createConnection;
|
|
40
|
+
private _createBufferRuntime;
|
|
41
|
+
private _createVaoRuntime;
|
|
11
42
|
}
|
|
@@ -1,108 +1,299 @@
|
|
|
1
|
+
import { AbstractWebGl2Renderer } from './AbstractWebGl2Renderer.js';
|
|
2
|
+
import { Shader } from '../shader/Shader.js';
|
|
3
|
+
import { createWebGl2ShaderProgram } from './WebGl2ShaderProgram.js';
|
|
4
|
+
import { WebGl2RenderBuffer } from './WebGl2RenderBuffer.js';
|
|
1
5
|
import { WebGl2VertexArrayObject } from './WebGl2VertexArrayObject.js';
|
|
6
|
+
import { RenderingPrimitives, BufferTypes, BufferUsage } from '../types.js';
|
|
2
7
|
import vertexSource from './glsl/particle.vert.js';
|
|
3
8
|
import fragmentSource from './glsl/particle.frag.js';
|
|
4
|
-
import { AbstractWebGl2BatchedRenderer } from './AbstractWebGl2BatchedRenderer.js';
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Instanced particle renderer for WebGL2.
|
|
12
|
+
*
|
|
13
|
+
* One ParticleSystem = one batch. Each `render(system)` flushes any
|
|
14
|
+
* pending batch, sets the system-level uniforms (transform, local
|
|
15
|
+
* bounds, UV bounds, texture), and packs every active particle into
|
|
16
|
+
* the per-instance buffer. The next `flush()` issues a single
|
|
17
|
+
* `drawElementsInstanced` for that system.
|
|
18
|
+
*
|
|
19
|
+
* Per-instance layout (24 bytes per particle, 4 attributes):
|
|
20
|
+
* ```
|
|
21
|
+
* translation f32x2 (offset 0, 8 bytes) particle position (system-local)
|
|
22
|
+
* scale f32x2 (offset 8, 8 bytes)
|
|
23
|
+
* rotation f32 (offset 16, 4 bytes) degrees
|
|
24
|
+
* color u8x4 (offset 20, 4 bytes) RGBA tint, normalised
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* vs the previous per-vertex layout (36 bytes per vertex × 4 verts =
|
|
28
|
+
* 144 bytes per particle), this is an 83% bandwidth reduction and
|
|
29
|
+
* roughly 80% fewer CPU writes per particle (one pack call vs four
|
|
30
|
+
* duplicated vertex writes plus per-corner UV coords).
|
|
31
|
+
*
|
|
32
|
+
* The system transform stays as a uniform — mixing systems in one
|
|
33
|
+
* batch would require either a per-instance transform matrix (more
|
|
34
|
+
* bandwidth) or per-particle texture-slot indexing (multi-texture
|
|
35
|
+
* support similar to the sprite renderer). Both are follow-ups; the
|
|
36
|
+
* current pattern keeps the renderer focused on the per-particle win.
|
|
37
|
+
*/
|
|
38
|
+
const instanceStrideBytes = 24;
|
|
39
|
+
const wordsPerInstance = instanceStrideBytes / Uint32Array.BYTES_PER_ELEMENT;
|
|
40
|
+
const indicesPerQuad = 6;
|
|
41
|
+
const quadIndices = new Uint16Array([0, 1, 2, 0, 2, 3]);
|
|
42
|
+
class WebGl2ParticleRenderer extends AbstractWebGl2Renderer {
|
|
43
|
+
_shader;
|
|
44
|
+
_batchSize;
|
|
45
|
+
_instanceData;
|
|
46
|
+
_instanceFloat32;
|
|
47
|
+
_instanceUint32;
|
|
48
|
+
_instanceCount = 0;
|
|
49
|
+
_currentTexture = null;
|
|
50
|
+
_currentBlendMode = null;
|
|
51
|
+
_currentView = null;
|
|
52
|
+
_currentViewId = -1;
|
|
53
|
+
_instanceBuffer = null;
|
|
54
|
+
_indexBuffer = null;
|
|
55
|
+
_vao = null;
|
|
56
|
+
_connection = null;
|
|
7
57
|
constructor(batchSize) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* 1 = rotation (x, y) +
|
|
15
|
-
* 1 = color (ARGB int)
|
|
16
|
-
*/
|
|
17
|
-
super(batchSize, 36, vertexSource, fragmentSource);
|
|
58
|
+
super();
|
|
59
|
+
this._batchSize = batchSize;
|
|
60
|
+
this._shader = new Shader(vertexSource, fragmentSource);
|
|
61
|
+
this._instanceData = new ArrayBuffer(batchSize * instanceStrideBytes);
|
|
62
|
+
this._instanceFloat32 = new Float32Array(this._instanceData);
|
|
63
|
+
this._instanceUint32 = new Uint32Array(this._instanceData);
|
|
18
64
|
}
|
|
19
65
|
render(system) {
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
//
|
|
66
|
+
const backend = this.getBackend();
|
|
67
|
+
const { texture, particles, blendMode } = system;
|
|
68
|
+
const textureChanged = texture !== this._currentTexture;
|
|
69
|
+
const blendModeChanged = blendMode !== this._currentBlendMode;
|
|
70
|
+
// System transform / texture / UV / local-bounds are uniforms, so
|
|
71
|
+
// mixing systems in one batch is invalid. Flush any prior system
|
|
72
|
+
// before setting up this one.
|
|
27
73
|
this.flush();
|
|
28
|
-
if (textureChanged || blendModeChanged) {
|
|
29
|
-
if (textureChanged) {
|
|
30
|
-
this.currentTexture = texture;
|
|
31
|
-
}
|
|
32
|
-
if (blendModeChanged) {
|
|
33
|
-
this.currentBlendMode = blendMode;
|
|
34
|
-
runtime.setBlendMode(blendMode);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
74
|
if (textureChanged) {
|
|
38
|
-
|
|
75
|
+
this._currentTexture = texture;
|
|
76
|
+
backend.bindTexture(texture);
|
|
77
|
+
}
|
|
78
|
+
if (blendModeChanged) {
|
|
79
|
+
this._currentBlendMode = blendMode;
|
|
80
|
+
backend.setBlendMode(blendMode);
|
|
39
81
|
}
|
|
40
|
-
|
|
41
|
-
|
|
82
|
+
// System-level uniforms are set before packing so the eventual
|
|
83
|
+
// flush() can sync them in one go.
|
|
84
|
+
const localBounds = system.vertices;
|
|
85
|
+
const uvBounds = this._unpackUvBounds(system);
|
|
86
|
+
this._shader
|
|
87
|
+
.getUniform('u_systemTransform')
|
|
42
88
|
.setValue(system.getGlobalTransform().toArray(false));
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
= position.x;
|
|
62
|
-
float32View[index + 4]
|
|
63
|
-
= float32View[index + 13]
|
|
64
|
-
= float32View[index + 22]
|
|
65
|
-
= float32View[index + 31]
|
|
66
|
-
= position.y;
|
|
67
|
-
float32View[index + 5]
|
|
68
|
-
= float32View[index + 14]
|
|
69
|
-
= float32View[index + 23]
|
|
70
|
-
= float32View[index + 32]
|
|
71
|
-
= scale.x;
|
|
72
|
-
float32View[index + 6]
|
|
73
|
-
= float32View[index + 15]
|
|
74
|
-
= float32View[index + 24]
|
|
75
|
-
= float32View[index + 33]
|
|
76
|
-
= scale.y;
|
|
77
|
-
float32View[index + 7]
|
|
78
|
-
= float32View[index + 16]
|
|
79
|
-
= float32View[index + 25]
|
|
80
|
-
= float32View[index + 34]
|
|
81
|
-
= rotation;
|
|
82
|
-
uint32View[index + 8]
|
|
83
|
-
= uint32View[index + 17]
|
|
84
|
-
= uint32View[index + 26]
|
|
85
|
-
= uint32View[index + 35]
|
|
86
|
-
= tint.toRgba();
|
|
87
|
-
this.batchIndex++;
|
|
89
|
+
this._shader
|
|
90
|
+
.getUniform('u_localBounds')
|
|
91
|
+
.setValue(localBounds);
|
|
92
|
+
this._shader
|
|
93
|
+
.getUniform('u_uvBounds')
|
|
94
|
+
.setValue(uvBounds);
|
|
95
|
+
const f32 = this._instanceFloat32;
|
|
96
|
+
const u32 = this._instanceUint32;
|
|
97
|
+
const limit = Math.min(particles.length, this._batchSize);
|
|
98
|
+
for (let i = 0; i < limit; i++) {
|
|
99
|
+
const particle = particles[i];
|
|
100
|
+
const offset = i * wordsPerInstance;
|
|
101
|
+
f32[offset + 0] = particle.position.x;
|
|
102
|
+
f32[offset + 1] = particle.position.y;
|
|
103
|
+
f32[offset + 2] = particle.scale.x;
|
|
104
|
+
f32[offset + 3] = particle.scale.y;
|
|
105
|
+
f32[offset + 4] = particle.rotation;
|
|
106
|
+
u32[offset + 5] = particle.tint.toRgba();
|
|
88
107
|
}
|
|
108
|
+
this._instanceCount = limit;
|
|
89
109
|
return this;
|
|
90
110
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
111
|
+
flush() {
|
|
112
|
+
const backend = this.getBackendOrNull();
|
|
113
|
+
const instanceBuffer = this._instanceBuffer;
|
|
114
|
+
const indexBuffer = this._indexBuffer;
|
|
115
|
+
const vao = this._vao;
|
|
116
|
+
if (this._instanceCount === 0 || backend === null || instanceBuffer === null || indexBuffer === null || vao === null) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const view = backend.view;
|
|
120
|
+
if (this._currentView !== view || this._currentViewId !== view.updateId) {
|
|
121
|
+
this._currentView = view;
|
|
122
|
+
this._currentViewId = view.updateId;
|
|
123
|
+
this._shader
|
|
124
|
+
.getUniform('u_projection')
|
|
125
|
+
.setValue(view.getTransform().toArray(false));
|
|
126
|
+
}
|
|
127
|
+
this._shader.sync();
|
|
128
|
+
backend.bindVertexArrayObject(vao);
|
|
129
|
+
instanceBuffer.upload(this._instanceFloat32.subarray(0, this._instanceCount * wordsPerInstance));
|
|
130
|
+
vao.drawInstanced(indicesPerQuad, 0, this._instanceCount, RenderingPrimitives.Triangles);
|
|
131
|
+
backend.stats.batches++;
|
|
132
|
+
backend.stats.drawCalls++;
|
|
133
|
+
this._instanceCount = 0;
|
|
100
134
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
135
|
+
onConnect(backend) {
|
|
136
|
+
const gl = backend.context;
|
|
137
|
+
this._shader.connect(createWebGl2ShaderProgram(gl));
|
|
138
|
+
this._connection = this._createConnection(gl);
|
|
139
|
+
this._indexBuffer = new WebGl2RenderBuffer(BufferTypes.ElementArrayBuffer, quadIndices, BufferUsage.StaticDraw)
|
|
140
|
+
.connect(this._createBufferRuntime(this._connection));
|
|
141
|
+
this._instanceBuffer = new WebGl2RenderBuffer(BufferTypes.ArrayBuffer, this._instanceData, BufferUsage.DynamicDraw)
|
|
142
|
+
.connect(this._createBufferRuntime(this._connection));
|
|
143
|
+
this._shader.sync();
|
|
144
|
+
this._vao = new WebGl2VertexArrayObject()
|
|
145
|
+
.addIndex(this._indexBuffer)
|
|
146
|
+
.addAttribute(this._instanceBuffer, this._shader.getAttribute('a_translation'), gl.FLOAT, false, instanceStrideBytes, 0, false, 1)
|
|
147
|
+
.addAttribute(this._instanceBuffer, this._shader.getAttribute('a_scale'), gl.FLOAT, false, instanceStrideBytes, 8, false, 1)
|
|
148
|
+
.addAttribute(this._instanceBuffer, this._shader.getAttribute('a_rotation'), gl.FLOAT, false, instanceStrideBytes, 16, false, 1)
|
|
149
|
+
.addAttribute(this._instanceBuffer, this._shader.getAttribute('a_color'), gl.UNSIGNED_BYTE, true, instanceStrideBytes, 20, false, 1)
|
|
150
|
+
.connect(this._createVaoRuntime(this._connection));
|
|
151
|
+
}
|
|
152
|
+
onDisconnect() {
|
|
153
|
+
this._shader.disconnect();
|
|
154
|
+
this._instanceBuffer?.destroy();
|
|
155
|
+
this._instanceBuffer = null;
|
|
156
|
+
this._indexBuffer?.destroy();
|
|
157
|
+
this._indexBuffer = null;
|
|
158
|
+
this._vao?.destroy();
|
|
159
|
+
this._vao = null;
|
|
160
|
+
this._connection = null;
|
|
161
|
+
this._currentTexture = null;
|
|
162
|
+
this._currentBlendMode = null;
|
|
163
|
+
this._currentView = null;
|
|
164
|
+
this._currentViewId = -1;
|
|
165
|
+
this._instanceCount = 0;
|
|
166
|
+
}
|
|
167
|
+
destroy() {
|
|
168
|
+
this.disconnect();
|
|
169
|
+
this._shader.destroy();
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Convert the system's per-corner packed-u32 texCoords into the
|
|
173
|
+
* (uMin, vMin, uMax, vMax) bounds the new vertex shader expects.
|
|
174
|
+
* Already accounts for flipY (the system's `texCoords` baked it in).
|
|
175
|
+
*
|
|
176
|
+
* The four packed u32s, by corner index, are:
|
|
177
|
+
* [0] TL = (uMin/uMax | vMin/vMax depending on flipY)
|
|
178
|
+
* [1] TR
|
|
179
|
+
* [2] BR
|
|
180
|
+
* [3] BL
|
|
181
|
+
* We need (uMin, vMin, uMax, vMax) — the corner-extreme values.
|
|
182
|
+
*/
|
|
183
|
+
_uvBoundsScratch = new Float32Array(4);
|
|
184
|
+
_unpackUvBounds(system) {
|
|
185
|
+
const texCoords = system.texCoords;
|
|
186
|
+
// Each packed u32: low 16 bits = U normalised to 0..65535,
|
|
187
|
+
// high 16 bits = V normalised.
|
|
188
|
+
const uTopLeft = (texCoords[0] & 0xFFFF) / 0xFFFF;
|
|
189
|
+
const vTopLeft = ((texCoords[0] >>> 16) & 0xFFFF) / 0xFFFF;
|
|
190
|
+
const uBottomRight = (texCoords[2] & 0xFFFF) / 0xFFFF;
|
|
191
|
+
const vBottomRight = ((texCoords[2] >>> 16) & 0xFFFF) / 0xFFFF;
|
|
192
|
+
// For flipY: TL.v becomes vMax (was minY → maxY). The shader picks
|
|
193
|
+
// (cornerY == 0 ? vMin : vMax); writing TL.v into vMin and BR.v into
|
|
194
|
+
// vMax matches the original per-corner ordering whether or not flipY
|
|
195
|
+
// was applied at texCoords pack time.
|
|
196
|
+
this._uvBoundsScratch[0] = uTopLeft;
|
|
197
|
+
this._uvBoundsScratch[1] = vTopLeft;
|
|
198
|
+
this._uvBoundsScratch[2] = uBottomRight;
|
|
199
|
+
this._uvBoundsScratch[3] = vBottomRight;
|
|
200
|
+
return this._uvBoundsScratch;
|
|
201
|
+
}
|
|
202
|
+
_createConnection(gl) {
|
|
203
|
+
const vaoHandle = gl.createVertexArray();
|
|
204
|
+
if (vaoHandle === null) {
|
|
205
|
+
throw new Error('WebGl2ParticleRenderer: could not create vertex array object.');
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
gl,
|
|
209
|
+
buffers: new Map(),
|
|
210
|
+
vaoHandle,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
_createBufferRuntime(connection) {
|
|
214
|
+
const handle = connection.gl.createBuffer();
|
|
215
|
+
if (handle === null) {
|
|
216
|
+
throw new Error('WebGl2ParticleRenderer: could not create render buffer.');
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
bind: (buffer) => {
|
|
220
|
+
connection.gl.bindBuffer(buffer.type, handle);
|
|
221
|
+
},
|
|
222
|
+
upload: (buffer, offset) => {
|
|
223
|
+
const gl = connection.gl;
|
|
224
|
+
const data = buffer.data;
|
|
225
|
+
const state = connection.buffers.get(buffer);
|
|
226
|
+
gl.bindBuffer(buffer.type, handle);
|
|
227
|
+
if (state && state.dataByteLength >= data.byteLength) {
|
|
228
|
+
gl.bufferSubData(buffer.type, offset, data);
|
|
229
|
+
state.dataByteLength = data.byteLength;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
gl.bufferData(buffer.type, data, buffer.usage);
|
|
233
|
+
connection.buffers.set(buffer, { handle, dataByteLength: data.byteLength });
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
destroy: (buffer) => {
|
|
237
|
+
connection.gl.deleteBuffer(handle);
|
|
238
|
+
connection.buffers.delete(buffer);
|
|
239
|
+
buffer.disconnect();
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
_createVaoRuntime(connection) {
|
|
244
|
+
let appliedVersion = -1;
|
|
245
|
+
return {
|
|
246
|
+
bind: (vao) => {
|
|
247
|
+
const gl = connection.gl;
|
|
248
|
+
gl.bindVertexArray(connection.vaoHandle);
|
|
249
|
+
if (appliedVersion !== vao.version) {
|
|
250
|
+
let lastBuffer = null;
|
|
251
|
+
for (const attribute of vao.attributes) {
|
|
252
|
+
if (lastBuffer !== attribute.buffer) {
|
|
253
|
+
attribute.buffer.bind();
|
|
254
|
+
lastBuffer = attribute.buffer;
|
|
255
|
+
}
|
|
256
|
+
if (attribute.integer) {
|
|
257
|
+
gl.vertexAttribIPointer(attribute.location, attribute.size, attribute.type, attribute.stride, attribute.start);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
gl.vertexAttribPointer(attribute.location, attribute.size, attribute.type, attribute.normalized, attribute.stride, attribute.start);
|
|
261
|
+
}
|
|
262
|
+
gl.enableVertexAttribArray(attribute.location);
|
|
263
|
+
gl.vertexAttribDivisor(attribute.location, attribute.divisor);
|
|
264
|
+
}
|
|
265
|
+
if (vao.indexBuffer) {
|
|
266
|
+
vao.indexBuffer.bind();
|
|
267
|
+
}
|
|
268
|
+
appliedVersion = vao.version;
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
unbind: () => {
|
|
272
|
+
connection.gl.bindVertexArray(null);
|
|
273
|
+
},
|
|
274
|
+
draw: (vao, size, start, type) => {
|
|
275
|
+
const gl = connection.gl;
|
|
276
|
+
if (vao.indexBuffer) {
|
|
277
|
+
gl.drawElements(type, size, gl.UNSIGNED_SHORT, start);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
gl.drawArrays(type, start, size);
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
drawInstanced: (vao, count, start, instanceCount, type) => {
|
|
284
|
+
const gl = connection.gl;
|
|
285
|
+
if (vao.indexBuffer) {
|
|
286
|
+
gl.drawElementsInstanced(type, count, gl.UNSIGNED_SHORT, start, instanceCount);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
gl.drawArraysInstanced(type, start, count, instanceCount);
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
destroy: (vao) => {
|
|
293
|
+
connection.gl.deleteVertexArray(connection.vaoHandle);
|
|
294
|
+
vao.disconnect();
|
|
295
|
+
},
|
|
296
|
+
};
|
|
106
297
|
}
|
|
107
298
|
}
|
|
108
299
|
|