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
- traefik
- digital ocean docker contianer registry
- dynamic shader generation

View file

@ -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",

View file

@ -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();

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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);
}
}

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 { 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)
);
}
}

View file

@ -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)
);
}
}

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 { 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 &&

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 { 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;
}

View file

@ -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;
}
}

View file

@ -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
)
)
);
}

View file

@ -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);
}
}

View file

@ -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;

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() {
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);
}