@codexo/exojs-tilemap 0.15.3 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +33 -26
  2. package/dist/esm/ChunkSource.d.ts +33 -0
  3. package/dist/esm/ChunkSource.d.ts.map +1 -0
  4. package/dist/esm/ChunkStreamer.d.ts +98 -0
  5. package/dist/esm/ChunkStreamer.d.ts.map +1 -0
  6. package/dist/esm/ChunkStreamer.js +201 -0
  7. package/dist/esm/ChunkStreamer.js.map +1 -0
  8. package/dist/esm/ImageLayer.d.ts +11 -3
  9. package/dist/esm/ImageLayer.d.ts.map +1 -0
  10. package/dist/esm/ImageLayer.js +71 -64
  11. package/dist/esm/ImageLayer.js.map +1 -1
  12. package/dist/esm/ImageLayerNode.d.ts +89 -0
  13. package/dist/esm/ImageLayerNode.d.ts.map +1 -0
  14. package/dist/esm/ImageLayerNode.js +205 -0
  15. package/dist/esm/ImageLayerNode.js.map +1 -0
  16. package/dist/esm/MapObject.d.ts +70 -0
  17. package/dist/esm/MapObject.d.ts.map +1 -0
  18. package/dist/esm/MapObject.js +34 -0
  19. package/dist/esm/MapObject.js.map +1 -0
  20. package/dist/esm/MapObjectSpawner.d.ts +133 -0
  21. package/dist/esm/MapObjectSpawner.d.ts.map +1 -0
  22. package/dist/esm/MapObjectSpawner.js +147 -0
  23. package/dist/esm/MapObjectSpawner.js.map +1 -0
  24. package/dist/esm/MapSpawnSession.d.ts +53 -0
  25. package/dist/esm/MapSpawnSession.d.ts.map +1 -0
  26. package/dist/esm/MapSpawnSession.js +88 -0
  27. package/dist/esm/MapSpawnSession.js.map +1 -0
  28. package/dist/esm/MapWorld.d.ts +134 -0
  29. package/dist/esm/MapWorld.d.ts.map +1 -0
  30. package/dist/esm/MapWorld.js +150 -0
  31. package/dist/esm/MapWorld.js.map +1 -0
  32. package/dist/esm/MapWorldRuntime.d.ts +182 -0
  33. package/dist/esm/MapWorldRuntime.d.ts.map +1 -0
  34. package/dist/esm/MapWorldRuntime.js +261 -0
  35. package/dist/esm/MapWorldRuntime.js.map +1 -0
  36. package/dist/esm/ObjectLayer.d.ts +93 -7
  37. package/dist/esm/ObjectLayer.d.ts.map +1 -0
  38. package/dist/esm/ObjectLayer.js +184 -167
  39. package/dist/esm/ObjectLayer.js.map +1 -1
  40. package/dist/esm/SampledChunkSource.d.ts +43 -0
  41. package/dist/esm/SampledChunkSource.d.ts.map +1 -0
  42. package/dist/esm/SampledChunkSource.js +48 -0
  43. package/dist/esm/SampledChunkSource.js.map +1 -0
  44. package/dist/esm/TileAnimator.d.ts +12 -2
  45. package/dist/esm/TileAnimator.d.ts.map +1 -0
  46. package/dist/esm/TileAnimator.js +154 -165
  47. package/dist/esm/TileAnimator.js.map +1 -1
  48. package/dist/esm/TileChunk.d.ts +30 -5
  49. package/dist/esm/TileChunk.d.ts.map +1 -0
  50. package/dist/esm/TileChunk.js +190 -188
  51. package/dist/esm/TileChunk.js.map +1 -1
  52. package/dist/esm/TileChunkNode.d.ts +18 -3
  53. package/dist/esm/TileChunkNode.d.ts.map +1 -0
  54. package/dist/esm/TileChunkNode.js +104 -85
  55. package/dist/esm/TileChunkNode.js.map +1 -1
  56. package/dist/esm/TileLayer.d.ts +104 -24
  57. package/dist/esm/TileLayer.d.ts.map +1 -0
  58. package/dist/esm/TileLayer.js +511 -469
  59. package/dist/esm/TileLayer.js.map +1 -1
  60. package/dist/esm/TileLayerNode.d.ts +52 -11
  61. package/dist/esm/TileLayerNode.d.ts.map +1 -0
  62. package/dist/esm/TileLayerNode.js +224 -166
  63. package/dist/esm/TileLayerNode.js.map +1 -1
  64. package/dist/esm/TileMap.d.ts +96 -31
  65. package/dist/esm/TileMap.d.ts.map +1 -0
  66. package/dist/esm/TileMap.js +401 -357
  67. package/dist/esm/TileMap.js.map +1 -1
  68. package/dist/esm/TileMapBand.d.ts +42 -29
  69. package/dist/esm/TileMapBand.d.ts.map +1 -0
  70. package/dist/esm/TileMapBand.js +151 -158
  71. package/dist/esm/TileMapBand.js.map +1 -1
  72. package/dist/esm/TileMapNode.d.ts +34 -18
  73. package/dist/esm/TileMapNode.d.ts.map +1 -0
  74. package/dist/esm/TileMapNode.js +122 -109
  75. package/dist/esm/TileMapNode.js.map +1 -1
  76. package/dist/esm/TileMapView.d.ts +108 -47
  77. package/dist/esm/TileMapView.d.ts.map +1 -0
  78. package/dist/esm/TileMapView.js +385 -338
  79. package/dist/esm/TileMapView.js.map +1 -1
  80. package/dist/esm/TileSet.d.ts +15 -6
  81. package/dist/esm/TileSet.d.ts.map +1 -0
  82. package/dist/esm/TileSet.js +161 -181
  83. package/dist/esm/TileSet.js.map +1 -1
  84. package/dist/esm/WangSet.d.ts +5 -4
  85. package/dist/esm/WangSet.d.ts.map +1 -0
  86. package/dist/esm/WangSet.js +70 -78
  87. package/dist/esm/WangSet.js.map +1 -1
  88. package/dist/esm/WorkerSampledChunkSource.d.ts +95 -0
  89. package/dist/esm/WorkerSampledChunkSource.d.ts.map +1 -0
  90. package/dist/esm/WorkerSampledChunkSource.js +117 -0
  91. package/dist/esm/WorkerSampledChunkSource.js.map +1 -0
  92. package/dist/esm/autoTile.d.ts +10 -5
  93. package/dist/esm/autoTile.d.ts.map +1 -0
  94. package/dist/esm/autoTile.js +164 -205
  95. package/dist/esm/autoTile.js.map +1 -1
  96. package/dist/esm/chunkGeometry.d.ts +8 -7
  97. package/dist/esm/chunkGeometry.d.ts.map +1 -0
  98. package/dist/esm/chunkGeometry.js +83 -94
  99. package/dist/esm/chunkGeometry.js.map +1 -1
  100. package/dist/esm/index.d.ts +1 -0
  101. package/dist/esm/index.d.ts.map +1 -0
  102. package/dist/esm/index.js +26 -15
  103. package/dist/esm/nodeBounds.d.ts +20 -0
  104. package/dist/esm/nodeBounds.d.ts.map +1 -0
  105. package/dist/esm/nodeBounds.js +40 -0
  106. package/dist/esm/nodeBounds.js.map +1 -0
  107. package/dist/esm/pixelSnap.d.ts +4 -3
  108. package/dist/esm/pixelSnap.d.ts.map +1 -0
  109. package/dist/esm/pixelSnap.js +18 -16
  110. package/dist/esm/pixelSnap.js.map +1 -1
  111. package/dist/esm/public.d.ts +23 -3
  112. package/dist/esm/public.d.ts.map +1 -0
  113. package/dist/esm/tileCollision.d.ts +190 -0
  114. package/dist/esm/tileCollision.d.ts.map +1 -0
  115. package/dist/esm/tileCollision.js +367 -0
  116. package/dist/esm/tileCollision.js.map +1 -0
  117. package/dist/esm/tileWord.d.ts +12 -0
  118. package/dist/esm/tileWord.d.ts.map +1 -0
  119. package/dist/esm/tileWord.js +16 -0
  120. package/dist/esm/tileWord.js.map +1 -0
  121. package/dist/esm/tilemapExtension.d.ts +4 -3
  122. package/dist/esm/tilemapExtension.d.ts.map +1 -0
  123. package/dist/esm/tilemapExtension.js +47 -47
  124. package/dist/esm/tilemapExtension.js.map +1 -1
  125. package/dist/esm/tilemapSerializers.d.ts +5 -4
  126. package/dist/esm/tilemapSerializers.d.ts.map +1 -0
  127. package/dist/esm/tilemapSerializers.js +48 -41
  128. package/dist/esm/tilemapSerializers.js.map +1 -1
  129. package/dist/esm/types.d.ts +34 -18
  130. package/dist/esm/types.d.ts.map +1 -0
  131. package/dist/esm/types.js +111 -120
  132. package/dist/esm/types.js.map +1 -1
  133. package/dist/esm/webgl2/WebGl2TileChunkRenderer.d.ts +50 -2
  134. package/dist/esm/webgl2/WebGl2TileChunkRenderer.d.ts.map +1 -0
  135. package/dist/esm/webgl2/WebGl2TileChunkRenderer.js +366 -332
  136. package/dist/esm/webgl2/WebGl2TileChunkRenderer.js.map +1 -1
  137. package/dist/esm/webgl2/shaders/tile-chunk.frag.js +6 -0
  138. package/dist/esm/webgl2/shaders/tile-chunk.frag.js.map +1 -0
  139. package/dist/esm/webgl2/shaders/tile-chunk.vert.js +6 -0
  140. package/dist/esm/webgl2/shaders/tile-chunk.vert.js.map +1 -0
  141. package/dist/esm/webgpu/WebGpuTileChunkRenderer.d.ts +70 -4
  142. package/dist/esm/webgpu/WebGpuTileChunkRenderer.d.ts.map +1 -0
  143. package/dist/esm/webgpu/WebGpuTileChunkRenderer.js +496 -383
  144. package/dist/esm/webgpu/WebGpuTileChunkRenderer.js.map +1 -1
  145. package/dist/esm/webgpu/shaders/tile-chunk.wgsl.js +6 -0
  146. package/dist/esm/webgpu/shaders/tile-chunk.wgsl.js.map +1 -0
  147. package/package.json +8 -15
  148. package/dist/esm/index.js.map +0 -1
  149. package/dist/esm/register.d.ts +0 -1
  150. package/dist/esm/register.js +0 -25
  151. package/dist/esm/register.js.map +0 -1
