瀏覽代碼

Merge pull request #11484 from kchapelier/prwm-loader

PRWM Loader
Mr.doob 8 年之前
父節點
當前提交
def6a359df

+ 1 - 0
examples/files.js

@@ -111,6 +111,7 @@ var files = {
 		"webgl_loader_pcd",
 		"webgl_loader_pdb",
 		"webgl_loader_ply",
+		"webgl_loader_prwm",
 		"webgl_loader_ttf",
 		"webgl_loader_sea3d",
 		"webgl_loader_sea3d_hierarchy",

+ 293 - 0
examples/js/loaders/PRWMLoader.js

@@ -0,0 +1,293 @@
+/**
+ * @author Kevin Chapelier / https://github.com/kchapelier
+ * See https://github.com/kchapelier/PRWM for more informations about this file format
+ */
+
+( function ( THREE ) {
+
+	"use strict";
+
+	var bigEndianPlatform = null;
+
+	/**
+	 * Check if the endianness of the platform is big-endian (most significant bit first)
+	 * @returns {boolean} True if big-endian, false if little-endian
+	 */
+	function isBigEndianPlatform() {
+
+		if ( bigEndianPlatform === null ) {
+
+			var buffer = new ArrayBuffer( 2 ),
+				uint8Array = new Uint8Array( buffer ),
+				uint16Array = new Uint16Array( buffer );
+
+			uint8Array[ 0 ] = 0xAA; // set first byte
+			uint8Array[ 1 ] = 0xBB; // set second byte
+			bigEndianPlatform = ( uint16Array[ 0 ] === 0xAABB );
+
+		}
+
+		return bigEndianPlatform;
+
+	}
+
+	// match the values defined in the spec to the TypedArray types
+	var InvertedEncodingTypes = [
+		null,
+		Float32Array,
+		null,
+		Int8Array,
+		Int16Array,
+		null,
+		Int32Array,
+		Uint8Array,
+		Uint16Array,
+		null,
+		Uint32Array
+	];
+
+	// define the method to use on a DataView, corresponding the TypedArray type
+	var getMethods = {
+		Uint16Array: 'getUint16',
+		Uint32Array: 'getUint32',
+		Int16Array: 'getInt16',
+		Int32Array: 'getInt32',
+		Float32Array: 'getFloat32',
+		Float64Array: 'getFloat64'
+	};
+
+
+	function copyFromBuffer( sourceArrayBuffer, viewType, position, length, fromBigEndian ) {
+
+		var bytesPerElement = viewType.BYTES_PER_ELEMENT,
+			result;
+
+		if ( fromBigEndian === isBigEndianPlatform() || bytesPerElement === 1 ) {
+
+			result = new viewType( sourceArrayBuffer, position, length );
+
+		} else {
+
+			var readView = new DataView( sourceArrayBuffer, position, length * bytesPerElement ),
+				getMethod = getMethods[ viewType.name ],
+				littleEndian = ! fromBigEndian,
+				i = 0;
+
+			result = new viewType( length );
+
+			for ( ; i < length; i ++ ) {
+
+				result[ i ] = readView[ getMethod ]( i * bytesPerElement, littleEndian );
+
+			}
+
+		}
+
+		return result;
+
+	}
+
+
+	function decodePrwm( buffer ) {
+
+		var array = new Uint8Array( buffer ),
+			version = array[ 0 ],
+			flags = array[ 1 ],
+			indexedGeometry = !! ( flags >> 7 & 0x01 ),
+			indicesType = flags >> 6 & 0x01,
+			bigEndian = ( flags >> 5 & 0x01 ) === 1,
+			attributesNumber = flags & 0x1F,
+			valuesNumber = 0,
+			indicesNumber = 0;
+
+		if ( bigEndian ) {
+
+			valuesNumber = ( array[ 2 ] << 16 ) + ( array[ 3 ] << 8 ) + array[ 4 ];
+			indicesNumber = ( array[ 5 ] << 16 ) + ( array[ 6 ] << 8 ) + array[ 7 ];
+
+		} else {
+
+			valuesNumber = array[ 2 ] + ( array[ 3 ] << 8 ) + ( array[ 4 ] << 16 );
+			indicesNumber = array[ 5 ] + ( array[ 6 ] << 8 ) + ( array[ 7 ] << 16 );
+
+		}
+
+		/** PRELIMINARY CHECKS **/
+
+		if ( version === 0 ) {
+
+			throw new Error( 'PRWM decoder: Invalid format version: 0' );
+
+		} else if ( version !== 1 ) {
+
+			throw new Error( 'PRWM decoder: Unsupported format version: ' + version );
+
+		}
+
+		if ( ! indexedGeometry ) {
+
+			if ( indicesType !== 0 ) {
+
+				throw new Error( 'PRWM decoder: Indices type must be set to 0 for non-indexed geometries' );
+
+			} else if ( indicesNumber !== 0 ) {
+
+				throw new Error( 'PRWM decoder: Number of indices must be set to 0 for non-indexed geometries' );
+
+			}
+
+		}
+
+		/** PARSING **/
+
+		var pos = 8;
+
+		var attributes = {},
+			attributeName,
+			char,
+			attributeType,
+			attributeNormalized,
+			cardinality,
+			encodingType,
+			arrayType,
+			values,
+			indices,
+			i;
+
+		for ( i = 0; i < attributesNumber; i ++ ) {
+
+			attributeName = '';
+
+			while ( pos < array.length ) {
+
+				char = array[ pos ];
+				pos ++;
+
+				if ( char === 0 ) {
+
+					break;
+
+				} else {
+
+					attributeName += String.fromCharCode( char );
+
+				}
+
+			}
+
+			flags = array[ pos ];
+
+			attributeType = flags >> 7 & 0x01;
+			attributeNormalized = !! ( flags >> 6 & 0x01 );
+			cardinality = ( flags >> 4 & 0x03 ) + 1;
+			encodingType = flags & 0x0F;
+			arrayType = InvertedEncodingTypes[ encodingType ];
+
+			pos ++;
+
+			// padding to next multiple of 4
+			pos = Math.ceil( pos / 4 ) * 4;
+
+			values = copyFromBuffer( buffer, arrayType, pos, cardinality * valuesNumber, bigEndian );
+
+			pos += arrayType.BYTES_PER_ELEMENT * cardinality * valuesNumber;
+
+			attributes[ attributeName ] = {
+				type: attributeType,
+				cardinality: cardinality,
+				values: values
+			};
+
+		}
+
+		pos = Math.ceil( pos / 4 ) * 4;
+
+		indices = null;
+
+		if ( indexedGeometry ) {
+
+			indices = copyFromBuffer(
+				buffer,
+				indicesType === 1 ? Uint32Array : Uint16Array,
+				pos,
+				indicesNumber,
+				bigEndian
+			);
+
+		}
+
+		return {
+			version: version,
+			attributes: attributes,
+			indices: indices
+		};
+
+	}
+
+	// Define the public interface
+
+	THREE.PRWMLoader = function PRWMLoader( manager ) {
+
+		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+	};
+
+	THREE.PRWMLoader.prototype = {
+
+		constructor: THREE.PRWMLoader,
+
+		load: function ( url, onLoad, onProgress, onError ) {
+
+			var scope = this;
+
+			var loader = new THREE.FileLoader( scope.manager );
+			loader.setResponseType( 'arraybuffer' );
+
+			url = url.replace( /\*/g, isBigEndianPlatform() ? 'be' : 'le' );
+
+			loader.load( url, function ( arrayBuffer ) {
+
+				onLoad( scope.parse( arrayBuffer ) );
+
+			}, onProgress, onError );
+
+		},
+
+		parse: function ( arrayBuffer ) {
+
+			console.time( 'PRWMLoader' );
+
+			var data = decodePrwm( arrayBuffer ),
+				attributesKey = Object.keys( data.attributes ),
+				bufferGeometry = new THREE.BufferGeometry(),
+				attribute,
+				i;
+
+			for ( i = 0; i < attributesKey.length; i ++ ) {
+
+				attribute = data.attributes[ attributesKey[ i ] ];
+				bufferGeometry.addAttribute( attributesKey[ i ], new THREE.BufferAttribute( attribute.values, attribute.cardinality, attribute.normalized ) );
+
+			}
+
+			if ( data.indices !== null ) {
+
+				bufferGeometry.setIndex( new THREE.BufferAttribute( data.indices, 1 ) );
+
+			}
+
+			console.timeEnd( 'PRWMLoader' );
+
+			return bufferGeometry;
+
+		}
+
+	};
+
+	THREE.PRWMLoader.isBigEndianPlatform = function () {
+
+		return isBigEndianPlatform();
+
+	};
+
+} )( THREE );

二進制
examples/models/prwm/faceted-nefertiti.be.prwm


二進制
examples/models/prwm/faceted-nefertiti.le.prwm


二進制
examples/models/prwm/smooth-suzanne.be.prwm


二進制
examples/models/prwm/smooth-suzanne.le.prwm


二進制
examples/models/prwm/vive-controller.be.prwm


二進制
examples/models/prwm/vive-controller.le.prwm


+ 250 - 0
examples/webgl_loader_prwm.html

@@ -0,0 +1,250 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - loaders - PRWM loader</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			html, body {
+				padding:0;
+				margin:0;
+				background:#000000;
+			}
+
+			.models {
+				position:absolute;
+				top:10px;
+				left:12px;
+				z-index:2;
+				font-family: "Arial", "Helvetica Neue", "Helvetica", sans-serif;
+				font-size:13px;
+			}
+
+			.notes {
+				position:absolute;
+				left:12px;
+				bottom:10px;
+				z-index:2;
+				font-family: "Arial", "Helvetica Neue", "Helvetica", sans-serif;
+				font-size:13px;
+				text-align:left;
+				color:#FFFFFF;
+				max-width:300px;
+			}
+
+			.notes a, .notes a:visited {
+				color:#FFFFFF;
+			}
+
+			.models strong {
+				color:#FFFFFF;
+				text-transform: uppercase;
+			}
+
+			.models a, .models a:visited {
+				color:#FFFFFF;
+				margin-left:12px;
+				text-decoration: none;
+			}
+
+			.models a:hover, .models a:focus {
+				text-decoration: underline;
+			}
+		</style>
+	</head>
+
+	<body>
+
+
+		<div class="models">
+			<strong>Models</strong>
+			<a class="model" href="models/prwm/faceted-nefertiti.*.prwm">Faceted Nefertiti</a>
+			<a class="model" href="models/prwm/smooth-suzanne.*.prwm">Smooth Suzanne</a>
+			<a class="model" href="models/prwm/vive-controller.*.prwm">Vive Controller</a>
+		</div>
+		<div class="notes">
+			The parsing of PRWM file is especially fast when the endianness of the file is the same as the endianness
+			of the client platform. The loader will automatically replace the <strong>*</strong> in the model url
+			by either <strong>le</strong> or <strong>be</strong> depending on the client platform's endianness to
+			download the most appropriate file.<br><br>
+			This platform endianness is <strong id="endianness"></strong>.<br><br>
+			See your console for stats.<br><br>
+			<a href="https://github.com/kchapelier/PRWM" target="_blank">Specifications and implementations</a>
+		</div>
+
+		<script src="../build/three.js"></script>
+		<script src="js/loaders/PRWMLoader.js"></script>
+
+		<script>
+
+			var container;
+
+			var camera, scene, renderer;
+
+			var mouseX = 0, mouseY = 0;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+
+			init();
+			animate();
+
+
+			function init() {
+
+				document.getElementById( 'endianness' ).innerHTML = THREE.PRWMLoader.isBigEndianPlatform() ? 'big-endian' : 'little-endian';
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.z = 250;
+
+				// scene
+
+				scene = new THREE.Scene();
+
+				var ambient = new THREE.AmbientLight( 0x101030 );
+				scene.add( ambient );
+
+				var directionalLight = new THREE.DirectionalLight( 0xffeedd );
+				directionalLight.position.set( 0, 0, 1 );
+				scene.add( directionalLight );
+
+				// model
+
+				var loader = new THREE.PRWMLoader();
+				var material = new THREE.MeshPhongMaterial( {} );
+				var busy = false;
+				var mesh = null;
+
+				var onProgress = function ( xhr ) {
+
+					if ( xhr.lengthComputable ) {
+
+						var percentComplete = xhr.loaded / xhr.total * 100;
+						console.log( Math.round( percentComplete, 2 ) + '% downloaded' );
+
+						if ( xhr.loaded === xhr.total ) {
+
+							console.log( 'File size: ' + ( xhr.total / 1024 ).toFixed( 2 ) + 'kB' );
+							console.timeEnd( 'Download' );
+
+						}
+
+					}
+
+				};
+
+				var onError = function ( xhr ) {
+
+					busy = false;
+
+				};
+
+				function loadGeometry( url ) {
+
+					if ( busy ) return;
+
+					busy = true;
+
+					if ( mesh !== null ) {
+
+						scene.remove( mesh );
+						mesh.geometry.dispose();
+
+					}
+
+					console.log( '-- Loading', url );
+					console.time( 'Download' );
+
+					loader.load( url, function ( geometry ) {
+
+						mesh = new THREE.Mesh( geometry, material );
+						mesh.scale.set( 50, 50, 50 );
+						scene.add( mesh );
+
+						console.log( geometry.index ? 'indexed geometry' : 'non-indexed geometry' );
+						console.log( '# of vertices: ' + geometry.attributes.position.count );
+						console.log( '# of polygons: ' + ( geometry.index ? geometry.index.count / 3 : geometry.attributes.position.count / 3 ) );
+						busy = false;
+
+					}, onProgress, onError );
+
+				}
+
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( 1 );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+
+				//
+
+				document.querySelectorAll( 'a.model' ).forEach( function ( anchor ) {
+
+					anchor.addEventListener( 'click', function ( e ) {
+
+						e.preventDefault();
+
+						loadGeometry( anchor.href );
+
+					} );
+
+				} );
+
+				//
+
+				// * is automatically replaced by 'le' or 'be' depending on the client platform's endianness
+				loadGeometry( './models/prwm/smooth-suzanne.*.prwm' );
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				windowHalfX = window.innerWidth / 2;
+				windowHalfY = window.innerHeight / 2;
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				mouseX = ( event.clientX - windowHalfX ) / 2;
+				mouseY = ( event.clientY - windowHalfY ) / 2;
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+				render();
+
+			}
+
+			function render() {
+
+				camera.position.x += ( mouseX - camera.position.x ) * .05;
+				camera.position.y += ( - mouseY - camera.position.y ) * .05;
+
+				camera.lookAt( scene.position );
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>