From 2588006407b58100219d4c3ec7079bb7596e7316 Mon Sep 17 00:00:00 2001 From: Erik Onarheim Date: Mon, 23 Dec 2024 08:54:51 -0600 Subject: [PATCH] fix: PostProcessor drawing when no actors present --- CHANGELOG.md | 3 +- sandbox/tests/clip-canvas/index.html | 12 +++ sandbox/tests/clip-canvas/index.ts | 55 ++++++++++ sandbox/tests/occluder/index.html | 12 +++ sandbox/tests/occluder/index.ts | 98 ++++++++++++++++++ sandbox/tests/occluder/test.png | Bin 0 -> 511 bytes .../Context/ExcaliburGraphicsContextWebGL.ts | 3 +- .../screen-pass-painter.ts | 8 +- .../Graphics/PostProcessor/PostProcessor.ts | 6 ++ 9 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 sandbox/tests/clip-canvas/index.html create mode 100644 sandbox/tests/clip-canvas/index.ts create mode 100644 sandbox/tests/occluder/index.html create mode 100644 sandbox/tests/occluder/index.ts create mode 100644 sandbox/tests/occluder/test.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f47356c2..f353f779b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +15,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added -- +- New PostProcessor.onDraw() hook to handle uploading textures ### Fixed - Fixed CollidePolygonPolygon crash with some defense against invalid separation +- Fixed issue with PostProcessor where it would not run correctly if no actors present ### Updates diff --git a/sandbox/tests/clip-canvas/index.html b/sandbox/tests/clip-canvas/index.html new file mode 100644 index 000000000..0d4633ad6 --- /dev/null +++ b/sandbox/tests/clip-canvas/index.html @@ -0,0 +1,12 @@ + + + + + + Clip Canvas + + + + + + diff --git a/sandbox/tests/clip-canvas/index.ts b/sandbox/tests/clip-canvas/index.ts new file mode 100644 index 000000000..e2e14f382 --- /dev/null +++ b/sandbox/tests/clip-canvas/index.ts @@ -0,0 +1,55 @@ +var engine = new ex.Engine({ + width: 800, + height: 800, + displayMode: ex.DisplayMode.FitScreen +}); + +var mouse = ex.vec(400, 400); + +var canvas = new ex.Canvas({ + width: 800, + height: 800, + draw: (ctx) => { + // Clipping path + ctx.beginPath(); + ctx.rect(0, 0, 800, 800); // Outer rectangle + ctx.arc(mouse.x, mouse.y, 60, 0, Math.PI * 2, true); // Hole anticlockwise + ctx.clip(); + + // Draw background + ctx.fillStyle = ex.Color.Black.toString(); + ctx.fillRect(0, 0, 800, 800); + } +}); + +engine.input.pointers.on('move', (evt) => { + mouse.x = evt.screenPos.x; + mouse.y = evt.screenPos.y; + canvas.flagDirty(); +}); + +var mask = new ex.Actor({ + anchor: ex.vec(0, 0), + pos: ex.vec(0, 0), + coordPlane: ex.CoordPlane.Screen, + z: 1 // on top +}); +mask.graphics.use(canvas); +engine.add(mask); + +var normal = new ex.Actor({ + pos: ex.vec(400, 400), + color: ex.Color.Red, + width: 100, + height: 100 +}); + +normal.actions.repeatForever((ctx) => { + ctx.moveBy({ offset: ex.vec(100, 0), duration: 1000 }); + ctx.moveBy({ offset: ex.vec(0, 100), duration: 1000 }); + ctx.moveBy({ offset: ex.vec(-100, 0), duration: 1000 }); + ctx.moveBy({ offset: ex.vec(0, -100), duration: 1000 }); +}); +engine.add(normal); + +engine.start(); diff --git a/sandbox/tests/occluder/index.html b/sandbox/tests/occluder/index.html new file mode 100644 index 000000000..b43d83cce --- /dev/null +++ b/sandbox/tests/occluder/index.html @@ -0,0 +1,12 @@ + + + + + + Occluder + + + + + + diff --git a/sandbox/tests/occluder/index.ts b/sandbox/tests/occluder/index.ts new file mode 100644 index 000000000..d436e9a9a --- /dev/null +++ b/sandbox/tests/occluder/index.ts @@ -0,0 +1,98 @@ +var occluder = new ex.ImageSource('test.png'); + +var shader = `#version 300 es +precision mediump float; + +uniform sampler2D u_image; // Default texture slot +uniform sampler2D u_myTexture; // Slot 2 texture + +uniform vec2 u_resolution; +uniform vec2 u_texturePosition; // Position of the second texture +uniform vec2 u_textureSize; // Size of the second texture + +in vec2 v_uv; +out vec4 fragColor; + +void main() { + vec2 pixelCoord = v_uv * u_resolution; + vec2 adjustedUV = (pixelCoord - u_texturePosition) / u_textureSize; + + vec4 defaultColor = texture(u_image, v_uv); + vec4 secondTexture = texture(u_myTexture, adjustedUV); + vec4 endColor = mix(defaultColor, secondTexture, .5); + fragColor = vec4(endColor.rgb * endColor.a, endColor.a); + + //fragColor = vec4(secondTexture.rgb * secondTexture.a, secondTexture.a); + +} +`; + +class LightingPostProcessor implements ex.PostProcessor { + private _shader: ex.ScreenShader | undefined; + gctx: ex.ExcaliburGraphicsContextWebGL; + texture: WebGLTexture; + + constructor(public graphicsContext: ex.ExcaliburGraphicsContextWebGL) { + this.gctx = graphicsContext; + this.texture = this.graphicsContext.textureLoader.load( + occluder.image, + { wrapping: { x: ex.ImageWrapping.Repeat, y: ex.ImageWrapping.Repeat } }, + true + ) as WebGLTexture; + console.log(occluder); + + console.log(this.texture); + } + + initialize(gl: WebGL2RenderingContext): void { + this._shader = new ex.ScreenShader(gl, shader); + } + getLayout(): ex.VertexLayout { + return this._shader.getLayout(); + } + getShader(): ex.Shader { + return this._shader.getShader(); + } + onUpdate(elapsed: number): void { + let myShader = this._shader?.getShader(); + + if (myShader) { + myShader.trySetUniformInt('u_myTexture', 1); + myShader.trySetUniformFloatVector('u_texturePosition', new ex.Vector(100.0, 100.0)); + myShader.trySetUniformFloatVector('u_textureSize', new ex.Vector(64.0, 64.0)); + } + } + onDraw(): void { + let myShader = this._shader?.getShader(); + if (myShader) { + this.gctx.textureLoader.load(occluder.image); + myShader.setTexture(1, this.texture as WebGLTexture); + myShader.trySetUniformInt('u_myTexture', 1); + } + } +} + +var game = new ex.Engine({ + width: 600, + height: 400, + displayMode: ex.DisplayMode.FitScreenAndFill +}); + +var actor = new ex.Actor({ + width: 100, + height: 100, + color: ex.Color.Red +}); +actor.angularVelocity = 0.2; +actor.actions.repeatForever((ctx) => { + ctx.moveTo(ex.vec(1000, 0), 200); + ctx.moveTo(ex.vec(0, 400), 200); + ctx.moveTo(ex.vec(1000, 400), 200); + ctx.moveTo(ex.vec(0, 0), 200); +}); +game.currentScene.add(actor); + +var ctx = game.graphicsContext as ex.ExcaliburGraphicsContextWebGL; +game.start(new ex.Loader([occluder])).then(() => { + ctx.addPostProcessor(new LightingPostProcessor(ctx)); +}); diff --git a/sandbox/tests/occluder/test.png b/sandbox/tests/occluder/test.png new file mode 100644 index 0000000000000000000000000000000000000000..9316a4135e65c5302ab9e5a4da44343084431582 GIT binary patch literal 511 zcmeAS@N?(olHy`uVBq!ia0y~yV4MJCr*NFSZ?7KYJYc}X z;;8-i`m$Eki@SkN|@aBcp=gC`HvE bsASx(!g^HmuZjdP@)$f_{an^LB{Ts5h!RS~ literal 0 HcmV?d00001 diff --git a/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts b/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts index 36c00d8e1..cbfd1d266 100644 --- a/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts +++ b/src/engine/Graphics/Context/ExcaliburGraphicsContextWebGL.ts @@ -776,8 +776,7 @@ export class ExcaliburGraphicsContextWebGL implements ExcaliburGraphicsContext { // post process step if (this._postprocessors.length > 0) { - const source = currentTarget.toRenderSource(); - source.use(); + currentTarget.toRenderSource().use(); } // flip flop render targets for post processing diff --git a/src/engine/Graphics/Context/screen-pass-painter/screen-pass-painter.ts b/src/engine/Graphics/Context/screen-pass-painter/screen-pass-painter.ts index 9b85cf54f..db25bf63e 100644 --- a/src/engine/Graphics/Context/screen-pass-painter/screen-pass-painter.ts +++ b/src/engine/Graphics/Context/screen-pass-painter/screen-pass-painter.ts @@ -46,8 +46,14 @@ export class ScreenPassPainter { renderWithPostProcessor(postprocessor: PostProcessor): void { const gl = this._gl; - postprocessor.getShader().use(); + const shader = postprocessor.getShader(); + shader.use(); postprocessor.getLayout().use(); + gl.activeTexture(gl.TEXTURE0); + shader.trySetUniformInt('u_image', 0); + if (postprocessor.onDraw) { + postprocessor.onDraw(); + } gl.drawArrays(gl.TRIANGLES, 0, 6); } diff --git a/src/engine/Graphics/PostProcessor/PostProcessor.ts b/src/engine/Graphics/PostProcessor/PostProcessor.ts index a9edda217..de48056da 100644 --- a/src/engine/Graphics/PostProcessor/PostProcessor.ts +++ b/src/engine/Graphics/PostProcessor/PostProcessor.ts @@ -28,4 +28,10 @@ export interface PostProcessor { * @param elapsed */ onUpdate?(elapsed: number): void; + + /** + * Use the onDraw hook to upload any textures or command that need to run right before draw + * @param elapsed + */ + onDraw?(): void; }