@codexo/exojs-tilemap 0.13.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/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/esm/TileChunk.d.ts +136 -0
- package/dist/esm/TileChunk.js +192 -0
- package/dist/esm/TileChunk.js.map +1 -0
- package/dist/esm/TileChunkNode.d.ts +59 -0
- package/dist/esm/TileChunkNode.js +89 -0
- package/dist/esm/TileChunkNode.js.map +1 -0
- package/dist/esm/TileLayer.d.ts +229 -0
- package/dist/esm/TileLayer.js +450 -0
- package/dist/esm/TileLayer.js.map +1 -0
- package/dist/esm/TileLayerNode.d.ts +81 -0
- package/dist/esm/TileLayerNode.js +142 -0
- package/dist/esm/TileLayerNode.js.map +1 -0
- package/dist/esm/TileMap.d.ts +178 -0
- package/dist/esm/TileMap.js +262 -0
- package/dist/esm/TileMap.js.map +1 -0
- package/dist/esm/TileMapBand.d.ts +97 -0
- package/dist/esm/TileMapBand.js +161 -0
- package/dist/esm/TileMapBand.js.map +1 -0
- package/dist/esm/TileMapNode.d.ts +71 -0
- package/dist/esm/TileMapNode.js +113 -0
- package/dist/esm/TileMapNode.js.map +1 -0
- package/dist/esm/TileMapView.d.ts +182 -0
- package/dist/esm/TileMapView.js +342 -0
- package/dist/esm/TileMapView.js.map +1 -0
- package/dist/esm/TileSet.d.ts +99 -0
- package/dist/esm/TileSet.js +157 -0
- package/dist/esm/TileSet.js.map +1 -0
- package/dist/esm/chunkGeometry.d.ts +71 -0
- package/dist/esm/chunkGeometry.js +98 -0
- package/dist/esm/chunkGeometry.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/pixelSnap.d.ts +12 -0
- package/dist/esm/pixelSnap.js +19 -0
- package/dist/esm/pixelSnap.js.map +1 -0
- package/dist/esm/public.d.ts +18 -0
- package/dist/esm/register.d.ts +1 -0
- package/dist/esm/register.js +20 -0
- package/dist/esm/register.js.map +1 -0
- package/dist/esm/tilemapExtension.d.ts +18 -0
- package/dist/esm/tilemapExtension.js +52 -0
- package/dist/esm/tilemapExtension.js.map +1 -0
- package/dist/esm/types.d.ts +139 -0
- package/dist/esm/types.js +121 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.d.ts +39 -0
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.js +339 -0
- package/dist/esm/webgl2/WebGl2TileChunkRenderer.js.map +1 -0
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.d.ts +42 -0
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.js +390 -0
- package/dist/esm/webgpu/WebGpuTileChunkRenderer.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { TileChunk } from './TileChunk.js';
|
|
2
|
+
import { validateNonNegativeInteger, validatePositiveInteger, validateInteger, tileToChunkCoord, tileToLocalInChunk, packTile, unpackTile } from './types.js';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_CHUNK_SIZE = 32;
|
|
5
|
+
/**
|
|
6
|
+
* A generic, format-independent tile layer with chunk-first storage.
|
|
7
|
+
*
|
|
8
|
+
* Tile data is stored in fixed-size {@link TileChunk}s indexed by signed
|
|
9
|
+
* chunk coordinates. For finite maps, only chunks that intersect the layer
|
|
10
|
+
* bounds exist.
|
|
11
|
+
*
|
|
12
|
+
* **Mutation must go through the layer's public APIs only.**
|
|
13
|
+
* `setTileAt` / `clearTileAt` validate coordinates, tileset references,
|
|
14
|
+
* and increment revision counters only when the stored value actually
|
|
15
|
+
* changes. Direct chunk mutation is not supported — the layer owns chunk
|
|
16
|
+
* storage and exposes only a {@link ReadonlyTileChunk} view.
|
|
17
|
+
*
|
|
18
|
+
* The layer is NOT a SceneNode — that integration lives in the future
|
|
19
|
+
* renderer slice.
|
|
20
|
+
*
|
|
21
|
+
* @advanced
|
|
22
|
+
*/
|
|
23
|
+
class TileLayer {
|
|
24
|
+
/** Stable unique ID within the map. */
|
|
25
|
+
id;
|
|
26
|
+
/** Display name (may not be unique). */
|
|
27
|
+
name;
|
|
28
|
+
/** Width in tiles. */
|
|
29
|
+
width;
|
|
30
|
+
/** Height in tiles. */
|
|
31
|
+
height;
|
|
32
|
+
/** Chunk width (tiles). */
|
|
33
|
+
chunkWidth;
|
|
34
|
+
/** Chunk height (tiles). */
|
|
35
|
+
chunkHeight;
|
|
36
|
+
/** Tile width in pixels. */
|
|
37
|
+
tileWidth;
|
|
38
|
+
/** Tile height in pixels. */
|
|
39
|
+
tileHeight;
|
|
40
|
+
/** Pixel width. */
|
|
41
|
+
get pixelWidth() { return this.width * this.tileWidth; }
|
|
42
|
+
/** Pixel height. */
|
|
43
|
+
get pixelHeight() { return this.height * this.tileHeight; }
|
|
44
|
+
/** Visibility flag (mutable). */
|
|
45
|
+
visible;
|
|
46
|
+
/** Opacity 0..1 (mutable). */
|
|
47
|
+
opacity;
|
|
48
|
+
/** Horizontal pixel offset (mutable). */
|
|
49
|
+
offsetX;
|
|
50
|
+
/** Vertical pixel offset (mutable). */
|
|
51
|
+
offsetY;
|
|
52
|
+
/** Immutable layer properties. */
|
|
53
|
+
properties;
|
|
54
|
+
/** The tilesets available to this layer (shared array reference). */
|
|
55
|
+
tilesets;
|
|
56
|
+
/** Chunk storage: chunkKey → mutable TileChunk (internal). */
|
|
57
|
+
_chunks = new Map();
|
|
58
|
+
/**
|
|
59
|
+
* Monotonic layer revision counter.
|
|
60
|
+
* Increments on every cell mutation that actually changes a stored value.
|
|
61
|
+
* Does NOT increment on no-op writes or failed mutations.
|
|
62
|
+
*/
|
|
63
|
+
_revision = 0;
|
|
64
|
+
/** Whether the layer has been destroyed. */
|
|
65
|
+
_destroyed = false;
|
|
66
|
+
/**
|
|
67
|
+
* @throws When dimensions, chunk size, or other options are invalid.
|
|
68
|
+
*/
|
|
69
|
+
constructor(options) {
|
|
70
|
+
validateNonNegativeInteger(options.id, 'layer.id');
|
|
71
|
+
if (!options.name || typeof options.name !== 'string') {
|
|
72
|
+
throw new Error('TileLayer name must be a non-empty string.');
|
|
73
|
+
}
|
|
74
|
+
validatePositiveInteger(options.width, 'layer.width');
|
|
75
|
+
validatePositiveInteger(options.height, 'layer.height');
|
|
76
|
+
validatePositiveInteger(options.tileWidth, 'layer.tileWidth');
|
|
77
|
+
validatePositiveInteger(options.tileHeight, 'layer.tileHeight');
|
|
78
|
+
const chunkWidth = options.chunkWidth ?? DEFAULT_CHUNK_SIZE;
|
|
79
|
+
const chunkHeight = options.chunkHeight ?? DEFAULT_CHUNK_SIZE;
|
|
80
|
+
validatePositiveInteger(chunkWidth, 'chunkWidth');
|
|
81
|
+
validatePositiveInteger(chunkHeight, 'chunkHeight');
|
|
82
|
+
if (!Array.isArray(options.tilesets)) {
|
|
83
|
+
throw new Error('TileLayer tilesets must be an array.');
|
|
84
|
+
}
|
|
85
|
+
const opacity = options.opacity ?? 1;
|
|
86
|
+
if (typeof opacity !== 'number' || opacity < 0 || opacity > 1) {
|
|
87
|
+
throw new Error(`TileLayer opacity must be 0..1 (got ${opacity}).`);
|
|
88
|
+
}
|
|
89
|
+
const offsetX = options.offsetX ?? 0;
|
|
90
|
+
const offsetY = options.offsetY ?? 0;
|
|
91
|
+
if (!Number.isFinite(offsetX) || !Number.isFinite(offsetY)) {
|
|
92
|
+
throw new Error('TileLayer offset must be finite numbers.');
|
|
93
|
+
}
|
|
94
|
+
this.id = options.id;
|
|
95
|
+
this.name = options.name;
|
|
96
|
+
this.width = options.width;
|
|
97
|
+
this.height = options.height;
|
|
98
|
+
this.chunkWidth = chunkWidth;
|
|
99
|
+
this.chunkHeight = chunkHeight;
|
|
100
|
+
this.tileWidth = options.tileWidth;
|
|
101
|
+
this.tileHeight = options.tileHeight;
|
|
102
|
+
this.tilesets = options.tilesets;
|
|
103
|
+
this.visible = options.visible ?? true;
|
|
104
|
+
this.opacity = opacity;
|
|
105
|
+
this.offsetX = offsetX;
|
|
106
|
+
this.offsetY = offsetY;
|
|
107
|
+
this.properties = options.properties
|
|
108
|
+
? Object.freeze({ ...options.properties })
|
|
109
|
+
: Object.freeze({});
|
|
110
|
+
}
|
|
111
|
+
// ── Bounds helpers ────────────────────────────────────────────────────
|
|
112
|
+
/**
|
|
113
|
+
* Check whether a tile coordinate lies within the layer bounds.
|
|
114
|
+
* @advanced
|
|
115
|
+
*/
|
|
116
|
+
inBounds(tx, ty) {
|
|
117
|
+
return tx >= 0 && tx < this.width && ty >= 0 && ty < this.height;
|
|
118
|
+
}
|
|
119
|
+
/** Compute the range of chunk coordinates that intersect this layer. */
|
|
120
|
+
chunkRange() {
|
|
121
|
+
return {
|
|
122
|
+
minCx: 0,
|
|
123
|
+
minCy: 0,
|
|
124
|
+
maxCx: Math.floor((this.width - 1) / this.chunkWidth),
|
|
125
|
+
maxCy: Math.floor((this.height - 1) / this.chunkHeight),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// ── Chunk keying ──────────────────────────────────────────────────────
|
|
129
|
+
_chunkKey(cx, cy) {
|
|
130
|
+
return `${cx},${cy}`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get a readonly view of a chunk by signed chunk coordinates,
|
|
134
|
+
* or undefined if it does not exist (never been touched).
|
|
135
|
+
* @advanced
|
|
136
|
+
*/
|
|
137
|
+
getChunk(cx, cy) {
|
|
138
|
+
return this._chunks.get(this._chunkKey(cx, cy));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get or create a chunk at the given coordinates.
|
|
142
|
+
* For finite layers, creation is only allowed within the valid chunk range.
|
|
143
|
+
*
|
|
144
|
+
* @internal Package-private: used by {@link setTileAt}, {@link fillRect},
|
|
145
|
+
* and future adapter ingest. External users should not allocate
|
|
146
|
+
* chunks — use {@link setTileAt} to populate tiles.
|
|
147
|
+
*/
|
|
148
|
+
_ensureChunk(cx, cy) {
|
|
149
|
+
this._checkDestroyed();
|
|
150
|
+
const key = this._chunkKey(cx, cy);
|
|
151
|
+
let chunk = this._chunks.get(key);
|
|
152
|
+
if (!chunk) {
|
|
153
|
+
const range = this.chunkRange();
|
|
154
|
+
if (cx < range.minCx || cx > range.maxCx || cy < range.minCy || cy > range.maxCy) {
|
|
155
|
+
throw new Error(`Chunk (${cx}, ${cy}) outside layer chunk range ` +
|
|
156
|
+
`[${range.minCx}..${range.maxCx}, ${range.minCy}..${range.maxCy}].`);
|
|
157
|
+
}
|
|
158
|
+
// Compute the actual tile dimensions for this chunk (edge chunks may be smaller).
|
|
159
|
+
const startTx = cx * this.chunkWidth;
|
|
160
|
+
const startTy = cy * this.chunkHeight;
|
|
161
|
+
const cw = Math.min(this.chunkWidth, this.width - startTx);
|
|
162
|
+
const ch = Math.min(this.chunkHeight, this.height - startTy);
|
|
163
|
+
chunk = new TileChunk(cx, cy, cw, ch);
|
|
164
|
+
this._chunks.set(key, chunk);
|
|
165
|
+
}
|
|
166
|
+
return chunk;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Iterate over all loaded chunks in deterministic (cy, cx) ascending order.
|
|
170
|
+
* Returns readonly chunk views — callers cannot mutate storage.
|
|
171
|
+
* @advanced
|
|
172
|
+
*/
|
|
173
|
+
loadedChunks() {
|
|
174
|
+
const entries = [...this._chunks.values()];
|
|
175
|
+
entries.sort((a, b) => a.cy - b.cy || a.cx - b.cx);
|
|
176
|
+
return entries[Symbol.iterator]();
|
|
177
|
+
}
|
|
178
|
+
// ── Tile queries ──────────────────────────────────────────────────────
|
|
179
|
+
/**
|
|
180
|
+
* Get the raw packed tile word at (tx, ty). Returns 0 for empty or out-of-bounds.
|
|
181
|
+
* @advanced
|
|
182
|
+
*/
|
|
183
|
+
getRawTileAt(tx, ty) {
|
|
184
|
+
validateInteger(tx, 'tx');
|
|
185
|
+
validateInteger(ty, 'ty');
|
|
186
|
+
if (!this.inBounds(tx, ty))
|
|
187
|
+
return 0;
|
|
188
|
+
const { cx, cy } = tileToChunkCoord(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
189
|
+
const chunk = this._chunks.get(this._chunkKey(cx, cy));
|
|
190
|
+
if (!chunk)
|
|
191
|
+
return 0;
|
|
192
|
+
const { lx, ly } = tileToLocalInChunk(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
193
|
+
return chunk.getRawAt(lx, ly);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Query a resolved tile at (tx, ty). Returns null for empty or out-of-bounds.
|
|
197
|
+
* @advanced
|
|
198
|
+
*/
|
|
199
|
+
getTileAt(tx, ty) {
|
|
200
|
+
const packed = this.getRawTileAt(tx, ty);
|
|
201
|
+
if (packed === 0)
|
|
202
|
+
return null;
|
|
203
|
+
const decoded = unpackTile(packed);
|
|
204
|
+
if (!decoded)
|
|
205
|
+
return null;
|
|
206
|
+
if (decoded.tilesetIndex >= this.tilesets.length)
|
|
207
|
+
return null;
|
|
208
|
+
const tileset = this.tilesets[decoded.tilesetIndex];
|
|
209
|
+
if (decoded.localTileId >= tileset.tileCount)
|
|
210
|
+
return null;
|
|
211
|
+
return {
|
|
212
|
+
tileset,
|
|
213
|
+
localTileId: decoded.localTileId,
|
|
214
|
+
transform: decoded.transform,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
// ── Mutation ──────────────────────────────────────────────────────────
|
|
218
|
+
/**
|
|
219
|
+
* Validate a tile reference against the layer's tilesets.
|
|
220
|
+
* Returns the packed form or throws.
|
|
221
|
+
*/
|
|
222
|
+
_validateTileRef(tile) {
|
|
223
|
+
if (!tile?.tileset) {
|
|
224
|
+
throw new Error('setTileAt requires a valid ResolvedTile.');
|
|
225
|
+
}
|
|
226
|
+
const tilesetIndex = this.tilesets.indexOf(tile.tileset);
|
|
227
|
+
if (tilesetIndex === -1) {
|
|
228
|
+
throw new Error(`Tileset "${tile.tileset.name}" is not available to layer "${this.name}".`);
|
|
229
|
+
}
|
|
230
|
+
if (tile.localTileId < 0 || tile.localTileId >= tile.tileset.tileCount) {
|
|
231
|
+
throw new Error(`localTileId ${tile.localTileId} out of range for tileset "${tile.tileset.name}" ` +
|
|
232
|
+
`(max ${tile.tileset.tileCount - 1}).`);
|
|
233
|
+
}
|
|
234
|
+
return packTile(tilesetIndex, tile.localTileId, tile.transform);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Set a tile at the given tile coordinates.
|
|
238
|
+
* No-op if the effective value is unchanged.
|
|
239
|
+
* @throws If coordinates are out of bounds or the tile reference is invalid.
|
|
240
|
+
* @advanced
|
|
241
|
+
*/
|
|
242
|
+
setTileAt(tx, ty, tile) {
|
|
243
|
+
this._checkDestroyed();
|
|
244
|
+
validateInteger(tx, 'tx');
|
|
245
|
+
validateInteger(ty, 'ty');
|
|
246
|
+
if (!this.inBounds(tx, ty)) {
|
|
247
|
+
throw new Error(`setTileAt (${tx}, ${ty}) out of bounds [0..${this.width - 1}, 0..${this.height - 1}].`);
|
|
248
|
+
}
|
|
249
|
+
const packed = this._validateTileRef(tile);
|
|
250
|
+
const { cx, cy } = tileToChunkCoord(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
251
|
+
const chunk = this._ensureChunk(cx, cy);
|
|
252
|
+
const { lx, ly } = tileToLocalInChunk(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
253
|
+
if (chunk._setRawAt(lx, ly, packed)) {
|
|
254
|
+
this._revision++;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Clear (erase) the tile at the given coordinates.
|
|
259
|
+
* No-op if the cell is already empty.
|
|
260
|
+
* @throws If coordinates are out of bounds.
|
|
261
|
+
* @advanced
|
|
262
|
+
*/
|
|
263
|
+
clearTileAt(tx, ty) {
|
|
264
|
+
this._checkDestroyed();
|
|
265
|
+
validateInteger(tx, 'tx');
|
|
266
|
+
validateInteger(ty, 'ty');
|
|
267
|
+
if (!this.inBounds(tx, ty)) {
|
|
268
|
+
throw new Error(`clearTileAt (${tx}, ${ty}) out of bounds [0..${this.width - 1}, 0..${this.height - 1}].`);
|
|
269
|
+
}
|
|
270
|
+
const { cx, cy } = tileToChunkCoord(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
271
|
+
const chunk = this._chunks.get(this._chunkKey(cx, cy));
|
|
272
|
+
if (!chunk)
|
|
273
|
+
return; // no chunk = already empty
|
|
274
|
+
const { lx, ly } = tileToLocalInChunk(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
275
|
+
if (chunk._setRawAt(lx, ly, 0)) {
|
|
276
|
+
this._revision++;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// ── Bulk fill ─────────────────────────────────────────────────────────
|
|
280
|
+
/**
|
|
281
|
+
* Fill a rectangular region with a tile.
|
|
282
|
+
* @advanced
|
|
283
|
+
*/
|
|
284
|
+
fillRect(x, y, w, h, tile) {
|
|
285
|
+
this._checkDestroyed();
|
|
286
|
+
const packed = this._validateTileRef(tile);
|
|
287
|
+
let changed = false;
|
|
288
|
+
for (let ty = y; ty < y + h; ty++) {
|
|
289
|
+
for (let tx = x; tx < x + w; tx++) {
|
|
290
|
+
if (!this.inBounds(tx, ty))
|
|
291
|
+
continue;
|
|
292
|
+
const { cx, cy } = tileToChunkCoord(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
293
|
+
const chunk = this._ensureChunk(cx, cy);
|
|
294
|
+
const { lx, ly } = tileToLocalInChunk(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
295
|
+
if (chunk._setRawAt(lx, ly, packed)) {
|
|
296
|
+
changed = true;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (changed)
|
|
301
|
+
this._revision++;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Clear a rectangular region.
|
|
305
|
+
* @advanced
|
|
306
|
+
*/
|
|
307
|
+
clearRect(x, y, w, h) {
|
|
308
|
+
this._checkDestroyed();
|
|
309
|
+
let changed = false;
|
|
310
|
+
for (let ty = y; ty < y + h; ty++) {
|
|
311
|
+
for (let tx = x; tx < x + w; tx++) {
|
|
312
|
+
if (!this.inBounds(tx, ty))
|
|
313
|
+
continue;
|
|
314
|
+
const { cx, cy } = tileToChunkCoord(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
315
|
+
const chunk = this._chunks.get(this._chunkKey(cx, cy));
|
|
316
|
+
if (!chunk)
|
|
317
|
+
continue;
|
|
318
|
+
const { lx, ly } = tileToLocalInChunk(tx, ty, this.chunkWidth, this.chunkHeight);
|
|
319
|
+
if (chunk._setRawAt(lx, ly, 0)) {
|
|
320
|
+
changed = true;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (changed)
|
|
325
|
+
this._revision++;
|
|
326
|
+
}
|
|
327
|
+
// ── Iteration ─────────────────────────────────────────────────────────
|
|
328
|
+
/**
|
|
329
|
+
* Iterate non-empty tiles within a rectangular region in row-major order.
|
|
330
|
+
* Yields (tx, ty, resolvedTile) tuples. Skips empty cells.
|
|
331
|
+
* @advanced
|
|
332
|
+
*/
|
|
333
|
+
*tilesInRect(x, y, w, h) {
|
|
334
|
+
const startCx = Math.floor(x / this.chunkWidth);
|
|
335
|
+
const endCx = Math.floor((x + w - 1) / this.chunkWidth);
|
|
336
|
+
const startCy = Math.floor(y / this.chunkHeight);
|
|
337
|
+
const endCy = Math.floor((y + h - 1) / this.chunkHeight);
|
|
338
|
+
for (let cy = startCy; cy <= endCy; cy++) {
|
|
339
|
+
for (let cx = startCx; cx <= endCx; cx++) {
|
|
340
|
+
const chunk = this._chunks.get(this._chunkKey(cx, cy));
|
|
341
|
+
if (!chunk || chunk.empty)
|
|
342
|
+
continue;
|
|
343
|
+
const chunkStartTx = cx * this.chunkWidth;
|
|
344
|
+
const chunkStartTy = cy * this.chunkHeight;
|
|
345
|
+
const minLx = Math.max(0, x - chunkStartTx);
|
|
346
|
+
const maxLx = Math.min(chunk.width - 1, (x + w - 1) - chunkStartTx);
|
|
347
|
+
const minLy = Math.max(0, y - chunkStartTy);
|
|
348
|
+
const maxLy = Math.min(chunk.height - 1, (y + h - 1) - chunkStartTy);
|
|
349
|
+
for (let ly = minLy; ly <= maxLy; ly++) {
|
|
350
|
+
for (let lx = minLx; lx <= maxLx; lx++) {
|
|
351
|
+
const packed = chunk.getRawAt(lx, ly);
|
|
352
|
+
if (packed === 0)
|
|
353
|
+
continue;
|
|
354
|
+
const decoded = unpackTile(packed);
|
|
355
|
+
if (!decoded)
|
|
356
|
+
continue;
|
|
357
|
+
if (decoded.tilesetIndex >= this.tilesets.length)
|
|
358
|
+
continue;
|
|
359
|
+
const tileset = this.tilesets[decoded.tilesetIndex];
|
|
360
|
+
if (decoded.localTileId >= tileset.tileCount)
|
|
361
|
+
continue;
|
|
362
|
+
yield {
|
|
363
|
+
tx: chunkStartTx + lx,
|
|
364
|
+
ty: chunkStartTy + ly,
|
|
365
|
+
tile: {
|
|
366
|
+
tileset,
|
|
367
|
+
localTileId: decoded.localTileId,
|
|
368
|
+
transform: decoded.transform,
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// ── Coordinate conversion ─────────────────────────────────────────────
|
|
377
|
+
/**
|
|
378
|
+
* Convert a tile coordinate to the pixel position of its top-left corner
|
|
379
|
+
* in the layer's local space.
|
|
380
|
+
* @advanced
|
|
381
|
+
*/
|
|
382
|
+
tileToPixel(tx, ty) {
|
|
383
|
+
return {
|
|
384
|
+
x: tx * this.tileWidth + this.offsetX,
|
|
385
|
+
y: ty * this.tileHeight + this.offsetY,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Convert a local pixel position to the tile coordinate that contains it.
|
|
390
|
+
* Uses `floor`, so a point exactly on a tile boundary maps to the tile.
|
|
391
|
+
* May return coordinates outside layer bounds.
|
|
392
|
+
* @advanced
|
|
393
|
+
*/
|
|
394
|
+
pixelToTile(px, py) {
|
|
395
|
+
return {
|
|
396
|
+
tx: Math.floor((px - this.offsetX) / this.tileWidth),
|
|
397
|
+
ty: Math.floor((py - this.offsetY) / this.tileHeight),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
// ── Revision / lifecycle ──────────────────────────────────────────────
|
|
401
|
+
/**
|
|
402
|
+
* Monotonic layer revision counter.
|
|
403
|
+
* Increments on every cell mutation that changes a stored value.
|
|
404
|
+
* No-op writes and failed mutations do NOT increment.
|
|
405
|
+
* @advanced
|
|
406
|
+
*/
|
|
407
|
+
get revision() {
|
|
408
|
+
return this._revision;
|
|
409
|
+
}
|
|
410
|
+
/** Whether the layer has been destroyed. */
|
|
411
|
+
get destroyed() {
|
|
412
|
+
return this._destroyed;
|
|
413
|
+
}
|
|
414
|
+
_checkDestroyed() {
|
|
415
|
+
if (this._destroyed) {
|
|
416
|
+
throw new Error(`TileLayer "${this.name}" has been destroyed.`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Destroy this layer: clear chunk storage and mark destroyed.
|
|
421
|
+
* Does NOT destroy tileset textures or external resources.
|
|
422
|
+
* Idempotent.
|
|
423
|
+
*/
|
|
424
|
+
destroy() {
|
|
425
|
+
if (this._destroyed)
|
|
426
|
+
return;
|
|
427
|
+
this._destroyed = true;
|
|
428
|
+
this._chunks.clear();
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Total number of non-empty tiles across all chunks.
|
|
432
|
+
* Walk is cheap for dense layers; sparse layers benefit from empty-chunk fast path.
|
|
433
|
+
* @advanced
|
|
434
|
+
*/
|
|
435
|
+
countNonEmptyTiles() {
|
|
436
|
+
let count = 0;
|
|
437
|
+
for (const chunk of this._chunks.values()) {
|
|
438
|
+
for (let ly = 0; ly < chunk.height; ly++) {
|
|
439
|
+
for (let lx = 0; lx < chunk.width; lx++) {
|
|
440
|
+
if (chunk.getRawAt(lx, ly) !== 0)
|
|
441
|
+
count++;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return count;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export { TileLayer };
|
|
450
|
+
//# sourceMappingURL=TileLayer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TileLayer.js","sources":["../../../src/TileLayer.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Rectangle } from '@codexo/exojs';
|
|
2
|
+
import { Container } from '@codexo/exojs';
|
|
3
|
+
import type { PixelSnapMode, RenderPlanBuilder } from '@codexo/exojs/rendering';
|
|
4
|
+
import { TileChunkNode } from './TileChunkNode';
|
|
5
|
+
import type { TileLayer } from './TileLayer';
|
|
6
|
+
/**
|
|
7
|
+
* Options for a {@link TileLayerNode}. All optional; reserved for forward
|
|
8
|
+
* compatibility.
|
|
9
|
+
* @advanced
|
|
10
|
+
*/
|
|
11
|
+
export interface TileLayerNodeOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Whether the layer's chunk nodes participate in per-chunk view culling.
|
|
14
|
+
* Defaults to `true`. Disable only for tiny, always-on-screen layers where
|
|
15
|
+
* the per-chunk bounds test is pure overhead.
|
|
16
|
+
*/
|
|
17
|
+
readonly cullable?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A scene node that renders one generic {@link TileLayer} as a
|
|
21
|
+
* {@link Container} of per-chunk {@link TileChunkNode} drawables.
|
|
22
|
+
*
|
|
23
|
+
* Each non-empty loaded chunk becomes one child positioned at its pixel
|
|
24
|
+
* origin, so the engine's existing per-node culling drops individual chunks and
|
|
25
|
+
* the render-plan optimiser batches them by tileset texture. The layer's pixel
|
|
26
|
+
* `offset` is applied to this node's transform; `visible` and `opacity` are
|
|
27
|
+
* read live from the runtime layer on every frame (no rebuild required).
|
|
28
|
+
*
|
|
29
|
+
* The node references — but never owns — the {@link TileLayer}: destroying it
|
|
30
|
+
* frees its chunk nodes and their cached geometry but leaves the layer, map,
|
|
31
|
+
* and Loader-owned textures intact.
|
|
32
|
+
*
|
|
33
|
+
* Structural changes to the layer (tiles written into previously-empty chunks)
|
|
34
|
+
* are reflected only after {@link TileLayerNode.refresh}; in-place edits to
|
|
35
|
+
* existing chunks are picked up automatically via chunk revisions.
|
|
36
|
+
*
|
|
37
|
+
* @advanced
|
|
38
|
+
*/
|
|
39
|
+
export declare class TileLayerNode extends Container {
|
|
40
|
+
private readonly _layer;
|
|
41
|
+
private readonly _cullChunks;
|
|
42
|
+
private readonly _chunkNodes;
|
|
43
|
+
private _syncedOpacity;
|
|
44
|
+
private _pixelSnapMode;
|
|
45
|
+
constructor(layer: TileLayer, options?: TileLayerNodeOptions);
|
|
46
|
+
/** The runtime layer this node renders. */
|
|
47
|
+
get layer(): TileLayer;
|
|
48
|
+
/**
|
|
49
|
+
* Render-only pixel-snap mode applied to every chunk node in this layer (and
|
|
50
|
+
* to chunks rebuilt later by {@link refresh}). Each chunk's rendered origin is
|
|
51
|
+
* snapped to the active render target's device-pixel grid; because chunk
|
|
52
|
+
* origins are integer multiples of the integer tile pitch from the same layer
|
|
53
|
+
* origin, the whole grid stays exact and adjacent chunks cannot drift apart.
|
|
54
|
+
*
|
|
55
|
+
* Purely visual: tile data, the layer offset, chunk content revisions, and
|
|
56
|
+
* culling bounds are never changed. `'geometry'` and `'position'` both resolve
|
|
57
|
+
* to coherent origin snapping for tile chunks (chunk quads are already on the
|
|
58
|
+
* integer pixel grid by construction). Setting the current value is a no-op;
|
|
59
|
+
* an invalid value throws and leaves the prior mode unchanged.
|
|
60
|
+
*
|
|
61
|
+
* @default 'none'
|
|
62
|
+
* @stable
|
|
63
|
+
*/
|
|
64
|
+
get pixelSnapMode(): PixelSnapMode;
|
|
65
|
+
set pixelSnapMode(mode: PixelSnapMode);
|
|
66
|
+
/**
|
|
67
|
+
* Rebuild the chunk-node children from the layer's current loaded chunks.
|
|
68
|
+
* Call after structural mutation that creates or empties chunks. In-place
|
|
69
|
+
* tile edits to existing chunks do not need a refresh.
|
|
70
|
+
*/
|
|
71
|
+
refresh(): this;
|
|
72
|
+
/** The chunk render nodes owned by this layer node, in build order. @internal */
|
|
73
|
+
get chunkNodes(): readonly TileChunkNode[];
|
|
74
|
+
getLocalBounds(): Rectangle;
|
|
75
|
+
/** @internal */
|
|
76
|
+
protected _collectContent(builder: RenderPlanBuilder): void;
|
|
77
|
+
destroy(): void;
|
|
78
|
+
private _buildChunkNodes;
|
|
79
|
+
/** Propagate the layer's live opacity onto the chunk tints if it changed. */
|
|
80
|
+
private _syncOpacity;
|
|
81
|
+
}
|