@@ -1,170 +1,228 @@
1
- import { Container } from '@codexo/exojs';
2
- import { assertPixelSnapMode } from './pixelSnap.js';
3
- import { TileChunkNode } from './TileChunkNode.js';
1
+ import { TileChunkNode } from "./TileChunkNode.js";
2
+ import { assertPixelSnapMode } from "./pixelSnap.js";
3
+ import { aggregateChildLocalBounds } from "./nodeBounds.js";
4
+ import { PixelSnapMode } from "@codexo/exojs/renderer-sdk";
5
+ import { Container } from "@codexo/exojs";
4
6
 
7
+ //#region src/TileLayerNode.ts
5
8
  /**
6
- * A scene node that renders one generic {@link TileLayer} as a
7
- * {@link Container} of per-chunk {@link TileChunkNode} drawables.
8
- *
9
- * Each non-empty loaded chunk becomes one child positioned at its pixel
10
- * origin, so the engine's existing per-node culling drops individual chunks and
11
- * the render-plan optimiser batches them by tileset texture. The layer's pixel
12
- * `offset` is applied to this node's transform; `visible` and `opacity` are
13
- * read live from the runtime layer on every frame (no rebuild required).
14
- *
15
- * The node references but never owns the {@link TileLayer}: destroying it
16
- * frees its chunk nodes and their cached geometry but leaves the layer, map,
17
- * and Loader-owned textures intact.
18
- *
19
- * Structural changes to the layer (tiles written into previously-empty chunks)
20
- * are reflected only after {@link TileLayerNode.refresh}; in-place edits to
21
- * existing chunks are picked up automatically via chunk revisions.
22
- *
23
- * @advanced
24
- */
25
- class TileLayerNode extends Container {
26
- _layer;
27
- _cullChunks;
28
- _chunkNodes = [];
29
- _syncedOpacity = -1;
30
- _syncedTint = undefined;
31
- _pixelSnapMode = 'none';
32
- _baseOffsetX;
33
- _baseOffsetY;
34
- constructor(layer, options) {
35
- super();
36
- this._layer = layer;
37
- this._cullChunks = options?.cullable ?? true;
38
- this._baseOffsetX = layer.offsetX;
39
- this._baseOffsetY = layer.offsetY;
40
- this.setPosition(layer.offsetX, layer.offsetY);
41
- this._buildChunkNodes();
42
- }
43
- /** The runtime layer this node renders. */
44
- get layer() {
45
- return this._layer;
46
- }
47
- /**
48
- * Render-only pixel-snap mode applied to every chunk node in this layer (and
49
- * to chunks rebuilt later by {@link refresh}). Each chunk's rendered origin is
50
- * snapped to the active render target's device-pixel grid; because chunk
51
- * origins are integer multiples of the integer tile pitch from the same layer
52
- * origin, the whole grid stays exact and adjacent chunks cannot drift apart.
53
- *
54
- * Purely visual: tile data, the layer offset, chunk content revisions, and
55
- * culling bounds are never changed. `'geometry'` and `'position'` both resolve
56
- * to coherent origin snapping for tile chunks (chunk quads are already on the
57
- * integer pixel grid by construction). Setting the current value is a no-op;
58
- * an invalid value throws and leaves the prior mode unchanged.
59
- *
60
- * @default 'none'
61
- * @stable
62
- */
63
- get pixelSnapMode() {
64
- return this._pixelSnapMode;
65
- }
66
- set pixelSnapMode(mode) {
67
- if (mode === this._pixelSnapMode) {
68
- return;
69
- }
70
- assertPixelSnapMode(mode);
71
- this._pixelSnapMode = mode;
72
- for (const chunk of this._chunkNodes) {
73
- chunk.pixelSnapMode = mode;
74
- }
75
- }
76
- /**
77
- * Rebuild the chunk-node children from the layer's current loaded chunks.
78
- * Call after structural mutation that creates or empties chunks. In-place
79
- * tile edits to existing chunks do not need a refresh.
80
- */
81
- refresh() {
82
- const previous = [...this._chunkNodes];
83
- this.removeChildren();
84
- this._chunkNodes.length = 0;
85
- for (const child of previous) {
86
- child.destroy();
87
- }
88
- this._syncedOpacity = -1;
89
- this._syncedTint = undefined;
90
- this._buildChunkNodes();
91
- return this;
92
- }
93
- /** The chunk render nodes owned by this layer node, in build order. @internal */
94
- get chunkNodes() {
95
- return this._chunkNodes;
96
- }
97
- getLocalBounds() {
98
- const bounds = super.getLocalBounds();
99
- bounds.set(0, 0, this._layer.pixelWidth, this._layer.pixelHeight);
100
- return bounds;
101
- }
102
- /** @internal */
103
- _collectContent(builder) {
104
- if (!this._layer.visible) {
105
- return;
106
- }
107
- this._syncTint();
108
- const layer = this._layer;
109
- if (layer.parallaxX !== 1 || layer.parallaxY !== 1) {
110
- const camCenter = builder.view.center;
111
- const prevX = this.x;
112
- const prevY = this.y;
113
- this.x = this._baseOffsetX + camCenter.x * (1 - layer.parallaxX);
114
- this.y = this._baseOffsetY + camCenter.y * (1 - layer.parallaxY);
115
- super._collectContent(builder);
116
- this.x = prevX;
117
- this.y = prevY;
118
- }
119
- else {
120
- super._collectContent(builder);
121
- }
122
- }
123
- destroy() {
124
- const children = [...this._chunkNodes];
125
- this._chunkNodes.length = 0;
126
- super.destroy();
127
- for (const child of children) {
128
- child.destroy();
129
- }
130
- }
131
- _buildChunkNodes() {
132
- const layer = this._layer;
133
- for (const chunk of layer.loadedChunks()) {
134
- if (chunk.empty) {
135
- continue;
136
- }
137
- const node = new TileChunkNode(chunk, layer.tilesets, layer.tileWidth, layer.tileHeight, layer.chunkWidth, layer.chunkHeight);
138
- node.cullable = this._cullChunks;
139
- if (this._pixelSnapMode !== 'none') {
140
- node.pixelSnapMode = this._pixelSnapMode;
141
- }
142
- this._chunkNodes.push(node);
143
- this.addChild(node);
144
- }
145
- this._syncTint();
146
- }
147
- /**
148
- * Propagate the layer's live opacity and tint colour onto the chunk render
149
- * tints if either changed. Opacity drives the tint alpha; `tintColor`
150
- * (`0xRRGGBB`) multiplies the RGB (white = no tint).
151
- */
152
- _syncTint() {
153
- const opacity = this._layer.opacity;
154
- const tintColor = this._layer.tintColor;
155
- if (opacity === this._syncedOpacity && tintColor === this._syncedTint) {
156
- return;
157
- }
158
- const r = tintColor === null ? 255 : (tintColor >> 16) & 0xff;
159
- const g = tintColor === null ? 255 : (tintColor >> 8) & 0xff;
160
- const b = tintColor === null ? 255 : tintColor & 0xff;
161
- for (const child of this._chunkNodes) {
162
- child.tint.set(r, g, b, opacity);
163
- }
164
- this._syncedOpacity = opacity;
165
- this._syncedTint = tintColor;
166
- }
167
- }
9
+ * A scene node that renders one generic {@link TileLayer} as a
10
+ * {@link Container} of per-chunk {@link TileChunkNode} drawables.
11
+ *
12
+ * Each non-empty loaded chunk becomes one child positioned at its pixel
13
+ * origin, so the engine's existing per-node culling drops individual chunks and
14
+ * the render-plan optimiser batches them by tileset texture. The layer's pixel
15
+ * `offset` is applied to this node's transform; `visible` and `opacity` are
16
+ * read live from the runtime layer on every frame (no rebuild required).
17
+ *
18
+ * A `parallax` factor other than `1` makes this node's rendered position
19
+ * camera-dependent, resolved per frame in {@link _collectContent} by patching
20
+ * the node position against the camera centre, then restoring it - the cull
21
+ * test runs before that patch, so a bounded layer with parallax would be
22
+ * culled at its static base-offset bounds while actually rendering elsewhere.
23
+ * Such a layer therefore opts out of view culling (`cullable = false`), same
24
+ * as an unbounded layer (exactly like {@link import('./ImageLayerNode').ImageLayerNode}).
25
+ *
26
+ * The node references - but never owns - the {@link TileLayer}: destroying it
27
+ * frees its chunk nodes and their cached geometry but leaves the layer, map,
28
+ * and Loader-owned textures intact.
29
+ *
30
+ * Structural changes made via `_adoptChunk`/`_evictChunk` (chunk streaming)
31
+ * are picked up incrementally as they happen, without a full rebuild.
32
+ * {@link TileLayerNode.refresh} remains available for callers that mutate
33
+ * chunk structure by other means (e.g. a bulk edit that bypasses
34
+ * `_adoptChunk`/`_evictChunk`) and need a full resync. In-place tile edits to
35
+ * existing chunks are picked up automatically via chunk revisions.
36
+ *
37
+ * @advanced
38
+ */
39
+ var TileLayerNode = class extends Container {
40
+ _layer;
41
+ _cullChunks;
42
+ _chunkNodes = [];
43
+ _syncedOpacity = -1;
44
+ _syncedTint = void 0;
45
+ _pixelSnapMode = PixelSnapMode.None;
46
+ _baseOffsetX;
47
+ _baseOffsetY;
48
+ /**
49
+ * Bound once so `TileLayer._addStructuralListener`/`_removeStructuralListener`
50
+ * add and remove the SAME function reference. Incrementally adds/removes
51
+ * the `TileChunkNode` child for exactly the chunk that changed - never
52
+ * triggers a full {@link refresh}, so every other chunk node's
53
+ * revision-cached geometry survives untouched.
54
+ */
55
+ _onStructuralChange = (event) => {
56
+ const existingIndex = this._chunkNodes.findIndex((n) => n.chunkX === event.cx && n.chunkY === event.cy);
57
+ const existing = this._chunkNodes[existingIndex];
58
+ if (existing) {
59
+ this._chunkNodes.splice(existingIndex, 1);
60
+ this.removeChild(existing);
61
+ existing.destroy();
62
+ }
63
+ if (event.chunk === null || event.chunk.empty) return;
64
+ const node = this._createChunkNode(event.chunk);
65
+ this._chunkNodes.push(node);
66
+ this.addChild(node);
67
+ this._applyTint(node);
68
+ };
69
+ constructor(layer, options) {
70
+ super();
71
+ this._layer = layer;
72
+ this._cullChunks = options?.cullable ?? true;
73
+ this._baseOffsetX = layer.offsetX;
74
+ this._baseOffsetY = layer.offsetY;
75
+ this.setPosition(layer.offsetX, layer.offsetY);
76
+ this._buildChunkNodes();
77
+ if (!this._layer.bounded || layer.parallaxX !== 1 || layer.parallaxY !== 1 || layer.parallaxScale !== 1) this.cullable = false;
78
+ this._layer._addStructuralListener(this._onStructuralChange);
79
+ }
80
+ /** The runtime layer this node renders. */
81
+ get layer() {
82
+ return this._layer;
83
+ }
84
+ /**
85
+ * Render-only pixel-snap mode applied to every chunk node in this layer (and
86
+ * to chunks rebuilt later by {@link refresh}). Each chunk's rendered origin is
87
+ * snapped to the active render target's device-pixel grid; because chunk
88
+ * origins are integer multiples of the integer tile pitch from the same layer
89
+ * origin, the whole grid stays exact and adjacent chunks cannot drift apart.
90
+ *
91
+ * Purely visual: tile data, the layer offset, chunk content revisions, and
92
+ * culling bounds are never changed. `PixelSnapMode.Geometry` and `PixelSnapMode.Position` both resolve
93
+ * to coherent origin snapping for tile chunks (chunk quads are already on the
94
+ * integer pixel grid by construction). Setting the current value is a no-op;
95
+ * an invalid value throws and leaves the prior mode unchanged.
96
+ *
97
+ * @default PixelSnapMode.None
98
+ * @stable
99
+ */
100
+ get pixelSnapMode() {
101
+ return this._pixelSnapMode;
102
+ }
103
+ set pixelSnapMode(mode) {
104
+ if (mode === this._pixelSnapMode) return;
105
+ assertPixelSnapMode(mode);
106
+ this._pixelSnapMode = mode;
107
+ for (const chunk of this._chunkNodes) chunk.pixelSnapMode = mode;
108
+ }
109
+ /**
110
+ * Rebuild the chunk-node children from the layer's current loaded chunks.
111
+ * Call after structural mutation that creates or empties chunks. In-place
112
+ * tile edits to existing chunks do not need a refresh.
113
+ */
114
+ refresh() {
115
+ const previous = [...this._chunkNodes];
116
+ this.removeChildren();
117
+ this._chunkNodes.length = 0;
118
+ for (const child of previous) child.destroy();
119
+ this._syncedOpacity = -1;
120
+ this._syncedTint = void 0;
121
+ this._buildChunkNodes();
122
+ return this;
123
+ }
124
+ /** The chunk render nodes owned by this layer node, in build order. @internal */
125
+ get chunkNodes() {
126
+ return this._chunkNodes;
127
+ }
128
+ /**
129
+ * Recomputed lazily on read, so it writes `_localBounds` directly rather than
130
+ * going through `_setLocalBounds`: an invalidating write inside a getter
131
+ * would re-dirty the node on every read.
132
+ */
133
+ getLocalBounds() {
134
+ const bounds = this._localBounds;
135
+ const { pixelHeight, pixelWidth } = this._layer;
136
+ if (pixelWidth !== void 0 && pixelHeight !== void 0) bounds.set(0, 0, pixelWidth, pixelHeight);
137
+ else if (this._chunkNodes.length > 0) aggregateChildLocalBounds(this._chunkNodes, bounds);
138
+ return bounds;
139
+ }
140
+ /** @internal */
141
+ _collectContent(builder) {
142
+ if (!this._layer.visible) return;
143
+ this._syncTint();
144
+ const layer = this._layer;
145
+ const patchesPosition = layer.parallaxX !== 1 || layer.parallaxY !== 1;
146
+ const patchesScale = layer.parallaxScale !== 1;
147
+ if (patchesPosition || patchesScale) {
148
+ const camCenter = builder.view.center;
149
+ const prevX = this.x;
150
+ const prevY = this.y;
151
+ const prevScaleX = this.scale.x;
152
+ const prevScaleY = this.scale.y;
153
+ if (patchesPosition) {
154
+ this.x = this._baseOffsetX + camCenter.x * (1 - layer.parallaxX);
155
+ this.y = this._baseOffsetY + camCenter.y * (1 - layer.parallaxY);
156
+ }
157
+ if (patchesScale) this.setScale(prevScaleX * layer.parallaxScale, prevScaleY * layer.parallaxScale);
158
+ try {
159
+ super._collectContent(builder);
160
+ } finally {
161
+ if (patchesPosition) this.setPosition(prevX, prevY);
162
+ if (patchesScale) this.setScale(prevScaleX, prevScaleY);
163
+ }
164
+ } else super._collectContent(builder);
165
+ }
166
+ destroy() {
167
+ this._layer._removeStructuralListener(this._onStructuralChange);
168
+ const children = [...this._chunkNodes];
169
+ this._chunkNodes.length = 0;
170
+ super.destroy();
171
+ for (const child of children) child.destroy();
172
+ }
173
+ _buildChunkNodes() {
174
+ const layer = this._layer;
175
+ for (const chunk of layer.loadedChunks()) {
176
+ if (chunk.empty) continue;
177
+ const node = this._createChunkNode(chunk);
178
+ this._chunkNodes.push(node);
179
+ this.addChild(node);
180
+ }
181
+ this._syncTint();
182
+ }
183
+ /**
184
+ * Construct one configured {@link TileChunkNode} for `chunk` - shared by
185
+ * the initial bulk build ({@link _buildChunkNodes}) and the incremental
186
+ * structural-listener handler, so a future constructor argument or
187
+ * per-node setting only needs to be added in one place.
188
+ */
189
+ _createChunkNode(chunk) {
190
+ const layer = this._layer;
191
+ const node = new TileChunkNode(chunk, layer.tilesets, layer.tileWidth, layer.tileHeight, layer.chunkWidth, layer.chunkHeight);
192
+ node.cullable = this._cullChunks;
193
+ if (this._pixelSnapMode !== PixelSnapMode.None) node.pixelSnapMode = this._pixelSnapMode;
194
+ return node;
195
+ }
196
+ /**
197
+ * Compute and apply this layer's live tint (opacity as alpha, `tintColor`
198
+ * as RGB multiply) to a single chunk node. Used both by {@link _syncTint}'s
199
+ * bulk pass and by the structural-listener handler for a freshly-added
200
+ * node - a node added between two "nothing changed" full syncs must still
201
+ * receive the current tint, which a change-detection-guarded bulk pass
202
+ * alone would skip.
203
+ */
204
+ _applyTint(node) {
205
+ const tintColor = this._layer.tintColor;
206
+ const r = tintColor === null ? 255 : tintColor >> 16 & 255;
207
+ const g = tintColor === null ? 255 : tintColor >> 8 & 255;
208
+ const b = tintColor === null ? 255 : tintColor & 255;
209
+ node.tint.set(r, g, b, this._layer.opacity);
210
+ }
211
+ /**
212
+ * Propagate the layer's live opacity and tint colour onto every chunk
213
+ * render tint if either changed since the last call. Opacity drives the
214
+ * tint alpha; `tintColor` (`0xRRGGBB`) multiplies the RGB (white = no tint).
215
+ */
216
+ _syncTint() {
217
+ const opacity = this._layer.opacity;
218
+ const tintColor = this._layer.tintColor;
219
+ if (opacity === this._syncedOpacity && tintColor === this._syncedTint) return;
220
+ for (const child of this._chunkNodes) this._applyTint(child);
221
+ this._syncedOpacity = opacity;
222
+ this._syncedTint = tintColor;
223
+ }
224
+ };
168
225
 
