Skip to content
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

Loader for PCD files #8043

Merged
merged 1 commit into from
Feb 1, 2016
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
84 changes: 84 additions & 0 deletions docs/api/loaders/PCDLoader.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<base href="../../" />
<script src="list.js"></script>
<script src="page.js"></script>
<link type="text/css" rel="stylesheet" href="page.css" />
</head>
<body>

<h1>[name]</h1>

<div class="desc">A loader for <em>PCD</em> files. Loads ascii and binary. Compressed binary files are not suported.</div>


<h2>Constructor</h2>

<h3>[name]( [page:LoadingManager manager] )</h3>
<div>
[page:LoadingManager manager] — The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].
</div>
<div>
Creates a new [name].
</div>

<h2>Properties</h2>

<h3>[page:Boolean littleEndian]</h3>
<div>
Default value is true.
</div>

<h2>Methods</h2>

<h3>[method:null load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )</h3>
<div>
[page:String url] — required<br />
[page:Function onLoad] — Will be called when load completes. The argument will be the loaded [page:Object3D].<br />
[page:Function onProgress] — Will be called while load progresses. The argument will be the XmlHttpRequest instance, that contain .[page:Integer total] and .[page:Integer loaded] bytes.<br />
[page:Function onError] — Will be called when load errors.<br />
</div>
<div>
Begin loading from url and call onLoad with the parsed response content.
</div>

<h3>[method:Object3D parse]( [page:Arraybuffer data],[page:String url] )</h3>
<div>
[page:Arraybuffer data] — The binary structure to parse.
</div>
<div>
[page:String url] — The file name or file url.
</div>
<div>
Parse an <em>pcd</em> binary structure and return an [page:Object3D].<br />
The object is converted to [page:Points] with a [page:BufferGeometry] and a [page:PointsMaterial].
</div>

<h2>Example</h2>

<code>

// instantiate a loader
var loader = new THREE.PCDLoader();

// load a resource
loader.load(
// resource URL
'pointcloud.pcd' ,
// Function when resource is loaded
function ( mesh ) {
scene.add( mesh );
}
);
</code>

[example:webgl_loader_pcd]


<h2>Source</h2>

[link:https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/PCDLoader.js examples/js/loaders/PCDLoader.js]
</body>
</html>
251 changes: 251 additions & 0 deletions examples/js/loaders/PCDLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
/**
* @author Filipe Caixeta / http://filipecaixeta.com.br
*
* Description: A THREE loader for PCD ascii and binary files.
*
* Limitations: Compressed binary files are not supported.
*
*/

