Add glMatrix

This commit is contained in:
schmelczerandras 2020-07-22 20:00:57 +02:00
parent 582979d3ed
commit e88b4589d2
26 changed files with 211 additions and 358 deletions

View file

@ -22,3 +22,4 @@ A good-looking 2D adventure.
- monitoring - monitoring
- traefik - traefik
- digital ocean docker contianer registry - digital ocean docker contianer registry
- dynamic shader generation

View file

@ -26,11 +26,13 @@
"usedExports": true "usedExports": true
}, },
"devDependencies": { "devDependencies": {
"@types/gl-matrix": "^2.4.5",
"@types/uuid": "^8.0.0", "@types/uuid": "^8.0.0",
"autoprefixer": "^9.8.5", "autoprefixer": "^9.8.5",
"clean-webpack-plugin": "^3.0.0", "clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.5.2", "css-loader": "^3.5.2",
"cssnano": "^4.1.10", "cssnano": "^4.1.10",
"gl-matrix": "^3.3.0",
"html-webpack-inline-source-plugin": "0.0.10", "html-webpack-inline-source-plugin": "0.0.10",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"image-webpack-loader": "^6.0.0", "image-webpack-loader": "^6.0.0",

View file

@ -1,4 +1,9 @@
import './styles/main.scss'; import './styles/main.scss';
import { Game } from './scripts/game'; import { Game } from './scripts/game';
import { applyArrayPlugins } from './scripts/helper/array';
import { glMatrix } from 'gl-matrix';
glMatrix.setMatrixArrayType(Array);
applyArrayPlugins();
new Game(); new Game();

View file

@ -1,8 +1,8 @@
import { Command } from '../command'; import { Command } from '../command';
import { Vec2 } from '../../math/vec2'; import { vec2 } from 'gl-matrix';
export class MoveToCommand extends Command { export class MoveToCommand extends Command {
public constructor(public readonly position?: Vec2) { public constructor(public readonly position?: vec2) {
super(); super();
} }
} }

View file

@ -1,8 +1,8 @@
import { Command } from '../command'; import { Command } from '../command';
import { Vec2 } from '../../math/vec2'; import { vec2 } from 'gl-matrix';
export class PrimaryActionCommand extends Command { export class PrimaryActionCommand extends Command {
public constructor(public readonly position?: Vec2) { public constructor(public readonly position?: vec2) {
super(); super();
} }
} }

View file

@ -1,8 +1,8 @@
import { Command } from '../command'; import { Command } from '../command';
import { Vec2 } from '../../math/vec2'; import { vec2 } from 'gl-matrix';
export class SecondaryActionCommand extends Command { export class SecondaryActionCommand extends Command {
public constructor(public readonly position?: Vec2) { public constructor(public readonly position?: vec2) {
super(); super();
} }
} }

View file

@ -1,8 +1,8 @@
import { Command } from '../command'; import { Command } from '../command';
import { Vec2 } from '../../math/vec2'; import { vec2 } from 'gl-matrix';
export class SwipeCommand extends Command { export class SwipeCommand extends Command {
public constructor(public readonly delta?: Vec2) { public constructor(public readonly delta?: vec2) {
super(); super();
} }
} }

View file

@ -1,12 +1,12 @@
import { Vec2 } from '../math/vec2'; import { vec2 } from 'gl-matrix';
import { Rectangle } from '../math/rectangle';
export interface Drawer { export interface Drawer {
startFrame(): void; startFrame(): void;
finishFrame(): void; finishFrame(): void;
giveUniforms(uniforms: any): void; giveUniforms(uniforms: any): void;
setCameraPosition(position: Vec2): void; setCameraPosition(position: vec2): void;
setInViewArea(size: number): Vec2; setCursorPosition(position: vec2): void;
setInViewArea(size: number): vec2;
drawInfoText(text: string): void; drawInfoText(text: string): void;
isOnScreen(position: Vec2): boolean; isOnScreen(position: vec2): boolean;
} }

View file

@ -1,11 +1,11 @@
import { Vec2 } from '../../math/vec2';
import { FragmentShaderOnlyProgram } from './fragment-shader-only-program'; import { FragmentShaderOnlyProgram } from './fragment-shader-only-program';
import { vec2 } from 'gl-matrix';
export abstract class FrameBuffer { export abstract class FrameBuffer {
public renderScale = 1; public renderScale = 1;
public enableHighDpiRendering = false; public enableHighDpiRendering = false;
protected size: Vec2; protected size: vec2;
protected frameBuffer: WebGLFramebuffer; protected frameBuffer: WebGLFramebuffer;
constructor( constructor(
@ -39,10 +39,10 @@ export abstract class FrameBuffer {
const displayWidth = Math.floor(canvasWidth * realToCssPixels); const displayWidth = Math.floor(canvasWidth * realToCssPixels);
const displayHeight = Math.floor(canvasHeight * realToCssPixels); const displayHeight = Math.floor(canvasHeight * realToCssPixels);
this.size = new Vec2(displayWidth, displayHeight); this.size = vec2.fromValues(displayWidth, displayHeight);
} }
public getSize(): Vec2 { public getSize(): vec2 {
return this.size; return this.size;
} }
} }

View file

@ -1,5 +1,6 @@
import { Vec2 } from '../../math/vec2'; import { mat3, vec2 } from 'gl-matrix';
import { Mat3 } from '../../math/mat3';
const loaderMat3 = mat3.create();
export const loadUniform = ( export const loadUniform = (
gl: WebGL2RenderingContext, gl: WebGL2RenderingContext,
@ -22,23 +23,23 @@ export const loadUniform = (
converters.set( converters.set(
WebGL2RenderingContext.FLOAT_VEC2, WebGL2RenderingContext.FLOAT_VEC2,
(gl, v: Vec2 | Array<Vec2>, l) => { (gl, v: vec2 | Array<vec2>, l) => {
if (v instanceof Array) { if (v[0] instanceof Array) {
const result = new Float32Array(v.length * 2); const result = new Float32Array(v.length * 2);
for (let i = 0; i < v.length; i++) { for (let i = 0; i < v.length; i++) {
result[2 * i] = v[i].x; result[2 * i] = (v[i] as Array<number>).x;
result[2 * i + 1] = v[i].y; result[2 * i + 1] = (v[i] as Array<number>).y;
} }
gl.uniform2fv(l, new Float32Array(result)); gl.uniform2fv(l, new Float32Array(result));
} else { } else {
gl.uniform2fv(l, new Float32Array(v.list)); gl.uniform2fv(l, v as vec2);
} }
} }
); );
converters.set(WebGL2RenderingContext.FLOAT_MAT3, (gl, v: Mat3, l) => converters.set(WebGL2RenderingContext.FLOAT_MAT3, (gl, v, l) => {
gl.uniformMatrix3fv(l, false, new Float32Array(v.transposedFlat)) gl.uniformMatrix3fv(l, true, mat3.fromMat2d(loaderMat3, v));
); });
converters.set(WebGL2RenderingContext.BOOL, (gl, v, l) => converters.set(WebGL2RenderingContext.BOOL, (gl, v, l) =>
gl.uniform1i(l, v) gl.uniform1i(l, v)

View file

@ -1,6 +1,5 @@
import { Drawer } from './drawer'; import { Drawer } from './drawer';
import { Vec2 } from '../math/vec2'; import { mat2d, vec2, mat2, mat3 } from 'gl-matrix';
import { Mat3 } from '../math/mat3';
import { FragmentShaderOnlyProgram } from './graphics-library/fragment-shader-only-program'; import { FragmentShaderOnlyProgram } from './graphics-library/fragment-shader-only-program';
import { WebGlStopwatch } from './graphics-library/stopwatch'; import { WebGlStopwatch } from './graphics-library/stopwatch';
import { Rectangle } from '../math/rectangle'; import { Rectangle } from '../math/rectangle';
@ -14,6 +13,7 @@ export class WebGl2Renderer implements Drawer {
private viewBox: Rectangle = new Rectangle(); private viewBox: Rectangle = new Rectangle();
private nextFrameUniforms: any; private nextFrameUniforms: any;
private cursorPosition = vec2.create();
private frameBuffers: Array<FrameBuffer> = []; private frameBuffers: Array<FrameBuffer> = [];
constructor( constructor(
@ -38,7 +38,7 @@ export class WebGl2Renderer implements Drawer {
]) ])
); );
this.frameBuffers[0].renderScale = 0.2; this.frameBuffers[0].renderScale = 1;
this.frameBuffers[1].renderScale = 1; this.frameBuffers[1].renderScale = 1;
try { try {
@ -53,19 +53,30 @@ export class WebGl2Renderer implements Drawer {
} }
public finishFrame() { public finishFrame() {
const resolution = new Vec2(this.canvas.width, this.canvas.height); const resolution = vec2.fromValues(this.canvas.width, this.canvas.height);
this.nextFrameUniforms.transform = Mat3.translateMatrix(new Vec2(0.5, 0.5)) const transform = mat2d.fromTranslation(
.times( mat2d.create(),
Mat3.scaleMatrix( this.viewBox.topLeft
this.viewBox.size.divide(this.frameBuffers[0].getSize()) );
) mat2d.scale(
transform,
transform,
vec2.divide(
vec2.create(),
this.viewBox.size,
this.frameBuffers[0].getSize()
) )
.times(Mat3.translateMatrix(this.viewBox.topLeft)); );
mat2d.translate(transform, transform, vec2.fromValues(0.5, 0.5));
this.nextFrameUniforms.transform = transform;
this.nextFrameUniforms.transformUV = Mat3.translateMatrix( const transformUV = mat2d.fromScaling(
new Vec2(0.5, 0.5) mat2d.create(),
).times(Mat3.scaleMatrix(new Vec2(1).divide(resolution))); vec2.divide(vec2.create(), vec2.fromValues(1, 1), resolution)
);
mat2d.translate(transformUV, transformUV, vec2.fromValues(0.5, 0.5));
this.nextFrameUniforms.transformUV = transformUV;
this.frameBuffers[0].render(this.nextFrameUniforms); this.frameBuffers[0].render(this.nextFrameUniforms);
this.frameBuffers[1].render( this.frameBuffers[1].render(
@ -76,19 +87,23 @@ export class WebGl2Renderer implements Drawer {
this.stopwatch?.stop(); this.stopwatch?.stop();
} }
public setCameraPosition(position: Vec2) { public setCameraPosition(position: vec2) {
this.viewBox.topLeft = position; this.viewBox.topLeft = position;
} }
public setCursorPosition(position: vec2): void {
this.cursorPosition = position;
}
public giveUniforms(uniforms: any): void { public giveUniforms(uniforms: any): void {
this.nextFrameUniforms = { ...this.nextFrameUniforms, ...uniforms }; this.nextFrameUniforms = { ...this.nextFrameUniforms, ...uniforms };
} }
public setInViewArea(size: number): Vec2 { public setInViewArea(size: number): vec2 {
const canvasAspectRatio = const canvasAspectRatio =
this.canvas.clientWidth / this.canvas.clientHeight; this.canvas.clientWidth / this.canvas.clientHeight;
this.viewBox.size = new Vec2( this.viewBox.size = vec2.fromValues(
Math.sqrt(size * canvasAspectRatio), Math.sqrt(size * canvasAspectRatio),
Math.sqrt(size / canvasAspectRatio) Math.sqrt(size / canvasAspectRatio)
); );
@ -101,7 +116,7 @@ export class WebGl2Renderer implements Drawer {
} }
} }
isOnScreen(position: Vec2): boolean { isOnScreen(position: vec2): boolean {
return this.viewBox.isInside(position); return this.viewBox.isInside(position);
} }
} }

View file

@ -0,0 +1,49 @@
declare global {
interface Array<T> {
x: number;
y: number;
}
interface Float32Array {
x: number;
y: number;
}
}
export const applyArrayPlugins = () => {
Object.defineProperty(Array.prototype, 'x', {
get: function () {
return this[0];
},
set: function (value) {
this[0] = value;
},
});
Object.defineProperty(Array.prototype, 'y', {
get: function () {
return this[1];
},
set: function (value) {
this[1] = value;
},
});
Object.defineProperty(Float32Array.prototype, 'x', {
get: function () {
return this[0];
},
set: function (value) {
this[0] = value;
},
});
Object.defineProperty(Float32Array.prototype, 'y', {
get: function () {
return this[1];
},
set: function (value) {
this[1] = value;
},
});
};

View file

@ -0,0 +1,4 @@
export const clamp = (value: number, min: number, max: number): number =>
Math.min(max, Math.max(min, value));
export const clamp01 = (value: number): number => clamp(value, 0, 1);

View file

@ -1,12 +1,13 @@
import { CommandGenerator } from '../commands/command-generator'; import { CommandGenerator } from '../commands/command-generator';
import { PrimaryActionCommand } from '../commands/types/primary-action'; import { PrimaryActionCommand } from '../commands/types/primary-action';
import { SecondaryActionCommand } from '../commands/types/secondary-action'; import { SecondaryActionCommand } from '../commands/types/secondary-action';
import { Vec2 } from '../math/vec2';
import { SwipeCommand } from '../commands/types/swipe'; import { SwipeCommand } from '../commands/types/swipe';
import { ZoomCommand } from '../commands/types/zoom'; import { ZoomCommand } from '../commands/types/zoom';
import { vec2 } from 'gl-matrix';
import { clamp01 } from '../helper/clamp';
export class MouseListener extends CommandGenerator { export class MouseListener extends CommandGenerator {
private previousPosition: Vec2 = null; private previousPosition = vec2.create();
private isMouseDown = false; private isMouseDown = false;
constructor(private target: Element) { constructor(private target: Element) {
@ -27,7 +28,9 @@ export class MouseListener extends CommandGenerator {
if (this.isMouseDown) { if (this.isMouseDown) {
const position = this.positionFromEvent(event); const position = this.positionFromEvent(event);
this.sendCommand( this.sendCommand(
new SwipeCommand(this.previousPosition.subtract(position)) new SwipeCommand(
vec2.subtract(vec2.create(), this.previousPosition, position)
)
); );
this.previousPosition = position; this.previousPosition = position;
} }
@ -52,12 +55,12 @@ export class MouseListener extends CommandGenerator {
}); });
} }
private positionFromEvent(event: MouseEvent): Vec2 { private positionFromEvent(event: MouseEvent): vec2 {
const bb = this.target.getBoundingClientRect(); const bb = this.target.getBoundingClientRect();
return new Vec2( return vec2.fromValues(
(event.clientX - bb.x) / bb.width, clamp01((event.clientX - bb.x) / bb.width),
1 - (event.clientY - bb.y) / bb.height clamp01(1 - (event.clientY - bb.y) / bb.height)
).clamped_0_1; );
} }
} }

View file

@ -1,11 +1,12 @@
import { CommandGenerator } from '../commands/command-generator'; import { CommandGenerator } from '../commands/command-generator';
import { PrimaryActionCommand } from '../commands/types/primary-action'; import { PrimaryActionCommand } from '../commands/types/primary-action';
import { SecondaryActionCommand } from '../commands/types/secondary-action'; import { SecondaryActionCommand } from '../commands/types/secondary-action';
import { Vec2 } from '../math/vec2';
import { SwipeCommand } from '../commands/types/swipe'; import { SwipeCommand } from '../commands/types/swipe';
import { vec2 } from 'gl-matrix';
import { clamp01 } from '../helper/clamp';
export class TouchListener extends CommandGenerator { export class TouchListener extends CommandGenerator {
private previousPosition: Vec2 = null; private previousPosition = vec2.create();
constructor(private target: HTMLElement) { constructor(private target: HTMLElement) {
super(); super();
@ -30,19 +31,21 @@ export class TouchListener extends CommandGenerator {
const position = this.positionFromEvent(event); const position = this.positionFromEvent(event);
this.sendCommand( this.sendCommand(
new SwipeCommand(position.subtract(this.previousPosition)) new SwipeCommand(
vec2.subtract(vec2.create(), position, this.previousPosition)
)
); );
this.previousPosition = position; this.previousPosition = position;
}); });
} }
private positionFromEvent(event: TouchEvent): Vec2 { private positionFromEvent(event: TouchEvent): vec2 {
const bb = this.target.getBoundingClientRect(); const bb = this.target.getBoundingClientRect();
return new Vec2( return vec2.fromValues(
1 - (event.touches[0].clientX - bb.x) / bb.width, clamp01(1 - (event.touches[0].clientX - bb.x) / bb.width),
(event.touches[0].clientY - bb.y) / bb.height clamp01((event.touches[0].clientY - bb.y) / bb.height)
).clamped_0_1; );
} }
} }

View file

@ -1,78 +0,0 @@
// https://github.com/Azleur/mat3/blob/master/src/index.ts
import { Vec2 } from './vec2';
export class Mat3 {
constructor(private readonly values: Array<Array<number>>) {}
public static get Zero() {
return new Mat3([
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
]);
}
public static get Id() {
return new Mat3([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
]);
}
public static get Ones() {
return new Mat3([
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
]);
}
public static translateMatrix(by: Vec2): Mat3 {
return new Mat3([
[1, 0, 0],
[0, 1, 0],
[by.x, by.y, 1],
]);
}
public static scaleMatrix(by: Vec2): Mat3 {
return new Mat3([
[by.x, 0, 0],
[0, by.y, 0],
[0, 0, 1],
]);
}
public get transposed(): Mat3 {
const values: number[][] = [[], [], []];
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
values[i][j] = this.values[j][i];
}
}
return new Mat3(values);
}
public times(other: Mat3): Mat3 {
const values: number[][] = Mat3.Zero.values;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
for (let k = 0; k < 3; k++) {
values[i][j] += this.values[i][k] * other.values[k][j];
}
}
}
return new Mat3(values);
}
public get transposedFlat(): Array<number> {
const transposed = this.transposed;
return [
...transposed.values[0],
...transposed.values[1],
...transposed.values[2],
];
}
}

