From 78444d8cc6de0274b0187fb2db7077e79ac9524e Mon Sep 17 00:00:00 2001 From: schmelczerandras Date: Wed, 16 Sep 2020 11:31:26 +0200 Subject: [PATCH] Improve API --- custom.d.ts | 20 --- package.json | 6 +- src/drawables/drawable-descriptor.ts | 6 +- src/drawables/drawable.ts | 19 ++- src/drawables/lights/circle-light.ts | 16 +-- src/drawables/lights/flashlight.ts | 16 +-- src/drawables/shapes/circle.ts | 39 +++--- src/game.ts | 121 ------------------ .../compiling/create-program.ts | 13 +- .../program/fragment-shader-only-program.ts | 4 - .../graphics-library/program/i-program.ts | 1 - .../graphics-library/program/program.ts | 6 +- .../uniform-array-autoscaling-program.ts | 57 +++++++-- src/graphics/i-renderer.ts | 16 --- src/graphics/rendering/renderer.ts | 14 ++ src/graphics/rendering/rendering-pass-name.ts | 4 + src/graphics/rendering/rendering-pass.ts | 8 +- src/graphics/rendering/webgl2-renderer.ts | 76 +++++------ src/graphics/shaders/distance-fs.glsl | 54 ++------ src/graphics/shaders/shading-fs.glsl | 3 +- src/graphics/shaders/shading-vs.glsl | 3 +- src/main.ts | 12 +- tsconfig.json | 2 + webpack.config.js | 2 +- 24 files changed, 193 insertions(+), 325 deletions(-) delete mode 100644 src/game.ts delete mode 100644 src/graphics/i-renderer.ts create mode 100644 src/graphics/rendering/renderer.ts create mode 100644 src/graphics/rendering/rendering-pass-name.ts diff --git a/custom.d.ts b/custom.d.ts index 7c29b64..fa0c7d4 100644 --- a/custom.d.ts +++ b/custom.d.ts @@ -2,23 +2,3 @@ declare module '*.glsl' { const content: string; export default content; } - -/* -declare module '*.png' { - import { ResponsiveImage } from 'src/framework/model/misc'; - const content: ResponsiveImage; - export default content; -} - -declare module '*.jpg' { - import { ResponsiveImage } from 'src/framework/model/misc'; - const content: ResponsiveImage; - export default content; -} - -declare module '*.jpeg' { - import { ResponsiveImage } from 'src/framework/model/misc'; - const content: ResponsiveImage; - export default content; -} -*/ diff --git a/package.json b/package.json index ca21416..f0079d3 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "scripts": { "clean": "shx rm -rf _bundles lib lib-esm", + "start": "shx rm -rf lib/* && webpack --watch", "build": "npm run clean && tsc && tsc -m es6 --outDir lib-esm && webpack" }, "keywords": [], @@ -10,9 +11,6 @@ "browserslist": [ "defaults" ], - "sideEffects": [ - "*.scss" - ], "main": "lib/sdf-2d.js", "types": "lib/src/main.d.ts", "files": [ @@ -28,7 +26,7 @@ "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-unused-imports": "^0.1.3", "gl-matrix": "^3.3.0", - "prettier": "^2.1.1", + "prettier": "^2.1.2", "raw-loader": "^4.0.1", "resolve-url-loader": "^3.1.1", "shx": "^0.3.2", diff --git a/src/drawables/drawable-descriptor.ts b/src/drawables/drawable-descriptor.ts index 234606d..f8e468f 100644 --- a/src/drawables/drawable-descriptor.ts +++ b/src/drawables/drawable-descriptor.ts @@ -2,7 +2,11 @@ import { Drawable } from './drawable'; export interface DrawableDescriptor { uniformName: string; - countMacroName: string; + uniformCountMacroName: string; + sdf?: { + shader: string; + distanceFunctionName: string; + }; shaderCombinationSteps: Array; readonly empty: Drawable; } diff --git a/src/drawables/drawable.ts b/src/drawables/drawable.ts index 3ea548d..f6c7e21 100644 --- a/src/drawables/drawable.ts +++ b/src/drawables/drawable.ts @@ -7,9 +7,20 @@ export abstract class Drawable { } public abstract distance(target: vec2): number; - public abstract serializeToUniforms( + + protected abstract getObjectToSerialize(transform2d: mat2d, transform1d: number): any; + + public serializeToUniforms( uniforms: any, - scale: number, - transform: mat2d - ): void; + transform2d: mat2d, + transform1d: number + ): void { + const { uniformName } = (this.constructor as typeof Drawable).descriptor; + + if (!Object.prototype.hasOwnProperty.call(uniforms, uniformName)) { + uniforms[uniformName] = []; + } + + uniforms[uniformName].push(this.getObjectToSerialize(transform2d, transform1d)); + } } diff --git a/src/drawables/lights/circle-light.ts b/src/drawables/lights/circle-light.ts index 7b1f2ad..1bccf56 100644 --- a/src/drawables/lights/circle-light.ts +++ b/src/drawables/lights/circle-light.ts @@ -7,7 +7,7 @@ export class CircleLight extends Drawable { public static get descriptor(): DrawableDescriptor { return { uniformName: 'circleLights', - countMacroName: 'circleLightCount', + uniformCountMacroName: 'CIRCLE_LIGHT_COUNT', shaderCombinationSteps: settings.shaderCombinations.circleLightSteps, empty: new CircleLight(vec2.fromValues(0, 0), 0, vec3.fromValues(0, 0, 0), 0), }; @@ -26,18 +26,12 @@ export class CircleLight extends Drawable { return 0; } - public serializeToUniforms(uniforms: any, scale: number, transform: mat2d): void { - const { uniformName } = CircleLight.descriptor; - - if (!Object.prototype.hasOwnProperty.call(uniforms, uniformName)) { - uniforms[uniformName] = []; - } - - uniforms[uniformName].push({ - center: vec2.transformMat2d(vec2.create(), this.center, transform), + protected getObjectToSerialize(transform2d: mat2d, transform1d: number): any { + return { + center: vec2.transformMat2d(vec2.create(), this.center, transform2d), lightDrop: this.lightDrop, value: this.value, - }); + }; } public get value(): vec3 { diff --git a/src/drawables/lights/flashlight.ts b/src/drawables/lights/flashlight.ts index fa98b45..5abfdc8 100644 --- a/src/drawables/lights/flashlight.ts +++ b/src/drawables/lights/flashlight.ts @@ -7,7 +7,7 @@ export class Flashlight extends Drawable { public static get descriptor(): DrawableDescriptor { return { uniformName: 'flashlights', - countMacroName: 'flashlightCount', + uniformCountMacroName: 'FLASHLIGHT_COUNT', shaderCombinationSteps: settings.shaderCombinations.flashlightSteps, empty: new Flashlight( vec2.fromValues(0, 0), @@ -33,19 +33,13 @@ export class Flashlight extends Drawable { return 0; } - public serializeToUniforms(uniforms: any, scale: number, transform: mat2d): void { - const listName = Flashlight.descriptor.uniformName; - - if (!Object.prototype.hasOwnProperty.call(uniforms, listName)) { - uniforms[listName] = []; - } - - uniforms[listName].push({ - center: vec2.transformMat2d(vec2.create(), this.center, transform), + protected getObjectToSerialize(transform2d: mat2d, transform1d: number): any { + return { + center: vec2.transformMat2d(vec2.create(), this.center, transform2d), direction: this.direction, lightDrop: this.lightDrop, value: this.value, - }); + }; } public get value(): vec3 { diff --git a/src/drawables/shapes/circle.ts b/src/drawables/shapes/circle.ts index f640650..0f14783 100644 --- a/src/drawables/shapes/circle.ts +++ b/src/drawables/shapes/circle.ts @@ -1,14 +1,29 @@ import { mat2d, vec2 } from 'gl-matrix'; -import { settings } from '../../graphics/settings'; import { Drawable } from '../drawable'; import { DrawableDescriptor } from '../drawable-descriptor'; export class Circle extends Drawable { public static get descriptor(): DrawableDescriptor { return { + sdf: { + shader: ` + uniform struct { + vec2 center; + float radius; + }[CIRCLE_COUNT] circles; + + void circleMinDistance(inout float minDistance, inout float color) { + for (int i = 0; i < CIRCLE_COUNT; i++) { + float dist = distance(circles[i].center, position) - circles[i].radius; + minDistance = min(minDistance, dist); + } + } + `, + distanceFunctionName: 'circleMinDistance', + }, uniformName: 'circles', - countMacroName: 'circleCount', - shaderCombinationSteps: settings.shaderCombinations.circleSteps, + uniformCountMacroName: 'CIRCLE_COUNT', + shaderCombinationSteps: [0, 1, 2, 3, 16, 32], empty: new Circle(vec2.fromValues(0, 0), 0), }; } @@ -18,19 +33,13 @@ export class Circle extends Drawable { } public distance(position: vec2): number { - return 0; + return vec2.dist(this.center, position) - this.radius; } - public serializeToUniforms(uniforms: any, scale: number, transform: mat2d): void { - const { uniformName } = Circle.descriptor; - - if (!Object.prototype.hasOwnProperty.call(uniforms, uniformName)) { - uniforms[uniformName] = []; - } - - uniforms[uniformName].push({ - center: vec2.transformMat2d(vec2.create(), this.center, transform), - radius: this.radius * scale, - }); + protected getObjectToSerialize(transform2d: mat2d, transform1d: number): any { + return { + center: vec2.transformMat2d(vec2.create(), this.center, transform2d), + radius: this.radius * transform1d, + }; } } diff --git a/src/game.ts b/src/game.ts deleted file mode 100644 index 656b40d..0000000 --- a/src/game.ts +++ /dev/null @@ -1,121 +0,0 @@ -/* -export class Game implements IGame { - public readonly objects = new Objects(); - public readonly physics = new Physics(); - public readonly camera = new Camera(); - private previousTime?: DOMHighResTimeStamp = null; - private previousFpsValues: Array = []; - private infoText = new InfoText(); - private character: Character; - private renderer: IRenderer; - private initializeRendererPromise: Promise; - - constructor() { - const canvas: HTMLCanvasElement = document.querySelector('canvas#main'); - const overlay: HTMLElement = document.querySelector('#overlay'); - - document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this)); - - new CommandBroadcaster( - [ - new KeyboardListener(document.body), - new MouseListener(canvas), - new TouchListener(canvas), - ], - [this.objects] - ); - - this.renderer = new WebGl2Renderer(canvas, overlay); - this.initializeRendererPromise = this.renderer.initialize(); - this.initializeScene(); - this.physics.start(); - } - - public async start(): Promise { - await this.initializeRendererPromise; - requestAnimationFrame(this.gameLoop.bind(this)); - } - - public addObject(o: GameObject) { - this.objects.addObject(o); - } - - public get viewArea(): BoundingBoxBase { - return this.camera.viewArea; - } - - public findIntersecting(box: BoundingBoxBase): Array { - return this.physics.findIntersecting(box); - } - - private initializeScene() { - this.objects.addObject(this.infoText); - - const start = createDungeon(this.objects, this.physics); - createDungeon(this.objects, this.physics); - - this.character = new Character(this); - // this.physics.addDynamicBoundingBox(this.character.boundingBox); - this.addObject(this.character); - this.addObject(this.camera); - let pos: any = localStorage.getItem('character-position'); - pos = pos ? JSON.parse(pos) : start.from; - this.character.sendCommand(new TeleportToCommand(pos)); - } - - private handleVisibilityChange() { - if (!document.hidden) { - this.previousTime = null; - } - } - - @timeIt() - private gameLoop(time: DOMHighResTimeStamp) { - if (this.previousTime === null) { - this.previousTime = time; - } - - const deltaTime = time - this.previousTime; - this.previousTime = time; - this.calculateFps(deltaTime); - - this.objects.sendCommand(new StepCommand(deltaTime)); - this.camera.sendCommand(new MoveToCommand(this.character.position)); - - this.renderer.startFrame(deltaTime); - - this.camera.sendCommand(new BeforeRenderCommand(this.renderer)); - - const shouldBeDrawn = this.physics - .findIntersecting(this.camera.viewArea) - .map((b) => b.shape?.gameObject); - - for (const object of shouldBeDrawn) { - object?.sendCommand(new BeforeRenderCommand(this.renderer)); - object?.sendCommand(new RenderCommand(this.renderer)); - } - - this.character.sendCommand(new RenderCommand(this.renderer)); - this.infoText.sendCommand(new RenderCommand(this.renderer)); - this.renderer.finishFrame(); - - localStorage.setItem('character-position', JSON.stringify(this.character.position)); - window.requestAnimationFrame(this.gameLoop.bind(this)); - } - - private calculateFps(deltaTime: number) { - this.previousFpsValues.push(1000 / deltaTime); - if (this.previousFpsValues.length > 30) { - this.previousFpsValues.sort(); - const min = this.previousFpsValues[0]; - const median = this.previousFpsValues[ - Math.floor(this.previousFpsValues.length / 2) - ]; - - InfoText.modifyRecord('FPS', { min, median }); - - this.previousFpsValues = []; - } - } -} -*/ diff --git a/src/graphics/graphics-library/compiling/create-program.ts b/src/graphics/graphics-library/compiling/create-program.ts index e1f94ea..0416fb5 100644 --- a/src/graphics/graphics-library/compiling/create-program.ts +++ b/src/graphics/graphics-library/compiling/create-program.ts @@ -1,21 +1,19 @@ -import { waitWhileFalse } from '../../../helper/wait-while-false'; -import { tryEnableExtension } from '../helper/enable-extension'; import { checkProgram } from './check-program'; import { createShader } from './create-shader'; -export const createProgram = async ( +export const createProgram = ( gl: WebGL2RenderingContext, vertexShaderSource: string, fragmentShaderSource: string, substitutions: { [name: string]: string } -): Promise => { +): WebGLProgram => { const program = gl.createProgram(); if (!program) { throw new Error('Could not create program'); } - const extension = tryEnableExtension(gl, 'KHR_parallel_shader_compile'); + // const extension = tryEnableExtension(gl, 'KHR_parallel_shader_compile'); const vertexShader = createShader( gl, @@ -31,16 +29,17 @@ export const createProgram = async ( fragmentShaderSource, substitutions ); + gl.attachShader(program, fragmentShader); gl.linkProgram(program); - if (extension) { + /*if (extension) { const checkCompileStatus = () => gl.getProgramParameter(program, extension.COMPLETION_STATUS_KHR); await waitWhileFalse(checkCompileStatus); - } + }*/ checkProgram(gl, program); diff --git a/src/graphics/graphics-library/program/fragment-shader-only-program.ts b/src/graphics/graphics-library/program/fragment-shader-only-program.ts index 4c68de4..f92dbab 100644 --- a/src/graphics/graphics-library/program/fragment-shader-only-program.ts +++ b/src/graphics/graphics-library/program/fragment-shader-only-program.ts @@ -9,10 +9,6 @@ export class FragmentShaderOnlyProgram extends Program { substitutions: { [name: string]: string } ) { super(gl, sources, substitutions); - } - - public async initialize(): Promise { - await super.initialize(); this.prepareScreenQuad('vertexPosition'); } diff --git a/src/graphics/graphics-library/program/i-program.ts b/src/graphics/graphics-library/program/i-program.ts index 901c327..633ea3c 100644 --- a/src/graphics/graphics-library/program/i-program.ts +++ b/src/graphics/graphics-library/program/i-program.ts @@ -1,7 +1,6 @@ import { vec2 } from 'gl-matrix'; export interface IProgram { - initialize(): Promise; setDrawingRectangleUV(bottomLeft: vec2, size: vec2): void; bindAndSetUniforms(values: { [name: string]: any }): void; draw(): void; diff --git a/src/graphics/graphics-library/program/program.ts b/src/graphics/graphics-library/program/program.ts index 05c77e2..6e7e1b4 100644 --- a/src/graphics/graphics-library/program/program.ts +++ b/src/graphics/graphics-library/program/program.ts @@ -7,7 +7,6 @@ import { IProgram } from './i-program'; export default abstract class Program implements IProgram { protected program?: WebGLProgram; - private programPromise: Promise; private modelTransform = mat2d.identity(mat2d.create()); private readonly ndcToUv = mat2d.fromValues(0.5, 0, 0, 0.5, 0.5, 0.5); private uniforms: Array<{ @@ -23,16 +22,13 @@ export default abstract class Program implements IProgram { ) { substitutions = { ...settings.shaderMacros, ...substitutions }; - this.programPromise = createProgram( + this.program = createProgram( this.gl, vertexShaderSource, fragmentShaderSource, substitutions ); - } - public async initialize(): Promise { - this.program = await this.programPromise; this.queryUniforms(); } diff --git a/src/graphics/graphics-library/program/uniform-array-autoscaling-program.ts b/src/graphics/graphics-library/program/uniform-array-autoscaling-program.ts index 3ee2862..ac22c4e 100644 --- a/src/graphics/graphics-library/program/uniform-array-autoscaling-program.ts +++ b/src/graphics/graphics-library/program/uniform-array-autoscaling-program.ts @@ -20,18 +20,13 @@ export class UniformArrayAutoScalingProgram implements IProgram { shaderSources: [string, string], private descriptors: Array ) { - const names = descriptors.map((o) => o.countMacroName); for (const combination of getCombinations( descriptors.map((o) => o.shaderCombinationSteps) )) { - this.createProgram(names, combination, shaderSources); + this.createProgram(descriptors, combination, shaderSources); } } - public async initialize(): Promise { - await Promise.all(this.programs.map((p) => p.program.initialize())); - } - public bindAndSetUniforms(uniforms: { [name: string]: any }): void { const values = this.descriptors.map((d) => uniforms[d.uniformName] ? uniforms[d.uniformName].length : 0 @@ -45,7 +40,7 @@ export class UniformArrayAutoScalingProgram implements IProgram { this.descriptors.map((d, i) => { const difference = closest.values[i] - values[i]; for (let i = 0; i < difference; i++) { - d.empty.serializeToUniforms(uniforms, 0, mat2d.create()); + d.empty.serializeToUniforms(uniforms, mat2d.create(), 0); } }); } @@ -75,12 +70,15 @@ export class UniformArrayAutoScalingProgram implements IProgram { } private createProgram( - names: Array, + descriptors: Array, combination: Array, shaderSources: [string, string] ): FragmentShaderOnlyProgram { - const substitutions = {}; - names.forEach((v, i) => (substitutions[v] = combination[i].toString())); + const substitutions = { + macroDefinitions: this.getMacroDefinitions(combination, descriptors), + declarations: this.getDeclarations(combination, descriptors), + functionCalls: this.getFunctionCalls(combination, descriptors), + }; const program = new FragmentShaderOnlyProgram(this.gl, shaderSources, substitutions); @@ -91,4 +89,43 @@ export class UniformArrayAutoScalingProgram implements IProgram { return program; } + + private getMacroDefinitions( + combination: Array, + descriptors: Array + ): string { + return combination + .map((v, i) => `#define ${descriptors[i].uniformCountMacroName} ${v}`) + .join('\n'); + } + + private getDeclarations( + combination: Array, + descriptors: Array + ): string { + return combination + .map((v, i) => { + if (v == 0 || descriptors[i].sdf === undefined) { + return ''; + } + + return descriptors[i].sdf!.shader; + }) + .join('\n'); + } + + private getFunctionCalls( + combination: Array, + descriptors: Array + ): string { + return combination + .map((v, i) => { + if (v == 0 || descriptors[i].sdf === undefined) { + return ''; + } + + return `${descriptors[i].sdf!.distanceFunctionName}(minDistance, color);`; + }) + .join('\n'); + } } diff --git a/src/graphics/i-renderer.ts b/src/graphics/i-renderer.ts deleted file mode 100644 index 267c5e3..0000000 --- a/src/graphics/i-renderer.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { vec2 } from 'gl-matrix'; -import { Drawable } from '../drawables/drawable'; - -export interface IRenderer { - initialize(): Promise; - - startFrame(deltaTime: DOMHighResTimeStamp): void; - finishFrame(): void; - - drawShape(drawable: Drawable): void; - drawLight(light: Drawable): void; - - readonly canvasSize: vec2; - setViewArea(topLeft: vec2, size: vec2): void; - setCursorPosition(position: vec2): void; -} diff --git a/src/graphics/rendering/renderer.ts b/src/graphics/rendering/renderer.ts new file mode 100644 index 0000000..d340fa2 --- /dev/null +++ b/src/graphics/rendering/renderer.ts @@ -0,0 +1,14 @@ +import { vec2 } from 'gl-matrix'; +import { Drawable } from '../../drawables/drawable'; + +export interface Renderer { + setViewArea(topLeft: vec2, size: vec2): void; + + addDrawable(drawable: Drawable): void; + renderDrawables(): void; + + autoscaleQuality(deltaTime: DOMHighResTimeStamp): void; + + readonly canvasSize: vec2; + setCursorPosition(position: vec2): void; +} diff --git a/src/graphics/rendering/rendering-pass-name.ts b/src/graphics/rendering/rendering-pass-name.ts new file mode 100644 index 0000000..5d593c6 --- /dev/null +++ b/src/graphics/rendering/rendering-pass-name.ts @@ -0,0 +1,4 @@ +export enum RenderingPassName { + distance = 'distance', + pixel = 'pixel', +} diff --git a/src/graphics/rendering/rendering-pass.ts b/src/graphics/rendering/rendering-pass.ts index dda5c6d..9e1d708 100644 --- a/src/graphics/rendering/rendering-pass.ts +++ b/src/graphics/rendering/rendering-pass.ts @@ -22,10 +22,6 @@ export class RenderingPass { ); } - public async initialize(): Promise { - await this.program.initialize(); - } - public addDrawable(drawable: Drawable) { this.drawables.push(drawable); } @@ -71,8 +67,8 @@ export class RenderingPass { primitivesNearTile.forEach((p) => p.serializeToUniforms( uniforms, - uniforms.scaleWorldLengthToNDC, - uniforms.transformWorldToNDC + uniforms.transformWorldToNDC, + uniforms.scaleWorldLengthToNDC ) ); diff --git a/src/graphics/rendering/webgl2-renderer.ts b/src/graphics/rendering/webgl2-renderer.ts index b0629af..46513fc 100644 --- a/src/graphics/rendering/webgl2-renderer.ts +++ b/src/graphics/rendering/webgl2-renderer.ts @@ -1,57 +1,49 @@ import { vec2 } from 'gl-matrix'; import { Drawable } from '../../drawables/drawable'; -import { CircleLight } from '../../drawables/lights/circle-light'; -import { Flashlight } from '../../drawables/lights/flashlight'; -import { Circle } from '../../drawables/shapes/circle'; +import { DrawableDescriptor } from '../../drawables/drawable-descriptor'; import { DefaultFrameBuffer } from '../graphics-library/frame-buffer/default-frame-buffer'; import { IntermediateFrameBuffer } from '../graphics-library/frame-buffer/intermediate-frame-buffer'; import { getWebGl2Context } from '../graphics-library/helper/get-webgl2-context'; import { WebGlStopwatch } from '../graphics-library/helper/stopwatch'; -import { IRenderer } from '../i-renderer'; import distanceFragmentShader from '../shaders/distance-fs.glsl'; import distanceVertexShader from '../shaders/distance-vs.glsl'; import lightsFragmentShader from '../shaders/shading-fs.glsl'; import lightsVertexShader from '../shaders/shading-vs.glsl'; import { FpsAutoscaler } from './fps-autoscaler'; +import { Renderer } from './renderer'; import { RenderingPass } from './rendering-pass'; +import { RenderingPassName } from './rendering-pass-name'; import { UniformsProvider } from './uniforms-provider'; -export class WebGl2Renderer implements IRenderer { +export class WebGl2Renderer implements Renderer { private gl: WebGL2RenderingContext; private stopwatch?: WebGlStopwatch; private uniformsProvider: UniformsProvider; private distanceFieldFrameBuffer: IntermediateFrameBuffer; private lightingFrameBuffer: DefaultFrameBuffer; - private distancePass: RenderingPass; - private lightingPass: RenderingPass; + private passes: { [key in keyof typeof RenderingPassName]: RenderingPass }; private autoscaler: FpsAutoscaler; - private initializePromise: Promise<[void, void]>; - - constructor(private canvas: HTMLCanvasElement) { + constructor(private canvas: HTMLCanvasElement, descriptors: Array) { this.gl = getWebGl2Context(canvas); this.distanceFieldFrameBuffer = new IntermediateFrameBuffer(this.gl); this.lightingFrameBuffer = new DefaultFrameBuffer(this.gl); - this.distancePass = new RenderingPass( - this.gl, - [distanceVertexShader, distanceFragmentShader], - [Circle.descriptor], - this.distanceFieldFrameBuffer - ); - - this.lightingPass = new RenderingPass( - this.gl, - [lightsVertexShader, lightsFragmentShader], - [CircleLight.descriptor, Flashlight.descriptor], - this.lightingFrameBuffer - ); - - this.initializePromise = Promise.all([ - this.distancePass.initialize(), - this.lightingPass.initialize(), - ]); + this.passes = { + [RenderingPassName.distance]: new RenderingPass( + this.gl, + [distanceVertexShader, distanceFragmentShader], + descriptors.filter(WebGl2Renderer.hasSdf), + this.distanceFieldFrameBuffer + ), + [RenderingPassName.pixel]: new RenderingPass( + this.gl, + [lightsVertexShader, lightsFragmentShader], + descriptors.filter((d) => !WebGl2Renderer.hasSdf(d)), + this.lightingFrameBuffer + ), + }; this.uniformsProvider = new UniformsProvider(this.gl); @@ -70,35 +62,37 @@ export class WebGl2Renderer implements IRenderer { } } - public async initialize(): Promise { - await this.initializePromise; + private static hasSdf(descriptor: DrawableDescriptor) { + return Object.prototype.hasOwnProperty.call(descriptor, 'sdf'); } - public drawShape(shape: Drawable) { - this.distancePass.addDrawable(shape); + public addDrawable(drawable: Drawable): void { + if (WebGl2Renderer.hasSdf((drawable.constructor as typeof Drawable).descriptor)) { + this.passes[RenderingPassName.distance].addDrawable(drawable); + } else { + this.passes[RenderingPassName.pixel].addDrawable(drawable); + } } - public drawLight(light: Drawable) { - this.lightingPass.addDrawable(light); - } - - public startFrame(deltaTime: DOMHighResTimeStamp) { + public autoscaleQuality(deltaTime: DOMHighResTimeStamp) { this.autoscaler.autoscale(deltaTime); + } + public renderDrawables() { this.stopwatch?.start(); this.distanceFieldFrameBuffer.setSize(); this.lightingFrameBuffer.setSize(); - } - public finishFrame() { const common = { distanceNdcPixelSize: 2 / Math.max(...this.distanceFieldFrameBuffer.getSize()), shadingNdcPixelSize: 2 / Math.max(...this.distanceFieldFrameBuffer.getSize()), }; - this.distancePass.render(this.uniformsProvider.getUniforms(common)); - this.lightingPass.render( + this.passes[RenderingPassName.distance].render( + this.uniformsProvider.getUniforms(common) + ); + this.passes[RenderingPassName.pixel].render( this.uniformsProvider.getUniforms(common), this.distanceFieldFrameBuffer.colorTexture ); diff --git a/src/graphics/shaders/distance-fs.glsl b/src/graphics/shaders/distance-fs.glsl index 6c21e36..da85409 100644 --- a/src/graphics/shaders/distance-fs.glsl +++ b/src/graphics/shaders/distance-fs.glsl @@ -2,25 +2,16 @@ precision lowp float; -// #define LINE_COUNT {lineCount} -// #define BLOB_COUNT {blobCount} -#define CIRCLE_COUNT {circleCount} - #define SURFACE_OFFSET 0.001 uniform float maxMinDistance; uniform float distanceNdcPixelSize; - in vec2 position; -float smoothMin(float a, float b) -{ - const float k = 2.0; - - a = pow(a, k); - b = pow(b, k); - return pow((a * b) / (a + b), 1.0 / k); -} +{macroDefinitions} + +{declarations} + /* #if LINE_COUNT > 0 uniform struct { @@ -54,22 +45,6 @@ float smoothMin(float a, float b) minDistance = -myMinDistance; } #endif -*/ -#if CIRCLE_COUNT > 0 - uniform struct { - vec2 center; - float radius; - }[CIRCLE_COUNT] circles; - - void circleMinDistance(inout float minDistance, inout float color) { - for (int i = 0; i < CIRCLE_COUNT; i++) { - float dist = distance(circles[i].center, position) - circles[i].radius; - minDistance = min(minDistance, dist); - } - } -#endif - -/* #if BLOB_COUNT > 0 uniform struct { vec2 headCenter; @@ -80,6 +55,15 @@ float smoothMin(float a, float b) float k; }[BLOB_COUNT] blobs; + float smoothMin(float a, float b) + { + const float k = 2.0; + + a = pow(a, k); + b = pow(b, k); + return pow((a * b) / (a + b), 1.0 / k); + } + float circleMinDistance(vec2 circleCenter, float radius) { return distance(position, circleCenter) - radius; } @@ -112,17 +96,7 @@ void main() { float minDistance = 10.0; //-maxMinDistance; float color = 1.0; - /*#if LINE_COUNT > 0 - lineMinDistance(minDistance, color); - #endif - - #if BLOB_COUNT > 0 - blobMinDistance(minDistance, color); - #endif*/ - - #if CIRCLE_COUNT > 0 - circleMinDistance(minDistance, color); - #endif + {functionCalls} // minDistance / 2.0: NDC to UV scale fragmentColor = vec2(minDistance / 2.0, color); diff --git a/src/graphics/shaders/shading-fs.glsl b/src/graphics/shaders/shading-fs.glsl index 8a67946..2acf257 100644 --- a/src/graphics/shaders/shading-fs.glsl +++ b/src/graphics/shaders/shading-fs.glsl @@ -7,8 +7,7 @@ precision lowp float; #define AMBIENT_LIGHT vec3(0.25, 0.15, 0.25) #define SHADOW_HARDNESS 150.0 -#define CIRCLE_LIGHT_COUNT {circleLightCount} -#define FLASHLIGHT_COUNT {flashlightCount} +{macroDefinitions} uniform bool softShadowsEnabled; uniform vec2 squareToAspectRatioTimes2; diff --git a/src/graphics/shaders/shading-vs.glsl b/src/graphics/shaders/shading-vs.glsl index 5d13acf..7a3ca0a 100644 --- a/src/graphics/shaders/shading-vs.glsl +++ b/src/graphics/shaders/shading-vs.glsl @@ -2,8 +2,7 @@ precision lowp float; -#define CIRCLE_LIGHT_COUNT {circleLightCount} -#define FLASHLIGHT_COUNT {flashlightCount} +{macroDefinitions} uniform mat3 modelTransform; in vec4 vertexPosition; diff --git a/src/main.ts b/src/main.ts index 3a4760e..acef3c4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,14 +1,20 @@ import { glMatrix } from 'gl-matrix'; -import { IRenderer } from './graphics/i-renderer'; +import { DrawableDescriptor } from './drawables/drawable-descriptor'; +import { Renderer } from './graphics/rendering/renderer'; import { WebGl2Renderer } from './graphics/rendering/webgl2-renderer'; import { applyArrayPlugins } from './helper/array'; export { Drawable } from './drawables/drawable'; export { CircleLight } from './drawables/lights/circle-light'; +export { Flashlight } from './drawables/lights/flashlight'; export { Circle } from './drawables/shapes/circle'; +export { RenderingPassName } from './graphics/rendering/rendering-pass-name'; -export const compile = (canvas: HTMLCanvasElement): IRenderer => { +export const compile = ( + canvas: HTMLCanvasElement, + descriptors: Array +): Renderer => { glMatrix.setMatrixArrayType(Array); applyArrayPlugins(); - return new WebGl2Renderer(canvas); + return new WebGl2Renderer(canvas, descriptors); }; diff --git a/tsconfig.json b/tsconfig.json index a0502b6..4db5f72 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,8 @@ "moduleResolution": "Node", "module": "commonjs", "declaration": true, + "composite": true, + "declarationMap": true, "lib": ["es6", "dom", "es2017"] } } diff --git a/webpack.config.js b/webpack.config.js index dc57bd7..1c578fb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -65,7 +65,7 @@ module.exports = { { test: /\.ts$/, use: { - loader: 'awesome-typescript-loader', + loader: 'ts-loader', }, exclude: /node_modules/, },