melonjs 19.3.0 → 19.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/build/application/application.d.ts.map +1 -1
- package/build/application/defaultApplicationSettings.d.ts +1 -0
- package/build/application/defaultApplicationSettings.d.ts.map +1 -1
- package/build/application/settings.d.ts +12 -0
- package/build/application/settings.d.ts.map +1 -1
- package/build/index.js +1063 -121
- package/build/index.js.map +4 -4
- package/build/input/pointerevent.d.ts.map +1 -1
- package/build/level/tiled/TMXLayer.d.ts +72 -5
- package/build/level/tiled/TMXLayer.d.ts.map +1 -1
- package/build/level/tiled/TMXTile.d.ts +19 -1
- package/build/level/tiled/TMXTile.d.ts.map +1 -1
- package/build/level/tiled/TMXTileMap.d.ts.map +1 -1
- package/build/level/tiled/TMXTileset.d.ts +15 -0
- package/build/level/tiled/TMXTileset.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXHexagonalRenderer.d.ts +7 -1
- package/build/level/tiled/renderer/TMXHexagonalRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXIsometricRenderer.d.ts +7 -1
- package/build/level/tiled/renderer/TMXIsometricRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXObliqueRenderer.d.ts.map +1 -1
- package/build/level/tiled/renderer/TMXOrthogonalRenderer.d.ts +7 -1
- package/build/level/tiled/renderer/TMXOrthogonalRenderer.d.ts.map +1 -1
- package/build/physics/sat.d.ts.map +1 -1
- package/build/physics/world.d.ts +16 -0
- package/build/physics/world.d.ts.map +1 -1
- package/build/renderable/imagelayer.d.ts.map +1 -1
- package/build/utils/function.d.ts +1 -1
- package/build/utils/function.d.ts.map +1 -1
- package/build/video/renderer.d.ts +15 -1
- package/build/video/renderer.d.ts.map +1 -1
- package/build/video/texture/cache.d.ts.map +1 -1
- package/build/video/texture/resource.d.ts +113 -0
- package/build/video/texture/resource.d.ts.map +1 -0
- package/build/video/webgl/batchers/material_batcher.d.ts +8 -1
- package/build/video/webgl/batchers/material_batcher.d.ts.map +1 -1
- package/build/video/webgl/renderers/tmxlayer/orthogonal.d.ts +108 -0
- package/build/video/webgl/renderers/tmxlayer/orthogonal.d.ts.map +1 -0
- package/build/video/webgl/utils/attributes.d.ts +7 -0
- package/build/video/webgl/utils/attributes.d.ts.map +1 -1
- package/build/video/webgl/utils/precision.d.ts +1 -1
- package/build/video/webgl/utils/precision.d.ts.map +1 -1
- package/build/video/webgl/utils/uniforms.d.ts +13 -0
- package/build/video/webgl/utils/uniforms.d.ts.map +1 -1
- package/build/video/webgl/webgl_renderer.d.ts +19 -0
- package/build/video/webgl/webgl_renderer.d.ts.map +1 -1
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* melonJS Game Engine - 19.
|
|
2
|
+
* melonJS Game Engine - 19.4.0
|
|
3
3
|
* http://www.melonjs.org
|
|
4
4
|
* melonjs is licensed under the MIT License.
|
|
5
5
|
* http://www.opensource.org/licenses/mit-license
|
|
@@ -6249,6 +6249,7 @@ var defaultApplicationSettings = {
|
|
|
6249
6249
|
consoleHeader: true,
|
|
6250
6250
|
blendMode: "normal",
|
|
6251
6251
|
physic: "builtin",
|
|
6252
|
+
gpuTilemap: true,
|
|
6252
6253
|
failIfMajorPerformanceCaveat: true,
|
|
6253
6254
|
highPrecisionShader: true,
|
|
6254
6255
|
subPixel: false,
|
|
@@ -15792,6 +15793,41 @@ var Renderer = class {
|
|
|
15792
15793
|
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
15793
15794
|
drawLight(light) {
|
|
15794
15795
|
}
|
|
15796
|
+
/**
|
|
15797
|
+
* Draw a TMX tile layer. Default behavior:
|
|
15798
|
+
* - if `layer.canvasRenderer` is set (preRender bake), blit the cached
|
|
15799
|
+
* offscreen canvas in a single `drawImage` call;
|
|
15800
|
+
* - otherwise delegate to the layer's TMX orientation renderer for
|
|
15801
|
+
* the per-tile loop.
|
|
15802
|
+
*
|
|
15803
|
+
* `WebGLRenderer` overrides this to add the procedural shader fast
|
|
15804
|
+
* path on top (when `layer.renderMode === "shader"`) and fall through
|
|
15805
|
+
* to this base behavior for all other layers.
|
|
15806
|
+
* @param {object} layer - the TMXLayer to draw
|
|
15807
|
+
* @param {Rect} rect - the visible region in world coords
|
|
15808
|
+
*/
|
|
15809
|
+
drawTileLayer(layer, rect) {
|
|
15810
|
+
if (layer.canvasRenderer) {
|
|
15811
|
+
const width = Math.min(rect.width, layer.width - rect.pos.x);
|
|
15812
|
+
const height = Math.min(rect.height, layer.height - rect.pos.y);
|
|
15813
|
+
if (width <= 0 || height <= 0) {
|
|
15814
|
+
return;
|
|
15815
|
+
}
|
|
15816
|
+
this.drawImage(
|
|
15817
|
+
layer.canvasRenderer.getCanvas(),
|
|
15818
|
+
rect.pos.x,
|
|
15819
|
+
rect.pos.y,
|
|
15820
|
+
width,
|
|
15821
|
+
height,
|
|
15822
|
+
rect.pos.x,
|
|
15823
|
+
rect.pos.y,
|
|
15824
|
+
width,
|
|
15825
|
+
height
|
|
15826
|
+
);
|
|
15827
|
+
return;
|
|
15828
|
+
}
|
|
15829
|
+
layer.getRenderer().drawTileLayer(this, layer, rect);
|
|
15830
|
+
}
|
|
15795
15831
|
/**
|
|
15796
15832
|
* Set the current fill & stroke style color.
|
|
15797
15833
|
* By default, or upon reset, the value is set to #000000.
|
|
@@ -16890,14 +16926,6 @@ var TextureCache = class {
|
|
|
16890
16926
|
* cache the textureAltas for the given image
|
|
16891
16927
|
*/
|
|
16892
16928
|
set(image, textureAtlas) {
|
|
16893
|
-
const width = image.width || image.videoWidth;
|
|
16894
|
-
const height = image.height || image.videoHeight;
|
|
16895
|
-
if (this.renderer.WebGLVersion === 1 && (!isPowerOfTwo(width) || !isPowerOfTwo(height))) {
|
|
16896
|
-
const src = typeof image.src !== "undefined" ? image.src : image;
|
|
16897
|
-
console.warn(
|
|
16898
|
-
"[Texture] " + src + " is not a POT texture (" + width + "x" + height + ")"
|
|
16899
|
-
);
|
|
16900
|
-
}
|
|
16901
16929
|
return this.cache.put(image, textureAtlas);
|
|
16902
16930
|
}
|
|
16903
16931
|
/**
|
|
@@ -18073,6 +18101,30 @@ var CanvasRenderer = class extends Renderer {
|
|
|
18073
18101
|
};
|
|
18074
18102
|
|
|
18075
18103
|
// src/level/tiled/TMXTile.js
|
|
18104
|
+
var FLIP_H_BIT = 1 << 0;
|
|
18105
|
+
var FLIP_V_BIT = 1 << 1;
|
|
18106
|
+
var FLIP_AD_BIT = 1 << 2;
|
|
18107
|
+
function buildFlipTransform(transform, flipMask, width, height) {
|
|
18108
|
+
const halfW = width / 2;
|
|
18109
|
+
const halfH = height / 2;
|
|
18110
|
+
const flippedH = (flipMask & FLIP_H_BIT) !== 0;
|
|
18111
|
+
const flippedV = (flipMask & FLIP_V_BIT) !== 0;
|
|
18112
|
+
const flippedAD = (flipMask & FLIP_AD_BIT) !== 0;
|
|
18113
|
+
transform.identity();
|
|
18114
|
+
transform.translate(halfW, halfH);
|
|
18115
|
+
if (flippedAD) {
|
|
18116
|
+
transform.rotate(degToRad(-90));
|
|
18117
|
+
transform.scale(-1, 1);
|
|
18118
|
+
}
|
|
18119
|
+
if (flippedH) {
|
|
18120
|
+
transform.scale(flippedAD ? 1 : -1, flippedAD ? -1 : 1);
|
|
18121
|
+
}
|
|
18122
|
+
if (flippedV) {
|
|
18123
|
+
transform.scale(flippedAD ? -1 : 1, flippedAD ? 1 : -1);
|
|
18124
|
+
}
|
|
18125
|
+
transform.translate(-halfW, -halfH);
|
|
18126
|
+
return transform;
|
|
18127
|
+
}
|
|
18076
18128
|
var Tile = class extends Bounds {
|
|
18077
18129
|
/**
|
|
18078
18130
|
* @param {number} x - x index of the Tile in the map
|
|
@@ -18623,12 +18675,17 @@ function applyTMXProperties(obj, data2) {
|
|
|
18623
18675
|
}
|
|
18624
18676
|
|
|
18625
18677
|
// src/level/tiled/TMXLayer.js
|
|
18626
|
-
|
|
18627
|
-
|
|
18628
|
-
|
|
18629
|
-
|
|
18630
|
-
|
|
18631
|
-
|
|
18678
|
+
var FLIP_H_BIT2 = 1 << 0;
|
|
18679
|
+
var FLIP_V_BIT2 = 1 << 1;
|
|
18680
|
+
var FLIP_AD_BIT2 = 1 << 2;
|
|
18681
|
+
function flipMaskFromGid(gid) {
|
|
18682
|
+
return (gid & TMX_FLIP_H ? FLIP_H_BIT2 : 0) | (gid & TMX_FLIP_V ? FLIP_V_BIT2 : 0) | (gid & TMX_FLIP_AD ? FLIP_AD_BIT2 : 0);
|
|
18683
|
+
}
|
|
18684
|
+
function flipMaskFromTile(tile) {
|
|
18685
|
+
return (tile.flippedX ? FLIP_H_BIT2 : 0) | (tile.flippedY ? FLIP_V_BIT2 : 0) | (tile.flippedAD ? FLIP_AD_BIT2 : 0);
|
|
18686
|
+
}
|
|
18687
|
+
function gidWithFlips(gid, flipMask) {
|
|
18688
|
+
return gid | (flipMask & FLIP_H_BIT2 ? TMX_FLIP_H : 0) | (flipMask & FLIP_V_BIT2 ? TMX_FLIP_V : 0) | (flipMask & FLIP_AD_BIT2 ? TMX_FLIP_AD : 0);
|
|
18632
18689
|
}
|
|
18633
18690
|
function setLayerData(layer, bounds, data2) {
|
|
18634
18691
|
let idx = 0;
|
|
@@ -18641,18 +18698,30 @@ function setLayerData(layer, bounds, data2) {
|
|
|
18641
18698
|
width = bounds.cols;
|
|
18642
18699
|
height = bounds.rows;
|
|
18643
18700
|
}
|
|
18701
|
+
const cols = layer.cols;
|
|
18702
|
+
const layerData = layer.layerData;
|
|
18703
|
+
const offsetX = bounds.x;
|
|
18704
|
+
const offsetY = bounds.y;
|
|
18705
|
+
let overflowedGid = 0;
|
|
18644
18706
|
for (let y = 0; y < height; y++) {
|
|
18645
18707
|
for (let x = 0; x < width; x++) {
|
|
18646
|
-
const
|
|
18647
|
-
if (
|
|
18648
|
-
|
|
18649
|
-
|
|
18650
|
-
|
|
18651
|
-
|
|
18652
|
-
|
|
18708
|
+
const rawGid = data2[idx++];
|
|
18709
|
+
if (rawGid !== 0) {
|
|
18710
|
+
const flatIdx = ((y + offsetY) * cols + (x + offsetX)) * 2;
|
|
18711
|
+
const cleanGid = rawGid & TMX_CLEAR_BIT_MASK;
|
|
18712
|
+
if (cleanGid > 65535 && overflowedGid === 0) {
|
|
18713
|
+
overflowedGid = cleanGid;
|
|
18714
|
+
}
|
|
18715
|
+
layerData[flatIdx] = cleanGid;
|
|
18716
|
+
layerData[flatIdx + 1] = flipMaskFromGid(rawGid);
|
|
18653
18717
|
}
|
|
18654
18718
|
}
|
|
18655
18719
|
}
|
|
18720
|
+
if (overflowedGid !== 0) {
|
|
18721
|
+
console.warn(
|
|
18722
|
+
"melonJS: TMX layer contains GID " + overflowedGid + " which exceeds the 16-bit cell capacity (max 65535). Tiles will be truncated and render incorrectly."
|
|
18723
|
+
);
|
|
18724
|
+
}
|
|
18656
18725
|
}
|
|
18657
18726
|
var TMXLayer = class extends Renderable {
|
|
18658
18727
|
/**
|
|
@@ -18712,7 +18781,10 @@ var TMXLayer = class extends Renderable {
|
|
|
18712
18781
|
}
|
|
18713
18782
|
applyTMXProperties(this, data2);
|
|
18714
18783
|
this.setRenderer(map.getRenderer());
|
|
18715
|
-
this.layerData =
|
|
18784
|
+
this.layerData = new Uint16Array(this.cols * this.rows * 2);
|
|
18785
|
+
this.cachedTile = null;
|
|
18786
|
+
this.dataVersion = 0;
|
|
18787
|
+
this.renderMode = "auto";
|
|
18716
18788
|
if (map.infinite === 0) {
|
|
18717
18789
|
setLayerData(
|
|
18718
18790
|
this,
|
|
@@ -18742,12 +18814,8 @@ var TMXLayer = class extends Renderable {
|
|
|
18742
18814
|
}
|
|
18743
18815
|
}
|
|
18744
18816
|
this.isAnimated = this.animatedTilesets.length > 0;
|
|
18745
|
-
|
|
18746
|
-
|
|
18747
|
-
} else {
|
|
18748
|
-
this.preRender = false;
|
|
18749
|
-
}
|
|
18750
|
-
if (this.preRender === true && !this.canvasRenderer) {
|
|
18817
|
+
this._resolveRenderMode();
|
|
18818
|
+
if (this.renderMode === "prerender" && !this.canvasRenderer) {
|
|
18751
18819
|
this.canvasRenderer = new CanvasRenderer({
|
|
18752
18820
|
canvas: createCanvas(this.width, this.height),
|
|
18753
18821
|
width: this.width,
|
|
@@ -18756,8 +18824,109 @@ var TMXLayer = class extends Renderable {
|
|
|
18756
18824
|
});
|
|
18757
18825
|
this.getRenderer().drawTileLayer(this.canvasRenderer, this, this);
|
|
18758
18826
|
}
|
|
18827
|
+
this.preRender = this.renderMode === "prerender";
|
|
18759
18828
|
this.isDirty = true;
|
|
18760
18829
|
}
|
|
18830
|
+
/**
|
|
18831
|
+
* Resolve `this.renderMode` to one of "shader" / "prerender" / "perTile"
|
|
18832
|
+
* based on eligibility checks and user/world hints. Emits a single
|
|
18833
|
+
* `console.warn` at activation when a forced mode is ineligible, or
|
|
18834
|
+
* when an auto-eligible mode falls back due to a layer feature the GPU
|
|
18835
|
+
* path doesn't support (orientation, collection-of-image tileset, etc.).
|
|
18836
|
+
* @ignore
|
|
18837
|
+
*/
|
|
18838
|
+
_resolveRenderMode() {
|
|
18839
|
+
const root = this.ancestor?.getRootAncestor?.();
|
|
18840
|
+
const renderer2 = this.parentApp?.renderer;
|
|
18841
|
+
const gpuAllowed = root?.gpuTilemap !== false;
|
|
18842
|
+
const preRenderHint = typeof this.preRender === "boolean" ? this.preRender : root?.preRender;
|
|
18843
|
+
const elig = this._checkShaderEligibility(renderer2, gpuAllowed);
|
|
18844
|
+
const requested = this.renderMode;
|
|
18845
|
+
if (requested === "shader") {
|
|
18846
|
+
if (elig.ok) {
|
|
18847
|
+
return;
|
|
18848
|
+
}
|
|
18849
|
+
console.warn(
|
|
18850
|
+
`melonJS: layer "${this.name}" forced renderMode "shader" not available (${elig.reason}) \u2014 falling back to perTile`
|
|
18851
|
+
);
|
|
18852
|
+
this.renderMode = "perTile";
|
|
18853
|
+
return;
|
|
18854
|
+
}
|
|
18855
|
+
if (requested === "prerender") {
|
|
18856
|
+
if (this.isAnimated) {
|
|
18857
|
+
console.warn(
|
|
18858
|
+
`melonJS: layer "${this.name}" forced renderMode "prerender" disabled (layer has animated tiles) \u2014 falling back to perTile`
|
|
18859
|
+
);
|
|
18860
|
+
this.renderMode = "perTile";
|
|
18861
|
+
}
|
|
18862
|
+
return;
|
|
18863
|
+
}
|
|
18864
|
+
if (requested === "perTile") {
|
|
18865
|
+
return;
|
|
18866
|
+
}
|
|
18867
|
+
if (elig.ok) {
|
|
18868
|
+
this.renderMode = "shader";
|
|
18869
|
+
return;
|
|
18870
|
+
}
|
|
18871
|
+
if (gpuAllowed && elig.reason !== "no-webgl2-renderer") {
|
|
18872
|
+
console.warn(
|
|
18873
|
+
`melonJS: layer "${this.name}" using legacy tile renderer (${elig.reason})`
|
|
18874
|
+
);
|
|
18875
|
+
}
|
|
18876
|
+
if (preRenderHint && !this.isAnimated) {
|
|
18877
|
+
this.renderMode = "prerender";
|
|
18878
|
+
return;
|
|
18879
|
+
}
|
|
18880
|
+
this.renderMode = "perTile";
|
|
18881
|
+
}
|
|
18882
|
+
/**
|
|
18883
|
+
* Check whether this layer is eligible for the WebGL2 shader path.
|
|
18884
|
+
* @param {object} renderer
|
|
18885
|
+
* @param {boolean} gpuAllowed - whether `gpuTilemap` is enabled at the world level
|
|
18886
|
+
* @returns {{ok: boolean, reason?: string}}
|
|
18887
|
+
* @ignore
|
|
18888
|
+
*/
|
|
18889
|
+
_checkShaderEligibility(renderer2, gpuAllowed) {
|
|
18890
|
+
if (!gpuAllowed) {
|
|
18891
|
+
return { ok: false, reason: "gpuTilemap disabled" };
|
|
18892
|
+
}
|
|
18893
|
+
if (!renderer2 || renderer2.WebGLVersion !== 2) {
|
|
18894
|
+
return { ok: false, reason: "no-webgl2-renderer" };
|
|
18895
|
+
}
|
|
18896
|
+
if (this.orientation !== "orthogonal") {
|
|
18897
|
+
return {
|
|
18898
|
+
ok: false,
|
|
18899
|
+
reason: `no gpu renderer supported yet for "${this.orientation}" orientation`
|
|
18900
|
+
};
|
|
18901
|
+
}
|
|
18902
|
+
if (!this.tilesets || this.tilesets.tilesets.length === 0) {
|
|
18903
|
+
return { ok: false, reason: "no tilesets" };
|
|
18904
|
+
}
|
|
18905
|
+
const MAX_OVERFLOW_CELLS = 4;
|
|
18906
|
+
for (const ts of this.tilesets.tilesets) {
|
|
18907
|
+
if (ts.isCollection) {
|
|
18908
|
+
return { ok: false, reason: "collection-of-image tileset" };
|
|
18909
|
+
}
|
|
18910
|
+
if (ts.tilerendersize !== "tile") {
|
|
18911
|
+
return {
|
|
18912
|
+
ok: false,
|
|
18913
|
+
reason: `tilerendersize "${ts.tilerendersize}" not supported`
|
|
18914
|
+
};
|
|
18915
|
+
}
|
|
18916
|
+
if (ts.tileoffset.x !== 0 || ts.tileoffset.y !== 0) {
|
|
18917
|
+
return { ok: false, reason: "non-zero tileoffset" };
|
|
18918
|
+
}
|
|
18919
|
+
const overflowX = Math.ceil(ts.tilewidth / this.tilewidth) - 1;
|
|
18920
|
+
const overflowY = Math.ceil(ts.tileheight / this.tileheight) - 1;
|
|
18921
|
+
if (overflowX > MAX_OVERFLOW_CELLS || overflowY > MAX_OVERFLOW_CELLS) {
|
|
18922
|
+
return {
|
|
18923
|
+
ok: false,
|
|
18924
|
+
reason: `tile overflow exceeds shader limit (${MAX_OVERFLOW_CELLS} cells)`
|
|
18925
|
+
};
|
|
18926
|
+
}
|
|
18927
|
+
}
|
|
18928
|
+
return { ok: true };
|
|
18929
|
+
}
|
|
18761
18930
|
// called when the layer is removed from the game world or a container
|
|
18762
18931
|
onDeactivateEvent() {
|
|
18763
18932
|
this.animatedTilesets = void 0;
|
|
@@ -18818,12 +18987,29 @@ var TMXLayer = class extends Renderable {
|
|
|
18818
18987
|
/**
|
|
18819
18988
|
* assign the given Tile object to the specified position
|
|
18820
18989
|
* @param {Tile} tile - the tile object to be assigned
|
|
18821
|
-
* @param {number} x - x coordinate (in
|
|
18822
|
-
* @param {number} y - y coordinate (in
|
|
18990
|
+
* @param {number} x - x coordinate (in tile/column coordinates)
|
|
18991
|
+
* @param {number} y - y coordinate (in tile/row coordinates)
|
|
18823
18992
|
* @returns {Tile} the tile object
|
|
18824
18993
|
*/
|
|
18825
18994
|
setTile(tile, x, y) {
|
|
18826
|
-
this.
|
|
18995
|
+
if (x < 0 || x >= this.cols || y < 0 || y >= this.rows) {
|
|
18996
|
+
return tile;
|
|
18997
|
+
}
|
|
18998
|
+
const slot = y * this.cols + x;
|
|
18999
|
+
const idx = slot * 2;
|
|
19000
|
+
const cleanGid = tile.tileId & TMX_CLEAR_BIT_MASK;
|
|
19001
|
+
if (cleanGid > 65535 && !this._truncationWarned) {
|
|
19002
|
+
this._truncationWarned = true;
|
|
19003
|
+
console.warn(
|
|
19004
|
+
"melonJS: setTile received GID " + cleanGid + " which exceeds the 16-bit cell capacity (max 65535). Tile will be truncated and render incorrectly."
|
|
19005
|
+
);
|
|
19006
|
+
}
|
|
19007
|
+
this.layerData[idx] = cleanGid;
|
|
19008
|
+
this.layerData[idx + 1] = flipMaskFromTile(tile);
|
|
19009
|
+
if (this.cachedTile !== null) {
|
|
19010
|
+
this.cachedTile[slot] = tile;
|
|
19011
|
+
}
|
|
19012
|
+
this.dataVersion++;
|
|
18827
19013
|
this.isDirty = true;
|
|
18828
19014
|
return tile;
|
|
18829
19015
|
}
|
|
@@ -18853,12 +19039,27 @@ var TMXLayer = class extends Renderable {
|
|
|
18853
19039
|
cellAt(x, y, boundsCheck) {
|
|
18854
19040
|
const _x = ~~x;
|
|
18855
19041
|
const _y = ~~y;
|
|
18856
|
-
|
|
18857
|
-
|
|
18858
|
-
|
|
18859
|
-
|
|
19042
|
+
if (boundsCheck !== false && (_x < 0 || _x >= this.cols || _y < 0 || _y >= this.rows)) {
|
|
19043
|
+
return null;
|
|
19044
|
+
}
|
|
19045
|
+
const slot = _y * this.cols + _x;
|
|
19046
|
+
const idx = slot * 2;
|
|
19047
|
+
const gid = this.layerData[idx];
|
|
19048
|
+
if (!gid) {
|
|
18860
19049
|
return null;
|
|
18861
19050
|
}
|
|
19051
|
+
if (this.cachedTile === null) {
|
|
19052
|
+
this.cachedTile = new Array(this.cols * this.rows).fill(null);
|
|
19053
|
+
} else {
|
|
19054
|
+
const cached = this.cachedTile[slot];
|
|
19055
|
+
if (cached !== null) {
|
|
19056
|
+
return cached;
|
|
19057
|
+
}
|
|
19058
|
+
}
|
|
19059
|
+
const flipMask = this.layerData[idx + 1];
|
|
19060
|
+
const tile = this.getTileById(gidWithFlips(gid, flipMask), _x, _y);
|
|
19061
|
+
this.cachedTile[slot] = tile;
|
|
19062
|
+
return tile;
|
|
18862
19063
|
}
|
|
18863
19064
|
/**
|
|
18864
19065
|
* clear the tile at the specified position
|
|
@@ -18871,7 +19072,16 @@ var TMXLayer = class extends Renderable {
|
|
|
18871
19072
|
* });
|
|
18872
19073
|
*/
|
|
18873
19074
|
clearTile(x, y) {
|
|
18874
|
-
this.
|
|
19075
|
+
if (x < 0 || x >= this.cols || y < 0 || y >= this.rows) {
|
|
19076
|
+
return;
|
|
19077
|
+
}
|
|
19078
|
+
const slot = y * this.cols + x;
|
|
19079
|
+
const idx = slot * 2;
|
|
19080
|
+
this.layerData[idx] = 0;
|
|
19081
|
+
this.layerData[idx + 1] = 0;
|
|
19082
|
+
if (this.cachedTile !== null) {
|
|
19083
|
+
this.cachedTile[slot] = null;
|
|
19084
|
+
}
|
|
18875
19085
|
if (this.preRender) {
|
|
18876
19086
|
this.canvasRenderer.clearRect(
|
|
18877
19087
|
x * this.tilewidth,
|
|
@@ -18880,6 +19090,7 @@ var TMXLayer = class extends Renderable {
|
|
|
18880
19090
|
this.tileheight
|
|
18881
19091
|
);
|
|
18882
19092
|
}
|
|
19093
|
+
this.dataVersion++;
|
|
18883
19094
|
this.isDirty = true;
|
|
18884
19095
|
}
|
|
18885
19096
|
/**
|
|
@@ -18900,27 +19111,7 @@ var TMXLayer = class extends Renderable {
|
|
|
18900
19111
|
* @ignore
|
|
18901
19112
|
*/
|
|
18902
19113
|
draw(renderer2, rect) {
|
|
18903
|
-
|
|
18904
|
-
const width = Math.min(rect.width, this.width);
|
|
18905
|
-
const height = Math.min(rect.height, this.height);
|
|
18906
|
-
renderer2.drawImage(
|
|
18907
|
-
this.canvasRenderer.getCanvas(),
|
|
18908
|
-
rect.pos.x,
|
|
18909
|
-
rect.pos.y,
|
|
18910
|
-
// sx,sy
|
|
18911
|
-
width,
|
|
18912
|
-
height,
|
|
18913
|
-
// sw,sh
|
|
18914
|
-
rect.pos.x,
|
|
18915
|
-
rect.pos.y,
|
|
18916
|
-
// dx,dy
|
|
18917
|
-
width,
|
|
18918
|
-
height
|
|
18919
|
-
// dw,dh
|
|
18920
|
-
);
|
|
18921
|
-
} else {
|
|
18922
|
-
this.getRenderer().drawTileLayer(renderer2, this, rect);
|
|
18923
|
-
}
|
|
19114
|
+
renderer2.drawTileLayer(this, rect);
|
|
18924
19115
|
}
|
|
18925
19116
|
};
|
|
18926
19117
|
|
|
@@ -19284,7 +19475,7 @@ var TMXHexagonalRenderer = class extends TMXRenderer {
|
|
|
19284
19475
|
return ret;
|
|
19285
19476
|
}
|
|
19286
19477
|
/**
|
|
19287
|
-
* draw the tile map
|
|
19478
|
+
* draw the tile map (legacy entry point — accepts a fully-constructed Tile)
|
|
19288
19479
|
* @ignore
|
|
19289
19480
|
*/
|
|
19290
19481
|
drawTile(renderer2, x, y, tmxTile) {
|
|
@@ -19298,12 +19489,27 @@ var TMXHexagonalRenderer = class extends TMXRenderer {
|
|
|
19298
19489
|
);
|
|
19299
19490
|
vector2dPool.release(point);
|
|
19300
19491
|
}
|
|
19492
|
+
/**
|
|
19493
|
+
* draw a tile from raw (gid, flipMask, tileset) data — used by the hot
|
|
19494
|
+
* rendering loop to bypass Tile construction
|
|
19495
|
+
* @ignore
|
|
19496
|
+
*/
|
|
19497
|
+
drawTileRaw(renderer2, x, y, gid, flipMask, tileset) {
|
|
19498
|
+
const point = this.tileToPixelCoords(x, y, vector2dPool.get());
|
|
19499
|
+
tileset.drawTileRaw(
|
|
19500
|
+
renderer2,
|
|
19501
|
+
tileset.tileoffset.x + point.x,
|
|
19502
|
+
tileset.tileoffset.y + point.y + (this.tileheight - tileset.tileheight),
|
|
19503
|
+
gid,
|
|
19504
|
+
flipMask
|
|
19505
|
+
);
|
|
19506
|
+
vector2dPool.release(point);
|
|
19507
|
+
}
|
|
19301
19508
|
/**
|
|
19302
19509
|
* draw the tile map
|
|
19303
19510
|
* @ignore
|
|
19304
19511
|
*/
|
|
19305
19512
|
drawTileLayer(renderer2, layer, rect) {
|
|
19306
|
-
let tile;
|
|
19307
19513
|
const startTile = this.pixelToTileCoords(
|
|
19308
19514
|
rect.pos.x,
|
|
19309
19515
|
rect.pos.y,
|
|
@@ -19327,6 +19533,15 @@ var TMXHexagonalRenderer = class extends TMXRenderer {
|
|
|
19327
19533
|
}
|
|
19328
19534
|
const endX = layer.cols;
|
|
19329
19535
|
const endY = layer.rows;
|
|
19536
|
+
const layerCols = layer.cols;
|
|
19537
|
+
const data2 = layer.layerData;
|
|
19538
|
+
const tilesets = layer.tilesets;
|
|
19539
|
+
let tilesetCache = layer.tileset;
|
|
19540
|
+
if (tilesetCache === null) {
|
|
19541
|
+
vector2dPool.release(startTile);
|
|
19542
|
+
vector2dPool.release(startPos);
|
|
19543
|
+
return;
|
|
19544
|
+
}
|
|
19330
19545
|
if (this.staggerX) {
|
|
19331
19546
|
startTile.x = Math.max(0, startTile.x);
|
|
19332
19547
|
startTile.y = Math.max(0, startTile.y);
|
|
@@ -19340,9 +19555,22 @@ var TMXHexagonalRenderer = class extends TMXRenderer {
|
|
|
19340
19555
|
rowTile.setV(startTile);
|
|
19341
19556
|
rowPos.setV(startPos);
|
|
19342
19557
|
for (; rowPos.x < rect.right && rowTile.x < endX; rowTile.x += 2) {
|
|
19343
|
-
|
|
19344
|
-
|
|
19345
|
-
|
|
19558
|
+
if (rowTile.x >= 0 && rowTile.y >= 0 && rowTile.y < layer.rows) {
|
|
19559
|
+
const idx = (rowTile.y * layerCols + rowTile.x) * 2;
|
|
19560
|
+
const gid = data2[idx];
|
|
19561
|
+
if (gid) {
|
|
19562
|
+
const flipMask = data2[idx + 1];
|
|
19563
|
+
if (!tilesetCache.contains(gid)) {
|
|
19564
|
+
tilesetCache = tilesets.getTilesetByGid(gid);
|
|
19565
|
+
}
|
|
19566
|
+
tilesetCache.drawTileRaw(
|
|
19567
|
+
renderer2,
|
|
19568
|
+
rowPos.x,
|
|
19569
|
+
rowPos.y,
|
|
19570
|
+
gid,
|
|
19571
|
+
flipMask
|
|
19572
|
+
);
|
|
19573
|
+
}
|
|
19346
19574
|
}
|
|
19347
19575
|
rowPos.x += this.tilewidth + this.sidelengthx;
|
|
19348
19576
|
}
|
|
@@ -19378,9 +19606,22 @@ var TMXHexagonalRenderer = class extends TMXRenderer {
|
|
|
19378
19606
|
rowPos.x += this.columnwidth;
|
|
19379
19607
|
}
|
|
19380
19608
|
for (; rowPos.x < rect.right && rowTile.x < endX; rowTile.x++) {
|
|
19381
|
-
|
|
19382
|
-
|
|
19383
|
-
|
|
19609
|
+
if (rowTile.x >= 0 && rowTile.y >= 0 && rowTile.y < layer.rows) {
|
|
19610
|
+
const idx = (rowTile.y * layerCols + rowTile.x) * 2;
|
|
19611
|
+
const gid = data2[idx];
|
|
19612
|
+
if (gid) {
|
|
19613
|
+
const flipMask = data2[idx + 1];
|
|
19614
|
+
if (!tilesetCache.contains(gid)) {
|
|
19615
|
+
tilesetCache = tilesets.getTilesetByGid(gid);
|
|
19616
|
+
}
|
|
19617
|
+
tilesetCache.drawTileRaw(
|
|
19618
|
+
renderer2,
|
|
19619
|
+
rowPos.x,
|
|
19620
|
+
rowPos.y,
|
|
19621
|
+
gid,
|
|
19622
|
+
flipMask
|
|
19623
|
+
);
|
|
19624
|
+
}
|
|
19384
19625
|
}
|
|
19385
19626
|
rowPos.x += this.tilewidth + this.sidelengthx;
|
|
19386
19627
|
}
|
|
@@ -19463,7 +19704,7 @@ var TMXIsometricRenderer = class extends TMXRenderer {
|
|
|
19463
19704
|
vector2dPool.release(isoPos);
|
|
19464
19705
|
}
|
|
19465
19706
|
/**
|
|
19466
|
-
* draw the tile map
|
|
19707
|
+
* draw the tile map (legacy entry point — accepts a fully-constructed Tile)
|
|
19467
19708
|
* @ignore
|
|
19468
19709
|
*/
|
|
19469
19710
|
drawTile(renderer2, x, y, tmxTile) {
|
|
@@ -19475,6 +19716,20 @@ var TMXIsometricRenderer = class extends TMXRenderer {
|
|
|
19475
19716
|
tmxTile
|
|
19476
19717
|
);
|
|
19477
19718
|
}
|
|
19719
|
+
/**
|
|
19720
|
+
* draw a tile from raw (gid, flipMask, tileset) data — used by the hot
|
|
19721
|
+
* rendering loop to bypass Tile construction
|
|
19722
|
+
* @ignore
|
|
19723
|
+
*/
|
|
19724
|
+
drawTileRaw(renderer2, x, y, gid, flipMask, tileset) {
|
|
19725
|
+
tileset.drawTileRaw(
|
|
19726
|
+
renderer2,
|
|
19727
|
+
(this.cols - 1) * tileset.tilewidth + (x - y) * tileset.tilewidth >> 1,
|
|
19728
|
+
-tileset.tilewidth + (x + y) * tileset.tileheight >> 2,
|
|
19729
|
+
gid,
|
|
19730
|
+
flipMask
|
|
19731
|
+
);
|
|
19732
|
+
}
|
|
19478
19733
|
/**
|
|
19479
19734
|
* draw the tile map
|
|
19480
19735
|
* @ignore
|
|
@@ -19517,19 +19772,41 @@ var TMXIsometricRenderer = class extends TMXRenderer {
|
|
|
19517
19772
|
}
|
|
19518
19773
|
let shifted = inUpperHalf ^ inLeftHalf;
|
|
19519
19774
|
const columnItr = vector2dPool.get().setV(rowItr);
|
|
19775
|
+
const layerCols = layer.cols;
|
|
19776
|
+
const layerRows = layer.rows;
|
|
19777
|
+
const data2 = layer.layerData;
|
|
19778
|
+
const tilesets = layer.tilesets;
|
|
19779
|
+
let tilesetCache = tileset;
|
|
19780
|
+
if (tilesetCache === null) {
|
|
19781
|
+
vector2dPool.release(columnItr);
|
|
19782
|
+
vector2dPool.release(rowItr);
|
|
19783
|
+
vector2dPool.release(tileEnd);
|
|
19784
|
+
vector2dPool.release(rectEnd);
|
|
19785
|
+
vector2dPool.release(startPos);
|
|
19786
|
+
return;
|
|
19787
|
+
}
|
|
19520
19788
|
for (let y = startPos.y * 2; y - this.tileheight * 2 < rectEnd.y * 2; y += this.tileheight) {
|
|
19521
19789
|
columnItr.setV(rowItr);
|
|
19522
19790
|
for (let x = startPos.x; x < rectEnd.x; x += this.tilewidth) {
|
|
19523
|
-
const
|
|
19524
|
-
|
|
19525
|
-
|
|
19526
|
-
const
|
|
19527
|
-
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
|
|
19531
|
-
|
|
19532
|
-
|
|
19791
|
+
const cx = columnItr.x;
|
|
19792
|
+
const cy = columnItr.y;
|
|
19793
|
+
if (cx >= 0 && cx < layerCols && cy >= 0 && cy < layerRows) {
|
|
19794
|
+
const idx = (cy * layerCols + cx) * 2;
|
|
19795
|
+
const gid = data2[idx];
|
|
19796
|
+
if (gid !== 0) {
|
|
19797
|
+
const flipMask = data2[idx + 1];
|
|
19798
|
+
if (!tilesetCache.contains(gid)) {
|
|
19799
|
+
tilesetCache = tilesets.getTilesetByGid(gid);
|
|
19800
|
+
}
|
|
19801
|
+
const offset = tilesetCache.tileoffset;
|
|
19802
|
+
tilesetCache.drawTileRaw(
|
|
19803
|
+
renderer2,
|
|
19804
|
+
offset.x + x,
|
|
19805
|
+
offset.y + y / 2 - tilesetCache.tileheight,
|
|
19806
|
+
gid,
|
|
19807
|
+
flipMask
|
|
19808
|
+
);
|
|
19809
|
+
}
|
|
19533
19810
|
}
|
|
19534
19811
|
columnItr.x++;
|
|
19535
19812
|
columnItr.y--;
|
|
@@ -19584,7 +19861,7 @@ var TMXOrthogonalRenderer = class extends TMXRenderer {
|
|
|
19584
19861
|
return ret.set(x * this.tilewidth, y * this.tileheight);
|
|
19585
19862
|
}
|
|
19586
19863
|
/**
|
|
19587
|
-
* draw the tile map
|
|
19864
|
+
* draw the tile map (legacy entry point — accepts a fully-constructed Tile)
|
|
19588
19865
|
* @ignore
|
|
19589
19866
|
*/
|
|
19590
19867
|
drawTile(renderer2, x, y, tmxTile) {
|
|
@@ -19596,6 +19873,20 @@ var TMXOrthogonalRenderer = class extends TMXRenderer {
|
|
|
19596
19873
|
tmxTile
|
|
19597
19874
|
);
|
|
19598
19875
|
}
|
|
19876
|
+
/**
|
|
19877
|
+
* draw a tile from raw (gid, flipMask, tileset) data — used by the hot
|
|
19878
|
+
* rendering loop to bypass Tile construction
|
|
19879
|
+
* @ignore
|
|
19880
|
+
*/
|
|
19881
|
+
drawTileRaw(renderer2, x, y, gid, flipMask, tileset) {
|
|
19882
|
+
tileset.drawTileRaw(
|
|
19883
|
+
renderer2,
|
|
19884
|
+
tileset.tileoffset.x + x * this.tilewidth,
|
|
19885
|
+
tileset.tileoffset.y + (y + 1) * this.tileheight - tileset.tileheight,
|
|
19886
|
+
gid,
|
|
19887
|
+
flipMask
|
|
19888
|
+
);
|
|
19889
|
+
}
|
|
19599
19890
|
/**
|
|
19600
19891
|
* draw the tile map
|
|
19601
19892
|
* @ignore
|
|
@@ -19633,12 +19924,27 @@ var TMXOrthogonalRenderer = class extends TMXRenderer {
|
|
|
19633
19924
|
default:
|
|
19634
19925
|
break;
|
|
19635
19926
|
}
|
|
19927
|
+
const cols = layer.cols;
|
|
19928
|
+
const data2 = layer.layerData;
|
|
19929
|
+
const tilesets = layer.tilesets;
|
|
19930
|
+
let tilesetCache = layer.tileset;
|
|
19931
|
+
if (tilesetCache === null) {
|
|
19932
|
+
vector2dPool.release(start);
|
|
19933
|
+
vector2dPool.release(end);
|
|
19934
|
+
return;
|
|
19935
|
+
}
|
|
19636
19936
|
for (let y = start.y; y !== end.y; y += incY) {
|
|
19637
19937
|
for (let x = start.x; x !== end.x; x += incX) {
|
|
19638
|
-
const
|
|
19639
|
-
|
|
19640
|
-
|
|
19938
|
+
const idx = (y * cols + x) * 2;
|
|
19939
|
+
const gid = data2[idx];
|
|
19940
|
+
if (!gid) {
|
|
19941
|
+
continue;
|
|
19641
19942
|
}
|
|
19943
|
+
const flipMask = data2[idx + 1];
|
|
19944
|
+
if (!tilesetCache.contains(gid)) {
|
|
19945
|
+
tilesetCache = tilesets.getTilesetByGid(gid);
|
|
19946
|
+
}
|
|
19947
|
+
this.drawTileRaw(renderer2, x, y, gid, flipMask, tilesetCache);
|
|
19642
19948
|
}
|
|
19643
19949
|
}
|
|
19644
19950
|
vector2dPool.release(start);
|
|
@@ -19707,7 +20013,7 @@ var TMXObliqueRenderer = class extends TMXOrthogonalRenderer {
|
|
|
19707
20013
|
return ret.set(px + this.shearX * py, this.shearY * px + py);
|
|
19708
20014
|
}
|
|
19709
20015
|
/**
|
|
19710
|
-
* draw the tile map
|
|
20016
|
+
* draw the tile map (legacy entry point — accepts a fully-constructed Tile)
|
|
19711
20017
|
* @ignore
|
|
19712
20018
|
*/
|
|
19713
20019
|
drawTile(renderer2, x, y, tmxTile) {
|
|
@@ -19716,6 +20022,16 @@ var TMXObliqueRenderer = class extends TMXOrthogonalRenderer {
|
|
|
19716
20022
|
const dy = tileset.tileoffset.y + (y + 1) * this.tileheight - tileset.tileheight + this.skewY * x;
|
|
19717
20023
|
tileset.drawTile(renderer2, dx, dy, tmxTile);
|
|
19718
20024
|
}
|
|
20025
|
+
/**
|
|
20026
|
+
* draw a tile from raw (gid, flipMask, tileset) data — used by the hot
|
|
20027
|
+
* rendering loop to bypass Tile construction
|
|
20028
|
+
* @ignore
|
|
20029
|
+
*/
|
|
20030
|
+
drawTileRaw(renderer2, x, y, gid, flipMask, tileset) {
|
|
20031
|
+
const dx = tileset.tileoffset.x + x * this.tilewidth + this.skewX * y;
|
|
20032
|
+
const dy = tileset.tileoffset.y + (y + 1) * this.tileheight - tileset.tileheight + this.skewY * x;
|
|
20033
|
+
tileset.drawTileRaw(renderer2, dx, dy, gid, flipMask);
|
|
20034
|
+
}
|
|
19719
20035
|
/**
|
|
19720
20036
|
* draw the given TMX Layer for the given area
|
|
19721
20037
|
* @ignore
|
|
@@ -19786,12 +20102,25 @@ var TMXObliqueRenderer = class extends TMXOrthogonalRenderer {
|
|
|
19786
20102
|
default:
|
|
19787
20103
|
break;
|
|
19788
20104
|
}
|
|
20105
|
+
const cols = layer.cols;
|
|
20106
|
+
const data2 = layer.layerData;
|
|
20107
|
+
const tilesets = layer.tilesets;
|
|
20108
|
+
let tilesetCache = layer.tileset;
|
|
20109
|
+
if (tilesetCache === null) {
|
|
20110
|
+
return;
|
|
20111
|
+
}
|
|
19789
20112
|
for (let y = startY; y !== endY; y += incY) {
|
|
19790
20113
|
for (let x = startX; x !== endX; x += incX) {
|
|
19791
|
-
const
|
|
19792
|
-
|
|
19793
|
-
|
|
20114
|
+
const idx = (y * cols + x) * 2;
|
|
20115
|
+
const gid = data2[idx];
|
|
20116
|
+
if (!gid) {
|
|
20117
|
+
continue;
|
|
20118
|
+
}
|
|
20119
|
+
const flipMask = data2[idx + 1];
|
|
20120
|
+
if (!tilesetCache.contains(gid)) {
|
|
20121
|
+
tilesetCache = tilesets.getTilesetByGid(gid);
|
|
19794
20122
|
}
|
|
20123
|
+
this.drawTileRaw(renderer2, x, y, gid, flipMask, tilesetCache);
|
|
19795
20124
|
}
|
|
19796
20125
|
}
|
|
19797
20126
|
}
|
|
@@ -20147,6 +20476,7 @@ var TMXGroup = class {
|
|
|
20147
20476
|
};
|
|
20148
20477
|
|
|
20149
20478
|
// src/level/tiled/TMXTileset.js
|
|
20479
|
+
var SCRATCH_MATRIX = new Matrix2d();
|
|
20150
20480
|
var TMXTileset = class {
|
|
20151
20481
|
/**
|
|
20152
20482
|
* @param {object} tileset - tileset data in JSON format ({@link http://docs.mapeditor.org/en/stable/reference/tmx-map-format/#tileset})
|
|
@@ -20477,6 +20807,96 @@ var TMXTileset = class {
|
|
|
20477
20807
|
renderer2.restore();
|
|
20478
20808
|
}
|
|
20479
20809
|
}
|
|
20810
|
+
/**
|
|
20811
|
+
* draw a tile at the specified position from raw (gid, flipMask) data
|
|
20812
|
+
*
|
|
20813
|
+
* Like {@link drawTile} but bypasses the {@link Tile} object entirely:
|
|
20814
|
+
* the renderer hot loop can pass the GID and flip mask straight from
|
|
20815
|
+
* `layer.layerData` without ever allocating a Tile instance.
|
|
20816
|
+
*
|
|
20817
|
+
* @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance
|
|
20818
|
+
* @param {number} dx - destination x position
|
|
20819
|
+
* @param {number} dy - destination y position
|
|
20820
|
+
* @param {number} gid - the tile's global id (with flip bits already stripped)
|
|
20821
|
+
* @param {number} flipMask - 3-bit packed flip mask (H=1, V=2, AD=4)
|
|
20822
|
+
* @ignore
|
|
20823
|
+
*/
|
|
20824
|
+
drawTileRaw(renderer2, dx, dy, gid, flipMask) {
|
|
20825
|
+
let dw, dh;
|
|
20826
|
+
let tileImage;
|
|
20827
|
+
if (this.isCollection) {
|
|
20828
|
+
tileImage = this.imageCollection.get(gid);
|
|
20829
|
+
const tileWidth = tileImage.width;
|
|
20830
|
+
const tileHeight = tileImage.height;
|
|
20831
|
+
if (this.tilerendersize === "grid") {
|
|
20832
|
+
let scaleX = this.mapTilewidth / tileWidth;
|
|
20833
|
+
let scaleY = this.mapTileheight / tileHeight;
|
|
20834
|
+
if (this.fillmode === "preserve-aspect-fit") {
|
|
20835
|
+
const scale2 = Math.min(scaleX, scaleY);
|
|
20836
|
+
scaleX = scale2;
|
|
20837
|
+
scaleY = scale2;
|
|
20838
|
+
}
|
|
20839
|
+
dw = tileWidth * scaleX;
|
|
20840
|
+
dh = tileHeight * scaleY;
|
|
20841
|
+
dy += this.tileheight - dh;
|
|
20842
|
+
if (this.fillmode === "preserve-aspect-fit") {
|
|
20843
|
+
dx += (this.mapTilewidth - dw) / 2;
|
|
20844
|
+
dy -= (this.mapTileheight - dh) / 2;
|
|
20845
|
+
}
|
|
20846
|
+
} else {
|
|
20847
|
+
dw = tileWidth;
|
|
20848
|
+
dh = tileHeight;
|
|
20849
|
+
}
|
|
20850
|
+
} else {
|
|
20851
|
+
dw = this._renderDw;
|
|
20852
|
+
dh = this._renderDh;
|
|
20853
|
+
dy += this._renderDyOffset;
|
|
20854
|
+
dx += this._renderDxCenter;
|
|
20855
|
+
dy += this._renderDyCenter;
|
|
20856
|
+
}
|
|
20857
|
+
if (flipMask !== 0) {
|
|
20858
|
+
renderer2.save();
|
|
20859
|
+
renderer2.translate(dx, dy);
|
|
20860
|
+
renderer2.transform(
|
|
20861
|
+
buildFlipTransform(
|
|
20862
|
+
SCRATCH_MATRIX,
|
|
20863
|
+
flipMask,
|
|
20864
|
+
this.isCollection ? tileImage.width : this.tilewidth,
|
|
20865
|
+
this.isCollection ? tileImage.height : this.tileheight
|
|
20866
|
+
)
|
|
20867
|
+
);
|
|
20868
|
+
dx = dy = 0;
|
|
20869
|
+
}
|
|
20870
|
+
if (this.isCollection) {
|
|
20871
|
+
renderer2.drawImage(
|
|
20872
|
+
tileImage,
|
|
20873
|
+
0,
|
|
20874
|
+
0,
|
|
20875
|
+
tileImage.width,
|
|
20876
|
+
tileImage.height,
|
|
20877
|
+
dx,
|
|
20878
|
+
dy,
|
|
20879
|
+
dw,
|
|
20880
|
+
dh
|
|
20881
|
+
);
|
|
20882
|
+
} else {
|
|
20883
|
+
const offset = this.atlas[this.getViewTileId(gid)].offset;
|
|
20884
|
+
renderer2.drawImage(
|
|
20885
|
+
this.image,
|
|
20886
|
+
offset.x,
|
|
20887
|
+
offset.y,
|
|
20888
|
+
this.tilewidth,
|
|
20889
|
+
this.tileheight,
|
|
20890
|
+
dx,
|
|
20891
|
+
dy,
|
|
20892
|
+
dw + renderer2.uvOffset,
|
|
20893
|
+
dh + renderer2.uvOffset
|
|
20894
|
+
);
|
|
20895
|
+
}
|
|
20896
|
+
if (flipMask !== 0) {
|
|
20897
|
+
renderer2.restore();
|
|
20898
|
+
}
|
|
20899
|
+
}
|
|
20480
20900
|
};
|
|
20481
20901
|
|
|
20482
20902
|
// src/level/tiled/TMXTilesetGroup.js
|
|
@@ -20595,6 +21015,14 @@ function readTileset(data2, mapTilewidth, mapTileheight) {
|
|
|
20595
21015
|
function readObjectGroup(map, data2, z) {
|
|
20596
21016
|
return new TMXGroup(map, data2, z);
|
|
20597
21017
|
}
|
|
21018
|
+
function refreshAbsoluteBounds(container) {
|
|
21019
|
+
container.forEach((child) => {
|
|
21020
|
+
child.updateBounds(true);
|
|
21021
|
+
if (child instanceof Container) {
|
|
21022
|
+
refreshAbsoluteBounds(child);
|
|
21023
|
+
}
|
|
21024
|
+
});
|
|
21025
|
+
}
|
|
20598
21026
|
var TMXTileMap = class {
|
|
20599
21027
|
/**
|
|
20600
21028
|
* @param {string} levelId - name of TMX map
|
|
@@ -20756,12 +21184,12 @@ var TMXTileMap = class {
|
|
|
20756
21184
|
Math.max(levelBounds.width, width),
|
|
20757
21185
|
Math.max(levelBounds.height, height)
|
|
20758
21186
|
);
|
|
20759
|
-
|
|
20760
|
-
|
|
20761
|
-
|
|
20762
|
-
|
|
20763
|
-
container
|
|
20764
|
-
|
|
21187
|
+
const newX = Math.max(0, ~~((width - levelBounds.width) / 2));
|
|
21188
|
+
const newY = Math.max(0, ~~((height - levelBounds.height) / 2));
|
|
21189
|
+
if (container.pos.x !== newX || container.pos.y !== newY) {
|
|
21190
|
+
container.pos.set(newX, newY, container.pos.z);
|
|
21191
|
+
refreshAbsoluteBounds(container);
|
|
21192
|
+
}
|
|
20765
21193
|
};
|
|
20766
21194
|
var _setBounds = _setBounds2;
|
|
20767
21195
|
const app = container.getRootAncestor().app;
|
|
@@ -22401,7 +22829,7 @@ var ColorMatrix = class extends Matrix3d {
|
|
|
22401
22829
|
// src/video/webgl/utils/attributes.js
|
|
22402
22830
|
function extractAttributes(gl, shader) {
|
|
22403
22831
|
const attributes = {};
|
|
22404
|
-
const attrRx = /attribute\
|
|
22832
|
+
const attrRx = /(?:^|\n)[ \t]*(?:attribute|in)[ \t]+(?:\w+[ \t]+)+(\w+)/g;
|
|
22405
22833
|
let match;
|
|
22406
22834
|
let i = 0;
|
|
22407
22835
|
while (match = attrRx.exec(shader.vertex)) {
|
|
@@ -22412,10 +22840,18 @@ function extractAttributes(gl, shader) {
|
|
|
22412
22840
|
|
|
22413
22841
|
// src/video/webgl/utils/precision.js
|
|
22414
22842
|
function setPrecision(src, precision) {
|
|
22415
|
-
if (
|
|
22416
|
-
return
|
|
22843
|
+
if (/^\s*(?:#version[^\n]*\n)?\s*precision\b/.test(src)) {
|
|
22844
|
+
return src;
|
|
22417
22845
|
}
|
|
22418
|
-
|
|
22846
|
+
if (src.substring(0, 8) === "#version") {
|
|
22847
|
+
const inject = "\nprecision " + precision + " float;\nprecision " + precision + " int;";
|
|
22848
|
+
const nl = src.indexOf("\n");
|
|
22849
|
+
if (nl < 0) {
|
|
22850
|
+
return src + inject;
|
|
22851
|
+
}
|
|
22852
|
+
return src.substring(0, nl) + inject + src.substring(nl);
|
|
22853
|
+
}
|
|
22854
|
+
return "precision " + precision + " float;\n" + src;
|
|
22419
22855
|
}
|
|
22420
22856
|
function getMaxShaderPrecision(gl, highPrecision = true) {
|
|
22421
22857
|
if (highPrecision && gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT).precision > 0 && gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT).precision > 0) {
|
|
@@ -22481,17 +22917,56 @@ var fnHash = {
|
|
|
22481
22917
|
ivec2: "2iv",
|
|
22482
22918
|
ivec3: "3iv",
|
|
22483
22919
|
ivec4: "4iv",
|
|
22920
|
+
uvec2: "2uiv",
|
|
22921
|
+
uvec3: "3uiv",
|
|
22922
|
+
uvec4: "4uiv",
|
|
22484
22923
|
mat2: "Matrix2fv",
|
|
22485
22924
|
mat3: "Matrix3fv",
|
|
22486
22925
|
mat4: "Matrix4fv",
|
|
22487
|
-
sampler2D: "1i"
|
|
22926
|
+
sampler2D: "1i",
|
|
22927
|
+
// WebGL2 integer-typed samplers — bound to a unit just like a
|
|
22928
|
+
// `sampler2D`; the GLSL `usampler2D` / `isampler2D` types let the
|
|
22929
|
+
// shader read raw integer values via `texelFetch` instead of the
|
|
22930
|
+
// normalized-float `texture()` path.
|
|
22931
|
+
usampler2D: "1i",
|
|
22932
|
+
isampler2D: "1i"
|
|
22488
22933
|
};
|
|
22934
|
+
function valuesMatch(cached, val) {
|
|
22935
|
+
if (cached === void 0) {
|
|
22936
|
+
return false;
|
|
22937
|
+
}
|
|
22938
|
+
if (val !== null && typeof val === "object" && typeof val.length === "number") {
|
|
22939
|
+
if (cached.length !== val.length) {
|
|
22940
|
+
return false;
|
|
22941
|
+
}
|
|
22942
|
+
for (let i = 0; i < val.length; i++) {
|
|
22943
|
+
if (cached[i] !== val[i]) {
|
|
22944
|
+
return false;
|
|
22945
|
+
}
|
|
22946
|
+
}
|
|
22947
|
+
return true;
|
|
22948
|
+
}
|
|
22949
|
+
return cached === val;
|
|
22950
|
+
}
|
|
22951
|
+
function captureValue(prev, val) {
|
|
22952
|
+
if (val === null || typeof val !== "object" || typeof val.length !== "number") {
|
|
22953
|
+
return val;
|
|
22954
|
+
}
|
|
22955
|
+
if (prev !== void 0 && typeof prev === "object" && typeof prev.length === "number" && prev.length === val.length) {
|
|
22956
|
+
for (let i = 0; i < val.length; i++) {
|
|
22957
|
+
prev[i] = val[i];
|
|
22958
|
+
}
|
|
22959
|
+
return prev;
|
|
22960
|
+
}
|
|
22961
|
+
return typeof val.slice === "function" ? val.slice() : Array.from(val);
|
|
22962
|
+
}
|
|
22489
22963
|
function extractUniforms(gl, shader) {
|
|
22490
22964
|
const uniforms = {};
|
|
22491
22965
|
const uniRx = /uniform\s+(\w+)\s+(\w+)/g;
|
|
22492
22966
|
const uniformsData = {};
|
|
22493
22967
|
const descriptor = {};
|
|
22494
22968
|
const locations = {};
|
|
22969
|
+
const cache2 = {};
|
|
22495
22970
|
let match;
|
|
22496
22971
|
[shader.vertex, shader.fragment].forEach((shader2) => {
|
|
22497
22972
|
while (match = uniRx.exec(shader2)) {
|
|
@@ -22510,6 +22985,10 @@ function extractUniforms(gl, shader) {
|
|
|
22510
22985
|
set: (function(name2, type2, fn) {
|
|
22511
22986
|
if (/^mat/.test(type2)) {
|
|
22512
22987
|
return function(val) {
|
|
22988
|
+
if (valuesMatch(cache2[name2], val)) {
|
|
22989
|
+
return;
|
|
22990
|
+
}
|
|
22991
|
+
cache2[name2] = captureValue(cache2[name2], val);
|
|
22513
22992
|
gl[fn](locations[name2], false, val);
|
|
22514
22993
|
};
|
|
22515
22994
|
} else {
|
|
@@ -22518,6 +22997,10 @@ function extractUniforms(gl, shader) {
|
|
|
22518
22997
|
if (val.length && !/v$/.test(fn)) {
|
|
22519
22998
|
fnv += "v";
|
|
22520
22999
|
}
|
|
23000
|
+
if (valuesMatch(cache2[name2], val)) {
|
|
23001
|
+
return;
|
|
23002
|
+
}
|
|
23003
|
+
cache2[name2] = captureValue(cache2[name2], val);
|
|
22521
23004
|
gl[fnv](locations[name2], val);
|
|
22522
23005
|
};
|
|
22523
23006
|
}
|
|
@@ -27168,13 +27651,9 @@ var ImageLayer = class extends Sprite {
|
|
|
27168
27651
|
if (this.mask) {
|
|
27169
27652
|
renderer2.setMask(this.mask);
|
|
27170
27653
|
}
|
|
27171
|
-
|
|
27172
|
-
|
|
27173
|
-
|
|
27174
|
-
0,
|
|
27175
|
-
viewport.width * 2,
|
|
27176
|
-
viewport.height * 2
|
|
27177
|
-
);
|
|
27654
|
+
const drawW = this.repeatX ? viewport.width * 2 : width;
|
|
27655
|
+
const drawH = this.repeatY ? viewport.height * 2 : height;
|
|
27656
|
+
renderer2.drawPattern(this._pattern, 0, 0, drawW, drawH);
|
|
27178
27657
|
}
|
|
27179
27658
|
// called when the layer is removed from the game world or a container
|
|
27180
27659
|
onDeactivateEvent() {
|
|
@@ -27812,7 +28291,7 @@ var Trigger = class extends Renderable {
|
|
|
27812
28291
|
};
|
|
27813
28292
|
|
|
27814
28293
|
// src/version.ts
|
|
27815
|
-
var version = "19.
|
|
28294
|
+
var version = "19.4.0";
|
|
27816
28295
|
|
|
27817
28296
|
// src/system/bootstrap.ts
|
|
27818
28297
|
var initialized = false;
|
|
@@ -28673,10 +29152,7 @@ function enablePointerEvent() {
|
|
|
28673
29152
|
if (activeEventList.indexOf(events[i]) !== -1) {
|
|
28674
29153
|
pointerEventTarget.addEventListener(
|
|
28675
29154
|
events[i],
|
|
28676
|
-
throttle(
|
|
28677
|
-
onMoveEvent,
|
|
28678
|
-
throttlingInterval
|
|
28679
|
-
),
|
|
29155
|
+
throttle(onMoveEvent, throttlingInterval),
|
|
28680
29156
|
{ passive: true }
|
|
28681
29157
|
// do not preventDefault on Move events
|
|
28682
29158
|
);
|
|
@@ -29189,7 +29665,7 @@ function testEllipseEllipse(a, ellipseA, b, ellipseB, response) {
|
|
|
29189
29665
|
return testEllipsePolygon(a, ellipseA, b, ellipseB.toPolygon(), response);
|
|
29190
29666
|
}
|
|
29191
29667
|
}
|
|
29192
|
-
const differenceV = T_VECTORS[--T_VECTORS_IDX].copy(b.pos).add(b.ancestor.getAbsolutePosition()).add(ellipseB.pos).sub(a.pos).
|
|
29668
|
+
const differenceV = T_VECTORS[--T_VECTORS_IDX].copy(b.pos).add(b.ancestor.getAbsolutePosition()).add(ellipseB.pos).sub(a.pos).sub(a.ancestor.getAbsolutePosition()).sub(ellipseA.pos);
|
|
29193
29669
|
const radiusA = ellipseA.radius;
|
|
29194
29670
|
const radiusB = ellipseB.radius;
|
|
29195
29671
|
const totalRadius = radiusA + radiusB;
|
|
@@ -29216,7 +29692,7 @@ function testPolygonEllipse(a, polyA, b, ellipseB, response) {
|
|
|
29216
29692
|
if (ellipseB.radiusV.x !== ellipseB.radiusV.y) {
|
|
29217
29693
|
return testPolygonPolygon(a, polyA, b, ellipseB.toPolygon(), response);
|
|
29218
29694
|
}
|
|
29219
|
-
const circlePos = T_VECTORS[--T_VECTORS_IDX].copy(b.pos).add(b.ancestor.getAbsolutePosition()).add(ellipseB.pos).sub(a.pos).
|
|
29695
|
+
const circlePos = T_VECTORS[--T_VECTORS_IDX].copy(b.pos).add(b.ancestor.getAbsolutePosition()).add(ellipseB.pos).sub(a.pos).sub(a.ancestor.getAbsolutePosition()).sub(polyA.pos);
|
|
29220
29696
|
const radius = ellipseB.radius;
|
|
29221
29697
|
const radius2 = radius * radius;
|
|
29222
29698
|
const points = polyA.points;
|
|
@@ -29842,6 +30318,7 @@ var World = class extends Container {
|
|
|
29842
30318
|
this.fps = 60;
|
|
29843
30319
|
this.gravity = new Vector2d(0, 0.98);
|
|
29844
30320
|
this.preRender = false;
|
|
30321
|
+
this.gpuTilemap = true;
|
|
29845
30322
|
this.bodies = /* @__PURE__ */ new Set();
|
|
29846
30323
|
this.broadphase = new QuadTree(
|
|
29847
30324
|
this,
|
|
@@ -31037,8 +31514,15 @@ var MaterialBatcher = class extends Batcher {
|
|
|
31037
31514
|
createTexture2D(unit, pixels = null, filter, repeat = "no-repeat", w = pixels.width, h = pixels.height, premultipliedAlpha = true, mipmap = true, texture, flush = true) {
|
|
31038
31515
|
const gl = this.gl;
|
|
31039
31516
|
const isPOT = isPowerOfTwo(w) && isPowerOfTwo(h);
|
|
31040
|
-
const
|
|
31041
|
-
const
|
|
31517
|
+
const wantsRepeat = repeat !== "no-repeat";
|
|
31518
|
+
const canRepeat = isPOT || this.renderer.WebGLVersion > 1;
|
|
31519
|
+
const rs = repeat.search(/^repeat(-x)?$/) === 0 && canRepeat ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
31520
|
+
const rt = repeat.search(/^repeat(-y)?$/) === 0 && canRepeat ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
31521
|
+
if (wantsRepeat && !canRepeat) {
|
|
31522
|
+
console.warn(
|
|
31523
|
+
"melonJS: repeat wrap (" + repeat + ") requested on a non-power-of-two texture (" + w + "x" + h + ") under WebGL 1 \u2014 downgrading to clamp-to-edge"
|
|
31524
|
+
);
|
|
31525
|
+
}
|
|
31042
31526
|
let currentTexture = texture;
|
|
31043
31527
|
if (!currentTexture) {
|
|
31044
31528
|
currentTexture = gl.createTexture();
|
|
@@ -31049,7 +31533,9 @@ var MaterialBatcher = class extends Batcher {
|
|
|
31049
31533
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);
|
|
31050
31534
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
|
|
31051
31535
|
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultipliedAlpha);
|
|
31052
|
-
if (pixels !== null && pixels.
|
|
31536
|
+
if (pixels !== null && typeof pixels.upload === "function") {
|
|
31537
|
+
pixels.upload(gl, gl.TEXTURE_2D);
|
|
31538
|
+
} else if (pixels !== null && pixels.compressed === true) {
|
|
31053
31539
|
const mipmaps = pixels.mipmaps;
|
|
31054
31540
|
for (let i = 0; i < mipmaps.length; i++) {
|
|
31055
31541
|
gl.compressedTexImage2D(
|
|
@@ -31110,7 +31596,9 @@ var MaterialBatcher = class extends Batcher {
|
|
|
31110
31596
|
pixels
|
|
31111
31597
|
);
|
|
31112
31598
|
}
|
|
31113
|
-
if (isPOT && mipmap === true &&
|
|
31599
|
+
if (isPOT && mipmap === true && pixels !== null && pixels.compressed !== true && typeof pixels.upload !== "function") {
|
|
31600
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
31601
|
+
} else if (pixels === null && isPOT && mipmap === true) {
|
|
31114
31602
|
gl.generateMipmap(gl.TEXTURE_2D);
|
|
31115
31603
|
}
|
|
31116
31604
|
return currentTexture;
|
|
@@ -31188,18 +31676,29 @@ var MaterialBatcher = class extends Batcher {
|
|
|
31188
31676
|
}
|
|
31189
31677
|
/**
|
|
31190
31678
|
* @ignore
|
|
31679
|
+
* @param {TextureAtlas|TextureResource} texture
|
|
31680
|
+
* @param {number} [w] - ignored when the source has its own `width` (the
|
|
31681
|
+
* common case); kept for the legacy signature where callers passed a
|
|
31682
|
+
* destination size. Forwarded only as a last-resort default.
|
|
31683
|
+
* @param {number} [h] - same as `w`.
|
|
31684
|
+
* @param {boolean} [force=false]
|
|
31685
|
+
* @param {boolean} [flush=true]
|
|
31191
31686
|
*/
|
|
31192
31687
|
uploadTexture(texture, w, h, force = false, flush = true) {
|
|
31193
31688
|
const unit = this.renderer.cache.getUnit(texture);
|
|
31194
31689
|
const texture2D = this.boundTextures[unit];
|
|
31195
31690
|
if (typeof texture2D === "undefined" || force) {
|
|
31691
|
+
const filter = typeof texture.filter !== "undefined" ? texture.filter : this.renderer.settings.antiAlias ? this.gl.LINEAR : this.gl.NEAREST;
|
|
31692
|
+
const source = texture.getTexture();
|
|
31693
|
+
const texW = source.width || source.videoWidth || w;
|
|
31694
|
+
const texH = source.height || source.videoHeight || h;
|
|
31196
31695
|
this.createTexture2D(
|
|
31197
31696
|
unit,
|
|
31198
|
-
|
|
31199
|
-
|
|
31697
|
+
source,
|
|
31698
|
+
filter,
|
|
31200
31699
|
texture.repeat,
|
|
31201
|
-
|
|
31202
|
-
|
|
31700
|
+
texW,
|
|
31701
|
+
texH,
|
|
31203
31702
|
texture.premultipliedAlpha,
|
|
31204
31703
|
void 0,
|
|
31205
31704
|
texture2D,
|
|
@@ -32110,6 +32609,395 @@ function packLights(lights, ambient, translateX, translateY, scratch) {
|
|
|
32110
32609
|
};
|
|
32111
32610
|
}
|
|
32112
32611
|
|
|
32612
|
+
// src/video/texture/resource.js
|
|
32613
|
+
var TextureResource = class {
|
|
32614
|
+
/**
|
|
32615
|
+
* @param {object} options
|
|
32616
|
+
* @param {number} options.width - pixel width of the texture
|
|
32617
|
+
* @param {number} options.height - pixel height of the texture
|
|
32618
|
+
* @param {boolean} [options.premultipliedAlpha=false]
|
|
32619
|
+
* @param {string} [options.repeat="no-repeat"] - "no-repeat" | "repeat" | "repeat-x" | "repeat-y"
|
|
32620
|
+
* @param {number} [options.filter] - `gl.NEAREST` or `gl.LINEAR`; when
|
|
32621
|
+
* omitted the batcher falls back to the renderer's `antiAlias` setting
|
|
32622
|
+
*/
|
|
32623
|
+
constructor({
|
|
32624
|
+
width,
|
|
32625
|
+
height,
|
|
32626
|
+
premultipliedAlpha = false,
|
|
32627
|
+
repeat = "no-repeat",
|
|
32628
|
+
filter
|
|
32629
|
+
} = {}) {
|
|
32630
|
+
this.width = width;
|
|
32631
|
+
this.height = height;
|
|
32632
|
+
this.premultipliedAlpha = premultipliedAlpha;
|
|
32633
|
+
this.repeat = repeat;
|
|
32634
|
+
this.filter = filter;
|
|
32635
|
+
this.sources = /* @__PURE__ */ new Map([["default", this]]);
|
|
32636
|
+
this.activeAtlas = "default";
|
|
32637
|
+
}
|
|
32638
|
+
/**
|
|
32639
|
+
* Returns the upload "source" the batcher hands to `createTexture2D`.
|
|
32640
|
+
* For a resource this is the resource itself — `createTexture2D`
|
|
32641
|
+
* dispatches to `resource.upload(gl, target)`.
|
|
32642
|
+
* @ignore
|
|
32643
|
+
*/
|
|
32644
|
+
getTexture() {
|
|
32645
|
+
return this;
|
|
32646
|
+
}
|
|
32647
|
+
/**
|
|
32648
|
+
* Issue the `gl.texImage2D` (or equivalent) call that uploads this
|
|
32649
|
+
* resource's data into the currently-bound `TEXTURE_2D` slot.
|
|
32650
|
+
* Subclasses MUST override.
|
|
32651
|
+
* @abstract
|
|
32652
|
+
* @param {WebGLRenderingContext|WebGL2RenderingContext} gl
|
|
32653
|
+
* @param {number} target - `gl.TEXTURE_2D` (or future cube-map targets)
|
|
32654
|
+
*/
|
|
32655
|
+
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
|
|
32656
|
+
upload(gl, target) {
|
|
32657
|
+
throw new Error("TextureResource subclasses must implement upload()");
|
|
32658
|
+
}
|
|
32659
|
+
};
|
|
32660
|
+
var BufferTextureResource = class extends TextureResource {
|
|
32661
|
+
/**
|
|
32662
|
+
* @param {ArrayBufferView} data - the pixel data; size must be
|
|
32663
|
+
* `width * height * 4` bytes for the default RGBA / UNSIGNED_BYTE
|
|
32664
|
+
* format
|
|
32665
|
+
* @param {object} options
|
|
32666
|
+
* @param {number} options.width
|
|
32667
|
+
* @param {number} options.height
|
|
32668
|
+
* @param {boolean} [options.premultipliedAlpha=false]
|
|
32669
|
+
* @param {string} [options.repeat="no-repeat"]
|
|
32670
|
+
* @param {number} [options.filter]
|
|
32671
|
+
* @param {"rgba8"|"rgba8ui"} [options.format="rgba8"] - storage format.
|
|
32672
|
+
* `"rgba8"` (default): normalized RGBA, sampled via `sampler2D` /
|
|
32673
|
+
* `texture()`. `"rgba8ui"`: unsigned-integer RGBA, sampled via
|
|
32674
|
+
* `usampler2D` / `texelFetch()` — requires WebGL2. Use the integer
|
|
32675
|
+
* form for raw-data lookups (GID tables, palette indices, etc.) to
|
|
32676
|
+
* skip the float-decode round trip and gain exact integer reads.
|
|
32677
|
+
*/
|
|
32678
|
+
constructor(data2, options) {
|
|
32679
|
+
super(options);
|
|
32680
|
+
this.data = data2;
|
|
32681
|
+
this.format = options.format || "rgba8";
|
|
32682
|
+
}
|
|
32683
|
+
/** @ignore */
|
|
32684
|
+
upload(gl, target) {
|
|
32685
|
+
if (this.format === "rgba8ui") {
|
|
32686
|
+
if (typeof gl.RGBA8UI === "undefined") {
|
|
32687
|
+
throw new Error(
|
|
32688
|
+
'BufferTextureResource: format "rgba8ui" requires a WebGL 2 context'
|
|
32689
|
+
);
|
|
32690
|
+
}
|
|
32691
|
+
gl.texImage2D(
|
|
32692
|
+
target,
|
|
32693
|
+
0,
|
|
32694
|
+
gl.RGBA8UI,
|
|
32695
|
+
this.width,
|
|
32696
|
+
this.height,
|
|
32697
|
+
0,
|
|
32698
|
+
gl.RGBA_INTEGER,
|
|
32699
|
+
gl.UNSIGNED_BYTE,
|
|
32700
|
+
this.data
|
|
32701
|
+
);
|
|
32702
|
+
} else {
|
|
32703
|
+
gl.texImage2D(
|
|
32704
|
+
target,
|
|
32705
|
+
0,
|
|
32706
|
+
gl.RGBA,
|
|
32707
|
+
this.width,
|
|
32708
|
+
this.height,
|
|
32709
|
+
0,
|
|
32710
|
+
gl.RGBA,
|
|
32711
|
+
gl.UNSIGNED_BYTE,
|
|
32712
|
+
this.data
|
|
32713
|
+
);
|
|
32714
|
+
}
|
|
32715
|
+
}
|
|
32716
|
+
};
|
|
32717
|
+
|
|
32718
|
+
// src/video/webgl/shaders/orthogonal-tmxlayer.frag
|
|
32719
|
+
var orthogonal_tmxlayer_default = "#version 300 es\n\n// Fragment shader for the orthogonal TMX layer GPU renderer (WebGL2 /\n// GLSL ES 3.00).\n//\n// Per fragment the shader:\n// 1. recovers the world-pixel position from the host UV,\n// 2. walks candidate cells (geometric cell + cells whose oversized,\n// bottom-aligned tiles could reach this fragment),\n// 3. fetches GIDs from the per-layer index texture, and\n// 4. samples the tileset atlas at the correct sub-region.\n//\n// Fast path: when the tileset has no oversized tiles (`uOverflow == (0, 0)`)\n// \u2014 the common case for grid-aligned maps \u2014 only the geometric cell can\n// hold this fragment's tile, so we skip the candidate loop entirely and\n// run a single `tryRenderCell` call. The loop branch is on a uniform\n// value, coherent across the wave, and trims 25 worst-case loop\n// iterations + their guard checks to a single inlined function call.\n//\n// Slow path: tiles drawing larger than the cell are bottom-aligned\n// vertically and left-aligned horizontally. Render order is \"right-down\":\n// later cells end up on top, so the candidate loop scans dy high\u2192low,\n// dx low\u2192high and picks the FIRST match.\n//\n// Index texture encoding (`RGBA8`, one cell per texel):\n// R = GID low byte\n// G = GID high byte (combined: R | (G << 8) = 16-bit GID)\n// B = flip mask (bit 0 = H, bit 1 = V, bit 2 = AD)\n// A = unused\n//\n// Animation lookup (`RGBA8`, 1 row, `tileCount` texels wide): per local\n// tile id, the CURRENT frame's local id, same R/G byte-pair encoding.\n//\n// Why `sampler2D` + float decode rather than `usampler2D`: the engine's\n// multi-texture default shader declares `uSampler0..uSamplerN-1` as\n// `sampler2D` \u2014 all of them are active for WebGL's draw-time validation.\n// A `usampler2D`-backed `RGBA8UI` texture cached at any of those units\n// (units 0..15 on a typical 16-unit fragment stage) would mismatch when\n// the default shader next draws sprites, killing every quad with\n// `GL_INVALID_OPERATION`. Staying on regular RGBA8 keeps the cache\n// path coherent \u2014 the cost is one `floor(c * 255 + 0.5)` per fetch.\n//\n// `texelFetch` is still used (vs `texture()`) for byte-exact reads \u2014\n// it bypasses interpolation, so the integer byte values come out\n// unmolested even on a normalized-float sampler.\n\n// The engine's `setPrecision` step injects precision declarations for\n// float and int after the `#version` line, using whatever precision the\n// renderer was configured with (`highPrecisionShader` setting on the\n// Application). Individual shader files don't hardcode precision so the\n// engine-wide preference applies.\n\nin vec2 vRegion;\nin vec4 vColor;\n\nuniform sampler2D uSampler; // tileset atlas (RGBA)\nuniform sampler2D uTileIndex; // per-layer GID index (RGBA8)\nuniform sampler2D uAnimLookup; // per-tileset animation table (RGBA8)\n\nuniform vec2 uMapSize;\nuniform vec2 uCellSize;\nuniform vec2 uTileSize;\nuniform vec2 uOverflow;\nuniform vec2 uTilesetCols;\nuniform vec2 uInvTilesetSize;\nuniform vec4 uTilesetMargin; // (marginX, marginY, spacingX, spacingY)\nuniform vec2 uGidRange; // (firstgid, lastgid)\nuniform vec2 uVisibleStart;\nuniform vec2 uVisibleSize;\nuniform int uAnimSize; // number of entries in uAnimLookup, 0 if disabled\nuniform float uOpacity;\nuniform vec4 uTint;\n\nout vec4 fragColor;\n\nconst int MAX_OVERFLOW = 4;\n\n// Try to render the tile at cell (cx, cy) for the current fragment.\n// Returns true and writes the sampled color to `outColor` when the cell\n// contains a visible, in-range tile whose pixel covers this fragment.\n// Identical logic for both fast and slow paths \u2014 GLSL inlines this\n// trivially so there's no function-call overhead at runtime.\nbool tryRenderCell(int cx, int cy, vec2 worldPx, out vec4 outColor) {\n int mapW = int(uMapSize.x);\n int mapH = int(uMapSize.y);\n if (cx < 0 || cx >= mapW || cy < 0 || cy >= mapH) {\n return false;\n }\n\n // `texelFetch` skips filtering \u2014 the 8-bit channel values come back\n // as normalized floats, decoded to byte ints below\n vec4 cellF = texelFetch(uTileIndex, ivec2(cx, cy), 0);\n uvec4 cell = uvec4(cellF * 255.0 + 0.5);\n int firstGid = int(uGidRange.x);\n int gid = int(cell.r) | (int(cell.g) << 8);\n if (gid < firstGid || gid > int(uGidRange.y)) {\n return false;\n }\n\n vec2 tileWorldOrigin = vec2(\n float(cx) * uCellSize.x,\n (float(cy) + 1.0) * uCellSize.y - uTileSize.y\n );\n vec2 inTile = (worldPx - tileWorldOrigin) / uTileSize;\n if (inTile.x < 0.0 || inTile.x >= 1.0 || inTile.y < 0.0 || inTile.y >= 1.0) {\n return false;\n }\n\n // flip mask + axis-swap trick: see TMX shader-path flip spec.\n // AD performs a transpose; with AD set, H and V swap their effective\n // axes (matches the legacy `buildFlipTransform`).\n int flipMask = int(cell.b);\n float flipH = float(flipMask & 1);\n float flipV = float((flipMask >> 1) & 1);\n float flipAD = float((flipMask >> 2) & 1);\n inTile = mix(inTile, inTile.yx, flipAD);\n float effH = mix(flipH, flipV, flipAD);\n float effV = mix(flipV, flipH, flipAD);\n inTile.x = mix(inTile.x, 1.0 - inTile.x, effH);\n inTile.y = mix(inTile.y, 1.0 - inTile.y, effV);\n\n int localId = gid - firstGid;\n\n // animation: if the tileset has animated tiles, swap the local id for\n // its current frame's id via the lookup texture (CPU updates the\n // lookup in lockstep with `tileset.update(dt)`).\n if (uAnimSize > 0) {\n vec4 animF = texelFetch(uAnimLookup, ivec2(localId, 0), 0);\n uvec4 animTexel = uvec4(animF * 255.0 + 0.5);\n localId = int(animTexel.r) | (int(animTexel.g) << 8);\n }\n\n float row = floor(float(localId) / uTilesetCols.x);\n float col = float(localId) - row * uTilesetCols.x;\n vec2 tileOriginPx = uTilesetMargin.xy\n + vec2(col, row) * (uTileSize + uTilesetMargin.zw);\n vec2 texelPx = tileOriginPx + inTile * uTileSize;\n vec2 texelUV = texelPx * uInvTilesetSize;\n\n vec4 sampled = texture(uSampler, texelUV);\n if (sampled.a <= 0.0) {\n return false;\n }\n outColor = sampled;\n return true;\n}\n\nvoid main(void) {\n vec2 tileCoord = uVisibleStart + vRegion * uVisibleSize;\n vec2 geomCell = floor(tileCoord);\n vec2 worldPx = tileCoord * uCellSize;\n\n int gx = int(geomCell.x);\n int gy = int(geomCell.y);\n int overflowX = int(uOverflow.x + 0.5);\n int overflowY = int(uOverflow.y + 0.5);\n\n vec4 result;\n\n // Fast path: tiles fit the cell exactly (the common case) \u2014 only the\n // geometric cell can contain this fragment's tile.\n if (overflowX == 0 && overflowY == 0) {\n if (!tryRenderCell(gx, gy, worldPx, result)) {\n discard;\n }\n result.a *= uOpacity;\n fragColor = result * uTint;\n return;\n }\n\n // Slow path: oversized tiles. Walk candidates dy high\u2192low, dx low\u2192high\n // and pick the FIRST match \u2014 render order is \"right-down\" so later\n // cells go on top.\n bool found = false;\n for (int idy = 0; idy <= MAX_OVERFLOW; idy++) {\n int dy = MAX_OVERFLOW - idy;\n if (dy > overflowY) continue;\n for (int dx = 0; dx <= MAX_OVERFLOW; dx++) {\n if (dx > overflowX) break;\n if (tryRenderCell(gx - dx, gy + dy, worldPx, result)) {\n found = true;\n break;\n }\n }\n if (found) break;\n }\n if (!found) discard;\n result.a *= uOpacity;\n fragColor = result * uTint;\n}\n";
|
|
32720
|
+
|
|
32721
|
+
// src/video/webgl/shaders/orthogonal-tmxlayer.vert
|
|
32722
|
+
var orthogonal_tmxlayer_default2 = '#version 300 es\n\n// Vertex shader for the orthogonal TMX layer GPU renderer.\n//\n// Matches the quad batcher\'s vertex layout (`aVertex`, `aRegion`, `aColor`,\n// `uProjectionMatrix`) so the standard `setBatcher("quad", this.shader)` +\n// `addQuad()` flow drives it like any other quad. Same attribute names,\n// same uniforms \u2014 just expressed in GLSL ES 3.00 (`in`/`out` in place of\n// `attribute`/`varying`) so the program can pair with the 3.00 fragment\n// shader that uses `usampler2D` / `texelFetch` for integer-typed lookups.\n\nin vec2 aVertex;\nin vec2 aRegion;\nin vec4 aColor;\n\nuniform mat4 uProjectionMatrix;\n\nout vec2 vRegion;\nout vec4 vColor;\n\nvoid main(void) {\n gl_Position = uProjectionMatrix * vec4(aVertex, 0.0, 1.0);\n // premultiplied-alpha + bgra \u2192 rgba swap, same convention the batcher\n // uses for its default sprite shader\n vColor = vec4(aColor.bgr * aColor.a, aColor.a);\n vRegion = aRegion;\n}\n';
|
|
32723
|
+
|
|
32724
|
+
// src/video/webgl/renderers/tmxlayer/orthogonal.js
|
|
32725
|
+
var DEFAULT_TINT = new Float32Array([1, 1, 1, 1]);
|
|
32726
|
+
var OrthogonalTMXLayerGPURenderer = class {
|
|
32727
|
+
/**
|
|
32728
|
+
* @param {WebGLRenderer} renderer - the WebGL renderer instance
|
|
32729
|
+
*/
|
|
32730
|
+
constructor(renderer2) {
|
|
32731
|
+
this.renderer = renderer2;
|
|
32732
|
+
this.gl = renderer2.gl;
|
|
32733
|
+
this.shader = new GLShader(
|
|
32734
|
+
renderer2.gl,
|
|
32735
|
+
orthogonal_tmxlayer_default2,
|
|
32736
|
+
orthogonal_tmxlayer_default,
|
|
32737
|
+
renderer2.shaderPrecision
|
|
32738
|
+
);
|
|
32739
|
+
this.resources = /* @__PURE__ */ new Map();
|
|
32740
|
+
this.animLookups = /* @__PURE__ */ new Map();
|
|
32741
|
+
this._v2 = new Float32Array(2);
|
|
32742
|
+
this._v4 = new Float32Array(4);
|
|
32743
|
+
}
|
|
32744
|
+
/**
|
|
32745
|
+
* Free every cached per-layer index texture and empty the local
|
|
32746
|
+
* resource map. Called from `WebGLRenderer.reset()` (which
|
|
32747
|
+
* `GAME_RESET` triggers) so each level transition starts clean.
|
|
32748
|
+
* @ignore
|
|
32749
|
+
*/
|
|
32750
|
+
reset() {
|
|
32751
|
+
const batcher = this.renderer.currentBatcher;
|
|
32752
|
+
const cache2 = this.renderer.cache;
|
|
32753
|
+
const drop = (resource) => {
|
|
32754
|
+
if (batcher !== void 0) {
|
|
32755
|
+
batcher.deleteTexture2D(resource);
|
|
32756
|
+
} else {
|
|
32757
|
+
cache2.freeTextureUnit(resource);
|
|
32758
|
+
cache2.delete(resource);
|
|
32759
|
+
}
|
|
32760
|
+
};
|
|
32761
|
+
for (const resource of this.resources.values()) {
|
|
32762
|
+
drop(resource);
|
|
32763
|
+
}
|
|
32764
|
+
for (const entry of this.animLookups.values()) {
|
|
32765
|
+
drop(entry.resource);
|
|
32766
|
+
}
|
|
32767
|
+
this.resources.clear();
|
|
32768
|
+
this.animLookups.clear();
|
|
32769
|
+
}
|
|
32770
|
+
/**
|
|
32771
|
+
* Write a `vec2` uniform without allocating a fresh Float32Array per
|
|
32772
|
+
* call. Both components flow into the shared `_v2` scratch buffer,
|
|
32773
|
+
* which `setUniform` reads synchronously and forwards to
|
|
32774
|
+
* `gl.uniform2fv` — so reusing the buffer across calls is safe.
|
|
32775
|
+
* @param {string} name
|
|
32776
|
+
* @param {number} x
|
|
32777
|
+
* @param {number} y
|
|
32778
|
+
* @private
|
|
32779
|
+
*/
|
|
32780
|
+
_setV2(name, x, y) {
|
|
32781
|
+
this._v2[0] = x;
|
|
32782
|
+
this._v2[1] = y;
|
|
32783
|
+
this.shader.setUniform(name, this._v2);
|
|
32784
|
+
}
|
|
32785
|
+
/**
|
|
32786
|
+
* `vec4` counterpart to {@link _setV2}.
|
|
32787
|
+
* @param {string} name
|
|
32788
|
+
* @param {number} x
|
|
32789
|
+
* @param {number} y
|
|
32790
|
+
* @param {number} z
|
|
32791
|
+
* @param {number} w
|
|
32792
|
+
* @private
|
|
32793
|
+
*/
|
|
32794
|
+
_setV4(name, x, y, z, w) {
|
|
32795
|
+
this._v4[0] = x;
|
|
32796
|
+
this._v4[1] = y;
|
|
32797
|
+
this._v4[2] = z;
|
|
32798
|
+
this._v4[3] = w;
|
|
32799
|
+
this.shader.setUniform(name, this._v4);
|
|
32800
|
+
}
|
|
32801
|
+
/**
|
|
32802
|
+
* Get-or-create the per-tileset animation-lookup entry. Returns
|
|
32803
|
+
* `undefined` for tilesets that have no animated tiles (the shader's
|
|
32804
|
+
* `uAnimEnabled` uniform is then set to 0 and the lookup texture is
|
|
32805
|
+
* not bound).
|
|
32806
|
+
*
|
|
32807
|
+
* The entry holds a `tileCount × 1` RGBA8 `BufferTextureResource`
|
|
32808
|
+
* where texel `localId` encodes the CURRENT frame's local id as
|
|
32809
|
+
* `R = lo byte, G = hi byte` (same encoding as the GID index
|
|
32810
|
+
* texture). Each call walks `tileset.animations` and rewrites
|
|
32811
|
+
* dirty texels — `tileset.update(dt)` (driven by the layer) advances
|
|
32812
|
+
* `anim.cur.tileid` independently of this renderer.
|
|
32813
|
+
*
|
|
32814
|
+
* @param {object} tileset
|
|
32815
|
+
* @param {number} tileCount - tiles in the tileset's atlas grid
|
|
32816
|
+
* (`atlasCols * atlasRows`)
|
|
32817
|
+
* @returns {{resource: BufferTextureResource, data: Uint8Array,
|
|
32818
|
+
* tileCount: number, dirty: boolean}|undefined}
|
|
32819
|
+
*/
|
|
32820
|
+
_getOrUpdateAnimLookup(tileset, tileCount) {
|
|
32821
|
+
if (!tileset.isAnimated || tileset.animations.size === 0) {
|
|
32822
|
+
return void 0;
|
|
32823
|
+
}
|
|
32824
|
+
let entry = this.animLookups.get(tileset);
|
|
32825
|
+
if (entry === void 0) {
|
|
32826
|
+
const data3 = new Uint8Array(tileCount * 4);
|
|
32827
|
+
for (let id = 0; id < tileCount; id++) {
|
|
32828
|
+
data3[id * 4 + 0] = id & 255;
|
|
32829
|
+
data3[id * 4 + 1] = id >> 8 & 255;
|
|
32830
|
+
}
|
|
32831
|
+
const resource = new BufferTextureResource(data3, {
|
|
32832
|
+
width: tileCount,
|
|
32833
|
+
height: 1,
|
|
32834
|
+
premultipliedAlpha: false,
|
|
32835
|
+
repeat: "no-repeat",
|
|
32836
|
+
// NEAREST so the shader's `texelFetch` reads byte-exact
|
|
32837
|
+
// channel values back as normalized floats
|
|
32838
|
+
filter: this.gl.NEAREST,
|
|
32839
|
+
format: "rgba8"
|
|
32840
|
+
});
|
|
32841
|
+
entry = { resource, data: data3, tileCount, dirty: false };
|
|
32842
|
+
this.animLookups.set(tileset, entry);
|
|
32843
|
+
}
|
|
32844
|
+
const data2 = entry.data;
|
|
32845
|
+
for (const [localId, anim] of tileset.animations) {
|
|
32846
|
+
const off2 = localId * 4;
|
|
32847
|
+
const cur = anim.cur.tileid;
|
|
32848
|
+
const lo = cur & 255;
|
|
32849
|
+
const hi = cur >> 8 & 255;
|
|
32850
|
+
if (data2[off2] !== lo || data2[off2 + 1] !== hi) {
|
|
32851
|
+
data2[off2] = lo;
|
|
32852
|
+
data2[off2 + 1] = hi;
|
|
32853
|
+
entry.dirty = true;
|
|
32854
|
+
}
|
|
32855
|
+
}
|
|
32856
|
+
return entry;
|
|
32857
|
+
}
|
|
32858
|
+
/**
|
|
32859
|
+
* Get-or-create the per-layer index `BufferTextureResource`.
|
|
32860
|
+
* @param {TMXLayer} layer
|
|
32861
|
+
* @returns {BufferTextureResource}
|
|
32862
|
+
*/
|
|
32863
|
+
_getResource(layer) {
|
|
32864
|
+
let resource = this.resources.get(layer);
|
|
32865
|
+
if (resource === void 0) {
|
|
32866
|
+
resource = new BufferTextureResource(
|
|
32867
|
+
new Uint8Array(layer.layerData.buffer),
|
|
32868
|
+
{
|
|
32869
|
+
width: layer.cols,
|
|
32870
|
+
height: layer.rows,
|
|
32871
|
+
// raw GID bytes — must NOT have alpha pre-multiplied
|
|
32872
|
+
// into RGB, otherwise A=0 cells wipe their R/G/B
|
|
32873
|
+
premultipliedAlpha: false,
|
|
32874
|
+
repeat: "no-repeat",
|
|
32875
|
+
// NEAREST so `texelFetch` returns the original byte
|
|
32876
|
+
// values (as normalized floats) for the GID/flip
|
|
32877
|
+
// decode below
|
|
32878
|
+
filter: this.gl.NEAREST,
|
|
32879
|
+
format: "rgba8"
|
|
32880
|
+
}
|
|
32881
|
+
);
|
|
32882
|
+
resource._uploadedVersion = -1;
|
|
32883
|
+
this.resources.set(layer, resource);
|
|
32884
|
+
}
|
|
32885
|
+
return resource;
|
|
32886
|
+
}
|
|
32887
|
+
/**
|
|
32888
|
+
* Draw an orthogonal TMX layer through the shader path.
|
|
32889
|
+
* @param {TMXLayer} layer
|
|
32890
|
+
* @param {object} rect - the visible viewport rect (world coords)
|
|
32891
|
+
*/
|
|
32892
|
+
draw(layer, rect) {
|
|
32893
|
+
const renderer2 = this.renderer;
|
|
32894
|
+
const tileWidth = layer.tilewidth;
|
|
32895
|
+
const tileHeight = layer.tileheight;
|
|
32896
|
+
const cols = layer.cols;
|
|
32897
|
+
const rows = layer.rows;
|
|
32898
|
+
const startTileX = Math.max(0, Math.floor(rect.pos.x / tileWidth));
|
|
32899
|
+
const startTileY = Math.max(0, Math.floor(rect.pos.y / tileHeight));
|
|
32900
|
+
const endTileX = Math.min(
|
|
32901
|
+
cols,
|
|
32902
|
+
Math.ceil((rect.pos.x + rect.width) / tileWidth)
|
|
32903
|
+
);
|
|
32904
|
+
const endTileY = Math.min(
|
|
32905
|
+
rows,
|
|
32906
|
+
Math.ceil((rect.pos.y + rect.height) / tileHeight)
|
|
32907
|
+
);
|
|
32908
|
+
if (endTileX <= startTileX || endTileY <= startTileY) {
|
|
32909
|
+
return;
|
|
32910
|
+
}
|
|
32911
|
+
const worldX = startTileX * tileWidth;
|
|
32912
|
+
const worldY = startTileY * tileHeight;
|
|
32913
|
+
const worldW = (endTileX - startTileX) * tileWidth;
|
|
32914
|
+
const worldH = (endTileY - startTileY) * tileHeight;
|
|
32915
|
+
const visStartX = startTileX;
|
|
32916
|
+
const visStartY = startTileY;
|
|
32917
|
+
const visSizeX = endTileX - startTileX;
|
|
32918
|
+
const visSizeY = endTileY - startTileY;
|
|
32919
|
+
const batcher = renderer2.setBatcher("quad", this.shader);
|
|
32920
|
+
const resource = this._getResource(layer);
|
|
32921
|
+
const indexUnit = batcher.uploadTexture(
|
|
32922
|
+
resource,
|
|
32923
|
+
cols,
|
|
32924
|
+
rows,
|
|
32925
|
+
resource._uploadedVersion !== layer.dataVersion
|
|
32926
|
+
);
|
|
32927
|
+
resource._uploadedVersion = layer.dataVersion;
|
|
32928
|
+
this.shader.setUniform("uTileIndex", indexUnit);
|
|
32929
|
+
this._setV2("uMapSize", cols, rows);
|
|
32930
|
+
this._setV2("uVisibleStart", visStartX, visStartY);
|
|
32931
|
+
this._setV2("uVisibleSize", visSizeX, visSizeY);
|
|
32932
|
+
this._setV2("uCellSize", tileWidth, tileHeight);
|
|
32933
|
+
this.shader.setUniform("uOpacity", layer.getOpacity());
|
|
32934
|
+
this.shader.setUniform(
|
|
32935
|
+
"uTint",
|
|
32936
|
+
layer.tint ? layer.tint.toArray() : DEFAULT_TINT
|
|
32937
|
+
);
|
|
32938
|
+
const tilesets = layer.tilesets.tilesets;
|
|
32939
|
+
for (let i = 0; i < tilesets.length; i++) {
|
|
32940
|
+
const tileset = tilesets[i];
|
|
32941
|
+
if (tileset.isCollection || tileset.image === void 0) {
|
|
32942
|
+
continue;
|
|
32943
|
+
}
|
|
32944
|
+
const tsW = tileset.tilewidth;
|
|
32945
|
+
const tsH = tileset.tileheight;
|
|
32946
|
+
const margin = tileset.margin;
|
|
32947
|
+
const spacing = tileset.spacing;
|
|
32948
|
+
const atlasW = tileset.image.width;
|
|
32949
|
+
const atlasH = tileset.image.height;
|
|
32950
|
+
const atlasCols = Math.max(
|
|
32951
|
+
1,
|
|
32952
|
+
Math.floor((atlasW - margin * 2 + spacing) / (tsW + spacing))
|
|
32953
|
+
);
|
|
32954
|
+
const atlasRows = Math.max(
|
|
32955
|
+
1,
|
|
32956
|
+
Math.floor((atlasH - margin * 2 + spacing) / (tsH + spacing))
|
|
32957
|
+
);
|
|
32958
|
+
const tileCount = atlasCols * atlasRows;
|
|
32959
|
+
const animEntry = this._getOrUpdateAnimLookup(tileset, tileCount);
|
|
32960
|
+
if (animEntry !== void 0) {
|
|
32961
|
+
const animUnit = batcher.uploadTexture(
|
|
32962
|
+
animEntry.resource,
|
|
32963
|
+
animEntry.tileCount,
|
|
32964
|
+
1,
|
|
32965
|
+
animEntry.dirty
|
|
32966
|
+
);
|
|
32967
|
+
animEntry.dirty = false;
|
|
32968
|
+
this.shader.setUniform("uAnimLookup", animUnit);
|
|
32969
|
+
this.shader.setUniform("uAnimSize", animEntry.tileCount);
|
|
32970
|
+
} else {
|
|
32971
|
+
this.shader.setUniform("uAnimLookup", indexUnit);
|
|
32972
|
+
this.shader.setUniform("uAnimSize", 0);
|
|
32973
|
+
}
|
|
32974
|
+
this._setV2("uTileSize", tsW, tsH);
|
|
32975
|
+
this._setV2("uTilesetCols", atlasCols, atlasRows);
|
|
32976
|
+
this._setV2("uInvTilesetSize", 1 / atlasW, 1 / atlasH);
|
|
32977
|
+
this._setV4("uTilesetMargin", margin, margin, spacing, spacing);
|
|
32978
|
+
this._setV2(
|
|
32979
|
+
"uOverflow",
|
|
32980
|
+
Math.max(0, Math.ceil(tsW / tileWidth) - 1),
|
|
32981
|
+
Math.max(0, Math.ceil(tsH / tileHeight) - 1)
|
|
32982
|
+
);
|
|
32983
|
+
this._setV2("uGidRange", tileset.firstgid, tileset.lastgid);
|
|
32984
|
+
batcher.addQuad(
|
|
32985
|
+
tileset.texture,
|
|
32986
|
+
worldX,
|
|
32987
|
+
worldY,
|
|
32988
|
+
worldW,
|
|
32989
|
+
worldH,
|
|
32990
|
+
0,
|
|
32991
|
+
0,
|
|
32992
|
+
1,
|
|
32993
|
+
1,
|
|
32994
|
+
4294967295
|
|
32995
|
+
);
|
|
32996
|
+
batcher.flush();
|
|
32997
|
+
}
|
|
32998
|
+
}
|
|
32999
|
+
};
|
|
33000
|
+
|
|
32113
33001
|
// src/video/webgl/webgl_renderer.js
|
|
32114
33002
|
var _tempMatrix = new Matrix3d();
|
|
32115
33003
|
var _savedTransform = new Matrix3d();
|
|
@@ -32252,6 +33140,7 @@ var WebGLRenderer = class extends Renderer {
|
|
|
32252
33140
|
reset() {
|
|
32253
33141
|
super.reset();
|
|
32254
33142
|
this.clear();
|
|
33143
|
+
this._orthogonalTMXGPURenderer?.reset();
|
|
32255
33144
|
this.setViewport();
|
|
32256
33145
|
if (this.gl.getParameter(this.gl.ARRAY_BUFFER_BINDING) !== this.vertexBuffer) {
|
|
32257
33146
|
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
@@ -32280,6 +33169,53 @@ var WebGLRenderer = class extends Renderer {
|
|
|
32280
33169
|
});
|
|
32281
33170
|
this._lightAtlas = void 0;
|
|
32282
33171
|
}
|
|
33172
|
+
if (this.isContextValid === false) {
|
|
33173
|
+
this._orthogonalTMXGPURenderer = void 0;
|
|
33174
|
+
}
|
|
33175
|
+
}
|
|
33176
|
+
/**
|
|
33177
|
+
* Draw a TMX tile layer through whichever path the layer's `renderMode`
|
|
33178
|
+
* resolves to. WebGL2-eligible layers (`renderMode === "shader"`) take
|
|
33179
|
+
* the procedural shader path — one quad per tileset, GID lookup in a
|
|
33180
|
+
* per-layer data texture. All other layers fall through to the base
|
|
33181
|
+
* `Renderer.drawTileLayer` (preRender blit or per-tile loop).
|
|
33182
|
+
* @param {object} layer - the TMXLayer to draw
|
|
33183
|
+
* @param {object} rect - the visible region in world coords
|
|
33184
|
+
*/
|
|
33185
|
+
drawTileLayer(layer, rect) {
|
|
33186
|
+
if (layer.renderMode === "shader") {
|
|
33187
|
+
const gpu = this._getTMXGPURendererFor(layer.orientation);
|
|
33188
|
+
if (gpu !== void 0) {
|
|
33189
|
+
gpu.draw(layer, rect);
|
|
33190
|
+
return;
|
|
33191
|
+
}
|
|
33192
|
+
}
|
|
33193
|
+
super.drawTileLayer(layer, rect);
|
|
33194
|
+
}
|
|
33195
|
+
/**
|
|
33196
|
+
* Lazy-init the orientation-specific GPU tilemap renderer.
|
|
33197
|
+
* @param {string} orientation
|
|
33198
|
+
* @returns {object|undefined}
|
|
33199
|
+
* @ignore
|
|
33200
|
+
*/
|
|
33201
|
+
_getTMXGPURendererFor(orientation2) {
|
|
33202
|
+
if (orientation2 === "orthogonal") {
|
|
33203
|
+
if (this._orthogonalTMXGPURenderer === void 0) {
|
|
33204
|
+
try {
|
|
33205
|
+
this._orthogonalTMXGPURenderer = new OrthogonalTMXLayerGPURenderer(
|
|
33206
|
+
this
|
|
33207
|
+
);
|
|
33208
|
+
} catch (err) {
|
|
33209
|
+
console.warn(
|
|
33210
|
+
"melonJS: GPU tilemap shader failed to compile, falling back to legacy renderer",
|
|
33211
|
+
err
|
|
33212
|
+
);
|
|
33213
|
+
this._orthogonalTMXGPURenderer = null;
|
|
33214
|
+
}
|
|
33215
|
+
}
|
|
33216
|
+
return this._orthogonalTMXGPURenderer || void 0;
|
|
33217
|
+
}
|
|
33218
|
+
return void 0;
|
|
32283
33219
|
}
|
|
32284
33220
|
/**
|
|
32285
33221
|
* add a new batcher to this renderer
|
|
@@ -34202,6 +35138,14 @@ var Application = class {
|
|
|
34202
35138
|
this.world = new World(0, 0, this.settings.width, this.settings.height);
|
|
34203
35139
|
this.world.app = this;
|
|
34204
35140
|
this.world.physic = this.settings.physic;
|
|
35141
|
+
this.world.gpuTilemap = this.settings.gpuTilemap;
|
|
35142
|
+
if (this.settings.gpuTilemap && // duck-type rather than `instanceof WebGLRenderer` to avoid a
|
|
35143
|
+
// runtime import; only the WebGL renderer carries `WebGLVersion`
|
|
35144
|
+
this.renderer.WebGLVersion !== 2) {
|
|
35145
|
+
console.warn(
|
|
35146
|
+
"melonJS: gpuTilemap is enabled but the active renderer is not WebGL 2 \u2014 falling back to the legacy tile renderer for every tile layer"
|
|
35147
|
+
);
|
|
35148
|
+
}
|
|
34205
35149
|
this.lastUpdate = globalThis.performance.now();
|
|
34206
35150
|
if (!this.isInitialized) {
|
|
34207
35151
|
on(STATE_CHANGE, this.repaint, this);
|
|
@@ -34223,9 +35167,7 @@ var Application = class {
|
|
|
34223
35167
|
reset() {
|
|
34224
35168
|
const current = state_default.get();
|
|
34225
35169
|
if (typeof current !== "undefined") {
|
|
34226
|
-
this.viewport = current.cameras.get(
|
|
34227
|
-
"default"
|
|
34228
|
-
);
|
|
35170
|
+
this.viewport = current.cameras.get("default");
|
|
34229
35171
|
}
|
|
34230
35172
|
emit(GAME_RESET);
|
|
34231
35173
|
this.updateFrameRate();
|