Skip to content

Commit 57a6245

Browse files
committed
Made a copy of Phaser.TilemapLayer which instead extends PIXI.Tilemap, then added functions to build a batch list of tile source and destination coordinates (instead of drawing them directly onto a Canvas). This maintains all the existing Phaser functionality in regards to tile map decoding and interpretation, and also provides maximum performance from the PIXI component by using a TRIANGLE_STRIP to render everything as a single batch job.
1 parent 94d49f1 commit 57a6245

1 file changed

Lines changed: 209 additions & 0 deletions

File tree

src/tilemap/TilemapLayerGL.js

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ Phaser.TilemapLayerGL = function (game, tilemap, index, width, height) {
220220
*/
221221
this._results = [];
222222

223+
// TODO: need PIXI textures for each tileset source image
223224
var baseTexture = new PIXI.BaseTexture(this.map.tilesets[0].image);
224225
PIXI.Tilemap.call(this, new PIXI.Texture(baseTexture), this.map.width, this.map.height, this.map.tileWidth, this.map.tileHeight, this.layer);
225226

@@ -270,6 +271,8 @@ Phaser.TilemapLayerGL.prototype.postUpdate = function () {
270271
this.scrollX = camera.x * this.scrollFactorX / this.scale.x;
271272
this.scrollY = camera.y * this.scrollFactorY / this.scale.y;
272273

274+
this.render();
275+
273276
};
274277

275278
/**
@@ -652,6 +655,212 @@ Phaser.TilemapLayerGL.prototype.setScale = function (xScale, yScale) {
652655

653656
};
654657

658+
/**
659+
* Render tiles in the given area given by the virtual tile coordinates biased by the given scroll factor.
660+
* This will constrain the tile coordinates based on wrapping but not physical coordinates.
661+
*
662+
* @method Phaser.TilemapLayerGL#renderRegion
663+
* @private
664+
* @param {integer} scrollX - Render x offset/scroll.
665+
* @param {integer} scrollY - Render y offset/scroll.
666+
* @param {integer} left - Leftmost column to render.
667+
* @param {integer} top - Topmost row to render.
668+
* @param {integer} right - Rightmost column to render.
669+
* @param {integer} bottom - Bottommost row to render.
670+
*/
671+
Phaser.TilemapLayerGL.prototype.renderRegion = function (scrollX, scrollY, left, top, right, bottom) {
672+
673+
var context = this.context;
674+
675+
var width = this.layer.width;
676+
var height = this.layer.height;
677+
var tw = this._mc.tileWidth;
678+
var th = this._mc.tileHeight;
679+
680+
var tilesets = this._mc.tilesets;
681+
var lastAlpha = NaN;
682+
683+
if (!this._wrap)
684+
{
685+
if (left <= right) // Only adjust if going to render
686+
{
687+
left = Math.max(0, left);
688+
right = Math.min(width - 1, right);
689+
}
690+
if (top <= bottom)
691+
{
692+
top = Math.max(0, top);
693+
bottom = Math.min(height - 1, bottom);
694+
}
695+
}
696+
697+
// top-left pixel of top-left cell
698+
var baseX = (left * tw) - scrollX;
699+
var baseY = (top * th) - scrollY;
700+
701+
// Fix normStartX/normStartY such it is normalized [0..width/height). This allows a simple conditional and decrement to always keep in range [0..width/height) during the loop. The major offset bias is to take care of negative values.
702+
var normStartX = (left + ((1 << 20) * width)) % width;
703+
var normStartY = (top + ((1 << 20) * height)) % height;
704+
705+
// tx/ty - are pixel coordinates where tile is drawn
706+
// x/y - is cell location, normalized [0..width/height) in loop
707+
// xmax/ymax - remaining cells to render on column/row
708+
var tx, ty, x, y, xmax, ymax;
709+
710+
//context.fillStyle = this.tileColor;
711+
712+
for (y = normStartY, ymax = bottom - top, ty = baseY;
713+
ymax >= 0;
714+
y++, ymax--, ty += th)
715+
{
716+
717+
if (y >= height) { y -= height; }
718+
719+
var row = this.layer.data[y];
720+
721+
for (x = normStartX, xmax = right - left, tx = baseX;
722+
xmax >= 0;
723+
x++, xmax--, tx += tw)
724+
{
725+
726+
if (x >= width) { x -= width; }
727+
728+
var tile = row[x];
729+
730+
if (!tile || tile.index < 0)
731+
{
732+
continue;
733+
}
734+
735+
var index = tile.index;
736+
737+
var set = tilesets[index];
738+
739+
if (set === undefined)
740+
{
741+
set = this.resolveTileset(index);
742+
}
743+
744+
// Setting the globalAlpha is "surprisingly expensive" in Chrome (38)
745+
if (tile.alpha !== lastAlpha && !this.debug)
746+
{
747+
//context.globalAlpha = tile.alpha;
748+
lastAlpha = tile.alpha;
749+
}
750+
751+
if (set)
752+
{
753+
if (tile.rotation || tile.flipped)
754+
{
755+
//context.save();
756+
//context.translate(tx + tile.centerX, ty + tile.centerY);
757+
//context.rotate(tile.rotation);
758+
759+
// if (tile.flipped)
760+
// {
761+
// context.scale(-1, 1);
762+
// }
763+
764+
set.drawGl(this.glBatch, -tile.centerX, -tile.centerY, index);
765+
//context.restore();
766+
}
767+
else
768+
{
769+
set.drawGl(this.glBatch, tx, ty, index);
770+
}
771+
}
772+
// else if (this.debugSettings.missingImageFill)
773+
// {
774+
// context.fillStyle = this.debugSettings.missingImageFill;
775+
// context.fillRect(tx, ty, tw, th);
776+
// }
777+
778+
// if (tile.debug && this.debugSettings.debuggedTileOverfill)
779+
// {
780+
// context.fillStyle = this.debugSettings.debuggedTileOverfill;
781+
// context.fillRect(tx, ty, tw, th);
782+
// }
783+
784+
}
785+
786+
}
787+
788+
};
789+
790+
/**
791+
* Clear and render the entire canvas.
792+
*
793+
* @method Phaser.TilemapLayerGL#renderFull
794+
* @private
795+
*/
796+
Phaser.TilemapLayerGL.prototype.renderFull = function () {
797+
798+
var scrollX = this._mc.scrollX;
799+
var scrollY = this._mc.scrollY;
800+
801+
var renderW = this.game._width; //this.canvas.width;
802+
var renderH = this.game._height; //this.canvas.height;
803+
804+
var tw = this._mc.tileWidth;
805+
var th = this._mc.tileHeight;
806+
807+
var left = Math.floor(scrollX / tw);
808+
var right = Math.floor((renderW - 1 + scrollX) / tw);
809+
var top = Math.floor(scrollY / th);
810+
var bottom = Math.floor((renderH - 1 + scrollY) / th);
811+
812+
this.glBatch = [];
813+
this.renderRegion(scrollX, scrollY, left, top, right, bottom);
814+
};
815+
816+
/**
817+
* Renders the tiles to the layer canvas and pushes to the display.
818+
*
819+
* @method Phaser.TilemapLayerGL#render
820+
* @protected
821+
*/
822+
Phaser.TilemapLayerGL.prototype.render = function () {
823+
824+
var redrawAll = false;
825+
826+
if (!this.visible)
827+
{
828+
return;
829+
}
830+
831+
if (this.dirty || this.layer.dirty)
832+
{
833+
this.layer.dirty = false;
834+
redrawAll = true;
835+
}
836+
837+
// Scrolling bias; whole pixels only
838+
var scrollX = this._scrollX | 0;
839+
var scrollY = this._scrollY | 0;
840+
841+
var mc = this._mc;
842+
var shiftX = mc.scrollX - scrollX; // Negative when scrolling right/down
843+
var shiftY = mc.scrollY - scrollY;
844+
845+
if (!redrawAll &&
846+
shiftX === 0 && shiftY === 0)
847+
{
848+
// No reason to rebuild batch, looking at same thing and not invalidated.
849+
return;
850+
}
851+
852+
mc.scrollX = scrollX;
853+
mc.scrollY = scrollY;
854+
855+
this.renderFull();
856+
857+
this.texture.baseTexture.dirty();
858+
859+
this.dirty = false;
860+
861+
return true;
862+
863+
};
655864

656865
/**
657866
* Flag controlling if the layer tiles wrap at the edges. Only works if the World size matches the Map size.

0 commit comments

Comments
 (0)