THREE.PCDLoader = function( manager ) {

this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
this.littleEndian = true;

};
THREE.PCDLoader.prototype = {

constructor: THREE.PCDLoader,

load: function( url, onLoad, onProgress, onError ) {

var scope = this;

var loader = new THREE.XHRLoader( scope.manager );
loader.setResponseType( 'arraybuffer' );
loader.load( url, function( data ) {

onLoad( scope.parse( data, url ) );

}, onProgress, onError );

},

binarryToStr: function( data ) {

text = "";
var charArray = new Uint8Array( data );
for ( var i = 0; i < data.byteLength; i ++ ) {

text += String.fromCharCode( charArray[ i ] );

}
return text;

},

parseHeader: function( data ) {

var PCDheader = {};
var result1 = data.search( /[\r\n]DATA\s(\S*)\s/i );
var result2 = /[\r\n]DATA\s(\S*)\s/i.exec( data.substr( result1 - 1 ) );
PCDheader.data = result2[ 1 ];
PCDheader.headerLen = result2[ 0 ].length + result1;
PCDheader.str = data.substr( 0, PCDheader.headerLen );
// Remove comments
PCDheader.str = PCDheader.str.replace( /\#.*/gi, "" );
PCDheader.version = /VERSION (.*)/i.exec( PCDheader.str );
if ( PCDheader.version != null )
PCDheader.version = parseFloat( PCDheader.version[ 1 ] );
PCDheader.fields = /FIELDS (.*)/i.exec( PCDheader.str );
if ( PCDheader.fields != null )
PCDheader.fields = PCDheader.fields[ 1 ].split( " " );
PCDheader.size = /SIZE (.*)/i.exec( PCDheader.str );
if ( PCDheader.size != null )
PCDheader.size = PCDheader.size[ 1 ].split( " " ).map( function( x ) {

return parseInt( x, 10 );

} );
PCDheader.type = /TYPE (.*)/i.exec( PCDheader.str );
if ( PCDheader.type != null )
PCDheader.type = PCDheader.type[ 1 ].split( " " );
PCDheader.count = /COUNT (.*)/i.exec( PCDheader.str );
if ( PCDheader.count != null )
PCDheader.count = PCDheader.count[ 1 ].split( " " ).map( function( x ) {

return parseInt( x, 10 );

} );
PCDheader.width = /WIDTH (.*)/i.exec( PCDheader.str );
if ( PCDheader.width != null )
PCDheader.width = parseInt( PCDheader.width[ 1 ] );
PCDheader.height = /HEIGHT (.*)/i.exec( PCDheader.str );
if ( PCDheader.height != null )
PCDheader.height = parseInt( PCDheader.height[ 1 ] );
PCDheader.viewpoint = /VIEWPOINT (.*)/i.exec( PCDheader.str );
if ( PCDheader.viewpoint != null )
PCDheader.viewpoint = PCDheader.viewpoint[ 1 ];
PCDheader.points = /POINTS (.*)/i.exec( PCDheader.str );
if ( PCDheader.points != null )
PCDheader.points = parseInt( PCDheader.points[ 1 ], 10 );
if ( PCDheader.points == null )
PCDheader.points = PCDheader.width * PCDheader.height;

if ( PCDheader.count == null ) {

PCDheader.count = [];
for ( var i = 0; i < PCDheader.fields; i ++ )
PCDheader.count.push( 1 );

}

PCDheader.offset = {}
var sizeSum = 0;
for ( var i = 0; i < PCDheader.fields.length; i ++ ) {

if ( PCDheader.data == "ascii" ) {

PCDheader.offset[ PCDheader.fields[ i ]] = i;

} else {

PCDheader.offset[ PCDheader.fields[ i ]] = sizeSum;
sizeSum += PCDheader.size[ i ];

}

}
// For binary only
PCDheader.rowSize = sizeSum;

return PCDheader;

},

parse: function( data, url ) {

textData = this.binarryToStr( data );

// Parse the header
// Header is always ascii format
var PCDheader = this.parseHeader( textData );

// Parse the data
var position = false;
if ( PCDheader.offset.x != undefined )
position = new Float32Array( PCDheader.points * 3 );
var color = false;
if ( PCDheader.offset.rgb != undefined)
color = new Float32Array( PCDheader.points * 3 );
var normal = false;
if ( PCDheader.offset.normal_x != undefined )
normal = new Float32Array( PCDheader.points * 3 );

if ( PCDheader.data == "ascii" ) {

var offset = PCDheader.offset;
var pcdData = textData.substr( PCDheader.headerLen );
var lines = pcdData.split( '\n' );
var i3 = 0;
for ( var i = 0; i < lines.length; i ++, i3 += 3 ) {

var line = lines[ i ].split( " " );
if ( offset.x != undefined ) {

position[ i3 + 0 ] = parseFloat( line[ offset.x ] );
position[ i3 + 1 ] = parseFloat( line[ offset.y ] );
position[ i3 + 2 ] = parseFloat( line[ offset.z ] );

}
if ( offset.rgb != undefined ) {

var c = new Float32Array([parseFloat( line[ offset.rgb ] )]);
var dataview = new DataView( c.buffer, 0 );
color[ i3 + 0 ] = dataview.getUint8(0)/255.0;
color[ i3 + 1 ] = dataview.getUint8(1)/255.0;
color[ i3 + 2 ] = dataview.getUint8(2)/255.0;

}
if ( offset.normal_x != undefined ) {

normal[ i3 + 0 ] = parseFloat( line[ offset.normal_x ] );
normal[ i3 + 1 ] = parseFloat( line[ offset.normal_y ] );
normal[ i3 + 2 ] = 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" ) {

var row = 0;
var dataview = new DataView( data, PCDheader.headerLen );
var i = 0;
var offset = PCDheader.offset;
for ( var i3 = 0; i < PCDheader.points; i3 += 3, row += PCDheader.rowSize, i ++ ) {

if ( offset.x != undefined ) {

position[ i3 + 0 ] = dataview.getFloat32( row + offset.x, this.littleEndian );
position[ i3 + 1 ] = dataview.getFloat32( row + offset.y, this.littleEndian );
position[ i3 + 2 ] = dataview.getFloat32( row + offset.z, this.littleEndian );

}
if ( offset.rgb != undefined ) {

color[ i3 + 0 ] = dataview.getUint8( row + offset.rgb + 0 ) / 255.0;
color[ i3 + 1 ] = dataview.getUint8( row + offset.rgb + 1 ) / 255.0;
color[ i3 + 2 ] = dataview.getUint8( row + offset.rgb + 2 ) / 255.0;

}
if ( offset.normal_x != undefined ) {

normal[ i3 + 0 ] = dataview.getFloat32( row + offset.normal_x, this.littleEndian );
normal[ i3 + 1 ] = dataview.getFloat32( row + offset.normal_y, this.littleEndian );
normal[ i3 + 2 ] = dataview.getFloat32( row + offset.normal_z, this.littleEndian );

}

}

}

var geometry = new THREE.BufferGeometry();
if ( position != false )
geometry.addAttribute( 'position', new THREE.BufferAttribute( position, 3 ) );
if ( color != false )
geometry.addAttribute( 'color', new THREE.BufferAttribute( color, 3 ) );
if ( normal != false )
geometry.addAttribute( 'normal', new THREE.BufferAttribute( normal, 3 ) );

geometry.computeBoundingSphere();

var material = new THREE.PointsMaterial( { size: 0.005,
vertexColors: !(color == false) } );
if ( color == false )
material.color.setHex( Math.random() * 0xffffff );

var mesh = new THREE.Points( geometry, material );
var name = url.split( '' ).reverse().join( '' );
name = /([^\/]*)/.exec( name );
name = name[ 1 ].split( '' ).reverse().join( '' );
mesh.name = name;
mesh.PCDheader = PCDheader;

return mesh;

},

};

THREE.EventDispatcher.prototype.apply( THREE.PCDLoader.prototype );
Binary file added examples/models/pcd/Zaghetto.pcd
Binary file not shown.
Loading