Add glMatrix
This commit is contained in:
parent
582979d3ed
commit
e88b4589d2
26 changed files with 211 additions and 358 deletions
|
|
@ -22,3 +22,4 @@ A good-looking 2D adventure.
|
|||
- monitoring
|
||||
- traefik
|
||||
- digital ocean docker contianer registry
|
||||
- dynamic shader generation
|
||||
|
|
|
|||
|
|
@ -26,11 +26,13 @@
|
|||
"usedExports": true
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/gl-matrix": "^2.4.5",
|
||||
"@types/uuid": "^8.0.0",
|
||||
"autoprefixer": "^9.8.5",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"css-loader": "^3.5.2",
|
||||
"cssnano": "^4.1.10",
|
||||
"gl-matrix": "^3.3.0",
|
||||
"html-webpack-inline-source-plugin": "0.0.10",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"image-webpack-loader": "^6.0.0",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import './styles/main.scss';
|
||||
import { Game } from './scripts/game';
|
||||
import { applyArrayPlugins } from './scripts/helper/array';
|
||||
import { glMatrix } from 'gl-matrix';
|
||||
|
||||
glMatrix.setMatrixArrayType(Array);
|
||||
applyArrayPlugins();
|
||||
|
||||
new Game();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Command } from '../command';
|
||||
import { Vec2 } from '../../math/vec2';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export class MoveToCommand extends Command {
|
||||
public constructor(public readonly position?: Vec2) {
|
||||
public constructor(public readonly position?: vec2) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Command } from '../command';
|
||||
import { Vec2 } from '../../math/vec2';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export class PrimaryActionCommand extends Command {
|
||||
public constructor(public readonly position?: Vec2) {
|
||||
public constructor(public readonly position?: vec2) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Command } from '../command';
|
||||
import { Vec2 } from '../../math/vec2';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export class SecondaryActionCommand extends Command {
|
||||
public constructor(public readonly position?: Vec2) {
|
||||
public constructor(public readonly position?: vec2) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Command } from '../command';
|
||||
import { Vec2 } from '../../math/vec2';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export class SwipeCommand extends Command {
|
||||
public constructor(public readonly delta?: Vec2) {
|
||||
public constructor(public readonly delta?: vec2) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { Vec2 } from '../math/vec2';
|
||||
import { Rectangle } from '../math/rectangle';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export interface Drawer {
|
||||
startFrame(): void;
|
||||
finishFrame(): void;
|
||||
giveUniforms(uniforms: any): void;
|
||||
setCameraPosition(position: Vec2): void;
|
||||
setInViewArea(size: number): Vec2;
|
||||
setCameraPosition(position: vec2): void;
|
||||
setCursorPosition(position: vec2): void;
|
||||
setInViewArea(size: number): vec2;
|
||||
drawInfoText(text: string): void;
|
||||
isOnScreen(position: Vec2): boolean;
|
||||
isOnScreen(position: vec2): boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { Vec2 } from '../../math/vec2';
|
||||
import { FragmentShaderOnlyProgram } from './fragment-shader-only-program';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export abstract class FrameBuffer {
|
||||
public renderScale = 1;
|
||||
public enableHighDpiRendering = false;
|
||||
|
||||
protected size: Vec2;
|
||||
protected size: vec2;
|
||||
protected frameBuffer: WebGLFramebuffer;
|
||||
|
||||
constructor(
|
||||
|
|
@ -39,10 +39,10 @@ export abstract class FrameBuffer {
|
|||
const displayWidth = Math.floor(canvasWidth * 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Vec2 } from '../../math/vec2';
|
||||
import { Mat3 } from '../../math/mat3';
|
||||
import { mat3, vec2 } from 'gl-matrix';
|
||||
|
||||
const loaderMat3 = mat3.create();
|
||||
|
||||
export const loadUniform = (
|
||||
gl: WebGL2RenderingContext,
|
||||
|
|
@ -22,23 +23,23 @@ export const loadUniform = (
|
|||
|
||||
converters.set(
|
||||
WebGL2RenderingContext.FLOAT_VEC2,
|
||||
(gl, v: Vec2 | Array<Vec2>, l) => {
|
||||
if (v instanceof Array) {
|
||||
(gl, v: vec2 | Array<vec2>, l) => {
|
||||
if (v[0] instanceof Array) {
|
||||
const result = new Float32Array(v.length * 2);
|
||||
for (let i = 0; i < v.length; i++) {
|
||||
result[2 * i] = v[i].x;
|
||||
result[2 * i + 1] = v[i].y;
|
||||
result[2 * i] = (v[i] as Array<number>).x;
|
||||
result[2 * i + 1] = (v[i] as Array<number>).y;
|
||||
}
|
||||
gl.uniform2fv(l, new Float32Array(result));
|
||||
} else {
|
||||
gl.uniform2fv(l, new Float32Array(v.list));
|
||||
gl.uniform2fv(l, v as vec2);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
converters.set(WebGL2RenderingContext.FLOAT_MAT3, (gl, v: Mat3, l) =>
|
||||
gl.uniformMatrix3fv(l, false, new Float32Array(v.transposedFlat))
|
||||
);
|
||||
converters.set(WebGL2RenderingContext.FLOAT_MAT3, (gl, v, l) => {
|
||||
gl.uniformMatrix3fv(l, true, mat3.fromMat2d(loaderMat3, v));
|
||||
});
|
||||
|
||||
converters.set(WebGL2RenderingContext.BOOL, (gl, v, l) =>
|
||||
gl.uniform1i(l, v)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Drawer } from './drawer';
|
||||
import { Vec2 } from '../math/vec2';
|
||||
import { Mat3 } from '../math/mat3';
|
||||
import { mat2d, vec2, mat2, mat3 } from 'gl-matrix';
|
||||
import { FragmentShaderOnlyProgram } from './graphics-library/fragment-shader-only-program';
|
||||
import { WebGlStopwatch } from './graphics-library/stopwatch';
|
||||
import { Rectangle } from '../math/rectangle';
|
||||
|
|
@ -14,6 +13,7 @@ export class WebGl2Renderer implements Drawer {
|
|||
|
||||
private viewBox: Rectangle = new Rectangle();
|
||||
private nextFrameUniforms: any;
|
||||
private cursorPosition = vec2.create();
|
||||
private frameBuffers: Array<FrameBuffer> = [];
|
||||
|
||||
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;
|
||||
|
||||
try {
|
||||
|
|
@ -53,19 +53,30 @@ export class WebGl2Renderer implements Drawer {
|
|||
}
|
||||
|
||||
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))
|
||||
.times(
|
||||
Mat3.scaleMatrix(
|
||||
this.viewBox.size.divide(this.frameBuffers[0].getSize())
|
||||
const transform = mat2d.fromTranslation(
|
||||
mat2d.create(),
|
||||
this.viewBox.topLeft
|
||||
);
|
||||
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(
|
||||
new Vec2(0.5, 0.5)
|
||||
).times(Mat3.scaleMatrix(new Vec2(1).divide(resolution)));
|
||||
const transformUV = mat2d.fromScaling(
|
||||
mat2d.create(),
|
||||
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[1].render(
|
||||
|
|
@ -76,19 +87,23 @@ export class WebGl2Renderer implements Drawer {
|
|||
this.stopwatch?.stop();
|
||||
}
|
||||
|
||||
public setCameraPosition(position: Vec2) {
|
||||
public setCameraPosition(position: vec2) {
|
||||
this.viewBox.topLeft = position;
|
||||
}
|
||||
|
||||
public setCursorPosition(position: vec2): void {
|
||||
this.cursorPosition = position;
|
||||
}
|
||||
|
||||
public giveUniforms(uniforms: any): void {
|
||||
this.nextFrameUniforms = { ...this.nextFrameUniforms, ...uniforms };
|
||||
}
|
||||
|
||||
public setInViewArea(size: number): Vec2 {
|
||||
public setInViewArea(size: number): vec2 {
|
||||
const canvasAspectRatio =
|
||||
this.canvas.clientWidth / this.canvas.clientHeight;
|
||||
|
||||
this.viewBox.size = new Vec2(
|
||||
this.viewBox.size = vec2.fromValues(
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
49
frontend/src/scripts/helper/array.ts
Normal file
49
frontend/src/scripts/helper/array.ts
Normal 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;
|
||||
},
|
||||
});
|
||||
};
|
||||
4
frontend/src/scripts/helper/clamp.ts
Normal file
4
frontend/src/scripts/helper/clamp.ts
Normal 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);
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
import { CommandGenerator } from '../commands/command-generator';
|
||||
import { PrimaryActionCommand } from '../commands/types/primary-action';
|
||||
import { SecondaryActionCommand } from '../commands/types/secondary-action';
|
||||
import { Vec2 } from '../math/vec2';
|
||||
import { SwipeCommand } from '../commands/types/swipe';
|
||||
import { ZoomCommand } from '../commands/types/zoom';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
import { clamp01 } from '../helper/clamp';
|
||||
|
||||
export class MouseListener extends CommandGenerator {
|
||||
private previousPosition: Vec2 = null;
|
||||
private previousPosition = vec2.create();
|
||||
private isMouseDown = false;
|
||||
|
||||
constructor(private target: Element) {
|
||||
|
|
@ -27,7 +28,9 @@ export class MouseListener extends CommandGenerator {
|
|||
if (this.isMouseDown) {
|
||||
const position = this.positionFromEvent(event);
|
||||
this.sendCommand(
|
||||
new SwipeCommand(this.previousPosition.subtract(position))
|
||||
new SwipeCommand(
|
||||
vec2.subtract(vec2.create(), 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();
|
||||
|
||||
return new Vec2(
|
||||
(event.clientX - bb.x) / bb.width,
|
||||
1 - (event.clientY - bb.y) / bb.height
|
||||
).clamped_0_1;
|
||||
return vec2.fromValues(
|
||||
clamp01((event.clientX - bb.x) / bb.width),
|
||||
clamp01(1 - (event.clientY - bb.y) / bb.height)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { CommandGenerator } from '../commands/command-generator';
|
||||
import { PrimaryActionCommand } from '../commands/types/primary-action';
|
||||
import { SecondaryActionCommand } from '../commands/types/secondary-action';
|
||||
import { Vec2 } from '../math/vec2';
|
||||
import { SwipeCommand } from '../commands/types/swipe';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
import { clamp01 } from '../helper/clamp';
|
||||
|
||||
export class TouchListener extends CommandGenerator {
|
||||
private previousPosition: Vec2 = null;
|
||||
private previousPosition = vec2.create();
|
||||
|
||||
constructor(private target: HTMLElement) {
|
||||
super();
|
||||
|
|
@ -30,19 +31,21 @@ export class TouchListener extends CommandGenerator {
|
|||
const position = this.positionFromEvent(event);
|
||||
|
||||
this.sendCommand(
|
||||
new SwipeCommand(position.subtract(this.previousPosition))
|
||||
new SwipeCommand(
|
||||
vec2.subtract(vec2.create(), position, this.previousPosition)
|
||||
)
|
||||
);
|
||||
|
||||
this.previousPosition = position;
|
||||
});
|
||||
}
|
||||
|
||||
private positionFromEvent(event: TouchEvent): Vec2 {
|
||||
private positionFromEvent(event: TouchEvent): vec2 {
|
||||
const bb = this.target.getBoundingClientRect();
|
||||
|
||||
return new Vec2(
|
||||
1 - (event.touches[0].clientX - bb.x) / bb.width,
|
||||
(event.touches[0].clientY - bb.y) / bb.height
|
||||
).clamped_0_1;
|
||||
return vec2.fromValues(
|
||||
clamp01(1 - (event.touches[0].clientX - bb.x) / bb.width),
|
||||
clamp01((event.touches[0].clientY - bb.y) / bb.height)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import { Typed } from '../transport/serializable';
|
||||
import { Vec2 } from './vec2';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export class Rectangle extends Typed {
|
||||
public constructor(
|
||||
public topLeft: Vec2 = new Vec2(),
|
||||
public size: Vec2 = new Vec2()
|
||||
public topLeft: vec2 = vec2.create(),
|
||||
public size: vec2 = vec2.create()
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public isInside(position: Vec2): boolean {
|
||||
const translated = position.subtract(this.topLeft);
|
||||
public isInside(position: vec2): boolean {
|
||||
const translated = vec2.subtract(vec2.create(), position, this.topLeft);
|
||||
return (
|
||||
0 <= translated.x &&
|
||||
translated.x < this.size.x &&
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
import { Typed } from '../transport/serializable';
|
||||
import { Vec2 } from '../math/vec2';
|
||||
import { IdentityManager } from '../identity/identity-manager';
|
||||
import { Command } from '../commands/command';
|
||||
import { CommandReceiver } from '../commands/command-receiver';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export abstract class GameObject extends Typed implements CommandReceiver {
|
||||
public readonly id = IdentityManager.generateId();
|
||||
|
||||
protected _position = new Vec2();
|
||||
public get position(): Vec2 {
|
||||
protected _position = vec2.create();
|
||||
public get position(): vec2 {
|
||||
return this._position;
|
||||
}
|
||||
|
||||
protected _boundingBoxSize = new Vec2();
|
||||
public get boundingBoxSize(): Vec2 {
|
||||
protected _boundingBoxSize = vec2.create();
|
||||
public get boundingBoxSize(): vec2 {
|
||||
return this._boundingBoxSize;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ import { GameObject } from '../game-object';
|
|||
import { MoveToCommand } from '../../commands/types/move-to';
|
||||
import { ZoomCommand } from '../../commands/types/zoom';
|
||||
import { BeforeDrawCommand } from '../../commands/types/before-draw';
|
||||
import { PrimaryActionCommand } from '../../commands/types/primary-action';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export class Camera extends GameObject {
|
||||
private inViewArea = 1920 * 1080;
|
||||
private cursorPosition = vec2.create();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -12,10 +15,15 @@ export class Camera extends GameObject {
|
|||
this.addCommandExecutor(BeforeDrawCommand, this.draw.bind(this));
|
||||
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
|
||||
this.addCommandExecutor(ZoomCommand, this.zoom.bind(this));
|
||||
this.addCommandExecutor(
|
||||
PrimaryActionCommand,
|
||||
this.setCursorPosition.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
private draw(c: BeforeDrawCommand) {
|
||||
c.drawer.setCameraPosition(this.position);
|
||||
c.drawer.setCursorPosition(this.cursorPosition);
|
||||
this._boundingBoxSize = c.drawer.setInViewArea(this.inViewArea);
|
||||
}
|
||||
|
||||
|
|
@ -26,4 +34,8 @@ export class Camera extends GameObject {
|
|||
private zoom(c: ZoomCommand) {
|
||||
this.inViewArea *= c.factor;
|
||||
}
|
||||
|
||||
private setCursorPosition(c: PrimaryActionCommand) {
|
||||
this.cursorPosition = c.position;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { GameObject } from '../game-object';
|
||||
import { Vec2 } from '../../math/vec2';
|
||||
import { ObjectContainer } from '../object-container';
|
||||
import { Camera } from './camera';
|
||||
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 { KeyUpCommand } from '../../commands/types/key-up';
|
||||
import { SwipeCommand } from '../../commands/types/swipe';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export class Character extends GameObject {
|
||||
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(SwipeCommand, (c) =>
|
||||
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.camera.sendCommand(new MoveToCommand(this.position));
|
||||
}
|
||||
|
|
@ -41,11 +45,17 @@ export class Character extends GameObject {
|
|||
const left = ~~this.keysDown.has('a');
|
||||
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) {
|
||||
this.setPosition(
|
||||
this.position.add(
|
||||
movementVector.normalized.scale(Character.speed * deltaTime)
|
||||
vec2.add(
|
||||
vec2.create(),
|
||||
this.position,
|
||||
vec2.scale(
|
||||
vec2.create(),
|
||||
vec2.normalize(movementVector, movementVector),
|
||||
Character.speed * deltaTime
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { GameObject } from '../game-object';
|
||||
import { DrawCommand } from '../../commands/types/draw';
|
||||
import { Vec2 } from '../../math/vec2';
|
||||
import { last } from '../../helper/last';
|
||||
import { vec2 } from 'gl-matrix';
|
||||
|
||||
export interface Line {
|
||||
start: Vec2;
|
||||
end: Vec2;
|
||||
radius: number;
|
||||
start: vec2;
|
||||
end: vec2;
|
||||
radiusFrom: number;
|
||||
radiusTo: number;
|
||||
}
|
||||
|
||||
export class Dungeon extends GameObject {
|
||||
|
|
@ -17,33 +17,36 @@ export class Dungeon extends GameObject {
|
|||
|
||||
this.addCommandExecutor(DrawCommand, this.draw.bind(this));
|
||||
|
||||
let previousHeight = 0;
|
||||
let previousEnd = new Vec2();
|
||||
let previousRadius = 0;
|
||||
let previousEnd = vec2.create();
|
||||
|
||||
for (let i = 0; i < 5000; i += 50) {
|
||||
const height = previousHeight + (Math.random() - 0.5) * 200;
|
||||
previousHeight = height;
|
||||
const currentEnd = new Vec2(i, height);
|
||||
const height = previousEnd.y + (Math.random() - 0.5) * 200;
|
||||
const currentEnd = vec2.fromValues(i, height);
|
||||
const currentToRadius = Math.random() * 10 + 30;
|
||||
|
||||
this.lines.push({
|
||||
start: previousEnd,
|
||||
end: currentEnd,
|
||||
radius: Math.random() * 15 + 15,
|
||||
radiusFrom: previousRadius,
|
||||
radiusTo: currentToRadius,
|
||||
});
|
||||
|
||||
previousEnd = currentEnd;
|
||||
previousRadius = currentToRadius;
|
||||
}
|
||||
}
|
||||
|
||||
private draw(c: DrawCommand) {
|
||||
const lines: Array<Vec2> = [];
|
||||
const lines: Array<vec2> = [];
|
||||
const radii: Array<number> = [];
|
||||
|
||||
for (let line of this.lines) {
|
||||
if (c.drawer.isOnScreen(line.start) || c.drawer.isOnScreen(line.end)) {
|
||||
lines.push(line.start);
|
||||
lines.push(line.end);
|
||||
radii.push(line.radius);
|
||||
radii.push(line.radiusFrom);
|
||||
radii.push(line.radiusTo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,12 +8,18 @@ precision mediump float;
|
|||
|
||||
|
||||
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;
|
||||
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) {
|
||||
|
|
@ -22,8 +28,9 @@ float getDistance(in vec2 target) {
|
|||
for (int i = 0; i < LINE_COUNT; i++) {
|
||||
vec2 start = lines[2 * i];
|
||||
vec2 end = lines[2 * i + 1];
|
||||
float r = radii[i];
|
||||
minDistance = min(minDistance, lineDistance(target, start, end, r));
|
||||
float rFrom = radii[2 * i];
|
||||
float rTo = radii[2 * i + 1];
|
||||
minDistance = min(minDistance, lineDistance(target, start, end, rFrom, rTo));
|
||||
}
|
||||
|
||||
return -minDistance;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -28,5 +28,6 @@ out vec4 fragmentColor;
|
|||
void main() {
|
||||
vec2 position = (vec3(gl_FragCoord.xy, 1.0) * transformUV).xy;
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue