Skip to content

Commit

Permalink
Store PLY header comments (playcanvas#7246)
Browse files Browse the repository at this point in the history
  • Loading branch information
slimbuck authored Jan 3, 2025
1 parent 456f872 commit c1034c3
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 33 deletions.
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

0 comments on commit c1034c3

Please sign in to comment.