浏览代码

Merge pull request #8043 from filipecaixeta/dev

Loader for PCD files
Mr.doob 9 年之前
父节点
当前提交
be44e73ad0
共有 4 个文件被更改,包括 501 次插入0 次删除
  1. 84 0
      docs/api/loaders/PCDLoader.html
  2. 251 0
      examples/js/loaders/PCDLoader.js
  3. 二进制
      examples/models/pcd/Zaghetto.pcd
  4. 166 0
      examples/webgl_loader_pcd.html

+ 84 - 0
docs/api/loaders/PCDLoader.html

@@ -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 - 0
examples/js/loaders/PCDLoader.js

@@ -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 );

二进制
examples/models/pcd/Zaghetto.pcd


+ 166 - 0
examples/webgl_loader_pcd.html

@@ -0,0 +1,166 @@
+
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - PCD</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #000000;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display:block;
+
+			}
+
+			a { color: #d14826 }
+			.button { background:#999; color:#eee; padding:0.2em 0.5em; cursor:pointer }
+			.highlight { background:orange; color:#fff; }
+
+			span {
+				display: inline-block;
+				width: 60px;
+				float: left;
+				text-align: center;
+			}
+
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a>
+			<a href="http://pointclouds.org/documentation/tutorials/pcd_file_format.php#pcd-file-format" target="_blank">PCD File format</a>
+			<div>PCD loader test by <a href="http://filipecaixeta.com.br" target="_blank">Filipe Caixeta</a></div>
+			<div>+/-: Increase/Decrease point size</a></div>
+			<div>c: Toggle color</a></div>
+		</div>
+
+
+		<script src="../build/three.min.js"></script>
+		<script src="js/loaders/PCDLoader.js"></script>
+		<script src="js/controls/TrackballControls.js"></script>
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var container, stats;
+			var camera, controls, scene, renderer;
+
+			init();
+			animate();
+			function init() {
+
+				scene = new THREE.Scene();
+
+				camera = new THREE.PerspectiveCamera( 15, window.innerWidth / window.innerHeight, 0.01, 40 );
+				camera.position.x = 0.4;
+				camera.position.z = -2;
+				camera.up.set(0,0,1);
+
+				controls = new THREE.TrackballControls( camera );
+
+				controls.rotateSpeed = 2.0;
+				controls.zoomSpeed = 0.3;
+				controls.panSpeed = 0.2;
+
+				controls.noZoom = false;
+				controls.noPan = false;
+
+				controls.staticMoving = true;
+				controls.dynamicDampingFactor = 0.3;
+
+				controls.minDistance = 0.3;
+				controls.maxDistance = 0.3 * 100;
+
+				scene.add( camera );
+
+				var axisHelper = new THREE.AxisHelper( 0.1 );
+				scene.add( axisHelper );
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setClearColor( 0x000000 );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				var loader = new THREE.PCDLoader();
+				loader.load( './models/pcd/Zaghetto.pcd', function ( mesh ) {
+
+					scene.add( mesh );
+					var center = mesh.geometry.boundingSphere.center;
+					controls.target.set( center.x, center.y, center.z);
+					controls.update();
+					
+				} );
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+				container.appendChild( renderer.domElement );
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.domElement );
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+				window.addEventListener('keydown', keyboard);
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				controls.handleResize();
+
+			}
+
+			function keyboard (ev) 
+			{
+				var ZaghettoMesh = scene.getObjectByName( "Zaghetto.pcd" );
+				switch (ev.key)
+				{
+					case '+':
+						ZaghettoMesh.material.size*=1.2;
+						ZaghettoMesh.material.needsUpdate = true;
+						break;
+					case '-':
+						ZaghettoMesh.material.size/=1.2;
+						ZaghettoMesh.material.needsUpdate = true;
+						break;
+					case 'c':
+						ZaghettoMesh.material.color.setHex(Math.random()*0xffffff);
+						ZaghettoMesh.material.needsUpdate = true;
+						break;
+					default:
+						break;
+				}
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+				controls.update();
+				renderer.render( scene, camera );
+				stats.update();
+
+			}
+
+		</script>
+	</body>
+</html>