Skip to content

v19.7.0

Latest

Choose a tag to compare

@github-actionsgithub-actions released this 08 Jun 23:58
fe9d160

What's New in melonJS 19.7.0

Highlights: Camera3d perspective camera lands. Every batched shader carries per-sprite depth as vec3 aVertex, unlocking 3D-projected sprites and meshes. Backward compatible with existing 2D code.

New Features

  • Camera3d — perspective camera extending Camera2d with fov, aspect, pitch, yaw, followOffset, lookAhead. Opt in via new Application(w, h, { cameraClass: Camera3d }) or Stage({ cameras: [new Camera3d(...)] }). Y-down + +Z forward.
  • Octree broadphase — 3D spatial subdivision sibling to QuadTree. World.broadphase reactively swaps based on sortOn so 2D↔3D transitions are transparent. Region queries: queryAABB, querySphere, queryFrustum, queryRay.
  • Sphere geometry primitive (new Sphere(x, y, z, r)) + AABB3d + Frustum — the 3D shape vocabulary. Sphere is the canonical 3D query shape (adapter.querySphere(sphere), Octree.querySphere(sphere)).
  • Camera3d.queryVisible(world) — bulk frustum cull broadphase pass for dense 3D scenes.
  • Mesh under Camera3d — world-space GPU projection with lazy back-face winding reversal.
  • Multi-material OBJMesh draws OBJ files with multiple usemtl directives + MTL, baking per-material Kd into a per-vertex color buffer. Single draw call regardless of material count. New Multi-material OBJ example.
  • Per-sprite depth on the GPUQuad, LitQuad, Primitive, and GPU TMX batchers all carry .depth as the z component. renderer.setDepth(depth) mirrors setTint.
  • PhysicsAdapter.querySphere? + raycast3d? — optional 3D query surface, capability-gated by capabilities.raycasts3d. BuiltinAdapter implements both; matter / planck omit them.
  • math.lerp(a, b, t) — scalar linear interpolation, single source of truth for vector lerps.
  • math.damp(current, target, lambda, dt) — frame-rate-independent exponential damping (Three.js MathUtils.damp parity). Vector2d.damp / Vector3d.damp follow the same shape.
  • event.GPU_TEXTURE_CACHE_RESET + event.RENDER_TARGET_CHANGED — renderer-agnostic events for cross-batcher state invalidation. Future WebGPU port emits the same events.
  • Application#requestFullscreen / exitFullscreen / isFullscreen — app-instance fullscreen control replacing the deprecated device.requestFullscreen global-game lookup.
  • device.setAutoFocus(enable) (#1486) — function setter for the autofocus-on-visibility behaviour, finally writable from user code (direct field assignment was a read-only ESM binding).

Changed

  • Camera2d smooth follow is now frame-rate independent. updateTarget swaps pos.lerp for pos.damp(target, lambda, dt) with lambda = -ln(1 - damping) * timer.maxfps. Existing damping values keep their feel at the target framerate; high-refresh users finally get the same convergence the dev tuned for. No tuning change required.
  • Mesh rendering clears depth once per target, not per mesh (#1468). MeshBatcher now owns mesh-mode GL state (bind() enters, unbind() restores). Consecutive mesh draws pay zero state-toggle cost between them. Side-effect: intersecting / painter-wrong-order meshes resolve per-pixel via the GPU depth test (closer wins regardless of draw order), where the old per-mesh clear let the second draw silently overwrite.
  • Application fails loudly on WebGL-required misconfiguration (#1479). Throws when renderer: video.WEBGL is requested but WebGL is unavailable (was silent Canvas fallback); warns when cameraClass.defaultSortOn === "depth" (Camera3d or subclass) under a Canvas renderer.
  • device.platform.isMobile drops the dead-platform regexes (wp, BlackBerry, Kindle) — chain is now /Mobi/.test(ua) || iOS || android, covering ~99.9% of mobile traffic per MDN.
  • device.platform.iOS / isMobile correctly identify iPads on iPadOS 13+ (#1467). Since Sept 2019 Safari on iPad ships the desktop Mac UA — no iPad token — so every modern iPad was falling through isMobile as desktop. Detection layers navigator.platform === "MacIntel" && maxTouchPoints > 1 on top of the UA regex.
  • timer.step is now the precise 1000 / maxfps (was Math.ceil), fixing a ~2% tick-interpolation undershoot under frame drops.
  • Breaking-ish: QuadTree dropped from public package exports — broadphase is implementation detail behind world.adapter.*. Direct import { QuadTree } from "melonjs" should be removed; the broadphase instance is still reachable as world.broadphase for tooling.
  • Breaking-ish: aVertex widened from vec2 to vec3 across quad-multi.vert, quad-multi-lit.vert, primitive.vert, orthogonal-tmxlayer.vert. Custom shaders binding by name keep working (attribute vec2 aVertex; is fine, z is dropped).
  • Breaking-ish: VertexArrayBuffer.push() gained a z parameter between y and u. Custom batchers that reimplement addQuad / drawVertices need to insert z (default 0); subclasses that delegate to super.addQuad are unaffected.
  • Camera2d default near / far widened from ±1000 to ±1e6 so depth-participating sprites don't cull-clip under Container.autoDepth or Y-sort patterns on tall maps.
  • system/device converted to TypeScript (#1467).

Bug Fixes

  • WebGLRenderer.createPattern cache-key collision (#1448) — two patterns created from the same source image with different repeat modes used to silently collide on a single GL texture unit. TextureCache now keys units by (source, repeat) via a nested Map<source, Map<repeat, unit>>; each distinct repeat mode gets its own unit. Canvas / WebGL createPattern(image) (no repeat arg) defaults to "no-repeat" in both renderers for parity.
  • GPU TMX layer reset crash (#1471) when a non-material batcher was active at stage change. Reset now pins to batchers.get("quad") instead of grabbing renderer.currentBatcher.
  • WebGL TextureCache cross-batcher binding desync — a unit-pool reset only cleared the current batcher's boundTextures map; stale entries on every other batcher caused meshes to render as black silhouettes and bullets as pure white in mixed-batcher scenes. Fixed via the new event.GPU_TEXTURE_CACHE_RESET event.
  • WebGL color-attribute NaN canonicalization on Apple Metal / ANGLEMeshBatcher color attribute switched from UNSIGNED_BYTE × 4 normalized to FLOAT × 4; packed-color bytes upload via vertex.toUint8() so they survive driver canonicalization.
  • Stage.reset re-applies the chosen camera's defaultSortOn on every reset — covers the loader-pinned-Camera2d → user-stage handoff so distant meshes no longer paint on top of nearer ones under perspective.

Performance

  • Mesh-state ownership migration unlocks dense-3D-scene CPU wins (50+ mesh draws). Per-mesh state-toggle cost goes to zero between consecutive draws. Extrapolated 3–30ms saved per frame at 50+ mesh scenes; sparse scenes (~5 meshes / frame) are within noise.
  • GPU TMX uniform cachinggl.uniform* calls on hot per-layer paths skip when the value matches the previously-set cached value.

Install

npm install melonjs@19.7.0