Permalink
Browse files

Fix tile seams by redrawing the tileset and expanding each tile

  • Loading branch information...
phoboslab committed Oct 26, 2015
1 parent bf5bcf9 commit 77063cd9d84a6d4b71af7c18899468f5cd92c02c
Showing with 95 additions and 14 deletions.
  1. +58 −1 lib/plugins/twopointfive/image.js
  2. +7 −0 lib/plugins/twopointfive/world/map.js
  3. +21 −13 lib/plugins/twopointfive/world/tile.js
  4. +9 −0 readme.md
@@ -12,11 +12,68 @@ ig.module(
ig.Image.inject({
texture: null,
seamsExpanded: false,
textureWidth: 0,
textureHeight: 0,
onload: function( event ) {
this.texture = ig.system.renderer.loadTexture(this.data);
this.textureWidth = this.data.width;
this.textureHeight = this.data.height;
this.parent(event);
},
expandSeams: function(tilesize) {
if( this.seamsExpanded ) { return; }
this.seamsExpanded = true;
var tw = (this.width / tilesize)|0,
th = (this.height / tilesize)|0;
this.textureWidth = this.width + tw * 2 - 2;
this.textureHeight = this.height + th * 2 - 2;
var expandedCanvas = ig.$new('canvas');
expandedCanvas.width = this.textureWidth;
expandedCanvas.height = this.textureHeight;
var ctx = expandedCanvas.getContext('2d');
ig.System.SCALE.CRISP(expandedCanvas, ctx);
for( var y = 0, dy = -1; y < th; y++, dy += (tilesize+2) ) {
for( var x = 0, dx = -1; x < tw; x++, dx += (tilesize+2) ) {
// Left edge
if( dx > 0 ) {
ctx.drawImage(this.data, x*tilesize, y*tilesize, 1, tilesize, dx, dy+1, 1, tilesize);
}
// Right edge
if( dx < this.width - tilesize ) {
ctx.drawImage(this.data, (x+1)*tilesize-1, y*tilesize, 1, tilesize, dx+tilesize+1, dy+1, 1, tilesize);
}
// Top edge, draw expanded first to cover the corners
if( dy > 0 ) {
ctx.drawImage(this.data, x*tilesize, y*tilesize, tilesize, 1, dx, dy, tilesize+2, 1);
ctx.drawImage(this.data, x*tilesize, y*tilesize, tilesize, 1, dx+1, dy, tilesize, 1);
}
// Bottom edge, draw expanded first to cover the corners
if( dy < this.height - tilesize ) {
ctx.drawImage(this.data, x*tilesize, (y+1)*tilesize-1, tilesize, 1, dx, dy+tilesize+1, tilesize+2, 1);
ctx.drawImage(this.data, x*tilesize, (y+1)*tilesize-1, tilesize, 1, dx+1, dy+tilesize+1, tilesize, 1);
}
// Tile
ctx.drawImage(this.data, x*tilesize, y*tilesize, tilesize, tilesize, dx+1, dy+1, tilesize, tilesize);
}
}
// Replace texture with the expanded version
this.texture = ig.system.renderer.loadTexture(expandedCanvas);
}
});
});
});
@@ -16,6 +16,11 @@ tpf.Map = ig.BackgroundMap.extend({
init: function( tilesize, data, tileset, orientation, anims ) {
this.parent( tilesize, data, tileset );
if( tpf.Map.fixTileSeams ) {
this.tiles.expandSeams(tilesize);
}
this.yOffset = this.tilesize/2 * (orientation == 'floor' ? -1 : 1);
this.anims = anims || {};
@@ -79,5 +84,7 @@ tpf.Map = ig.BackgroundMap.extend({
}
});
tpf.Map.fixTileSeams = true;
});
@@ -38,21 +38,29 @@ tpf.Tile = ig.Class.extend({
if( t == this.tile ) { return; }
this.tile = t;
var tx = (Math.floor(t * this.tileWidth) % this.image.width) / this.image.width,
ty = (Math.floor(t * this.tileWidth / this.image.width) * this.tileHeight) / this.image.height,
wx = this.tileWidth / this.image.width,
wy = this.tileHeight / this.image.height;
this.quad.setUV(tx, ty+wy, tx+wx, ty);
var tileSpacing = this.image.seamsExpanded ? 2 : 0,
tx = t % Math.floor(this.image.width / this.tileWidth),
ty = Math.floor(t / Math.floor(this.image.width / this.tileWidth));
var px = (tx * this.tileWidth + tx * tileSpacing) / this.image.textureWidth,
py = (ty * this.tileHeight + ty * tileSpacing) / this.image.textureHeight,
wx = this.tileWidth / this.image.textureWidth,
wy = this.tileHeight / this.image.textureHeight;
this.quad.setUV(px, py + wy, px + wx, py);
},
setTileInBuffer: function(buffer, offset, t) {
var tileSpacing = this.image.seamsExpanded ? 2 : 0,
tx = t % Math.floor(this.image.width / this.tileWidth),
ty = Math.floor(t / Math.floor(this.image.width / this.tileWidth));
setTileInBuffer: function( buffer, offset, t ) {
var tx = (Math.floor(t * this.tileWidth) % this.image.width) / this.image.width,
ty = (Math.floor(t * this.tileWidth / this.image.width) * this.tileHeight) / this.image.height,
wx = this.tileWidth / this.image.width,
wy = this.tileHeight / this.image.height;
tpf.Quad.setUVInBuffer(buffer, offset, tx, ty+wy, tx+wx, ty);
var px = (tx * this.tileWidth + tx * tileSpacing) / this.image.textureWidth,
py = (ty * this.tileHeight + ty * tileSpacing) / this.image.textureHeight,
wx = this.tileWidth / this.image.textureWidth,
wy = this.tileHeight / this.image.textureHeight;
tpf.Quad.setUVInBuffer(buffer, offset, px, py + wy, px + wx, py);
},
draw: function() {
@@ -22,3 +22,12 @@ The layers in your level need to be named in a certain way for TwoPointFive to r
TwoPointFive comes with some additions to Impact's Debug Module. To load it, simply require the `plugins.twopointfive.debug` module in your `main.js`.
### A note about Tile Seams
Whenever drawing parts of an image in WebGL, such is done here when drawing tiles, WebGL may sample pixels from a region of the image that is outside the one you specified. This happens mostly due to rounding errors and will result in ugly seams between tiles.
TwoPointFive attempts to work around this issue by redrawing your tileset into a slightly larger image and adding a 1 pixel border around each tile. This 1px border is a copy of the neighboring pixels. Whenever WebGL now samples a texture slightly outside the tile boundary, it will sample from this 1px border and thus avoid any seams in your map.
If you do not want this behaviour, you can disable it by setting `tpf.Map.fixTileSeams = false;` before calling `ig.main()`.

1 comment on commit 77063cd

@EnMod

This comment has been minimized.

EnMod commented on 77063cd Oct 27, 2015

Yes, this seems to have done the trick! Great work!

Please sign in to comment.