Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ melonJS is designed so you can **focus on making games, not on graphics plumbing

- **Complete engine, minimal footprint** — Physics, tilemaps, audio, input, cameras, tweens, particles, UI — a full game stack in a single tree-shakeable ES module. No dependency sprawl, no library stitching.

- **Tiled as a first-class citizen** — Deep [Tiled](https://www.mapeditor.org) integration built into the core: orthogonal, isometric, hexagonal and staggered maps, animated tilesets, collision shapes, object properties, compressed formats — all parsed and rendered natively, with GPU-accelerated tile rendering for orthogonal maps under WebGL 2.
- **Scenes, loaded in one call** — `me.level.load(name)` brings an authored scene straight into your world. [Tiled](https://www.mapeditor.org) is a first-class citizen for **2D** — orthogonal, isometric, hexagonal & staggered maps, animated tilesets, collision shapes, object properties, compressed formats, with GPU-accelerated tile rendering under WebGL 2 — and **glTF / GLB** is the equivalent for **3D scenes**: author in Blender (or any DCC tool), export a `.glb`, and the whole scene — meshes, materials, cameras, and lights — loads under a `Camera3d`, no per-mesh wiring.

- **Batteries included, hackable by design** — Get started in minutes with minimal setup. When you need to go deeper: ES6 classes throughout, a plugin system for engine extensions, and a clean architecture that's easy to extend without fighting the framework.

Expand All @@ -49,12 +49,14 @@ Graphics
- Fast WebGL renderer for desktop and mobile devices with fallback to Canvas rendering
- Extensible batcher system for custom rendering pipelines
- High DPI resolution & Canvas advanced auto scaling
- Sprite with 9-slice scaling option, frame animation, and optional per-pixel normal-map shading for 3D-looking dynamic lights
- Sprite with 9-slice scaling option and frame animation
- Built-in effects such as tinting, masking, and CSS-style blend modes (normal, additive, multiply, screen, darken, lighten)
- Standard spritesheet, single and multiple Packed Textures support
- Compressed texture support (DDS, KTX, KTX2, PVR, PKM) with automatic format detection and fallback
- 3D mesh rendering with OBJ/MTL model loading, multi-material support, hardware depth testing, and perspective projection via `Camera3d`
- `Light2d` as a first-class `Renderable` — multiple dynamic lights, radial-gradient falloff, illumination-only mode, and procedural rendering via `drawLight`
- Lighting, in 2D and 3D:
- **2D** — `Light2d` as a first-class `Renderable` (multiple dynamic lights, radial-gradient falloff, illumination-only mode, procedural rendering via `drawLight`), plus optional per-pixel normal-map shading on sprites for 3D-looking dynamic lights
- **3D** — directional lights via `Light3d` / `LightingEnvironment` (half-Lambert diffuse + ambient floor), auto-loaded from a glTF scene's authored sun
- Built-in shader effects (Flash, Outline, Glow, Dissolve, CRT, Hologram, etc.) with multi-pass chaining via `postEffects`, plus custom shader support via `ShaderEffect` for per-sprite fragment effects (WebGL)
- Trail renderable for fading, tapering ribbons behind moving objects (speed lines, sword slashes, magic trails)
- System & Bitmap Text with built-in typewriter effect
Expand Down Expand Up @@ -92,7 +94,8 @@ UI
- `Draggable` / `DropTarget` for drag-and-drop with configurable overlap or contains-check
- `UITextButton` text button with hover, press, and key-bind support — built on `BitmapText`

Level Editor
Scenes
- Load a scene in one call with `me.level.load(name)` — 2D Tiled maps and 3D glTF scenes alike, auto-registered on preload
- [Tiled](https://www.mapeditor.org) map format [up to 1.12](https://doc.mapeditor.org/en/stable/reference/tmx-changelog/) built-in support for easy level design
- **GPU-accelerated tile rendering** for orthogonal maps under WebGL 2 — each layer draws as a single quad with no per-tile loop, ~5–8× faster than the legacy CPU renderer on dense maps. Honors animated tiles, flip bits, per-layer opacity/tint/blend, and oversized bottom-aligned tiles; falls back transparently to the CPU renderer on isometric/staggered/hexagonal layers or non-WebGL-2 contexts
- Uncompressed and [compressed](https://github.com/melonjs/melonJS/tree/master/packages/tiled-inflate-plugin) Plain, Base64, CSV and JSON encoded XML tilemap loading
Expand All @@ -110,11 +113,17 @@ Level Editor
- Dynamic Layer and Object/Group ordering
- Dynamic Entity loading via an extensible object factory registry — register custom handlers for any Tiled class name without modifying engine code
- Shape based Tile collision support
- glTF / GLB 3D scenes — load an authored 3D scene with `me.level.load(...)`, the same one call as a Tiled map
- The whole scene loads at once — meshes, materials, cameras and lights — viewed under a `Camera3d`
- Automatically lit by the scene's directional lights (the sun set up in the authoring tool)
- Textured, solid-colored, and vertex-colored materials
- `.glb` and self-contained `.gltf` files
- Works with any glTF authoring tool (Blender, Maya, 3ds Max, Cinema 4D, …)

Assets
- Asynchronous asset loading with progress tracking
- A fully customizable preloader
- Support for images, JSON, TMX/TSX, `.aseprite` / `.ase` binary, audio, video, binary and fonts
- Support for images, JSON, TMX/TSX, glTF / GLB 3D scenes, `.aseprite` / `.ase` binary, audio, video, binary and fonts

Core
- `Application` class as the modern entry point with built-in pause, resume, and `freeze()` (hit-stop) primitives
Expand Down Expand Up @@ -173,6 +182,7 @@ Examples
* [3D Mesh](https://melonjs.github.io/melonJS/examples/#/mesh-3d) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/mesh3d))
* [3D Mesh Material](https://melonjs.github.io/melonJS/examples/#/mesh-3d-material) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/mesh3dMaterial))
* [AfterBurner Clone](https://melonjs.github.io/melonJS/examples/#/after-burner) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/afterBurner)) — `Camera3d` + 3D Mesh arcade shooter
* [glTF Scene](https://melonjs.github.io/melonJS/examples/#/gltf) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/gltf)) — a Blender-authored, lit 3D scene loaded with `me.level.load`
* [Trail](https://melonjs.github.io/melonJS/examples/#/trail) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/trail))
* [Shader Effects](https://melonjs.github.io/melonJS/examples/#/shader-effects) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/shaderEffects))
* [Spine](https://melonjs.github.io/melonJS/examples/#/spine) ([source](https://github.com/melonjs/melonJS/tree/master/packages/examples/src/examples/spine))
Expand Down
8 changes: 8 additions & 0 deletions packages/debug-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 16.1.0

### Requirements
- Requires melonJS **19.8.0 or later** — the 3D mesh bounding-box overlay uses the new `Mesh.getBounds3d()` and `Camera3d.worldToScreen()` APIs (and the exported `AABB3d` type) introduced in 19.8.

### Improvements
- The hitbox overlay now draws a correct **3D bounding-box wireframe** for `Mesh` renderables under a `Camera3d`. Previously a mesh only got the inherited flat 2D `getBounds()` rectangle — screen-flat, oversized, and unrelated to the 3D geometry. The new overlay projects the 8 corners of the mesh's world-space `AABB3d` through the camera (`Camera3d.worldToScreen`) and strokes the 12 edges in a screen-space pass, so the box tracks the mesh in perspective. Meshes under a `Camera2d` (the self-projected 2D path) keep the existing 2D box.

## 16.0.0

### Breaking Changes
Expand Down
4 changes: 2 additions & 2 deletions packages/debug-plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@melonjs/debug-plugin",
"version": "16.0.0",
"version": "16.1.0",
"description": "melonJS debug plugin",
"homepage": "https://www.npmjs.com/package/@melonjs/debug-plugin",
"type": "module",
Expand Down Expand Up @@ -43,7 +43,7 @@
"README.md"
],
"peerDependencies": {
"melonjs": ">=19.5.0"
"melonjs": ">=19.8.0"
},
"devDependencies": {
"concurrently": "^9.2.1",
Expand Down
112 changes: 112 additions & 0 deletions packages/debug-plugin/src/patches.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import {
BitmapText,
Bounds,
Camera2d,
Camera3d,
Container,
Entity,
game,
ImageLayer,
Matrix3d,
Mesh,
plugin,
Renderable,
Text,
Vector2d,
Vector3d,
} from "melonjs";

/**
Expand All @@ -23,6 +27,102 @@ import {
const sharedBodyAABB = new Bounds();
const sharedBodyVel = new Vector2d();

// Scratch for the Mesh 3D bounding-box wireframe overlay. The 8 box corners
// (world space) and their projected screen positions are reused every frame;
// the two matrices save/restore the perspective projection around the
// screen-space line pass. Single-instance is safe — drawing is synchronous.
const _meshCorners = Array.from({ length: 8 }, () => {
return new Vector3d();
});
const _meshScreen = Array.from({ length: 8 }, () => {
return new Vector2d();
});
const _meshSavedProj = new Matrix3d();
const _meshScreenProj = new Matrix3d();
// the 12 edges of a box, indexing the 8 corners laid out by
// `strokeMeshWireframe`: near face (z=min) 0-3, far face (z=max) 4-7.
const BOX_EDGES = [
[0, 1],
[1, 2],
[2, 3],
[3, 0], // near face
[4, 5],
[5, 6],
[6, 7],
[7, 4], // far face
[0, 4],
[1, 5],
[2, 6],
[3, 7], // connecting edges
];

/**
* Draw a {@link Mesh}'s world-space 3D bounding box as a green wireframe
* under a `Camera3d`. The 8 corners of `mesh.getBounds3d()` are projected to
* screen via `camera.worldToScreen`, then the 12 edges are stroked in a
* screen-space pass (identity transform + a screen-ortho projection) so the
* lines land exactly where the perspective-projected mesh is drawn.
*
* This replaces the flat 2D `getBounds()` rectangle the generic overlay would
* draw, which cannot describe a 3D mesh's extent.
* @param {*} renderer
* @param {import("./index").DebugPanelPlugin} panel
* @param {import("melonjs").Mesh} mesh
* @param {import("melonjs").Camera3d} camera
*/
function strokeMeshWireframe(renderer, panel, mesh, camera) {
const box = mesh.getBounds3d();
if (!box.isFinite()) {
return;
}
const min = box.min;
const max = box.max;
// near face (z = min): 0..3, far face (z = max): 4..7
_meshCorners[0].set(min.x, min.y, min.z);
_meshCorners[1].set(max.x, min.y, min.z);
_meshCorners[2].set(max.x, max.y, min.z);
_meshCorners[3].set(min.x, max.y, min.z);
_meshCorners[4].set(min.x, min.y, max.z);
_meshCorners[5].set(max.x, min.y, max.z);
_meshCorners[6].set(max.x, max.y, max.z);
_meshCorners[7].set(min.x, max.y, max.z);
for (let i = 0; i < 8; i++) {
// worldToScreen returns null for a corner at/behind the camera —
// projecting it would mirror the point and draw edges shooting across
// the screen, so skip the whole box when the mesh straddles the camera.
if (camera.worldToScreen(_meshCorners[i], _meshScreen[i]) === null) {
return;
}
}

// stroke the edges in screen space: drop the camera view transform and
// swap the perspective projection for a screen ortho, so the already-
// projected pixel coordinates draw 1:1. The projection isn't part of the
// save/restore stack, so it's saved/restored explicitly (same pattern as
// the renderer's own blit path).
renderer.save();
_meshSavedProj.copy(renderer.projectionMatrix);
renderer.currentTransform.identity();
_meshScreenProj.ortho(0, camera.width, camera.height, 0, -1, 1);
renderer.setProjection(_meshScreenProj);
renderer.setColor("green");
renderer.lineWidth = 1;
for (const [a, b] of BOX_EDGES) {
renderer.strokeLine(
_meshScreen[a].x,
_meshScreen[a].y,
_meshScreen[b].x,
_meshScreen[b].y,
);
}
// flush the lines under the screen projection before restoring the
// perspective projection, or they'd be re-projected on the next flush.
renderer.flush();
renderer.setProjection(_meshSavedProj);
renderer.restore();
panel.counters.inc("shapes");
}

/**
* Stroke the orange body AABB and the red collision shapes for the
* given renderable. Caller is responsible for positioning the renderer
Expand Down Expand Up @@ -109,6 +209,18 @@ export function applyPatches(panel) {
panel.counters.inc("draws");
}

// Mesh under a Camera3d: draw the proper 3D bounding-box wireframe
// instead of the flat, oversized 2D getBounds() box (which can't
// describe 3D geometry). Under a Camera2d a mesh self-projects to 2D,
// so it falls through to the generic box below.
if (this instanceof Mesh && panel.options.hitbox) {
const cam = this.parentApp?.viewport ?? game.viewport;
if (cam instanceof Camera3d) {
strokeMeshWireframe(renderer, panel, this, cam);
return;
}
}

// skip types that have their own dedicated patches, or when no
// overlay is enabled (hitbox AND velocity both off ⇒ nothing
// would be drawn). Note that hitbox and velocity are now
Expand Down
Binary file not shown.
Loading
Loading