@pixi/3d
    Preparing search index...

    Resources and cleanup

    Meshes, lights and cameras are nodes, the 3D counterpart of 2D containers. A mesh draws with a geometry, which holds its shape, and a material, which sets how its surface looks. The geometry, the material and the material's textures all take GPU memory. You free them in two ways, and PixiJS's garbage collector frees some for you:

    • destroy() takes a node or resource down for good.
    • unload() frees its GPU memory and keeps everything needed to draw it again, like unload() on a 2D texture's source. For most geometry, it frees the geometry's space in a shared buffer instead.

    Every Geometry3D takes its own space in GPU memory the first time it draws, so build each shape once and give it to every mesh of that shape. Sharing a geometry also lets meshes draw together, as Performance explains. You can share a material the same way, and a change to it then shows on every mesh that uses it.

    import { IcosahedronGeometry, Material3D, Mesh3D } from '@pixi/3d';

    const rock = new IcosahedronGeometry({ radius: 0.5 });
    const stone = new Material3D({ baseColor: 0x9a93ad, roughness: 0.9 });

    for (const [x, z] of [
    [-3.6, -2.4],
    [3, -3.8],
    [3.8, 1.6],
    ]) {
    const mesh = new Mesh3D({ geometry: rock, material: stone });

    mesh.position.set(x, 0.2, z);
    view3d.root.addChild(mesh);
    }

    As in PixiJS 2D, destroy() takes a node out of its parent and detaches its children, which survive. It destroys nothing you gave it, such as a mesh's geometry and material, unless you pass Destroy3DOptions flags. Passing true sets every flag, so it destroys shared resources too. Pass a flag only for resources nothing else uses.

    One 2D habit doesn't carry over. On a Mesh3D, the texture flag destroys only the textures of a material that the same call destroys. So mesh.destroy({ texture: true }) frees none of the textures of a material you gave it. Add material: true to free them, and textureSource to free their sources as well. A Sprite3D works like a 2D sprite, and texture: true frees its texture.

    // A tree whose meshes share geometry and materials with the other trees.
    // This destroys its meshes and keeps the geometry and materials.
    tree.destroy({ children: true });

    // A view with geometry and materials that only its scene uses, and the
    // orbit controller that turns its camera
    orbit.destroy();
    view3d.destroy({ children: true, geometry: true, material: true });

    Keep these flags away from what you loaded with Assets.load. Don't use texture on a material whose textures you loaded, or geometry and material on a model, since a model's geometry and materials belong to its file. Free loaded files with Assets.unload instead, as in PixiJS 2D. Loading models shows how for a model.

    Destroying a View3D with children destroys the whole scene, camera included. Without it, the view empties its root, and the nodes survive for you to reuse. The view never destroys the cube map it uses as its environment or background. A cube map is an image of the surroundings. Environment and IBL shows how to free one.

    Camera controllers and the passes in passes, which are drawing steps such as bloom, aren't part of the scene, so no flag destroys them. Destroy an OrbitCamera or FPSCamera yourself, as Camera controls shows, and destroy each pass you retire, as Post-processing says.

    If you destroy a view's camera, give the view a new one before it next renders, or that render throws, as Cameras explains.

    unload() keeps the data on the CPU, so the next frame that draws the object uploads it again. Use it for content you'll show again later, such as a level the player has left.

    Take the content out of the scene first, because hiding it isn't enough. The next frame uploads the geometry of every mesh in the scene again, even hidden or off-screen ones, so unloading content you only hid frees none of its geometry. For the same reason, unloading a geometry your content shares with a mesh that stays frees nothing, and neither does unloading a texture it shares with a mesh that's drawn.

    view3d.root.removeChild(level);
    level.unload();

    // When the player comes back, adding it is enough
    view3d.root.addChild(level);

    Each class unloads a different part:

    • Mesh3D and Container3D unload the geometry of every mesh under them. Materials stay, since they're usually shared, except under a Model3D, which unloads its materials too.
    • BaseMaterial3D unloads its textures, except render textures, whose pixels exist only on the GPU.
    • BatchedMesh3D unloads its buffers and the geometry of its slots, which are the meshes it draws. Their materials stay.
    • View3D unloads what the view keeps to draw the scene, such as its shadow textures, but not the scene's geometry or textures.

    Geometry that isn't dynamic shares a GPU buffer with other geometry that has the same vertex attributes, the per-vertex data such as positions and UVs. Unloading or destroying such a geometry frees its space in that buffer. The buffer never shrinks, though, so unloading even a whole level of geometry gives no GPU memory back. Removing, hiding or destroying a mesh without geometry: true keeps that space. So free the old level's geometry before you add the next one. New geometry with the same attributes then reuses that space before the buffer grows.

    PixiJS's renderer has a garbage collector that unloads GPU resources nothing has used for longer than its gcMaxUnusedTime option, 60 seconds by default. GCSystemOptions lists its options.

    In PixiJS 3D, the collector also calls unload on a View3D that has been out of the stage that long, and the view rebuilds on its next render. The collector never unloads a view that's still in the stage, even a hidden one; unload lists the other cases it skips. Set a view's autoGarbageCollect to false to keep it loaded while it's out of the stage.

    The collector never frees a geometry's space in the shared buffer, so unload or destroy the geometry to free it.