summaryrefslogtreecommitdiffstats
path: root/src/renderer/view
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2019-12-24 13:53:16 +0100
committerMatthias Schiffer <mschiffer@universe-factory.net>2019-12-24 13:53:16 +0100
commit3c51a1994f41b625823c4f15e92396b5498ce23c (patch)
tree7a96310ec32df82ac87039ea555300bcab510a5e /src/renderer/view
parent33926af829848050c54c698ed22da9fe2b912aea (diff)
downloadrpgedit-3c51a1994f41b625823c4f15e92396b5498ce23c.tar
rpgedit-3c51a1994f41b625823c4f15e92396b5498ce23c.zip
Move renderer into "runtime" subdirectory
Diffstat (limited to 'src/renderer/view')
-rw-r--r--src/renderer/view/entity.ts72
-rw-r--r--src/renderer/view/input/gameinput.ts78
-rw-r--r--src/renderer/view/input/inputhandler.ts44
-rw-r--r--src/renderer/view/map.ts173
-rw-r--r--src/renderer/view/renderer/renderer.ts100
-rw-r--r--src/renderer/view/renderer/shaders.ts79
-rw-r--r--src/renderer/view/renderer/shaders/default.fs10
-rw-r--r--src/renderer/view/renderer/shaders/default.vs13
-rw-r--r--src/renderer/view/sprite.ts72
-rw-r--r--src/renderer/view/util/image.ts33
10 files changed, 0 insertions, 674 deletions
diff --git a/src/renderer/view/entity.ts b/src/renderer/view/entity.ts
deleted file mode 100644
index ec91503..0000000
--- a/src/renderer/view/entity.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { EntityData } from '../model/data/entity';
-import { Renderer } from './renderer/renderer';
-import { SpriteCoords, SpriteView, SpriteViewBuilder } from './sprite';
-import { loadImage, mkTexture } from './util/image';
-
-import { getJSON } from '../util';
-
-import { vec2 } from 'gl-matrix';
-
-export class EntityView {
- public static async load(r: Renderer, name: string): Promise<EntityView> {
- const data = new EntityData(await getJSON(`resources/entity/${name}.json`));
- const tile = await loadImage(`resources/sprite/entity/${data.sprite}.png`);
-
- const [texture, size] = mkTexture(r, tile);
- const frameSize = [size[0], size[1] / data.frames];
-
- const offset = vec2.mul(vec2.create(), frameSize, data.anchor);
- r.snapToGrid(offset, offset);
-
- const coords: SpriteCoords = [
- -offset[0],
- -offset[1],
- -offset[0] + frameSize[0],
- -offset[1] + frameSize[1],
- ];
-
- const sprites: SpriteView[] = [];
-
- for (let frame = 0; frame < data.frames; frame++) {
- const builder = new SpriteViewBuilder(r, texture);
- builder.addSprite(coords, [0, frame / data.frames, 1, (frame + 1) / data.frames]);
- sprites.push(builder.build());
- }
-
- return new EntityView(
- data,
- sprites,
- );
- }
-
- private readonly totalTime: number;
-
- private constructor(
- public readonly data: EntityData,
- public readonly sprites: SpriteView[],
- ) {
- if (data.animation)
- this.totalTime = data.animation.sequence.reduce((a, s) => a + s[0], 0);
- else
- this.totalTime = 0;
- }
-
- public getSpriteByTime(time: number): SpriteView {
- time %= this.totalTime;
-
- if (this.data.animation) {
- for (const [len, sprite] of this.data.animation.sequence) {
- time -= len;
- if (time < 0)
- return this.sprites[sprite];
- }
- }
-
- return this.sprites[0];
- }
-
- public renderByTime(time: number) {
- this.getSpriteByTime(time).render();
- }
-
-}
diff --git a/src/renderer/view/input/gameinput.ts b/src/renderer/view/input/gameinput.ts
deleted file mode 100644
index 67fbe0c..0000000
--- a/src/renderer/view/input/gameinput.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { InputHandler } from './inputhandler';
-
-import { Listenable } from '../../util';
-
-import { vec2 } from 'gl-matrix';
-
-export enum ButtonCode {
- Action,
- Back,
- Menu,
-}
-
-const buttonMapping: Record<string, ButtonCode> = {
- KeyZ: ButtonCode.Action,
- KeyX: ButtonCode.Back,
- KeyC: ButtonCode.Menu,
-};
-
-export interface DirectionInput {
- type: 'direction';
- direction: vec2;
-}
-
-export interface ButtonInput {
- type: 'button';
- button: ButtonCode;
-}
-
-export type GameInput = DirectionInput | ButtonInput;
-
-export class GameInputHandler extends Listenable<[GameInput]> {
- private readonly input: InputHandler;
-
- constructor() {
- super();
-
- this.input = new InputHandler(
- new Set([
- 'ArrowLeft',
- 'ArrowUp',
- 'ArrowRight',
- 'ArrowDown',
- ...Object.keys(buttonMapping),
- ]));
-
- this.input.addListener((key: string, pressed: boolean) => {
- const button = buttonMapping[key];
- if (button !== undefined) {
- if (pressed)
- this.runListeners({
- type: 'button',
- button,
- });
-
- return;
- }
-
- const dir = vec2.create();
-
- if (this.input.has('ArrowLeft'))
- vec2.add(dir, dir, [-1, 0]);
- if (this.input.has('ArrowUp'))
- vec2.add(dir, dir, [0, -1]);
- if (this.input.has('ArrowRight'))
- vec2.add(dir, dir, [1, 0]);
- if (this.input.has('ArrowDown'))
- vec2.add(dir, dir, [0, 1]);
-
- if (vec2.sqrLen(dir) > 0)
- vec2.normalize(dir, dir);
-
- this.runListeners({
- type: 'direction',
- direction: dir,
- });
- });
- }
-}
diff --git a/src/renderer/view/input/inputhandler.ts b/src/renderer/view/input/inputhandler.ts
deleted file mode 100644
index 17abfe6..0000000
--- a/src/renderer/view/input/inputhandler.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { Listenable } from '../../util';
-
-export class InputHandler extends Listenable<[string, boolean]> {
- private readonly keys: Set<string> = new Set();
-
- constructor(relevantKeys: Set<string>) {
- super();
-
- window.addEventListener('keydown', (ev) => {
- if (!relevantKeys.has(ev.code))
- return;
-
- ev.preventDefault();
-
- if (ev.repeat)
- return;
-
- this.keys.add(ev.code);
- this.runListeners(ev.code, true);
- });
-
- window.addEventListener('keyup', (ev) => {
- if (!relevantKeys.has(ev.code))
- return;
-
- ev.preventDefault();
-
- if (!this.keys.has(ev.code))
- return;
-
- this.keys.delete(ev.code);
- this.runListeners(ev.code, false);
- });
-
- window.addEventListener('blur', () => {
- this.keys.clear();
- this.runListeners('', false);
- });
- }
-
- public has(key: string): boolean {
- return this.keys.has(key);
- }
-}
diff --git a/src/renderer/view/map.ts b/src/renderer/view/map.ts
deleted file mode 100644
index 18def05..0000000
--- a/src/renderer/view/map.ts
+++ /dev/null
@@ -1,173 +0,0 @@
-import { EntityView } from './entity';
-import { Renderer } from './renderer/renderer';
-import { SpriteCoords, SpriteView, SpriteViewBuilder } from './sprite';
-import { loadImage, mkTexture } from './util/image';
-
-import { MapData } from '../model/data/map';
-
-import { nextPowerOf2 } from '../util';
-
-interface StaticMapTile {
- type: 'static';
- image: HTMLImageElement;
-}
-
-interface EntityTile {
- type: 'entity';
- entity: EntityView;
-}
-
-type MapTile = StaticMapTile | EntityTile;
-
-interface StaticTilesetTile {
- type: 'static';
- coords: SpriteCoords;
-}
-
-type TilesetTile = StaticTilesetTile | EntityTile;
-
-interface Tileset {
- texture: WebGLTexture;
- tiles: TilesetTile[];
-}
-
-async function loadTile(r: Renderer, tile: string): Promise<MapTile> {
- const name = tile.substr(1);
- switch (tile[0]) {
- case '-':
- return {
- type: 'static',
- image: await loadImage(`resources/sprite/tile/${name}.png`),
- };
-
- case '@':
- return {
- type: 'entity',
- entity: await EntityView.load(r, name),
- };
-
- default:
- throw new Error('invalid tile specifier');
- }
-}
-
-function loadTiles(r: Renderer, tiles: string[]): Promise<MapTile[]> {
- return Promise.all(tiles.map((tile) => loadTile(r, tile)));
-}
-
-function mkTileset(
- r: Renderer,
- mapTiles: MapTile[],
-): Tileset {
- const tileSize = 32;
-
- const canvasDim = nextPowerOf2(Math.sqrt(mapTiles.length));
- const canvasSize = canvasDim * tileSize;
-
- const canvas = document.createElement('canvas');
- canvas.width = canvas.height = canvasSize;
-
- let x = 0, y = 0;
- const tiles: TilesetTile[] = [];
- const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
-
- for (const tile of mapTiles) {
- switch (tile.type) {
- case 'static':
- ctx.drawImage(tile.image, x * tileSize, y * tileSize);
- tiles.push({
- type: 'static',
- coords: [x / canvasDim, y / canvasDim, (x + 1) / canvasDim, (y + 1) / canvasDim],
- });
-
- x++;
- if (x === canvasDim) {
- x = 0;
- y++;
- }
- break;
-
- case 'entity':
- tiles.push(tile);
- break;
- }
- }
-
- const [texture] = mkTexture(r, canvas);
-
- return {
- texture,
- tiles,
- };
-}
-
-function addSprite(
- builder: SpriteViewBuilder,
- entityTiles: Array<[[number, number], EntityView]>,
- tileset: Tileset,
- x: number,
- y: number,
- tile: number) {
- if (!tile)
- return;
-
- const tilesetTile = tileset.tiles[tile - 1];
-
- switch (tilesetTile.type) {
- case 'static':
- builder.addSprite([x, y, x + 1, y + 1], tilesetTile.coords);
- break;
-
- case 'entity':
- entityTiles.push([[x + 0.5, y + 0.5], tilesetTile.entity]);
- break;
- }
-}
-
-function buildMapLayer(r: Renderer, tileset: Tileset, layer: number[][]): MapLayerView {
- const builder = new SpriteViewBuilder(r, tileset.texture);
- const entityTiles: Array<[[number, number], EntityView]> = [];
-
- for (let x = 0; x < layer[0].length; x++)
- for (let y = 0; y < layer.length; y++)
- addSprite(builder, entityTiles, tileset, x, y, layer[y][x]);
-
- return new MapLayerView(r, builder.build(), entityTiles);
-}
-
-class MapLayerView {
- public constructor(
- private r: Renderer,
- private staticTiles: SpriteView,
- private entityTiles: Array<[[number, number], EntityView]>,
- ) {
- }
-
- public render(time: number): void {
- this.r.setTranslation([0, 0]);
- this.staticTiles.render();
-
- for (const [coords, entity] of this.entityTiles) {
- this.r.setTranslation(coords);
- entity.renderByTime(time);
- }
- }
-}
-
-export class MapView {
- public static async load(r: Renderer, map: MapData): Promise<MapView> {
- const tiles = await loadTiles(r, map.tiles);
- const tileset = mkTileset(r, tiles);
-
- const layers = map.layers.map((layer) => buildMapLayer(r, tileset, layer.tiles));
- return new MapView(layers);
- }
-
- private constructor(private layers: MapLayerView[]) {
- }
-
- public render(time: number): void {
- for (const layer of this.layers)
- layer.render(time);
- }
-}
diff --git a/src/renderer/view/renderer/renderer.ts b/src/renderer/view/renderer/renderer.ts
deleted file mode 100644
index 9f711d3..0000000
--- a/src/renderer/view/renderer/renderer.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { nextPowerOf2 } from '../../util';
-import { Shaders } from './shaders';
-
-import { mat4, vec2 } from 'gl-matrix';
-
-export class Renderer {
- public readonly coordScale = 32;
- private readonly viewScale = 2;
-
- private readonly gl: WebGLRenderingContext;
- private readonly shaders: Shaders;
-
- private readonly center: vec2 = vec2.create();
- private readonly translation: vec2 = vec2.create();
- private readonly viewport: mat4 = mat4.create();
-
- constructor(private readonly canvas: HTMLCanvasElement) {
- this.gl = this.mkContext();
-
- this.shaders = new Shaders(this.gl);
-
- this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
-
- this.gl.enable(this.gl.BLEND);
- this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
- this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
- }
-
- public createBuffer(): WebGLBuffer {
- const ret = this.gl.createBuffer();
- if (!ret)
- throw new Error('unable to create buffer');
-
- return ret;
- }
-
- public getContext(): WebGLRenderingContext {
- return this.gl;
- }
-
- public getVertexPosLoc(): number {
- return this.shaders.vertexPosLoc;
- }
-
- public getTextureCoordLoc(): number {
- return this.shaders.textureCoordLoc;
- }
-
- public getSamplerLoc(): WebGLUniformLocation {
- return this.shaders.samplerLoc;
- }
-
- public setCenter(v: vec2|number[]) {
- this.snapToGrid(this.center, v);
- }
-
- public setTranslation(v: vec2|number[]) {
- vec2.sub(this.translation, v, this.center);
- this.snapToGrid(this.translation, this.translation);
- this.gl.uniform2fv(this.shaders.translateLoc, this.translation);
- }
-
- public clear(): void {
- this.gl.clear(this.gl.COLOR_BUFFER_BIT);
-
- this.setTranslation([0, 0]);
- }
-
- public snapToGrid(out: vec2, a: vec2|number[]): void {
- vec2.scale(out, a, this.coordScale);
- vec2.round(out, out);
- vec2.scale(out, out, 1 / this.coordScale);
- }
-
- public resize(): void {
- const w = this.canvas.width;
- const h = this.canvas.height;
- const ws = nextPowerOf2(w);
- const hs = nextPowerOf2(h);
-
- this.gl.viewport((w - ws) / 2, (h - hs) / 2, ws, hs);
- this.clear();
-
- const scale = this.viewScale * this.coordScale;
-
- mat4.identity(this.viewport);
- mat4.scale(this.viewport, this.viewport, [2 * scale / ws, -2 * scale / hs, 1.0]);
- this.gl.uniformMatrix4fv(this.shaders.viewportLoc, false, this.viewport);
- }
-
- private mkContext(): WebGLRenderingContext {
- const gl = (
- this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl')
- );
- if (!gl)
- throw new Error('unable to initialize WebGL context');
-
- return gl;
- }
-}
diff --git a/src/renderer/view/renderer/shaders.ts b/src/renderer/view/renderer/shaders.ts
deleted file mode 100644
index 8fd1fda..0000000
--- a/src/renderer/view/renderer/shaders.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import fragmentShaderSrc from './shaders/default.fs';
-import vertexShaderSrc from './shaders/default.vs';
-
-export class Shaders {
- public readonly viewportLoc: WebGLUniformLocation;
- public readonly translateLoc: WebGLUniformLocation;
-
- public readonly vertexPosLoc: number;
- public readonly textureCoordLoc: number;
- public readonly samplerLoc: WebGLUniformLocation;
-
- constructor(private readonly gl: WebGLRenderingContext) {
- const shaderProgram = this.gl.createProgram();
- if (!shaderProgram)
- throw new Error('Unable to create shader program');
-
- 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)) {
- const err = this.gl.getProgramInfoLog(shaderProgram);
-
- this.gl.deleteShader(vertexShader);
- this.gl.deleteShader(fragmentShader);
- this.gl.deleteProgram(shaderProgram);
-
- throw new Error('Unable to link shader: ' + err);
- }
-
- this.gl.useProgram(shaderProgram);
-
- this.vertexPosLoc = this.getAttribLocation(shaderProgram, 'aVertexPos');
- this.gl.enableVertexAttribArray(this.vertexPosLoc);
-
- this.textureCoordLoc = this.getAttribLocation(shaderProgram, 'aTextureCoord');
- this.gl.enableVertexAttribArray(this.textureCoordLoc);
-
- this.viewportLoc = this.getUniformLocation(shaderProgram, 'uViewport');
- this.translateLoc = this.getUniformLocation(shaderProgram, 'uTranslate');
- this.samplerLoc = this.getUniformLocation(shaderProgram, 'uSampler');
- }
-
- 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;
- }
-
- 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 + "'");
-
- return ret;
- }
-
- 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 + "'");
-
- return ret;
- }
-}
diff --git a/src/renderer/view/renderer/shaders/default.fs b/src/renderer/view/renderer/shaders/default.fs
deleted file mode 100644
index 2c8ad15..0000000
--- a/src/renderer/view/renderer/shaders/default.fs
+++ /dev/null
@@ -1,10 +0,0 @@
-precision highp float;
-
-varying vec2 vTextureCoord;
-
-uniform sampler2D uSampler;
-
-
-void main(void) {
- gl_FragColor = texture2D(uSampler, vTextureCoord);
-}
diff --git a/src/renderer/view/renderer/shaders/default.vs b/src/renderer/view/renderer/shaders/default.vs
deleted file mode 100644
index 4715a17..0000000
--- a/src/renderer/view/renderer/shaders/default.vs
+++ /dev/null
@@ -1,13 +0,0 @@
-attribute vec2 aVertexPos;
-attribute vec2 aTextureCoord;
-
-uniform mat4 uViewport;
-uniform vec2 uTranslate;
-
-varying highp vec2 vTextureCoord;
-
-
-void main(void) {
- gl_Position = uViewport * vec4(aVertexPos + uTranslate, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
-}
diff --git a/src/renderer/view/sprite.ts b/src/renderer/view/sprite.ts
deleted file mode 100644
index 3a5ebcb..0000000
--- a/src/renderer/view/sprite.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { Renderer } from './renderer/renderer';
-
-export type SpriteCoords = [number, number, number, number];
-
-export class SpriteViewBuilder {
- private static pushSprite(buf: number[], coords: SpriteCoords): void {
- const [x1, y1, x2, y2] = coords;
-
- buf.push(x1); buf.push(y1);
- buf.push(x2); buf.push(y1);
- buf.push(x1); buf.push(y2);
-
- buf.push(x1); buf.push(y2);
- buf.push(x2); buf.push(y1);
- buf.push(x2); buf.push(y2);
- }
-
- private readonly vertexData: number[] = [];
- private readonly textureData: number[] = [];
-
- constructor(private readonly r: Renderer, private readonly texture: WebGLTexture) {}
-
- public addSprite(vertexCoords: SpriteCoords, texCoords: SpriteCoords): void {
- SpriteViewBuilder.pushSprite(this.vertexData, vertexCoords);
- SpriteViewBuilder.pushSprite(this.textureData, texCoords);
- }
-
- public build(): SpriteView {
- return new SpriteView(this.r, this.texture, this.vertexData, this.textureData);
- }
-}
-
-export class SpriteView {
- private readonly vertexCount: number;
- private readonly vertexBuffer: WebGLBuffer;
- private readonly textureBuffer: WebGLBuffer;
-
- constructor(
- private readonly r: Renderer,
- private readonly texture: WebGLTexture,
- vertexData: number[],
- textureData: number[],
- ) {
- const gl = r.getContext();
-
- this.vertexBuffer = r.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);
-
- this.textureBuffer = r.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, this.textureBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureData), gl.STATIC_DRAW);
-
- this.vertexCount = vertexData.length / 2;
- }
-
- public render(): void {
- const gl = this.r.getContext();
-
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, this.texture);
- gl.uniform1i(this.r.getSamplerLoc(), 0);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
- gl.vertexAttribPointer(this.r.getVertexPosLoc(), 2, gl.FLOAT, false, 0, 0);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, this.textureBuffer);
- gl.vertexAttribPointer(this.r.getTextureCoordLoc(), 2, gl.FLOAT, false, 0, 0);
-
- gl.drawArrays(gl.TRIANGLES, 0, this.vertexCount);
- }
-}
diff --git a/src/renderer/view/util/image.ts b/src/renderer/view/util/image.ts
deleted file mode 100644
index 37c5e7f..0000000
--- a/src/renderer/view/util/image.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Renderer } from '../renderer/renderer';
-
-export function loadImage(url: string): Promise<HTMLImageElement> {
- return new Promise((resolve, reject) => {
- const img = new Image();
- img.addEventListener('load', () => { resolve(img); });
- img.addEventListener('error', () => { reject(new Error('failed to load ' + url)); });
- img.src = url;
- });
-}
-
-export function mkTexture(
- r: Renderer,
- src: HTMLCanvasElement|HTMLImageElement,
-): [WebGLTexture, [number, number]] {
- const gl = r.getContext();
- 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);
-
- const size: [number, number] = [
- src.width / r.coordScale, src.height / r.coordScale,
- ];
-
- return [texture, size];
-}