@eva/plugin-renderer-tilemap 2.1.0-beta.1 → 2.1.0-beta.11
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 +94 -1
- package/dist/EVA.plugin.renderer.tilemap.js +1630 -46
- package/dist/EVA.plugin.renderer.tilemap.min.js +1 -1
- package/dist/plugin-renderer-tilemap.cjs.js +2381 -67
- package/dist/plugin-renderer-tilemap.cjs.prod.js +1 -1
- package/dist/plugin-renderer-tilemap.d.ts +1010 -23
- package/dist/plugin-renderer-tilemap.esm.js +2343 -68
- package/package.json +7 -3
|
@@ -41,6 +41,10 @@ var _EVA_IIFE_tilemap = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
41
41
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
45
|
+
var e = new Error(message);
|
|
46
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
47
|
+
};
|
|
44
48
|
function __rest(s, e) {
|
|
45
49
|
var t = {};
|
|
46
50
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
|
|
@@ -150,6 +154,8 @@ var _EVA_IIFE_tilemap = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
150
154
|
this.tilesetSpacing = 0;
|
|
151
155
|
this.tilesetMargin = 0;
|
|
152
156
|
this.layers = [];
|
|
157
|
+
this.tilemapRef = '';
|
|
158
|
+
this.layersV2 = undefined;
|
|
153
159
|
}
|
|
154
160
|
init(obj) {
|
|
155
161
|
if (obj) _extends(this, obj);
|
|
@@ -161,78 +167,758 @@ var _EVA_IIFE_tilemap = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
161
167
|
__decorate([type('number'), __metadata("design:type", Number)], Tilemap.prototype, "tileHeight", void 0);
|
|
162
168
|
__decorate([type('number'), __metadata("design:type", Number)], Tilemap.prototype, "tilesetSpacing", void 0);
|
|
163
169
|
__decorate([type('number'), __metadata("design:type", Number)], Tilemap.prototype, "tilesetMargin", void 0);
|
|
170
|
+
__decorate([type('string'), __metadata("design:type", String)], Tilemap.prototype, "tilemapRef", void 0);
|
|
171
|
+
const CHUNK_SIZE$2 = 16;
|
|
172
|
+
const CHUNK_CELL_COUNT = CHUNK_SIZE$2 * CHUNK_SIZE$2;
|
|
173
|
+
function unpackCell(packed) {
|
|
174
|
+
return {
|
|
175
|
+
sourceSlot: packed & 0xff,
|
|
176
|
+
col: packed >>> 8 & 0xff,
|
|
177
|
+
row: packed >>> 16 & 0xff,
|
|
178
|
+
altIdx: packed >>> 24 & 0x1f,
|
|
179
|
+
flipH: (packed >>> 29 & 1) === 1,
|
|
180
|
+
flipV: (packed >>> 30 & 1) === 1,
|
|
181
|
+
transpose: (packed >>> 31 & 1) === 1
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function isEmptyCellValue(packed) {
|
|
185
|
+
return (packed & 0xff) === 0;
|
|
186
|
+
}
|
|
187
|
+
function decodeChunk(blob) {
|
|
188
|
+
const bytes = base64Decode(blob.blob);
|
|
189
|
+
if (bytes.byteLength !== CHUNK_CELL_COUNT * 4) {
|
|
190
|
+
throw new Error(`decodeChunk: expected ${CHUNK_CELL_COUNT * 4} bytes, got ${bytes.byteLength}`);
|
|
191
|
+
}
|
|
192
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
193
|
+
copy.set(bytes);
|
|
194
|
+
return new Int32Array(copy.buffer);
|
|
195
|
+
}
|
|
196
|
+
const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
197
|
+
function base64Decode(s) {
|
|
198
|
+
if (typeof atob === 'function') {
|
|
199
|
+
const bin = atob(s);
|
|
200
|
+
const out = new Uint8Array(bin.length);
|
|
201
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
const bufCtor = globalThis.Buffer;
|
|
205
|
+
if (bufCtor) {
|
|
206
|
+
return new Uint8Array(bufCtor.from(s, 'base64'));
|
|
207
|
+
}
|
|
208
|
+
const clean = s.replace(/[^A-Za-z0-9+/]/g, '');
|
|
209
|
+
const padding = s.endsWith('==') ? 2 : s.endsWith('=') ? 1 : 0;
|
|
210
|
+
const len = Math.floor(clean.length * 3 / 4) - padding;
|
|
211
|
+
const out = new Uint8Array(len);
|
|
212
|
+
let p = 0;
|
|
213
|
+
for (let i = 0; i < clean.length; i += 4) {
|
|
214
|
+
const v0 = BASE64_ALPHABET.indexOf(clean[i]);
|
|
215
|
+
const v1 = BASE64_ALPHABET.indexOf(clean[i + 1]);
|
|
216
|
+
const v2 = clean[i + 2] ? BASE64_ALPHABET.indexOf(clean[i + 2]) : 0;
|
|
217
|
+
const v3 = clean[i + 3] ? BASE64_ALPHABET.indexOf(clean[i + 3]) : 0;
|
|
218
|
+
const w = v0 << 18 | v1 << 12 | v2 << 6 | v3;
|
|
219
|
+
if (p < len) out[p++] = w >> 16 & 0xff;
|
|
220
|
+
if (p < len) out[p++] = w >> 8 & 0xff;
|
|
221
|
+
if (p < len) out[p++] = w & 0xff;
|
|
222
|
+
}
|
|
223
|
+
return out;
|
|
224
|
+
}
|
|
225
|
+
function makeLoadedTileset(raw) {
|
|
226
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
227
|
+
const slotByIdMap = new Map();
|
|
228
|
+
const sourcesBySlot = [];
|
|
229
|
+
const seenByAsset = new Map();
|
|
230
|
+
for (let i = 0; i < raw.sources.length; i++) {
|
|
231
|
+
const src = raw.sources[i];
|
|
232
|
+
sourcesBySlot.push(src);
|
|
233
|
+
slotByIdMap.set(src.id, i + 1);
|
|
234
|
+
if (src.kind === 'atlas' && src.textureAsset) {
|
|
235
|
+
const prev = seenByAsset.get(src.textureAsset);
|
|
236
|
+
if (prev) {
|
|
237
|
+
const sameRegion = prev.regionSize.width === src.regionSize.width && prev.regionSize.height === src.regionSize.height;
|
|
238
|
+
const sameMargins = ((_b = (_a = prev.margins) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0) === ((_d = (_c = src.margins) === null || _c === void 0 ? void 0 : _c.x) !== null && _d !== void 0 ? _d : 0) && ((_f = (_e = prev.margins) === null || _e === void 0 ? void 0 : _e.y) !== null && _f !== void 0 ? _f : 0) === ((_h = (_g = src.margins) === null || _g === void 0 ? void 0 : _g.y) !== null && _h !== void 0 ? _h : 0);
|
|
239
|
+
const sameSep = ((_k = (_j = prev.separation) === null || _j === void 0 ? void 0 : _j.x) !== null && _k !== void 0 ? _k : 0) === ((_m = (_l = src.separation) === null || _l === void 0 ? void 0 : _l.x) !== null && _m !== void 0 ? _m : 0) && ((_p = (_o = prev.separation) === null || _o === void 0 ? void 0 : _o.y) !== null && _p !== void 0 ? _p : 0) === ((_r = (_q = src.separation) === null || _q === void 0 ? void 0 : _q.y) !== null && _r !== void 0 ? _r : 0);
|
|
240
|
+
if (!sameRegion || !sameMargins || !sameSep) {
|
|
241
|
+
if (typeof console !== 'undefined') {
|
|
242
|
+
console.warn(`[Tilemap] Atlas sources '${prev.sourceId}' and '${src.id}' share textureAsset='${src.textureAsset}' ` + `but differ in regionSize/margins/separation. Frame texture cache may collide and render wrong frames. ` + `Use distinct textureAssets per atlas source.`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
seenByAsset.set(src.textureAsset, {
|
|
247
|
+
regionSize: src.regionSize,
|
|
248
|
+
margins: src.margins,
|
|
249
|
+
separation: src.separation,
|
|
250
|
+
sourceId: src.id
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
raw,
|
|
257
|
+
sourcesBySlot,
|
|
258
|
+
slotByIdMap,
|
|
259
|
+
tileWidth: raw.tileSize.width,
|
|
260
|
+
tileHeight: raw.tileSize.height
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
class TileAnimationDriver {
|
|
264
|
+
constructor() {
|
|
265
|
+
this.animations = [];
|
|
266
|
+
this.lastFrameIdxByKey = new Map();
|
|
267
|
+
}
|
|
268
|
+
loadFromTileset(raw) {
|
|
269
|
+
var _a;
|
|
270
|
+
this.animations = [];
|
|
271
|
+
this.lastFrameIdxByKey.clear();
|
|
272
|
+
for (let slotIdx = 0; slotIdx < raw.sources.length; slotIdx++) {
|
|
273
|
+
const src = raw.sources[slotIdx];
|
|
274
|
+
if (src.kind !== 'atlas') continue;
|
|
275
|
+
const slot = slotIdx + 1;
|
|
276
|
+
for (const tile of src.tiles) {
|
|
277
|
+
const anim = tile.animation;
|
|
278
|
+
if (!anim || !anim.frames || anim.frames.length === 0) continue;
|
|
279
|
+
const frames = anim.frames.map(f => {
|
|
280
|
+
var _a;
|
|
281
|
+
return {
|
|
282
|
+
col: f.atlasCoords.col,
|
|
283
|
+
row: f.atlasCoords.row,
|
|
284
|
+
durationMs: anim.stepMs * ((_a = f.durationFactor) !== null && _a !== void 0 ? _a : 1)
|
|
285
|
+
};
|
|
286
|
+
});
|
|
287
|
+
const totalDurationMs = frames.reduce((s, f) => s + f.durationMs, 0);
|
|
288
|
+
if (totalDurationMs <= 0) continue;
|
|
289
|
+
const phaseOffsetMs = anim.phase === 'randomStart' ? deterministicPhaseOffset(slot, tile.atlasCoords.col, tile.atlasCoords.row, totalDurationMs) : 0;
|
|
290
|
+
this.animations.push({
|
|
291
|
+
sourceSlot: slot,
|
|
292
|
+
col: tile.atlasCoords.col,
|
|
293
|
+
row: tile.atlasCoords.row,
|
|
294
|
+
frames,
|
|
295
|
+
totalDurationMs,
|
|
296
|
+
phase: (_a = anim.phase) !== null && _a !== void 0 ? _a : 'sync',
|
|
297
|
+
phaseOffsetMs
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
get animationCount() {
|
|
303
|
+
return this.animations.length;
|
|
304
|
+
}
|
|
305
|
+
isAnimatedSource(slot, col, row) {
|
|
306
|
+
for (const a of this.animations) {
|
|
307
|
+
if (a.sourceSlot === slot && a.col === col && a.row === row) return true;
|
|
308
|
+
}
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
advance(nowMs) {
|
|
312
|
+
const currentFrames = new Map();
|
|
313
|
+
const dirtyKeys = new Set();
|
|
314
|
+
for (const anim of this.animations) {
|
|
315
|
+
const key = `${anim.sourceSlot},${anim.col},${anim.row}`;
|
|
316
|
+
const t = ((nowMs + anim.phaseOffsetMs) % anim.totalDurationMs + anim.totalDurationMs) % anim.totalDurationMs;
|
|
317
|
+
let acc = 0;
|
|
318
|
+
let idx = 0;
|
|
319
|
+
for (; idx < anim.frames.length; idx++) {
|
|
320
|
+
acc += anim.frames[idx].durationMs;
|
|
321
|
+
if (t < acc) break;
|
|
322
|
+
}
|
|
323
|
+
if (idx >= anim.frames.length) idx = anim.frames.length - 1;
|
|
324
|
+
const frame = anim.frames[idx];
|
|
325
|
+
currentFrames.set(key, {
|
|
326
|
+
col: frame.col,
|
|
327
|
+
row: frame.row
|
|
328
|
+
});
|
|
329
|
+
const prev = this.lastFrameIdxByKey.get(key);
|
|
330
|
+
if (prev !== idx) {
|
|
331
|
+
dirtyKeys.add(key);
|
|
332
|
+
this.lastFrameIdxByKey.set(key, idx);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
currentFrames,
|
|
337
|
+
dirtyKeys
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
reset() {
|
|
341
|
+
this.lastFrameIdxByKey.clear();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function deterministicPhaseOffset(slot, col, row, total) {
|
|
345
|
+
let h = slot * 73856093 ^ col * 19349663 ^ row * 83492791;
|
|
346
|
+
h = (h ^ h >>> 13) * 1274126177;
|
|
347
|
+
h = h ^ h >>> 16;
|
|
348
|
+
const t = (h >>> 0) / 0xffffffff;
|
|
349
|
+
return Math.floor(t * total);
|
|
350
|
+
}
|
|
351
|
+
const TILEMAP_PROBE_NAMES = {
|
|
352
|
+
DIRTY_REBUILD_MS: 'tilemap.dirtyRebuild.ms',
|
|
353
|
+
CULL_CHECK_MS: 'tilemap.cullCheck.ms',
|
|
354
|
+
ANIM_TICK_MS: 'tilemap.animTick.ms',
|
|
355
|
+
DRAWCALLS_COUNT: 'tilemap.drawcalls.count',
|
|
356
|
+
DRAWCALLS_BY_ATLAS: 'tilemap.drawcalls.byAtlas',
|
|
357
|
+
PHYSICS_REBAKE_MS: 'tilemap.physicsRebake.ms',
|
|
358
|
+
GPU_UPLOAD_MS: 'tilemap.gpuUpload.ms',
|
|
359
|
+
GPU_UPLOAD_BYTES: 'tilemap.gpuUpload.bytes',
|
|
360
|
+
AUTOTILE_MS: 'tilemap.autotile.ms',
|
|
361
|
+
PATCH_APPLY_MS: 'tilemap.patchApply.ms',
|
|
362
|
+
DIRTY_PENDING: 'tilemap.dirty.pending',
|
|
363
|
+
DIRTY_FRAMES_BEHIND: 'tilemap.dirty.framesBehind',
|
|
364
|
+
BODIES_TOTAL: 'tilemap.bodies.total',
|
|
365
|
+
BODIES_CREATED: 'tilemap.bodies.created',
|
|
366
|
+
BODIES_DESTROYED: 'tilemap.bodies.destroyed',
|
|
367
|
+
STRATEGY_SPRITE_COUNT: 'tilemap.strategy.sprite.count',
|
|
368
|
+
STRATEGY_MESH_COUNT: 'tilemap.strategy.mesh.count',
|
|
369
|
+
STRATEGY_MESH_FALLBACK_COUNT: 'tilemap.strategy.meshFallback.count',
|
|
370
|
+
SCENECOLLECTION_SKIPPED_COUNT: 'tilemap.sceneCollection.skipped.count',
|
|
371
|
+
CULL_HITS_COUNT: 'tilemap.cull.hits',
|
|
372
|
+
MODE_SWITCH_FAILED_COUNT: 'tilemap.modeSwitch.failed',
|
|
373
|
+
ANIM_TICK_ERROR_COUNT: 'tilemap.animTick.error.count',
|
|
374
|
+
CONTEXT_LOST_COUNT: 'tilemap.context.lost'
|
|
375
|
+
};
|
|
376
|
+
function createInMemoryProbeRegistry() {
|
|
377
|
+
const inFlight = new Map();
|
|
378
|
+
const timings = new Map();
|
|
379
|
+
const counts = new Map();
|
|
380
|
+
const gauges = new Map();
|
|
381
|
+
return {
|
|
382
|
+
beginTiming(name) {
|
|
383
|
+
inFlight.set(name, nowMs());
|
|
384
|
+
},
|
|
385
|
+
endTiming(name) {
|
|
386
|
+
const t0 = inFlight.get(name);
|
|
387
|
+
if (t0 === undefined) return;
|
|
388
|
+
inFlight.delete(name);
|
|
389
|
+
const dur = nowMs() - t0;
|
|
390
|
+
if (!timings.has(name)) timings.set(name, []);
|
|
391
|
+
timings.get(name).push(dur);
|
|
392
|
+
},
|
|
393
|
+
count(name, delta = 1) {
|
|
394
|
+
var _a;
|
|
395
|
+
counts.set(name, ((_a = counts.get(name)) !== null && _a !== void 0 ? _a : 0) + delta);
|
|
396
|
+
},
|
|
397
|
+
gauge(name, value) {
|
|
398
|
+
gauges.set(name, value);
|
|
399
|
+
},
|
|
400
|
+
histogram(name, value) {
|
|
401
|
+
this.count(`${name}.histN`);
|
|
402
|
+
this.count(`${name}.histSum`, value);
|
|
403
|
+
},
|
|
404
|
+
timings,
|
|
405
|
+
counts,
|
|
406
|
+
gauges
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function nowMs() {
|
|
410
|
+
if (typeof performance !== 'undefined' && performance.now) return performance.now();
|
|
411
|
+
return 0;
|
|
412
|
+
}
|
|
413
|
+
function adaptGamePerfProbes(game) {
|
|
414
|
+
var _a, _b, _c, _d;
|
|
415
|
+
const p = game === null || game === void 0 ? void 0 : game.perfProbes;
|
|
416
|
+
if (!p) return null;
|
|
417
|
+
return {
|
|
418
|
+
beginTiming: (_a = p.beginTiming) !== null && _a !== void 0 ? _a : () => {},
|
|
419
|
+
endTiming: (_b = p.endTiming) !== null && _b !== void 0 ? _b : () => {},
|
|
420
|
+
count: (_c = p.count) !== null && _c !== void 0 ? _c : () => {},
|
|
421
|
+
gauge: (_d = p.gauge) !== null && _d !== void 0 ? _d : () => {},
|
|
422
|
+
histogram: p.histogram
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function getRequiredTilemapProbes() {
|
|
426
|
+
return Object.values(TILEMAP_PROBE_NAMES);
|
|
427
|
+
}
|
|
428
|
+
function resolveChunkRenderStrategy(ctx) {
|
|
429
|
+
if (ctx.preference && ctx.preference !== 'auto') return ctx.preference;
|
|
430
|
+
if (ctx.atlasesInChunk > 1) return 'sprite';
|
|
431
|
+
if (ctx.nonEmptyCellsInChunk >= 192) return 'mesh';
|
|
432
|
+
return 'sprite';
|
|
433
|
+
}
|
|
434
|
+
function estimateSpriteNodes(nonEmptyCellsInChunk) {
|
|
435
|
+
return 1 + nonEmptyCellsInChunk;
|
|
436
|
+
}
|
|
437
|
+
function buildChunkMesh(_config) {
|
|
438
|
+
const container = new pixi_js.Container();
|
|
439
|
+
container.label = `chunk-mesh-stub-${_config.chunkKey}`;
|
|
440
|
+
return container;
|
|
441
|
+
}
|
|
442
|
+
function isMeshPathAvailable() {
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
function estimateChunkMeshMemoryKB(config) {
|
|
446
|
+
const cellsBytes = 256 * (32 + 12);
|
|
447
|
+
return Math.ceil(cellsBytes / 1024);
|
|
448
|
+
}
|
|
164
449
|
let TilemapSystem = class TilemapSystem extends pluginRenderer.Renderer {
|
|
165
450
|
constructor() {
|
|
166
451
|
super(...arguments);
|
|
167
452
|
this.name = 'Tilemap';
|
|
168
453
|
this.records = {};
|
|
454
|
+
this.animationDrivers = new Map();
|
|
455
|
+
this.probes = null;
|
|
456
|
+
this.isHidden = false;
|
|
457
|
+
this.contextLost = false;
|
|
458
|
+
this.canvasWithCtxListeners = null;
|
|
169
459
|
}
|
|
170
460
|
init() {
|
|
171
461
|
this.renderSystem = this.game.getSystem(pluginRenderer.RendererSystem);
|
|
172
462
|
this.renderSystem.rendererManager.register(this);
|
|
463
|
+
this.probes = adaptGamePerfProbes(this.game);
|
|
464
|
+
this.installVisibilityHandlers();
|
|
465
|
+
}
|
|
466
|
+
installVisibilityHandlers() {
|
|
467
|
+
var _a, _b, _c;
|
|
468
|
+
if (typeof document === 'undefined') return;
|
|
469
|
+
try {
|
|
470
|
+
this.visibilityListener = () => {
|
|
471
|
+
this.isHidden = !!document.hidden;
|
|
472
|
+
};
|
|
473
|
+
document.addEventListener('visibilitychange', this.visibilityListener);
|
|
474
|
+
} catch (_d) {}
|
|
475
|
+
try {
|
|
476
|
+
const canvas = (_c = (_b = (_a = this.renderSystem) === null || _a === void 0 ? void 0 : _a.application) === null || _b === void 0 ? void 0 : _b.canvas) !== null && _c !== void 0 ? _c : null;
|
|
477
|
+
if (canvas && typeof canvas.addEventListener === 'function') {
|
|
478
|
+
this.contextLostListener = () => {
|
|
479
|
+
this.contextLost = true;
|
|
480
|
+
this.probeCount(TILEMAP_PROBE_NAMES.CONTEXT_LOST_COUNT, 1);
|
|
481
|
+
if (typeof console !== 'undefined') {
|
|
482
|
+
console.warn('[Tilemap] WebGL context lost — pausing animation tick');
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
this.contextRestoredListener = () => {
|
|
486
|
+
this.contextLost = false;
|
|
487
|
+
if (typeof console !== 'undefined') {
|
|
488
|
+
console.info('[Tilemap] WebGL context restored — resuming animation tick');
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
canvas.addEventListener('webglcontextlost', this.contextLostListener);
|
|
492
|
+
canvas.addEventListener('webglcontextrestored', this.contextRestoredListener);
|
|
493
|
+
this.canvasWithCtxListeners = canvas;
|
|
494
|
+
}
|
|
495
|
+
} catch (_e) {}
|
|
496
|
+
}
|
|
497
|
+
uninstallVisibilityHandlers() {
|
|
498
|
+
if (typeof document !== 'undefined' && this.visibilityListener) {
|
|
499
|
+
try {
|
|
500
|
+
document.removeEventListener('visibilitychange', this.visibilityListener);
|
|
501
|
+
} catch (_a) {}
|
|
502
|
+
this.visibilityListener = undefined;
|
|
503
|
+
}
|
|
504
|
+
if (this.canvasWithCtxListeners) {
|
|
505
|
+
try {
|
|
506
|
+
if (this.contextLostListener) {
|
|
507
|
+
this.canvasWithCtxListeners.removeEventListener('webglcontextlost', this.contextLostListener);
|
|
508
|
+
}
|
|
509
|
+
if (this.contextRestoredListener) {
|
|
510
|
+
this.canvasWithCtxListeners.removeEventListener('webglcontextrestored', this.contextRestoredListener);
|
|
511
|
+
}
|
|
512
|
+
} catch (_b) {}
|
|
513
|
+
this.contextLostListener = undefined;
|
|
514
|
+
this.contextRestoredListener = undefined;
|
|
515
|
+
this.canvasWithCtxListeners = null;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
attachPerfProbes(probes) {
|
|
519
|
+
this.probes = probes;
|
|
520
|
+
}
|
|
521
|
+
setCullingBoundsHint(gameObjectId, bounds) {
|
|
522
|
+
const record = this.records[gameObjectId];
|
|
523
|
+
if (!record) return;
|
|
524
|
+
if (bounds === null) {
|
|
525
|
+
record.cullingBoundsHint = undefined;
|
|
526
|
+
} else {
|
|
527
|
+
record.cullingBoundsHint = bounds;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
invalidateChunks(gameObjectId, layerId, chunkKeys) {
|
|
531
|
+
const record = this.records[gameObjectId];
|
|
532
|
+
if (!record || chunkKeys.length === 0) return;
|
|
533
|
+
let bucket = record.dirtyChunkKeys.get(layerId);
|
|
534
|
+
if (!bucket) {
|
|
535
|
+
bucket = new Set();
|
|
536
|
+
record.dirtyChunkKeys.set(layerId, bucket);
|
|
537
|
+
}
|
|
538
|
+
for (const k of chunkKeys) bucket.add(k);
|
|
539
|
+
let total = 0;
|
|
540
|
+
for (const s of record.dirtyChunkKeys.values()) total += s.size;
|
|
541
|
+
this.probeGauge(TILEMAP_PROBE_NAMES.DIRTY_PENDING, total);
|
|
542
|
+
if (!record.flushScheduled) {
|
|
543
|
+
record.flushScheduled = true;
|
|
544
|
+
const sched = typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : cb => setTimeout(() => cb(0), 16);
|
|
545
|
+
sched(() => this.flushDirtyChunks(gameObjectId));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
flushDirtyChunks(gameObjectId) {
|
|
549
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
550
|
+
const record = this.records[gameObjectId];
|
|
551
|
+
if (!record) return;
|
|
552
|
+
record.flushScheduled = false;
|
|
553
|
+
if (record.dirtyChunkKeys.size === 0) return;
|
|
554
|
+
if (record.mode !== 'v2' || !record.loadedTileset) {
|
|
555
|
+
record.dirtyChunkKeys.clear();
|
|
556
|
+
this.probeGauge(TILEMAP_PROBE_NAMES.DIRTY_PENDING, 0);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
this.probeCount(TILEMAP_PROBE_NAMES.DIRTY_FRAMES_BEHIND, 1);
|
|
560
|
+
const loaded = record.loadedTileset;
|
|
561
|
+
const animDriver = (_a = this.animationDrivers.get(gameObjectId)) !== null && _a !== void 0 ? _a : null;
|
|
562
|
+
const game = this.game;
|
|
563
|
+
let component = null;
|
|
564
|
+
if (game === null || game === void 0 ? void 0 : game.gameObjects) {
|
|
565
|
+
for (const go of game.gameObjects) {
|
|
566
|
+
if (go.id !== gameObjectId) continue;
|
|
567
|
+
const c = (_b = go.components) === null || _b === void 0 ? void 0 : _b.find(cc => (cc === null || cc === void 0 ? void 0 : cc.name) === 'Tilemap');
|
|
568
|
+
if (c) {
|
|
569
|
+
component = c;
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const cellW = (_d = (_c = component === null || component === void 0 ? void 0 : component.cellSize) === null || _c === void 0 ? void 0 : _c.width) !== null && _d !== void 0 ? _d : loaded.tileWidth;
|
|
575
|
+
const cellH = (_f = (_e = component === null || component === void 0 ? void 0 : component.cellSize) === null || _e === void 0 ? void 0 : _e.height) !== null && _f !== void 0 ? _f : loaded.tileHeight;
|
|
576
|
+
const originX = (_h = (_g = component === null || component === void 0 ? void 0 : component.mapOrigin) === null || _g === void 0 ? void 0 : _g.x) !== null && _h !== void 0 ? _h : 0;
|
|
577
|
+
const originY = (_k = (_j = component === null || component === void 0 ? void 0 : component.mapOrigin) === null || _j === void 0 ? void 0 : _j.y) !== null && _k !== void 0 ? _k : 0;
|
|
578
|
+
this.probeBegin(TILEMAP_PROBE_NAMES.DIRTY_REBUILD_MS);
|
|
579
|
+
try {
|
|
580
|
+
for (const [layerId, chunkKeys] of record.dirtyChunkKeys) {
|
|
581
|
+
const layerEntry = record.layerContainersV2.get(layerId);
|
|
582
|
+
if (!layerEntry) continue;
|
|
583
|
+
const layerSpec = (_m = (_l = component === null || component === void 0 ? void 0 : component.layersV2) === null || _l === void 0 ? void 0 : _l.find(l => l.id === layerId)) !== null && _m !== void 0 ? _m : null;
|
|
584
|
+
if (!layerSpec) continue;
|
|
585
|
+
for (const chunkKey of chunkKeys) {
|
|
586
|
+
const oldChunkContainer = layerEntry.chunkContainers.get(chunkKey);
|
|
587
|
+
if (oldChunkContainer) {
|
|
588
|
+
layerEntry.container.removeChild(oldChunkContainer);
|
|
589
|
+
try {
|
|
590
|
+
oldChunkContainer.destroy({
|
|
591
|
+
children: true
|
|
592
|
+
});
|
|
593
|
+
} catch (_q) {}
|
|
594
|
+
layerEntry.chunkContainers.delete(chunkKey);
|
|
595
|
+
}
|
|
596
|
+
const blob = (_p = (_o = layerSpec.cellData) === null || _o === void 0 ? void 0 : _o.chunks) === null || _p === void 0 ? void 0 : _p[chunkKey];
|
|
597
|
+
if (!blob || blob.nonEmpty <= 0) continue;
|
|
598
|
+
const chunkContainer = new pixi_js.Container();
|
|
599
|
+
chunkContainer.label = `chunk-${chunkKey}`;
|
|
600
|
+
this.populateChunkSprites(chunkContainer, chunkKey, blob, record, loaded, cellW, cellH, originX, originY, layerSpec, animDriver);
|
|
601
|
+
layerEntry.container.addChild(chunkContainer);
|
|
602
|
+
layerEntry.chunkContainers.set(chunkKey, chunkContainer);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
} finally {
|
|
606
|
+
this.probeEnd(TILEMAP_PROBE_NAMES.DIRTY_REBUILD_MS);
|
|
607
|
+
}
|
|
608
|
+
record.dirtyChunkKeys.clear();
|
|
609
|
+
this.probeGauge(TILEMAP_PROBE_NAMES.DIRTY_PENDING, 0);
|
|
610
|
+
this.requestRedraw();
|
|
611
|
+
}
|
|
612
|
+
probeBegin(name) {
|
|
613
|
+
var _a;
|
|
614
|
+
(_a = this.probes) === null || _a === void 0 ? void 0 : _a.beginTiming(name);
|
|
615
|
+
}
|
|
616
|
+
probeEnd(name) {
|
|
617
|
+
var _a;
|
|
618
|
+
(_a = this.probes) === null || _a === void 0 ? void 0 : _a.endTiming(name);
|
|
619
|
+
}
|
|
620
|
+
probeCount(name, delta = 1) {
|
|
621
|
+
var _a;
|
|
622
|
+
(_a = this.probes) === null || _a === void 0 ? void 0 : _a.count(name, delta);
|
|
623
|
+
}
|
|
624
|
+
probeGauge(name, value) {
|
|
625
|
+
var _a;
|
|
626
|
+
(_a = this.probes) === null || _a === void 0 ? void 0 : _a.gauge(name, value);
|
|
173
627
|
}
|
|
174
628
|
rendererUpdate(_gameObject) {}
|
|
629
|
+
update(_frame) {
|
|
630
|
+
if (this.isHidden || this.contextLost) return;
|
|
631
|
+
if (this.animationDrivers.size === 0) return;
|
|
632
|
+
this.probeBegin(TILEMAP_PROBE_NAMES.ANIM_TICK_MS);
|
|
633
|
+
let totalSpritesSwapped = 0;
|
|
634
|
+
try {
|
|
635
|
+
const now = typeof performance !== 'undefined' && performance.now ? performance.now() : 0;
|
|
636
|
+
for (const [gameObjectId, driver] of this.animationDrivers) {
|
|
637
|
+
const record = this.records[gameObjectId];
|
|
638
|
+
if (!record || record.mode !== 'v2') continue;
|
|
639
|
+
try {
|
|
640
|
+
const result = driver.advance(now);
|
|
641
|
+
if (result.dirtyKeys.size === 0) continue;
|
|
642
|
+
const loaded = record.loadedTileset;
|
|
643
|
+
if (!loaded) continue;
|
|
644
|
+
let anyApplied = false;
|
|
645
|
+
for (const animKey of result.dirtyKeys) {
|
|
646
|
+
const sprites = record.animatedSpritesByAnimKey.get(animKey);
|
|
647
|
+
if (!sprites || sprites.length === 0) continue;
|
|
648
|
+
const nextFrame = result.currentFrames.get(animKey);
|
|
649
|
+
if (!nextFrame) continue;
|
|
650
|
+
const parts = animKey.split(',');
|
|
651
|
+
const slot = Number.parseInt(parts[0], 10);
|
|
652
|
+
const newTex = this.getAtlasFrameTexture(record, loaded, {
|
|
653
|
+
sourceSlot: slot,
|
|
654
|
+
col: nextFrame.col,
|
|
655
|
+
row: nextFrame.row
|
|
656
|
+
});
|
|
657
|
+
if (!newTex) continue;
|
|
658
|
+
for (const sp of sprites) {
|
|
659
|
+
sp.texture = newTex;
|
|
660
|
+
anyApplied = true;
|
|
661
|
+
totalSpritesSwapped++;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (anyApplied) this.requestRedraw();
|
|
665
|
+
} catch (e) {
|
|
666
|
+
this.probeCount(TILEMAP_PROBE_NAMES.ANIM_TICK_ERROR_COUNT, 1);
|
|
667
|
+
if (typeof console !== 'undefined') {
|
|
668
|
+
console.error(`[Tilemap] animation tick failed for entity ${gameObjectId}`, e);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
} finally {
|
|
673
|
+
this.probeEnd(TILEMAP_PROBE_NAMES.ANIM_TICK_MS);
|
|
674
|
+
if (totalSpritesSwapped > 0) this.probeCount('tilemap.anim.spritesSwapped', totalSpritesSwapped);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
175
677
|
componentChanged(changed) {
|
|
176
|
-
var _a, _b, _c;
|
|
177
678
|
return __awaiter(this, void 0, void 0, function* () {
|
|
178
679
|
if (changed.componentName !== 'Tilemap') return;
|
|
179
680
|
const component = changed.component;
|
|
180
681
|
const gameObjectId = changed.gameObject.id;
|
|
181
682
|
if (changed.type === eva_js.OBSERVER_TYPE.ADD) {
|
|
683
|
+
yield this.handleAdd(gameObjectId, changed.gameObject, component);
|
|
684
|
+
} else if (changed.type === eva_js.OBSERVER_TYPE.CHANGE) {
|
|
685
|
+
yield this.handleChange(gameObjectId, component, changed);
|
|
686
|
+
} else if (changed.type === eva_js.OBSERVER_TYPE.REMOVE) {
|
|
687
|
+
this.handleRemove(gameObjectId);
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
handleAdd(gameObjectId, gameObject, component) {
|
|
692
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
693
|
+
this.probeBegin(TILEMAP_PROBE_NAMES.DIRTY_REBUILD_MS);
|
|
694
|
+
try {
|
|
182
695
|
const asyncId = this.increaseAsyncId(gameObjectId);
|
|
183
|
-
|
|
184
|
-
if (component.tileset) {
|
|
185
|
-
const {
|
|
186
|
-
instance
|
|
187
|
-
} = yield eva_js.resource.getResource(component.tileset);
|
|
188
|
-
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
189
|
-
if (!instance) {
|
|
190
|
-
console.error(`GameObject:${changed.gameObject.name}'s Tilemap tileset load error`);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
texture = instance;
|
|
194
|
-
}
|
|
195
|
-
if (!texture) return;
|
|
696
|
+
const mode = this.detectMode(component);
|
|
196
697
|
const root = new pixi_js.Container();
|
|
197
|
-
const
|
|
198
|
-
if (
|
|
698
|
+
const containerHost = this.containerManager.getContainer(gameObjectId);
|
|
699
|
+
if (containerHost) containerHost.addChildAt(root, 0);
|
|
199
700
|
const record = {
|
|
200
701
|
root,
|
|
201
702
|
layerContainers: [],
|
|
703
|
+
layerContainersV2: new Map(),
|
|
202
704
|
frameTextures: [],
|
|
203
|
-
|
|
705
|
+
frameTexturesV2: new Map(),
|
|
706
|
+
baseTexture: null,
|
|
707
|
+
atlasTextures: new Map(),
|
|
708
|
+
loadedTileset: null,
|
|
709
|
+
mode,
|
|
710
|
+
animatedSpritesByAnimKey: new Map(),
|
|
711
|
+
sceneCollectionWarnedSourceIds: new Set(),
|
|
712
|
+
dirtyChunkKeys: new Map(),
|
|
713
|
+
flushScheduled: false
|
|
204
714
|
};
|
|
205
715
|
this.records[gameObjectId] = record;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
716
|
+
if (mode === 'v1') {
|
|
717
|
+
yield this.ensureV1Built(record, gameObjectId, gameObject, component, asyncId);
|
|
718
|
+
} else if (mode === 'v2') {
|
|
719
|
+
yield this.ensureV2Built(record, gameObjectId, gameObject, component, asyncId);
|
|
720
|
+
}
|
|
721
|
+
} finally {
|
|
722
|
+
this.probeEnd(TILEMAP_PROBE_NAMES.DIRTY_REBUILD_MS);
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
ensureV1Built(record, gameObjectId, gameObject, component, asyncId) {
|
|
727
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
728
|
+
const texture = yield this.loadV1Texture(component);
|
|
729
|
+
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
730
|
+
if (!texture) {
|
|
731
|
+
if (typeof console !== 'undefined') {
|
|
732
|
+
console.error(`GameObject:${gameObject.name}'s Tilemap tileset load error`);
|
|
733
|
+
}
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
record.baseTexture = texture;
|
|
737
|
+
this.buildLayersV1(record, component);
|
|
738
|
+
this.requestRedraw();
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
ensureV2Built(record, gameObjectId, gameObject, component, asyncId) {
|
|
742
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
743
|
+
const loaded = yield this.loadV2Tileset(component);
|
|
744
|
+
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
745
|
+
if (!loaded) {
|
|
746
|
+
if (typeof console !== 'undefined') {
|
|
747
|
+
console.error(`GameObject:${gameObject.name}'s Tilemap tilemapRef load error`);
|
|
748
|
+
}
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
record.loadedTileset = loaded;
|
|
752
|
+
yield this.loadAtlasTextures(record, loaded);
|
|
753
|
+
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
754
|
+
const animDriver = new TileAnimationDriver();
|
|
755
|
+
animDriver.loadFromTileset(loaded.raw);
|
|
756
|
+
const driverForBuild = animDriver.animationCount > 0 ? animDriver : null;
|
|
757
|
+
if (driverForBuild) this.animationDrivers.set(gameObjectId, driverForBuild);
|
|
758
|
+
this.buildLayersV2(record, component, driverForBuild);
|
|
759
|
+
this.requestRedraw();
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
requestRedraw() {
|
|
763
|
+
var _a;
|
|
764
|
+
try {
|
|
765
|
+
const app = (_a = this.renderSystem) === null || _a === void 0 ? void 0 : _a.application;
|
|
766
|
+
if ((app === null || app === void 0 ? void 0 : app.renderer) && app.stage) {
|
|
767
|
+
app.renderer.render(app.stage);
|
|
768
|
+
}
|
|
769
|
+
} catch (e) {
|
|
770
|
+
if (typeof console !== 'undefined') {
|
|
771
|
+
console.warn('[Tilemap] requestRedraw failed', e);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
handleChange(gameObjectId, component, changed) {
|
|
776
|
+
var _a, _b;
|
|
777
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
778
|
+
const record = this.records[gameObjectId];
|
|
779
|
+
if (!record) return;
|
|
780
|
+
const prop = (_b = (_a = changed.prop) === null || _a === void 0 ? void 0 : _a.prop) === null || _b === void 0 ? void 0 : _b[0];
|
|
781
|
+
this.probeBegin(TILEMAP_PROBE_NAMES.PATCH_APPLY_MS);
|
|
782
|
+
try {
|
|
783
|
+
const newMode = this.detectMode(component);
|
|
784
|
+
if (newMode !== record.mode) {
|
|
785
|
+
const asyncId = this.increaseAsyncId(gameObjectId);
|
|
786
|
+
try {
|
|
787
|
+
if (record.mode === 'v1') {
|
|
788
|
+
this.tearDownChildrenV1(record);
|
|
789
|
+
} else if (record.mode === 'v2') {
|
|
790
|
+
this.tearDownChildrenV2(record);
|
|
791
|
+
this.animationDrivers.delete(gameObjectId);
|
|
792
|
+
}
|
|
793
|
+
record.baseTexture = null;
|
|
794
|
+
record.loadedTileset = null;
|
|
795
|
+
if (newMode === 'v1') {
|
|
796
|
+
const texture = yield this.loadV1Texture(component);
|
|
797
|
+
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
798
|
+
if (!texture) {
|
|
799
|
+
record.mode = 'unknown';
|
|
800
|
+
this.probeCount(TILEMAP_PROBE_NAMES.MODE_SWITCH_FAILED_COUNT, 1);
|
|
801
|
+
if (typeof console !== 'undefined') {
|
|
802
|
+
console.error(`GameObject:${changed.gameObject.name}'s Tilemap mode-switch v→v1 failed (tileset load returned null)`);
|
|
803
|
+
}
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
record.mode = 'v1';
|
|
807
|
+
record.baseTexture = texture;
|
|
808
|
+
this.buildLayersV1(record, component);
|
|
809
|
+
this.requestRedraw();
|
|
810
|
+
} else if (newMode === 'v2') {
|
|
811
|
+
const loaded = yield this.loadV2Tileset(component);
|
|
812
|
+
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
813
|
+
if (!loaded) {
|
|
814
|
+
record.mode = 'unknown';
|
|
815
|
+
this.probeCount(TILEMAP_PROBE_NAMES.MODE_SWITCH_FAILED_COUNT, 1);
|
|
816
|
+
if (typeof console !== 'undefined') {
|
|
817
|
+
console.error(`GameObject:${changed.gameObject.name}'s Tilemap mode-switch v→v2 failed (tilemapRef load returned null)`);
|
|
818
|
+
}
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
yield this.loadAtlasTextures(record, loaded);
|
|
822
|
+
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
823
|
+
record.mode = 'v2';
|
|
824
|
+
record.loadedTileset = loaded;
|
|
825
|
+
const animDriver = new TileAnimationDriver();
|
|
826
|
+
animDriver.loadFromTileset(loaded.raw);
|
|
827
|
+
const driverForBuild = animDriver.animationCount > 0 ? animDriver : null;
|
|
828
|
+
if (driverForBuild) this.animationDrivers.set(gameObjectId, driverForBuild);
|
|
829
|
+
this.buildLayersV2(record, component, driverForBuild);
|
|
830
|
+
this.requestRedraw();
|
|
831
|
+
} else {
|
|
832
|
+
record.mode = 'unknown';
|
|
833
|
+
}
|
|
834
|
+
} catch (e) {
|
|
835
|
+
record.mode = 'unknown';
|
|
836
|
+
this.probeCount(TILEMAP_PROBE_NAMES.MODE_SWITCH_FAILED_COUNT, 1);
|
|
837
|
+
if (typeof console !== 'undefined') {
|
|
838
|
+
console.error('[Tilemap] mode switch threw', e);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
if (prop === 'tileset' && record.mode === 'v1') {
|
|
844
|
+
const asyncId = this.increaseAsyncId(gameObjectId);
|
|
845
|
+
const texture = yield this.loadV1Texture(component);
|
|
846
|
+
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
847
|
+
if (!texture) return;
|
|
848
|
+
this.tearDownChildrenV1(record);
|
|
849
|
+
record.baseTexture = texture;
|
|
850
|
+
this.buildLayersV1(record, component);
|
|
851
|
+
this.requestRedraw();
|
|
852
|
+
} else if (prop === 'tilemapRef' && record.mode === 'v2') {
|
|
211
853
|
const asyncId = this.increaseAsyncId(gameObjectId);
|
|
212
|
-
const
|
|
213
|
-
instance
|
|
214
|
-
} = yield eva_js.resource.getResource(component.tileset);
|
|
854
|
+
const loaded = yield this.loadV2Tileset(component);
|
|
215
855
|
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
216
|
-
if (!
|
|
217
|
-
this.
|
|
218
|
-
record.
|
|
219
|
-
this.
|
|
856
|
+
if (!loaded) return;
|
|
857
|
+
this.tearDownChildrenV2(record);
|
|
858
|
+
record.loadedTileset = loaded;
|
|
859
|
+
yield this.loadAtlasTextures(record, loaded);
|
|
860
|
+
if (!this.validateAsyncId(gameObjectId, asyncId)) return;
|
|
861
|
+
this.animationDrivers.delete(gameObjectId);
|
|
862
|
+
const animDriver = new TileAnimationDriver();
|
|
863
|
+
animDriver.loadFromTileset(loaded.raw);
|
|
864
|
+
const driverForBuild = animDriver.animationCount > 0 ? animDriver : null;
|
|
865
|
+
if (driverForBuild) this.animationDrivers.set(gameObjectId, driverForBuild);
|
|
866
|
+
this.buildLayersV2(record, component, driverForBuild);
|
|
867
|
+
this.requestRedraw();
|
|
868
|
+
} else if (prop === 'layersV2' && record.mode === 'v2') {
|
|
869
|
+
this.tearDownChildrenV2(record);
|
|
870
|
+
this.animationDrivers.delete(gameObjectId);
|
|
871
|
+
record.dirtyChunkKeys.clear();
|
|
872
|
+
const loaded = record.loadedTileset;
|
|
873
|
+
let driverForBuild = null;
|
|
874
|
+
if (loaded) {
|
|
875
|
+
const animDriver = new TileAnimationDriver();
|
|
876
|
+
animDriver.loadFromTileset(loaded.raw);
|
|
877
|
+
if (animDriver.animationCount > 0) {
|
|
878
|
+
driverForBuild = animDriver;
|
|
879
|
+
this.animationDrivers.set(gameObjectId, animDriver);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
this.buildLayersV2(record, component, driverForBuild);
|
|
883
|
+
this.requestRedraw();
|
|
220
884
|
}
|
|
221
|
-
}
|
|
222
|
-
this.
|
|
223
|
-
const record = this.records[gameObjectId];
|
|
224
|
-
if (!record) return;
|
|
225
|
-
this.tearDownChildren(record);
|
|
226
|
-
const container = (_c = this.containerManager) === null || _c === void 0 ? void 0 : _c.getContainer(gameObjectId);
|
|
227
|
-
if (container) container.removeChild(record.root);
|
|
228
|
-
record.root.destroy({
|
|
229
|
-
children: true
|
|
230
|
-
});
|
|
231
|
-
delete this.records[gameObjectId];
|
|
885
|
+
} finally {
|
|
886
|
+
this.probeEnd(TILEMAP_PROBE_NAMES.PATCH_APPLY_MS);
|
|
232
887
|
}
|
|
233
888
|
});
|
|
234
889
|
}
|
|
235
|
-
|
|
890
|
+
handleRemove(gameObjectId) {
|
|
891
|
+
var _a;
|
|
892
|
+
this.increaseAsyncId(gameObjectId);
|
|
893
|
+
const record = this.records[gameObjectId];
|
|
894
|
+
if (!record) return;
|
|
895
|
+
this.tearDownChildrenV1(record);
|
|
896
|
+
this.tearDownChildrenV2(record);
|
|
897
|
+
const containerHost = (_a = this.containerManager) === null || _a === void 0 ? void 0 : _a.getContainer(gameObjectId);
|
|
898
|
+
if (containerHost) containerHost.removeChild(record.root);
|
|
899
|
+
record.root.destroy({
|
|
900
|
+
children: true
|
|
901
|
+
});
|
|
902
|
+
delete this.records[gameObjectId];
|
|
903
|
+
this.animationDrivers.delete(gameObjectId);
|
|
904
|
+
this.probeGauge('tilemap.records.alive', Object.keys(this.records).length);
|
|
905
|
+
}
|
|
906
|
+
detectMode(component) {
|
|
907
|
+
if (component.tilemapRef) return 'v2';
|
|
908
|
+
if (component.tileset) return 'v1';
|
|
909
|
+
return 'unknown';
|
|
910
|
+
}
|
|
911
|
+
loadV1Texture(component) {
|
|
912
|
+
var _a;
|
|
913
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
914
|
+
if (!component.tileset) return null;
|
|
915
|
+
const {
|
|
916
|
+
instance
|
|
917
|
+
} = yield eva_js.resource.getResource(component.tileset);
|
|
918
|
+
return (_a = instance) !== null && _a !== void 0 ? _a : null;
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
tearDownChildrenV1(record) {
|
|
236
922
|
for (const layer of record.layerContainers) {
|
|
237
923
|
record.root.removeChild(layer);
|
|
238
924
|
layer.destroy({
|
|
@@ -247,7 +933,7 @@ var _EVA_IIFE_tilemap = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
247
933
|
}
|
|
248
934
|
record.frameTextures = [];
|
|
249
935
|
}
|
|
250
|
-
|
|
936
|
+
buildLayersV1(record, component) {
|
|
251
937
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
252
938
|
const base = record.baseTexture;
|
|
253
939
|
if (!base) return;
|
|
@@ -260,12 +946,12 @@ var _EVA_IIFE_tilemap = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
260
946
|
const sourceWidth = (_j = (_g = (_f = (_d = (_c = base.orig) === null || _c === void 0 ? void 0 : _c.width) !== null && _d !== void 0 ? _d : (_e = base.source) === null || _e === void 0 ? void 0 : _e.width) !== null && _f !== void 0 ? _f : base.width) !== null && _g !== void 0 ? _g : (_h = base.frame) === null || _h === void 0 ? void 0 : _h.width) !== null && _j !== void 0 ? _j : 0;
|
|
261
947
|
const inferredCols = sourceWidth > 0 ? Math.max(1, Math.floor((sourceWidth - margin + spacing) / (tileW + spacing))) : 1;
|
|
262
948
|
const cols = component.tilesetColumns && component.tilesetColumns > 0 ? component.tilesetColumns : inferredCols;
|
|
263
|
-
const
|
|
949
|
+
const cache = new Map();
|
|
264
950
|
const getFrameTexture = tileId => {
|
|
265
951
|
var _a, _b;
|
|
266
952
|
if (tileId <= 0) return null;
|
|
267
953
|
const idx = tileId - 1;
|
|
268
|
-
const cached =
|
|
954
|
+
const cached = cache.get(idx);
|
|
269
955
|
if (cached) return cached;
|
|
270
956
|
const col = idx % cols;
|
|
271
957
|
const row = Math.floor(idx / cols);
|
|
@@ -277,7 +963,7 @@ var _EVA_IIFE_tilemap = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
277
963
|
source,
|
|
278
964
|
frame: new pixi_js.Rectangle(x, y, tileW, tileH)
|
|
279
965
|
});
|
|
280
|
-
|
|
966
|
+
cache.set(idx, sub);
|
|
281
967
|
record.frameTextures.push(sub);
|
|
282
968
|
return sub;
|
|
283
969
|
} catch (e) {
|
|
@@ -317,12 +1003,274 @@ var _EVA_IIFE_tilemap = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
317
1003
|
record.layerContainers.push(layerContainer);
|
|
318
1004
|
}
|
|
319
1005
|
}
|
|
1006
|
+
loadV2Tileset(component) {
|
|
1007
|
+
var _a, _b;
|
|
1008
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1009
|
+
if (!component.tilemapRef) return null;
|
|
1010
|
+
const res = yield eva_js.resource.getResource(component.tilemapRef);
|
|
1011
|
+
if (!res) return null;
|
|
1012
|
+
const json = (_a = res.instance) !== null && _a !== void 0 ? _a : (_b = res.data) === null || _b === void 0 ? void 0 : _b.json;
|
|
1013
|
+
if (!json || json.kind !== 'tileset') {
|
|
1014
|
+
if (typeof console !== 'undefined') {
|
|
1015
|
+
console.error(`[Tilemap] resource '${component.tilemapRef}' is not a TileSet document`);
|
|
1016
|
+
}
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
return makeLoadedTileset(json);
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
loadAtlasTextures(record, loaded) {
|
|
1023
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1024
|
+
const assetsToLoad = new Set();
|
|
1025
|
+
for (const src of loaded.sourcesBySlot) {
|
|
1026
|
+
if (src.kind === 'atlas' && src.textureAsset) assetsToLoad.add(src.textureAsset);
|
|
1027
|
+
}
|
|
1028
|
+
yield Promise.all(Array.from(assetsToLoad).map(assetId => __awaiter(this, void 0, void 0, function* () {
|
|
1029
|
+
var _a, _b, _c;
|
|
1030
|
+
try {
|
|
1031
|
+
const res = yield eva_js.resource.getResource(assetId);
|
|
1032
|
+
const texture = (_c = (_a = res === null || res === void 0 ? void 0 : res.instance) !== null && _a !== void 0 ? _a : (_b = res === null || res === void 0 ? void 0 : res.data) === null || _b === void 0 ? void 0 : _b.image) !== null && _c !== void 0 ? _c : null;
|
|
1033
|
+
if (texture) record.atlasTextures.set(assetId, texture);
|
|
1034
|
+
} catch (e) {
|
|
1035
|
+
if (typeof console !== 'undefined') {
|
|
1036
|
+
console.warn(`[Tilemap] failed to load atlas asset '${assetId}'`, e);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
})));
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
tearDownChildrenV2(record) {
|
|
1043
|
+
for (const {
|
|
1044
|
+
container
|
|
1045
|
+
} of record.layerContainersV2.values()) {
|
|
1046
|
+
record.root.removeChild(container);
|
|
1047
|
+
container.destroy({
|
|
1048
|
+
children: true
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
record.layerContainersV2.clear();
|
|
1052
|
+
for (const tex of record.frameTexturesV2.values()) {
|
|
1053
|
+
try {
|
|
1054
|
+
tex.destroy(false);
|
|
1055
|
+
} catch (_a) {}
|
|
1056
|
+
}
|
|
1057
|
+
record.frameTexturesV2.clear();
|
|
1058
|
+
record.animatedSpritesByAnimKey.clear();
|
|
1059
|
+
}
|
|
1060
|
+
buildLayersV2(record, component, animDriver) {
|
|
1061
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
1062
|
+
const loaded = record.loadedTileset;
|
|
1063
|
+
if (!loaded) return;
|
|
1064
|
+
const cellW = (_b = (_a = component.cellSize) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : loaded.tileWidth;
|
|
1065
|
+
const cellH = (_d = (_c = component.cellSize) === null || _c === void 0 ? void 0 : _c.height) !== null && _d !== void 0 ? _d : loaded.tileHeight;
|
|
1066
|
+
const originX = (_f = (_e = component.mapOrigin) === null || _e === void 0 ? void 0 : _e.x) !== null && _f !== void 0 ? _f : 0;
|
|
1067
|
+
const originY = (_h = (_g = component.mapOrigin) === null || _g === void 0 ? void 0 : _g.y) !== null && _h !== void 0 ? _h : 0;
|
|
1068
|
+
const layers = (_j = component.layersV2) !== null && _j !== void 0 ? _j : [];
|
|
1069
|
+
const sorted = layers.map((l, i) => ({
|
|
1070
|
+
layer: l,
|
|
1071
|
+
idx: i
|
|
1072
|
+
})).sort((a, b) => {
|
|
1073
|
+
var _a, _b;
|
|
1074
|
+
return ((_a = a.layer.zIndex) !== null && _a !== void 0 ? _a : 0) - ((_b = b.layer.zIndex) !== null && _b !== void 0 ? _b : 0);
|
|
1075
|
+
});
|
|
1076
|
+
for (const {
|
|
1077
|
+
layer
|
|
1078
|
+
} of sorted) {
|
|
1079
|
+
if (layer.enabled === false) continue;
|
|
1080
|
+
const container = new pixi_js.Container();
|
|
1081
|
+
container.label = (_k = layer.name) !== null && _k !== void 0 ? _k : layer.id;
|
|
1082
|
+
container.alpha = (_l = layer.opacity) !== null && _l !== void 0 ? _l : 1;
|
|
1083
|
+
container.visible = layer.visible !== false;
|
|
1084
|
+
const chunkContainers = new Map();
|
|
1085
|
+
this.buildChunksForLayer(record, container, chunkContainers, layer, loaded, cellW, cellH, originX, originY, animDriver);
|
|
1086
|
+
record.root.addChild(container);
|
|
1087
|
+
record.layerContainersV2.set(layer.id, {
|
|
1088
|
+
container,
|
|
1089
|
+
chunkContainers
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
buildChunksForLayer(record, layerContainer, chunkContainers, layer, loaded, cellW, cellH, originX, originY, animDriver) {
|
|
1094
|
+
var _a;
|
|
1095
|
+
const cellData = layer.cellData;
|
|
1096
|
+
if (!cellData || !cellData.chunks) return;
|
|
1097
|
+
const visibleChunks = Object.entries(cellData.chunks).filter(([, b]) => b && b.nonEmpty > 0);
|
|
1098
|
+
const totalChunksVisible = visibleChunks.length;
|
|
1099
|
+
const cullingHint = (_a = record.cullingBoundsHint) !== null && _a !== void 0 ? _a : null;
|
|
1100
|
+
let culledCount = 0;
|
|
1101
|
+
this.probeBegin(TILEMAP_PROBE_NAMES.CULL_CHECK_MS);
|
|
1102
|
+
for (const [chunkKey, blob] of visibleChunks) {
|
|
1103
|
+
if (cullingHint) {
|
|
1104
|
+
const parts = chunkKey.split(',');
|
|
1105
|
+
const ckX = Number.parseInt(parts[0], 10);
|
|
1106
|
+
const ckY = Number.parseInt(parts[1], 10);
|
|
1107
|
+
const chunkMinX = originX + ckX * CHUNK_SIZE$2 * cellW;
|
|
1108
|
+
const chunkMinY = originY + ckY * CHUNK_SIZE$2 * cellH;
|
|
1109
|
+
const chunkMaxX = chunkMinX + CHUNK_SIZE$2 * cellW;
|
|
1110
|
+
const chunkMaxY = chunkMinY + CHUNK_SIZE$2 * cellH;
|
|
1111
|
+
const intersects = chunkMaxX > cullingHint.minX && chunkMinX < cullingHint.maxX && chunkMaxY > cullingHint.minY && chunkMinY < cullingHint.maxY;
|
|
1112
|
+
if (!intersects) {
|
|
1113
|
+
culledCount++;
|
|
1114
|
+
continue;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
const chunkContainer = new pixi_js.Container();
|
|
1118
|
+
chunkContainer.label = `chunk-${chunkKey}`;
|
|
1119
|
+
let atlasesInChunk = 1;
|
|
1120
|
+
let preDecoded = null;
|
|
1121
|
+
try {
|
|
1122
|
+
preDecoded = decodeChunk(blob);
|
|
1123
|
+
const seenAtlasSlots = new Set();
|
|
1124
|
+
for (let i = 0; i < preDecoded.length; i++) {
|
|
1125
|
+
const v = preDecoded[i];
|
|
1126
|
+
if (isEmptyCellValue(v)) continue;
|
|
1127
|
+
const slot = v & 0xff;
|
|
1128
|
+
const src = loaded.sourcesBySlot[slot - 1];
|
|
1129
|
+
if (src && src.kind === 'atlas') seenAtlasSlots.add(slot);
|
|
1130
|
+
}
|
|
1131
|
+
atlasesInChunk = Math.max(1, seenAtlasSlots.size);
|
|
1132
|
+
} catch (_b) {
|
|
1133
|
+
atlasesInChunk = 1;
|
|
1134
|
+
preDecoded = null;
|
|
1135
|
+
}
|
|
1136
|
+
const strategy = resolveChunkRenderStrategy({
|
|
1137
|
+
nonEmptyCellsInChunk: blob.nonEmpty,
|
|
1138
|
+
atlasesInChunk,
|
|
1139
|
+
totalChunksVisible
|
|
1140
|
+
});
|
|
1141
|
+
let dispatched = 'sprite';
|
|
1142
|
+
if (strategy === 'mesh') {
|
|
1143
|
+
if (this.tryBuildMeshChunk(chunkContainer, chunkKey, blob, loaded, cellW, cellH, originX, originY, layer)) {
|
|
1144
|
+
dispatched = 'mesh';
|
|
1145
|
+
} else {
|
|
1146
|
+
dispatched = 'meshFallback';
|
|
1147
|
+
this.probeCount(TILEMAP_PROBE_NAMES.STRATEGY_MESH_FALLBACK_COUNT, 1);
|
|
1148
|
+
this.populateChunkSprites(chunkContainer, chunkKey, blob, record, loaded, cellW, cellH, originX, originY, layer, animDriver, preDecoded);
|
|
1149
|
+
}
|
|
1150
|
+
} else {
|
|
1151
|
+
this.populateChunkSprites(chunkContainer, chunkKey, blob, record, loaded, cellW, cellH, originX, originY, layer, animDriver, preDecoded);
|
|
1152
|
+
}
|
|
1153
|
+
if (dispatched === 'sprite') this.probeCount(TILEMAP_PROBE_NAMES.STRATEGY_SPRITE_COUNT, 1);else if (dispatched === 'mesh') this.probeCount(TILEMAP_PROBE_NAMES.STRATEGY_MESH_COUNT, 1);
|
|
1154
|
+
layerContainer.addChild(chunkContainer);
|
|
1155
|
+
chunkContainers.set(chunkKey, chunkContainer);
|
|
1156
|
+
}
|
|
1157
|
+
this.probeEnd(TILEMAP_PROBE_NAMES.CULL_CHECK_MS);
|
|
1158
|
+
if (culledCount > 0) this.probeCount(TILEMAP_PROBE_NAMES.CULL_HITS_COUNT, culledCount);
|
|
1159
|
+
}
|
|
1160
|
+
tryBuildMeshChunk(chunkContainer, chunkKey, blob, loaded, cellW, cellH, _originX, _originY, _layer) {
|
|
1161
|
+
return false;
|
|
1162
|
+
}
|
|
1163
|
+
populateChunkSprites(chunkContainer, chunkKey, blob, record, loaded, cellW, cellH, originX, originY, layer, animDriver, decoded) {
|
|
1164
|
+
let arr;
|
|
1165
|
+
if (decoded) {
|
|
1166
|
+
arr = decoded;
|
|
1167
|
+
} else {
|
|
1168
|
+
try {
|
|
1169
|
+
arr = decodeChunk(blob);
|
|
1170
|
+
} catch (e) {
|
|
1171
|
+
if (typeof console !== 'undefined') {
|
|
1172
|
+
console.warn(`[Tilemap] chunk decode failed for ${chunkKey}`, e);
|
|
1173
|
+
}
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
const parts = chunkKey.split(',');
|
|
1178
|
+
const ckX = Number.parseInt(parts[0], 10);
|
|
1179
|
+
const ckY = Number.parseInt(parts[1], 10);
|
|
1180
|
+
const baseCx = ckX * CHUNK_SIZE$2;
|
|
1181
|
+
const baseCy = ckY * CHUNK_SIZE$2;
|
|
1182
|
+
const tintNum = parseHexTint(layer.modulate);
|
|
1183
|
+
let spritesInChunk = 0;
|
|
1184
|
+
for (let ly = 0; ly < CHUNK_SIZE$2; ly++) {
|
|
1185
|
+
for (let lx = 0; lx < CHUNK_SIZE$2; lx++) {
|
|
1186
|
+
const packed = arr[ly * CHUNK_SIZE$2 + lx];
|
|
1187
|
+
if (isEmptyCellValue(packed)) continue;
|
|
1188
|
+
const cell = unpackCell(packed);
|
|
1189
|
+
const tex = this.getAtlasFrameTexture(record, loaded, cell);
|
|
1190
|
+
if (!tex) continue;
|
|
1191
|
+
const sprite = new pixi_js.Sprite(tex);
|
|
1192
|
+
spritesInChunk++;
|
|
1193
|
+
sprite.x = originX + (baseCx + lx) * cellW;
|
|
1194
|
+
sprite.y = originY + (baseCy + ly) * cellH;
|
|
1195
|
+
sprite.width = cellW;
|
|
1196
|
+
sprite.height = cellH;
|
|
1197
|
+
if (cell.flipH) sprite.scale.x = -Math.abs(sprite.scale.x || 1);
|
|
1198
|
+
if (cell.flipV) sprite.scale.y = -Math.abs(sprite.scale.y || 1);
|
|
1199
|
+
if (cell.transpose) sprite.rotation = Math.PI / 2;
|
|
1200
|
+
if (cell.flipH) sprite.x += cellW;
|
|
1201
|
+
if (cell.flipV) sprite.y += cellH;
|
|
1202
|
+
if (tintNum != null) sprite.tint = tintNum;
|
|
1203
|
+
chunkContainer.addChild(sprite);
|
|
1204
|
+
if (animDriver && animDriver.isAnimatedSource(cell.sourceSlot, cell.col, cell.row)) {
|
|
1205
|
+
const animKey = `${cell.sourceSlot},${cell.col},${cell.row}`;
|
|
1206
|
+
let bucket = record.animatedSpritesByAnimKey.get(animKey);
|
|
1207
|
+
if (!bucket) {
|
|
1208
|
+
bucket = [];
|
|
1209
|
+
record.animatedSpritesByAnimKey.set(animKey, bucket);
|
|
1210
|
+
}
|
|
1211
|
+
bucket.push(sprite);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
if (spritesInChunk > 0) {
|
|
1216
|
+
this.probeCount(TILEMAP_PROBE_NAMES.DRAWCALLS_COUNT, spritesInChunk);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
getAtlasFrameTexture(record, loaded, cell) {
|
|
1220
|
+
var _a, _b, _c, _d;
|
|
1221
|
+
const src = loaded.sourcesBySlot[cell.sourceSlot - 1];
|
|
1222
|
+
if (!src) return null;
|
|
1223
|
+
if (src.kind !== 'atlas') {
|
|
1224
|
+
if (src.kind === 'sceneCollection') {
|
|
1225
|
+
this.probeCount(TILEMAP_PROBE_NAMES.SCENECOLLECTION_SKIPPED_COUNT, 1);
|
|
1226
|
+
if (!record.sceneCollectionWarnedSourceIds.has(src.id)) {
|
|
1227
|
+
record.sceneCollectionWarnedSourceIds.add(src.id);
|
|
1228
|
+
if (typeof console !== 'undefined') {
|
|
1229
|
+
console.warn(`[Tilemap] sceneCollection source '${src.id}' cells are not yet rendered ` + `(prefab instantiation pending). Skipped cell at slot=${cell.sourceSlot} col=${cell.col} row=${cell.row}.`);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return null;
|
|
1234
|
+
}
|
|
1235
|
+
const atlas = record.atlasTextures.get(src.textureAsset);
|
|
1236
|
+
if (!atlas) return null;
|
|
1237
|
+
const margins = (_a = src.margins) !== null && _a !== void 0 ? _a : {
|
|
1238
|
+
x: 0,
|
|
1239
|
+
y: 0
|
|
1240
|
+
};
|
|
1241
|
+
const sep = (_b = src.separation) !== null && _b !== void 0 ? _b : {
|
|
1242
|
+
x: 0,
|
|
1243
|
+
y: 0
|
|
1244
|
+
};
|
|
1245
|
+
const fw = src.regionSize.width;
|
|
1246
|
+
const fh = src.regionSize.height;
|
|
1247
|
+
const cacheKey = `${src.textureAsset}#${fw}x${fh}|${margins.x},${margins.y}|${sep.x},${sep.y}#${cell.col},${cell.row}`;
|
|
1248
|
+
const cached = record.frameTexturesV2.get(cacheKey);
|
|
1249
|
+
if (cached) return cached;
|
|
1250
|
+
const fx = margins.x + cell.col * (fw + sep.x);
|
|
1251
|
+
const fy = margins.y + cell.row * (fh + sep.y);
|
|
1252
|
+
try {
|
|
1253
|
+
const source = (_d = (_c = atlas.source) !== null && _c !== void 0 ? _c : atlas.baseTexture) !== null && _d !== void 0 ? _d : atlas;
|
|
1254
|
+
const sub = new pixi_js.Texture({
|
|
1255
|
+
source,
|
|
1256
|
+
frame: new pixi_js.Rectangle(fx, fy, fw, fh)
|
|
1257
|
+
});
|
|
1258
|
+
record.frameTexturesV2.set(cacheKey, sub);
|
|
1259
|
+
return sub;
|
|
1260
|
+
} catch (e) {
|
|
1261
|
+
if (typeof console !== 'undefined') {
|
|
1262
|
+
console.warn(`[Tilemap] frame slice failed for ${cacheKey}`, e);
|
|
1263
|
+
}
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
320
1267
|
destroy() {
|
|
321
1268
|
var _a;
|
|
322
1269
|
for (const key in this.records) {
|
|
323
1270
|
const id = parseInt(key);
|
|
324
1271
|
const record = this.records[id];
|
|
325
|
-
this.
|
|
1272
|
+
this.tearDownChildrenV1(record);
|
|
1273
|
+
this.tearDownChildrenV2(record);
|
|
326
1274
|
const container = (_a = this.containerManager) === null || _a === void 0 ? void 0 : _a.getContainer(id);
|
|
327
1275
|
if (container) container.removeChild(record.root);
|
|
328
1276
|
record.root.destroy({
|
|
@@ -330,6 +1278,7 @@ var _EVA_IIFE_tilemap = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
330
1278
|
});
|
|
331
1279
|
delete this.records[id];
|
|
332
1280
|
}
|
|
1281
|
+
this.uninstallVisibilityHandlers();
|
|
333
1282
|
}
|
|
334
1283
|
};
|
|
335
1284
|
TilemapSystem.systemName = 'Tilemap';
|
|
@@ -337,11 +1286,646 @@ var _EVA_IIFE_tilemap = function (exports, eva_js, pluginRenderer, pixi_js) {
|
|
|
337
1286
|
Tilemap: [{
|
|
338
1287
|
prop: ['tileset'],
|
|
339
1288
|
deep: false
|
|
1289
|
+
}, {
|
|
1290
|
+
prop: ['tilemapRef'],
|
|
1291
|
+
deep: false
|
|
1292
|
+
}, {
|
|
1293
|
+
prop: ['layersV2'],
|
|
1294
|
+
deep: false
|
|
340
1295
|
}]
|
|
341
1296
|
})], TilemapSystem);
|
|
342
1297
|
var TilemapSystem$1 = TilemapSystem;
|
|
1298
|
+
function parseHexTint(modulate) {
|
|
1299
|
+
if (!modulate) return undefined;
|
|
1300
|
+
const m = /^#?([0-9a-fA-F]{6})(?:[0-9a-fA-F]{2})?$/.exec(modulate);
|
|
1301
|
+
if (!m) return undefined;
|
|
1302
|
+
return parseInt(m[1], 16);
|
|
1303
|
+
}
|
|
1304
|
+
const NEIGHBOR_DIRECTIONS = ["topLeft", "top", "topRight", "right", "bottomRight", "bottom", "bottomLeft", "left"];
|
|
1305
|
+
const NO_NEIGHBOR = 0xf;
|
|
1306
|
+
const MAX_TERRAIN_ID = 14;
|
|
1307
|
+
function packBits(values) {
|
|
1308
|
+
var _a;
|
|
1309
|
+
let key = 0;
|
|
1310
|
+
for (let i = 0; i < 8; i++) {
|
|
1311
|
+
const v = ((_a = values[i]) !== null && _a !== void 0 ? _a : NO_NEIGHBOR) & 0xf;
|
|
1312
|
+
key |= v << i * 4;
|
|
1313
|
+
}
|
|
1314
|
+
return key >>> 0;
|
|
1315
|
+
}
|
|
1316
|
+
function readNibble(key, slot) {
|
|
1317
|
+
return key >>> slot * 4 & 0xf;
|
|
1318
|
+
}
|
|
1319
|
+
class PeeringBitIndex {
|
|
1320
|
+
constructor(terrainSetIndex) {
|
|
1321
|
+
this.terrainSetIndex = terrainSetIndex;
|
|
1322
|
+
this.exact = new Map();
|
|
1323
|
+
this.wildcards = [];
|
|
1324
|
+
}
|
|
1325
|
+
add(candidate, bits) {
|
|
1326
|
+
var _a;
|
|
1327
|
+
const present = [];
|
|
1328
|
+
const mask = [];
|
|
1329
|
+
for (let i = 0; i < 8; i++) {
|
|
1330
|
+
const dir = NEIGHBOR_DIRECTIONS[i];
|
|
1331
|
+
const v = bits === null || bits === void 0 ? void 0 : bits[dir];
|
|
1332
|
+
if (v === undefined) {
|
|
1333
|
+
present.push(NO_NEIGHBOR);
|
|
1334
|
+
mask.push(0);
|
|
1335
|
+
} else {
|
|
1336
|
+
present.push(v & 0xf);
|
|
1337
|
+
mask.push(0xf);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
const key = packBits(present);
|
|
1341
|
+
const maskKey = packBits(mask);
|
|
1342
|
+
const isFullExact = mask.every(m => m === 0xf);
|
|
1343
|
+
if (isFullExact) {
|
|
1344
|
+
const list = (_a = this.exact.get(key)) !== null && _a !== void 0 ? _a : [];
|
|
1345
|
+
list.push(candidate);
|
|
1346
|
+
this.exact.set(key, list);
|
|
1347
|
+
} else {
|
|
1348
|
+
this.wildcards.push({
|
|
1349
|
+
key,
|
|
1350
|
+
mask: maskKey,
|
|
1351
|
+
candidate
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
match(actual) {
|
|
1356
|
+
const key = packBits(actual);
|
|
1357
|
+
const exact = this.exact.get(key);
|
|
1358
|
+
if (exact && exact.length > 0) {
|
|
1359
|
+
return {
|
|
1360
|
+
candidates: exact.slice(),
|
|
1361
|
+
exactMatch: true,
|
|
1362
|
+
bestHammingDistance: 0
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1365
|
+
let bestDist = Infinity;
|
|
1366
|
+
let pool = [];
|
|
1367
|
+
for (const {
|
|
1368
|
+
key: candKey,
|
|
1369
|
+
mask,
|
|
1370
|
+
candidate
|
|
1371
|
+
} of this.wildcards) {
|
|
1372
|
+
let dist = 0;
|
|
1373
|
+
let viable = true;
|
|
1374
|
+
for (let slot = 0; slot < 8; slot++) {
|
|
1375
|
+
const m = readNibble(mask, slot);
|
|
1376
|
+
if (m === 0) continue;
|
|
1377
|
+
const a = readNibble(key, slot);
|
|
1378
|
+
const b = readNibble(candKey, slot);
|
|
1379
|
+
if (a !== b) {
|
|
1380
|
+
dist++;
|
|
1381
|
+
if (dist > bestDist) {
|
|
1382
|
+
viable = false;
|
|
1383
|
+
break;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
if (!viable) continue;
|
|
1388
|
+
if (dist < bestDist) {
|
|
1389
|
+
bestDist = dist;
|
|
1390
|
+
pool = [candidate];
|
|
1391
|
+
} else if (dist === bestDist) {
|
|
1392
|
+
pool.push(candidate);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
return {
|
|
1396
|
+
candidates: pool,
|
|
1397
|
+
exactMatch: false,
|
|
1398
|
+
bestHammingDistance: bestDist === Infinity ? -1 : bestDist
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
get exactBucketCount() {
|
|
1402
|
+
return this.exact.size;
|
|
1403
|
+
}
|
|
1404
|
+
get wildcardCount() {
|
|
1405
|
+
return this.wildcards.length;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
function pickAutotileCandidate(candidates, rng) {
|
|
1409
|
+
if (candidates.length === 0) return null;
|
|
1410
|
+
if (candidates.length === 1) return candidates[0];
|
|
1411
|
+
const totalProb = candidates.reduce((sum, c) => sum + (c.probability > 0 ? c.probability : 1), 0);
|
|
1412
|
+
const t = rng() * totalProb;
|
|
1413
|
+
let acc = 0;
|
|
1414
|
+
for (const c of candidates) {
|
|
1415
|
+
acc += c.probability > 0 ? c.probability : 1;
|
|
1416
|
+
if (t < acc) return c;
|
|
1417
|
+
}
|
|
1418
|
+
return candidates[candidates.length - 1];
|
|
1419
|
+
}
|
|
1420
|
+
const TILE_VERT_SHADER = `#version 300 es
|
|
1421
|
+
precision highp float;
|
|
1422
|
+
|
|
1423
|
+
in vec2 aPosition;
|
|
1424
|
+
in vec2 aTexCoord;
|
|
1425
|
+
in vec4 aFlags;
|
|
1426
|
+
|
|
1427
|
+
uniform mat3 uProjectionMatrix;
|
|
1428
|
+
uniform mat3 uWorldTransformMatrix;
|
|
1429
|
+
|
|
1430
|
+
out vec2 vTexCoord;
|
|
1431
|
+
|
|
1432
|
+
void main() {
|
|
1433
|
+
vec3 worldPos = uWorldTransformMatrix * vec3(aPosition, 1.0);
|
|
1434
|
+
gl_Position = vec4((uProjectionMatrix * worldPos).xy, 0.0, 1.0);
|
|
1435
|
+
|
|
1436
|
+
vec2 uv = aTexCoord;
|
|
1437
|
+
if (aFlags.x > 0.5) uv.x = 1.0 - uv.x;
|
|
1438
|
+
if (aFlags.y > 0.5) uv.y = 1.0 - uv.y;
|
|
1439
|
+
if (aFlags.z > 0.5) { float t = uv.x; uv.x = uv.y; uv.y = t; }
|
|
1440
|
+
vTexCoord = uv;
|
|
1441
|
+
}
|
|
1442
|
+
`;
|
|
1443
|
+
const TILE_FRAG_SHADER = `#version 300 es
|
|
1444
|
+
precision highp float;
|
|
1445
|
+
|
|
1446
|
+
in vec2 vTexCoord;
|
|
1447
|
+
out vec4 fragColor;
|
|
1448
|
+
|
|
1449
|
+
uniform sampler2D uTexture;
|
|
1450
|
+
uniform vec4 uModulate;
|
|
1451
|
+
|
|
1452
|
+
void main() {
|
|
1453
|
+
vec4 sampled = texture(uTexture, vTexCoord);
|
|
1454
|
+
fragColor = sampled * uModulate;
|
|
1455
|
+
if (fragColor.a < 0.01) discard;
|
|
1456
|
+
}
|
|
1457
|
+
`;
|
|
1458
|
+
const TILE_SHADER_SOURCES = {
|
|
1459
|
+
vertex: TILE_VERT_SHADER,
|
|
1460
|
+
fragment: TILE_FRAG_SHADER
|
|
1461
|
+
};
|
|
1462
|
+
const CHUNK_SIZE$1 = 16;
|
|
1463
|
+
const VERTS_PER_QUAD = 4;
|
|
1464
|
+
const FLOATS_PER_VERT = 8;
|
|
1465
|
+
function buildChunkGeometry(args) {
|
|
1466
|
+
const {
|
|
1467
|
+
cells,
|
|
1468
|
+
chunkWorldX,
|
|
1469
|
+
chunkWorldY,
|
|
1470
|
+
cellWidth,
|
|
1471
|
+
cellHeight,
|
|
1472
|
+
atlasInfo
|
|
1473
|
+
} = args;
|
|
1474
|
+
if (cells.length !== CHUNK_SIZE$1 * CHUNK_SIZE$1) {
|
|
1475
|
+
throw new Error(`buildChunkGeometry: cells length ${cells.length} != ${CHUNK_SIZE$1 * CHUNK_SIZE$1}`);
|
|
1476
|
+
}
|
|
1477
|
+
let nonEmpty = 0;
|
|
1478
|
+
for (let i = 0; i < cells.length; i++) if (!isEmptyCellValue(cells[i])) nonEmpty++;
|
|
1479
|
+
const vertexData = new Float32Array(nonEmpty * VERTS_PER_QUAD * FLOATS_PER_VERT);
|
|
1480
|
+
const indices = new Uint16Array(nonEmpty * 6);
|
|
1481
|
+
let v = 0;
|
|
1482
|
+
let i = 0;
|
|
1483
|
+
let quadIdx = 0;
|
|
1484
|
+
for (let ly = 0; ly < CHUNK_SIZE$1; ly++) {
|
|
1485
|
+
for (let lx = 0; lx < CHUNK_SIZE$1; lx++) {
|
|
1486
|
+
const packed = cells[ly * CHUNK_SIZE$1 + lx];
|
|
1487
|
+
if (isEmptyCellValue(packed)) continue;
|
|
1488
|
+
const cell = unpackCell(packed);
|
|
1489
|
+
const x0 = chunkWorldX + lx * cellWidth;
|
|
1490
|
+
const y0 = chunkWorldY + ly * cellHeight;
|
|
1491
|
+
const x1 = x0 + cellWidth;
|
|
1492
|
+
const y1 = y0 + cellHeight;
|
|
1493
|
+
const u0 = (atlasInfo.margins.x + cell.col * (atlasInfo.regionWidth + atlasInfo.separation.x)) / atlasInfo.textureWidth;
|
|
1494
|
+
const u1 = u0 + atlasInfo.regionWidth / atlasInfo.textureWidth;
|
|
1495
|
+
const v0 = (atlasInfo.margins.y + cell.row * (atlasInfo.regionHeight + atlasInfo.separation.y)) / atlasInfo.textureHeight;
|
|
1496
|
+
const v1 = v0 + atlasInfo.regionHeight / atlasInfo.textureHeight;
|
|
1497
|
+
const fh = cell.flipH ? 1 : 0;
|
|
1498
|
+
const fv = cell.flipV ? 1 : 0;
|
|
1499
|
+
const tr = cell.transpose ? 1 : 0;
|
|
1500
|
+
const animPhase = 0;
|
|
1501
|
+
const writeVert = (x, y, u, vv) => {
|
|
1502
|
+
vertexData[v++] = x;
|
|
1503
|
+
vertexData[v++] = y;
|
|
1504
|
+
vertexData[v++] = u;
|
|
1505
|
+
vertexData[v++] = vv;
|
|
1506
|
+
vertexData[v++] = fh;
|
|
1507
|
+
vertexData[v++] = fv;
|
|
1508
|
+
vertexData[v++] = tr;
|
|
1509
|
+
vertexData[v++] = animPhase;
|
|
1510
|
+
};
|
|
1511
|
+
writeVert(x0, y0, u0, v0);
|
|
1512
|
+
writeVert(x1, y0, u1, v0);
|
|
1513
|
+
writeVert(x1, y1, u1, v1);
|
|
1514
|
+
writeVert(x0, y1, u0, v1);
|
|
1515
|
+
const base = quadIdx * VERTS_PER_QUAD;
|
|
1516
|
+
indices[i++] = base;
|
|
1517
|
+
indices[i++] = base + 1;
|
|
1518
|
+
indices[i++] = base + 2;
|
|
1519
|
+
indices[i++] = base;
|
|
1520
|
+
indices[i++] = base + 2;
|
|
1521
|
+
indices[i++] = base + 3;
|
|
1522
|
+
quadIdx++;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
return {
|
|
1526
|
+
vertexData,
|
|
1527
|
+
indices,
|
|
1528
|
+
quadCount: quadIdx
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
function diffTilesetForChunkRebuild(prev, next) {
|
|
1532
|
+
const prevSources = new Map();
|
|
1533
|
+
const nextSources = new Map();
|
|
1534
|
+
for (const s of prev.sources) prevSources.set(s.id, s);
|
|
1535
|
+
for (const s of next.sources) nextSources.set(s.id, s);
|
|
1536
|
+
const addedSources = [];
|
|
1537
|
+
const removedSourceIds = [];
|
|
1538
|
+
const changedSourceIds = [];
|
|
1539
|
+
for (const id of nextSources.keys()) {
|
|
1540
|
+
if (!prevSources.has(id)) addedSources.push(nextSources.get(id));else if (JSON.stringify(prevSources.get(id)) !== JSON.stringify(nextSources.get(id))) changedSourceIds.push(id);
|
|
1541
|
+
}
|
|
1542
|
+
for (const id of prevSources.keys()) {
|
|
1543
|
+
if (!nextSources.has(id)) removedSourceIds.push(id);
|
|
1544
|
+
}
|
|
1545
|
+
const addedTilesByKey = new Set();
|
|
1546
|
+
const removedTilesByKey = new Set();
|
|
1547
|
+
const changedTilesByKey = new Set();
|
|
1548
|
+
const indexTiles = src => {
|
|
1549
|
+
const m = new Map();
|
|
1550
|
+
if (src.kind !== "atlas") return m;
|
|
1551
|
+
for (const t of src.tiles) m.set(`${src.id}/${t.atlasCoords.col},${t.atlasCoords.row}`, t);
|
|
1552
|
+
return m;
|
|
1553
|
+
};
|
|
1554
|
+
for (const [id, src] of nextSources) {
|
|
1555
|
+
const prevSrc = prevSources.get(id);
|
|
1556
|
+
if (!prevSrc) {
|
|
1557
|
+
const tiles = indexTiles(src);
|
|
1558
|
+
for (const k of tiles.keys()) addedTilesByKey.add(k);
|
|
1559
|
+
continue;
|
|
1560
|
+
}
|
|
1561
|
+
const a = indexTiles(prevSrc);
|
|
1562
|
+
const b = indexTiles(src);
|
|
1563
|
+
for (const k of b.keys()) {
|
|
1564
|
+
if (!a.has(k)) addedTilesByKey.add(k);else if (JSON.stringify(a.get(k)) !== JSON.stringify(b.get(k))) changedTilesByKey.add(k);
|
|
1565
|
+
}
|
|
1566
|
+
for (const k of a.keys()) {
|
|
1567
|
+
if (!b.has(k)) removedTilesByKey.add(k);
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
for (const id of removedSourceIds) {
|
|
1571
|
+
const a = indexTiles(prevSources.get(id));
|
|
1572
|
+
for (const k of a.keys()) removedTilesByKey.add(k);
|
|
1573
|
+
}
|
|
1574
|
+
return {
|
|
1575
|
+
addedSources,
|
|
1576
|
+
removedSourceIds,
|
|
1577
|
+
changedSourceIds,
|
|
1578
|
+
addedTilesByKey,
|
|
1579
|
+
removedTilesByKey,
|
|
1580
|
+
changedTilesByKey
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
const diffTilesetDocuments = diffTilesetForChunkRebuild;
|
|
1584
|
+
function computeAffectedChunks(chunks, sourceIdBySlot, diff) {
|
|
1585
|
+
const dirty = new Set();
|
|
1586
|
+
const cellKeyMatches = (cellSlot, col, row) => {
|
|
1587
|
+
const sourceId = sourceIdBySlot.get(cellSlot);
|
|
1588
|
+
if (!sourceId) return false;
|
|
1589
|
+
const k = `${sourceId}/${col},${row}`;
|
|
1590
|
+
return diff.changedTilesByKey.has(k) || diff.removedTilesByKey.has(k) || diff.addedTilesByKey.has(k);
|
|
1591
|
+
};
|
|
1592
|
+
for (const [chunkKey, arr] of Object.entries(chunks)) {
|
|
1593
|
+
for (let i = 0; i < arr.length; i++) {
|
|
1594
|
+
const packed = arr[i];
|
|
1595
|
+
const slot = packed & 0xff;
|
|
1596
|
+
if (slot === 0) continue;
|
|
1597
|
+
const col = packed >>> 8 & 0xff;
|
|
1598
|
+
const row = packed >>> 16 & 0xff;
|
|
1599
|
+
if (cellKeyMatches(slot, col, row)) {
|
|
1600
|
+
dirty.add(chunkKey);
|
|
1601
|
+
break;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
return dirty;
|
|
1606
|
+
}
|
|
1607
|
+
function expandSceneCollectionSource(src) {
|
|
1608
|
+
if (src.kind !== "sceneCollection") return [];
|
|
1609
|
+
return src.prefabRefs.map((p, i) => ({
|
|
1610
|
+
sourceId: src.id,
|
|
1611
|
+
prefabName: p,
|
|
1612
|
+
index: i
|
|
1613
|
+
}));
|
|
1614
|
+
}
|
|
1615
|
+
function resolveCellPrefabName(packed, sourcesBySlot) {
|
|
1616
|
+
var _a;
|
|
1617
|
+
const slot = packed & 0xff;
|
|
1618
|
+
if (slot === 0) return null;
|
|
1619
|
+
const src = sourcesBySlot[slot - 1];
|
|
1620
|
+
if (!src || src.kind !== "sceneCollection") return null;
|
|
1621
|
+
const col = packed >>> 8 & 0xff;
|
|
1622
|
+
return (_a = src.prefabRefs[col]) !== null && _a !== void 0 ? _a : null;
|
|
1623
|
+
}
|
|
1624
|
+
function cacheKeyOf(k) {
|
|
1625
|
+
return `${k.sourceSlot},${k.col},${k.row},${k.altIdx},${k.layerId}`;
|
|
1626
|
+
}
|
|
1627
|
+
class DecompositionCache {
|
|
1628
|
+
constructor(maxEntries = 1000) {
|
|
1629
|
+
this.cache = new Map();
|
|
1630
|
+
this.accessOrder = new Map();
|
|
1631
|
+
this.accessCounter = 0;
|
|
1632
|
+
this.maxEntries = maxEntries;
|
|
1633
|
+
}
|
|
1634
|
+
get(key) {
|
|
1635
|
+
const k = cacheKeyOf(key);
|
|
1636
|
+
const cached = this.cache.get(k);
|
|
1637
|
+
if (cached) this.accessOrder.set(k, ++this.accessCounter);
|
|
1638
|
+
return cached;
|
|
1639
|
+
}
|
|
1640
|
+
set(key, value) {
|
|
1641
|
+
const k = cacheKeyOf(key);
|
|
1642
|
+
this.cache.set(k, value);
|
|
1643
|
+
this.accessOrder.set(k, ++this.accessCounter);
|
|
1644
|
+
if (this.cache.size > this.maxEntries) this.evictLRU();
|
|
1645
|
+
}
|
|
1646
|
+
has(key) {
|
|
1647
|
+
return this.cache.has(cacheKeyOf(key));
|
|
1648
|
+
}
|
|
1649
|
+
clear() {
|
|
1650
|
+
this.cache.clear();
|
|
1651
|
+
this.accessOrder.clear();
|
|
1652
|
+
this.accessCounter = 0;
|
|
1653
|
+
}
|
|
1654
|
+
get size() {
|
|
1655
|
+
return this.cache.size;
|
|
1656
|
+
}
|
|
1657
|
+
evictLRU() {
|
|
1658
|
+
let oldestKey;
|
|
1659
|
+
let oldestAccess = Infinity;
|
|
1660
|
+
for (const [k, c] of this.accessOrder) {
|
|
1661
|
+
if (c < oldestAccess) {
|
|
1662
|
+
oldestAccess = c;
|
|
1663
|
+
oldestKey = k;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
if (oldestKey) {
|
|
1667
|
+
this.cache.delete(oldestKey);
|
|
1668
|
+
this.accessOrder.delete(oldestKey);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
function translateConvexParts(parts, dx, dy) {
|
|
1673
|
+
return parts.map(p => ({
|
|
1674
|
+
vertices: p.vertices.map(v => ({
|
|
1675
|
+
x: v.x + dx,
|
|
1676
|
+
y: v.y + dy
|
|
1677
|
+
}))
|
|
1678
|
+
}));
|
|
1679
|
+
}
|
|
1680
|
+
function isConvex(polygon) {
|
|
1681
|
+
if (polygon.length < 3) return true;
|
|
1682
|
+
let sign = 0;
|
|
1683
|
+
const n = polygon.length;
|
|
1684
|
+
for (let i = 0; i < n; i++) {
|
|
1685
|
+
const a = polygon[i];
|
|
1686
|
+
const b = polygon[(i + 1) % n];
|
|
1687
|
+
const c = polygon[(i + 2) % n];
|
|
1688
|
+
const ex1 = b.x - a.x;
|
|
1689
|
+
const ey1 = b.y - a.y;
|
|
1690
|
+
const ex2 = c.x - b.x;
|
|
1691
|
+
const ey2 = c.y - b.y;
|
|
1692
|
+
const cross = ex1 * ey2 - ey1 * ex2;
|
|
1693
|
+
if (cross !== 0) {
|
|
1694
|
+
if (sign === 0) sign = cross > 0 ? 1 : -1;else if ((cross > 0 ? 1 : -1) !== sign) return false;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
return true;
|
|
1698
|
+
}
|
|
1699
|
+
function decomposePolygon(polygon) {
|
|
1700
|
+
if (polygon.length < 3) return [];
|
|
1701
|
+
if (!isConvex(polygon)) {
|
|
1702
|
+
const err = new Error(`decomposePolygon: input polygon (${polygon.length} vertices) is non-convex. ` + `This is a placeholder implementation that assumes convex input — concave polygons silently produce ` + `invalid Matter.js bodies. Real concave decomposition requires the 'poly-decomp' npm dependency ` + `(see ADR-0019 Phase 1). If you need concave physics tiles right now, split into convex parts manually.`);
|
|
1703
|
+
if (typeof console !== 'undefined') console.error('[Tilemap]', err.message);
|
|
1704
|
+
throw err;
|
|
1705
|
+
}
|
|
1706
|
+
return [{
|
|
1707
|
+
vertices: polygon.slice()
|
|
1708
|
+
}];
|
|
1709
|
+
}
|
|
1710
|
+
function cacheKeyOfNum(slot, col, row, altIdx) {
|
|
1711
|
+
return ((slot & 0xff) << 24 | (altIdx & 0x3f) << 18 | (row & 0x1ff) << 9 | col & 0x1ff) >>> 0;
|
|
1712
|
+
}
|
|
1713
|
+
DecompositionCache.prototype.getNum = function (key) {
|
|
1714
|
+
return this.get({
|
|
1715
|
+
sourceSlot: key >>> 24 & 0xff,
|
|
1716
|
+
altIdx: key >>> 18 & 0x3f,
|
|
1717
|
+
row: key >>> 9 & 0x1ff,
|
|
1718
|
+
col: key & 0x1ff,
|
|
1719
|
+
layerId: "__num__"
|
|
1720
|
+
});
|
|
1721
|
+
};
|
|
1722
|
+
DecompositionCache.prototype.setNum = function (key, value) {
|
|
1723
|
+
this.set({
|
|
1724
|
+
sourceSlot: key >>> 24 & 0xff,
|
|
1725
|
+
altIdx: key >>> 18 & 0x3f,
|
|
1726
|
+
row: key >>> 9 & 0x1ff,
|
|
1727
|
+
col: key & 0x1ff,
|
|
1728
|
+
layerId: "__num__"
|
|
1729
|
+
}, value);
|
|
1730
|
+
};
|
|
1731
|
+
class BodySourceRegistryImpl {
|
|
1732
|
+
constructor() {
|
|
1733
|
+
this.builders = new Map();
|
|
1734
|
+
}
|
|
1735
|
+
register(name, builder) {
|
|
1736
|
+
this.builders.set(name, builder);
|
|
1737
|
+
}
|
|
1738
|
+
unregister(name) {
|
|
1739
|
+
this.builders.delete(name);
|
|
1740
|
+
}
|
|
1741
|
+
has(name) {
|
|
1742
|
+
return this.builders.has(name);
|
|
1743
|
+
}
|
|
1744
|
+
build(name, ctx) {
|
|
1745
|
+
const b = this.builders.get(name);
|
|
1746
|
+
if (!b) return null;
|
|
1747
|
+
return b(ctx);
|
|
1748
|
+
}
|
|
1749
|
+
listRegisteredNames() {
|
|
1750
|
+
return Array.from(this.builders.keys());
|
|
1751
|
+
}
|
|
1752
|
+
clear() {
|
|
1753
|
+
this.builders.clear();
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
const TILEMAP_BODY_SOURCE_REGISTRY = new BodySourceRegistryImpl();
|
|
1757
|
+
const CHUNK_SIZE = 16;
|
|
1758
|
+
function unpackSlot(packed) {
|
|
1759
|
+
return packed & 0xff;
|
|
1760
|
+
}
|
|
1761
|
+
function unpackCol(packed) {
|
|
1762
|
+
return packed >>> 8 & 0xff;
|
|
1763
|
+
}
|
|
1764
|
+
function unpackRow(packed) {
|
|
1765
|
+
return packed >>> 16 & 0xff;
|
|
1766
|
+
}
|
|
1767
|
+
function unpackAlt(packed) {
|
|
1768
|
+
return packed >>> 24 & 0x1f;
|
|
1769
|
+
}
|
|
1770
|
+
function buildTileMapStaticBodies(input, ctx) {
|
|
1771
|
+
var _a;
|
|
1772
|
+
const out = [];
|
|
1773
|
+
for (const [chunkKey, arr] of Object.entries(input.chunksByKey)) {
|
|
1774
|
+
const [ckxStr, ckyStr] = chunkKey.split(",");
|
|
1775
|
+
const ckx = Number.parseInt(ckxStr, 10);
|
|
1776
|
+
const cky = Number.parseInt(ckyStr, 10);
|
|
1777
|
+
if (!Number.isFinite(ckx) || !Number.isFinite(cky)) continue;
|
|
1778
|
+
for (let ly = 0; ly < CHUNK_SIZE; ly++) {
|
|
1779
|
+
for (let lx = 0; lx < CHUNK_SIZE; lx++) {
|
|
1780
|
+
const packed = arr[ly * CHUNK_SIZE + lx];
|
|
1781
|
+
const slot = unpackSlot(packed);
|
|
1782
|
+
if (slot === 0) continue;
|
|
1783
|
+
const col = unpackCol(packed);
|
|
1784
|
+
const row = unpackRow(packed);
|
|
1785
|
+
const altIdx = unpackAlt(packed);
|
|
1786
|
+
const physicsKey = `${slot},${col},${row},${altIdx}`;
|
|
1787
|
+
const physicsEntries = input.physicsByCellKey.get(physicsKey);
|
|
1788
|
+
if (!physicsEntries || physicsEntries.length === 0) continue;
|
|
1789
|
+
const cellWorldX = input.mapOriginX + (ckx * CHUNK_SIZE + lx) * input.cellWidth + ctx.worldX;
|
|
1790
|
+
const cellWorldY = input.mapOriginY + (cky * CHUNK_SIZE + ly) * input.cellHeight + ctx.worldY;
|
|
1791
|
+
for (const entry of physicsEntries) {
|
|
1792
|
+
for (const poly of entry.polygons) {
|
|
1793
|
+
const cacheKeyNum = cacheKeyOfNum(slot, col, row, altIdx);
|
|
1794
|
+
let parts = (_a = input.cache.getNum(cacheKeyNum)) === null || _a === void 0 ? void 0 : _a.parts;
|
|
1795
|
+
if (!parts) {
|
|
1796
|
+
parts = decomposePolygon(poly.points);
|
|
1797
|
+
input.cache.setNum(cacheKeyNum, {
|
|
1798
|
+
parts
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
const translated = translateConvexParts(parts, cellWorldX, cellWorldY);
|
|
1802
|
+
for (let pi = 0; pi < translated.length; pi++) {
|
|
1803
|
+
out.push({
|
|
1804
|
+
id: `tile-${chunkKey}-${lx}-${ly}-${entry.layerId}-${pi}`,
|
|
1805
|
+
vertices: translated[pi].vertices,
|
|
1806
|
+
centerX: cellWorldX + input.cellWidth / 2,
|
|
1807
|
+
centerY: cellWorldY + input.cellHeight / 2,
|
|
1808
|
+
isStatic: true,
|
|
1809
|
+
oneWay: entry.oneWay,
|
|
1810
|
+
friction: entry.friction,
|
|
1811
|
+
restitution: entry.restitution,
|
|
1812
|
+
metadata: {
|
|
1813
|
+
layerId: entry.layerId,
|
|
1814
|
+
chunkKey,
|
|
1815
|
+
cellLocal: [lx, ly]
|
|
1816
|
+
}
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
return out;
|
|
1825
|
+
}
|
|
1826
|
+
function buildTileMapStaticBodyDefinitions(input, ctx, probes) {
|
|
1827
|
+
var _a, _b;
|
|
1828
|
+
probes === null || probes === void 0 ? void 0 : probes.beginTiming(TILEMAP_PROBE_NAMES.PHYSICS_REBAKE_MS);
|
|
1829
|
+
let bodies;
|
|
1830
|
+
try {
|
|
1831
|
+
bodies = buildTileMapStaticBodies(input, ctx);
|
|
1832
|
+
} finally {
|
|
1833
|
+
probes === null || probes === void 0 ? void 0 : probes.endTiming(TILEMAP_PROBE_NAMES.PHYSICS_REBAKE_MS);
|
|
1834
|
+
}
|
|
1835
|
+
const bodiesByLayerId = new Map();
|
|
1836
|
+
for (const b of bodies) {
|
|
1837
|
+
const layerId = (_b = (_a = b.metadata) === null || _a === void 0 ? void 0 : _a.layerId) !== null && _b !== void 0 ? _b : "<unlabeled>";
|
|
1838
|
+
let bucket = bodiesByLayerId.get(layerId);
|
|
1839
|
+
if (!bucket) {
|
|
1840
|
+
bucket = [];
|
|
1841
|
+
bodiesByLayerId.set(layerId, bucket);
|
|
1842
|
+
}
|
|
1843
|
+
bucket.push(b);
|
|
1844
|
+
}
|
|
1845
|
+
const totalBodyCount = bodies.length;
|
|
1846
|
+
probes === null || probes === void 0 ? void 0 : probes.gauge(TILEMAP_PROBE_NAMES.BODIES_TOTAL, totalBodyCount);
|
|
1847
|
+
if (totalBodyCount > 0) {
|
|
1848
|
+
probes === null || probes === void 0 ? void 0 : probes.count(TILEMAP_PROBE_NAMES.BODIES_CREATED, totalBodyCount);
|
|
1849
|
+
}
|
|
1850
|
+
return {
|
|
1851
|
+
bodies,
|
|
1852
|
+
totalBodyCount,
|
|
1853
|
+
bodiesByLayerId
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
function extractPhysicsByCellKey(tilesetRaw) {
|
|
1857
|
+
const out = new Map();
|
|
1858
|
+
for (let slotIdx = 0; slotIdx < tilesetRaw.sources.length; slotIdx++) {
|
|
1859
|
+
const src = tilesetRaw.sources[slotIdx];
|
|
1860
|
+
if (!src || src.kind !== "atlas" || !src.tiles) continue;
|
|
1861
|
+
const slot = slotIdx + 1;
|
|
1862
|
+
for (const tile of src.tiles) {
|
|
1863
|
+
const {
|
|
1864
|
+
col,
|
|
1865
|
+
row
|
|
1866
|
+
} = tile.atlasCoords;
|
|
1867
|
+
for (const alt of tile.alternatives) {
|
|
1868
|
+
if (!alt.physics || alt.physics.length === 0) continue;
|
|
1869
|
+
const key = `${slot},${col},${row},${alt.altId & 0x1f}`;
|
|
1870
|
+
out.set(key, alt.physics);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
return out;
|
|
1875
|
+
}
|
|
1876
|
+
const ADR_0019_NATIVE_REGISTRY_AVAILABLE = false;
|
|
1877
|
+
try {
|
|
1878
|
+
eva_js.resource.registerResourceType('TILESET');
|
|
1879
|
+
} catch (_a) {}
|
|
1880
|
+
try {
|
|
1881
|
+
eva_js.resource.registerInstance('TILESET', res => {
|
|
1882
|
+
var _a, _b, _c, _d;
|
|
1883
|
+
const fromData = (_a = res.data) === null || _a === void 0 ? void 0 : _a.json;
|
|
1884
|
+
const fromSrc = (_c = (_b = res.src) === null || _b === void 0 ? void 0 : _b.json) === null || _c === void 0 ? void 0 : _c.data;
|
|
1885
|
+
return (_d = fromData !== null && fromData !== void 0 ? fromData : fromSrc) !== null && _d !== void 0 ? _d : null;
|
|
1886
|
+
});
|
|
1887
|
+
} catch (_b) {}
|
|
1888
|
+
exports.ADR_0019_NATIVE_REGISTRY_AVAILABLE = ADR_0019_NATIVE_REGISTRY_AVAILABLE;
|
|
1889
|
+
exports.CHUNK_SIZE = CHUNK_SIZE$2;
|
|
1890
|
+
exports.DecompositionCache = DecompositionCache;
|
|
1891
|
+
exports.MAX_TERRAIN_ID = MAX_TERRAIN_ID;
|
|
1892
|
+
exports.NEIGHBOR_DIRECTIONS = NEIGHBOR_DIRECTIONS;
|
|
1893
|
+
exports.NO_NEIGHBOR = NO_NEIGHBOR;
|
|
1894
|
+
exports.PeeringBitIndex = PeeringBitIndex;
|
|
1895
|
+
exports.TILEMAP_BODY_SOURCE_REGISTRY = TILEMAP_BODY_SOURCE_REGISTRY;
|
|
1896
|
+
exports.TILEMAP_PROBE_NAMES = TILEMAP_PROBE_NAMES;
|
|
1897
|
+
exports.TILE_FRAG_SHADER = TILE_FRAG_SHADER;
|
|
1898
|
+
exports.TILE_SHADER_SOURCES = TILE_SHADER_SOURCES;
|
|
1899
|
+
exports.TILE_VERT_SHADER = TILE_VERT_SHADER;
|
|
1900
|
+
exports.TileAnimationDriver = TileAnimationDriver;
|
|
343
1901
|
exports.Tilemap = Tilemap;
|
|
344
1902
|
exports.TilemapSystem = TilemapSystem$1;
|
|
1903
|
+
exports.adaptGamePerfProbes = adaptGamePerfProbes;
|
|
1904
|
+
exports.buildChunkGeometry = buildChunkGeometry;
|
|
1905
|
+
exports.buildChunkMesh = buildChunkMesh;
|
|
1906
|
+
exports.buildTileMapStaticBodies = buildTileMapStaticBodies;
|
|
1907
|
+
exports.buildTileMapStaticBodyDefinitions = buildTileMapStaticBodyDefinitions;
|
|
1908
|
+
exports.cacheKeyOf = cacheKeyOf;
|
|
1909
|
+
exports.cacheKeyOfNum = cacheKeyOfNum;
|
|
1910
|
+
exports.computeAffectedChunks = computeAffectedChunks;
|
|
1911
|
+
exports.createInMemoryProbeRegistry = createInMemoryProbeRegistry;
|
|
1912
|
+
exports.decodeChunk = decodeChunk;
|
|
1913
|
+
exports.decomposePolygon = decomposePolygon;
|
|
1914
|
+
exports.diffTilesetDocuments = diffTilesetDocuments;
|
|
1915
|
+
exports.diffTilesetForChunkRebuild = diffTilesetForChunkRebuild;
|
|
1916
|
+
exports.estimateChunkMeshMemoryKB = estimateChunkMeshMemoryKB;
|
|
1917
|
+
exports.estimateSpriteNodes = estimateSpriteNodes;
|
|
1918
|
+
exports.expandSceneCollectionSource = expandSceneCollectionSource;
|
|
1919
|
+
exports.extractPhysicsByCellKey = extractPhysicsByCellKey;
|
|
1920
|
+
exports.getRequiredTilemapProbes = getRequiredTilemapProbes;
|
|
1921
|
+
exports.isEmptyCellValue = isEmptyCellValue;
|
|
1922
|
+
exports.isMeshPathAvailable = isMeshPathAvailable;
|
|
1923
|
+
exports.makeLoadedTileset = makeLoadedTileset;
|
|
1924
|
+
exports.pickAutotileCandidate = pickAutotileCandidate;
|
|
1925
|
+
exports.resolveCellPrefabName = resolveCellPrefabName;
|
|
1926
|
+
exports.resolveChunkRenderStrategy = resolveChunkRenderStrategy;
|
|
1927
|
+
exports.translateConvexParts = translateConvexParts;
|
|
1928
|
+
exports.unpackCell = unpackCell;
|
|
345
1929
|
Object.defineProperty(exports, '__esModule', {
|
|
346
1930
|
value: true
|
|
347
1931
|
});
|