diff --git a/packages/melonjs/CHANGELOG.md b/packages/melonjs/CHANGELOG.md index 09f9e4a35..e719541db 100644 --- a/packages/melonjs/CHANGELOG.md +++ b/packages/melonjs/CHANGELOG.md @@ -26,6 +26,10 @@ - TypeScript: convert application, input, and UI modules to TypeScript — application, header, resize, input, pointer, pointerevent, gamepad, uibaseelement, uispriteelement, uitextbutton ### Fixed +- WebGLRenderer: `setBatcher()` now rebinds the shared vertex buffer when switching batchers, allowing custom batchers with their own GL buffers to integrate without manual cleanup +- WebGLRenderer: `setBlendMode()` now accepts a `premultipliedAlpha` parameter (default `true`) for correct blending with non-premultiplied textures; removed internal `gl`/`context` parameter from both WebGL and Canvas renderers +- Renderer: add base `setBlendMode()` and `GPURenderer` property to fix TypeScript `as any` casts in `header.ts` and `resize.ts` +- Plugin: `plugin.register()` now uses `pluginClass.name` for name derivation, fixing class name extraction when bundlers strip class names - TMXTileset: fix animation key using first frame tileid instead of the tile's own id - CanvasRenderer: replace bezier ellipse approximation with native `context.ellipse()` (with polyfill for older browsers) - Plugin: fix `plugin.get()` throwing `TypeError` when searching by name with no match (instanceof on string) diff --git a/packages/melonjs/src/application/header.ts b/packages/melonjs/src/application/header.ts index de971c24d..c5dfe598f 100644 --- a/packages/melonjs/src/application/header.ts +++ b/packages/melonjs/src/application/header.ts @@ -8,12 +8,11 @@ import type Application from "./application.ts"; export function consoleHeader(app: Application): void { const renderType = app.renderer.type; const gpu_renderer = - typeof (app.renderer as any).GPURenderer === "string" - ? ` (${(app.renderer as any).GPURenderer})` + typeof app.renderer.GPURenderer === "string" + ? ` (${app.renderer.GPURenderer})` : ""; const depthTesting = - renderType.includes("WebGL") && - (app.renderer as any).depthTest === "z-buffer" + renderType.includes("WebGL") && app.renderer.depthTest === "z-buffer" ? "Depth Test | " : ""; const audioType = device.hasWebAudio ? "Web Audio" : "HTML5 Audio"; diff --git a/packages/melonjs/src/application/resize.ts b/packages/melonjs/src/application/resize.ts index 64df1b99a..deaf0130c 100644 --- a/packages/melonjs/src/application/resize.ts +++ b/packages/melonjs/src/application/resize.ts @@ -12,7 +12,6 @@ import type Application from "./application.ts"; function scale(game: Application, x: number, y: number): void { const renderer = game.renderer; const canvas = renderer.getCanvas(); - const context = renderer.getContext(); const settings = renderer.settings as any; const pixelRatio = device.devicePixelRatio; @@ -28,7 +27,7 @@ function scale(game: Application, x: number, y: number): void { // if anti-alias and blend mode were reset (e.g. Canvas mode) renderer.setAntiAlias(settings.antiAlias); - (renderer as any).setBlendMode(settings.blendMode, context); + renderer.setBlendMode(settings.blendMode); // force repaint game.repaint(); diff --git a/packages/melonjs/src/plugin/plugin.ts b/packages/melonjs/src/plugin/plugin.ts index 639cc9b3e..635be4632 100644 --- a/packages/melonjs/src/plugin/plugin.ts +++ b/packages/melonjs/src/plugin/plugin.ts @@ -95,7 +95,8 @@ export function register( ...args: any[] ): void { // derive name from class if not provided - const pluginName = name || pluginClass.toString().match(/ (\w+)/)![1]; + const pluginName = + name || pluginClass.name || pluginClass.toString().match(/ (\w+)/)![1]; // ensure me.plugins[name] is not already "used" if (cache[pluginName]) { diff --git a/packages/melonjs/src/video/canvas/canvas_renderer.js b/packages/melonjs/src/video/canvas/canvas_renderer.js index c49413a49..9918e83c1 100644 --- a/packages/melonjs/src/video/canvas/canvas_renderer.js +++ b/packages/melonjs/src/video/canvas/canvas_renderer.js @@ -108,7 +108,7 @@ export default class CanvasRenderer extends Renderer { } /** - * set a blend mode for the given context.
+ * set the current blend mode for this renderer.
* All renderers support:
* - "normal" : draws new content on top of the existing content
*
@@ -140,11 +140,10 @@ export default class CanvasRenderer extends Renderer { *
* @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation * @param {string} [mode="normal"] - blend mode - * @param {CanvasRenderingContext2D} [context] * @returns {string} the blend mode actually applied (may differ if the requested mode is unsupported) */ - setBlendMode(mode = "normal", context) { - context = context || this.getContext(); + setBlendMode(mode = "normal") { + const context = this.getContext(); this.currentBlendMode = mode; switch (mode) { case "lighter": diff --git a/packages/melonjs/src/video/renderer.js b/packages/melonjs/src/video/renderer.js index 7d7fc87ec..d45e81514 100644 --- a/packages/melonjs/src/video/renderer.js +++ b/packages/melonjs/src/video/renderer.js @@ -73,6 +73,12 @@ export default class Renderer { */ this.depthTest = "sorting"; + /** + * The GPU renderer string (WebGL only, undefined for Canvas) + * @type {string|undefined} + */ + this.GPURenderer = undefined; + /** * The Path2D instance used by the renderer to draw primitives * @type {Path2D} @@ -240,6 +246,16 @@ export default class Renderer { return this.currentBlendMode; } + /** + * set the current blend mode. + * Subclasses (CanvasRenderer, WebGLRenderer) implement the actual GL/Canvas logic. + * @param {string} [mode="normal"] - blend mode + * @param {boolean} [premultipliedAlpha=true] - whether textures use premultiplied alpha (WebGL only) + */ + setBlendMode(mode = "normal") { + this.currentBlendMode = mode; + } + /** * get the current fill & stroke style color. * @returns {Color} current global color diff --git a/packages/melonjs/src/video/webgl/webgl_renderer.js b/packages/melonjs/src/video/webgl/webgl_renderer.js index 0556ea81d..89b72fb29 100644 --- a/packages/melonjs/src/video/webgl/webgl_renderer.js +++ b/packages/melonjs/src/video/webgl/webgl_renderer.js @@ -358,6 +358,8 @@ export default class WebGLRenderer extends Renderer { // flush the current batcher this.currentBatcher.flush(); } + // rebind the renderer's shared vertex buffer (custom batchers may have bound their own) + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer); this.currentBatcher = batcher; this.currentBatcher.bind(); } @@ -724,7 +726,7 @@ export default class WebGLRenderer extends Renderer { } /** - * set a blend mode for the given context.
+ * set the current blend mode for this renderer.
* All renderers support:
* - "normal" : draws new content on top of the existing content
*
@@ -744,15 +746,19 @@ export default class WebGLRenderer extends Renderer { * and will always fall back to "normal" in WebGL.
* @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation * @param {string} [mode="normal"] - blend mode - * @param {WebGLRenderingContext} [gl] - a WebGL context + * @param {boolean} [premultipliedAlpha=true] - whether textures use premultiplied alpha (affects source blend factor) * @returns {string} the blend mode actually applied (may differ if the requested mode is unsupported) */ - setBlendMode(mode = "normal", gl = this.gl) { + setBlendMode(mode = "normal", premultipliedAlpha = true) { if (this.currentBlendMode !== mode) { + const gl = this.gl; this.flush(); gl.enable(gl.BLEND); this.currentBlendMode = mode; + // source factor depends on whether textures use premultiplied alpha + const srcAlpha = premultipliedAlpha ? gl.ONE : gl.SRC_ALPHA; + switch (mode) { case "screen": gl.blendEquation(gl.FUNC_ADD); @@ -763,7 +769,7 @@ export default class WebGLRenderer extends Renderer { case "additive": case "add": gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE); + gl.blendFunc(srcAlpha, gl.ONE); break; case "multiply": @@ -777,7 +783,7 @@ export default class WebGLRenderer extends Renderer { gl.blendFunc(gl.ONE, gl.ONE); } else { gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + gl.blendFunc(srcAlpha, gl.ONE_MINUS_SRC_ALPHA); this.currentBlendMode = "normal"; } break; @@ -788,14 +794,14 @@ export default class WebGLRenderer extends Renderer { gl.blendFunc(gl.ONE, gl.ONE); } else { gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + gl.blendFunc(srcAlpha, gl.ONE_MINUS_SRC_ALPHA); this.currentBlendMode = "normal"; } break; default: gl.blendEquation(gl.FUNC_ADD); - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + gl.blendFunc(srcAlpha, gl.ONE_MINUS_SRC_ALPHA); this.currentBlendMode = "normal"; break; }