From c9000b7c385b530bddf87cfed4b580b5d1a2d6d2 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 10 Nov 2018 20:24:25 +0100 Subject: view: add support for non-square, non-power-of-2 sprites --- src/view/entity.ts | 18 ++++++++++++++---- src/view/map.ts | 8 +++++--- src/view/renderer/renderer.ts | 11 +++++++---- src/view/util/image.ts | 31 ++++++++++++++++++++++++++++--- 4 files changed, 54 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/view/entity.ts b/src/view/entity.ts index 421a238..b59bf92 100644 --- a/src/view/entity.ts +++ b/src/view/entity.ts @@ -1,18 +1,28 @@ import { EntityData } from '../model/data/entity'; import { Renderer } from './renderer/renderer'; -import { SpriteView, SpriteViewBuilder } from './sprite'; +import { SpriteCoords, SpriteView, SpriteViewBuilder } from './sprite'; import { loadImage, mkTexture } from './util/image'; +import { vec2 } from 'gl-matrix'; + export async function loadEntity( r: Renderer, data: EntityData, ): Promise { const tile = await loadImage(`resources/sprite/entity/${data.sprite}.png`); - const texture = mkTexture(r.getContext(), tile); + const [texture, size, coords] = mkTexture(r, tile); + + const offset = vec2.mul(vec2.create(), data.anchor, size); + r.snapToGrid(offset, offset); - const [x, y] = data.anchor; + const anchorCoords: SpriteCoords = [ + coords[0] - offset[0], + coords[1] - offset[1], + coords[2] - offset[0], + coords[3] - offset[1], + ]; const builder = new SpriteViewBuilder(r, texture); - builder.addSprite([-x, -y, -x + 1, -y + 1], [0, 0, 1, 1]); + builder.addSprite(anchorCoords, [0, 0, 1, 1]); return builder.build(); } diff --git a/src/view/map.ts b/src/view/map.ts index ff37737..9bdbd30 100644 --- a/src/view/map.ts +++ b/src/view/map.ts @@ -16,7 +16,7 @@ function loadTiles(tiles: string[]): Promise { } function mkTileset( - gl: WebGLRenderingContext, + r: Renderer, tiles: HTMLImageElement[], ): Tileset { const tileSize = 32; @@ -42,8 +42,10 @@ function mkTileset( } } + const [texture] = mkTexture(r, canvas); + return { - texture: mkTexture(gl, canvas), + texture, tiles: tileCoords, }; } @@ -58,7 +60,7 @@ function addSprite(builder: SpriteViewBuilder, tileset: Tileset, x: number, y: n export async function loadMap(r: Renderer, map: MapData): Promise { const tiles = await loadTiles(map.tiles); - const tileset = mkTileset(r.getContext(), tiles); + const tileset = mkTileset(r, tiles); const builder = new SpriteViewBuilder(r, tileset.texture); diff --git a/src/view/renderer/renderer.ts b/src/view/renderer/renderer.ts index 062c868..93f8589 100644 --- a/src/view/renderer/renderer.ts +++ b/src/view/renderer/renderer.ts @@ -3,7 +3,8 @@ import { Shaders } from './shaders'; import { mat4, vec2 } from 'gl-matrix'; export class Renderer { - private readonly viewScale = 64; + public readonly coordScale = 32; + private readonly viewScale = 2; private readonly gl: WebGLRenderingContext; private readonly shaders: Shaders; @@ -67,9 +68,9 @@ export class Renderer { } public snapToGrid(out: vec2, a: vec2|number[]): void { - vec2.scale(out, a, this.viewScale); + vec2.scale(out, a, this.coordScale); vec2.round(out, out); - vec2.scale(out, out, 1 / this.viewScale); + vec2.scale(out, out, 1 / this.coordScale); } private mkContext(): WebGLRenderingContext { @@ -89,8 +90,10 @@ export class Renderer { this.gl.viewport(0, 0, w, h); this.clear(); + const scale = this.viewScale * this.coordScale; + mat4.identity(this.viewport); - mat4.scale(this.viewport, this.viewport, [2 * this.viewScale / w, -2 * this.viewScale / h, 1.0]); + mat4.scale(this.viewport, this.viewport, [2 * scale / w, -2 * scale / h, 1.0]); this.gl.uniformMatrix4fv(this.shaders.viewportLoc, false, this.viewport); } } diff --git a/src/view/util/image.ts b/src/view/util/image.ts index 73edbba..e01246c 100644 --- a/src/view/util/image.ts +++ b/src/view/util/image.ts @@ -1,3 +1,7 @@ +import { nextPowerOf2 } from '../../util'; +import { Renderer } from '../renderer/renderer'; +import { SpriteCoords } from '../sprite'; + export function loadImage(url: string): Promise { return new Promise((resolve, reject) => { const img = new Image(); @@ -7,17 +11,38 @@ export function loadImage(url: string): Promise { }); } -export function mkTexture(gl: WebGLRenderingContext, src: HTMLCanvasElement|HTMLImageElement): WebGLTexture { +export function mkTexture( + r: Renderer, + src: HTMLCanvasElement|HTMLImageElement, +): [WebGLTexture, [number, number], SpriteCoords] { + const gl = r.getContext(); const texture = gl.createTexture(); if (!texture) throw new Error('unable to create texture'); + const w = src.width, h = src.height; + const w2 = nextPowerOf2(w), h2 = nextPowerOf2(h); + + const canvas = document.createElement('canvas'); + canvas.width = w2; + canvas.height = h2; + + const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + ctx.drawImage(src, 0, 0); + gl.bindTexture(gl.TEXTURE_2D, texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, src); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas); 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 size: [number, number] = [ + w / r.coordScale, h / r.coordScale, + ]; + const coords: SpriteCoords = [ + 0, 0, w2 / r.coordScale, h2 / r.coordScale, + ]; + + return [texture, size, coords]; } -- cgit v1.2.3