View file

@ -1,16 +1,16 @@
import { Typed } from '../transport/serializable'; import { Typed } from '../transport/serializable';
import { Vec2 } from './vec2'; import { vec2 } from 'gl-matrix';
export class Rectangle extends Typed { export class Rectangle extends Typed {
public constructor( public constructor(
public topLeft: Vec2 = new Vec2(), public topLeft: vec2 = vec2.create(),
public size: Vec2 = new Vec2() public size: vec2 = vec2.create()
) { ) {
super(); super();
} }
public isInside(position: Vec2): boolean { public isInside(position: vec2): boolean {
const translated = position.subtract(this.topLeft); const translated = vec2.subtract(vec2.create(), position, this.topLeft);
return ( return (
0 <= translated.x && 0 <= translated.x &&
translated.x < this.size.x && translated.x < this.size.x &&

View file

@ -1,50 +0,0 @@
import { Typed } from '../transport/serializable';
export class Vec2 extends Typed {
public constructor(public x: number = 0.0, public y: number = null) {
super();
if (this.y === null) {
this.y = this.x;
}
}
public scale(scalar: number): Vec2 {
return new Vec2(this.x * scalar, this.y * scalar);
}
public add(other: Vec2): Vec2 {
return new Vec2(this.x + other.x, this.y + other.y);
}
public subtract(other: Vec2): Vec2 {
return new Vec2(this.x - other.x, this.y - other.y);
}
public times(other: Vec2): Vec2 {
return new Vec2(this.x * other.x, this.y * other.y);
}
public divide(other: Vec2): Vec2 {
return new Vec2(this.x / other.x, this.y / other.y);
}
public get length(): number {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
public get normalized(): Vec2 {
return this.scale(1 / this.length);
}
public get clamped_0_1(): Vec2 {
return new Vec2(
Math.min(1, Math.max(0, this.x)),
Math.min(1, Math.max(0, this.y))
);
}
public get list(): [number, number] {
return [this.x, this.y];
}
}

View file

@ -1,19 +1,19 @@
import { Typed } from '../transport/serializable'; import { Typed } from '../transport/serializable';
import { Vec2 } from '../math/vec2';
import { IdentityManager } from '../identity/identity-manager'; import { IdentityManager } from '../identity/identity-manager';
import { Command } from '../commands/command'; import { Command } from '../commands/command';
import { CommandReceiver } from '../commands/command-receiver'; import { CommandReceiver } from '../commands/command-receiver';
import { vec2 } from 'gl-matrix';
export abstract class GameObject extends Typed implements CommandReceiver { export abstract class GameObject extends Typed implements CommandReceiver {
public readonly id = IdentityManager.generateId(); public readonly id = IdentityManager.generateId();
protected _position = new Vec2(); protected _position = vec2.create();
public get position(): Vec2 { public get position(): vec2 {
return this._position; return this._position;
} }
protected _boundingBoxSize = new Vec2(); protected _boundingBoxSize = vec2.create();
public get boundingBoxSize(): Vec2 { public get boundingBoxSize(): vec2 {
return this._boundingBoxSize; return this._boundingBoxSize;
} }

View file

@ -2,9 +2,12 @@ import { GameObject } from '../game-object';
import { MoveToCommand } from '../../commands/types/move-to'; import { MoveToCommand } from '../../commands/types/move-to';
import { ZoomCommand } from '../../commands/types/zoom'; import { ZoomCommand } from '../../commands/types/zoom';
import { BeforeDrawCommand } from '../../commands/types/before-draw'; import { BeforeDrawCommand } from '../../commands/types/before-draw';
import { PrimaryActionCommand } from '../../commands/types/primary-action';
import { vec2 } from 'gl-matrix';
export class Camera extends GameObject { export class Camera extends GameObject {
private inViewArea = 1920 * 1080; private inViewArea = 1920 * 1080;
private cursorPosition = vec2.create();
constructor() { constructor() {
super(); super();
@ -12,10 +15,15 @@ export class Camera extends GameObject {
this.addCommandExecutor(BeforeDrawCommand, this.draw.bind(this)); this.addCommandExecutor(BeforeDrawCommand, this.draw.bind(this));
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this)); this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
this.addCommandExecutor(ZoomCommand, this.zoom.bind(this)); this.addCommandExecutor(ZoomCommand, this.zoom.bind(this));
this.addCommandExecutor(
PrimaryActionCommand,
this.setCursorPosition.bind(this)
);
} }
private draw(c: BeforeDrawCommand) { private draw(c: BeforeDrawCommand) {
c.drawer.setCameraPosition(this.position); c.drawer.setCameraPosition(this.position);
c.drawer.setCursorPosition(this.cursorPosition);
this._boundingBoxSize = c.drawer.setInViewArea(this.inViewArea); this._boundingBoxSize = c.drawer.setInViewArea(this.inViewArea);
} }
@ -26,4 +34,8 @@ export class Camera extends GameObject {
private zoom(c: ZoomCommand) { private zoom(c: ZoomCommand) {
this.inViewArea *= c.factor; this.inViewArea *= c.factor;
} }
private setCursorPosition(c: PrimaryActionCommand) {
this.cursorPosition = c.position;
}
} }

View file

@ -1,5 +1,4 @@
import { GameObject } from '../game-object'; import { GameObject } from '../game-object';
import { Vec2 } from '../../math/vec2';
import { ObjectContainer } from '../object-container'; import { ObjectContainer } from '../object-container';
import { Camera } from './camera'; import { Camera } from './camera';
import { MoveToCommand } from '../../commands/types/move-to'; import { MoveToCommand } from '../../commands/types/move-to';
@ -7,6 +6,7 @@ import { StepCommand } from '../../commands/types/step';
import { KeyDownCommand } from '../../commands/types/key-down'; import { KeyDownCommand } from '../../commands/types/key-down';
import { KeyUpCommand } from '../../commands/types/key-up'; import { KeyUpCommand } from '../../commands/types/key-up';
import { SwipeCommand } from '../../commands/types/swipe'; import { SwipeCommand } from '../../commands/types/swipe';
import { vec2 } from 'gl-matrix';
export class Character extends GameObject { export class Character extends GameObject {
private keysDown: Set<string> = new Set(); private keysDown: Set<string> = new Set();
@ -23,12 +23,16 @@ export class Character extends GameObject {
this.addCommandExecutor(KeyUpCommand, (c) => this.keysDown.delete(c.key)); this.addCommandExecutor(KeyUpCommand, (c) => this.keysDown.delete(c.key));
this.addCommandExecutor(SwipeCommand, (c) => this.addCommandExecutor(SwipeCommand, (c) =>
this.setPosition( this.setPosition(
this.position.add(c.delta.times(this.camera.boundingBoxSize)) vec2.add(
vec2.create(),
this.position,
vec2.multiply(vec2.create(), c.delta, this.camera.boundingBoxSize)
)
) )
); );
} }
private setPosition(value: Vec2) { private setPosition(value: vec2) {
this._position = value; this._position = value;
this.camera.sendCommand(new MoveToCommand(this.position)); this.camera.sendCommand(new MoveToCommand(this.position));
} }
@ -41,11 +45,17 @@ export class Character extends GameObject {
const left = ~~this.keysDown.has('a'); const left = ~~this.keysDown.has('a');
const right = ~~this.keysDown.has('d'); const right = ~~this.keysDown.has('d');
const movementVector = new Vec2(right - left, up - down); const movementVector = vec2.fromValues(right - left, up - down);
if (movementVector.length > 0) { if (movementVector.length > 0) {
this.setPosition( this.setPosition(
this.position.add( vec2.add(
movementVector.normalized.scale(Character.speed * deltaTime) vec2.create(),
this.position,
vec2.scale(
vec2.create(),
vec2.normalize(movementVector, movementVector),
Character.speed * deltaTime
)
) )
); );
} }

View file

@ -1,12 +1,12 @@
import { GameObject } from '../game-object'; import { GameObject } from '../game-object';
import { DrawCommand } from '../../commands/types/draw'; import { DrawCommand } from '../../commands/types/draw';
import { Vec2 } from '../../math/vec2'; import { vec2 } from 'gl-matrix';
import { last } from '../../helper/last';
export interface Line { export interface Line {
start: Vec2; start: vec2;
end: Vec2; end: vec2;
radius: number; radiusFrom: number;
radiusTo: number;
} }
export class Dungeon extends GameObject { export class Dungeon extends GameObject {
@ -17,33 +17,36 @@ export class Dungeon extends GameObject {
this.addCommandExecutor(DrawCommand, this.draw.bind(this)); this.addCommandExecutor(DrawCommand, this.draw.bind(this));
let previousHeight = 0; let previousRadius = 0;
let previousEnd = new Vec2(); let previousEnd = vec2.create();
for (let i = 0; i < 5000; i += 50) { for (let i = 0; i < 5000; i += 50) {
const height = previousHeight + (Math.random() - 0.5) * 200; const height = previousEnd.y + (Math.random() - 0.5) * 200;
previousHeight = height; const currentEnd = vec2.fromValues(i, height);
const currentEnd = new Vec2(i, height); const currentToRadius = Math.random() * 10 + 30;
this.lines.push({ this.lines.push({
start: previousEnd, start: previousEnd,
end: currentEnd, end: currentEnd,
radius: Math.random() * 15 + 15, radiusFrom: previousRadius,
radiusTo: currentToRadius,
}); });
previousEnd = currentEnd; previousEnd = currentEnd;
previousRadius = currentToRadius;
} }
} }
private draw(c: DrawCommand) { private draw(c: DrawCommand) {
const lines: Array<Vec2> = []; const lines: Array<vec2> = [];
const radii: Array<number> = []; const radii: Array<number> = [];
for (let line of this.lines) { for (let line of this.lines) {
if (c.drawer.isOnScreen(line.start) || c.drawer.isOnScreen(line.end)) { if (c.drawer.isOnScreen(line.start) || c.drawer.isOnScreen(line.end)) {
lines.push(line.start); lines.push(line.start);
lines.push(line.end); lines.push(line.end);
radii.push(line.radius); radii.push(line.radiusFrom);
radii.push(line.radiusTo);
} }
} }

View file

@ -8,12 +8,18 @@ precision mediump float;
uniform vec2[LINE_COUNT * 2] lines; uniform vec2[LINE_COUNT * 2] lines;
uniform float[LINE_COUNT] radii; uniform float[LINE_COUNT * 2] radii;
float lineDistance(in vec2 target, in vec2 start, in vec2 end, in float radius) { float lineDistance(
in vec2 target,
in vec2 start,
in vec2 end,
in float radiusFrom,
in float radiusTo
) {
vec2 pa = target - start, ba = end - start; vec2 pa = target - start, ba = end - start;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
return distance(pa, ba * h) - radius; return distance(pa, ba * h) - mix(radiusFrom, radiusTo, smoothstep(0.0, 1.0, h));
} }
float getDistance(in vec2 target) { float getDistance(in vec2 target) {
@ -22,8 +28,9 @@ float getDistance(in vec2 target) {
for (int i = 0; i < LINE_COUNT; i++) { for (int i = 0; i < LINE_COUNT; i++) {
vec2 start = lines[2 * i]; vec2 start = lines[2 * i];
vec2 end = lines[2 * i + 1]; vec2 end = lines[2 * i + 1];
float r = radii[i]; float rFrom = radii[2 * i];
minDistance = min(minDistance, lineDistance(target, start, end, r)); float rTo = radii[2 * i + 1];
minDistance = min(minDistance, lineDistance(target, start, end, rFrom, rTo));
} }
return -minDistance; return -minDistance;

View file

@ -1,135 +0,0 @@
#version 300 es
precision mediump float;
#define INFINITY 1.0 / 0.0
#define WORLD_SIZE 4
#define N (20)
#define CIRCLE 1
#define LINE 2
#define TRIANGLE 3
out vec4 fragmentColor;
uniform vec2 resolution;
uniform float time;
struct Material {
vec3 color;
};
struct Primitive {
uint type;
uint index;
};
struct Object {
Material material;
Primitive[10] primitives;
};
struct Circle {
uint parent;
vec2 center;
float radius;
};
struct Line {
uint parent;
vec2 a;
vec2 b;
};
struct Triangle {
uint parent;
vec2 a;
vec2 b;
vec2 c;
};
// uniform Object[100] objects;
Circle[3] circles;
Line[1] lines;
Triangle[1] triangles;
float triangleDistance(in vec2 position, in Triangle triangle)
{
vec2 e0 = triangle.b - triangle.a, e1 = triangle.c - triangle.b, e2 = triangle.a - triangle.c;
vec2 v0 = position - triangle.a, v1 = position - triangle.b, v2 = position - triangle.c;
vec2 pq0 = v0 - e0 * clamp(dot(v0, e0) / dot(e0, e0), 0.0, 1.0);
vec2 pq1 = v1 - e1 * clamp(dot(v1, e1) / dot(e1, e1), 0.0, 1.0);
vec2 pq2 = v2 - e2 * clamp(dot(v2, e2) / dot(e2, e2), 0.0, 1.0);
float s = sign(e0.x*e2.y - e0.y*e2.x);
vec2 d = min(min(vec2(dot(pq0,pq0), s*(v0.x*e0.y-v0.y*e0.x)),
vec2(dot(pq1,pq1), s*(v1.x*e1.y-v1.y*e1.x))),
vec2(dot(pq2,pq2), s*(v2.x*e2.y-v2.y*e2.x)));
return -sqrt(d.x)*sign(d.y);
}
float lineDistance(in vec2 position, in Line line)
{
vec2 pa = position - line.a, ba = line.b - line.a;
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
return length(pa - ba*h);
}
float circleDistance(in vec2 position, in Circle circle)
{
return length(position - circle.center) - circle.radius;
}
float getDistance(in vec2 target, out uint nearestParentIndex) {
float distance = INFINITY;
for (int i = 0; i < circles.length(); i++) {
float distanceToCurrent = circleDistance(target, circles[i]);
if (distanceToCurrent < distance) {
distance = distanceToCurrent;
nearestParentIndex = circles[i].parent;
}
}
for (int i = 0; i < lines.length(); i++) {
float distanceToCurrent = lineDistance(target, lines[i]);
if (distanceToCurrent < distance) {
distance = distanceToCurrent;
nearestParentIndex = lines[i].parent;
}
}
for (int i = 0; i < triangles.length(); i++) {
float distanceToCurrent = triangleDistance(target, triangles[i]);
if (distanceToCurrent < distance) {
distance = distanceToCurrent;
nearestParentIndex = triangles[i].parent;
}
}
return distance;
}
void createWorld() {
circles[0] = Circle(0u, vec2(250.0, 100.0), 12.5);
circles[1] = Circle(0u, vec2(150.0, 50.0), 32.5);
circles[2] = Circle(0u, vec2(300.0, 350.0), 52.5);
lines[0] = Line(0u, vec2(100.0, 300.0), vec2(550.0, 140.0));
triangles[0] = Triangle(0u, vec2(400.0, 100.0), vec2(200.0, 240.0), vec2(600.0, 340.0));
}
float linearstep(float a, float b, float q) {
return a + clamp((b - a) * q, 0.0, 1.0);
}
void main() {
createWorld();
vec2 position = gl_FragCoord.xy + vec2(0.5);
uint index;
vec3 color = vec3(1.0) * linearstep(0.0, 1.0, getDistance(position, index));
fragmentColor = vec4(color, 1.0);
}

View file

@ -28,5 +28,6 @@ out vec4 fragmentColor;
void main() { void main() {
vec2 position = (vec3(gl_FragCoord.xy, 1.0) * transformUV).xy; vec2 position = (vec3(gl_FragCoord.xy, 1.0) * transformUV).xy;
vec4 previous = texture(distanceTexture, position); vec4 previous = texture(distanceTexture, position);
fragmentColor = smoothRainbow(previous.a); //fragmentColor = smoothRainbow(previous.a);
fragmentColor = previous.a > 0.5 ? vec4(1.0, 1.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0);
} }