226
+ //#endregion
169
227
  export { TileLayerNode };
170
- //# sourceMappingURL=TileLayerNode.js.map
228
+ //# sourceMappingURL=TileLayerNode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TileLayerNode.js","sources":["../../../src/TileLayerNode.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TileLayerNode.js","names":[],"sources":["../../src/TileLayerNode.ts"],"sourcesContent":["import type { ReadonlyRectangle } from '@codexo/exojs';\nimport { Container } from '@codexo/exojs';\nimport { PixelSnapMode, type RenderPlanBuilder } from '@codexo/exojs/renderer-sdk';\n\nimport { aggregateChildLocalBounds } from './nodeBounds';\nimport { assertPixelSnapMode } from './pixelSnap';\nimport type { ReadonlyTileChunk } from './TileChunk';\nimport { TileChunkNode } from './TileChunkNode';\nimport type { ChunkStructuralEvent, TileLayer } from './TileLayer';\n\n/**\n * Options for a {@link TileLayerNode}. All optional; reserved for forward\n * compatibility.\n * @advanced\n */\nexport interface TileLayerNodeOptions {\n /**\n * Whether the layer's chunk nodes participate in per-chunk view culling.\n * Defaults to `true`. Disable only for tiny, always-on-screen layers where\n * the per-chunk bounds test is pure overhead.\n */\n readonly cullable?: boolean;\n}\n\n/**\n * A scene node that renders one generic {@link TileLayer} as a\n * {@link Container} of per-chunk {@link TileChunkNode} drawables.\n *\n * Each non-empty loaded chunk becomes one child positioned at its pixel\n * origin, so the engine's existing per-node culling drops individual chunks and\n * the render-plan optimiser batches them by tileset texture. The layer's pixel\n * `offset` is applied to this node's transform; `visible` and `opacity` are\n * read live from the runtime layer on every frame (no rebuild required).\n *\n * A `parallax` factor other than `1` makes this node's rendered position\n * camera-dependent, resolved per frame in {@link _collectContent} by patching\n * the node position against the camera centre, then restoring it - the cull\n * test runs before that patch, so a bounded layer with parallax would be\n * culled at its static base-offset bounds while actually rendering elsewhere.\n * Such a layer therefore opts out of view culling (`cullable = false`), same\n * as an unbounded layer (exactly like {@link import('./ImageLayerNode').ImageLayerNode}).\n *\n * The node references - but never owns - the {@link TileLayer}: destroying it\n * frees its chunk nodes and their cached geometry but leaves the layer, map,\n * and Loader-owned textures intact.\n *\n * Structural changes made via `_adoptChunk`/`_evictChunk` (chunk streaming)\n * are picked up incrementally as they happen, without a full rebuild.\n * {@link TileLayerNode.refresh} remains available for callers that mutate\n * chunk structure by other means (e.g. a bulk edit that bypasses\n * `_adoptChunk`/`_evictChunk`) and need a full resync. In-place tile edits to\n * existing chunks are picked up automatically via chunk revisions.\n *\n * @advanced\n */\nexport class TileLayerNode extends Container {\n private readonly _layer: TileLayer;\n private readonly _cullChunks: boolean;\n private readonly _chunkNodes: TileChunkNode[] = [];\n private _syncedOpacity = -1;\n private _syncedTint: number | null | undefined = undefined;\n private _pixelSnapMode: PixelSnapMode = PixelSnapMode.None;\n private readonly _baseOffsetX: number;\n private readonly _baseOffsetY: number;\n\n /**\n * Bound once so `TileLayer._addStructuralListener`/`_removeStructuralListener`\n * add and remove the SAME function reference. Incrementally adds/removes\n * the `TileChunkNode` child for exactly the chunk that changed - never\n * triggers a full {@link refresh}, so every other chunk node's\n * revision-cached geometry survives untouched.\n */\n private readonly _onStructuralChange = (event: ChunkStructuralEvent): void => {\n const existingIndex = this._chunkNodes.findIndex(n => n.chunkX === event.cx && n.chunkY === event.cy);\n\n const existing = this._chunkNodes[existingIndex];\n\n if (existing) {\n this._chunkNodes.splice(existingIndex, 1);\n this.removeChild(existing);\n existing.destroy();\n }\n\n if (event.chunk === null || event.chunk.empty) {\n return;\n }\n\n const node = this._createChunkNode(event.chunk);\n\n this._chunkNodes.push(node);\n this.addChild(node);\n this._applyTint(node);\n };\n\n public constructor(layer: TileLayer, options?: TileLayerNodeOptions) {\n super();\n\n this._layer = layer;\n this._cullChunks = options?.cullable ?? true;\n this._baseOffsetX = layer.offsetX;\n this._baseOffsetY = layer.offsetY;\n\n this.setPosition(layer.offsetX, layer.offsetY);\n this._buildChunkNodes();\n\n if (!this._layer.bounded || layer.parallaxX !== 1 || layer.parallaxY !== 1 || layer.parallaxScale !== 1) {\n this.cullable = false;\n }\n\n this._layer._addStructuralListener(this._onStructuralChange);\n }\n\n /** The runtime layer this node renders. */\n public get layer(): TileLayer {\n return this._layer;\n }\n\n /**\n * Render-only pixel-snap mode applied to every chunk node in this layer (and\n * to chunks rebuilt later by {@link refresh}). Each chunk's rendered origin is\n * snapped to the active render target's device-pixel grid; because chunk\n * origins are integer multiples of the integer tile pitch from the same layer\n * origin, the whole grid stays exact and adjacent chunks cannot drift apart.\n *\n * Purely visual: tile data, the layer offset, chunk content revisions, and\n * culling bounds are never changed. `PixelSnapMode.Geometry` and `PixelSnapMode.Position` both resolve\n * to coherent origin snapping for tile chunks (chunk quads are already on the\n * integer pixel grid by construction). Setting the current value is a no-op;\n * an invalid value throws and leaves the prior mode unchanged.\n *\n * @default PixelSnapMode.None\n * @stable\n */\n public get pixelSnapMode(): PixelSnapMode {\n return this._pixelSnapMode;\n }\n\n public set pixelSnapMode(mode: PixelSnapMode) {\n if (mode === this._pixelSnapMode) {\n return;\n }\n\n assertPixelSnapMode(mode);\n this._pixelSnapMode = mode;\n\n for (const chunk of this._chunkNodes) {\n chunk.pixelSnapMode = mode;\n }\n }\n\n /**\n * Rebuild the chunk-node children from the layer's current loaded chunks.\n * Call after structural mutation that creates or empties chunks. In-place\n * tile edits to existing chunks do not need a refresh.\n */\n public refresh(): this {\n const previous = [...this._chunkNodes];\n\n this.removeChildren();\n this._chunkNodes.length = 0;\n\n for (const child of previous) {\n child.destroy();\n }\n\n this._syncedOpacity = -1;\n this._syncedTint = undefined;\n this._buildChunkNodes();\n\n return this;\n }\n\n /** The chunk render nodes owned by this layer node, in build order. @internal */\n public get chunkNodes(): readonly TileChunkNode[] {\n return this._chunkNodes;\n }\n\n /**\n * Recomputed lazily on read, so it writes `_localBounds` directly rather than\n * going through `_setLocalBounds`: an invalidating write inside a getter\n * would re-dirty the node on every read.\n */\n public override getLocalBounds(): ReadonlyRectangle {\n const bounds = this._localBounds;\n\n // Reading the pixel extents directly (rather than gating on `bounded`)\n // is what narrows them to numbers - `bounded` is a plain boolean getter.\n const { pixelHeight, pixelWidth } = this._layer;\n\n if (pixelWidth !== undefined && pixelHeight !== undefined) {\n bounds.set(0, 0, pixelWidth, pixelHeight);\n } else if (this._chunkNodes.length > 0) {\n aggregateChildLocalBounds(this._chunkNodes, bounds);\n }\n\n return bounds;\n }\n\n /** @internal */\n protected override _collectContent(builder: RenderPlanBuilder): void {\n if (!this._layer.visible) {\n return;\n }\n\n this._syncTint();\n\n const layer = this._layer;\n\n const patchesPosition = layer.parallaxX !== 1 || layer.parallaxY !== 1;\n const patchesScale = layer.parallaxScale !== 1;\n\n if (patchesPosition || patchesScale) {\n const camCenter = builder.view.center;\n const prevX = this.x;\n const prevY = this.y;\n const prevScaleX = this.scale.x;\n const prevScaleY = this.scale.y;\n\n if (patchesPosition) {\n this.x = this._baseOffsetX + camCenter.x * (1 - layer.parallaxX);\n this.y = this._baseOffsetY + camCenter.y * (1 - layer.parallaxY);\n }\n\n if (patchesScale) {\n this.setScale(prevScaleX * layer.parallaxScale, prevScaleY * layer.parallaxScale);\n }\n\n try {\n super._collectContent(builder);\n } finally {\n if (patchesPosition) {\n this.setPosition(prevX, prevY);\n }\n\n if (patchesScale) {\n this.setScale(prevScaleX, prevScaleY);\n }\n }\n } else {\n super._collectContent(builder);\n }\n }\n\n public override destroy(): void {\n this._layer._removeStructuralListener(this._onStructuralChange);\n\n const children = [...this._chunkNodes];\n\n this._chunkNodes.length = 0;\n\n super.destroy();\n\n for (const child of children) {\n child.destroy();\n }\n }\n\n private _buildChunkNodes(): void {\n const layer = this._layer;\n\n for (const chunk of layer.loadedChunks()) {\n if (chunk.empty) {\n continue;\n }\n\n const node = this._createChunkNode(chunk);\n\n this._chunkNodes.push(node);\n this.addChild(node);\n }\n\n this._syncTint();\n }\n\n /**\n * Construct one configured {@link TileChunkNode} for `chunk` - shared by\n * the initial bulk build ({@link _buildChunkNodes}) and the incremental\n * structural-listener handler, so a future constructor argument or\n * per-node setting only needs to be added in one place.\n */\n private _createChunkNode(chunk: ReadonlyTileChunk): TileChunkNode {\n const layer = this._layer;\n const node = new TileChunkNode(chunk, layer.tilesets, layer.tileWidth, layer.tileHeight, layer.chunkWidth, layer.chunkHeight);\n\n node.cullable = this._cullChunks;\n\n if (this._pixelSnapMode !== PixelSnapMode.None) {\n node.pixelSnapMode = this._pixelSnapMode;\n }\n\n return node;\n }\n\n /**\n * Compute and apply this layer's live tint (opacity as alpha, `tintColor`\n * as RGB multiply) to a single chunk node. Used both by {@link _syncTint}'s\n * bulk pass and by the structural-listener handler for a freshly-added\n * node - a node added between two \"nothing changed\" full syncs must still\n * receive the current tint, which a change-detection-guarded bulk pass\n * alone would skip.\n */\n private _applyTint(node: TileChunkNode): void {\n const tintColor = this._layer.tintColor;\n const r = tintColor === null ? 255 : (tintColor >> 16) & 0xff;\n const g = tintColor === null ? 255 : (tintColor >> 8) & 0xff;\n const b = tintColor === null ? 255 : tintColor & 0xff;\n node.tint.set(r, g, b, this._layer.opacity);\n }\n\n /**\n * Propagate the layer's live opacity and tint colour onto every chunk\n * render tint if either changed since the last call. Opacity drives the\n * tint alpha; `tintColor` (`0xRRGGBB`) multiplies the RGB (white = no tint).\n */\n private _syncTint(): void {\n const opacity = this._layer.opacity;\n const tintColor = this._layer.tintColor;\n\n if (opacity === this._syncedOpacity && tintColor === this._syncedTint) {\n return;\n }\n\n for (const child of this._chunkNodes) {\n this._applyTint(child);\n }\n\n this._syncedOpacity = opacity;\n this._syncedTint = tintColor;\n }\n}\n"],"mappings":"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"}