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;
}