Skip to content

Store PLY header comments #7246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/framework/parsers/gsplat-resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,22 @@ class GSplatResource {
*/
splat = null;

/**
* @type {string[] | null}
* @ignore
*/
comments = null;

/**
* @param {GraphicsDevice} device - The graphics device.
* @param {GSplatData} splatData - The splat data.
* @param {string[]} comments - The PLY file header comments
* @ignore
*/
constructor(device, splatData) {
constructor(device, splatData, comments) {
this.device = device;
this.splatData = splatData;
this.comments = comments;
}

destroy() {
Expand Down
75 changes: 43 additions & 32 deletions src/framework/parsers/ply.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,16 @@ class StreamBuf {
// string containing the ply format
const parseHeader = (lines) => {
const elements = [];
const comments = [];
let format;

for (let i = 1; i < lines.length; ++i) {
const words = lines[i].split(' ');

switch (words[0]) {
case 'comment':
comments.push(words.slice(1).join(' '));
break;
case 'format':
format = words[1];
break;
Expand Down Expand Up @@ -196,7 +200,7 @@ const parseHeader = (lines) => {
}
}

return { elements, format };
return { elements, format, comments };
};

// return true if the array of elements references a compressed ply file
Expand Down Expand Up @@ -239,7 +243,7 @@ const isFloatPly = (elements) => {
};

// read the data of a compressed ply file
const readCompressedPly = async (streamBuf, elements, littleEndian) => {
const readCompressedPly = async (streamBuf, elements) => {
const result = new GSplatCompressedData();

const numChunks = elements[0].count;
Expand Down Expand Up @@ -294,7 +298,7 @@ const readCompressedPly = async (streamBuf, elements, littleEndian) => {
};

// read the data of a floating point ply file
const readFloatPly = async (streamBuf, elements, littleEndian) => {
const readFloatPly = async (streamBuf, elements) => {
// calculate the size of an input element record
const element = elements[0];
const properties = element.properties;
Expand Down Expand Up @@ -336,7 +340,7 @@ const readFloatPly = async (streamBuf, elements, littleEndian) => {
return new GSplatData(elements);
};

const readGeneralPly = async (streamBuf, elements, littleEndian) => {
const readGeneralPly = async (streamBuf, elements) => {
// read and deinterleave the data
for (let i = 0; i < elements.length; ++i) {
const element = elements[i];
Expand Down Expand Up @@ -391,7 +395,7 @@ const readGeneralPly = async (streamBuf, elements, littleEndian) => {
*
* @param {ReadableStreamDefaultReader<Uint8Array>} reader - The reader.
* @param {Function|null} propertyFilter - Function to filter properties with.
* @returns {Promise<GSplatData | GSplatCompressedData>} The ply file data.
* @returns {Promise<{ data: GSplatData | GSplatCompressedData, comments: string[] }>} The ply file data.
*/
const readPly = async (reader, propertyFilter = null) => {
/**
Expand Down Expand Up @@ -464,14 +468,13 @@ const readPly = async (reader, propertyFilter = null) => {
// decode buffer header text and split into lines and remove comments
const lines = new TextDecoder('ascii')
.decode(streamBuf.data.subarray(0, headerLength))
.split('\n')
.filter(line => !line.startsWith('comment '));
.split('\n');

// decode header and build element and property list
const { elements, format } = parseHeader(lines);
const { elements, format, comments } = parseHeader(lines);

// check format is supported
if (format !== 'binary_little_endian' && format !== 'binary_big_endian') {
if (format !== 'binary_little_endian') {
throw new Error('Unsupported ply format');
}

Expand All @@ -480,29 +483,36 @@ const readPly = async (reader, propertyFilter = null) => {
streamBuf.head = headerLength + endHeaderBytes.length;
streamBuf.compact();

// load compressed PLY with fast path
if (isCompressedPly(elements)) {
return await readCompressedPly(streamBuf, elements, format === 'binary_little_endian');
}
const readData = async () => {
// load compressed PLY with fast path
if (isCompressedPly(elements)) {
return await readCompressedPly(streamBuf, elements);
}

// allocate element storage
elements.forEach((e) => {
e.properties.forEach((p) => {
const storageType = dataTypeMap.get(p.type);
if (storageType) {
const storage = (!propertyFilter || propertyFilter(p.name)) ? new storageType(e.count) : null;
p.storage = storage;
}
// allocate element storage
elements.forEach((e) => {
e.properties.forEach((p) => {
const storageType = dataTypeMap.get(p.type);
if (storageType) {
const storage = (!propertyFilter || propertyFilter(p.name)) ? new storageType(e.count) : null;
p.storage = storage;
}
});
});
});

// load float32 PLY with fast path
if (isFloatPly(elements)) {
return await readFloatPly(streamBuf, elements, format === 'binary_little_endian');
}
// load float32 PLY with fast path
if (isFloatPly(elements)) {
return await readFloatPly(streamBuf, elements);
}

// fallback, general case
return await readGeneralPly(streamBuf, elements, format === 'binary_little_endian');
// fallback, general case
return await readGeneralPly(streamBuf, elements);
};

return {
data: await readData(),
comments
};
};

// by default load everything
Expand Down Expand Up @@ -543,19 +553,20 @@ class PlyParser {
if (!response || !response.body) {
callback('Error loading resource', null);
} else {
const gsplatData = await readPly(response.body.getReader(), asset.data.elementFilter ?? defaultElementFilter);
const { data, comments } = await readPly(response.body.getReader(), asset.data.elementFilter ?? defaultElementFilter);

// reorder data
if (!gsplatData.isCompressed) {
if (!data.isCompressed) {
if (asset.data.reorder ?? true) {
gsplatData.reorderData();
data.reorderData();
}
}

// construct the resource
const resource = new GSplatResource(
this.device,
gsplatData.isCompressed && asset.data.decompress ? gsplatData.decompress() : gsplatData
data.isCompressed && asset.data.decompress ? data.decompress() : data,
comments
);

callback(null, resource);
Expand Down
Loading