Browse Source

skinning -> morph-targets

timk 14 years ago
parent
commit
f7c4d504f9
3 changed files with 151 additions and 105 deletions
  1. 1 1
      build/Three.js
  2. 22 103
      examples/webgl_collada.html
  3. 128 1
      src/extras/collada/dae.js

+ 1 - 1
build/Three.js

@@ -39,7 +39,7 @@ h*k,m=h*g,p=f*k,u=f*g;this.n11=o+u*e;this.n12=p*e-m;this.n13=j*f;this.n21=j*g;th
 j*f;p=e*h;u=e*f;this.n11=h*k;this.n12=u-o*g;this.n13=p*g+m;this.n21=g;this.n22=j*k;this.n23=-e*k;this.n31=-f*k;this.n32=m*g+p;this.n33=o-u*g;break;case "XZY":o=j*h;m=j*f;p=e*h;u=e*f;this.n11=h*k;this.n12=-g;this.n13=f*k;this.n21=o*g+u;this.n22=j*k;this.n23=m*g-p;this.n31=p*g-m;this.n32=e*k;this.n33=u*g+o;break;default:o=j*k,m=j*g,p=e*k,u=e*g,this.n11=h*k,this.n12=-h*g,this.n13=f,this.n21=m+p*f,this.n22=o-u*f,this.n23=-e*h,this.n31=u-o*f,this.n32=p+m*f,this.n33=j*h}return this},setRotationFromQuaternion:function(b){var c=
 b.x,e=b.y,f=b.z,g=b.w,j=c+c,h=e+e,k=f+f,b=c*j,o=c*h;c*=k;var m=e*h;e*=k;f*=k;j*=g;h*=g;g*=k;this.n11=1-(m+f);this.n12=o-g;this.n13=c+h;this.n21=o+g;this.n22=1-(b+f);this.n23=e-j;this.n31=c-h;this.n32=e+j;this.n33=1-(b+m);return this},scale:function(b){var c=b.x,e=b.y,b=b.z;this.n11*=c;this.n12*=e;this.n13*=b;this.n21*=c;this.n22*=e;this.n23*=b;this.n31*=c;this.n32*=e;this.n33*=b;this.n41*=c;this.n42*=e;this.n43*=b;return this},compose:function(b,c,e){var f=new THREE.Matrix4,g=new THREE.Matrix4;f.setRotationFromQuaternion(c);
 g.setScale(e.x,e.y,e.z);this.multiply(f,g);this.n14=b.x;this.n24=b.y;this.n34=b.z;return this},decompose:function(b,c,e){var f=new THREE.Vector3(this.n11,this.n21,this.n31),g=new THREE.Vector3(this.n12,this.n22,this.n32),j=new THREE.Vector3(this.n13,this.n23,this.n33),b=b instanceof THREE.Vector3?b:new THREE.Vector3,c=c instanceof THREE.Quaternion?c:new THREE.Quaternion,e=e instanceof THREE.Vector3?e:new THREE.Vector3;e.x=f.length();e.y=g.length();e.z=j.length();b.x=this.n14;b.y=this.n24;b.z=this.n34;
-b=this.clone();b.n11/=e.x;b.n21/=e.x;b.n31/=e.x;b.n12/=e.y;b.n22/=e.y;b.n32/=e.y;b.n13/=e.z;b.n23/=e.z;b.n33/=e.z;c.setFromRotationMatrix(b)},extractPosition:function(b){this.n14=b.n14;this.n24=b.n24;this.n34=b.n34},extractRotation:function(b,c){var e=1/c.x,f=1/c.y,g=1/c.z;this.n11=b.n11*e;this.n21=b.n21*e;this.n31=b.n31*e;this.n12=b.n12*f;this.n22=b.n22*f;this.n32=b.n32*f;this.n13=b.n13*g;this.n23=b.n23*g;this.n33=b.n33*g}};
+f=this.clone();f.n11/=e.x;f.n21/=e.x;f.n31/=e.x;f.n12/=e.y;f.n22/=e.y;f.n32/=e.y;f.n13/=e.z;f.n23/=e.z;f.n33/=e.z;c.setFromRotationMatrix(f);return[b,c,e]},extractPosition:function(b){this.n14=b.n14;this.n24=b.n24;this.n34=b.n34},extractRotation:function(b,c){var e=1/c.x,f=1/c.y,g=1/c.z;this.n11=b.n11*e;this.n21=b.n21*e;this.n31=b.n31*e;this.n12=b.n12*f;this.n22=b.n22*f;this.n32=b.n32*f;this.n13=b.n13*g;this.n23=b.n23*g;this.n33=b.n33*g}};
 THREE.Matrix4.makeInvert=function(b,c){var e=b.n11,f=b.n12,g=b.n13,j=b.n14,h=b.n21,k=b.n22,o=b.n23,m=b.n24,p=b.n31,u=b.n32,v=b.n33,t=b.n34,w=b.n41,x=b.n42,B=b.n43,A=b.n44;c===void 0&&(c=new THREE.Matrix4);c.n11=o*t*x-m*v*x+m*u*B-k*t*B-o*u*A+k*v*A;c.n12=j*v*x-g*t*x-j*u*B+f*t*B+g*u*A-f*v*A;c.n13=g*m*x-j*o*x+j*k*B-f*m*B-g*k*A+f*o*A;c.n14=j*o*u-g*m*u-j*k*v+f*m*v+g*k*t-f*o*t;c.n21=m*v*w-o*t*w-m*p*B+h*t*B+o*p*A-h*v*A;c.n22=g*t*w-j*v*w+j*p*B-e*t*B-g*p*A+e*v*A;c.n23=j*o*w-g*m*w-j*h*B+e*m*B+g*h*A-e*o*A;c.n24=
 g*m*p-j*o*p+j*h*v-e*m*v-g*h*t+e*o*t;c.n31=k*t*w-m*u*w+m*p*x-h*t*x-k*p*A+h*u*A;c.n32=j*u*w-f*t*w-j*p*x+e*t*x+f*p*A-e*u*A;c.n33=g*m*w-j*k*w+j*h*x-e*m*x-f*h*A+e*k*A;c.n34=j*k*p-f*m*p-j*h*u+e*m*u+f*h*t-e*k*t;c.n41=o*u*w-k*v*w-o*p*x+h*v*x+k*p*B-h*u*B;c.n42=f*v*w-g*u*w+g*p*x-e*v*x-f*p*B+e*u*B;c.n43=g*k*w-f*o*w-g*h*x+e*o*x+f*h*B-e*k*B;c.n44=f*o*p-g*k*p+g*h*u-e*o*u-f*h*v+e*k*v;c.multiplyScalar(1/b.determinant());return c};
 THREE.Matrix4.makeInvert3x3=function(b){var c=b.m33,e=c.m,f=b.n33*b.n22-b.n32*b.n23,g=-b.n33*b.n21+b.n31*b.n23,j=b.n32*b.n21-b.n31*b.n22,h=-b.n33*b.n12+b.n32*b.n13,k=b.n33*b.n11-b.n31*b.n13,o=-b.n32*b.n11+b.n31*b.n12,m=b.n23*b.n12-b.n22*b.n13,p=-b.n23*b.n11+b.n21*b.n13,u=b.n22*b.n11-b.n21*b.n12,b=b.n11*f+b.n21*h+b.n31*m;b==0&&console.error("THREE.Matrix4.makeInvert3x3: Matrix not invertible.");b=1/b;e[0]=b*f;e[1]=b*g;e[2]=b*j;e[3]=b*h;e[4]=b*k;e[5]=b*o;e[6]=b*m;e[7]=b*p;e[8]=b*u;return c};

+ 22 - 103
examples/webgl_collada.html

@@ -31,14 +31,16 @@
 
 			var camera, scene, renderer, objects;
 			var particleLight, pointLight;
-			var dae;
+			var dae, skin;
 			
 			DAE.load('./models/monster.dae', colladaReady);
 			
 			function colladaReady(collada) {
 				dae = collada.scene;
-				dae.scale.x = dae.scale.y = dae.scale.z = 0.003;
-				//dae.rotation.x = -Math.PI/2;
+				skin = collada.skins[0];
+
+				dae.scale.x = dae.scale.y = dae.scale.z = 0.002;
+				dae.rotation.x = -Math.PI/2;
 				dae.updateMatrix();
 
 				init();
@@ -59,7 +61,7 @@
 
 				// Grid
 
-				var line_material = new THREE.LineBasicMaterial( { color: 0x0, opacity: 0.2 } ),
+				var line_material = new THREE.LineBasicMaterial( { color: 0xcccccc, opacity: 0.2 } ),
 					geometry = new THREE.Geometry(),
 					floor = -0.04, step = 1, size = 14;
 
@@ -75,85 +77,18 @@
 
 				var line = new THREE.Line( geometry, line_material, THREE.LinePieces );
 				scene.addObject( line );
-
-				// Materials
-
-				var generatedTexture = new THREE.Texture( generateTexture() );
-				generatedTexture.needsUpdate = true;
-
-				var materials = [];
-				materials.push( new THREE.MeshLambertMaterial( { map: generatedTexture } ) );
-				materials.push( new THREE.MeshLambertMaterial( { color: 0xdddddd, shading: THREE.FlatShading } ) );
-				materials.push( new THREE.MeshPhongMaterial( { ambient: 0x030303, color: 0xdddddd, specular: 0x009900, shininess: 30, shading: THREE.FlatShading } ) );
-				materials.push( new THREE.MeshNormalMaterial( ) );
-				materials.push( new THREE.MeshBasicMaterial( { color: 0x665500, blending: THREE.AdditiveBlending } ) );
-				//materials.push( new THREE.MeshBasicMaterial( { color: 0xff0000, blending: THREE.SubtractiveBlending } ) );
-
-				materials.push( new THREE.MeshLambertMaterial( { color: 0xdddddd, shading: THREE.SmoothShading } ) );
-				materials.push( new THREE.MeshPhongMaterial( { ambient: 0x030303, color: 0xdddddd, specular: 0x009900, shininess: 30, shading: THREE.SmoothShading } ) );
-				materials.push( new THREE.MeshNormalMaterial( { shading: THREE.SmoothShading } ) );
-				materials.push( new THREE.MeshBasicMaterial( { color: 0xffaa00, wireframe: true } ) );
-
-				materials.push( new THREE.MeshDepthMaterial() );
-				materials.push( new THREE.MeshBasicMaterial( { map: generatedTexture } ) );
-
-				// Spheres geometry
-
-				var geometry_smooth = new THREE.SphereGeometry( 70, 32, 16 );
-				var geometry_flat = new THREE.SphereGeometry( 70, 32, 16 );
-				var geometry_pieces = new THREE.SphereGeometry( 70, 32, 16 ); // Extra geometry to be broken down for MeshFaceMaterial
-
-				for ( var i = 0, l = geometry_pieces.faces.length; i < l; i ++ ) {
-
-					var face = geometry_pieces.faces[ i ];
-					if ( Math.random() > 0.7 ) face.materials = [ materials[ Math.floor( Math.random() * materials.length )  ] ];
-
-				}
-
-				materials.push( new THREE.MeshFaceMaterial() );
-
-				objects = [];
-
-				var sphere, geometry, material;
-
-				for ( var i = 0, l = materials.length; i < l; i ++ ) {
-
-					material = materials[ i ];
-
-					geometry = material instanceof THREE.MeshFaceMaterial ? geometry_pieces :
-							   ( material.shading == THREE.FlatShading ? geometry_flat : geometry_smooth );
-
-					sphere = new THREE.Mesh( geometry, material );
-
-					sphere.position.x = ( i % 4 ) * 200 - 400;
-					sphere.position.z = Math.floor( i / 4 ) * 200 - 200;
-
-					sphere.rotation.x = Math.random() * 200 - 100;
-					sphere.rotation.y = Math.random() * 200 - 100;
-					sphere.rotation.z = Math.random() * 200 - 100;
-
-					objects.push( sphere );
-
-					//scene.addObject( sphere );
-
-				}
 				
-				//dae.rotation.x = -Math.PI/2;
+				// Add the COLLADA
 				scene.addObject(dae);
-				//var wall = dae_geometries['wall-geometry'][0];
-		
-				//var dae = new THREE.Mesh( wall, materials[3] );
-				//dae.scale.x = dae.scale.y = dae.scale.z = 100.0;				
-				//scene.addObject(dae);
 
 				particleLight = new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
 				scene.addObject( particleLight );
 
 				// Lights
 
-				scene.addLight( new THREE.AmbientLight( 0x202020 ) );
+				scene.addLight( new THREE.AmbientLight( 0xaaaaaa) );
 
-				var directionalLight = new THREE.DirectionalLight(/*Math.random() * 0xffffff*/0xcccccc);
+				var directionalLight = new THREE.DirectionalLight(/*Math.random() * 0xffffff*/0xbbbbbb);
 				directionalLight.position.x = Math.random() - 0.5;
 				directionalLight.position.y = Math.random() - 0.5;
 				directionalLight.position.z = Math.random() - 0.5;
@@ -175,39 +110,23 @@
 
 			}
 
-			function generateTexture() {
-
-				var canvas = document.createElement( 'canvas' );
-				canvas.width = 256;
-				canvas.height = 256;
-
-				var context = canvas.getContext( '2d' );
-				var image = context.getImageData( 0, 0, 256, 256 );
-
-				var x = 0, y = 0;
-
-				for ( var i = 0, j = 0, l = image.data.length; i < l; i += 4, j ++ ) {
-
-					x = j % 256;
-					y = x == 0 ? y + 1 : y;
-
-					image.data[ i + 2 ] = Math.floor( x ^ y );
-					image.data[ i + 3 ] = 255;
-
-				}
-
-				context.putImageData( image, 0, 0 );
-
-				return canvas;
-
-			}
-
 			//
-
+			var t = 0;
 			function animate() {
 
 				requestAnimationFrame( animate );
-
+				
+				if (t > 101) t = 0;
+				if (skin) {
+					// guess this can be done smarter...
+					for (var i = 0; i < skin.morphTargetInfluences.length; i++) {
+						skin.morphTargetInfluences[i] = 0;
+					}
+					skin.morphTargetInfluences[Math.floor(t)] = 1;
+
+					t += 0.5;
+				}
+				
 				render();
 				stats.update();
 

+ 128 - 1
src/extras/collada/dae.js

@@ -152,6 +152,7 @@ var DAE = (function() {
 	function calcAnimationBounds() {
 		var start = 1000000;
 		var end = -start;
+		var frames = 0;
 		
 		for (id in animations) {
 			var animation = animations[id];
@@ -160,9 +161,10 @@ var DAE = (function() {
 				sampler.create();	
 				start = Math.min(start, sampler.startTime);
 				end = Math.max(end, sampler.endTime);
+				frames = Math.max(frames, sampler.input.length);
 			}
 		}
-		return {start:start, end:end}
+		return {start:start, end:end, frames:frames}
 	}
 	
 	function createMorph(geometry, ctrl) {
@@ -227,6 +229,127 @@ var DAE = (function() {
 		}
 	}
 	
+	function setupSkeleton(node, bones, frame, parent) {
+		node.world = node.world || new THREE.Matrix4();
+		node.world.copy(node.matrix);
+		
+		if (node.channels && node.channels.length) {
+			var channel = node.channels[0];
+			var m = channel.sampler.output[frame];
+			if (m instanceof THREE.Matrix4) {
+				node.world.copy(m);
+			}
+		}
+		
+		if (parent) {
+			node.world.multiply(parent, node.world);
+		}
+
+		bones.push(node);
+		
+		for (var i = 0; i < node.nodes.length; i++) {
+			setupSkeleton(node.nodes[i], bones, frame, node.world);
+		}
+	}
+	
+	function setupSkinningMatrices(bones, skin) {
+		// FIXME: this is dumb...
+		for (var i = 0; i < bones.length; i++) {
+			var bone = bones[i];
+			var found = -1;
+			for (var j = 0; j < skin.joints.length; j++) {
+				if (bone.sid == skin.joints[j]) {
+					found = j;
+					break;
+				}
+			}
+			if (found >= 0){
+				var inv = skin.invBindMatrices[found];
+				bone.invBindMatrix = inv;
+				bone.skinningMatrix = new THREE.Matrix4();
+				bone.skinningMatrix.multiply(bone.world, inv);
+				bone.weights = [];
+				for (var j = 0; j < skin.weights.length; j++) {
+					for (var k = 0; k < skin.weights[j].length; k++) {
+						var w = skin.weights[j][k];
+						if (w.joint == found) {
+							bone.weights.push(w);
+						}
+					}
+				}
+			} else {
+				throw "could not find joint!";
+			}
+		}
+	}
+	
+	function applySkin(geometry, instanceCtrl, frame) {
+		var skinController = controllers[instanceCtrl.url];
+		
+		frame = frame !== undefined ? frame : 40;
+		
+		if (!skinController || !skinController.skin) {
+			console.log("could not find skin controller!");
+			return;
+		}
+
+		if (!instanceCtrl.skeleton || !instanceCtrl.skeleton.length) {
+			console.log("could not find the skeleton for the skin!");
+			return;
+		}
+		
+		var animationBounds = calcAnimationBounds();
+		var skeleton = daeScene.getChildById(instanceCtrl.skeleton[0], true) ||
+					   daeScene.getChildBySid(instanceCtrl.skeleton[0], true);
+		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++) {
+			skinController.skin.bindShapeMatrix.multiplyVector3(geometry.vertices[i].position);
+		}
+		
+		// process animation, or simply pose the rig if no animation
+		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.Vertex( 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
+			for (i = 0; i < bones.length; i++) {
+				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.position.x;
+					v.y = o.position.y;
+					v.z = o.position.z;
+				
+					bones[i].skinningMatrix.multiplyVector3(v);
+				
+					s.position.x += (v.x * weight);
+					s.position.y += (v.y * weight);
+					s.position.z += (v.z * weight);
+				}
+			}
+			
+			geometry.morphTargets.push( { name: "target_" + frame, vertices: skinned } );
+		}
+	}
+	
 	function createSceneGraph(node, parent) {
 		var obj = new THREE.Object3D();
 		var skinned = false;
@@ -322,9 +445,12 @@ var DAE = (function() {
 				}
 				
 				if (skinController !== undefined) {
+					applySkin(geom, skinController);
+					material.morphTargets = true;
 					mesh = new THREE.SkinnedMesh( geom, material );
 					mesh.skeleton = skinController.skeleton;
 					mesh.skinController = controllers[skinController.url];
+					mesh.skinInstanceController = skinController;
 					mesh.name = 'skin_' + skins.length;
 					skins.push(mesh);
 				} else if (morphController !== undefined) {
@@ -1898,6 +2024,7 @@ var DAE = (function() {
 	return {
 		load: load,
 		setPreferredShading: setPreferredShading,
+		applySkin: applySkin,
 		geometries : geometries
 	};
 })();