import { Quaternion } from "../math/Quaternion"; class Converter { public static SH_C0 = 0.28209479177387814; public static SplatToPLY(buffer: ArrayBuffer, vertexCount: number): ArrayBuffer { let header = "ply\nformat binary_little_endian 1.0\n"; header += `element vertex ${vertexCount}\n`; const properties = ["x", "y", "z", "nx", "ny", "nz", "f_dc_0", "f_dc_1", "f_dc_2"]; for (let i = 0; i < 45; i++) { properties.push(`f_rest_${i}`); } properties.push("opacity"); properties.push("scale_0"); properties.push("scale_1"); properties.push("scale_2"); properties.push("rot_0"); properties.push("rot_1"); properties.push("rot_2"); properties.push("rot_3"); for (const property of properties) { header += `property float ${property}\n`; } header += "end_header\n"; const headerBuffer = new TextEncoder().encode(header); const plyRowLength = 4 * 3 + 4 * 3 + 4 * 3 + 4 * 45 + 4 + 4 * 3 + 4 * 4; const plyLength = vertexCount * plyRowLength; const output = new DataView(new ArrayBuffer(headerBuffer.length + plyLength)); new Uint8Array(output.buffer).set(headerBuffer, 0); const f_buffer = new Float32Array(buffer); const u_buffer = new Uint8Array(buffer); const offset = headerBuffer.length; const f_dc_offset = 4 * 3 + 4 * 3; const opacity_offset = f_dc_offset + 4 * 3 + 4 * 45; const scale_offset = opacity_offset + 4; const rot_offset = scale_offset + 4 * 3; for (let i = 0; i < vertexCount; i++) { const pos0 = f_buffer[8 * i + 0]; const pos1 = f_buffer[8 * i + 1]; const pos2 = f_buffer[8 * i + 2]; const f_dc_0 = (u_buffer[32 * i + 24 + 0] / 255 - 0.5) / this.SH_C0; const f_dc_1 = (u_buffer[32 * i + 24 + 1] / 255 - 0.5) / this.SH_C0; const f_dc_2 = (u_buffer[32 * i + 24 + 2] / 255 - 0.5) / this.SH_C0; const alpha = u_buffer[32 * i + 24 + 3] / 255; const opacity = Math.log(alpha / (1 - alpha)); const scale0 = Math.log(f_buffer[8 * i + 3 + 0]); const scale1 = Math.log(f_buffer[8 * i + 3 + 1]); const scale2 = Math.log(f_buffer[8 * i + 3 + 2]); let q = new Quaternion( (u_buffer[32 * i + 28 + 1] - 128) / 128, (u_buffer[32 * i + 28 + 2] - 128) / 128, (u_buffer[32 * i + 28 + 3] - 128) / 128, (u_buffer[32 * i + 28 + 0] - 128) / 128, ); q = q.normalize(); const rot0 = q.w; const rot1 = q.x; const rot2 = q.y; const rot3 = q.z; output.setFloat32(offset + plyRowLength * i + 0, pos0, true); output.setFloat32(offset + plyRowLength * i + 4, pos1, true); output.setFloat32(offset + plyRowLength * i + 8, pos2, true); output.setFloat32(offset + plyRowLength * i + f_dc_offset + 0, f_dc_0, true); output.setFloat32(offset + plyRowLength * i + f_dc_offset + 4, f_dc_1, true); output.setFloat32(offset + plyRowLength * i + f_dc_offset + 8, f_dc_2, true); output.setFloat32(offset + plyRowLength * i + opacity_offset, opacity, true); output.setFloat32(offset + plyRowLength * i + scale_offset + 0, scale0, true); output.setFloat32(offset + plyRowLength * i + scale_offset + 4, scale1, true); output.setFloat32(offset + plyRowLength * i + scale_offset + 8, scale2, true); output.setFloat32(offset + plyRowLength * i + rot_offset + 0, rot0, true); output.setFloat32(offset + plyRowLength * i + rot_offset + 4, rot1, true); output.setFloat32(offset + plyRowLength * i + rot_offset + 8, rot2, true); output.setFloat32(offset + plyRowLength * i + rot_offset + 12, rot3, true); } return output.buffer; } } export { Converter };