UNPKG

itowns

Version:

A JS/WebGL framework for 3D geospatial data visualization

131 lines (124 loc) 5.25 kB
import * as THREE from 'three'; import C3DTBatchTable from "../Core/3DTiles/C3DTBatchTable.js"; const utf8Decoder = new TextDecoder(); export default { /** @module PntsParser */ /** Parse pnts buffer and extract THREE.Points and batch table * @function parse * @param {ArrayBuffer} buffer - the pnts buffer. * @param {Object} registeredExtensions - 3D Tiles extensions registered * in the layer * @return {Promise} - a promise that resolves with an object containig a THREE.Points (point) and a batch table (batchTable). * */ parse: function (buffer, registeredExtensions) { if (!buffer) { throw new Error('No array buffer provided.'); } const view = new DataView(buffer); let byteOffset = 0; const pntsHeader = {}; let batchTable = {}; let point = {}; // Magic type is unsigned char [4] pntsHeader.magic = utf8Decoder.decode(new Uint8Array(buffer, byteOffset, 4)); byteOffset += 4; if (pntsHeader.magic) { // Version, byteLength, batchTableJSONByteLength, batchTableBinaryByteLength and batchTable types are uint32 pntsHeader.version = view.getUint32(byteOffset, true); byteOffset += Uint32Array.BYTES_PER_ELEMENT; pntsHeader.byteLength = view.getUint32(byteOffset, true); byteOffset += Uint32Array.BYTES_PER_ELEMENT; pntsHeader.FTJSONLength = view.getUint32(byteOffset, true); byteOffset += Uint32Array.BYTES_PER_ELEMENT; pntsHeader.FTBinaryLength = view.getUint32(byteOffset, true); byteOffset += Uint32Array.BYTES_PER_ELEMENT; pntsHeader.BTJSONLength = view.getUint32(byteOffset, true); byteOffset += Uint32Array.BYTES_PER_ELEMENT; pntsHeader.BTBinaryLength = view.getUint32(byteOffset, true); byteOffset += Uint32Array.BYTES_PER_ELEMENT; // feature table let FTJSON = {}; if (pntsHeader.FTJSONLength > 0) { const sizeBegin = byteOffset; const jsonBuffer = buffer.slice(sizeBegin, pntsHeader.FTJSONLength + sizeBegin); const content = utf8Decoder.decode(new Uint8Array(jsonBuffer)); FTJSON = JSON.parse(content); } // binary table if (pntsHeader.FTBinaryLength > 0) { point = parseFeatureBinary(buffer, byteOffset, pntsHeader.FTJSONLength); } // batch table if (pntsHeader.BTJSONLength > 0) { // parse batch table const sizeBegin = byteOffset + pntsHeader.FTJSONLength + pntsHeader.FTBinaryLength; const BTBuffer = buffer.slice(sizeBegin, pntsHeader.BTJSONLength + pntsHeader.BTBinaryLength + sizeBegin); // If the BATCH_ID semantic is not defined, then the Batch Table stores per-point metadata, and the length of the Batch Table arrays will equal POINTS_LENGTH. batchTable = new C3DTBatchTable(BTBuffer, pntsHeader.BTJSONLength, pntsHeader.BTBinaryLength, FTJSON.BATCH_ID && FTJSON.BATCH_LENGTH ? FTJSON.BATCH_LENGTH : FTJSON.POINTS_LENGTH, registeredExtensions); point = setClassification(point, batchTable); } const pnts = { point, batchTable }; return Promise.resolve(pnts); } else { throw new Error('Invalid pnts file.'); } } }; function parseFeatureBinary(array, byteOffset, FTJSONLength) { // Init geometry const geometry = new THREE.BufferGeometry(); // init Array feature binary const subArrayJson = utf8Decoder.decode(new Uint8Array(array, byteOffset, FTJSONLength)); const parseJSON = JSON.parse(subArrayJson); let lengthFeature; if (parseJSON.POINTS_LENGTH) { lengthFeature = parseJSON.POINTS_LENGTH; } if (parseJSON.POSITION) { const byteOffsetPos = parseJSON.POSITION.byteOffset + subArrayJson.length + byteOffset; const positionArray = new Float32Array(array, byteOffsetPos, lengthFeature * 3); geometry.setAttribute('position', new THREE.BufferAttribute(positionArray, 3)); } if (parseJSON.RGB) { const byteOffsetCol = parseJSON.RGB.byteOffset + subArrayJson.length + byteOffset; const colorArray = new Uint8Array(array, byteOffsetCol, lengthFeature * 3); geometry.setAttribute('color', new THREE.BufferAttribute(colorArray, 3, true)); } if (parseJSON.POSITION_QUANTIZED) { throw new Error('For pnts loader, POSITION_QUANTIZED: not yet managed'); } if (parseJSON.RGBA) { throw new Error('For pnts loader, RGBA: not yet managed'); } if (parseJSON.RGB565) { throw new Error('For pnts loader, RGB565: not yet managed'); } if (parseJSON.NORMAL) { throw new Error('For pnts loader, NORMAL: not yet managed'); } if (parseJSON.NORMAL_OCT16P) { throw new Error('For pnts loader, NORMAL_OCT16P: not yet managed'); } if (parseJSON.BATCH_ID) { throw new Error('For pnts loader, BATCH_ID: not yet managed'); } // Add RTC feature const offset = parseJSON.RTC_CENTER ? new THREE.Vector3().fromArray(parseJSON.RTC_CENTER) : undefined; return { geometry, offset }; } function setClassification(point, batchTable) { if (!point.geometry) { return; } if (batchTable.content && batchTable.content.Classification) { point.geometry.setAttribute('classification', new THREE.BufferAttribute(new Uint8Array(batchTable.content.Classification), 1)); } return point; }