From 4b680776a3c0896a7dbe9943e1fa5c8bed0109b0 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 26 Oct 2018 23:11:14 +0200 Subject: MapLoader: use square tile texture rather than long Nx1 rectangle By placing the tiles in a square texture, the dimensions are bounded by the square root of the dimension in the old solution. This way we can fit a significantly higher number of tiles into it without using up all the accuracy of the coordinates. --- src/view/MapLoader.ts | 42 +++++++++++++++++++++++++++++++----------- src/view/MapView.ts | 43 +++++++++++++++++++------------------------ 2 files changed, 50 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/view/MapLoader.ts b/src/view/MapLoader.ts index 290f095..6953321 100644 --- a/src/view/MapLoader.ts +++ b/src/view/MapLoader.ts @@ -4,6 +4,11 @@ import MapState from '../model/state/MapState'; import MapView from './MapView'; import Renderer from './renderer/Renderer'; +export interface TileMap { + texture: WebGLTexture; + tiles: Map; +} + function loadImage(url: string): Promise { return new Promise((resolve, reject) => { const img = new Image(); @@ -36,27 +41,42 @@ function mkTexture(gl: WebGLRenderingContext, src: HTMLCanvasElement|HTMLImageEl return texture; } -function mkTileTexture(gl: WebGLRenderingContext, tiles: Map): -[WebGLTexture, Map] { +function mkTileMap( + gl: WebGLRenderingContext, + tiles: Map, +): TileMap { + const tileSize = MapView.tileSize; + + const canvasDim = nextPowerOf2(Math.sqrt(tiles.size)); + const canvasSize = canvasDim * tileSize; + const canvas = document.createElement('canvas'); - canvas.width = nextPowerOf2(tiles.size) * MapView.tileSize; - canvas.height = MapView.tileSize; + canvas.width = canvas.height = canvasSize; - let i = 0; - const ret: Map = new Map(); + let x = 0, y = 0; + const map: Map = new Map(); const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; for (const [k, tile] of tiles) { - ctx.drawImage(tile, i * MapView.tileSize, 0); - ret.set(k, i++); + ctx.drawImage(tile, x * tileSize, y * tileSize); + map.set(k, [x / canvasDim, y / canvasDim, (x + 1) / canvasDim, (y + 1) / canvasDim]); + + x++; + if (x === canvasDim) { + x = 0; + y++; + } } - return [mkTexture(gl, canvas), ret]; + return { + texture: mkTexture(gl, canvas), + tiles: map, + }; } export async function loadMap(r: Renderer, map: MapState): Promise { const tiles = await loadTiles(map.data.tiles); - const [tileTexture, tileMap] = mkTileTexture(r.getContext(), tiles); + const tileMap = mkTileMap(r.getContext(), tiles); - return new MapView(r, map, tileTexture, tileMap); + return new MapView(r, map, tileMap); } diff --git a/src/view/MapView.ts b/src/view/MapView.ts index 92784b7..5c30f20 100644 --- a/src/view/MapView.ts +++ b/src/view/MapView.ts @@ -1,6 +1,5 @@ -import {nextPowerOf2} from '../util'; - import MapState from '../model/state/MapState'; +import {TileMap} from './MapLoader'; import Renderer from './renderer/Renderer'; export default class MapView { @@ -14,17 +13,14 @@ export default class MapView { constructor( private readonly r: Renderer, private readonly map: MapState, - private readonly tileTexture: WebGLTexture, - private readonly tileMap: Map, + private readonly tileMap: TileMap, ) { const vertexData: number[] = []; const textureData: number[] = []; - const tileCount = nextPowerOf2(tileMap.size); - for (let x = 0; x < map.data.width; x++) for (let y = 0; y < map.data.height; y++) - this.addTile(vertexData, textureData, x, y, map.data.layers[0][y][x], tileCount); + this.addTile(vertexData, textureData, x, y, map.data.layers[0][y][x]); const gl = r.getContext(); @@ -43,7 +39,7 @@ export default class MapView { gl.clear(gl.COLOR_BUFFER_BIT); gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, this.tileTexture); + gl.bindTexture(gl.TEXTURE_2D, this.tileMap.texture); gl.uniform1i(this.r.getSamplerLoc(), 0); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); @@ -55,25 +51,24 @@ export default class MapView { gl.drawArrays(gl.TRIANGLES, 0, 6 * this.map.data.width * this.map.data.height); } - private addTile(vertexData: number[], textureData: number[], x: number, y: number, tile: string, tileCount: number) { - const tileID = this.tileMap.get(tile); - if (tileID === undefined) - throw new Error('invalid tile specifier in map data'); + private pushTile(buf: number[], coords: [number, number, number, number]) { + const [x1, y1, x2, y2] = coords; - vertexData.push(x); vertexData.push(y); - vertexData.push(x + 1); vertexData.push(y); - vertexData.push(x); vertexData.push(y + 1); + buf.push(x1); buf.push(y1); + buf.push(x2); buf.push(y1); + buf.push(x1); buf.push(y2); - vertexData.push(x); vertexData.push(y + 1); - vertexData.push(x + 1); vertexData.push(y); - vertexData.push(x + 1); vertexData.push(y + 1); + buf.push(x1); buf.push(y2); + buf.push(x2); buf.push(y1); + buf.push(x2); buf.push(y2); + } - textureData.push(tileID / tileCount); textureData.push(0); - textureData.push((tileID + 1) / tileCount); textureData.push(0); - textureData.push(tileID / tileCount); textureData.push(1); + private addTile(vertexData: number[], textureData: number[], x: number, y: number, tile: string) { + const tilePos = this.tileMap.tiles.get(tile); + if (!tilePos) + throw new Error('invalid tile specifier in map data'); - textureData.push(tileID / tileCount); textureData.push(1); - textureData.push((tileID + 1) / tileCount); textureData.push(0); - textureData.push((tileID + 1) / tileCount); textureData.push(1); + this.pushTile(vertexData, [x, y, x + 1, y + 1]); + this.pushTile(textureData, tilePos); } } -- cgit v1.2.3