Fix rSplat viewer: add file extension to URL for format detection

GaussianSplats3D infers format from URL file extension. Changed file
serve route to /api/splats/:id/:filename so URLs end in .splat/.ply/.spz.
Also fixed camera orientation (Y-up) and scale values in test data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-22 00:31:46 +00:00
parent e703405f68
commit 91d600ed93
2 changed files with 20 additions and 13 deletions

View File

@ -263,12 +263,11 @@ export class FolkSplatViewer extends HTMLElement {
try { try {
// Dynamic import from CDN (via importmap) // Dynamic import from CDN (via importmap)
const THREE = await import("three");
const GaussianSplats3D = await import("@mkkellogg/gaussian-splats-3d"); const GaussianSplats3D = await import("@mkkellogg/gaussian-splats-3d");
const viewer = new GaussianSplats3D.Viewer({ const viewer = new GaussianSplats3D.Viewer({
cameraUp: [0, -1, 0], cameraUp: [0, 1, 0],
initialCameraPosition: [1, 0.5, 1], initialCameraPosition: [5, 3, 5],
initialCameraLookAt: [0, 0, 0], initialCameraLookAt: [0, 0, 0],
rootElement: container, rootElement: container,
sharedMemoryForWorkers: false, sharedMemoryForWorkers: false,
@ -276,21 +275,28 @@ export class FolkSplatViewer extends HTMLElement {
this._viewer = viewer; this._viewer = viewer;
await viewer.addSplatScene(this._splatUrl, { viewer.addSplatScene(this._splatUrl, {
showLoadingUI: false, showLoadingUI: false,
progressiveLoad: true, progressiveLoad: true,
}); })
.then(() => {
viewer.start(); viewer.start();
if (loading) loading.classList.add("hidden");
})
.catch((e: Error) => {
console.error("[rSplat] Scene load error:", e);
if (loading) { if (loading) {
loading.classList.add("hidden"); const text = loading.querySelector(".splat-loading__text");
if (text) text.textContent = `Error: ${e.message}`;
const spinner = loading.querySelector(".splat-loading__spinner") as HTMLElement;
if (spinner) spinner.style.display = "none";
} }
});
} catch (e) { } catch (e) {
console.error("[rSplat] Viewer init error:", e); console.error("[rSplat] Viewer init error:", e);
if (loading) { if (loading) {
const text = loading.querySelector(".splat-loading__text"); const text = loading.querySelector(".splat-loading__text");
if (text) text.textContent = `Error loading splat: ${(e as Error).message}`; if (text) text.textContent = `Error loading viewer: ${(e as Error).message}`;
const spinner = loading.querySelector(".splat-loading__spinner") as HTMLElement; const spinner = loading.querySelector(".splat-loading__spinner") as HTMLElement;
if (spinner) spinner.style.display = "none"; if (spinner) spinner.style.display = "none";
} }

View File

@ -143,7 +143,8 @@ routes.get("/api/splats/:id", async (c) => {
}); });
// ── API: Serve splat file ── // ── API: Serve splat file ──
routes.get("/api/splats/:id/file", async (c) => { // Matches both /api/splats/:id/file and /api/splats/:id/:filename (e.g. rainbow-sphere.splat)
routes.get("/api/splats/:id/:filename", async (c) => {
const id = c.req.param("id"); const id = c.req.param("id");
const rows = await sql.unsafe( const rows = await sql.unsafe(
@ -351,7 +352,7 @@ routes.get("/view/:id", async (c) => {
[splat.id] [splat.id]
); );
const fileUrl = `/${spaceSlug}/splat/api/splats/${splat.slug}/file`; const fileUrl = `/${spaceSlug}/splat/api/splats/${splat.slug}/${splat.slug}.${splat.file_format}`;
const html = renderShell({ const html = renderShell({
title: `${splat.title} | rSplat`, title: `${splat.title} | rSplat`,