Skip to content

Commit

Permalink
allow importing polycam ply format (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
dylanebert authored Nov 17, 2023
1 parent d49ba84 commit f69a0c4
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 18 deletions.
13 changes: 10 additions & 3 deletions examples/file-loader/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,16 @@ async function selectFile(file: File) {
console.log("Loading SPLAT file: " + progress);
});
} else if (file.name.endsWith(".ply")) {
await SPLAT.PLYLoader.LoadFromFileAsync(file, scene, (progress: number) => {
console.log("Loading PLY file: " + progress);
});
const format = "";
// const format = "polycam"; // Uncomment to load a Polycam PLY file
await SPLAT.PLYLoader.LoadFromFileAsync(
file,
scene,
(progress: number) => {
console.log("Loading PLY file: " + progress);
},
format,
);
}
loading = false;
}
Expand Down
16 changes: 12 additions & 4 deletions examples/ply-converter/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ const scene = new SPLAT.Scene();
const camera = new SPLAT.Camera();
const controls = new SPLAT.OrbitControls(camera, canvas);

const format = "";
// const format = "polycam"; // Uncomment to use polycam format

async function main() {
// Load and convert ply from url
const url =
"https://huggingface.co/datasets/dylanebert/3dgs/resolve/main/bonsai/point_cloud/iteration_7000/point_cloud.ply";
await SPLAT.PLYLoader.LoadAsync(url, scene, (progress) => (progressIndicator.value = progress * 100));
await SPLAT.PLYLoader.LoadAsync(url, scene, (progress) => (progressIndicator.value = progress * 100), format);
progressDialog.close();
scene.saveToFile("bonsai.splat");

Expand All @@ -37,9 +40,14 @@ async function main() {
progressIndicator.value = progress * 100;
});
} else if (file.name.endsWith(".ply")) {
await SPLAT.PLYLoader.LoadFromFileAsync(file, scene, (progress: number) => {
progressIndicator.value = progress * 100;
});
await SPLAT.PLYLoader.LoadFromFileAsync(
file,
scene,
(progress: number) => {
progressIndicator.value = progress * 100;
},
format,
);
}
scene.saveToFile(file.name.replace(".ply", ".splat"));
loading = false;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gsplat",
"version": "0.2.9",
"version": "0.2.10",
"description": "JavaScript Gaussian Splatting library",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
50 changes: 40 additions & 10 deletions src/loaders/PLYLoader.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { Scene } from "../core/Scene";
import { Vector3 } from "../math/Vector3";
import { Quaternion } from "../math/Quaternion";

class PLYLoader {
static SH_C0 = 0.28209479177387814;

static async LoadAsync(url: string, scene: Scene, onProgress?: (progress: number) => void): Promise<void> {
static async LoadAsync(
url: string,
scene: Scene,
onProgress?: (progress: number) => void,
format: string = "",
): Promise<void> {
const req = await fetch(url, {
mode: "cors",
credentials: "omit",
Expand Down Expand Up @@ -34,14 +41,19 @@ class PLYLoader {
throw new Error("Invalid PLY file");
}

const data = new Uint8Array(this._ParsePLYBuffer(plyData.buffer));
const data = new Uint8Array(this._ParsePLYBuffer(plyData.buffer, format));
scene.setData(data);
}

static async LoadFromFileAsync(file: File, scene: Scene, onProgress?: (progress: number) => void): Promise<void> {
static async LoadFromFileAsync(
file: File,
scene: Scene,
onProgress?: (progress: number) => void,
format: string = "",
): Promise<void> {
const reader = new FileReader();
reader.onload = (e) => {
const data = new Uint8Array(this._ParsePLYBuffer(e.target!.result as ArrayBuffer));
const data = new Uint8Array(this._ParsePLYBuffer(e.target!.result as ArrayBuffer, format));
scene.setData(data);
};
reader.onprogress = (e) => {
Expand All @@ -55,7 +67,7 @@ class PLYLoader {
});
}

private static _ParsePLYBuffer(inputBuffer: ArrayBuffer): ArrayBuffer {
private static _ParsePLYBuffer(inputBuffer: ArrayBuffer, format: string): ArrayBuffer {
type PlyProperty = {
name: string;
type: string;
Expand Down Expand Up @@ -96,6 +108,8 @@ class PLYLoader {
const dataView = new DataView(inputBuffer, header_end_index + header_end.length);
const buffer = new ArrayBuffer(Scene.RowLength * vertexCount);

const q_polycam = Quaternion.FromEuler(new Vector3(Math.PI / 2, 0, 0));

for (let i = 0; i < vertexCount; i++) {
const position = new Float32Array(buffer, i * Scene.RowLength, 3);
const scale = new Float32Array(buffer, i * Scene.RowLength + 12, 3);
Expand Down Expand Up @@ -178,11 +192,27 @@ class PLYLoader {
}
});

const qlen = r0 * r0 + r1 * r1 + r2 * r2 + r3 * r3;
rot[0] = (r0 / qlen) * 128 + 128;
rot[1] = (r1 / qlen) * 128 + 128;
rot[2] = (r2 / qlen) * 128 + 128;
rot[3] = (r3 / qlen) * 128 + 128;
let q = new Quaternion(r1, r2, r3, r0);

switch (format) {
case "polycam": {
const temp = position[1];
position[1] = -position[2];
position[2] = temp;
q = q_polycam.multiply(q);
break;
}
case "":
break;
default:
throw new Error(`Unsupported format: ${format}`);
}

q = q.normalize();
rot[0] = q.w * 128 + 128;
rot[1] = q.x * 128 + 128;
rot[2] = q.y * 128 + 128;
rot[3] = q.z * 128 + 128;
}

return buffer;
Expand Down

0 comments on commit f69a0c4

Please sign in to comment.