Skip to content

Canvas meshes: textures, depth order, palette shading; examples on Canvas, proportional minimap - #1855

Merged
obiot merged 4 commits into
masterfrom
canvas-mesh-fixes
Nov 12, 2026
Merged

obiot merged 4 commits into
masterfrom
canvas-mesh-fixes

Conversation

@obiot

@obiot obiot commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Description

Findings from the full examples retest before 20.14 (every example, every renderer, Chrome, Safari and headless, compared with the build before #1854: #1854 broke nothing). These are older bugs the retest turned up.

Canvas renderer, meshes (CanvasRenderer#drawMesh), checked against WebGL:

  • A textured OBJ + MTL model (white Kd plus a texture, as Kenney kits ship) drew as a white silhouette: any vertexColors sent it down the solid-fill path. Broken since multi-material support (19.x). It now draws its texture times the material color and tint, as the GPU renderers do.
  • The painter's sort was inverted since 19.0: projectVertices maps the near plane to +zScale, but the sort took the largest Z as the farthest, so meshes showed their far side over their near side. The back-face test was right and is now covered.
  • Palette models (each face mapped onto a line of a shared texture; all 568 triangles of the Kenney fox) got one flat texel per triangle. They are now filled with a gradient of the texels along their UV line, as the GPU interpolates them.
  • Seams: opaque triangles stroke their edge in their own fill, covering the line two antialiased neighbours leave.
  • Texture pixels are read once per image, not one getImageData per triangle per frame (that readback froze Ground Shadows on Canvas).

Remaining Canvas limits, documented in the skills: triangles are sorted, not pixels (near-coplanar layers can show slivers), no perspective-correct mapping, unlit, no per-material textures.

Examples

  • 3D Mesh and Multi-material OBJ use a 2D camera and now run on Canvas (~60 fps) instead of refusing it.
  • The six Camera3d examples that showed an empty sky on Canvas now stop with their existing "needs WebGL" message, like AfterBurner and the others.
  • Both platformers: the minimap is 18% of the canvas width, so it no longer covers the buttons in portrait (checked through resizes and emulated orientation changes).
  • The matter platformer's touch controls come from @melonjs/gamepad-plugin, as the built-in platformer's do since Examples: the platformer's touch controls come from the gamepad plugin #1800; its own joypad covered the jump button in portrait.

Docs

  • Meshes target WebGL 2 / WebGPU; Canvas is a last-resort fallback for 2D-camera meshes. Said consistently in the Mesh, InstancedMesh, CanvasRenderer#drawMesh and video.CANVAS docs, README, DOC_README and the 3D and renderer-backends skills, with a supportsDepthBuffer example.
  • Seven classes had no description on the API site because a helper or typedef sat between their doc comment and the class: Mesh (since Text: gradient fills, moving labels, and the typings the docs promised #1662), Light3d, InstancedMesh, Color, Noise, Trigger, ShaderEffect. Moved back; TypeDoc warnings unchanged.

Type of change

  • Bug fix
  • New feature
  • Documentation update
  • Performance improvement
  • Refactoring (no functional changes)

Checklist

  • I have read the Contributing Guide
  • My code follows the existing code style (pnpm lint passes)
  • I have tested my changes locally (pnpm test passes, apart from text-multiline-bounds, which also fails on master on this Mac, and a timing flake that passes alone)
  • I have added tests that cover my changes: canvas-mesh.spec.js (12 tests, draws and reads back, including an OBJ + MTL palette model through the loader and a colored cube through the real projection); each fix fails its test when reverted
  • The build succeeds (pnpm build)

Related issues

Found while retesting the examples after #1854.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SeWCkETjW35ZbXDRswV1hx

obiot and others added 3 commits November 12, 2026 13:43
The Canvas renderer's drawMesh, measured against WebGL on the examples:

- A mesh with baked material colors (vertexColors, from an MTL) was
  always solid-filled, so a textured OBJ + MTL model came out as a white
  silhouette. The texture is drawn, multiplied by each triangle's
  color and the tint, as the GPU renderers do; only a mesh with no
  texture (the 1x1 white fallback) is filled.
- The painter's sort took the largest projected Z as the farthest, but
  projectVertices puts the near plane at +zScale: meshes drew their far
  side over their near side. It now sorts on each triangle's farthest
  point, smallest Z first. The back-face test was right, and is now
  covered.
- Palette models (all 568 triangles of a Kenney animal) map each face
  onto a line of one texture. Such a triangle was filled with the one
  texel under its first vertex; it is now filled with a gradient of the
  texels along its UV line, as the GPU interpolates it, and a point is
  one color. A UV area under a quarter texel counts as degenerate,
  instead of exactly zero, so near-zero ones no longer smear.
- Opaque triangles also stroke their edge in their own fill, covering
  the faint seam two antialiased neighbours leave.
- Texture pixels are read once per image and cached, instead of one
  getImageData readback per triangle per frame, which froze the ground
  shadows example on Canvas.

Tests: canvas-mesh.spec.js draws and reads back, including an OBJ + MTL
palette model through the loader and a colored cube through the real
projection; each fix fails its test when reverted. Skills updated where
they described the old Canvas behaviour.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SeWCkETjW35ZbXDRswV1hx
- The 3D Mesh and Multi-material OBJ examples draw their meshes under a
  2D camera, which the Canvas renderer supports, at about 60 fps; they
  no longer refuse it.
- Billboard, Instanced Forest, glTF Scene, glTF Animated Model, Ground
  Shadows and Night City need Camera3d, which needs a depth buffer.
  On Canvas they showed an empty sky (and Instanced Forest ran at about
  1 fps); they now stop with their existing "needs WebGL" message, as
  AfterBurner, Earth Defender, Jungle Rabbit and Material Textures do.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SeWCkETjW35ZbXDRswV1hx
…d plugin

- Both platformers' minimap is 18% of the canvas width (about the old
  180 px at the default size), zoomed to the whole level, and resized
  through Camera2d#setViewport on CANVAS_ONRESIZE, which an orientation
  change also emits. In a portrait window it no longer covers the audio
  and fullscreen buttons.
- The matter platformer's touch controls come from
  @melonjs/gamepad-plugin, as the built-in platformer's have since
  #1800: its own VirtualJoypad (controls.ts, removed) was laid out for
  landscape and covered the jump button in portrait. The player's
  gamepad bindings drive it unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SeWCkETjW35ZbXDRswV1hx
Copilot AI balanced review requested due to automatic review settings November 12, 2026 05:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

- Mesh, InstancedMesh, CanvasRenderer#drawMesh, the video.CANVAS setting,
  the README, DOC_README and the 3D and renderer-backends skills now say
  it the same way: 3D is built for WebGL 2 and WebGPU; a mesh under a 2D
  camera still draws on Canvas as a last-resort fallback (CPU-projected,
  unlit, one texture, slower with every triangle), and a Camera3d scene
  does not draw there at all. Mesh and the 3D skill gain an example of
  checking supportsDepthBuffer after init().
- Two Mesh comments still described the old Canvas solid fill.
- Seven class descriptions were not on the API site: a helper or a
  typedef had been inserted between each doc comment and its class
  (Mesh since #1662), so TypeDoc showed Mesh, Light3d and Trigger with
  Renderable's description and InstancedMesh, Color, Noise and
  ShaderEffect with none. Each comment is back on its class; TypeDoc's
  warnings are unchanged (208 before and after).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SeWCkETjW35ZbXDRswV1hx
Copilot AI balanced review requested due to automatic review settings November 12, 2026 05:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@obiot
obiot merged commit 70bfe75 into master Nov 12, 2026
6 checks passed
@obiot
obiot deleted the canvas-mesh-fixes branch November 12, 2026 06:27
@obiot obiot mentioned this pull request Nov 12, 2026
5 of 10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants