Browse Source

ColladaLoader2: Basic support of Visual Scenes. More robust geometry parsing.

Mr.doob 9 years ago
parent
commit
180e139c28
1 changed files with 316 additions and 58 deletions
  1. 316 58
      examples/js/loaders/ColladaLoader2.js

+ 316 - 58
examples/js/loaders/ColladaLoader2.js

@@ -29,7 +29,7 @@ THREE.ColladaLoader.prototype = {
 	options: {
 
 		set convertUpAxis ( value ) {
-			console.log( 'ColladaLoder2: TODO' );
+			console.log( 'ColladaLoder.options.convertUpAxis: TODO' );
 		}
 
 	},
@@ -68,129 +68,387 @@ THREE.ColladaLoader.prototype = {
 
 		}
 
-		function parseGeometries( xml ) {
+		function parseId( text ) {
 
-			xml = xml.getElementsByTagName( 'geometry' );
+			return text.substring( 1 );
 
-			var geometries = [];
+		}
+
+		// cameras
+
+		function parseCamerasLibrary( xml ) {
+
+			var library = {};
 
-			for ( var i = 0; i < xml.length; i ++ ) {
+			var cameras = xml.getElementsByTagName( 'library_cameras' )[ 0 ];
 
-				geometries.push( parseGeometry( xml[ i ].getElementsByTagName( 'mesh' )[ 0 ] ) );
+			if ( cameras !== undefined ) {
+
+				var elements = cameras.getElementsByTagName( 'camera' );
+
+				for ( var i = 0; i < elements.length; i ++ ) {
+
+					var element = elements[ i ];
+					library[ element.getAttribute( 'id' ) ] = parseCamera( element );
+
+				}
 
 			}
 
-			return geometries;
+			return library;
 
 		}
 
-		function parseGeometry( xml ) {
+		function parseCamera( xml ) {
 
-			var geometry = new THREE.BufferGeometry();
+			console.log( 'ColladaLoader.parseCamera: TODO')
+
+			var camera = new THREE.PerspectiveCamera();
+			camera.name = xml.getAttribute( 'name' );
+			return camera;
+
+		}
+
+		// geometries
+
+		function parseGeometriesLibrary( xml ) {
 
-			// sources
+			var library = {};
 
-			var sources = {};
-			var sourceNodes = xml.getElementsByTagName( 'source' );
+			var geometries = xml.getElementsByTagName( 'library_geometries' )[ 0 ];
+			var elements = geometries.getElementsByTagName( 'geometry' );
 
-			for ( var i = 0; i < sourceNodes.length; i ++ ) {
+			for ( var i = 0; i < elements.length; i ++ ) {
 
-				var sourceNode = sourceNodes[ i ];
-				var array = parseFloats( sourceNode.getElementsByTagName( 'float_array' )[ 0 ].textContent );
-				sources[ sourceNode.getAttribute( 'id' ) ] = array;
+				var element = elements[ i ];
+				var mesh = parseMesh( element.getElementsByTagName( 'mesh' )[ 0 ] );
+
+				library[ element.getAttribute( 'id' ) ] = createGeometry( mesh );
 
 			}
 
-			// vertices
+			return library;
+
+		}
+
+		function parseMesh( xml ) {
+
+			var mesh = {
+				sources: {}
+			};
+
+			for ( var i = 0; i < xml.childNodes.length; i ++ ) {
+
+				var child = xml.childNodes[ i ];
+
+				if ( child.nodeType !== 1 ) continue;
 
-			var verticesNode = xml.getElementsByTagName( 'vertices' )[ 0 ];
-			sources[ verticesNode.getAttribute( 'id' ) ] = sources[ verticesNode.getElementsByTagName( 'input' )[ 0 ].getAttribute( 'source' ).substring( 1 ) ];
+				switch ( child.nodeName ) {
 
-			// triangles
+					case 'source':
+						mesh.sources[ child.getAttribute( 'id' ) ] = parseFloats( child.getElementsByTagName( 'float_array' )[ 0 ].textContent );
+						break;
 
-			var triangleNodes = xml.getElementsByTagName( 'triangles' );
+					case 'vertices':
+						mesh.sources[ child.getAttribute( 'id' ) ] = mesh.sources[ parseId( child.getElementsByTagName( 'input' )[ 0 ].getAttribute( 'source' ) ) ];
+						break;
 
-			if ( triangleNodes === null ) return geometry;
+					case 'polylist':
+					case 'triangles':
+						mesh.primitive = parsePrimitive( child );
+						break;
 
-			for ( var i = 0; i < triangleNodes.length; i ++ ) {
+					default:
+						console.log( child );
+
+				}
+
+			}
+
+			return mesh;
+
+		}
 
-				var triangleNode = triangleNodes[ i ];
+		function parsePrimitive( xml ) {
 
-				// indices
+			var primitive = {
+				inputs: {},
+				stride: 0
+			};
 
-				var indices = parseInts( triangleNode.getElementsByTagName( 'p' )[ 0 ].textContent );
+			for ( var i = 0; i < xml.childNodes.length; i ++ ) {
 
-				// inputs
+				var child = xml.childNodes[ i ];
 
-				var inputNodes = triangleNode.getElementsByTagName( 'input' );
+				if ( child.nodeType !== 1 ) continue;
 
-				var maxOffset = 0;
+				switch ( child.nodeName ) {
 
-				for ( var j = 0; j < inputNodes.length; j ++ ) {
+					case 'input':
+						var id = parseId( child.getAttribute( 'source' ) );
+						var semantic = child.getAttribute( 'semantic' );
+						var offset = parseInt( child.getAttribute( 'offset' ) );
+						primitive.inputs[ semantic ] = { id: id, offset: offset };
+						primitive.stride = Math.max( primitive.stride, offset + 1 );
+						break;
 
-					var inputNode = inputNodes[ j ];
-					maxOffset = Math.max( maxOffset, parseInt( inputNode.getAttribute( 'offset' ) ) + 1 );
+					case 'vcount':
+						primitive.vcount = parseInts( child.textContent );
+						break;
+
+					case 'p':
+						primitive.p = parseInts( child.textContent );
+						break;
 
 				}
 
-				for ( var j = 0; j < inputNodes.length; j ++ ) {
+			}
+
+			return primitive;
+
+		}
+
+		function createGeometry( mesh ) {
+
+			var sources = mesh.sources;
+			var primitive = mesh.primitive;
+
+			var inputs = primitive.inputs;
+			var stride = primitive.stride;
+			var vcount = primitive.vcount;
+
+			var vcount = primitive.vcount;
+			var indices = primitive.p;
+
+			var geometry = new THREE.BufferGeometry();
+
+			for ( var name in inputs ) {
+
+				var input = inputs[ name ];
+
+				var source = sources[ input.id ];
+				var offset = input.offset;
+
+				var array = [];
+
+				function pushVector( i ) {
+
+					var index = indices[ i + offset ] * 3;
+					array.push( source[ index + 0 ], source[ index + 1 ], source[ index + 2 ] );
+
+				}
+
+				if ( primitive.vcount !== undefined ) {
+
+					var index = 0;
+
+					for ( var i = 0, l = vcount.length; i < l; i ++ ) {
+
+						var count = vcount[ i ];
+
+						if ( count === 4 ) {
+
+							var a = index + stride * 0;
+							var b = index + stride * 1;
+							var c = index + stride * 2;
+							var d = index + stride * 3;
+
+							pushVector( a );
+							pushVector( b );
+							pushVector( d );
+
+							pushVector( b );
+							pushVector( c );
+							pushVector( d );
+
+						} else if ( count === 3 ) {
+
+							var a = index + stride * 0;
+							var b = index + stride * 1;
+							var c = index + stride * 2;
 
-					var inputNode = inputNodes[ j ];
+							pushVector( a );
+							pushVector( b );
+							pushVector( c );
 
-					var source = sources[ inputNode.getAttribute( 'source' ).substring( 1 ) ];
-					var offset = parseInt( inputNode.getAttribute( 'offset' ) );
+						} else {
 
-					var array = [];
+							console.log( 'ColladaLoader.createGeometry:', count, 'not supported.' );
 
-					for ( var k = offset; k < indices.length; k += maxOffset ) {
+						}
 
-						var index = indices[ k ] * 3;
-						array.push( source[ index + 0 ], source[ index + 1 ], source[ index + 2 ] );
+						index += stride * count;
 
 					}
 
-					switch ( inputNode.getAttribute( 'semantic' ) ) {
+				} else {
 
-						case 'VERTEX':
-							geometry.addAttribute( 'position', new THREE.Float32Attribute( array, 3 ) );
-							break;
+					for ( var i = 0, l = indices.length; i < l; i += stride ) {
 
-						case 'NORMAL':
-							geometry.addAttribute( 'normal', new THREE.Float32Attribute( array, 3 ) );
-							break;
+						pushVector( i );
 
 					}
 
 				}
 
+				switch ( name )	{
+
+					case 'VERTEX':
+						geometry.addAttribute( 'position', new THREE.Float32Attribute( array, 3 ) );
+						break;
+
+					case 'NORMAL':
+						geometry.addAttribute( 'normal', new THREE.Float32Attribute( array, 3 ) );
+						break;
+
+				}
+
 			}
 
 			return geometry;
 
 		}
 
-		console.time( 'ColladaLoader2' );
+		// nodes
 
-		var xml = new DOMParser().parseFromString( text, 'text/xml' );
+		function parseNodesLibrary( xml ) {
+
+			var library = {};
+			return library;
+
+		}
+
+		function parseNode( xml ) {
+
+			var group = new THREE.Group();
+			group.name = xml.getAttribute( 'name' );
+
+			var matrix = new THREE.Matrix4();
+
+			for ( var i = 0; i < xml.childNodes.length; i ++ ) {
+
+				var child = xml.childNodes[ i ];
 
-		var geometries = xml.getElementsByTagName( 'library_geometries' )[ 0 ];
-		// var materials = xml.getElementsByTagName( 'library_materials' )[ 0 ];
-		// var images = xml.getElementsByTagName( 'library_images' )[ 0 ];
-		// var effects = xml.getElementsByTagName( 'library_effects' )[ 0 ];
+				if ( child.nodeType !== 1 ) continue;
 
-		var scene = new THREE.Scene();
+				switch ( child.nodeName ) {
 
-		var geometries = parseGeometries( geometries );
-		var material = new THREE.MeshPhongMaterial();
+					case 'node':
+						group.add( parseNode( child ) );
+						break;
 
-		for ( var i = 0; i < geometries.length; i ++ ) {
+					case 'instance_camera':
+						var camera = camerasLibrary[ parseId( child.getAttribute( 'url' ) ) ];
+						group.add( camera );
+						break;
 
-			scene.add( new THREE.Mesh( geometries[ i ], material ) );
+					case 'instance_geometry':
+						var geometry = geometriesLibrary[ parseId( child.getAttribute( 'url' ) ) ];
+						var material = new THREE.MeshPhongMaterial();
+						var mesh = new THREE.Mesh( geometry, material );
+						group.add( mesh );
+						break;
+
+					case 'matrix':
+						matrix.multiply( new THREE.Matrix4().fromArray( parseFloats( child.textContent ) ) );
+						break;
+
+					case 'translate':
+						var vector = new THREE.Vector3().fromArray( parseFloats( child.textContent ) );
+						matrix.multiply( new THREE.Matrix4().makeTranslation( vector.x, vector.y, vector.z ) );
+						break;
+
+					case 'rotate':
+						var array = parseFloats( child.textContent );
+						var axis = new THREE.Vector3().fromArray( array );
+						var angle = THREE.Math.degToRad( array[ 3 ] );
+						matrix.multiply( new THREE.Matrix4().makeRotationAxis( axis, angle ) );
+						break;
+
+					case 'scale':
+						var vector = new THREE.Vector3().fromArray( parseFloats( child.textContent ) );
+						matrix.scale( vector );
+						break;
+
+					case 'extra':
+						break;
+
+					default:
+						console.log( child );
+						break;
+
+				}
+
+			}
+
+			matrix.decompose( group.position, group.quaternion, group.scale );
+
+			return group;
 
 		}
 
+		// visual scenes
+
+		function parseVisualScenesLibrary( xml ) {
+
+			var library = {};
+
+			var visualScenes = xml.getElementsByTagName( 'library_visual_scenes' )[ 0 ];
+			var elements = visualScenes.getElementsByTagName( 'visual_scene' );
+
+			for ( var i = 0; i < elements.length; i ++ ) {
+
+				var element = elements[ i ];
+				library[ element.getAttribute( 'id' ) ] = parseVisualScene( element );
+
+			}
+
+			return library;
+
+		}
+
+		function parseVisualScene( xml ) {
+
+			var group = new THREE.Group();
+			group.name = xml.getAttribute( 'name' );
+
+			var elements = xml.getElementsByTagName( 'node' );
+
+			for ( var i = 0; i < elements.length; i ++ ) {
+
+				var element = elements[ i ];
+				group.add( parseNode( element ) );
+
+			}
+
+			return group;
+
+		}
+
+		// scenes
+
+		function parseScene( xml ) {
+
+			var scene = xml.getElementsByTagName( 'scene' )[ 0 ];
+			var instance = scene.getElementsByTagName( 'instance_visual_scene' )[ 0 ];
+			return visualScenesLibrary[ parseId( instance.getAttribute( 'url' ) ) ];
+
+		}
+
+		console.time( 'ColladaLoader2' );
+
+		var xml = new DOMParser().parseFromString( text, 'text/xml' );
+
+		var camerasLibrary = parseCamerasLibrary( xml );
+		var geometriesLibrary = parseGeometriesLibrary( xml );
+		var nodesLibrary = parseNodesLibrary( xml );
+		var visualScenesLibrary = parseVisualScenesLibrary( xml );
+		var scene = parseScene( xml );
+
 		console.timeEnd( 'ColladaLoader2' );
 
+		// console.log( scene );
+
 		return {
 			animations: [],
 			kinematics: { joints: [] },