Add basic tiled rendering
This commit is contained in:
parent
854e5a55a1
commit
edffd22c2e
26 changed files with 353 additions and 170 deletions
8
frontend/src/scripts/commands/types/before-render.ts
Normal file
8
frontend/src/scripts/commands/types/before-render.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { IRenderer } from '../../drawing/i-renderer';
|
||||||
|
import { Command } from '../command';
|
||||||
|
|
||||||
|
export class BeforeRenderCommand extends Command {
|
||||||
|
public constructor(public readonly renderer?: IRenderer) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { IRenderer } from '../../drawing/i-renderer';
|
import { IRenderer } from '../../drawing/i-renderer';
|
||||||
import { Command } from '../command';
|
import { Command } from '../command';
|
||||||
|
|
||||||
export class DrawCommand extends Command {
|
export class RenderCommand extends Command {
|
||||||
public constructor(public readonly drawer?: IRenderer) {
|
public constructor(public readonly renderer?: IRenderer) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { IProgram } from '../program/i-program';
|
|
||||||
import { FrameBuffer } from './frame-buffer';
|
import { FrameBuffer } from './frame-buffer';
|
||||||
|
|
||||||
export class DefaultFrameBuffer extends FrameBuffer {
|
export class DefaultFrameBuffer extends FrameBuffer {
|
||||||
constructor(gl: WebGL2RenderingContext, programs: Array<IProgram>) {
|
constructor(gl: WebGL2RenderingContext) {
|
||||||
super(gl, programs);
|
super(gl);
|
||||||
this.frameBuffer = null;
|
this.frameBuffer = null;
|
||||||
|
|
||||||
this.setSize();
|
this.setSize();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { IProgram } from '../program/i-program';
|
|
||||||
|
|
||||||
export abstract class FrameBuffer {
|
export abstract class FrameBuffer {
|
||||||
public renderScale = 1;
|
public renderScale = 1;
|
||||||
|
|
@ -8,12 +7,9 @@ export abstract class FrameBuffer {
|
||||||
protected size: vec2;
|
protected size: vec2;
|
||||||
protected frameBuffer: WebGLFramebuffer;
|
protected frameBuffer: WebGLFramebuffer;
|
||||||
|
|
||||||
constructor(
|
constructor(protected gl: WebGL2RenderingContext) {}
|
||||||
protected gl: WebGL2RenderingContext,
|
|
||||||
protected programs: Array<IProgram>
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public render(uniforms: any, colorInput?: WebGLTexture) {
|
public bindAndClear(colorInput?: WebGLTexture) {
|
||||||
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer);
|
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.frameBuffer);
|
||||||
|
|
||||||
if (colorInput !== null) {
|
if (colorInput !== null) {
|
||||||
|
|
@ -23,11 +19,6 @@ export abstract class FrameBuffer {
|
||||||
this.gl.viewport(0, 0, this.size.x, this.size.y);
|
this.gl.viewport(0, 0, this.size.x, this.size.y);
|
||||||
this.gl.clearColor(0, 0, 0, 0);
|
this.gl.clearColor(0, 0, 0, 0);
|
||||||
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
this.programs.forEach((p) => {
|
|
||||||
p.bindAndSetUniforms(uniforms);
|
|
||||||
p.draw();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSize() {
|
public setSize() {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import { IProgram } from '../program/i-program';
|
|
||||||
import { FrameBuffer } from './frame-buffer';
|
import { FrameBuffer } from './frame-buffer';
|
||||||
|
|
||||||
export class IntermediateFrameBuffer extends FrameBuffer {
|
export class IntermediateFrameBuffer extends FrameBuffer {
|
||||||
private frameTexture: WebGLTexture;
|
private frameTexture: WebGLTexture;
|
||||||
|
|
||||||
constructor(gl: WebGL2RenderingContext, programs: Array<IProgram>) {
|
constructor(gl: WebGL2RenderingContext) {
|
||||||
super(gl, programs);
|
super(gl);
|
||||||
|
|
||||||
this.frameTexture = this.gl.createTexture();
|
this.frameTexture = this.gl.createTexture();
|
||||||
this.configureTexture();
|
this.configureTexture();
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export class FragmentShaderOnlyProgram extends Program {
|
||||||
substitutions: { [name: string]: string }
|
substitutions: { [name: string]: string }
|
||||||
) {
|
) {
|
||||||
super(gl, vertexShaderSource, fragmentShaderSource, substitutions);
|
super(gl, vertexShaderSource, fragmentShaderSource, substitutions);
|
||||||
this.prepareScreenQuad('a_position');
|
this.prepareScreenQuad('vertexPosition');
|
||||||
}
|
}
|
||||||
|
|
||||||
public bind() {
|
public bind() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
export interface IProgram {
|
export interface IProgram {
|
||||||
bindAndSetUniforms(values: { [name: string]: any }): void;
|
bindAndSetUniforms(values: { [name: string]: any }): void;
|
||||||
|
setDrawingRectangle(topLeft: vec2, size: vec2): void;
|
||||||
draw(): void;
|
draw(): void;
|
||||||
delete(): void;
|
delete(): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { mat2d, vec2 } from 'gl-matrix';
|
||||||
import { createShader } from '../helper/create-shader';
|
import { createShader } from '../helper/create-shader';
|
||||||
import { loadUniform } from '../helper/load-uniform';
|
import { loadUniform } from '../helper/load-uniform';
|
||||||
import { IProgram } from './i-program';
|
import { IProgram } from './i-program';
|
||||||
|
|
@ -5,6 +6,8 @@ import { IProgram } from './i-program';
|
||||||
export abstract class Program implements IProgram {
|
export abstract class Program implements IProgram {
|
||||||
protected program: WebGLProgram;
|
protected program: WebGLProgram;
|
||||||
private shaders: Array<WebGLShader> = [];
|
private shaders: Array<WebGLShader> = [];
|
||||||
|
private modelTransform = mat2d.identity(mat2d.create());
|
||||||
|
private readonly ndcToUv: mat2d;
|
||||||
|
|
||||||
private uniforms: Array<{
|
private uniforms: Array<{
|
||||||
name: Array<string>;
|
name: Array<string>;
|
||||||
|
|
@ -20,15 +23,22 @@ export abstract class Program implements IProgram {
|
||||||
) {
|
) {
|
||||||
this.createProgram(vertexShaderSource, fragmentShaderSource, substitutions);
|
this.createProgram(vertexShaderSource, fragmentShaderSource, substitutions);
|
||||||
this.queryUniforms();
|
this.queryUniforms();
|
||||||
|
|
||||||
|
this.ndcToUv = mat2d.fromScaling(mat2d.create(), vec2.fromValues(0.5, 0.5));
|
||||||
|
mat2d.translate(this.ndcToUv, this.ndcToUv, vec2.fromValues(1, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bindAndSetUniforms(values: { [name: string]: any }) {
|
public bindAndSetUniforms(values: { [name: string]: any }) {
|
||||||
this.bind();
|
this.bind();
|
||||||
this.setUniforms(values);
|
this.setUniforms({ modelTransform: this.modelTransform, ...values });
|
||||||
}
|
}
|
||||||
|
|
||||||
public bind() {
|
public setDrawingRectangle(topLeft: vec2, size: vec2) {
|
||||||
this.gl.useProgram(this.program);
|
mat2d.invert(this.modelTransform, this.ndcToUv);
|
||||||
|
|
||||||
|
mat2d.translate(this.modelTransform, this.modelTransform, topLeft);
|
||||||
|
mat2d.scale(this.modelTransform, this.modelTransform, size);
|
||||||
|
mat2d.multiply(this.modelTransform, this.modelTransform, this.ndcToUv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setUniforms(values: { [name: string]: any }) {
|
public setUniforms(values: { [name: string]: any }) {
|
||||||
|
|
@ -51,6 +61,10 @@ export abstract class Program implements IProgram {
|
||||||
|
|
||||||
public abstract draw(): void;
|
public abstract draw(): void;
|
||||||
|
|
||||||
|
protected bind() {
|
||||||
|
this.gl.useProgram(this.program);
|
||||||
|
}
|
||||||
|
|
||||||
private queryUniforms() {
|
private queryUniforms() {
|
||||||
const uniformCount = this.gl.getProgramParameter(
|
const uniformCount = this.gl.getProgramParameter(
|
||||||
this.program,
|
this.program,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { InfoText } from '../../../objects/types/info-text';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { FragmentShaderOnlyProgram } from './fragment-shader-only-program';
|
import { FragmentShaderOnlyProgram } from './fragment-shader-only-program';
|
||||||
import { IProgram } from './i-program';
|
import { IProgram } from './i-program';
|
||||||
|
|
||||||
|
|
@ -9,6 +9,9 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
}> = [];
|
}> = [];
|
||||||
private current: FragmentShaderOnlyProgram;
|
private current: FragmentShaderOnlyProgram;
|
||||||
|
|
||||||
|
private drawingRectangleTopLeft = vec2.fromValues(0, 0);
|
||||||
|
private drawingRectangleSize = vec2.fromValues(1, 1);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private gl: WebGL2RenderingContext,
|
private gl: WebGL2RenderingContext,
|
||||||
private vertexShaderSource: string,
|
private vertexShaderSource: string,
|
||||||
|
|
@ -17,6 +20,7 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
private options: {
|
private options: {
|
||||||
getValueFromUniforms: (values: { [name: string]: any }) => number;
|
getValueFromUniforms: (values: { [name: string]: any }) => number;
|
||||||
uniformArraySizeName: string;
|
uniformArraySizeName: string;
|
||||||
|
enablingMacro: string;
|
||||||
startingValue: number;
|
startingValue: number;
|
||||||
steps: number;
|
steps: number;
|
||||||
maximumValue: number;
|
maximumValue: number;
|
||||||
|
|
@ -31,12 +35,10 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bindAndSetUniforms(values: { [name: string]: any }): void {
|
public bindAndSetUniforms(values: { [name: string]: any }): void {
|
||||||
let value = this.options.getValueFromUniforms(values);
|
let value = this.options.getValueFromUniforms(values);
|
||||||
value = Math.min(this.options.maximumValue, value);
|
value = Math.min(this.options.maximumValue, value);
|
||||||
|
|
||||||
InfoText.modifyRecord(this.options.uniformArraySizeName, value.toString());
|
|
||||||
|
|
||||||
const closest = this.programs.find(
|
const closest = this.programs.find(
|
||||||
(p) => value < p.value && value + this.options.steps >= p.value
|
(p) => value < p.value && value + this.options.steps >= p.value
|
||||||
);
|
);
|
||||||
|
|
@ -46,14 +48,23 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
this.current = this.createProgram(value + this.options.steps);
|
this.current = this.createProgram(value + this.options.steps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.current.setDrawingRectangle(
|
||||||
|
this.drawingRectangleTopLeft,
|
||||||
|
this.drawingRectangleSize
|
||||||
|
);
|
||||||
this.current.bindAndSetUniforms(values);
|
this.current.bindAndSetUniforms(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(): void {
|
public setDrawingRectangle(topLeft: vec2, size: vec2) {
|
||||||
|
this.drawingRectangleTopLeft = topLeft;
|
||||||
|
this.drawingRectangleSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public draw(): void {
|
||||||
this.current.draw();
|
this.current.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(): void {
|
public delete(): void {
|
||||||
this.programs.forEach((p) => p.program.delete());
|
this.programs.forEach((p) => p.program.delete());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,6 +75,7 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
this.fragmentShaderSource,
|
this.fragmentShaderSource,
|
||||||
{
|
{
|
||||||
[this.options.uniformArraySizeName]: Math.floor(arraySize).toString(),
|
[this.options.uniformArraySizeName]: Math.floor(arraySize).toString(),
|
||||||
|
[this.options.enablingMacro]: arraySize > 0 ? '1' : '0',
|
||||||
...this.substitutions,
|
...this.substitutions,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { Circle } from '../math/circle';
|
import { Circle } from './primitives/circle';
|
||||||
|
import { IPrimitive } from './primitives/i-primitive';
|
||||||
|
|
||||||
export interface IRenderer {
|
export interface IRenderer {
|
||||||
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
||||||
finishFrame(): void;
|
finishFrame(): void;
|
||||||
giveUniforms(uniforms: any): void;
|
|
||||||
appendToUniformList(listName: string, ...values: Array<any>): void;
|
drawPrimitive(primitive: IPrimitive): void;
|
||||||
|
|
||||||
setCameraPosition(position: vec2): void;
|
setCameraPosition(position: vec2): void;
|
||||||
setCursorPosition(position: vec2): void;
|
setCursorPosition(position: vec2): void;
|
||||||
setInViewArea(size: number): vec2;
|
setInViewArea(size: number): vec2;
|
||||||
|
|
||||||
|
giveUniforms(uniforms: any): void;
|
||||||
|
appendToUniformList(listName: string, ...values: Array<any>): void;
|
||||||
|
|
||||||
screenUvToWorldCoordinate(mousePosition: vec2): vec2;
|
screenUvToWorldCoordinate(mousePosition: vec2): vec2;
|
||||||
drawInfoText(text: string): void;
|
drawInfoText(text: string): void;
|
||||||
isOnScreen(boundingCircle: Circle): boolean;
|
isOnScreen(boundingCircle: Circle): boolean;
|
||||||
|
|
|
||||||
23
frontend/src/scripts/drawing/primitives/circle.ts
Normal file
23
frontend/src/scripts/drawing/primitives/circle.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { IPrimitive } from './i-primitive';
|
||||||
|
|
||||||
|
export class Circle implements IPrimitive {
|
||||||
|
public constructor(public center: vec2, public radius: number) {}
|
||||||
|
|
||||||
|
public distance(target: vec2): number {
|
||||||
|
return vec2.distance(this.center, target) - this.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public minimumDistance(target: vec2): number {
|
||||||
|
return vec2.distance(this.center, target) - this.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isInside(target: vec2): boolean {
|
||||||
|
return this.distance(target) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public areIntersecting(other: Circle): boolean {
|
||||||
|
const distance = vec2.distance(this.center, other.center);
|
||||||
|
return distance < this.radius + other.radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
frontend/src/scripts/drawing/primitives/i-primitive.ts
Normal file
6
frontend/src/scripts/drawing/primitives/i-primitive.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
|
export interface IPrimitive {
|
||||||
|
distance(target: vec2): number;
|
||||||
|
minimumDistance(target: vec2): number;
|
||||||
|
}
|
||||||
18
frontend/src/scripts/drawing/primitives/rectangle.ts
Normal file
18
frontend/src/scripts/drawing/primitives/rectangle.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
|
export class Rectangle {
|
||||||
|
public constructor(
|
||||||
|
public topLeft: vec2 = vec2.create(),
|
||||||
|
public size: vec2 = vec2.create()
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public isInside(position: vec2): boolean {
|
||||||
|
const translated = vec2.subtract(vec2.create(), position, this.topLeft);
|
||||||
|
return (
|
||||||
|
0 <= translated.x &&
|
||||||
|
translated.x < this.size.x &&
|
||||||
|
0 <= translated.y &&
|
||||||
|
translated.y < this.size.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
frontend/src/scripts/drawing/primitives/tunnel-shape.ts
Normal file
46
frontend/src/scripts/drawing/primitives/tunnel-shape.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { clamp01 } from '../../helper/clamp';
|
||||||
|
import { mix } from '../../helper/mix';
|
||||||
|
import { Circle } from './circle';
|
||||||
|
import { IPrimitive } from './i-primitive';
|
||||||
|
|
||||||
|
export class TunnelShape implements IPrimitive {
|
||||||
|
public readonly toFromDelta: vec2;
|
||||||
|
private toFromDeltaLength: number;
|
||||||
|
|
||||||
|
private boundingCircle: Circle;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly from: vec2,
|
||||||
|
public readonly to: vec2,
|
||||||
|
public readonly fromRadius: number,
|
||||||
|
public readonly toRadius: number
|
||||||
|
) {
|
||||||
|
this.toFromDelta = vec2.subtract(vec2.create(), to, from);
|
||||||
|
this.toFromDeltaLength = vec2.length(this.toFromDelta);
|
||||||
|
|
||||||
|
this.boundingCircle = new Circle(
|
||||||
|
vec2.fromValues(from.x / 2 + to.x / 2, from.y / 2 + to.y / 2),
|
||||||
|
Math.max(fromRadius, toRadius) + vec2.distance(from, to)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public distance(target: vec2): number {
|
||||||
|
const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
|
||||||
|
const h = clamp01(
|
||||||
|
vec2.dot(targetFromDelta, this.toFromDelta) /
|
||||||
|
this.toFromDeltaLength /
|
||||||
|
this.toFromDeltaLength
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
vec2.distance(
|
||||||
|
targetFromDelta,
|
||||||
|
vec2.scale(vec2.create(), this.toFromDelta, h)
|
||||||
|
) - mix(this.fromRadius, this.toRadius, h)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public minimumDistance(target: vec2): number {
|
||||||
|
return this.boundingCircle.distance(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
import { mat2d, vec2 } from 'gl-matrix';
|
import { mat2d, vec2 } from 'gl-matrix';
|
||||||
import { clamp } from '../helper/clamp';
|
import { clamp } from '../helper/clamp';
|
||||||
import { Circle } from '../math/circle';
|
|
||||||
import { Rectangle } from '../math/rectangle';
|
|
||||||
import { InfoText } from '../objects/types/info-text';
|
import { InfoText } from '../objects/types/info-text';
|
||||||
import { DefaultFrameBuffer } from './graphics-library/frame-buffer/default-frame-buffer';
|
import { DefaultFrameBuffer } from './graphics-library/frame-buffer/default-frame-buffer';
|
||||||
import { IntermediateFrameBuffer } from './graphics-library/frame-buffer/intermediate-frame-buffer';
|
import { IntermediateFrameBuffer } from './graphics-library/frame-buffer/intermediate-frame-buffer';
|
||||||
import { WebGlStopwatch } from './graphics-library/helper/stopwatch';
|
import { WebGlStopwatch } from './graphics-library/helper/stopwatch';
|
||||||
|
import { IProgram } from './graphics-library/program/i-program';
|
||||||
import { UniformArrayAutoScalingProgram } from './graphics-library/program/uniform-array-autoscaling-program';
|
import { UniformArrayAutoScalingProgram } from './graphics-library/program/uniform-array-autoscaling-program';
|
||||||
import { IRenderer } from './i-renderer';
|
import { IRenderer } from './i-renderer';
|
||||||
|
import { Circle } from './primitives/circle';
|
||||||
|
import { IPrimitive } from './primitives/i-primitive';
|
||||||
|
import { Rectangle } from './primitives/rectangle';
|
||||||
|
import { TunnelShape } from './primitives/tunnel-shape';
|
||||||
|
|
||||||
export class WebGl2Renderer implements IRenderer {
|
export class WebGl2Renderer implements IRenderer {
|
||||||
private gl: WebGL2RenderingContext;
|
private gl: WebGL2RenderingContext;
|
||||||
|
|
@ -19,6 +22,12 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
private cursorPosition = vec2.create();
|
private cursorPosition = vec2.create();
|
||||||
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
||||||
private lightingFrameBuffer: DefaultFrameBuffer;
|
private lightingFrameBuffer: DefaultFrameBuffer;
|
||||||
|
private distanceProgram: IProgram;
|
||||||
|
private lightingProgram: IProgram;
|
||||||
|
|
||||||
|
private primitives: Array<IPrimitive>;
|
||||||
|
|
||||||
|
private tileMultiplier = 5;
|
||||||
|
|
||||||
private targetDeltaTime = (1 / 30) * 1000;
|
private targetDeltaTime = (1 / 30) * 1000;
|
||||||
private deltaTimeError = (1 / 1000) * 1000;
|
private deltaTimeError = (1 / 1000) * 1000;
|
||||||
|
|
@ -46,7 +55,7 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
this.targetDeltaTime - this.deltaTimeError
|
this.targetDeltaTime - this.deltaTimeError
|
||||||
) {
|
) {
|
||||||
this.distanceFieldFrameBuffer.renderScale +=
|
this.distanceFieldFrameBuffer.renderScale +=
|
||||||
this.additiveQualityIncrease / 4;
|
this.additiveQualityIncrease / 3;
|
||||||
this.lightingFrameBuffer.renderScale += this.additiveQualityIncrease;
|
this.lightingFrameBuffer.renderScale += this.additiveQualityIncrease;
|
||||||
} else if (
|
} else if (
|
||||||
this.exponentialDecayedDeltaTime >
|
this.exponentialDecayedDeltaTime >
|
||||||
|
|
@ -91,39 +100,7 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
throw new Error('WebGl2 is not supported');
|
throw new Error('WebGl2 is not supported');
|
||||||
}
|
}
|
||||||
|
|
||||||
const distanceScale = 64;
|
this.createPipeline(shaderSources);
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl, [
|
|
||||||
new UniformArrayAutoScalingProgram(
|
|
||||||
this.gl,
|
|
||||||
shaderSources[0][0],
|
|
||||||
shaderSources[0][1],
|
|
||||||
{ distanceScale },
|
|
||||||
{
|
|
||||||
getValueFromUniforms: (v) => (v.lines ? v.lines.length / 2 : 0),
|
|
||||||
uniformArraySizeName: 'lineCount',
|
|
||||||
startingValue: 5,
|
|
||||||
steps: 5,
|
|
||||||
maximumValue: 100,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl, [
|
|
||||||
new UniformArrayAutoScalingProgram(
|
|
||||||
this.gl,
|
|
||||||
shaderSources[1][0],
|
|
||||||
shaderSources[1][1],
|
|
||||||
{ distanceScale },
|
|
||||||
{
|
|
||||||
getValueFromUniforms: (v) => (v.lights ? v.lights.length : 0),
|
|
||||||
uniformArraySizeName: 'lightCount',
|
|
||||||
startingValue: 1,
|
|
||||||
steps: 1,
|
|
||||||
maximumValue: 8,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer.renderScale = 0.5;
|
this.distanceFieldFrameBuffer.renderScale = 0.5;
|
||||||
this.lightingFrameBuffer.renderScale = 1;
|
this.lightingFrameBuffer.renderScale = 1;
|
||||||
|
|
@ -133,8 +110,49 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createPipeline(shaderSources: Array<[string, string]>) {
|
||||||
|
const distanceScale = 64;
|
||||||
|
|
||||||
|
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl);
|
||||||
|
this.distanceProgram = new UniformArrayAutoScalingProgram(
|
||||||
|
this.gl,
|
||||||
|
shaderSources[0][0],
|
||||||
|
shaderSources[0][1],
|
||||||
|
{ distanceScale },
|
||||||
|
{
|
||||||
|
getValueFromUniforms: (v) => (v.lines ? v.lines.length / 2 : 0),
|
||||||
|
uniformArraySizeName: 'lineCount',
|
||||||
|
startingValue: 0,
|
||||||
|
enablingMacro: 'linesEnabled',
|
||||||
|
steps: 1,
|
||||||
|
maximumValue: 15,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl);
|
||||||
|
this.lightingProgram = new UniformArrayAutoScalingProgram(
|
||||||
|
this.gl,
|
||||||
|
shaderSources[1][0],
|
||||||
|
shaderSources[1][1],
|
||||||
|
{ distanceScale },
|
||||||
|
{
|
||||||
|
getValueFromUniforms: (v) => (v.lights ? v.lights.length : 0),
|
||||||
|
uniformArraySizeName: 'lightCount',
|
||||||
|
startingValue: 1,
|
||||||
|
enablingMacro: null,
|
||||||
|
steps: 1,
|
||||||
|
maximumValue: 8,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public drawPrimitive(primitive: IPrimitive) {
|
||||||
|
this.primitives.push(primitive);
|
||||||
|
}
|
||||||
|
|
||||||
public startFrame(deltaTime: DOMHighResTimeStamp): void {
|
public startFrame(deltaTime: DOMHighResTimeStamp): void {
|
||||||
this.configureRenderScale(deltaTime);
|
this.configureRenderScale(deltaTime);
|
||||||
|
this.primitives = [];
|
||||||
|
|
||||||
this.stopwatch?.start();
|
this.stopwatch?.start();
|
||||||
this.uniforms = {};
|
this.uniforms = {};
|
||||||
|
|
@ -144,11 +162,81 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
|
|
||||||
public finishFrame() {
|
public finishFrame() {
|
||||||
this.calculateOwnUniforms();
|
this.calculateOwnUniforms();
|
||||||
this.distanceFieldFrameBuffer.render(this.uniforms);
|
|
||||||
this.lightingFrameBuffer.render(
|
this.distanceFieldFrameBuffer.bindAndClear();
|
||||||
this.uniforms,
|
const q = 1 / this.tileMultiplier;
|
||||||
|
const uvSize = vec2.fromValues(q, q);
|
||||||
|
|
||||||
|
const possiblyOnScreenPrimitives = this.primitives.filter(
|
||||||
|
(p) => p.minimumDistance(this.viewCircle.center) < this.viewCircle.radius
|
||||||
|
) as Array<TunnelShape>;
|
||||||
|
|
||||||
|
InfoText.modifyRecord(
|
||||||
|
'nearby lines',
|
||||||
|
possiblyOnScreenPrimitives.length.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
const origin = vec2.transformMat2d(
|
||||||
|
vec2.create(),
|
||||||
|
vec2.fromValues(0, 0),
|
||||||
|
this.uniforms.uvToWorld
|
||||||
|
);
|
||||||
|
|
||||||
|
const firstCenter = vec2.transformMat2d(
|
||||||
|
vec2.create(),
|
||||||
|
vec2.fromValues(q, q),
|
||||||
|
this.uniforms.uvToWorld
|
||||||
|
);
|
||||||
|
|
||||||
|
vec2.subtract(firstCenter, firstCenter, origin);
|
||||||
|
|
||||||
|
const worldR = vec2.length(firstCenter);
|
||||||
|
this.uniforms.maxMinDistance = 2 * worldR;
|
||||||
|
|
||||||
|
let sumLineCount = 0;
|
||||||
|
|
||||||
|
for (let x = 0; x < 1; x += q) {
|
||||||
|
for (let y = 0; y < 1; y += q) {
|
||||||
|
const uvBottomLeft = vec2.fromValues(x, y);
|
||||||
|
this.distanceProgram.setDrawingRectangle(uvBottomLeft, uvSize);
|
||||||
|
|
||||||
|
const tileCenterWorldCoordinates = vec2.transformMat2d(
|
||||||
|
vec2.create(),
|
||||||
|
vec2.add(vec2.create(), uvBottomLeft, vec2.fromValues(q, q)),
|
||||||
|
this.uniforms.uvToWorld
|
||||||
|
);
|
||||||
|
|
||||||
|
const primitivesNearTile = possiblyOnScreenPrimitives.filter(
|
||||||
|
(p) => p.distance(tileCenterWorldCoordinates) < 2 * worldR
|
||||||
|
);
|
||||||
|
|
||||||
|
sumLineCount += primitivesNearTile.length;
|
||||||
|
|
||||||
|
this.uniforms.lines = [];
|
||||||
|
this.uniforms.radii = [];
|
||||||
|
|
||||||
|
for (let tunnel of primitivesNearTile) {
|
||||||
|
this.uniforms.lines.push(tunnel.from);
|
||||||
|
this.uniforms.lines.push(tunnel.toFromDelta);
|
||||||
|
this.uniforms.radii.push(tunnel.fromRadius);
|
||||||
|
this.uniforms.radii.push(tunnel.toRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.distanceProgram.bindAndSetUniforms(this.uniforms);
|
||||||
|
this.distanceProgram.draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoText.modifyRecord(
|
||||||
|
'lines',
|
||||||
|
(sumLineCount / this.tileMultiplier / this.tileMultiplier).toFixed(2)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.lightingFrameBuffer.bindAndClear(
|
||||||
this.distanceFieldFrameBuffer.colorTexture
|
this.distanceFieldFrameBuffer.colorTexture
|
||||||
);
|
);
|
||||||
|
this.lightingProgram.bindAndSetUniforms(this.uniforms);
|
||||||
|
this.lightingProgram.draw();
|
||||||
|
|
||||||
this.stopwatch?.stop();
|
this.stopwatch?.stop();
|
||||||
}
|
}
|
||||||
|
|
@ -158,13 +246,11 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
this.distanceFieldFrameBuffer.getSize()
|
this.distanceFieldFrameBuffer.getSize()
|
||||||
);
|
);
|
||||||
|
|
||||||
const ndcToWorld = mat2d.fromTranslation(
|
const uvToWorld = mat2d.fromTranslation(
|
||||||
mat2d.create(),
|
mat2d.create(),
|
||||||
this.viewBox.topLeft
|
this.viewBox.topLeft
|
||||||
);
|
);
|
||||||
mat2d.scale(ndcToWorld, ndcToWorld, this.viewBox.size);
|
mat2d.scale(uvToWorld, uvToWorld, this.viewBox.size);
|
||||||
mat2d.scale(ndcToWorld, ndcToWorld, vec2.fromValues(0.5, 0.5));
|
|
||||||
mat2d.translate(ndcToWorld, ndcToWorld, vec2.fromValues(1, 1));
|
|
||||||
|
|
||||||
const worldToDistanceUV = mat2d.scale(
|
const worldToDistanceUV = mat2d.scale(
|
||||||
mat2d.create(),
|
mat2d.create(),
|
||||||
|
|
@ -173,13 +259,20 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
);
|
);
|
||||||
mat2d.invert(worldToDistanceUV, worldToDistanceUV);
|
mat2d.invert(worldToDistanceUV, worldToDistanceUV);
|
||||||
|
|
||||||
|
const ndcToUv = mat2d.fromScaling(
|
||||||
|
mat2d.create(),
|
||||||
|
vec2.fromValues(0.5, 0.5)
|
||||||
|
);
|
||||||
|
mat2d.translate(ndcToUv, ndcToUv, vec2.fromValues(1, 1));
|
||||||
|
|
||||||
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
|
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
|
||||||
|
|
||||||
this.giveUniforms({
|
this.giveUniforms({
|
||||||
distanceScreenToWorld,
|
distanceScreenToWorld,
|
||||||
worldToDistanceUV,
|
worldToDistanceUV,
|
||||||
cursorPosition,
|
cursorPosition,
|
||||||
ndcToWorld,
|
ndcToUv,
|
||||||
|
uvToWorld,
|
||||||
viewBoxSize: this.viewBox.size,
|
viewBoxSize: this.viewBox.size,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -241,6 +334,7 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
);
|
);
|
||||||
|
|
||||||
const halfDiagonal = vec2.scale(vec2.create(), this.viewBox.size, 0.5);
|
const halfDiagonal = vec2.scale(vec2.create(), this.viewBox.size, 0.5);
|
||||||
|
|
||||||
this.viewCircle.center = vec2.add(
|
this.viewCircle.center = vec2.add(
|
||||||
vec2.create(),
|
vec2.create(),
|
||||||
this.viewBox.topLeft,
|
this.viewBox.topLeft,
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import caveVertexShader from '../shaders/passthrough-distance-vs.glsl';
|
||||||
import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl';
|
import lightsVertexShader from '../shaders/passthrough-shading-vs.glsl';
|
||||||
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
|
// import lightsShader from '../shaders/rainbow-shading-fs.glsl';
|
||||||
import { CommandBroadcaster } from './commands/command-broadcaster';
|
import { CommandBroadcaster } from './commands/command-broadcaster';
|
||||||
import { BeforeDrawCommand } from './commands/types/before-draw';
|
import { BeforeRenderCommand } from './commands/types/before-render';
|
||||||
import { DrawCommand } from './commands/types/draw';
|
import { RenderCommand } from './commands/types/draw';
|
||||||
import { StepCommand } from './commands/types/step';
|
import { StepCommand } from './commands/types/step';
|
||||||
import { WebGl2Renderer } from './drawing/webgl2-renderer';
|
import { WebGl2Renderer } from './drawing/webgl2-renderer';
|
||||||
import { timeIt } from './helper/timing';
|
import { timeIt } from './helper/timing';
|
||||||
|
|
@ -76,8 +76,8 @@ export class Game {
|
||||||
this.objects.sendCommand(new StepCommand(deltaTime));
|
this.objects.sendCommand(new StepCommand(deltaTime));
|
||||||
|
|
||||||
this.renderer.startFrame(deltaTime);
|
this.renderer.startFrame(deltaTime);
|
||||||
this.objects.sendCommand(new BeforeDrawCommand(this.renderer));
|
this.objects.sendCommand(new BeforeRenderCommand(this.renderer));
|
||||||
this.objects.sendCommand(new DrawCommand(this.renderer));
|
this.objects.sendCommand(new RenderCommand(this.renderer));
|
||||||
this.renderer.finishFrame();
|
this.renderer.finishFrame();
|
||||||
|
|
||||||
window.requestAnimationFrame(this.gameLoop.bind(this));
|
window.requestAnimationFrame(this.gameLoop.bind(this));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export const clamp = (value: number, min: number, max: number): number =>
|
export const clamp = (value: number, min: number, max: number): number =>
|
||||||
Math.min(max, Math.max(min, value));
|
Math.min(max, Math.max(min, value));
|
||||||
|
|
||||||
export const clamp01 = (value: number): number => clamp(value, 0, 1);
|
export const clamp01 = (value: number): number =>
|
||||||
|
Math.min(1, Math.max(0, value));
|
||||||
|
|
|
||||||
2
frontend/src/scripts/helper/mix.ts
Normal file
2
frontend/src/scripts/helper/mix.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const mix = (from: number, to: number, q: number) =>
|
||||||
|
from + (to - from) * q;
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
|
|
||||||
export class Circle {
|
|
||||||
public constructor(public center: vec2, public radius: number) {}
|
|
||||||
|
|
||||||
public isInside(position: vec2): boolean {
|
|
||||||
const distance = vec2.distance(this.center, position);
|
|
||||||
return distance < this.radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public areIntersecting(other: Circle): boolean {
|
|
||||||
const distance = vec2.distance(this.center, other.center);
|
|
||||||
return distance < this.radius + other.radius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
import { BeforeDrawCommand } from '../../commands/types/before-draw';
|
import { BeforeRenderCommand } from '../../commands/types/before-render';
|
||||||
import { CursorMoveCommand } from '../../commands/types/cursor-move-command';
|
import { CursorMoveCommand } from '../../commands/types/cursor-move-command';
|
||||||
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';
|
||||||
|
|
@ -13,7 +13,7 @@ export class Camera extends GameObject {
|
||||||
constructor(private light: CircleLight) {
|
constructor(private light: CircleLight) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.addCommandExecutor(BeforeDrawCommand, this.draw.bind(this));
|
this.addCommandExecutor(BeforeRenderCommand, this.draw.bind(this));
|
||||||
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
|
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
|
||||||
this.addCommandExecutor(
|
this.addCommandExecutor(
|
||||||
CursorMoveCommand,
|
CursorMoveCommand,
|
||||||
|
|
@ -22,10 +22,10 @@ export class Camera extends GameObject {
|
||||||
this.addCommandExecutor(ZoomCommand, this.zoom.bind(this));
|
this.addCommandExecutor(ZoomCommand, this.zoom.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private draw(c: BeforeDrawCommand) {
|
private draw(c: BeforeRenderCommand) {
|
||||||
c.drawer.setCameraPosition(this.position);
|
c.renderer.setCameraPosition(this.position);
|
||||||
c.drawer.setCursorPosition(this.cursorPosition);
|
c.renderer.setCursorPosition(this.cursorPosition);
|
||||||
this._boundingBoxSize = c.drawer.setInViewArea(this.inViewArea);
|
this._boundingBoxSize = c.renderer.setInViewArea(this.inViewArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
private moveTo(c: MoveToCommand) {
|
private moveTo(c: MoveToCommand) {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue