@codexo/exojs-tilemap 0.15.3 → 0.16.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 +33 -26
- package/dist/esm/ChunkSource.d.ts +33 -0
- package/dist/esm/ChunkSource.d.ts.map +1 -0
- package/dist/esm/ChunkStreamer.d.ts +98 -0
- package/dist/esm/ChunkStreamer.d.ts.map +1 -0
- package/dist/esm/ChunkStreamer.js +201 -0
- package/dist/esm/ChunkStreamer.js.map +1 -0
- package/dist/esm/ImageLayer.d.ts +11 -3
- package/dist/esm/ImageLayer.d.ts.map +1 -0
- package/dist/esm/ImageLayer.js +71 -64
- package/dist/esm/ImageLayer.js.map +1 -1
- package/dist/esm/ImageLayerNode.d.ts +89 -0
- package/dist/esm/ImageLayerNode.d.ts.map +1 -0
- package/dist/esm/ImageLayerNode.js +205 -0
- package/dist/esm/ImageLayerNode.js.map +1 -0
- package/dist/esm/MapObject.d.ts +70 -0
- package/dist/esm/MapObject.d.ts.map +1 -0
- package/dist/esm/MapObject.js +34 -0
- package/dist/esm/MapObject.js.map +1 -0
- package/dist/esm/MapObjectSpawner.d.ts +133 -0
- package/dist/esm/MapObjectSpawner.d.ts.map +1 -0
- package/dist/esm/MapObjectSpawner.js +147 -0
- package/dist/esm/MapObjectSpawner.js.map +1 -0
- package/dist/esm/MapSpawnSession.d.ts +53 -0
- package/dist/esm/MapSpawnSession.d.ts.map +1 -0
- package/dist/esm/MapSpawnSession.js +88 -0
- package/dist/esm/MapSpawnSession.js.map +1 -0
- package/dist/esm/MapWorld.d.ts +134 -0
- package/dist/esm/MapWorld.d.ts.map +1 -0
- package/dist/esm/MapWorld.js +150 -0
- package/dist/esm/MapWorld.js.map +1 -0
- package/dist/esm/MapWorldRuntime.d.ts +182 -0
- package/dist/esm/MapWorldRuntime.d.ts.map +1 -0
- package/dist/esm/MapWorldRuntime.js +261 -0
- package/dist/esm/MapWorldRuntime.js.map +1 -0
- package/dist/esm/ObjectLayer.d.ts +93 -7
- package/dist/esm/ObjectLayer.d.ts.map +1 -0
- package/dist/esm/ObjectLayer.js +184 -167
- package/dist/esm/ObjectLayer.js.map +1 -1
- package/dist/esm/SampledChunkSource.d.ts +43 -0
- package/dist/esm/SampledChunkSource.d.ts.map +1 -0
- package/dist/esm/SampledChunkSource.js +48 -0
- package/dist/esm/SampledChunkSource.js.map +1 -0
- package/dist/esm/TileAnimator.d.ts +12 -2
- package/dist/esm/TileAnimator.d.ts.map +1 -0
- package/dist/esm/TileAnimator.js +154 -165
- package/dist/esm/TileAnimator.js.map +1 -1
- package/dist/esm/TileChunk.d.ts +30 -5
- package/dist/esm/TileChunk.d.ts.map +1 -0
- package/dist/esm/TileChunk.js +190 -188
- package/dist/esm/TileChunk.js.map +1 -1
- package/dist/esm/TileChunkNode.d.ts +18 -3
- package/dist/esm/TileChunkNode.d.ts.map +1 -0
- package/dist/esm/TileChunkNode.js +104 -85
- package/dist/esm/TileChunkNode.js.map +1 -1
- package/dist/esm/TileLayer.d.ts +104 -24
- package/dist/esm/TileLayer.d.ts.map +1 -0
- package/dist/esm/TileLayer.js +511 -469
- package/dist/esm/TileLayer.js.map +1 -1
- package/dist/esm/TileLayerNode.d.ts +52 -11
- package/dist/esm/TileLayerNode.d.ts.map +1 -0
- package/dist/esm/TileLayerNode.js +224 -166
- package/dist/esm/TileLayerNode.js.map +1 -1
- package/dist/esm/TileMap.d.ts +96 -31
- package/dist/esm/TileMap.d.ts.map +1 -0
- package/dist/esm/TileMap.js +401 -357
- package/dist/esm/TileMap.js.map +1 -1
- package/dist/esm/TileMapBand.d.ts +42 -29
- package/dist/esm/TileMapBand.d.ts.map +1 -0
- package/dist/esm/TileMapBand.js +151 -158
- package/dist/esm/TileMapBand.js.map +1 -1
- package/dist/esm/TileMapNode.d.ts +34 -18
- package/dist/esm/TileMapNode.d.ts.map +1 -0
- package/dist/esm/TileMapNode.js +122 -109
- package/dist/esm/TileMapNode.js.map +1 -1
- package/dist/esm/TileMapView.d.ts +108 -47
- package/dist/esm/TileMapView.d.ts.map +1 -0
- package/dist/esm/TileMapView.js +385 -338
- package/dist/esm/TileMapView.js.map +1 -1
- package/dist/esm/TileSet.d.ts +15 -6
- package/dist/esm/TileSet.d.ts.map +1 -0
- package/dist/esm/TileSet.js +161 -181
- package/dist/esm/TileSet.js.map +1 -1
- package/dist/esm/WangSet.d.ts +5 -4
- package/dist/esm/WangSet.d.ts.map +1 -0
- package/dist/esm/WangSet.js +70 -78
- package/dist/esm/WangSet.js.map +1 -1
- package/dist/esm/WorkerSampledChunkSource.d.ts +95 -0
- package/dist/esm/WorkerSampledChunkSource.d.ts.map +1 -0
- package/dist/esm/WorkerSampledChunkSource.js +117 -0
- package/dist/esm/WorkerSampledChunkSource.js.map +1 -0
- package/dist/esm/autoTile.d.ts +10 -5
- package/dist/esm/autoTile.d.ts.map +1 -0
- package/dist/esm/autoTile.js +164 -205
- package/dist/esm/autoTile.js.map +1 -1
- package/dist/esm/chunkGeometry.d.ts +8 -7
- package/dist/esm/chunkGeometry.d.ts.map +1 -0
- package/dist/esm/chunkGeometry.js +83 -94
- package/dist/esm/chunkGeometry.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +26 -15
- package/dist/esm/nodeBounds.d.ts +20 -0
- package/dist/esm/nodeBounds.d.ts.map +1 -0
- package/dist/esm/nodeBounds.js +40 -0
- package/dist/esm/nodeBounds.js.map +1 -0
- package/dist/esm/pixelSnap.d.ts +4 -3
- package/dist/esm/pixelSnap.d.ts.map +1 -0
- package/dist/esm/pixelSnap.js +18 -16
- package/dist/esm/pixelSnap.js.map +1 -1
- package/dist/esm/public.d.ts +23 -3
- package/dist/esm/public.d.ts.map +1 -0
- package/dist/esm/tileCollision.d.ts +190 -0
- package/dist/esm/tileCollision.d.ts.map +1 -0
- package/dist/esm/tileCollision.js +367 -0
- package/dist/esm/tileCollision.js.map +1 -0
- package/dist/esm/tileWord.d.ts +12 -0
- package/dist/esm/tileWord.d.ts.map +1 -0
- package/dist/esm/tileWord.js +16 -0
- package/dist/esm/tileWord.js.map +1 -0
- package/dist/esm/tilemapExtension.d.ts +4 -3
- package/dist/esm/tilemapExtension.d.ts.map +1 -0
- package/dist/esm/tilemapExtension.js +47 -47
- package/dist/esm/tilemapExtension.js.map +1 -1
- package/dist/esm/tilemapSerializers.d.ts +5 -4
- package/dist/esm/tilemapSerializers.d.ts.map +1 -0
- package/dist/esm/tilemapSerializers.js +48 -41
- package/dist/esm/tilemapSerializers.js.map +1 -1
- package/dist/esm/types.d.ts +34 -18
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +111 -120
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.d.ts +50 -2
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.d.ts.map +1 -0
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.js +366 -332
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.js.map +1 -1
- package/dist/esm/webgl2/shaders/tile-chunk.frag.js +6 -0
- package/dist/esm/webgl2/shaders/tile-chunk.frag.js.map +1 -0
- package/dist/esm/webgl2/shaders/tile-chunk.vert.js +6 -0
- package/dist/esm/webgl2/shaders/tile-chunk.vert.js.map +1 -0
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.d.ts +70 -4
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.d.ts.map +1 -0
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.js +496 -383
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.js.map +1 -1
- package/dist/esm/webgpu/shaders/tile-chunk.wgsl.js +6 -0
- package/dist/esm/webgpu/shaders/tile-chunk.wgsl.js.map +1 -0
- package/package.json +8 -15
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/register.d.ts +0 -1
- package/dist/esm/register.js +0 -25
- package/dist/esm/register.js.map +0 -1
|
@@ -1,339 +1,373 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TILE_DIAGONAL_BIT, TILE_ROW_MASK } from "../tileWord.js";
|
|
2
|
+
import tile_chunk_default from "./shaders/tile-chunk.frag.js";
|
|
3
|
+
import tile_chunk_default$1 from "./shaders/tile-chunk.vert.js";
|
|
4
|
+
import { AbstractWebGl2Renderer, BlendModes, BufferTypes, BufferUsage, RenderingPrimitives, Shader, WebGl2RenderBuffer, WebGl2VertexArrayObject, createWebGl2ShaderProgram, fillShaderSource, packedGroupChanged, uploadBufferRange, uploadBufferStore } from "@codexo/exojs/renderer-sdk";
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
// convention (NineSlice/Repeating): float32x4 local rect, unorm16x4 UV bounds,
|
|
5
|
-
// unorm8x4 tint, uint32 tile word (transform row + diagonal bit).
|
|
6
|
+
//#region src/webgl2/WebGl2TileChunkRenderer.ts
|
|
6
7
|
const instanceStrideBytes = 32;
|
|
7
8
|
const wordsPerInstance = instanceStrideBytes / Uint32Array.BYTES_PER_ELEMENT;
|
|
8
9
|
const transformTextureUnit = 1;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
uniform mat3 u_projection;
|
|
26
|
-
uniform sampler2D u_transforms; // shared per-frame transform buffer (2 texels/row)
|
|
27
|
-
|
|
28
|
-
out vec2 v_texcoord;
|
|
29
|
-
out vec4 v_color;
|
|
30
|
-
|
|
31
|
-
void main(void) {
|
|
32
|
-
// gl_VertexID 0..3 -> corner: 0=TL, 1=TR, 2=BL, 3=BR (TRIANGLE_STRIP order)
|
|
33
|
-
int vid = gl_VertexID;
|
|
34
|
-
int cornerX = vid & 1;
|
|
35
|
-
int cornerY = (vid >> 1) & 1;
|
|
36
|
-
|
|
37
|
-
float localX = (cornerX == 0) ? a_quadBounds.x : a_quadBounds.z;
|
|
38
|
-
float localY = (cornerY == 0) ? a_quadBounds.y : a_quadBounds.w;
|
|
39
|
-
|
|
40
|
-
int row = int(a_tileWord & ${TILE_ROW_MASK}u);
|
|
41
|
-
bool diagonal = (a_tileWord & ${TILE_DIAGONAL_BIT}u) != 0u;
|
|
42
|
-
|
|
43
|
-
vec4 m0 = texelFetch(u_transforms, ivec2(0, row), 0); // a, b, c, d
|
|
44
|
-
vec4 m1 = texelFetch(u_transforms, ivec2(1, row), 0); // tx, ty, 0, 0
|
|
45
|
-
|
|
46
|
-
float worldX = (m0.x * localX) + (m0.y * localY) + m1.x;
|
|
47
|
-
float worldY = (m0.z * localX) + (m0.w * localY) + m1.y;
|
|
48
|
-
|
|
49
|
-
gl_Position = vec4((u_projection * vec3(worldX, worldY, 1.0)).xy, 0.0, 1.0);
|
|
50
|
-
|
|
51
|
-
// Tile orientation: the diagonal flip transposes the corner-coordinate axes
|
|
52
|
-
// before the UV corner is selected; flipX/flipY are already baked into the
|
|
53
|
-
// (uMin,uMax)/(vMin,vMax) ordering by the CPU writer.
|
|
54
|
-
int su = cornerX;
|
|
55
|
-
int sv = cornerY;
|
|
56
|
-
if (diagonal) { int t = su; su = sv; sv = t; }
|
|
57
|
-
|
|
58
|
-
float u = (su == 0) ? a_uvBounds.x : a_uvBounds.z;
|
|
59
|
-
float v = (sv == 0) ? a_uvBounds.y : a_uvBounds.w;
|
|
60
|
-
v_texcoord = vec2(u, v);
|
|
61
|
-
|
|
62
|
-
v_color = vec4(a_color.rgb * a_color.a, a_color.a);
|
|
63
|
-
}`;
|
|
64
|
-
const tileFragmentSource = `#version 300 es
|
|
65
|
-
precision highp float;
|
|
66
|
-
|
|
67
|
-
uniform sampler2D u_texture;
|
|
68
|
-
|
|
69
|
-
in vec2 v_texcoord;
|
|
70
|
-
in vec4 v_color;
|
|
71
|
-
|
|
72
|
-
layout(location = 0) out vec4 fragColor;
|
|
73
|
-
|
|
74
|
-
void main(void) {
|
|
75
|
-
vec4 sampleColor = texture(u_texture, v_texcoord);
|
|
76
|
-
fragColor = sampleColor * v_color;
|
|
77
|
-
}`;
|
|
10
|
+
const identityGroupMat3 = new Float32Array([
|
|
11
|
+
1,
|
|
12
|
+
0,
|
|
13
|
+
0,
|
|
14
|
+
0,
|
|
15
|
+
1,
|
|
16
|
+
0,
|
|
17
|
+
0,
|
|
18
|
+
0,
|
|
19
|
+
1
|
|
20
|
+
]);
|
|
21
|
+
const tileVertexSource = fillShaderSource(tile_chunk_default$1, {
|
|
22
|
+
tileRowMask: TILE_ROW_MASK,
|
|
23
|
+
tileDiagonalBit: TILE_DIAGONAL_BIT
|
|
24
|
+
});
|
|
78
25
|
/**
|
|
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
|
-
|
|
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
|
-
|
|
26
|
+
* Instanced WebGL2 renderer for {@link TileChunkNode}. Each tile is one
|
|
27
|
+
* instanced quad; tiles are batched by `(shader, tileset texture)` and one
|
|
28
|
+
* `drawArraysInstanced` is issued per batch. Per-chunk transforms ride on the
|
|
29
|
+
* shared transform buffer (one row per chunk node), so the chunk geometry is
|
|
30
|
+
* orientation-neutral and never re-uploaded for a camera pan.
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
var WebGl2TileChunkRenderer = class extends AbstractWebGl2Renderer {
|
|
34
|
+
/**
|
|
35
|
+
* Retained-batch capability opt-in: a tile chunk's per-flush
|
|
36
|
+
* instanced batches (fixed 32-byte layout, tile word at word 7) can be
|
|
37
|
+
* recorded into a group's instruction set and replayed from group-owned
|
|
38
|
+
* resources. Pixel-snapped draws are excluded by the collect-time
|
|
39
|
+
* recordability predicate (and belt-and-braces poisoning in {@link render});
|
|
40
|
+
* tile chunks have no custom-material path to exclude.
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
_supportsRetainedBatches = true;
|
|
44
|
+
_shader;
|
|
45
|
+
_batchSize;
|
|
46
|
+
_instanceData;
|
|
47
|
+
_instanceFloat32;
|
|
48
|
+
_instanceUint32;
|
|
49
|
+
_transformUnitScratch = new Int32Array([transformTextureUnit]);
|
|
50
|
+
_baseTextureUnitScratch = new Int32Array([0]);
|
|
51
|
+
_recordTextures = [null];
|
|
52
|
+
_quadIndex = 0;
|
|
53
|
+
_maxNodeIndex = 0;
|
|
54
|
+
_batchNodeCount = 0;
|
|
55
|
+
_nodeBooked = false;
|
|
56
|
+
_currentBlendMode = null;
|
|
57
|
+
_currentTexture = null;
|
|
58
|
+
_currentView = null;
|
|
59
|
+
_currentViewId = -1;
|
|
60
|
+
_hasWrittenGroup = false;
|
|
61
|
+
_writtenGroupData = /* @__PURE__ */ new Float32Array(9);
|
|
62
|
+
_instanceBuffer = null;
|
|
63
|
+
_vao = null;
|
|
64
|
+
_connection = null;
|
|
65
|
+
constructor(batchSize) {
|
|
66
|
+
super();
|
|
67
|
+
this._batchSize = batchSize;
|
|
68
|
+
this._shader = new Shader(tileVertexSource, tile_chunk_default);
|
|
69
|
+
this._instanceData = /* @__PURE__ */ new ArrayBuffer(batchSize * instanceStrideBytes);
|
|
70
|
+
this._instanceFloat32 = new Float32Array(this._instanceData);
|
|
71
|
+
this._instanceUint32 = new Uint32Array(this._instanceData);
|
|
72
|
+
}
|
|
73
|
+
render(node) {
|
|
74
|
+
const pages = node.pages;
|
|
75
|
+
this._nodeBooked = false;
|
|
76
|
+
if (pages.length === 0) return;
|
|
77
|
+
const backend = this.getBackend();
|
|
78
|
+
const blendMode = node.blendMode;
|
|
79
|
+
const tintRgba = node.tint.toRgba8();
|
|
80
|
+
const command = backend.activeDrawCommand;
|
|
81
|
+
const nodeIndex = command !== null ? command.nodeIndex : backend._pushTransform(node);
|
|
82
|
+
for (const page of pages) this._renderPage(backend, page.texture, page.quads, blendMode, tintRgba, nodeIndex);
|
|
83
|
+
}
|
|
84
|
+
_renderPage(backend, texture, quads, blendMode, tintRgba, nodeIndex) {
|
|
85
|
+
if (quads.length === 0) return;
|
|
86
|
+
const textureChanged = this._currentTexture !== null && texture !== this._currentTexture;
|
|
87
|
+
const blendModeChanged = blendMode !== this._currentBlendMode;
|
|
88
|
+
if (this._quadIndex > 0 && (blendModeChanged || textureChanged || this._quadIndex + quads.length > this._batchSize)) this.flush();
|
|
89
|
+
if (this._currentBlendMode === null || this._currentBlendMode !== blendMode) {
|
|
90
|
+
this._currentBlendMode = blendMode;
|
|
91
|
+
backend.setBlendMode(blendMode);
|
|
92
|
+
}
|
|
93
|
+
if (this._currentTexture !== texture) {
|
|
94
|
+
this._currentTexture = texture;
|
|
95
|
+
backend.bindTexture(texture, 0);
|
|
96
|
+
}
|
|
97
|
+
const flipY = texture.flipY;
|
|
98
|
+
let offset = 0;
|
|
99
|
+
while (offset < quads.length) {
|
|
100
|
+
const remaining = quads.length - offset;
|
|
101
|
+
const runSize = Math.min(remaining, this._batchSize);
|
|
102
|
+
this._writeRun(quads, offset, runSize, flipY, tintRgba, nodeIndex);
|
|
103
|
+
offset += runSize;
|
|
104
|
+
if (offset < quads.length) {
|
|
105
|
+
this.flush();
|
|
106
|
+
this._currentBlendMode = blendMode;
|
|
107
|
+
backend.setBlendMode(blendMode);
|
|
108
|
+
this._currentTexture = texture;
|
|
109
|
+
backend.bindTexture(texture, 0);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
_writeRun(quads, offset, count, flipY, tintRgba, nodeIndex) {
|
|
114
|
+
const f32 = this._instanceFloat32;
|
|
115
|
+
const u32 = this._instanceUint32;
|
|
116
|
+
const baseWord = nodeIndex & TILE_ROW_MASK;
|
|
117
|
+
if (!this._nodeBooked) {
|
|
118
|
+
this._nodeBooked = true;
|
|
119
|
+
this._batchNodeCount++;
|
|
120
|
+
}
|
|
121
|
+
for (let i = 0; i < count; i++) {
|
|
122
|
+
const q = quads[offset + i];
|
|
123
|
+
const idx = this._quadIndex * wordsPerInstance;
|
|
124
|
+
f32[idx + 0] = q.x0;
|
|
125
|
+
f32[idx + 1] = q.y0;
|
|
126
|
+
f32[idx + 2] = q.x1;
|
|
127
|
+
f32[idx + 3] = q.y1;
|
|
128
|
+
const flipX = (q.orient & 1) !== 0;
|
|
129
|
+
const tileFlipY = (q.orient & 2) !== 0;
|
|
130
|
+
const diagonal = (q.orient & 4) !== 0;
|
|
131
|
+
const uA = flipX ? q.u1 : q.u0;
|
|
132
|
+
const uB = flipX ? q.u0 : q.u1;
|
|
133
|
+
let vA = tileFlipY ? q.v1 : q.v0;
|
|
134
|
+
let vB = tileFlipY ? q.v0 : q.v1;
|
|
135
|
+
if (flipY) {
|
|
136
|
+
const swap = vA;
|
|
137
|
+
vA = vB;
|
|
138
|
+
vB = swap;
|
|
139
|
+
}
|
|
140
|
+
const uMin = uA * 65535 & 65535;
|
|
141
|
+
const vMin = vA * 65535 & 65535;
|
|
142
|
+
const uMax = uB * 65535 & 65535;
|
|
143
|
+
const vMax = vB * 65535 & 65535;
|
|
144
|
+
u32[idx + 4] = uMin | vMin << 16;
|
|
145
|
+
u32[idx + 5] = uMax | vMax << 16;
|
|
146
|
+
u32[idx + 6] = tintRgba;
|
|
147
|
+
u32[idx + 7] = (diagonal ? baseWord | TILE_DIAGONAL_BIT : baseWord) >>> 0;
|
|
148
|
+
this._quadIndex++;
|
|
149
|
+
}
|
|
150
|
+
if (nodeIndex > this._maxNodeIndex) this._maxNodeIndex = nodeIndex;
|
|
151
|
+
}
|
|
152
|
+
flush() {
|
|
153
|
+
const backend = this.getBackendOrNull();
|
|
154
|
+
const instanceBuffer = this._instanceBuffer;
|
|
155
|
+
const vao = this._vao;
|
|
156
|
+
if (this._quadIndex === 0 || backend === null || instanceBuffer === null || vao === null) {
|
|
157
|
+
this._quadIndex = 0;
|
|
158
|
+
this._maxNodeIndex = 0;
|
|
159
|
+
this._batchNodeCount = 0;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
this._stageViewUniforms(backend);
|
|
163
|
+
if (this._currentTexture !== null) this._shader.getUniform("u_texture").setValue(this._baseTextureUnitScratch);
|
|
164
|
+
backend.bindTransformBufferTexture(transformTextureUnit, this._maxNodeIndex + 1);
|
|
165
|
+
this._shader.getUniform("u_transforms").setValue(this._transformUnitScratch);
|
|
166
|
+
this._shader.sync();
|
|
167
|
+
backend.bindVertexArrayObject(vao);
|
|
168
|
+
instanceBuffer.upload(this._instanceFloat32, 0, this._quadIndex * wordsPerInstance);
|
|
169
|
+
vao.drawInstanced(4, 0, this._quadIndex, RenderingPrimitives.TriangleStrip);
|
|
170
|
+
backend.stats.batches++;
|
|
171
|
+
backend.stats.drawCalls++;
|
|
172
|
+
if (backend._isRetainedCapturing && this._currentTexture !== null) {
|
|
173
|
+
this._recordTextures[0] = this._currentTexture;
|
|
174
|
+
backend._recordRetainedBatch(this, this._instanceUint32.subarray(0, this._quadIndex * wordsPerInstance), this._quadIndex, this._currentBlendMode ?? BlendModes.Normal, this._recordTextures, 1, null, null, this._batchNodeCount);
|
|
175
|
+
}
|
|
176
|
+
this._quadIndex = 0;
|
|
177
|
+
this._maxNodeIndex = 0;
|
|
178
|
+
this._batchNodeCount = 0;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Stage `u_projection` (live view) and `u_group` (live composed group
|
|
182
|
+
* matrix) on the shader, guarded by cached view/group stamps. Shared by the
|
|
183
|
+
* live flush path and retained-batch replay - replay resolves exactly the
|
|
184
|
+
* same live state a slow-path flush would.
|
|
185
|
+
*/
|
|
186
|
+
_stageViewUniforms(backend) {
|
|
187
|
+
const view = backend.view;
|
|
188
|
+
if (this._currentView !== view || this._currentViewId !== view.updateId) {
|
|
189
|
+
this._currentView = view;
|
|
190
|
+
this._currentViewId = view.updateId;
|
|
191
|
+
this._shader.getUniform("u_projection").setValue(view.getTransform().toArray(false));
|
|
192
|
+
}
|
|
193
|
+
const groupTransform = backend.renderGroupTransform;
|
|
194
|
+
const groupData = groupTransform !== null ? groupTransform.toArray(false) : identityGroupMat3;
|
|
195
|
+
if (!this._hasWrittenGroup || packedGroupChanged(groupData, this._writtenGroupData, 0)) {
|
|
196
|
+
this._shader.getUniform("u_group").setValue(groupData);
|
|
197
|
+
this._writtenGroupData.set(groupData);
|
|
198
|
+
this._hasWrittenGroup = true;
|
|
199
|
+
}
|
|
200
|
+
backend._stageViewportUniform(this._shader);
|
|
201
|
+
}
|
|
202
|
+
/** @internal See {@link WebGl2RetainedBatchReplayer._scanRetainedNodeIndexRange}. */
|
|
203
|
+
_scanRetainedNodeIndexRange(payload, range) {
|
|
204
|
+
const words = payload.bundle.instanceWords;
|
|
205
|
+
const start = payload.byteOffset / Uint32Array.BYTES_PER_ELEMENT;
|
|
206
|
+
for (let i = 0; i < payload.instanceCount; i++) {
|
|
207
|
+
const row = words[start + i * wordsPerInstance + 7] & TILE_ROW_MASK;
|
|
208
|
+
if (row < range.min) range.min = row;
|
|
209
|
+
if (row > range.max) range.max = row;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/** @internal See {@link WebGl2RetainedBatchReplayer._rebaseRetainedNodeIndices} (group-local indices). */
|
|
213
|
+
_rebaseRetainedNodeIndices(payload, base) {
|
|
214
|
+
const words = payload.bundle.instanceWords;
|
|
215
|
+
const start = payload.byteOffset / Uint32Array.BYTES_PER_ELEMENT;
|
|
216
|
+
for (let i = 0; i < payload.instanceCount; i++) {
|
|
217
|
+
const index = start + i * wordsPerInstance + 7;
|
|
218
|
+
const word = words[index];
|
|
219
|
+
words[index] = (word & TILE_DIAGONAL_BIT | (word & TILE_ROW_MASK) - base & TILE_ROW_MASK) >>> 0;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Point the batch VAO's per-instance attributes at the bundle's persistent
|
|
224
|
+
* instance buffer, based at the batch's byte offset (WebGL2 has no
|
|
225
|
+
* baseInstance, hence one small VAO per recorded batch). Same attribute
|
|
226
|
+
* set/locations as the live VAO in {@link onConnect}.
|
|
227
|
+
* @internal
|
|
228
|
+
*/
|
|
229
|
+
_configureRetainedVao(payload) {
|
|
230
|
+
const gl = this.getBackend().context;
|
|
231
|
+
const buffer = payload.bundle.instanceBuffer;
|
|
232
|
+
const vao = payload.vao;
|
|
233
|
+
if (buffer === null || vao === null) throw new Error("WebGl2TileChunkRenderer: retained batch VAO configuration requires an uploaded bundle.");
|
|
234
|
+
const base = payload.byteOffset;
|
|
235
|
+
vao.addAttribute(buffer, this._shader.getAttribute("a_quadBounds"), gl.FLOAT, false, instanceStrideBytes, base + 0, false, 1).addAttribute(buffer, this._shader.getAttribute("a_uvBounds"), gl.UNSIGNED_SHORT, true, instanceStrideBytes, base + 16, false, 1).addAttribute(buffer, this._shader.getAttribute("a_color"), gl.UNSIGNED_BYTE, true, instanceStrideBytes, base + 24, false, 1).addAttribute(buffer, this._shader.getAttribute("a_tileWord"), gl.UNSIGNED_INT, false, instanceStrideBytes, base + 28, true, 1);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Replay one recorded batch: all STATE is resolved live - blend mode via
|
|
239
|
+
* the backend's dedup, `u_projection`/`u_group` from the live view + live
|
|
240
|
+
* composed group matrix (the camera-pan / group-move win), the single
|
|
241
|
+
* tileset texture bound to unit 0 by recorded slot order - and only the
|
|
242
|
+
* DATA is cached: the instance bytes in the bundle buffer (bound through
|
|
243
|
+
* the per-batch VAO) and the group-owned transform texture on the shared
|
|
244
|
+
* transform unit. The backend hook flushed any pending live batch before
|
|
245
|
+
* dispatching here and bumps the stats from the instruction descriptor.
|
|
246
|
+
* @internal
|
|
247
|
+
*/
|
|
248
|
+
_replayRetainedBatch(payload) {
|
|
249
|
+
const backend = this.getBackendOrNull();
|
|
250
|
+
const vao = payload.vao;
|
|
251
|
+
const transformTexture = payload.bundle.transformTexture;
|
|
252
|
+
if (backend === null || vao === null || transformTexture === null) return;
|
|
253
|
+
if (payload.blendMode !== this._currentBlendMode) this._currentBlendMode = payload.blendMode;
|
|
254
|
+
backend.setBlendMode(payload.blendMode);
|
|
255
|
+
this._stageViewUniforms(backend);
|
|
256
|
+
const textures = payload.textures;
|
|
257
|
+
for (let i = 0; i < textures.length; i++) backend.bindTexture(textures[i], i);
|
|
258
|
+
if (textures.length > 0) this._currentTexture = textures[0];
|
|
259
|
+
this._shader.getUniform("u_texture").setValue(this._baseTextureUnitScratch);
|
|
260
|
+
backend.bindTexture(transformTexture, transformTextureUnit);
|
|
261
|
+
this._shader.getUniform("u_transforms").setValue(this._transformUnitScratch);
|
|
262
|
+
this._shader.sync();
|
|
263
|
+
backend.bindVertexArrayObject(vao);
|
|
264
|
+
vao.drawInstanced(4, 0, payload.instanceCount, RenderingPrimitives.TriangleStrip);
|
|
265
|
+
}
|
|
266
|
+
onConnect(backend) {
|
|
267
|
+
const gl = backend.context;
|
|
268
|
+
this._shader.connect(createWebGl2ShaderProgram(gl));
|
|
269
|
+
this._connection = this._createConnection(gl);
|
|
270
|
+
this._instanceBuffer = new WebGl2RenderBuffer(BufferTypes.ArrayBuffer, this._instanceData, BufferUsage.DynamicDraw).connect(this._createBufferRuntime(this._connection));
|
|
271
|
+
this._shader.sync();
|
|
272
|
+
this._vao = new WebGl2VertexArrayObject(RenderingPrimitives.TriangleStrip).addAttribute(this._instanceBuffer, this._shader.getAttribute("a_quadBounds"), gl.FLOAT, false, instanceStrideBytes, 0, false, 1).addAttribute(this._instanceBuffer, this._shader.getAttribute("a_uvBounds"), gl.UNSIGNED_SHORT, true, instanceStrideBytes, 16, false, 1).addAttribute(this._instanceBuffer, this._shader.getAttribute("a_color"), gl.UNSIGNED_BYTE, true, instanceStrideBytes, 24, false, 1).addAttribute(this._instanceBuffer, this._shader.getAttribute("a_tileWord"), gl.UNSIGNED_INT, false, instanceStrideBytes, 28, true, 1).connect(this._createVaoRuntime(this._connection));
|
|
273
|
+
}
|
|
274
|
+
onDisconnect() {
|
|
275
|
+
this._shader.disconnect();
|
|
276
|
+
this._instanceBuffer?.destroy();
|
|
277
|
+
this._instanceBuffer = null;
|
|
278
|
+
this._vao?.destroy();
|
|
279
|
+
this._vao = null;
|
|
280
|
+
this._connection = null;
|
|
281
|
+
this._currentBlendMode = null;
|
|
282
|
+
this._currentTexture = null;
|
|
283
|
+
this._currentView = null;
|
|
284
|
+
this._currentViewId = -1;
|
|
285
|
+
this._hasWrittenGroup = false;
|
|
286
|
+
this._quadIndex = 0;
|
|
287
|
+
this._maxNodeIndex = 0;
|
|
288
|
+
this._batchNodeCount = 0;
|
|
289
|
+
this._nodeBooked = false;
|
|
290
|
+
}
|
|
291
|
+
destroy() {
|
|
292
|
+
this.disconnect();
|
|
293
|
+
this._shader.destroy();
|
|
294
|
+
}
|
|
295
|
+
_createConnection(gl) {
|
|
296
|
+
const vaoHandle = gl.createVertexArray();
|
|
297
|
+
if (vaoHandle === null) throw new Error("WebGl2TileChunkRenderer: could not create vertex array object.");
|
|
298
|
+
return {
|
|
299
|
+
gl,
|
|
300
|
+
buffers: /* @__PURE__ */ new Map(),
|
|
301
|
+
vaoHandle
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
_createBufferRuntime(connection) {
|
|
305
|
+
const handle = connection.gl.createBuffer();
|
|
306
|
+
if (handle === null) throw new Error("WebGl2TileChunkRenderer: could not create render buffer.");
|
|
307
|
+
return {
|
|
308
|
+
bind: (buffer) => {
|
|
309
|
+
connection.gl.bindBuffer(buffer.type, handle);
|
|
310
|
+
},
|
|
311
|
+
upload: (buffer, offset) => {
|
|
312
|
+
const gl = connection.gl;
|
|
313
|
+
const state = connection.buffers.get(buffer);
|
|
314
|
+
gl.bindBuffer(buffer.type, handle);
|
|
315
|
+
if (state && state.dataByteLength >= buffer.uploadByteLength) {
|
|
316
|
+
uploadBufferRange(gl, buffer, offset);
|
|
317
|
+
state.dataByteLength = buffer.uploadByteLength;
|
|
318
|
+
} else {
|
|
319
|
+
uploadBufferStore(gl, buffer);
|
|
320
|
+
connection.buffers.set(buffer, {
|
|
321
|
+
handle,
|
|
322
|
+
dataByteLength: buffer.uploadByteLength
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
destroy: (buffer) => {
|
|
327
|
+
connection.gl.deleteBuffer(handle);
|
|
328
|
+
connection.buffers.delete(buffer);
|
|
329
|
+
buffer.disconnect();
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
_createVaoRuntime(connection) {
|
|
334
|
+
let appliedVersion = -1;
|
|
335
|
+
return {
|
|
336
|
+
bind: (vao) => {
|
|
337
|
+
const gl = connection.gl;
|
|
338
|
+
gl.bindVertexArray(connection.vaoHandle);
|
|
339
|
+
if (appliedVersion !== vao.version) {
|
|
340
|
+
let lastBuffer = null;
|
|
341
|
+
for (const attribute of vao.attributes) {
|
|
342
|
+
if (lastBuffer !== attribute.buffer) {
|
|
343
|
+
attribute.buffer.bind();
|
|
344
|
+
lastBuffer = attribute.buffer;
|
|
345
|
+
}
|
|
346
|
+
if (attribute.integer) gl.vertexAttribIPointer(attribute.location, attribute.size, attribute.type, attribute.stride, attribute.start);
|
|
347
|
+
else gl.vertexAttribPointer(attribute.location, attribute.size, attribute.type, attribute.normalized, attribute.stride, attribute.start);
|
|
348
|
+
gl.enableVertexAttribArray(attribute.location);
|
|
349
|
+
gl.vertexAttribDivisor(attribute.location, attribute.divisor);
|
|
350
|
+
}
|
|
351
|
+
appliedVersion = vao.version;
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
unbind: () => {
|
|
355
|
+
connection.gl.bindVertexArray(null);
|
|
356
|
+
},
|
|
357
|
+
draw: (_vao, size, start, type) => {
|
|
358
|
+
connection.gl.drawArrays(type, start, size);
|
|
359
|
+
},
|
|
360
|
+
drawInstanced: (_vao, count, start, instanceCount, type) => {
|
|
361
|
+
connection.gl.drawArraysInstanced(type, start, count, instanceCount);
|
|
362
|
+
},
|
|
363
|
+
destroy: (vao) => {
|
|
364
|
+
connection.gl.deleteVertexArray(connection.vaoHandle);
|
|
365
|
+
vao.disconnect();
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
};
|
|
337
370
|
|
|
371
|
+
//#endregion
|
|
338
372
|
export { WebGl2TileChunkRenderer };
|
|
339
|
-
//# sourceMappingURL=WebGl2TileChunkRenderer.js.map
|
|
373
|
+
//# sourceMappingURL=WebGl2TileChunkRenderer.js.map
|