UNPKG

useloadpcd

Version:

A .pcd file loader for React with React hooks and Three.js.

272 lines (219 loc) 7.73 kB
import * as THREE from 'three'; class PCDLoader extends THREE.Loader { public manager?: THREE.LoadingManager; public littleEndian: boolean; public path: string; constructor(manager?: THREE.LoadingManager) { super(); this.manager = manager !== undefined ? manager : THREE.DefaultLoadingManager; this.littleEndian = true; this.path = ''; } public load( url: string, onLoad: (data: any) => void, onProgress: (request: ProgressEvent) => void, onError: (event: ErrorEvent) => void ) { const loader = new THREE.FileLoader(this.manager); loader.setPath(this.path); loader.setResponseType('arraybuffer'); loader.load( url, data => { try { onLoad(this.parse(data, url)); } catch (e) { if (onError) { onError(e); } else { throw e; } } }, onProgress, onError ); } public setPath(value: string) { this.path = value; return this; } public parse(data: any, url: string) { const parseHeader = (headerData: string) => { const header: any = {}; const result1 = headerData.search(/[\r\n]DATA\s(\S*)\s/i); const result2: any = /[\r\n]DATA\s(\S*)\s/i.exec( headerData.substr(result1 - 1) ); header.data = result2[1]; header.headerLen = result2[0].length + result1; header.str = headerData.substr(0, header.headerLen); // remove comments header.str = header.str.replace(/\#.*/gi, ''); // parse header.version = /VERSION (.*)/i.exec(header.str); header.fields = /FIELDS (.*)/i.exec(header.str); header.size = /SIZE (.*)/i.exec(header.str); header.type = /TYPE (.*)/i.exec(header.str); header.count = /COUNT (.*)/i.exec(header.str); header.width = /WIDTH (.*)/i.exec(header.str); header.height = /HEIGHT (.*)/i.exec(header.str); header.viewpoint = /VIEWPOINT (.*)/i.exec(header.str); header.points = /POINTS (.*)/i.exec(header.str); // evaluate if (header.version !== null) { header.version = parseFloat(header.version[1]); } if (header.fields !== null) header.fields = header.fields[1].split(' '); if (header.type !== null) header.type = header.type[1].split(' '); if (header.width !== null) header.width = parseInt(header.width[1], 10); if (header.height !== null) { header.height = parseInt(header.height[1], 10); } if (header.viewpoint !== null) header.viewpoint = header.viewpoint[1]; if (header.points !== null) { header.points = parseInt(header.points[1], 10); } if (header.points === null) header.points = header.width * header.height; if (header.size !== null) { header.size = header.size[1].split(' ').map((x: string) => { return parseInt(x, 10); }); } if (header.count !== null) { header.count = header.count[1].split(' ').map((x: string) => { return parseInt(x, 10); }); } else { header.count = []; for (let iter = 0, l = header.fields.length; iter < l; iter += 1) { header.count.push(1); } } header.offset = {}; let sizeSum = 0; for (let iter = 0, l = header.fields.length; iter < l; iter += 1) { if (header.data === 'ascii') { header.offset[header.fields[iter]] = iter; } else { header.offset[header.fields[iter]] = sizeSum; sizeSum += header.size[iter]; } } // for binary only header.rowSize = sizeSum; return header; }; const textData = THREE.LoaderUtils.decodeText(data); const pcdHeader = parseHeader(textData); const position = []; const normal = []; const color = []; if (pcdHeader.data === 'ascii') { const offset = pcdHeader.offset; const pcdData = textData.substr(pcdHeader.headerLen); const lines = pcdData.split('\n'); for (let iter = 0, l = lines.length; iter < l; iter += 1) { if (lines[iter] === '') continue; const line = lines[iter].split(' '); if (offset.x !== undefined) { position.push(parseFloat(line[offset.x])); position.push(parseFloat(line[offset.y])); position.push(parseFloat(line[offset.z])); } if (offset.rgb !== undefined) { const rgb = parseFloat(line[offset.rgb]); const r = (rgb >> 16) & 0x0000ff; const g = (rgb >> 8) & 0x0000ff; const b = (rgb >> 0) & 0x0000ff; color.push(r / 255, g / 255, b / 255); } if (offset.normal_x !== undefined) { normal.push(parseFloat(line[offset.normal_x])); normal.push(parseFloat(line[offset.normal_y])); normal.push(parseFloat(line[offset.normal_z])); } } } if (pcdHeader.data === 'binary_compressed') { console.error( 'THREE.PCDLoader: binary_compressed files are not supported' ); return; } if (pcdHeader.data === 'binary') { const dataview = new DataView(data, pcdHeader.headerLen); const offset = pcdHeader.offset; for ( let iter = 0, row = 0; iter < pcdHeader.points; iter += 1, row += pcdHeader.rowSize ) { if (offset.x !== undefined) { position.push(dataview.getFloat32(row + offset.x, this.littleEndian)); position.push(dataview.getFloat32(row + offset.y, this.littleEndian)); position.push(dataview.getFloat32(row + offset.z, this.littleEndian)); } if (offset.rgb !== undefined) { color.push(dataview.getUint8(row + offset.rgb + 2) / 255.0); color.push(dataview.getUint8(row + offset.rgb + 1) / 255.0); color.push(dataview.getUint8(row + offset.rgb + 0) / 255.0); } if (offset.normal_x !== undefined) { normal.push( dataview.getFloat32(row + offset.normal_x, this.littleEndian) ); normal.push( dataview.getFloat32(row + offset.normal_y, this.littleEndian) ); normal.push( dataview.getFloat32(row + offset.normal_z, this.littleEndian) ); } } } const geometry = new THREE.BufferGeometry(); if (position.length > 0) { geometry.addAttribute( 'position', new THREE.Float32BufferAttribute(position, 3) ); } if (normal.length > 0) { geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute(normal, 3) ); } if (color.length > 0) { geometry.addAttribute( 'color', new THREE.Float32BufferAttribute(color, 3) ); } geometry.computeBoundingSphere(); // build material const material = new THREE.PointsMaterial({ size: 0.005 }); if (color.length > 0) { material.vertexColors = THREE.VertexColors; } else { material.color.setHex(Math.random() * 0xffffff); } // build mesh const mesh = new THREE.Points(geometry, material); let name: string | RegExpExecArray = url .split('') .reverse() .join(''); name = /([^\/]*)/.exec(name) as RegExpExecArray; name = name[1] .split('') .reverse() .join(''); mesh.name = name; return mesh; } } export default PCDLoader;