BabylonJS/Babylon.js · critical · Error

Invalid quaternion mode

Error message

Invalid quaternion mode

What it means

SOG stores rotations as 3 quaternion components plus a 4th byte encoding which component is omitted (mode = 4th byte - 252, valid 0..3). This error means a splat's 4th rotation byte produced a mode outside 0-3, i.e. the quaternion image data is corrupt or not in the expected SOG encoding (bytes must be in 252..255 for the mode channel).

Source

Thrown at packages/dev/loaders/src/SPLAT/sog.pure.ts:289

        const d = Math.sqrt(Math.max(0, 1 - t));

        // Place components according to mode
        let q: [number, number, number, number];
        switch (mode) {
            case 0:
                q = [d, a, b, c];
                break; // omitted = x
            case 1:
                q = [a, d, b, c];
                break; // omitted = y
            case 2:
                q = [a, b, d, c];
                break; // omitted = z
            case 3:
                q = [a, b, c, d];
                break; // omitted = w
            default:
                throw new Error("Invalid quaternion mode");
        }

        rot[i * 32 + 28 + 0] = q[0] * 127.5 + 127.5;
        rot[i * 32 + 28 + 1] = q[1] * 127.5 + 127.5;
        rot[i * 32 + 28 + 2] = q[2] * 127.5 + 127.5;
        rot[i * 32 + 28 + 3] = q[3] * 127.5 + 127.5;
    }

    // --- SH
    if (data.shN) {
        const coeffs = data.shN.bands ? (data.shN.bands + 1) ** 2 - 1 : data.shN.shape[1] / 3; // 3 components per coeff
        const shDegree = data.shN.bands !== undefined && data.shN.bands !== null ? data.shN.bands : Math.round(Math.sqrt(coeffs + 1) - 1);
        const shCentroids = imageDataArrays[5].bits;
        const shLabelsData = imageDataArrays[6].bits;
        const shCentroidsWidth = imageDataArrays[5].width;

        const shComponentCount = coeffs * 3;

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Re-export the SOG asset with the official SOG encoder so quats bytes use the 252+mode packing.
  2. Check the quats webp was not re-compressed or color-converted after generation; keep it byte-exact (it is data, not a picture).
  3. Sanity-check quats data before parsing: every 4th byte per splat must be 252..255.

Example fix

// before (encoder wrote raw quaternion component)
quats[i * 4 + 3] = Math.round(w * 127.5 + 127.5);
// after (encoder packs the omitted-component mode)
quats[i * 4 + 3] = 252 + omittedComponentIndex; // 252..255
Defensive patterns

Strategy: validation

Validate before calling

function validateQuatModes(quatsBits, splatCount) {
  for (let i = 0; i < splatCount; i++) {
    const modeByte = quatsBits[i * 4 + 3];
    if (modeByte < 252 || modeByte > 255) {
      throw new Error(`quats byte ${i * 4 + 3} = ${modeByte}; expected 252..255 (mode channel)`);
    }
  }
}

Type guard

function hasValidQuatModeBytes(quatsBits, splatCount) {
  return Array.from({ length: Math.min(splatCount, 64) }, (_, i) => quatsBits[i * 4 + 3])
    .every((b) => b >= 252 && b <= 255);
}

Try / catch

try {
  return await ParseSogMeta(metaOrFiles, rootUrl, scene);
} catch (e) {
  if (e.message === 'Invalid quaternion mode') {
    // quats webp is corrupt or from an incompatible encoder: re-export the asset
  }
  throw e;
}

Prevention

When it happens

Trigger: Parsing a SOG bundle whose quats webp was produced by an incompatible encoder, re-encoded/re-compressed with lossy processing that changed byte 4, or where raw (non-252-offset) quaternion values were written instead of the packed mode representation.

Common situations: Third-party SOG files that store quaternions differently (e.g. full 4-component encoding); image pipeline re-compression or color-profile conversion altering pixel values; truncated/corrupted webp decode results.

Related errors


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