diff --git a/src/framework/parsers/gsplat-resource.js b/src/framework/parsers/gsplat-resource.js index d462b29118d..4a6cf86154d 100644 --- a/src/framework/parsers/gsplat-resource.js +++ b/src/framework/parsers/gsplat-resource.js @@ -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() { diff --git a/src/framework/parsers/ply.js b/src/framework/parsers/ply.js index 682d7e6fd83..a536f8414f9 100644 --- a/src/framework/parsers/ply.js +++ b/src/framework/parsers/ply.js @@ -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; @@ -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 @@ -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; @@ -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; @@ -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]; @@ -391,7 +395,7 @@ const readGeneralPly = async (streamBuf, elements, littleEndian) => { * * @param {ReadableStreamDefaultReader} reader - The reader. * @param {Function|null} propertyFilter - Function to filter properties with. - * @returns {Promise} The ply file data. + * @returns {Promise<{ data: GSplatData | GSplatCompressedData, comments: string[] }>} The ply file data. */ const readPly = async (reader, propertyFilter = null) => { /** @@ -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'); } @@ -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 @@ -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);