Add parallel shader compiling
This commit is contained in:
parent
a6efbc02b9
commit
39a6ee9a20
17 changed files with 209 additions and 77 deletions
|
|
@ -25,14 +25,16 @@ const testSDF = () => {
|
||||||
return -res;
|
return -res;
|
||||||
|
|
||||||
*/
|
*/
|
||||||
let min = 1000;
|
/*let min = 1000;
|
||||||
for (const t of objects) {
|
for (const t of objects) {
|
||||||
min = Math.min(min, t.distance(position));
|
min = Math.min(min, t.distance(position));
|
||||||
}
|
}
|
||||||
if (min < 0) {
|
if (min < 0) {
|
||||||
// min = Math.min(min, vec2.distance(position, vec2.fromValues(40, 80 - 31.62)) - 31.62);
|
// min = Math.min(min, vec2.distance(position, vec2.fromValues(40, 80 - 31.62)) - 31.62);
|
||||||
}
|
}
|
||||||
return -min;
|
return -min;*/
|
||||||
|
|
||||||
|
return -vec2.distance(position, vec2.fromValues(40, 40)) + 30;
|
||||||
};
|
};
|
||||||
|
|
||||||
const width = 80;
|
const width = 80;
|
||||||
|
|
@ -55,11 +57,12 @@ const testSDF = () => {
|
||||||
const position = vec2.fromValues(x, y);
|
const position = vec2.fromValues(x, y);
|
||||||
const redIndex = y * (width * 4) + x * 4;
|
const redIndex = y * (width * 4) + x * 4;
|
||||||
const dist = getPixelValue(position);
|
const dist = getPixelValue(position);
|
||||||
if (Math.abs(dist) < 1) {
|
if (Math.abs(dist) < 1.5) {
|
||||||
const blueIndex = y * (width * 4) + x * 4 + 2;
|
|
||||||
zeroes.push(position);
|
zeroes.push(position);
|
||||||
imageData.data[blueIndex] = 255;
|
|
||||||
}
|
}
|
||||||
|
const blueIndex = y * (width * 4) + x * 4 + 2;
|
||||||
|
//imageData.data[blueIndex] = dist * 10;
|
||||||
|
|
||||||
imageData.data[redIndex + 3] = 255;
|
imageData.data[redIndex + 3] = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,25 +78,37 @@ const testSDF = () => {
|
||||||
const position = vec2.fromValues(x, y);
|
const position = vec2.fromValues(x, y);
|
||||||
const dist = getPixelValue(position);
|
const dist = getPixelValue(position);
|
||||||
|
|
||||||
const nearestDist =
|
const realDistance = zeroes.map((z) => vec2.distance(z, position)).sort()[0];
|
||||||
zeroes.find((z) => Math.abs(vec2.distance(z, position) - dist) < 0.5)?.length > 0;
|
|
||||||
if (nearestDist) {
|
if (Math.abs(realDistance - dist) < 3) {
|
||||||
const greenIndex = y * (width * 4) + x * 4 + 1;
|
const greenIndex = y * (width * 4) + x * 4 + 1;
|
||||||
imageData.data[greenIndex] = 255;
|
imageData.data[greenIndex] = 255;
|
||||||
} else if (dist >= 0) {
|
} else if (dist >= 0) {
|
||||||
const redIndex = y * (width * 4) + x * 4;
|
const redIndex = y * (width * 4) + x * 4;
|
||||||
errors.push(dist);
|
errors.push(realDistance - dist);
|
||||||
imageData.data[redIndex] = 255;
|
imageData.data[redIndex] = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*if (zeroes.find((z) => vec2.distance(z, position) < dist) !== undefined) {
|
||||||
|
const blueIndex = y * (width * 4) + x * 4 + 2;
|
||||||
|
imageData.data[blueIndex] = 255;
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zeroes.forEach((z) => {
|
||||||
|
const blueIndex = z.y * (width * 4) + z.x * 4 + 2;
|
||||||
|
imageData.data[blueIndex] = 255;
|
||||||
|
imageData.data[blueIndex - 1] = 255;
|
||||||
|
imageData.data[blueIndex - 2] = 255;
|
||||||
|
});
|
||||||
|
|
||||||
console.log(errors);
|
console.log(errors);
|
||||||
|
|
||||||
ctx.putImageData(imageData, 0, 0);
|
ctx.putImageData(imageData, 0, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// testSDF();
|
//testSDF();
|
||||||
// extract as new image (data-uri)
|
// extract as new image (data-uri)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -113,10 +128,14 @@ tree.print();
|
||||||
console.log(tree.findIntersecting(new BoundingBox(960, 1050, 50, 190, 'G')));
|
console.log(tree.findIntersecting(new BoundingBox(960, 1050, 50, 190, 'G')));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
try {
|
const main = async () => {
|
||||||
Random.seed = 42;
|
try {
|
||||||
new Game();
|
Random.seed = 42;
|
||||||
} catch (e) {
|
await new Game().start();
|
||||||
console.error(e);
|
} catch (e) {
|
||||||
alert(e);
|
console.error(e);
|
||||||
}
|
alert(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { checkShader } from './check-shader';
|
||||||
|
|
||||||
|
export const checkProgram = (gl: WebGL2RenderingContext, program: WebGLProgram) => {
|
||||||
|
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
gl.getAttachedShaders(program).forEach((s) => {
|
||||||
|
checkShader(gl, s);
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new Error(gl.getProgramInfoLog(program));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const checkShader = (gl: WebGL2RenderingContext, shader: WebGLShader) => {
|
||||||
|
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(gl.getShaderInfoLog(shader));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { waitWhileFalse } from '../../../helper/wait-for';
|
||||||
|
import { tryEnableExtension } from '../helper/enable-extension';
|
||||||
|
import { checkProgram } from './check-program';
|
||||||
|
import { createShader } from './create-shader';
|
||||||
|
|
||||||
|
export const createProgram = async (
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
vertexShaderSource: string,
|
||||||
|
fragmentShaderSource: string,
|
||||||
|
substitutions: { [name: string]: string }
|
||||||
|
): Promise<WebGLProgram> => {
|
||||||
|
const program = gl.createProgram();
|
||||||
|
|
||||||
|
const extension = tryEnableExtension(gl, 'KHR_parallel_shader_compile');
|
||||||
|
|
||||||
|
const vertexShader = createShader(
|
||||||
|
gl,
|
||||||
|
gl.VERTEX_SHADER,
|
||||||
|
vertexShaderSource,
|
||||||
|
substitutions
|
||||||
|
);
|
||||||
|
gl.attachShader(program, vertexShader);
|
||||||
|
|
||||||
|
const fragmentShader = createShader(
|
||||||
|
gl,
|
||||||
|
gl.FRAGMENT_SHADER,
|
||||||
|
fragmentShaderSource,
|
||||||
|
substitutions
|
||||||
|
);
|
||||||
|
gl.attachShader(program, fragmentShader);
|
||||||
|
|
||||||
|
gl.linkProgram(program);
|
||||||
|
|
||||||
|
if (extension) {
|
||||||
|
const checkCompileStatus = () =>
|
||||||
|
gl.getProgramParameter(program, extension.COMPLETION_STATUS_KHR);
|
||||||
|
|
||||||
|
await waitWhileFalse(checkCompileStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkProgram(gl, program);
|
||||||
|
|
||||||
|
return program;
|
||||||
|
};
|
||||||
|
|
@ -1,26 +1,17 @@
|
||||||
import { settings } from '../../settings';
|
|
||||||
|
|
||||||
export const createShader = (
|
export const createShader = (
|
||||||
gl: WebGL2RenderingContext,
|
gl: WebGL2RenderingContext,
|
||||||
type: GLenum,
|
type: GLenum,
|
||||||
source: string,
|
source: string,
|
||||||
substitutions: { [name: string]: string }
|
substitutions: { [name: string]: string }
|
||||||
): WebGLShader => {
|
): WebGLShader => {
|
||||||
const allSubstitutions = { ...settings.shaderMacros, ...substitutions };
|
|
||||||
|
|
||||||
source = source.replace(/{(.+)}/gm, (_, name: string): string => {
|
source = source.replace(/{(.+)}/gm, (_, name: string): string => {
|
||||||
const value = allSubstitutions[name];
|
const value = substitutions[name];
|
||||||
return Number.isInteger(value) ? `${value}.0` : value;
|
return Number.isInteger(value) ? `${value}.0` : value;
|
||||||
});
|
});
|
||||||
|
|
||||||
const shader = gl.createShader(type);
|
const shader = gl.createShader(type);
|
||||||
gl.shaderSource(shader, source);
|
gl.shaderSource(shader, source);
|
||||||
gl.compileShader(shader);
|
gl.compileShader(shader);
|
||||||
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
throw new Error(gl.getShaderInfoLog(shader));
|
|
||||||
}
|
|
||||||
|
|
||||||
return shader;
|
return shader;
|
||||||
};
|
};
|
||||||
|
|
@ -1,7 +1,29 @@
|
||||||
|
const extensions: Map<string, any> = new Map();
|
||||||
|
|
||||||
|
export const tryEnableExtension = (
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
name: string
|
||||||
|
): any | null => {
|
||||||
|
if (extensions.has(name)) {
|
||||||
|
return extensions.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
let extension = null;
|
||||||
|
if (gl.getSupportedExtensions().indexOf(name) != -1) {
|
||||||
|
extension = gl.getExtension(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions.set(name, extension);
|
||||||
|
|
||||||
|
return extension;
|
||||||
|
};
|
||||||
|
|
||||||
export const enableExtension = (gl: WebGL2RenderingContext, name: string): any => {
|
export const enableExtension = (gl: WebGL2RenderingContext, name: string): any => {
|
||||||
if (gl.getSupportedExtensions().indexOf(name) == -1) {
|
const extension = tryEnableExtension(gl, name);
|
||||||
|
|
||||||
|
if (extension === null) {
|
||||||
throw new Error(`Unsupported extension ${name}`);
|
throw new Error(`Unsupported extension ${name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return gl.getExtension(name);
|
return extension;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,14 @@ export class FragmentShaderOnlyProgram extends Program {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
gl: WebGL2RenderingContext,
|
gl: WebGL2RenderingContext,
|
||||||
shaderSources: [string, string],
|
sources: [string, string],
|
||||||
substitutions: { [name: string]: string }
|
substitutions: { [name: string]: string }
|
||||||
) {
|
) {
|
||||||
super(gl, shaderSources, substitutions);
|
super(gl, sources, substitutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async initialize(): Promise<void> {
|
||||||
|
await super.initialize();
|
||||||
this.prepareScreenQuad('vertexPosition');
|
this.prepareScreenQuad('vertexPosition');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { vec2 } from 'gl-matrix';
|
import { vec2 } from 'gl-matrix';
|
||||||
|
|
||||||
export interface IProgram {
|
export interface IProgram {
|
||||||
|
initialize(): Promise<void>;
|
||||||
setDrawingRectangle(bottomLeft: vec2, size: vec2): void;
|
setDrawingRectangle(bottomLeft: vec2, size: vec2): void;
|
||||||
bindAndSetUniforms(values: { [name: string]: any }): void;
|
bindAndSetUniforms(values: { [name: string]: any }): void;
|
||||||
draw(): void;
|
draw(): void;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
import { mat2d, vec2 } from 'gl-matrix';
|
import { mat2d, vec2 } from 'gl-matrix';
|
||||||
import { createShader } from '../helper/create-shader';
|
import { settings } from '../../settings';
|
||||||
|
import { createProgram } from '../compiling/create-program';
|
||||||
import { loadUniform } from '../helper/load-uniform';
|
import { loadUniform } from '../helper/load-uniform';
|
||||||
import { IProgram } from './i-program';
|
import { IProgram } from './i-program';
|
||||||
|
|
||||||
export default abstract class Program implements IProgram {
|
export default abstract class Program implements IProgram {
|
||||||
protected program: WebGLProgram;
|
protected program: WebGLProgram;
|
||||||
|
private programPromise: Promise<WebGLProgram>;
|
||||||
private shaders: Array<WebGLShader> = [];
|
|
||||||
|
|
||||||
private modelTransform = mat2d.identity(mat2d.create());
|
private modelTransform = mat2d.identity(mat2d.create());
|
||||||
|
|
||||||
private readonly ndcToUv: mat2d;
|
private readonly ndcToUv: mat2d;
|
||||||
|
|
||||||
private uniforms: Array<{
|
private uniforms: Array<{
|
||||||
name: Array<string>;
|
name: Array<string>;
|
||||||
location: WebGLUniformLocation;
|
location: WebGLUniformLocation;
|
||||||
|
|
@ -23,13 +20,24 @@ export default abstract class Program implements IProgram {
|
||||||
[vertexShaderSource, fragmentShaderSource]: [string, string],
|
[vertexShaderSource, fragmentShaderSource]: [string, string],
|
||||||
substitutions: { [name: string]: string }
|
substitutions: { [name: string]: string }
|
||||||
) {
|
) {
|
||||||
this.createProgram(vertexShaderSource, fragmentShaderSource, substitutions);
|
substitutions = { ...settings.shaderMacros, ...substitutions };
|
||||||
this.queryUniforms();
|
|
||||||
|
this.programPromise = createProgram(
|
||||||
|
this.gl,
|
||||||
|
vertexShaderSource,
|
||||||
|
fragmentShaderSource,
|
||||||
|
substitutions
|
||||||
|
);
|
||||||
|
|
||||||
this.ndcToUv = mat2d.fromScaling(mat2d.create(), vec2.fromValues(0.5, 0.5));
|
this.ndcToUv = mat2d.fromScaling(mat2d.create(), vec2.fromValues(0.5, 0.5));
|
||||||
mat2d.translate(this.ndcToUv, this.ndcToUv, vec2.fromValues(1, 1));
|
mat2d.translate(this.ndcToUv, this.ndcToUv, vec2.fromValues(1, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async initialize(): Promise<void> {
|
||||||
|
this.program = await this.programPromise;
|
||||||
|
this.queryUniforms();
|
||||||
|
}
|
||||||
|
|
||||||
public bindAndSetUniforms(values: { [name: string]: any }) {
|
public bindAndSetUniforms(values: { [name: string]: any }) {
|
||||||
this.bind();
|
this.bind();
|
||||||
this.setUniforms({ modelTransform: this.modelTransform, ...values });
|
this.setUniforms({ modelTransform: this.modelTransform, ...values });
|
||||||
|
|
@ -56,7 +64,7 @@ export default abstract class Program implements IProgram {
|
||||||
}
|
}
|
||||||
|
|
||||||
public delete() {
|
public delete() {
|
||||||
this.shaders.forEach((s) => this.gl.deleteShader(s));
|
this.gl.getAttachedShaders(this.program).forEach((s) => this.gl.deleteShader(s));
|
||||||
this.gl.deleteProgram(this.program);
|
this.gl.deleteProgram(this.program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,38 +99,4 @@ export default abstract class Program implements IProgram {
|
||||||
return u1;
|
return u1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private createProgram(
|
|
||||||
passthroughVertexShaderSource: string,
|
|
||||||
fragmentShaderSource: string,
|
|
||||||
substitutions: { [name: string]: string }
|
|
||||||
) {
|
|
||||||
this.program = this.gl.createProgram();
|
|
||||||
|
|
||||||
const vertexShader = createShader(
|
|
||||||
this.gl,
|
|
||||||
this.gl.VERTEX_SHADER,
|
|
||||||
passthroughVertexShaderSource,
|
|
||||||
substitutions
|
|
||||||
);
|
|
||||||
this.gl.attachShader(this.program, vertexShader);
|
|
||||||
this.shaders.push(vertexShader);
|
|
||||||
|
|
||||||
const fragmentShader = createShader(
|
|
||||||
this.gl,
|
|
||||||
this.gl.FRAGMENT_SHADER,
|
|
||||||
fragmentShaderSource,
|
|
||||||
substitutions
|
|
||||||
);
|
|
||||||
this.gl.attachShader(this.program, fragmentShader);
|
|
||||||
this.shaders.push(fragmentShader);
|
|
||||||
|
|
||||||
this.gl.linkProgram(this.program);
|
|
||||||
|
|
||||||
const success = this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS);
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
throw new Error(this.gl.getProgramInfoLog(this.program));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,10 @@ export class UniformArrayAutoScalingProgram implements IProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async initialize(): Promise<void> {
|
||||||
|
await Promise.all(this.programs.map((p) => p.program.initialize()));
|
||||||
|
}
|
||||||
|
|
||||||
public bindAndSetUniforms(uniforms: { [name: string]: any }): void {
|
public bindAndSetUniforms(uniforms: { [name: string]: any }): void {
|
||||||
const values = this.descriptors.map((d) =>
|
const values = this.descriptors.map((d) =>
|
||||||
uniforms[d.uniformName] ? uniforms[d.uniformName].length : 0
|
uniforms[d.uniformName] ? uniforms[d.uniformName].length : 0
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import { IDrawable } from './drawables/i-drawable';
|
||||||
import { ILight } from './drawables/lights/i-light';
|
import { ILight } from './drawables/lights/i-light';
|
||||||
|
|
||||||
export interface IRenderer {
|
export interface IRenderer {
|
||||||
|
initialize(): Promise<void>;
|
||||||
|
|
||||||
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
startFrame(deltaTime: DOMHighResTimeStamp): void;
|
||||||
finishFrame(): void;
|
finishFrame(): void;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,10 @@ export class RenderingPass {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async initialize(): Promise<void> {
|
||||||
|
await this.program.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
public addDrawable(drawable: IDrawable) {
|
public addDrawable(drawable: IDrawable) {
|
||||||
this.drawables.push(drawable);
|
this.drawables.push(drawable);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,8 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
private lightingPass: RenderingPass;
|
private lightingPass: RenderingPass;
|
||||||
private autoscaler: FpsAutoscaler;
|
private autoscaler: FpsAutoscaler;
|
||||||
|
|
||||||
|
private initializePromise: Promise<[void, void]> = null;
|
||||||
|
|
||||||
private matrices: {
|
private matrices: {
|
||||||
distanceScreenToWorld?: mat2d;
|
distanceScreenToWorld?: mat2d;
|
||||||
worldToDistanceUV?: mat2d;
|
worldToDistanceUV?: mat2d;
|
||||||
|
|
@ -63,6 +65,11 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
this.lightingFrameBuffer
|
this.lightingFrameBuffer
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.initializePromise = Promise.all([
|
||||||
|
this.distancePass.initialize(),
|
||||||
|
this.lightingPass.initialize(),
|
||||||
|
]);
|
||||||
|
|
||||||
this.autoscaler = new FpsAutoscaler([
|
this.autoscaler = new FpsAutoscaler([
|
||||||
this.lightingFrameBuffer,
|
this.lightingFrameBuffer,
|
||||||
this.distanceFieldFrameBuffer,
|
this.distanceFieldFrameBuffer,
|
||||||
|
|
@ -75,6 +82,10 @@ export class WebGl2Renderer implements IRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async initialize(): Promise<void> {
|
||||||
|
await this.initializePromise;
|
||||||
|
}
|
||||||
|
|
||||||
public drawShape(shape: IDrawable) {
|
public drawShape(shape: IDrawable) {
|
||||||
this.distancePass.addDrawable(shape);
|
this.distancePass.addDrawable(shape);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ export const settings = {
|
||||||
[0.2, 0.1],
|
[0.2, 0.1],
|
||||||
[0.6, 0.1],
|
[0.6, 0.1],
|
||||||
[1, 1],
|
[1, 1],
|
||||||
[1.25, 0.75],
|
/*[1.25, 0.75],
|
||||||
[1.5, 1],
|
[1.5, 1],
|
||||||
[1.75, 1.25],
|
[1.75, 1.25],
|
||||||
//[1.75, 1.75],
|
//[1.75, 1.75],
|
||||||
//[2, 2],
|
//[2, 2],*/
|
||||||
],
|
],
|
||||||
startingTargetIndex: 2,
|
startingTargetIndex: 2,
|
||||||
scalingOptions: {
|
scalingOptions: {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ export class Game implements IGame {
|
||||||
private infoText = new InfoText();
|
private infoText = new InfoText();
|
||||||
private character: Character;
|
private character: Character;
|
||||||
private renderer: IRenderer;
|
private renderer: IRenderer;
|
||||||
|
private initializeRendererPromise: Promise<void>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const canvas: HTMLCanvasElement = document.querySelector('canvas#main');
|
const canvas: HTMLCanvasElement = document.querySelector('canvas#main');
|
||||||
|
|
@ -46,10 +47,13 @@ export class Game implements IGame {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.renderer = new WebGl2Renderer(canvas, overlay);
|
this.renderer = new WebGl2Renderer(canvas, overlay);
|
||||||
|
this.initializeRendererPromise = this.renderer.initialize();
|
||||||
this.initializeScene();
|
this.initializeScene();
|
||||||
this.physics.start();
|
this.physics.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start(): Promise<void> {
|
||||||
|
await this.initializeRendererPromise;
|
||||||
requestAnimationFrame(this.gameLoop.bind(this));
|
requestAnimationFrame(this.gameLoop.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
29
frontend/src/scripts/helper/wait-for.ts
Normal file
29
frontend/src/scripts/helper/wait-for.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
export const waitWhileFalse = (body: () => boolean): Promise<void> => {
|
||||||
|
let resolveOnDone: () => void;
|
||||||
|
let rejectOnDone: (e: Error) => void;
|
||||||
|
|
||||||
|
const onDone = new Promise<void>((resolve, reject) => {
|
||||||
|
resolveOnDone = resolve;
|
||||||
|
rejectOnDone = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
const waiter = () => {
|
||||||
|
let success: boolean;
|
||||||
|
|
||||||
|
try {
|
||||||
|
success = body();
|
||||||
|
} catch (e) {
|
||||||
|
rejectOnDone(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
resolveOnDone();
|
||||||
|
} else {
|
||||||
|
requestAnimationFrame(waiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
waiter();
|
||||||
|
return onDone;
|
||||||
|
};
|
||||||
3
frontend/src/scripts/helper/wait.ts
Normal file
3
frontend/src/scripts/helper/wait.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const wait = (ms: number): Promise<void> => {
|
||||||
|
return new Promise<void>((resolve, _) => setTimeout(resolve, ms));
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue