mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-09 01:20:09 +00:00
## Summary integrated sparkjs https://sparkjs.dev/, built by [world labs ](https://www.worldlabs.ai/) to support 3dgs. - Add 3D Gaussian Splatting (3DGS) support using @sparkjsdev/spark library - Add PLY file format support with multiple rendering engines - Support new file formats: `.ply`, `.spz`, `.splat`, `.ksplat` - Add PLY Engine setting with three options: `threejs` (mesh), `fastply` (optimized ASCII point clouds), `sparkjs` (3DGS) - Add `FastPLYLoader` for 4-5x faster ASCII PLY parsing - Add `original(Advanced)` material mode for point cloud rendering with THREE.Points 3dgs generated by https://marble.worldlabs.ai/ test ply file from: 1. made by https://github.com/PozzettiAndrea/ComfyUI-DepthAnythingV3 2. threejs offically repo ## Screenshots https://github.com/user-attachments/assets/44e64d3e-b58d-4341-9a70-a9aa64801220 https://github.com/user-attachments/assets/76b0dfba-0c12-4f64-91cb-bfc5d672294d https://github.com/user-attachments/assets/2a8bfe81-1fb2-44c4-8787-dff325369c61 https://github.com/user-attachments/assets/e4beecee-d7a2-40c9-97f7-79b09c60312d ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7602-3dgs-ply-support-2cd6d73d3650814098fcea86cfaf747d) by [Unito](https://www.unito.io)
154 lines
3.7 KiB
TypeScript
154 lines
3.7 KiB
TypeScript
/**
|
|
* PLY (Polygon File Format) decoder
|
|
* Parses ASCII PLY files and extracts vertex positions and colors
|
|
*/
|
|
|
|
interface PLYHeader {
|
|
vertexCount: number
|
|
hasColor: boolean
|
|
propertyIndices: {
|
|
x: number
|
|
y: number
|
|
z: number
|
|
red: number
|
|
green: number
|
|
blue: number
|
|
}
|
|
headerEndLine: number
|
|
}
|
|
|
|
interface PLYData {
|
|
positions: Float32Array
|
|
colors: Float32Array | null
|
|
vertexCount: number
|
|
}
|
|
|
|
function parsePLYHeader(lines: string[]): PLYHeader | null {
|
|
let vertexCount = 0
|
|
let headerEndLine = 0
|
|
let hasColor = false
|
|
let xIndex = -1
|
|
let yIndex = -1
|
|
let zIndex = -1
|
|
let redIndex = -1
|
|
let greenIndex = -1
|
|
let blueIndex = -1
|
|
let propertyIndex = 0
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i].trim()
|
|
|
|
if (line.startsWith('element vertex')) {
|
|
vertexCount = parseInt(line.split(/\s+/)[2])
|
|
} else if (line.startsWith('property')) {
|
|
const parts = line.split(/\s+/)
|
|
const propName = parts[parts.length - 1]
|
|
|
|
if (propName === 'x') xIndex = propertyIndex
|
|
else if (propName === 'y') yIndex = propertyIndex
|
|
else if (propName === 'z') zIndex = propertyIndex
|
|
else if (propName === 'red') {
|
|
hasColor = true
|
|
redIndex = propertyIndex
|
|
} else if (propName === 'green') greenIndex = propertyIndex
|
|
else if (propName === 'blue') blueIndex = propertyIndex
|
|
|
|
propertyIndex++
|
|
} else if (line === 'end_header') {
|
|
headerEndLine = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if (vertexCount === 0 || xIndex < 0 || yIndex < 0 || zIndex < 0) {
|
|
return null
|
|
}
|
|
|
|
return {
|
|
vertexCount,
|
|
hasColor,
|
|
propertyIndices: {
|
|
x: xIndex,
|
|
y: yIndex,
|
|
z: zIndex,
|
|
red: redIndex,
|
|
green: greenIndex,
|
|
blue: blueIndex
|
|
},
|
|
headerEndLine
|
|
}
|
|
}
|
|
|
|
function parsePLYVertices(lines: string[], header: PLYHeader): PLYData {
|
|
const { vertexCount, hasColor, propertyIndices, headerEndLine } = header
|
|
const { x: xIndex, y: yIndex, z: zIndex } = propertyIndices
|
|
const { red: redIndex, green: greenIndex, blue: blueIndex } = propertyIndices
|
|
|
|
const positions = new Float32Array(vertexCount * 3)
|
|
const colors = hasColor ? new Float32Array(vertexCount * 3) : null
|
|
|
|
let vertexIndex = 0
|
|
|
|
for (
|
|
let i = headerEndLine + 1;
|
|
i < lines.length && vertexIndex < vertexCount;
|
|
i++
|
|
) {
|
|
const line = lines[i].trim()
|
|
if (!line) continue
|
|
|
|
const parts = line.split(/\s+/)
|
|
if (parts.length < 3) continue
|
|
|
|
const posIndex = vertexIndex * 3
|
|
|
|
positions[posIndex] = parseFloat(parts[xIndex])
|
|
positions[posIndex + 1] = parseFloat(parts[yIndex])
|
|
positions[posIndex + 2] = parseFloat(parts[zIndex])
|
|
|
|
if (
|
|
hasColor &&
|
|
colors &&
|
|
redIndex >= 0 &&
|
|
greenIndex >= 0 &&
|
|
blueIndex >= 0
|
|
) {
|
|
if (parts.length > Math.max(redIndex, greenIndex, blueIndex)) {
|
|
colors[posIndex] = parseInt(parts[redIndex]) / 255
|
|
colors[posIndex + 1] = parseInt(parts[greenIndex]) / 255
|
|
colors[posIndex + 2] = parseInt(parts[blueIndex]) / 255
|
|
}
|
|
}
|
|
|
|
vertexIndex++
|
|
}
|
|
|
|
return {
|
|
positions,
|
|
colors,
|
|
vertexCount: vertexIndex
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse ASCII PLY data from an ArrayBuffer
|
|
* Returns positions and colors as typed arrays
|
|
*/
|
|
export function parseASCIIPLY(arrayBuffer: ArrayBuffer): PLYData | null {
|
|
const text = new TextDecoder().decode(arrayBuffer)
|
|
const lines = text.split('\n')
|
|
|
|
const header = parsePLYHeader(lines)
|
|
if (!header) return null
|
|
|
|
return parsePLYVertices(lines, header)
|
|
}
|
|
|
|
/**
|
|
* Check if PLY data is in ASCII format
|
|
*/
|
|
export function isPLYAsciiFormat(arrayBuffer: ArrayBuffer): boolean {
|
|
const header = new TextDecoder().decode(arrayBuffer.slice(0, 500))
|
|
return header.includes('format ascii')
|
|
}
|