Spaces:
Running
Running
Download engine/src/gs/init.js from Mike0021/splat-js-demo: direct link, hf CLI and curl.
- Browser
- Download file 3.44 kB
-
https://huggingface.co/spaces/Mike0021/splat-js-demo/resolve/main/engine/src/gs/init.js
- Command line
-
hf download hf://spaces/Mike0021/splat-js-demo/engine/src/gs/init.js
-
curl -L -o init.js https://huggingface.co/spaces/Mike0021/splat-js-demo/resolve/main/engine/src/gs/init.js
3.44 kB
| // init.js — build initial Gaussian parameters from the sparse SfM point cloud. | |
| // Layout per Gaussian (stride 8): [x, y, z, logScale, r, g, b, logitOpacity] | |
| import { makeRng } from '../sfm/geometry.js'; | |
| const logit = (p) => Math.log(p / (1 - p)); | |
| /** | |
| * points: [{ X: [3], rgb: [3] }] | |
| * clones: extra jittered copies per point (increases capacity without densification) | |
| * Returns { data, n, center, radius } | |
| */ | |
| export function initGaussians(points, clones = 2, maxGaussians = 600000) { | |
| const rng = makeRng(777); | |
| const np = points.length; | |
| // scene bounds (robust-ish: median absolute extent) | |
| const center = [0, 0, 0]; | |
| for (const p of points) { | |
| center[0] += p.X[0]; center[1] += p.X[1]; center[2] += p.X[2]; | |
| } | |
| center[0] /= np; center[1] /= np; center[2] /= np; | |
| const dists = points.map((p) => Math.hypot( | |
| p.X[0] - center[0], p.X[1] - center[1], p.X[2] - center[2])); | |
| const sorted = Float64Array.from(dists).sort(); | |
| const radius = Math.max(1e-3, sorted[Math.min(np - 1, Math.floor(np * 0.9))]); | |
| // nearest-neighbor distance estimate via a hash grid | |
| const cell = radius / 24; | |
| const grid = new Map(); | |
| const key = (x, y, z) => `${Math.floor(x / cell)},${Math.floor(y / cell)},${Math.floor(z / cell)}`; | |
| points.forEach((p, i) => { | |
| const k = key(p.X[0], p.X[1], p.X[2]); | |
| if (!grid.has(k)) grid.set(k, []); | |
| grid.get(k).push(i); | |
| }); | |
| const nnDist = (i) => { | |
| const [x, y, z] = points[i].X; | |
| let best = Infinity; | |
| const cx = Math.floor(x / cell), cy = Math.floor(y / cell), cz = Math.floor(z / cell); | |
| for (let dx = -1; dx <= 1; dx++) | |
| for (let dy = -1; dy <= 1; dy++) | |
| for (let dz = -1; dz <= 1; dz++) { | |
| const list = grid.get(`${cx + dx},${cy + dy},${cz + dz}`); | |
| if (!list) continue; | |
| for (const j of list) { | |
| if (j === i) continue; | |
| const q = points[j].X; | |
| const d2 = (q[0] - x) ** 2 + (q[1] - y) ** 2 + (q[2] - z) ** 2; | |
| if (d2 < best) best = d2; | |
| } | |
| } | |
| return best === Infinity ? cell * 2 : Math.sqrt(best); | |
| }; | |
| // stride-16 layout: [pos3, logScale3, quat4 (w,x,y,z), colorLogit3, | |
| // logitOpacity, pad2] — isotropic identity-rotation init | |
| const STRIDE = 16; | |
| const perPoint = 1 + clones; | |
| const n = Math.min(maxGaussians, np * perPoint); | |
| const data = new Float32Array(n * STRIDE); | |
| const minS = radius * 3e-4, maxS = radius * 0.05; | |
| const lop = logit(0.35); | |
| let g = 0; | |
| for (let i = 0; i < np && g < n; i++) { | |
| const p = points[i]; | |
| const s = Math.min(maxS, Math.max(minS, nnDist(i) * 0.7)); | |
| for (let c = 0; c < perPoint && g < n; c++, g++) { | |
| const jit = c === 0 ? 0 : s * 0.6; | |
| const b = g * STRIDE; | |
| data[b] = p.X[0] + (rng() - 0.5) * 2 * jit; | |
| data[b + 1] = p.X[1] + (rng() - 0.5) * 2 * jit; | |
| data[b + 2] = p.X[2] + (rng() - 0.5) * 2 * jit; | |
| const ls = Math.log(s * (c === 0 ? 1 : 0.8)); | |
| data[b + 3] = ls; | |
| data[b + 4] = ls; | |
| data[b + 5] = ls; | |
| data[b + 6] = 1; // identity quaternion (w,x,y,z) | |
| // colors are stored as logits (sigmoid-activated in the shaders) | |
| data[b + 10] = logit(Math.min(0.98, Math.max(0.02, p.rgb[0]))); | |
| data[b + 11] = logit(Math.min(0.98, Math.max(0.02, p.rgb[1]))); | |
| data[b + 12] = logit(Math.min(0.98, Math.max(0.02, p.rgb[2]))); | |
| data[b + 13] = lop; | |
| } | |
| } | |
| return { data, n: g, center, radius }; | |
| } | |