diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/index.ts | 18 | ||||
-rw-r--r-- | src/model/MapData.ts | 31 | ||||
-rw-r--r-- | src/util.ts | 12 | ||||
-rw-r--r-- | src/view/MapLoader.ts | 77 | ||||
-rw-r--r-- | src/view/MapView.ts | 82 | ||||
-rw-r--r-- | src/view/Renderer.ts | 102 |
6 files changed, 158 insertions, 164 deletions
diff --git a/src/index.ts b/src/index.ts index 6390017..1c2035f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import MapData from './model/MapData'; -import Renderer from './view/Renderer'; import {loadMap} from './view/MapLoader'; +import Renderer from './view/Renderer'; window.onload = () => { const canvas = document.getElementById('rpgedit') as HTMLCanvasElement; @@ -10,15 +10,15 @@ window.onload = () => { const renderer = new Renderer(canvas); - let xhr = new XMLHttpRequest(); + const xhr = new XMLHttpRequest(); - xhr.addEventListener('load', async function() { - let mapDef = new MapData(JSON.parse(this.responseText)); + xhr.addEventListener('load', async function() { + const mapDef = new MapData(JSON.parse(this.responseText)); - let mapView = await loadMap(renderer, mapDef); - mapView.draw(); - }); + const mapView = await loadMap(renderer, mapDef); + mapView.draw(); + }); - xhr.open('GET', 'resources/map/test.json', true); - xhr.send(); + xhr.open('GET', 'resources/map/test.json', true); + xhr.send(); }; diff --git a/src/model/MapData.ts b/src/model/MapData.ts index b83e146..dcb98b8 100644 --- a/src/model/MapData.ts +++ b/src/model/MapData.ts @@ -1,26 +1,25 @@ import {mapFromObject} from '../util'; - interface Input { - tiles: {[key: string]: string}; - collision: string[]; - layers: string[][][]; + tiles: {[key: string]: string}; + collision: string[]; + layers: string[][][]; } export default class MapData { - tiles: Map<string, string>; - collision: string[]; - layers: string[][][]; + public tiles: Map<string, string>; + public collision: string[]; + public layers: string[][][]; - width: number; - height: number; + public width: number; + public height: number; - constructor(data: Input) { - this.tiles = mapFromObject(data.tiles); - this.collision = data.collision; - this.layers = data.layers; + constructor(data: Input) { + this.tiles = mapFromObject(data.tiles); + this.collision = data.collision; + this.layers = data.layers; - this.height = this.collision.length; - this.width = this.collision[0].length; - } + this.height = this.collision.length; + this.width = this.collision[0].length; + } } diff --git a/src/util.ts b/src/util.ts index 73788ad..453966e 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,25 +1,25 @@ export function mapFromObject<T>(obj: {[key: string]: T}): Map<string, T> { - let ret = new Map(); + const ret = new Map(); - for (let k of Object.keys(obj)) + for (const k of Object.keys(obj)) ret.set(k, obj[k]); return ret; } export function mapValues<K, V1, V2>(f: (v: V1) => V2, map: Map<K, V1>): Map<K, V2> { - let ret: Map<K, V2> = new Map(); + const ret: Map<K, V2> = new Map(); - for (let [k, v] of map) + for (const [k, v] of map) ret.set(k, f(v)); return ret; } export async function mapValuesAsync<K, V1, V2>(f: (v: V1) => Promise<V2>, map: Map<K, V1>): Promise<Map<K, V2>> { - let ret: Map<K, V2> = new Map(); + const ret: Map<K, V2> = new Map(); - for (let [k, v] of mapValues(f, map)) + for (const [k, v] of mapValues(f, map)) ret.set(k, await v); return ret; diff --git a/src/view/MapLoader.ts b/src/view/MapLoader.ts index f39700e..f178c69 100644 --- a/src/view/MapLoader.ts +++ b/src/view/MapLoader.ts @@ -1,63 +1,62 @@ import {mapValues, mapValuesAsync, nextPowerOf2} from '../util'; -import Renderer from './Renderer'; -import MapView from './MapView'; import MapData from '../model/MapData'; - +import MapView from './MapView'; +import Renderer from './Renderer'; function loadImage(url: string): Promise<HTMLImageElement> { - return new Promise(function(resolve, reject) { - let img = new Image(); - img.addEventListener('load', () => { resolve(img); }); - img.addEventListener('error', () => { reject(Error('failed to load ' + url)); }); - img.src = url; - }); + return new Promise((resolve, reject) => { + const img = new Image(); + img.addEventListener('load', () => { resolve(img); }); + img.addEventListener('error', () => { reject(Error('failed to load ' + url)); }); + img.src = url; + }); } function loadImages(urls: Map<string, string>): Promise<Map<string, HTMLImageElement>> { - return mapValuesAsync(loadImage, urls); + return mapValuesAsync(loadImage, urls); } function loadTiles(tiles: Map<string, string>): Promise<Map<string, HTMLImageElement>> { - return loadImages(mapValues(t => `resources/sprite/tile/${t}.png`, tiles)); + return loadImages(mapValues((t) => `resources/sprite/tile/${t}.png`, tiles)); } function mkTexture(gl: WebGLRenderingContext, src: HTMLCanvasElement|HTMLImageElement): WebGLTexture { - let texture = gl.createTexture(); - if (!texture) - throw new Error('unable to create texture'); - - gl.bindTexture(gl.TEXTURE_2D, texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, src); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); - - return texture; + const texture = gl.createTexture(); + if (!texture) + throw new Error('unable to create texture'); + + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, src); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + return texture; } -function mkTileTexture(gl: WebGLRenderingContext, tiles: Map<string, HTMLImageElement>): [WebGLTexture, Map<string, number>] { - let canvas = document.createElement('canvas'); - canvas.width = nextPowerOf2(tiles.size) * MapView.tileSize; - canvas.height = MapView.tileSize; +function mkTileTexture(gl: WebGLRenderingContext, tiles: Map<string, HTMLImageElement>): +[WebGLTexture, Map<string, number>] { + const canvas = document.createElement('canvas'); + canvas.width = nextPowerOf2(tiles.size) * MapView.tileSize; + canvas.height = MapView.tileSize; - let i = 0; - let ret: Map<string, number> = new Map(); - let ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + let i = 0; + const ret: Map<string, number> = new Map(); + const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; - for (let [k, tile] of tiles) { - ctx.drawImage(tile, i * MapView.tileSize, 0); - ret.set(k, i++); - } + for (const [k, tile] of tiles) { + ctx.drawImage(tile, i * MapView.tileSize, 0); + ret.set(k, i++); + } - return [mkTexture(gl, canvas), ret]; + return [mkTexture(gl, canvas), ret]; } - export async function loadMap(r: Renderer, mapData: MapData): Promise<MapView> { - let tiles = await loadTiles(mapData.tiles); - let [tileTexture, tileMap] = mkTileTexture(r.gl, tiles); + const tiles = await loadTiles(mapData.tiles); + const [tileTexture, tileMap] = mkTileTexture(r.gl, tiles); - return new MapView(r, mapData, tileTexture, tileMap); + return new MapView(r, mapData, tileTexture, tileMap); } diff --git a/src/view/MapView.ts b/src/view/MapView.ts index 21f83d5..97d5d7d 100644 --- a/src/view/MapView.ts +++ b/src/view/MapView.ts @@ -1,49 +1,30 @@ import {nextPowerOf2} from '../util'; -import Renderer from './Renderer'; import MapData from '../model/MapData'; +import Renderer from './Renderer'; +export default class MapView { + public static readonly tileSize: number = 32; -class MapView { - private redrawPending: boolean = false; + private redrawPending: boolean = false; private vertexBuffer: WebGLBuffer; private textureBuffer: WebGLBuffer; + constructor( + private r: Renderer, + private map: MapData, + private tileTexture: WebGLTexture, + private tileMap: Map<string, number>, + ) { + const vertexData: number[] = []; + const textureData: number[] = []; - private addTile(vertexData: number[], textureData: number[], x: number, y: number, tile: string, tileCount: number) { - let tileID = this.tileMap.get(tile); - if (tileID === undefined) - throw new Error('invalid tile specifier in map data'); - - vertexData.push(x); vertexData.push(y); - vertexData.push(x+1); vertexData.push(y); - vertexData.push(x); vertexData.push(y+1); - - vertexData.push(x); vertexData.push(y+1); - vertexData.push(x+1); vertexData.push(y); - vertexData.push(x+1); vertexData.push(y+1); - - textureData.push((tileID) / tileCount); textureData.push(0); - textureData.push((tileID+1) / tileCount); textureData.push(0); - textureData.push((tileID) / tileCount); textureData.push(1); - - textureData.push((tileID) / tileCount); textureData.push(1); - textureData.push((tileID+1) / tileCount); textureData.push(0); - textureData.push((tileID+1) / tileCount); textureData.push(1); - } - - constructor(private r: Renderer, private map: MapData, private tileTexture: WebGLTexture, private tileMap: Map<string, number>) { - let vertexData: number[] = []; - let textureData: number[] = []; + const tileCount = nextPowerOf2(tileMap.size); - let tileCount = nextPowerOf2(tileMap.size); - - for (let x = 0; x < map.width; x++) { - for (let y = 0; y < map.height; y++) { - this.addTile(vertexData, textureData, x, y, map.layers[0][y][x], tileCount); - } - } + for (let x = 0; x < map.width; x++) + for (let y = 0; y < map.height; y++) + this.addTile(vertexData, textureData, x, y, map.layers[0][y][x], tileCount); this.vertexBuffer = r.createBuffer(); r.gl.bindBuffer(r.gl.ARRAY_BUFFER, this.vertexBuffer); @@ -52,9 +33,9 @@ class MapView { this.textureBuffer = r.createBuffer(); r.gl.bindBuffer(r.gl.ARRAY_BUFFER, this.textureBuffer); r.gl.bufferData(r.gl.ARRAY_BUFFER, new Float32Array(textureData), r.gl.STATIC_DRAW); - } + } - draw(): void { + public draw(): void { this.r.gl.clear(this.r.gl.COLOR_BUFFER_BIT); this.r.gl.activeTexture(this.r.gl.TEXTURE0); @@ -68,12 +49,27 @@ class MapView { this.r.gl.vertexAttribPointer(this.r.textureCoordLoc, 2, this.r.gl.FLOAT, false, 0, 0); this.r.gl.drawArrays(this.r.gl.TRIANGLES, 0, 6 * this.map.width * this.map.height); - } -} + } -module MapView { - export const tileSize = 32; -} + 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'); + vertexData.push(x); vertexData.push(y); + vertexData.push(x + 1); vertexData.push(y); + vertexData.push(x); vertexData.push(y + 1); -export default MapView; + vertexData.push(x); vertexData.push(y + 1); + vertexData.push(x + 1); vertexData.push(y); + vertexData.push(x + 1); vertexData.push(y + 1); + + textureData.push((tileID) / tileCount); textureData.push(0); + textureData.push((tileID + 1) / tileCount); textureData.push(0); + textureData.push((tileID) / tileCount); textureData.push(1); + + textureData.push((tileID) / tileCount); textureData.push(1); + textureData.push((tileID + 1) / tileCount); textureData.push(0); + textureData.push((tileID + 1) / tileCount); textureData.push(1); + } +} diff --git a/src/view/Renderer.ts b/src/view/Renderer.ts index 9b7bfe3..ae3c6e7 100644 --- a/src/view/Renderer.ts +++ b/src/view/Renderer.ts @@ -1,85 +1,62 @@ import {mat4} from 'gl-matrix'; - -import vertexShaderSrc from './default.vs'; import fragmentShaderSrc from './default.fs'; - +import vertexShaderSrc from './default.vs'; export default class Renderer { public gl: WebGLRenderingContext; public vertexPosLoc!: number; public textureCoordLoc!: number; - private viewportLoc!: WebGLUniformLocation; - private translateLoc!: WebGLUniformLocation; public samplerLoc!: WebGLUniformLocation; private viewport: mat4 = mat4.create(); - private mkContext(): WebGLRenderingContext { - let gl = (this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl')) as WebGLRenderingContext|null; - if (!gl) - throw new Error('unable to initialize WebGL context'); - - return gl; - } + private viewportLoc!: WebGLUniformLocation; + private translateLoc!: WebGLUniformLocation; - getAttribLocation(program: WebGLProgram, name: string): number { - let ret = this.gl.getAttribLocation(program, name); - if (ret < 0) - throw new Error("unable to get location of attribute '" + name + "'"); + constructor(private canvas: HTMLCanvasElement) { + this.gl = this.mkContext(); - return ret; - } + this.initShaders(); - getUniformLocation(program: WebGLProgram, name: string): WebGLUniformLocation { - let ret = this.gl.getUniformLocation(program, name); - if (!ret) - throw new Error("unable to get location of uniform '" + name + "'"); + this.gl.clearColor(0.0, 0.0, 0.0, 1.0); - return ret; + this.setSize(); } - createBuffer(): WebGLBuffer { - let ret = this.gl.createBuffer(); + public createBuffer(): WebGLBuffer { + const ret = this.gl.createBuffer(); if (!ret) throw new Error('unable to create buffer'); return ret; } - compileShader(type: number, src: string): WebGLShader { - let shader = this.gl.createShader(type); - if (!shader) - throw new Error('Unable to create shader'); - - this.gl.shaderSource(shader, src); - this.gl.compileShader(shader); - - if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { - let err = this.gl.getShaderInfoLog(shader); - this.gl.deleteShader(shader); - throw new Error('Unable to compile shader: ' + err); - } + private mkContext(): WebGLRenderingContext { + const gl = ( + this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl') + ) as WebGLRenderingContext|null; + if (!gl) + throw new Error('unable to initialize WebGL context'); - return shader; + return gl; } private initShaders(): void { - let shaderProgram = this.gl.createProgram(); + const shaderProgram = this.gl.createProgram(); if (!shaderProgram) throw new Error('Unable to create shader program'); - let vertexShader = this.compileShader(this.gl.VERTEX_SHADER, vertexShaderSrc); - let fragmentShader = this.compileShader(this.gl.FRAGMENT_SHADER, fragmentShaderSrc); - + const vertexShader = this.compileShader(this.gl.VERTEX_SHADER, vertexShaderSrc); + const fragmentShader = this.compileShader(this.gl.FRAGMENT_SHADER, fragmentShaderSrc); this.gl.attachShader(shaderProgram, vertexShader); this.gl.attachShader(shaderProgram, fragmentShader); this.gl.linkProgram(shaderProgram); if (!this.gl.getProgramParameter(shaderProgram, this.gl.LINK_STATUS)) { - let err = this.gl.getProgramInfoLog(shaderProgram); + const err = this.gl.getProgramInfoLog(shaderProgram); this.gl.deleteShader(vertexShader); this.gl.deleteShader(fragmentShader); @@ -102,8 +79,8 @@ export default class Renderer { } private setSize(): void { - let w = this.canvas.width; - let h = this.canvas.height; + const w = this.canvas.width; + const h = this.canvas.height; this.gl.viewport(0, 0, w, h); this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); @@ -115,13 +92,36 @@ export default class Renderer { this.gl.uniform2f(this.translateLoc, -5.0, -5.0); } - constructor(private canvas: HTMLCanvasElement) { - this.gl = this.mkContext(); + private getAttribLocation(program: WebGLProgram, name: string): number { + const ret = this.gl.getAttribLocation(program, name); + if (ret < 0) + throw new Error("unable to get location of attribute '" + name + "'"); - this.initShaders(); + return ret; + } - this.gl.clearColor(0.0, 0.0, 0.0, 1.0); + private getUniformLocation(program: WebGLProgram, name: string): WebGLUniformLocation { + const ret = this.gl.getUniformLocation(program, name); + if (!ret) + throw new Error("unable to get location of uniform '" + name + "'"); - this.setSize(); + return ret; + } + + private compileShader(type: number, src: string): WebGLShader { + const shader = this.gl.createShader(type); + if (!shader) + throw new Error('Unable to create shader'); + + this.gl.shaderSource(shader, src); + this.gl.compileShader(shader); + + if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { + const err = this.gl.getShaderInfoLog(shader); + this.gl.deleteShader(shader); + throw new Error('Unable to compile shader: ' + err); + } + + return shader; } } |