Start refactoring
This commit is contained in:
parent
6fc53ee51e
commit
9b47d56d8f
39 changed files with 423 additions and 234 deletions
|
|
@ -18,11 +18,8 @@ A good-looking 2D adventure.
|
||||||
- rancher os
|
- rancher os
|
||||||
- Frontend nginx disable logging
|
- Frontend nginx disable logging
|
||||||
- procedural piano
|
- procedural piano
|
||||||
- obfuscate function names
|
|
||||||
- lightweight object storage
|
- lightweight object storage
|
||||||
- dockerswarm.rocks
|
|
||||||
- local dev env
|
- local dev env
|
||||||
- CI pipeline:
|
- CI pipeline:
|
||||||
- prettier script
|
- prettier script
|
||||||
- tests?
|
- tests?
|
||||||
- monitoring: https://dockerswarm.rocks/swarmpit/
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<meta name="theme-color" content="#b7455e" />
|
<meta name="theme-color" content="#b7455e" />
|
||||||
<meta name="viewport" content="initial-scale=1.0" />
|
<meta name="viewport" content="initial-scale=1.0" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
|
|
@ -21,7 +20,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-family: 'Roboto', 'Helvetica Neue', sans-serif;
|
font-family: "Roboto", "Helvetica Neue", sans-serif;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
import { IRenderer } from '../../drawing/i-renderer';
|
|
||||||
import { Command } from '../command';
|
import { Command } from '../command';
|
||||||
|
import { IRenderer } from '../../drawing/rendering/i-renderer';
|
||||||
|
|
||||||
export class BeforeRenderCommand extends Command {
|
export class BeforeRenderCommand extends Command {
|
||||||
public constructor(public readonly renderer?: IRenderer) {
|
public constructor(public readonly renderer?: IRenderer) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'BeforeRenderCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,8 @@ export class CursorMoveCommand extends Command {
|
||||||
public constructor(public readonly position?: vec2) {
|
public constructor(public readonly position?: vec2) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'CursorMoveCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
import { IRenderer } from '../../drawing/i-renderer';
|
|
||||||
import { Command } from '../command';
|
import { Command } from '../command';
|
||||||
|
import { IRenderer } from '../../drawing/rendering/i-renderer';
|
||||||
|
|
||||||
export class RenderCommand extends Command {
|
export class RenderCommand extends Command {
|
||||||
public constructor(public readonly renderer?: IRenderer) {
|
public constructor(public readonly renderer?: IRenderer) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'RenderCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,8 @@ export class KeyDownCommand extends Command {
|
||||||
public constructor(public readonly key?: string) {
|
public constructor(public readonly key?: string) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'KeyDownCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,8 @@ export class KeyUpCommand extends Command {
|
||||||
public constructor(public readonly key?: string) {
|
public constructor(public readonly key?: string) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'KeyUpCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,8 @@ export class MoveToCommand extends Command {
|
||||||
public constructor(public readonly position?: vec2) {
|
public constructor(public readonly position?: vec2) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'MoveToCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,8 @@ export class PrimaryActionCommand extends Command {
|
||||||
public constructor(public readonly position?: vec2) {
|
public constructor(public readonly position?: vec2) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'PrimaryActionCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,8 @@ export class SecondaryActionCommand extends Command {
|
||||||
public constructor(public readonly position?: vec2) {
|
public constructor(public readonly position?: vec2) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'SecondaryActionCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,8 @@ export class StepCommand extends Command {
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'StepCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,8 @@ export class SwipeCommand extends Command {
|
||||||
public constructor(public readonly delta?: vec2) {
|
public constructor(public readonly delta?: vec2) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'SwipeCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,8 @@ export class ZoomCommand extends Command {
|
||||||
public constructor(public readonly factor?: number) {
|
public constructor(public readonly factor?: number) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'ZoomCommand';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
|
||||||
import { Circle } from './primitives/circle';
|
|
||||||
import { IPrimitive } from './primitives/i-primitive';
|
|
||||||
|
|
||||||
export interface IRenderer {
|
|
||||||
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
|
||||||
finishFrame(): void;
|
|
||||||
|
|
||||||
drawPrimitive(primitive: IPrimitive): void;
|
|
||||||
|
|
||||||
setCameraPosition(position: vec2): void;
|
|
||||||
setCursorPosition(position: vec2): void;
|
|
||||||
setInViewArea(size: number): vec2;
|
|
||||||
|
|
||||||
giveUniforms(uniforms: any): void;
|
|
||||||
appendToUniformList(listName: string, ...values: Array<any>): void;
|
|
||||||
|
|
||||||
screenUvToWorldCoordinate(mousePosition: vec2): vec2;
|
|
||||||
drawInfoText(text: string): void;
|
|
||||||
isOnScreen(boundingCircle: Circle): boolean;
|
|
||||||
isPositionOnScreen(position: vec2): boolean;
|
|
||||||
}
|
|
||||||
35
frontend/src/scripts/drawing/lights/circle-light.ts
Normal file
35
frontend/src/scripts/drawing/lights/circle-light.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { ILight } from './i-light';
|
||||||
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
|
|
||||||
|
export class CircleLight implements ILight {
|
||||||
|
public static uniformName = 'lights';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public center: vec2,
|
||||||
|
public radius: number,
|
||||||
|
public color: vec3,
|
||||||
|
public lightness: number
|
||||||
|
) {}
|
||||||
|
|
||||||
|
serializeToUniforms(uniforms: any): void {
|
||||||
|
const listName = CircleLight.uniformName;
|
||||||
|
|
||||||
|
if (!uniforms.hasOwnProperty(listName)) {
|
||||||
|
uniforms[listName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
uniforms[listName].push({
|
||||||
|
center: this.center,
|
||||||
|
radius: this.radius,
|
||||||
|
value: this.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get value(): vec3 {
|
||||||
|
return vec3.scale(
|
||||||
|
vec3.create(),
|
||||||
|
vec3.normalize(this.color, this.color),
|
||||||
|
this.lightness
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
frontend/src/scripts/drawing/lights/i-light.ts
Normal file
3
frontend/src/scripts/drawing/lights/i-light.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export interface ILight {
|
||||||
|
serializeToUniforms(uniforms: any): void;
|
||||||
|
}
|
||||||
30
frontend/src/scripts/drawing/lights/point-light.ts
Normal file
30
frontend/src/scripts/drawing/lights/point-light.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { ILight } from './i-light';
|
||||||
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
|
|
||||||
|
export class PointLight implements ILight {
|
||||||
|
public static uniformName = 'lights';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public center: vec2,
|
||||||
|
public color: vec3,
|
||||||
|
public lightness: number
|
||||||
|
) {}
|
||||||
|
|
||||||
|
serializeToUniforms(uniforms: any): void {
|
||||||
|
const listName = PointLight.uniformName;
|
||||||
|
|
||||||
|
if (!uniforms.hasOwnProperty(listName)) {
|
||||||
|
uniforms[listName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
uniforms[listName].push({
|
||||||
|
center: this.center,
|
||||||
|
radius: 0,
|
||||||
|
value: this.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get value(): vec3 {
|
||||||
|
return vec3.scale(vec3.create(), this.color, this.lightness);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,11 @@ import { vec2 } from 'gl-matrix';
|
||||||
import { IPrimitive } from './i-primitive';
|
import { IPrimitive } from './i-primitive';
|
||||||
|
|
||||||
export class Circle implements IPrimitive {
|
export class Circle implements IPrimitive {
|
||||||
public constructor(public center: vec2, public radius: number) {}
|
public constructor(public center = vec2.create(), public radius = 0) {}
|
||||||
|
|
||||||
|
public serializeToUniforms(uniforms: any): void {
|
||||||
|
throw new Error('Method not implemented.');
|
||||||
|
}
|
||||||
|
|
||||||
public distance(target: vec2): number {
|
public distance(target: vec2): number {
|
||||||
return vec2.distance(this.center, target) - this.radius;
|
return vec2.distance(this.center, target) - this.radius;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
export interface IPrimitive {
|
export interface IPrimitive {
|
||||||
|
serializeToUniforms(uniforms: any): void;
|
||||||
distance(target: vec2): number;
|
distance(target: vec2): number;
|
||||||
minimumDistance(target: vec2): number;
|
minimumDistance(target: vec2): number;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ import { Circle } from './circle';
|
||||||
import { IPrimitive } from './i-primitive';
|
import { IPrimitive } from './i-primitive';
|
||||||
|
|
||||||
export class TunnelShape implements IPrimitive {
|
export class TunnelShape implements IPrimitive {
|
||||||
|
public static uniformNameLines = 'lines';
|
||||||
|
public static uniformNameRadii = 'radii';
|
||||||
|
|
||||||
public readonly toFromDelta: vec2;
|
public readonly toFromDelta: vec2;
|
||||||
private toFromDeltaLength: number;
|
private toFromDeltaLength: number;
|
||||||
|
|
||||||
|
|
@ -25,6 +28,21 @@ export class TunnelShape implements IPrimitive {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serializeToUniforms(uniforms: any): void {
|
||||||
|
if (!uniforms.hasOwnProperty(TunnelShape.uniformNameLines)) {
|
||||||
|
uniforms[TunnelShape.uniformNameLines] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uniforms.hasOwnProperty(TunnelShape.uniformNameRadii)) {
|
||||||
|
uniforms[TunnelShape.uniformNameRadii] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
uniforms[TunnelShape.uniformNameLines].push(this.from);
|
||||||
|
uniforms[TunnelShape.uniformNameLines].push(this.toFromDelta);
|
||||||
|
uniforms[TunnelShape.uniformNameRadii].push(this.fromRadius);
|
||||||
|
uniforms[TunnelShape.uniformNameRadii].push(this.toRadius);
|
||||||
|
}
|
||||||
|
|
||||||
public distance(target: vec2): number {
|
public distance(target: vec2): number {
|
||||||
const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
|
const targetFromDelta = vec2.subtract(vec2.create(), target, this.from);
|
||||||
const h = clamp01(
|
const h = clamp01(
|
||||||
|
|
|
||||||
47
frontend/src/scripts/drawing/rendering/autoscaler.ts
Normal file
47
frontend/src/scripts/drawing/rendering/autoscaler.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { mix } from '../../helper/mix';
|
||||||
|
import { clamp } from '../../helper/clamp';
|
||||||
|
|
||||||
|
export class Autoscaler {
|
||||||
|
// can have fractions
|
||||||
|
private index: number;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private setters: Array<(value: number) => void>,
|
||||||
|
private targets: Array<Array<number>>,
|
||||||
|
startingIndex: number,
|
||||||
|
private scalingOptions: {
|
||||||
|
additiveIncrease: number;
|
||||||
|
multiplicativeDecrease: number;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
this.index = startingIndex;
|
||||||
|
this.applyScaling();
|
||||||
|
}
|
||||||
|
|
||||||
|
public increase() {
|
||||||
|
this.index += this.scalingOptions.additiveIncrease;
|
||||||
|
this.applyScaling();
|
||||||
|
}
|
||||||
|
|
||||||
|
public decrease() {
|
||||||
|
this.index /= this.scalingOptions.multiplicativeDecrease;
|
||||||
|
this.applyScaling();
|
||||||
|
}
|
||||||
|
|
||||||
|
private applyScaling() {
|
||||||
|
this.index = clamp(this.index, 0, this.targets.length - 1);
|
||||||
|
|
||||||
|
const floor = Math.floor(this.index);
|
||||||
|
const fract = this.index - floor;
|
||||||
|
|
||||||
|
const previousTarget = this.targets[floor];
|
||||||
|
const nextTarget =
|
||||||
|
floor + 1 == this.targets.length
|
||||||
|
? previousTarget
|
||||||
|
: this.targets[floor + 1];
|
||||||
|
|
||||||
|
this.setters.forEach((setter, i) =>
|
||||||
|
setter(mix(previousTarget[i], nextTarget[i], fract))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
frontend/src/scripts/drawing/rendering/i-renderer.ts
Normal file
16
frontend/src/scripts/drawing/rendering/i-renderer.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { vec2 } from 'gl-matrix';
|
||||||
|
import { IPrimitive } from '../primitives/i-primitive';
|
||||||
|
import { ILight } from '../lights/i-light';
|
||||||
|
|
||||||
|
export interface IRenderer {
|
||||||
|
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
||||||
|
finishFrame(): void;
|
||||||
|
|
||||||
|
drawPrimitive(primitive: IPrimitive): void;
|
||||||
|
drawLight(light: ILight): void;
|
||||||
|
drawInfoText(text: string): void;
|
||||||
|
|
||||||
|
setCameraPosition(position: vec2): void;
|
||||||
|
setCursorPosition(position: vec2): void;
|
||||||
|
setInViewArea(size: number): vec2;
|
||||||
|
}
|
||||||
8
frontend/src/scripts/drawing/rendering/rendering-pass.ts
Normal file
8
frontend/src/scripts/drawing/rendering/rendering-pass.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { IProgram } from '../graphics-library/program/i-program';
|
||||||
|
import { FrameBuffer } from '../graphics-library/frame-buffer/frame-buffer';
|
||||||
|
|
||||||
|
export class RenderingPass {
|
||||||
|
constructor(private program: IProgram, private frame: FrameBuffer) {}
|
||||||
|
|
||||||
|
public render(uniforms: any, inputTexture?: WebGLTexture) {}
|
||||||
|
}
|
||||||
27
frontend/src/scripts/drawing/rendering/settings.ts
Normal file
27
frontend/src/scripts/drawing/rendering/settings.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
export const settings = {
|
||||||
|
qualityScaling: {
|
||||||
|
targetDeltaTimeInMilliseconds: 30,
|
||||||
|
deltaTimeError: 2,
|
||||||
|
deltaTimeResponsiveness: 1 / 16,
|
||||||
|
adjusmentRateInMilliseconds: 300,
|
||||||
|
scaleTargets: [
|
||||||
|
[0.2, 0.1],
|
||||||
|
[0.6, 0.1],
|
||||||
|
[1, 0.3],
|
||||||
|
[1.25, 0.75],
|
||||||
|
[1.5, 1],
|
||||||
|
[1.75, 1.25],
|
||||||
|
[1.75, 1.75],
|
||||||
|
],
|
||||||
|
startingTargetIndex: 2,
|
||||||
|
scalingOptions: {
|
||||||
|
additiveIncrease: 0.2,
|
||||||
|
multiplicativeDecrease: 1.15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tileMultiplier: 5,
|
||||||
|
shaderUniforms: {
|
||||||
|
distanceScale: 64,
|
||||||
|
edgeSmoothing: 10,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -1,124 +1,69 @@
|
||||||
import { mat2d, vec2 } from 'gl-matrix';
|
import { mat2d, vec2 } from 'gl-matrix';
|
||||||
import { clamp } from '../helper/clamp';
|
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 { 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 { Circle } from '../primitives/circle';
|
||||||
import { IPrimitive } from './primitives/i-primitive';
|
import { IPrimitive } from '../primitives/i-primitive';
|
||||||
import { Rectangle } from './primitives/rectangle';
|
import { Rectangle } from '../primitives/rectangle';
|
||||||
import { TunnelShape } from './primitives/tunnel-shape';
|
import { TunnelShape } from '../primitives/tunnel-shape';
|
||||||
|
import { Autoscaler } from './autoscaler';
|
||||||
|
import { ILight } from '../lights/i-light';
|
||||||
|
import { toPercent } from '../../helper/to-percent';
|
||||||
|
import { exponentialDecay } from '../../helper/exponential-decay';
|
||||||
|
import { settings } from './settings';
|
||||||
|
|
||||||
export class WebGl2Renderer implements IRenderer {
|
export class WebGl2Renderer implements IRenderer {
|
||||||
private gl: WebGL2RenderingContext;
|
private gl: WebGL2RenderingContext;
|
||||||
|
private qualityScaler: Autoscaler;
|
||||||
private stopwatch?: WebGlStopwatch;
|
private stopwatch?: WebGlStopwatch;
|
||||||
|
|
||||||
private viewBox: Rectangle = new Rectangle();
|
private viewBox: Rectangle = new Rectangle();
|
||||||
private viewCircle: Circle = new Circle(vec2.create(), 0);
|
private viewCircle: Circle = new Circle();
|
||||||
private uniforms: any;
|
|
||||||
private cursorPosition = vec2.create();
|
private cursorPosition = vec2.create();
|
||||||
|
|
||||||
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
private distanceFieldFrameBuffer: IntermediateFrameBuffer;
|
||||||
private lightingFrameBuffer: DefaultFrameBuffer;
|
private lightingFrameBuffer: DefaultFrameBuffer;
|
||||||
private distanceProgram: IProgram;
|
private distanceProgram: IProgram;
|
||||||
private lightingProgram: IProgram;
|
private lightingProgram: IProgram;
|
||||||
|
|
||||||
private primitives: Array<IPrimitive>;
|
private primitives: Array<IPrimitive>;
|
||||||
|
private lights: Array<ILight>;
|
||||||
private tileMultiplier = 5;
|
|
||||||
|
|
||||||
private targetDeltaTime = (1 / 30) * 1000;
|
|
||||||
private deltaTimeError = (1 / 1000) * 1000;
|
|
||||||
private additiveQualityIncrease = 0.03;
|
|
||||||
private multiplicativeQualityDecrease = 1.2;
|
|
||||||
private timeSinceLastAdjusment = 0;
|
|
||||||
private adjusmentRate = 300;
|
|
||||||
private maxRenderScale = 1.5;
|
|
||||||
private minRenderScale = 0.2;
|
|
||||||
|
|
||||||
private exponentialDecayedDeltaTime = 0.0;
|
|
||||||
|
|
||||||
private configureRenderScale(deltaTime: DOMHighResTimeStamp) {
|
|
||||||
this.timeSinceLastAdjusment += deltaTime;
|
|
||||||
if (this.timeSinceLastAdjusment < this.adjusmentRate) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.timeSinceLastAdjusment = 0;
|
|
||||||
|
|
||||||
this.exponentialDecayedDeltaTime =
|
|
||||||
(15 / 16) * this.exponentialDecayedDeltaTime + deltaTime / 16;
|
|
||||||
|
|
||||||
if (
|
|
||||||
this.exponentialDecayedDeltaTime <=
|
|
||||||
this.targetDeltaTime - this.deltaTimeError
|
|
||||||
) {
|
|
||||||
this.distanceFieldFrameBuffer.renderScale +=
|
|
||||||
this.additiveQualityIncrease / 3;
|
|
||||||
this.lightingFrameBuffer.renderScale += this.additiveQualityIncrease;
|
|
||||||
} else if (
|
|
||||||
this.exponentialDecayedDeltaTime >
|
|
||||||
this.targetDeltaTime + this.deltaTimeError
|
|
||||||
) {
|
|
||||||
this.distanceFieldFrameBuffer.renderScale /= this.multiplicativeQualityDecrease;
|
|
||||||
this.lightingFrameBuffer.renderScale /= this.multiplicativeQualityDecrease;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer.renderScale = clamp(
|
|
||||||
this.distanceFieldFrameBuffer.renderScale,
|
|
||||||
0.1,
|
|
||||||
this.maxRenderScale
|
|
||||||
);
|
|
||||||
this.lightingFrameBuffer.renderScale = clamp(
|
|
||||||
this.lightingFrameBuffer.renderScale,
|
|
||||||
this.minRenderScale,
|
|
||||||
this.maxRenderScale
|
|
||||||
);
|
|
||||||
|
|
||||||
InfoText.modifyRecord(
|
|
||||||
'dt decay',
|
|
||||||
this.exponentialDecayedDeltaTime.toFixed(2)
|
|
||||||
);
|
|
||||||
InfoText.modifyRecord(
|
|
||||||
'q1',
|
|
||||||
this.distanceFieldFrameBuffer.renderScale.toFixed(2)
|
|
||||||
);
|
|
||||||
InfoText.modifyRecord(
|
|
||||||
'q2',
|
|
||||||
this.lightingFrameBuffer.renderScale.toFixed(2)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private canvas: HTMLCanvasElement,
|
private canvas: HTMLCanvasElement,
|
||||||
private overlay: HTMLElement,
|
private overlay: HTMLElement,
|
||||||
shaderSources: Array<[string, string]>
|
shaderSources: Array<[string, string]>
|
||||||
) {
|
) {
|
||||||
this.gl = this.canvas.getContext('webgl2');
|
this.getContext();
|
||||||
if (!this.gl) {
|
|
||||||
throw new Error('WebGl2 is not supported');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.createPipeline(shaderSources);
|
this.createPipeline(shaderSources);
|
||||||
|
this.setupAutoscaling();
|
||||||
this.distanceFieldFrameBuffer.renderScale = 0.5;
|
|
||||||
this.lightingFrameBuffer.renderScale = 1;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.stopwatch = new WebGlStopwatch(this.gl);
|
this.stopwatch = new WebGlStopwatch(this.gl);
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getContext() {
|
||||||
|
this.gl = this.canvas.getContext('webgl2');
|
||||||
|
if (!this.gl) {
|
||||||
|
throw new Error('WebGl2 is not supported');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private createPipeline(shaderSources: Array<[string, string]>) {
|
private createPipeline(shaderSources: Array<[string, string]>) {
|
||||||
const distanceScale = 64;
|
const distanceScale = settings.shaderUniforms.distanceScale;
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl);
|
this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl);
|
||||||
this.distanceProgram = new UniformArrayAutoScalingProgram(
|
this.distanceProgram = new UniformArrayAutoScalingProgram(
|
||||||
this.gl,
|
this.gl,
|
||||||
shaderSources[0][0],
|
shaderSources[0][0],
|
||||||
shaderSources[0][1],
|
shaderSources[0][1],
|
||||||
{ distanceScale },
|
{ ...settings.shaderUniforms },
|
||||||
{
|
{
|
||||||
getValueFromUniforms: (v) => (v.lines ? v.lines.length / 2 : 0),
|
getValueFromUniforms: (v) => (v.lines ? v.lines.length / 2 : 0),
|
||||||
uniformArraySizeName: 'lineCount',
|
uniformArraySizeName: 'lineCount',
|
||||||
|
|
@ -134,7 +79,7 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
this.gl,
|
this.gl,
|
||||||
shaderSources[1][0],
|
shaderSources[1][0],
|
||||||
shaderSources[1][1],
|
shaderSources[1][1],
|
||||||
{ distanceScale },
|
{ ...settings.shaderUniforms },
|
||||||
{
|
{
|
||||||
getValueFromUniforms: (v) => (v.lights ? v.lights.length : 0),
|
getValueFromUniforms: (v) => (v.lights ? v.lights.length : 0),
|
||||||
uniformArraySizeName: 'lightCount',
|
uniformArraySizeName: 'lightCount',
|
||||||
|
|
@ -146,25 +91,81 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setupAutoscaling() {
|
||||||
|
this.qualityScaler = new Autoscaler(
|
||||||
|
[
|
||||||
|
(v) => (this.lightingFrameBuffer.renderScale = v),
|
||||||
|
(v) => (this.distanceFieldFrameBuffer.renderScale = v),
|
||||||
|
],
|
||||||
|
settings.qualityScaling.scaleTargets,
|
||||||
|
settings.qualityScaling.startingTargetIndex,
|
||||||
|
settings.qualityScaling.scalingOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private timeSinceLastAdjusment = 0;
|
||||||
|
private exponentialDecayedDeltaTime = 0.0;
|
||||||
|
private configureQuality(deltaTime: DOMHighResTimeStamp) {
|
||||||
|
this.timeSinceLastAdjusment += deltaTime;
|
||||||
|
if (
|
||||||
|
this.timeSinceLastAdjusment >=
|
||||||
|
settings.qualityScaling.adjusmentRateInMilliseconds
|
||||||
|
) {
|
||||||
|
this.timeSinceLastAdjusment = 0;
|
||||||
|
this.exponentialDecayedDeltaTime = exponentialDecay(
|
||||||
|
this.exponentialDecayedDeltaTime,
|
||||||
|
deltaTime,
|
||||||
|
settings.qualityScaling.deltaTimeResponsiveness
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.exponentialDecayedDeltaTime <=
|
||||||
|
settings.qualityScaling.targetDeltaTimeInMilliseconds -
|
||||||
|
settings.qualityScaling.deltaTimeError
|
||||||
|
) {
|
||||||
|
this.qualityScaler.increase();
|
||||||
|
} else if (
|
||||||
|
this.exponentialDecayedDeltaTime >
|
||||||
|
settings.qualityScaling.targetDeltaTimeInMilliseconds +
|
||||||
|
settings.qualityScaling.deltaTimeError
|
||||||
|
) {
|
||||||
|
this.qualityScaler.decrease();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoText.modifyRecord(
|
||||||
|
'quality',
|
||||||
|
`${toPercent(this.distanceFieldFrameBuffer.renderScale)}, ${toPercent(
|
||||||
|
this.lightingFrameBuffer.renderScale
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public drawPrimitive(primitive: IPrimitive) {
|
public drawPrimitive(primitive: IPrimitive) {
|
||||||
this.primitives.push(primitive);
|
this.primitives.push(primitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public drawLight(light: ILight) {
|
||||||
|
this.lights.push(light);
|
||||||
|
}
|
||||||
|
|
||||||
public startFrame(deltaTime: DOMHighResTimeStamp): void {
|
public startFrame(deltaTime: DOMHighResTimeStamp): void {
|
||||||
this.configureRenderScale(deltaTime);
|
this.configureQuality(deltaTime);
|
||||||
this.primitives = [];
|
this.primitives = [];
|
||||||
|
this.lights = [];
|
||||||
|
|
||||||
this.stopwatch?.start();
|
this.stopwatch?.start();
|
||||||
this.uniforms = {};
|
|
||||||
this.distanceFieldFrameBuffer.setSize();
|
this.distanceFieldFrameBuffer.setSize();
|
||||||
this.lightingFrameBuffer.setSize();
|
this.lightingFrameBuffer.setSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public finishFrame() {
|
public finishFrame() {
|
||||||
this.calculateOwnUniforms();
|
const uniforms: any = this.calculateOwnUniforms();
|
||||||
|
|
||||||
|
this.lights.forEach((l) => l.serializeToUniforms(uniforms));
|
||||||
|
|
||||||
this.distanceFieldFrameBuffer.bindAndClear();
|
this.distanceFieldFrameBuffer.bindAndClear();
|
||||||
const q = 1 / this.tileMultiplier;
|
const q = 1 / settings.tileMultiplier;
|
||||||
const uvSize = vec2.fromValues(q, q);
|
const uvSize = vec2.fromValues(q, q);
|
||||||
|
|
||||||
const possiblyOnScreenPrimitives = this.primitives.filter(
|
const possiblyOnScreenPrimitives = this.primitives.filter(
|
||||||
|
|
@ -179,19 +180,19 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
const origin = vec2.transformMat2d(
|
const origin = vec2.transformMat2d(
|
||||||
vec2.create(),
|
vec2.create(),
|
||||||
vec2.fromValues(0, 0),
|
vec2.fromValues(0, 0),
|
||||||
this.uniforms.uvToWorld
|
uniforms.uvToWorld
|
||||||
);
|
);
|
||||||
|
|
||||||
const firstCenter = vec2.transformMat2d(
|
const firstCenter = vec2.transformMat2d(
|
||||||
vec2.create(),
|
vec2.create(),
|
||||||
vec2.fromValues(q, q),
|
vec2.fromValues(q, q),
|
||||||
this.uniforms.uvToWorld
|
uniforms.uvToWorld
|
||||||
);
|
);
|
||||||
|
|
||||||
vec2.subtract(firstCenter, firstCenter, origin);
|
vec2.subtract(firstCenter, firstCenter, origin);
|
||||||
|
|
||||||
const worldR = vec2.length(firstCenter);
|
const worldR = vec2.length(firstCenter);
|
||||||
this.uniforms.maxMinDistance = 2 * worldR;
|
uniforms.maxMinDistance = 2 * worldR;
|
||||||
|
|
||||||
let sumLineCount = 0;
|
let sumLineCount = 0;
|
||||||
|
|
||||||
|
|
@ -203,7 +204,7 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
const tileCenterWorldCoordinates = vec2.transformMat2d(
|
const tileCenterWorldCoordinates = vec2.transformMat2d(
|
||||||
vec2.create(),
|
vec2.create(),
|
||||||
vec2.add(vec2.create(), uvBottomLeft, vec2.fromValues(q, q)),
|
vec2.add(vec2.create(), uvBottomLeft, vec2.fromValues(q, q)),
|
||||||
this.uniforms.uvToWorld
|
uniforms.uvToWorld
|
||||||
);
|
);
|
||||||
|
|
||||||
const primitivesNearTile = possiblyOnScreenPrimitives.filter(
|
const primitivesNearTile = possiblyOnScreenPrimitives.filter(
|
||||||
|
|
@ -212,36 +213,35 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
|
|
||||||
sumLineCount += primitivesNearTile.length;
|
sumLineCount += primitivesNearTile.length;
|
||||||
|
|
||||||
this.uniforms.lines = [];
|
uniforms.lines = [];
|
||||||
this.uniforms.radii = [];
|
uniforms.radii = [];
|
||||||
|
|
||||||
for (let tunnel of primitivesNearTile) {
|
primitivesNearTile.forEach((p) => p.serializeToUniforms(uniforms));
|
||||||
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.bindAndSetUniforms(uniforms);
|
||||||
this.distanceProgram.draw();
|
this.distanceProgram.draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InfoText.modifyRecord(
|
InfoText.modifyRecord(
|
||||||
'lines',
|
'lines',
|
||||||
(sumLineCount / this.tileMultiplier / this.tileMultiplier).toFixed(2)
|
(
|
||||||
|
sumLineCount /
|
||||||
|
settings.tileMultiplier /
|
||||||
|
settings.tileMultiplier
|
||||||
|
).toFixed(2)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.lightingFrameBuffer.bindAndClear(
|
this.lightingFrameBuffer.bindAndClear(
|
||||||
this.distanceFieldFrameBuffer.colorTexture
|
this.distanceFieldFrameBuffer.colorTexture
|
||||||
);
|
);
|
||||||
this.lightingProgram.bindAndSetUniforms(this.uniforms);
|
this.lightingProgram.bindAndSetUniforms(uniforms);
|
||||||
this.lightingProgram.draw();
|
this.lightingProgram.draw();
|
||||||
|
|
||||||
this.stopwatch?.stop();
|
this.stopwatch?.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateOwnUniforms() {
|
private calculateOwnUniforms(): any {
|
||||||
const distanceScreenToWorld = this.getScreenToWorldTransform(
|
const distanceScreenToWorld = this.getScreenToWorldTransform(
|
||||||
this.distanceFieldFrameBuffer.getSize()
|
this.distanceFieldFrameBuffer.getSize()
|
||||||
);
|
);
|
||||||
|
|
@ -267,14 +267,14 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
|
|
||||||
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
|
const cursorPosition = this.screenUvToWorldCoordinate(this.cursorPosition);
|
||||||
|
|
||||||
this.giveUniforms({
|
return {
|
||||||
distanceScreenToWorld,
|
distanceScreenToWorld,
|
||||||
worldToDistanceUV,
|
worldToDistanceUV,
|
||||||
cursorPosition,
|
cursorPosition,
|
||||||
ndcToUv,
|
ndcToUv,
|
||||||
uvToWorld,
|
uvToWorld,
|
||||||
viewBoxSize: this.viewBox.size,
|
viewBoxSize: this.viewBox.size,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private getScreenToWorldTransform(screenSize: vec2) {
|
private getScreenToWorldTransform(screenSize: vec2) {
|
||||||
|
|
@ -304,26 +304,18 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
|
|
||||||
public setCameraPosition(position: vec2) {
|
public setCameraPosition(position: vec2) {
|
||||||
this.viewBox.topLeft = position;
|
this.viewBox.topLeft = position;
|
||||||
|
const halfDiagonal = vec2.scale(vec2.create(), this.viewBox.size, 0.5);
|
||||||
|
this.viewCircle.center = vec2.add(
|
||||||
|
vec2.create(),
|
||||||
|
this.viewBox.topLeft,
|
||||||
|
halfDiagonal
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCursorPosition(position: vec2): void {
|
public setCursorPosition(position: vec2): void {
|
||||||
this.cursorPosition = position;
|
this.cursorPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
public appendToUniformList(listName: string, ...values: any[]): void {
|
|
||||||
if (!this.uniforms.hasOwnProperty(listName)) {
|
|
||||||
this.uniforms[listName] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let value of values) {
|
|
||||||
this.uniforms[listName].push(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public giveUniforms(uniforms: any): void {
|
|
||||||
this.uniforms = { ...this.uniforms, ...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;
|
||||||
|
|
@ -350,12 +342,4 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
this.overlay.innerText = text;
|
this.overlay.innerText = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public isOnScreen(boundingCircle: Circle): boolean {
|
|
||||||
return this.viewCircle.areIntersecting(boundingCircle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public isPositionOnScreen(position: vec2): boolean {
|
|
||||||
return this.viewCircle.isInside(position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ import { CommandBroadcaster } from './commands/command-broadcaster';
|
||||||
import { BeforeRenderCommand } from './commands/types/before-render';
|
import { BeforeRenderCommand } from './commands/types/before-render';
|
||||||
import { RenderCommand } 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/rendering/webgl2-renderer';
|
||||||
import { timeIt } from './helper/timing';
|
import { timeIt } from './helper/timing';
|
||||||
import { KeyboardListener } from './input/keyboard-listener';
|
import { KeyboardListener } from './input/keyboard-listener';
|
||||||
import { MouseListener } from './input/mouse-listener';
|
import { MouseListener } from './input/mouse-listener';
|
||||||
|
|
|
||||||
5
frontend/src/scripts/helper/exponential-decay.ts
Normal file
5
frontend/src/scripts/helper/exponential-decay.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
export const exponentialDecay = (
|
||||||
|
accumulator: number,
|
||||||
|
nextValue: number,
|
||||||
|
biasOfNextValue: number
|
||||||
|
) => accumulator * (1 - biasOfNextValue) + nextValue * biasOfNextValue;
|
||||||
1
frontend/src/scripts/helper/to-percent.ts
Normal file
1
frontend/src/scripts/helper/to-percent.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export const toPercent = (value: number) => `${Math.round(value * 100)}%`;
|
||||||
|
|
@ -2,9 +2,8 @@ import { vec2 } from 'gl-matrix';
|
||||||
import { Command } from '../commands/command';
|
import { Command } from '../commands/command';
|
||||||
import { CommandReceiver } from '../commands/command-receiver';
|
import { CommandReceiver } from '../commands/command-receiver';
|
||||||
import { IdentityManager } from '../identity/identity-manager';
|
import { IdentityManager } from '../identity/identity-manager';
|
||||||
import { Typed } from '../transport/serializable';
|
|
||||||
|
|
||||||
export abstract class GameObject extends Typed implements CommandReceiver {
|
export abstract class GameObject implements CommandReceiver {
|
||||||
public readonly id = IdentityManager.generateId();
|
public readonly id = IdentityManager.generateId();
|
||||||
|
|
||||||
protected _position = vec2.create();
|
protected _position = vec2.create();
|
||||||
|
|
@ -21,12 +20,12 @@ export abstract class GameObject extends Typed implements CommandReceiver {
|
||||||
[commandType: string]: (e: Command) => void;
|
[commandType: string]: (e: Command) => void;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
// can only be called inside the constructor
|
// can only be called inside the constructor
|
||||||
protected addCommandExecutor<T extends Command>(
|
protected addCommandExecutor<T extends Command>(
|
||||||
commandType: new () => T,
|
commandType: new () => T,
|
||||||
handler: (command: T) => void
|
handler: (command: T) => void
|
||||||
) {
|
) {
|
||||||
this.commandExecutors[commandType.name] = handler;
|
this.commandExecutors[new commandType().type] = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public reactsToCommand(commandType: string): boolean {
|
public reactsToCommand(commandType: string): boolean {
|
||||||
|
|
@ -34,7 +33,7 @@ export abstract class GameObject extends Typed implements CommandReceiver {
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendCommand(command: Command) {
|
public sendCommand(command: Command) {
|
||||||
const commandType = command.constructor.name;
|
const commandType = command.type;
|
||||||
|
|
||||||
if (this.commandExecutors.hasOwnProperty(commandType)) {
|
if (this.commandExecutors.hasOwnProperty(commandType)) {
|
||||||
this.commandExecutors[commandType](command);
|
this.commandExecutors[commandType](command);
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ 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';
|
||||||
import { GameObject } from '../game-object';
|
import { GameObject } from '../game-object';
|
||||||
import { CircleLight } from './circle-light';
|
import { Lamp } from './lamp';
|
||||||
|
|
||||||
export class Camera extends GameObject {
|
export class Camera extends GameObject {
|
||||||
private inViewArea = 1920 * 1080 * 5;
|
private inViewArea = 1920 * 1080 * 5;
|
||||||
private cursorPosition = vec2.create();
|
private cursorPosition = vec2.create();
|
||||||
|
|
||||||
constructor(private light: CircleLight) {
|
constructor(private light: Lamp) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.addCommandExecutor(BeforeRenderCommand, this.draw.bind(this));
|
this.addCommandExecutor(BeforeRenderCommand, this.draw.bind(this));
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
import { vec2, vec3 } from 'gl-matrix';
|
|
||||||
import { RenderCommand } from '../../commands/types/draw';
|
|
||||||
import { MoveToCommand } from '../../commands/types/move-to';
|
|
||||||
import { Circle } from '../../drawing/primitives/circle';
|
|
||||||
import { GameObject } from '../game-object';
|
|
||||||
|
|
||||||
const range = 2000;
|
|
||||||
|
|
||||||
export class CircleLight extends GameObject {
|
|
||||||
private boundingCircle: Circle;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private center: vec2,
|
|
||||||
private radius: number,
|
|
||||||
private value: vec3
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.boundingCircle = new Circle(center, range);
|
|
||||||
|
|
||||||
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
|
|
||||||
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
private draw(c: RenderCommand) {
|
|
||||||
if (c.renderer.isPositionOnScreen(this.center)) {
|
|
||||||
c.renderer.appendToUniformList('lights', {
|
|
||||||
center: this.center,
|
|
||||||
radius: this.radius,
|
|
||||||
value: this.value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private moveTo(c: MoveToCommand) {
|
|
||||||
this.center = c.position;
|
|
||||||
this.boundingCircle.center = c.position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
28
frontend/src/scripts/objects/types/lamp.ts
Normal file
28
frontend/src/scripts/objects/types/lamp.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
|
import { RenderCommand } from '../../commands/types/draw';
|
||||||
|
import { MoveToCommand } from '../../commands/types/move-to';
|
||||||
|
import { GameObject } from '../game-object';
|
||||||
|
import { CircleLight } from '../../drawing/lights/circle-light';
|
||||||
|
|
||||||
|
const range = 2000;
|
||||||
|
|
||||||
|
export class Lamp extends GameObject {
|
||||||
|
private light: CircleLight;
|
||||||
|
|
||||||
|
constructor(center: vec2, radius: number, color: vec3, lightness: number) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.light = new CircleLight(center, radius, color, lightness);
|
||||||
|
|
||||||
|
this.addCommandExecutor(RenderCommand, this.draw.bind(this));
|
||||||
|
this.addCommandExecutor(MoveToCommand, this.moveTo.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private draw(c: RenderCommand) {
|
||||||
|
c.renderer.drawLight(this.light);
|
||||||
|
}
|
||||||
|
|
||||||
|
private moveTo(c: MoveToCommand) {
|
||||||
|
this.light.center = c.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,13 +2,14 @@ import { vec2, vec3 } from 'gl-matrix';
|
||||||
import { ObjectContainer } from '../object-container';
|
import { ObjectContainer } from '../object-container';
|
||||||
import { Camera } from '../types/camera';
|
import { Camera } from '../types/camera';
|
||||||
import { Character } from '../types/character';
|
import { Character } from '../types/character';
|
||||||
import { CircleLight } from '../types/circle-light';
|
import { Lamp } from '../types/lamp';
|
||||||
|
|
||||||
export const createCharacter = (objects: ObjectContainer) => {
|
export const createCharacter = (objects: ObjectContainer) => {
|
||||||
const light = new CircleLight(
|
const light = new Lamp(
|
||||||
vec2.create(),
|
vec2.create(),
|
||||||
40,
|
40,
|
||||||
vec3.fromValues(0.67, 0.0, 0.33)
|
vec3.fromValues(0.67, 0.0, 0.33),
|
||||||
|
2
|
||||||
);
|
);
|
||||||
|
|
||||||
const camera = new Camera(light);
|
const camera = new Camera(light);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { vec2, vec3 } from 'gl-matrix';
|
import { vec2, vec3 } from 'gl-matrix';
|
||||||
import { ObjectContainer } from '../object-container';
|
import { ObjectContainer } from '../object-container';
|
||||||
import { CircleLight } from '../types/circle-light';
|
import { Lamp } from '../types/lamp';
|
||||||
import { Tunnel } from '../types/tunnel';
|
import { Tunnel } from '../types/tunnel';
|
||||||
|
|
||||||
export const createDungeon = (objects: ObjectContainer) => {
|
export const createDungeon = (objects: ObjectContainer) => {
|
||||||
|
|
@ -19,14 +19,15 @@ export const createDungeon = (objects: ObjectContainer) => {
|
||||||
|
|
||||||
if (deltaHeight > 0 && Math.random() > 0.8) {
|
if (deltaHeight > 0 && Math.random() > 0.8) {
|
||||||
objects.addObject(
|
objects.addObject(
|
||||||
new CircleLight(
|
new Lamp(
|
||||||
currentEnd,
|
currentEnd,
|
||||||
Math.random() * 20 + 30,
|
Math.random() * 20 + 30,
|
||||||
vec3.scale(
|
vec3.scale(
|
||||||
vec3.create(),
|
vec3.create(),
|
||||||
vec3.normalize(vec3.create(), vec3.fromValues(0.5, 0.1, 0.8)),
|
vec3.normalize(vec3.create(), vec3.fromValues(0.5, 0.1, 0.8)),
|
||||||
Math.random() * 0.5 + 0.5
|
Math.random() * 0.5 + 0.5
|
||||||
)
|
),
|
||||||
|
1
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
export class Typed {
|
export abstract class Typed {
|
||||||
public get type(): string {
|
public abstract get type(): string;
|
||||||
return this.constructor.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public toJSON() {
|
public toJSON() {
|
||||||
return { type: this.type, ...this };
|
return { type: this.type, ...this };
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,14 @@ precision mediump float;
|
||||||
#define CAVE_COLOR vec3(0.36, 0.38, 0.76)
|
#define CAVE_COLOR vec3(0.36, 0.38, 0.76)
|
||||||
#define AIR_COLOR vec3(0.7)
|
#define AIR_COLOR vec3(0.7)
|
||||||
#define DISTANCE_SCALE {distanceScale}
|
#define DISTANCE_SCALE {distanceScale}
|
||||||
|
#define EDGE_SMOOTHING {edgeSmoothing}
|
||||||
|
|
||||||
uniform float maxMinDistance;
|
uniform float maxMinDistance;
|
||||||
|
|
||||||
#if LINES_ENABLED
|
#if LINES_ENABLED
|
||||||
// start, end - start
|
// start, end - start
|
||||||
uniform vec2[LINE_COUNT * 2] lines;
|
uniform vec2[LINE_COUNT * 2] lines;
|
||||||
// startRadius, endRadois
|
// startRadius, endRadius
|
||||||
uniform float[LINE_COUNT * 2] radii;
|
uniform float[LINE_COUNT * 2] radii;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ void main() {
|
||||||
|
|
||||||
float distance = -minDistance;
|
float distance = -minDistance;
|
||||||
fragmentColor = vec4(
|
fragmentColor = vec4(
|
||||||
mix(CAVE_COLOR, AIR_COLOR, clamp(distance, -10.0, 0.0) / 10.0 + 1.0),
|
mix(CAVE_COLOR, AIR_COLOR, clamp(distance, -EDGE_SMOOTHING, 0.0) / EDGE_SMOOTHING + 1.0),
|
||||||
distance / DISTANCE_SCALE
|
distance / DISTANCE_SCALE
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@ precision mediump float;
|
||||||
|
|
||||||
#define INFINITY 1000.0
|
#define INFINITY 1000.0
|
||||||
#define AMBIENT_LIGHT vec3(0.15)
|
#define AMBIENT_LIGHT vec3(0.15)
|
||||||
#define LIGHT_DROP 400.0
|
#define LIGHT_DROP 500.0
|
||||||
#define MIN_STEP 3.0
|
#define MIN_STEP 1.0
|
||||||
#define EDGE_SMOOTHING 5.0
|
#define LIGHT_COUNT 1
|
||||||
#define LIGHT_COUNT {lightCount}
|
|
||||||
#define DISTANCE_SCALE {distanceScale}
|
#define DISTANCE_SCALE {distanceScale}
|
||||||
|
#define EDGE_SMOOTHING {edgeSmoothing}
|
||||||
|
|
||||||
uniform struct Light {
|
uniform struct Light {
|
||||||
vec2 center;
|
vec2 center;
|
||||||
|
|
@ -48,10 +48,14 @@ float getFractionOfLightArriving(
|
||||||
float minDistance = getDistance(target + direction * rayLength);
|
float minDistance = getDistance(target + direction * rayLength);
|
||||||
movingAverageMeanDistance = movingAverageMeanDistance / 2.0 + minDistance / 2.0;
|
movingAverageMeanDistance = movingAverageMeanDistance / 2.0 + minDistance / 2.0;
|
||||||
q = min(q, movingAverageMeanDistance / rayLength);
|
q = min(q, movingAverageMeanDistance / rayLength);
|
||||||
rayLength = min(lightDistance, rayLength + max(MIN_STEP, minDistance));
|
|
||||||
|
rayLength += max(MIN_STEP, minDistance);
|
||||||
|
if (rayLength > lightDistance) {
|
||||||
|
return clamp(q * (lightDistance + lightRadius) / lightRadius, 0.0, 1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return clamp(q * (lightDistance + lightRadius) / lightRadius, 0.0, 1.0);
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
in vec2 worldCoordinates;
|
in vec2 worldCoordinates;
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
|
||||||
#define LIGHT_COUNT {lightCount}
|
#define LIGHT_COUNT 1
|
||||||
|
|
||||||
uniform mat3 ndcToUv;
|
uniform mat3 ndcToUv;
|
||||||
uniform mat3 uvToWorld;
|
uniform mat3 uvToWorld;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,21 +22,21 @@ module.exports = {
|
||||||
minimize: true,
|
minimize: true,
|
||||||
minimizer: [
|
minimizer: [
|
||||||
new TerserJSPlugin({
|
new TerserJSPlugin({
|
||||||
sourceMap: !isProduction,
|
sourceMap: isDevelopment,
|
||||||
cache: true,
|
cache: true,
|
||||||
terserOptions: {
|
terserOptions: {
|
||||||
ecma: 5,
|
ecma: 5,
|
||||||
warnings: false,
|
warnings: true,
|
||||||
parse: {},
|
parse: {},
|
||||||
compress: {},
|
compress: { defaults: true },
|
||||||
mangle: true,
|
mangle: true,
|
||||||
module: false,
|
module: false,
|
||||||
output: null,
|
output: null,
|
||||||
toplevel: false,
|
toplevel: true,
|
||||||
nameCache: null,
|
nameCache: null,
|
||||||
ie8: false,
|
ie8: false,
|
||||||
keep_classnames: true,
|
keep_classnames: false,
|
||||||
keep_fnames: true,
|
keep_fnames: false,
|
||||||
safari10: false,
|
safari10: false,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue