Merging Craters into a Matter.js Landscape

Matter.js does not provide native constructive solid geometry (CSG) or polygon clipping tools, making dynamic terrain modification require an external geometry pipeline. This article details how to approximate circular craters as polygons, merge overlapping craters using a 2D boolean clipping library, subtract them from a static landscape, and reconstruct the updated rigid body in Matter.js using vertex decomposition.

1. Representing Terrain and Craters as Polygons

To alter a terrain polygon in Matter.js, the terrain and the circular craters must be represented as discrete lists of 2D coordinates ({x, y}). Since true circles cannot be processed by standard 2D polygon-clipping algorithms, approximate each crater circle using regular polygon vertices:

function createCirclePolygon(centerX, centerY, radius, segments = 16) {
    const vertices = [];
    for (let i = 0; i < segments; i++) {
        const angle = (i / segments) * Math.PI * 2;
        vertices.push({
            x: centerX + radius * Math.cos(angle),
            y: centerY + radius * Math.sin(angle)
        });
    }
    return vertices;
}

2. Performing Boolean Operations with a Clipping Library

Matter.js relies on static vertex lists and cannot subtract shapes on its own. Use an external polygon-clipping library such as ClipperLib (Javascript port of Angus Johnson's Clipper) or PolyBool.js to perform the geometric operations.

When multiple craters overlap:

  1. Union overlapping craters: If craters are generated concurrently, combine them into a unified polygon mask using a Boolean Union operation.
  2. Subtract from the terrain: Subtract the crater polygon(s) from the base landscape polygon using a Boolean Difference operation.

Using ClipperLib terminology:

This operation returns an array of one or more non-intersecting polygons, accounting for scenarios where an explosion splits the landscape into multiple distinct landmasses.

3. Decomposing and Updating Matter.js Bodies

Matter.js bodies must be convex. When you modify terrain, the resulting polygon is almost always concave. Matter.js automatically handles concave polygons via its internal integration of poly-decomp.js, which breaks complex polygons into an array of convex parts.

Ensure poly-decomp.js is loaded into your execution environment before creating the bodies:

// Ensure poly-decomp is available to Matter.js
Matter.Common.setDecomp(decomp);

function updateTerrainBody(world, oldBody, newPolygonVertices) {
    // 1. Remove the old terrain body from the physics world
    Matter.Composite.remove(world, oldBody);

    // 2. Generate a new composite or multi-convex body from the clipped vertices
    // Note: fromVertices calculates the center of mass automatically
    const newBody = Matter.Bodies.fromVertices(
        0, 
        0, 
        newPolygonVertices, 
        {
            isStatic: true,
            render: {
                fillStyle: '#555555',
                strokeStyle: '#222222',
                lineWidth: 1
            }
        }, 
        true
    );

    // Reposition the body if offsets shifted during decomposition
    Matter.Body.setPosition(newBody, {
        x: newBody.bounds.min.x + (newBody.bounds.max.x - newBody.bounds.min.x) / 2,
        y: newBody.bounds.min.y + (newBody.bounds.max.y - newBody.bounds.min.y) / 2
    });

    // 3. Add the new body back to the physics simulation
    Matter.Composite.add(world, newBody);

    return newBody;
}

4. Performance Considerations

Regenerating concave collision geometry is computationally heavy: