2
0
Эх сурвалжийг харах

Use hardware skinning where possible for Collada imports

Rob Chadwick 11 жил өмнө
parent
commit
ffc0ee800e

+ 188 - 0
examples/ColladaHardwareSkinning.html

@@ -0,0 +1,188 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - animation - skinning</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 {
+				color: #000;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+
+				background-color: #000;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+			}
+
+			a {
+
+				color: #f00;
+			}
+
+		</style>
+	</head>
+	<body>
+
+		<div id="container"></div>
+
+		<div id="info">
+		<a href="http://threejs.org" target="_blank">three.js</a> webgl - animation - skinning -
+		buffalo model from <a href="http://ro.me">ro.me</a><br/>
+		click to start animation
+		</div>
+
+		<script src="../build/three.min.js"></script>
+		<script src="./js/loaders/ColladaLoader.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			window.onload = init;
+
+			var container, stats;
+
+			var camera, scene, renderer;
+
+			var mesh, light;
+
+			var mouseX = 0, mouseY = 0;
+
+			var windowHalfX = window.innerWidth / 2;
+			var windowHalfY = window.innerHeight / 2;
+
+			var animations = [];
+			var buffalos = [];
+			var offset = [];
+			var box;
+			var floor, dz = 0, dstep = -10, playback = false;
+			var skin;
+			var clock = new THREE.Clock();
+
+			function init() {
+
+				container = document.getElementById( 'container' );
+
+				camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 1, 10000 );
+				camera.position.set( -5, -5, 5 );
+				camera.up.set( 0, 0, 1 );
+				scene = new THREE.Scene();
+				scene.fog = new THREE.FogExp2( 0xfff4e5, 0.0003 );
+
+				light = new THREE.DirectionalLight( 0xffffff, 1.5 );
+				light.position.set( 0, -4, -4 ).normalize();
+				scene.add( light );
+
+				var planeSimple = new THREE.PlaneGeometry( 200, 300 );
+				var planeTesselated = new THREE.PlaneGeometry( 100, 300, 25, 40 );
+				var matWire = new THREE.MeshBasicMaterial( { color: 0x110000, wireframe: true, wireframeLinewidth: 2 } );
+				var matSolid = new THREE.MeshBasicMaterial( { color: 0xffb23f } );
+
+				renderer = new THREE.WebGLRenderer( { antialias: true, alpha: false } );
+				renderer.setClearColor( scene.fog.color, 1 );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.sortObjects = false;
+
+				container.appendChild( renderer.domElement );
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.domElement );
+
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+				
+
+				var loader = new THREE.ColladaLoader();
+				loader.load( "./models/collada/avatar.dae", createScene );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+				//
+
+				loop();
+
+			}
+
+			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 createScene( geometry, materials ) {
+			
+				scene.add( geometry.scene );
+				camera.lookAt( geometry.scene.position );
+				camera.lookAt(geometry.scene.children[0].children[1].position)
+				skin = geometry.scene.children[0].children[1];
+				
+				if(skin.geometry.animation)
+				{
+					THREE.AnimationHandler.add(skin.geometry.animation);
+					var animation = new THREE.Animation(
+						skin,
+						skin.geometry.animation.name,
+						THREE.AnimationHandler.LINEAR
+					  );
+					 
+					animation.play();
+					
+				}
+			}
+
+			
+
+
+			function onDocumentMouseMove( event ) {
+
+				mouseX = ( event.clientX - windowHalfX );
+				mouseY = ( event.clientY - windowHalfY );
+
+			}
+
+			function loop() {
+
+				requestAnimationFrame( loop, renderer.domElement );
+
+				var delta = clock.getDelta();
+
+				
+				THREE.AnimationHandler.update( delta );
+
+				
+				
+				
+				
+
+
+				renderer.render( scene, camera );
+
+				stats.update();
+
+			}
+
+		</script>
+
+	</body>
+</html>
+

+ 223 - 40
examples/js/loaders/ColladaLoader.js

@@ -346,14 +346,15 @@ THREE.ColladaLoader = function () {
 		var start = 1000000;
 		var end = -start;
 		var frames = 0;
-
+		var ID;
 		for ( var id in animations ) {
 
 			var animation = animations[ id ];
-
+			ID = ID || animation.id; 
 			for ( var i = 0; i < animation.sampler.length; i ++ ) {
 
 				var sampler = animation.sampler[ i ];
+				
 				sampler.create();
 
 				start = Math.min( start, sampler.startTime );
@@ -364,7 +365,7 @@ THREE.ColladaLoader = function () {
 
 		}
 
-		return { start:start, end:end, frames:frames };
+		return { start:start, end:end, frames:frames,ID:ID };
 
 	};
 
@@ -462,7 +463,9 @@ THREE.ColladaLoader = function () {
 	function setupSkeleton ( node, bones, frame, parent ) {
 
 		node.world = node.world || new THREE.Matrix4();
+		node.localworld = node.localworld || new THREE.Matrix4();
 		node.world.copy( node.matrix );
+		node.localworld.copy( node.matrix );
 
 		if ( node.channels && node.channels.length ) {
 
@@ -472,7 +475,9 @@ THREE.ColladaLoader = function () {
 			if ( m instanceof THREE.Matrix4 ) {
 
 				node.world.copy( m );
-
+				node.localworld.copy(m);
+				if(frame == 0)
+					node.matrix.copy(m);
 			}
 
 		}
@@ -522,7 +527,9 @@ THREE.ColladaLoader = function () {
 				bone.invBindMatrix = inv;
 				bone.skinningMatrix = new THREE.Matrix4();
 				bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi)
+				bone.animatrix = new THREE.Matrix4();
 
+				bone.animatrix.copy(bone.localworld);
 				bone.weights = [];
 
 				for ( var j = 0; j < skin.weights.length; j ++ ) {
@@ -549,13 +556,83 @@ THREE.ColladaLoader = function () {
 				bone.weights = [];
 
 			}
-
 		}
 
 	};
+	//Walk the Collada tree and flatten the bones into a list, extract the position, quat and scale from the matrix
+	function flattenSkeleton(skeleton)
+	{
+		var list = [];
+		var walk = function(parentid, node, list)
+		{
+			var bone = {}
+			bone.name = node.sid;
+			bone.parent = parentid;
+			bone.matrix = node.matrix;
+			var data = [new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3()];
+			bone.matrix.decompose(data[0],data[1],data[2]);
+							
+			bone.pos = [data[0].x,data[0].y,data[0].z];
+							
+			bone.scl = [data[2].x,data[2].y,data[2].z];
+			bone.rotq = [data[1].x,data[1].y,data[1].z,data[1].w];
+			list.push(bone);
+			for(var i in node.nodes)
+			{
+				walk(node.sid,node.nodes[i],list);
+			}
+		};
+		walk(-1,skeleton,list);
+		return list;
+	};
+	//Move the vertices into the pose that is proper for the start of the animation
+	function skinToBindPose(geometry,skeleton,skinController)
+	{
+			var bones = [];
+			setupSkeleton( skeleton, bones, -1 );
+			setupSkinningMatrices( bones, skinController.skin );
+			v = new THREE.Vector3();
+			var skinned = [];
+			for(var i =0; i < geometry.vertices.length; i++)
+			{
+				skinned.push(new THREE.Vector3());
+			}
+			for ( i = 0; i < bones.length; i ++ ) {
+
+					if ( bones[ i ].type != 'JOINT' ) continue;
+
+					for ( j = 0; j < bones[ i ].weights.length; j ++ ) {
+
+						w = bones[ i ].weights[ j ];
+						vidx = w.index;
+						weight = w.weight;
+
+						o = geometry.vertices[vidx];
+						s = skinned[vidx];
+						
+						v.x = o.x;
+						v.y = o.y;
+						v.z = o.z;
+
+						v.applyMatrix4( bones[i].skinningMatrix );
 
+						s.x += (v.x * weight);
+						s.y += (v.y * weight);
+						s.z += (v.z * weight);
+					}
+
+				}
+			for(var i =0; i < geometry.vertices.length; i++)
+			{
+				geometry.vertices[i] = skinned[i];
+			}	
+
+	}
 	function applySkin ( geometry, instanceCtrl, frame ) {
 
+		// TODO: get this from the renderer or options
+		var maxbones = 30;
+		
 		var skinController = controllers[ instanceCtrl.url ];
 
 		frame = frame !== undefined ? frame : 40;
@@ -577,70 +654,159 @@ THREE.ColladaLoader = function () {
 		var animationBounds = calcAnimationBounds();
 		var skeleton = daeScene.getChildById( instanceCtrl.skeleton[0], true ) ||
 					   daeScene.getChildBySid( instanceCtrl.skeleton[0], true );
+		
+		//flatten the skeleton into a list of bones
+		var bonelist = flattenSkeleton(skeleton);
+		var joints = skinController.skin.joints;
+
+		//sort that list so that the order reflects the order in the joint list
+		var sortedbones = [];
+		for(var i = 0; i < joints.length; i++)
+		{
+			for(var j =0; j < bonelist.length; j++)
+			{
+				if(bonelist[j].name == joints[i])
+				{
+					sortedbones[i] = bonelist[j];
+				}
+			}
+		}
+
+		//hook up the parents by index instead of name
+		for(var i = 0; i < sortedbones.length; i++)
+		{
+			for(var j =0; j < sortedbones.length; j++)
+			{
+				if(sortedbones[i].parent == sortedbones[j].name)
+				{
+					sortedbones[i].parent = j;
+				}
+			}
+		}
+
 
 		var i, j, w, vidx, weight;
 		var v = new THREE.Vector3(), o, s;
 
 		// move vertices to bind shape
-
 		for ( i = 0; i < geometry.vertices.length; i ++ ) {
+					geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix );
+		}
 
-			geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix );
+		var skinIndices = [];
+		var skinWeights = [];
+		var weights = skinController.skin.weights;
 
+		//hook up the skin weights
+		// TODO -  this might be a good place to choose greatest 4 weights
+		for(var i =0; i < weights.length; i++)
+		{
+			var indicies = new THREE.Vector4(weights[i][0]?weights[i][0].joint:0,weights[i][1]?weights[i][1].joint:0,weights[i][2]?weights[i][2].joint:0,weights[i][3]?weights[i][3].joint:0);
+			var weight = new THREE.Vector4(weights[i][0]?weights[i][0].weight:0,weights[i][1]?weights[i][1].weight:0,weights[i][2]?weights[i][2].weight:0,weights[i][3]?weights[i][3].weight:0);
+			
+			skinIndices.push(indicies);
+			skinWeights.push(weight);
 		}
-
+		geometry.skinIndices = skinIndices;
+		geometry.skinWeights = skinWeights;
+		geometry.bones = sortedbones;
 		// process animation, or simply pose the rig if no animation
+		
+		//create an animation for the animated bones
+		//NOTE: this has no effect when using morphtargets
+		var animationdata = {"name":animationBounds.ID,"fps":30,"length":animationBounds.frames/30,"hierarchy":[]};
+
+		for(var j =0; j < sortedbones.length; j++)
+		{
+			animationdata.hierarchy.push({parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[]});
+		}
+
+		//if using hardware skinning, move the vertices into the binding pose
+		if(sortedbones.length < maxbones)
+		{
+			skinToBindPose(geometry,skeleton,skinController);
+		}
 
 		for ( frame = 0; frame < animationBounds.frames; frame ++ ) {
 
 			var bones = [];
 			var skinned = [];
-
-			// zero skinned vertices
-
-			for ( i = 0; i < geometry.vertices.length; i++ ) {
-
-				skinned.push( new THREE.Vector3() );
-
-			}
-
 			// process the frame and setup the rig with a fresh
 			// transform, possibly from the bone's animation channel(s)
 
 			setupSkeleton( skeleton, bones, frame );
 			setupSkinningMatrices( bones, skinController.skin );
 
-			// skin 'm
+			//if using hardware skinning, just hook up the animiation data
+			if(sortedbones.length < maxbones)
+			{
+				for(var i = 0; i < bones.length; i ++)
+				{
+					for(var j = 0; j < animationdata.hierarchy.length; j ++)
+					{
+						if(animationdata.hierarchy[j].name == bones[i].sid)
+						{
+
+							var key = {};
+							key.time = (frame/30);
+							key.matrix = bones[i].animatrix;
+							
+							if(frame == 0)
+								bones[i].matrix = key.matrix;
+							
+							var data = [new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3()];
+							key.matrix.decompose(data[0],data[1],data[2]);
+							
+							key.pos = [data[0].x,data[0].y,data[0].z];
+							
+							key.scl = [data[2].x,data[2].y,data[2].z];
+							key.rot = data[1];
+
+							animationdata.hierarchy[j].keys.push(key);
+						}
+					}
+				}
+				geometry.animation = animationdata;
+			}
+			// otherwise, process the animation into morphtargets
+			else
+			{
+				
+				for ( i = 0; i < geometry.vertices.length; i++ ) {
+
+					skinned.push( new THREE.Vector3() );
 
-			for ( i = 0; i < bones.length; i ++ ) {
+				}
 
-				if ( bones[ i ].type != 'JOINT' ) continue;
+				for ( i = 0; i < bones.length; i ++ ) {
 
-				for ( j = 0; j < bones[ i ].weights.length; j ++ ) {
+					if ( bones[ i ].type != 'JOINT' ) continue;
 
-					w = bones[ i ].weights[ j ];
-					vidx = w.index;
-					weight = w.weight;
+					for ( j = 0; j < bones[ i ].weights.length; j ++ ) {
 
-					o = geometry.vertices[vidx];
-					s = skinned[vidx];
+						w = bones[ i ].weights[ j ];
+						vidx = w.index;
+						weight = w.weight;
 
-					v.x = o.x;
-					v.y = o.y;
-					v.z = o.z;
+						o = geometry.vertices[vidx];
+						s = skinned[vidx];
 
-					v.applyMatrix4( bones[i].skinningMatrix );
+						v.x = o.x;
+						v.y = o.y;
+						v.z = o.z;
 
-					s.x += (v.x * weight);
-					s.y += (v.y * weight);
-					s.z += (v.z * weight);
+						v.applyMatrix4( bones[i].skinningMatrix );
 
-				}
+						s.x += (v.x * weight);
+						s.y += (v.y * weight);
+						s.z += (v.z * weight);
 
-			}
+					}
 
-			geometry.morphTargets.push( { name: "target_" + frame, vertices: skinned } );
+				}
 
+			geometry.morphTargets.push( { name: "target_" + frame, vertices: skinned } );
+			}
 		}
 
 	};
@@ -802,16 +968,33 @@ THREE.ColladaLoader = function () {
 
 				if ( skinController !== undefined ) {
 
+					
 					applySkin( geom, skinController );
 
-					material.morphTargets = true;
+					if(geom.morphTargets.length > 0)
+					{
+						material.morphTargets = true;
+						material.skinning = false;
+					}
+					else
+					{
+						material.morphTargets = false;
+						material.skinning = true;
+					}
+				
+					
 
 					mesh = new THREE.SkinnedMesh( geom, material, false );
-					mesh.skeleton = skinController.skeleton;
-					mesh.skinController = controllers[ skinController.url ];
-					mesh.skinInstanceController = skinController;
+
+
+					//mesh.skeleton = skinController.skeleton;
+					//mesh.skinController = controllers[ skinController.url ];
+					//mesh.skinInstanceController = skinController;
 					mesh.name = 'skin_' + skins.length;
 
+					
+					
+					//mesh.animationHandle.setKey(0);
 					skins.push( mesh );
 
 				} else if ( morphController !== undefined ) {

BIN
examples/models/collada/VWS_B_Male2-2.jpg


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 304 - 0
examples/models/collada/avatar.DAE


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно