{"record":{"id":"dc9f9a4502a9eb08","repo":"BabylonJS/Babylon.js","slug":"missing-codebook-in-sog-version-2-shn-data","errorCode":null,"errorMessage":"Missing codebook in SOG version 2 shN data.","messagePattern":"Missing codebook in SOG version 2 shN data\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/dev/loaders/src/SPLAT/sog.pure.ts","lineNumber":320,"sourceCode":"        const shCentroids = imageDataArrays[5].bits;\r\n        const shLabelsData = imageDataArrays[6].bits;\r\n        const shCentroidsWidth = imageDataArrays[5].width;\r\n\r\n        const shComponentCount = coeffs * 3;\r\n\r\n        const textureCount = Math.ceil(shComponentCount / 16); // 4 components can be stored per texture, 4 sh per component\r\n        //let shIndexRead = byteOffset;\r\n\r\n        const engine = scene.getEngine();\r\n        const width = engine.getCaps().maxTextureSize;\r\n        const height = Math.ceil(splatCount / width);\r\n\r\n        // sh is an array of uint8array that will be used to create sh textures\r\n        const sh = AllocateShBuffers(textureCount, height * width * 4 * 4);\r\n\r\n        if (data.version === 2) {\r\n            if (!data.shN.codebook) {\r\n                throw new Error(\"Missing codebook in SOG version 2 shN data.\");\r\n            }\r\n\r\n            for (let i = 0; i < splatCount; i++) {\r\n                const n = shLabelsData[i * 4 + 0] + (shLabelsData[i * 4 + 1] << 8);\r\n                const u = (n % 64) * coeffs;\r\n                const v = Math.floor(n / 64);\r\n\r\n                for (let k = 0; k < coeffs; k++) {\r\n                    for (let j = 0; j < 3; j++) {\r\n                        const shIndexWrite = k * 3 + j;\r\n                        const textureIndex = Math.floor(shIndexWrite / 16);\r\n                        const shArray = sh[textureIndex];\r\n                        const byteIndexInTexture = shIndexWrite % 16; // [0..15]\r\n                        const offsetPerSplat = i * 16; // 16 sh values per texture per splat.\r\n\r\n                        const shValue = data.shN.codebook[shCentroids[(u + k) * 4 + j + v * shCentroidsWidth * 4]] * 127.5 + 127.5;\r\n                        shArray[byteIndexInTexture + offsetPerSplat] = Math.max(0, Math.min(255, shValue));\r\n                    }\r","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/loaders/src/SPLAT/sog.pure.ts#L302-L338","documentation":"When meta.json declares shN (higher-order spherical harmonics) and version 2, SH coefficients are palette entries: labels index centroids, and centroids are indices into data.shN.codebook. This error means version is 2 but the shN entry has no codebook, so higher-order SH values cannot be reconstructed. Only reached when data.shN exists in the metadata.","triggerScenarios":"ParseSogMeta with a meta.json containing an shN section and version: 2 but no shN.codebook array — e.g. v1-style shN metadata (which uses scalar mins/maxs) paired with version: 2, or a hand-edited meta.json.","commonSituations":"Assets converted from SOG v1 to v2 by only bumping the version field; encoders that emit shN without the v2 palette; metadata merging tools that keep shN from a different asset version.","solutions":["Regenerate the SOG v2 asset so shN.codebook (256 entries) is written into meta.json.","If shN data is v1-encoded, remove version: 2 (or set 1) so the scalar mins/maxs path is used.","Drop the shN section from meta.json entirely if higher-order SH is not needed (the field is optional).","Pre-validate: if (meta.shN && meta.version === 2) require Array.isArray(meta.shN.codebook)."],"exampleFix":"// before\n\"version\": 2, \"shN\": { \"bands\": 3, \"shape\": [100000, 45], \"dtype\": \"uint8\", \"files\": [\"shN_centroids.webp\", \"shN_labels.webp\"] }\n// after\n\"version\": 2, \"shN\": { \"bands\": 3, \"shape\": [100000, 45], \"dtype\": \"uint8\", \"files\": [\"shN_centroids.webp\", \"shN_labels.webp\"], \"codebook\": [-0.12, -0.09, ...] }","handlingStrategy":"validation","validationCode":"function validateV2ShN(meta) {\n  if (meta.shN && meta.version === 2 && !Array.isArray(meta.shN.codebook)) {\n    throw new Error('v2 SOG asset: meta.shN.codebook is required when shN is present');\n  }\n}","typeGuard":"function hasValidShN(meta) {\n  return !meta.shN || meta.version !== 2 || Array.isArray(meta.shN.codebook);\n}","tryCatchPattern":"try {\n  return await ParseSogMeta(metaOrFiles, rootUrl, scene);\n} catch (e) {\n  if (e.message.includes('shN')) {\n    // shN palette missing: remove shN from meta.json (optional field) or re-export as proper v2\n  }\n  throw e;\n}","preventionTips":["Remember shN is optional — drop it from meta.json if your pipeline doesn't produce v2 palettes.","When converting v1 -> v2 assets, regenerate the shN codebook rather than only changing the version field.","Include shN in your meta.json schema validation alongside scales and sh0."],"tags":["sog","gaussian-splatting","codebook","spherical-harmonics","asset-validation"],"backgroundTag":"missing-codebook-in-quantized-data","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}