BabylonJS/Babylon.js · error · Error

To call addPart()/addParts(), each source mesh must be fully

Error message

To call addPart()/addParts(), each source mesh must be fully loaded

What it means

The GaussianSplattingMesh addPart/addParts validation throws when a source mesh (`other`) has no `_splatsData` and is not a reserved-empty streaming placeholder — i.e. it hasn't finished loading/decoding its splat data. Compound meshes cannot be added; parts must be fully decoded first.

Source

Thrown at packages/dev/core/src/Meshes/GaussianSplatting/gaussianSplattingMesh.pure.ts:1128

     * Core implementation for adding one or more source parts as new
     * parts. Writes directly into texture-sized CPU arrays, updates the retained merged source
     * buffers, and uploads in one pass.
     *
     * @param others - Source meshes to append (must each be non-compound and fully loaded)
     * @param disposeOthers - Dispose source meshes after appending
     * @returns Proxy meshes and their assigned part indices
     */
    protected _addPartsInternal(others: IGaussianSplattingPartSource[], disposeOthers: boolean): { proxyMeshes: GaussianSplattingPartProxyMesh[]; assignedPartIndices: number[] } {
        if (others.length === 0) {
            return { proxyMeshes: [], assignedPartIndices: [] };
        }

        // Validate
        for (const other of others) {
            // Reserved-empty placeholders (reserveStreamingPart) intentionally carry no splat data —
            // their atlas region is left zeroed (invisible) for a streaming engine to decode into later.
            if (!other._splatsData && !other._isReservedEmpty) {
                throw new Error(`To call addPart()/addParts(), each source mesh must be fully loaded`);
            }
            if (other.isCompound) {
                throw new Error(`To call addPart()/addParts(), each source mesh must not be a compound`);
            }
        }

        const splatCountA = this._vertexCount;
        const totalOtherCount = others.reduce((s, o) => s + o._vertexCount, 0);
        const totalCount = splatCountA + totalOtherCount;

        const textureSize = this._getTextureSize(totalCount);
        const textureLength = textureSize.x * textureSize.y;
        const covBSItemSize = this._useRGBACovariants ? 4 : 2;

        // Allocate destination arrays for the full new texture
        const covA = new Uint16Array(textureLength * 4);
        const covB = new Uint16Array(covBSItemSize * textureLength);
        const colorArray = new Uint8Array(textureLength * 4);

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Await each source mesh's load/decode promise (e.g. `await other.whenReadyAsync()` or the promise from GaussianSplattingMesh creation) before addParts.
  2. Check `other._splatsData` (or a public loaded flag/`isReady`) for every source before merging.
  3. If using streaming, mark intentional placeholders via `reserveStreamingPart` instead of passing unloaded meshes.

Example fix

// before
const a = await GaussianSplattingMesh.Parse(...urlA...);
const b = new GaussianSplattingMesh("b", urlB, scene); // not awaited
a.addParts([b]);

// after
const a = await GaussianSplattingMesh.Parse(...urlA...);
const b = await new GaussianSplattingMesh("b", urlB, scene).whenReadyAsync(); // ensure loaded
a.addParts([b]);
Defensive patterns

Strategy: validation

Validate before calling

if (!other._splatsData && !other._isReservedEmpty) {
    await other.whenReadyAsync(); // or the mesh's load promise
}
mesh.addParts([other]);

Type guard

function isSplatsSourceLoaded(mesh: GaussianSplattingMeshBase): boolean {
    return !!mesh._splatsData || mesh._isReservedEmpty;
}

Try / catch

try {
    compound.addParts(sources);
} catch (e) {
    if (e instanceof Error && e.message.includes("fully loaded")) {
        await Promise.all(sources.map(s => s.whenReadyAsync()));
        return compound.addParts(sources);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling `mesh.addParts([other])` where `other` is still loading from a .splat/.ply/.ksplat URL (its async decode hasn't resolved, so `_splatsData` is undefined), or passing a mesh constructed but never awaited.

Common situations: Building a compound splat from several downloaded assets without awaiting each one; queuing addParts immediately after construction; a failed load leaving `_splatsData` null.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/92b1d4feb4d35820. Report an issue: GitHub.