Browse Source

Ccdik solver optimization (#10010)

* Optimize CCDIKSolver

* Remove lines I should have not commit

* Remove lines I should have not committed
Takahiro 8 years ago
parent
commit
b54ee2d4e5
2 changed files with 151 additions and 17 deletions
  1. 79 17
      examples/js/animation/CCDIKSolver.js
  2. 72 0
      examples/js/loaders/MMDLoader.js

+ 79 - 17
examples/js/animation/CCDIKSolver.js

@@ -6,13 +6,16 @@
  *
  * mesh.geometry needs to have iks array.
  *
- * ik parameter example
- *
+ * // ik parameter example
+ * //
  * // target, effector, index in links are bone index in skeleton.
+ * // the bones relation should be
+ * // <-- parent                                  child -->
+ * // links[ n ], links[ n - 1 ], ..., links[ 0 ], effector
  * ik = {
  *	target: 1,
  *	effector: 2,
- *	links: [ { index: 5 }, { index: 4, limitation: new THREE.Vector3( 1, 0, 0 ) }, { index : 3 } ],
+ *	links: [ { index: 5, limitation: new THREE.Vector3( 1, 0, 0 ) }, { index: 4, enabled: false }, { index : 3 } ],
  *	iteration: 10,
  *	minAngle: 0.0,
  *	maxAngle: 1.0,
@@ -23,43 +26,103 @@ THREE.CCDIKSolver = function ( mesh ) {
 
 	this.mesh = mesh;
 
+	this._valid();
+
 };
 
 THREE.CCDIKSolver.prototype = {
 
 	constructor: THREE.CCDIKSolver,
 
+	_valid: function () {
+
+		var iks = this.mesh.geometry.iks;
+		var bones = this.mesh.skeleton.bones;
+
+		for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+			var ik = iks[ i ];
+
+			var effector = bones[ ik.effector ];
+
+			var links = ik.links;
+
+			var link0, link1;
+
+			link0 = effector;
+
+			for ( var j = 0, jl = links.length; j < jl; j ++ ) {
+
+				link1 = bones[ links[ j ].index ];
+
+				if ( link0.parent !== link1 ) {
+
+					console.warn( 'THREE.CCDIKSolver: bone ' + link0.name + ' is not the child of bone ' + link1.name );
+
+				}
+
+				link0 = link1;
+
+			}
+
+		}
+
+	},
+
 	update: function () {
 
-		var effectorVec = new THREE.Vector3();
+		var q = new THREE.Quaternion();
+
+		var targetPos = new THREE.Vector3();
 		var targetVec = new THREE.Vector3();
+		var effectorPos = new THREE.Vector3();
+		var effectorVec = new THREE.Vector3();
+		var linkPos = new THREE.Vector3();
+		var invLinkQ = new THREE.Quaternion();
 		var axis = new THREE.Vector3();
-		var q = new THREE.Quaternion();
 
 		var bones = this.mesh.skeleton.bones;
 		var iks = this.mesh.geometry.iks;
 
+		var boneParams = this.mesh.geometry.bones;
+
 		// for reference overhead reduction in loop
 		var math = Math;
 
+		this.mesh.updateMatrixWorld( true );
+
 		for ( var i = 0, il = iks.length; i < il; i++ ) {
 
 			var ik = iks[ i ];
 			var effector = bones[ ik.effector ];
 			var target = bones[ ik.target ];
-			var targetPos = target.getWorldPosition();
+
+			// don't use getWorldPosition() here for the performance
+			// because it calls updateMatrixWorld( true ) inside.
+			targetPos.setFromMatrixPosition( target.matrixWorld );
+
 			var links = ik.links;
 			var iteration = ik.iteration !== undefined ? ik.iteration : 1;
 
 			for ( var j = 0; j < iteration; j++ ) {
 
+				var rotated = false;
+
 				for ( var k = 0, kl = links.length; k < kl; k++ ) {
 
 					var link = bones[ links[ k ].index ];
+
+					// skip this link and following links.
+					// this skip is used for MMD performance optimization.
+					if ( links[ k ].enabled === false ) break;
+
 					var limitation = links[ k ].limitation;
-					var linkPos = link.getWorldPosition();
-					var invLinkQ = link.getWorldQuaternion().inverse();
-					var effectorPos = effector.getWorldPosition();
+
+					// don't use getWorldPosition/Quaternion() here for the performance
+					// because they call updateMatrixWorld( true ) inside.
+					linkPos.setFromMatrixPosition( link.matrixWorld );
+					invLinkQ.setFromRotationMatrix( link.matrixWorld ).inverse();
+					effectorPos.setFromMatrixPosition( effector.matrixWorld );
 
 					// work in link world
 					effectorVec.subVectors( effectorPos, linkPos );
@@ -86,11 +149,7 @@ THREE.CCDIKSolver.prototype = {
 
 					// skip if changing angle is too small to prevent vibration of bone
 					// Refer to http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
-					if ( angle < 1e-5 ) {
-
-						continue;
-
-					}
+					if ( angle < 1e-5 ) continue;
 
 					if ( ik.minAngle !== undefined && angle < ik.minAngle ) {
 
@@ -130,16 +189,19 @@ THREE.CCDIKSolver.prototype = {
 					}
 
 					link.updateMatrixWorld( true );
+					rotated = true;
 
 				}
 
-			}
+				if ( ! rotated ) break;
 
-			this.mesh.updateMatrixWorld( true );
+			}
 
 		}
 
+		// just in case
+		this.mesh.updateMatrixWorld( true );
+
 	}
 
 };
-

+ 72 - 0
examples/js/loaders/MMDLoader.js

@@ -1764,6 +1764,21 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 		var bones = [];
 
+		var rigidBodies = model.rigidBodies;
+		var dictionary = {};
+
+		for ( var i = 0, il = rigidBodies.length; i < il; i ++ ) {
+
+			var body = rigidBodies[ i ];
+			var value = dictionary[ body.boneIndex ];
+
+			// keeps greater number if already value is set without any special reasons
+			value = value === undefined ? body.type : Math.max( body.type, value );
+
+			dictionary[ body.boneIndex ] = value;
+
+		}
+
 		for ( var i = 0; i < model.metadata.boneCount; i++ ) {
 
 			var bone = {};
@@ -1783,6 +1798,8 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 			}
 
+			bone.rigidBodyType = dictionary[ i ] !== undefined ? dictionary[ i ] : -1;
+
 			bones.push( bone );
 
 		}
@@ -1853,6 +1870,7 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 					var link = {};
 					link.index = ik.links[ j ].index;
+					link.enabled = true;
 
 					if ( ik.links[ j ].angleLimitation === 1 ) {
 
@@ -3921,10 +3939,64 @@ THREE.MMDHelper.prototype = {
 
 		physics.warmup( warmup );
 
+		this.updateIKParametersDependingOnPhysicsEnabled( mesh, true );
+
 		mesh.physics = physics;
 
 	},
 
+	enablePhysics: function ( enabled ) {
+
+		if ( enabled === true ) {
+
+			this.doPhysics = true;
+
+		} else {
+
+			this.doPhysics = false;
+
+		}
+
+		for ( var i = 0, il = this.meshes.length; i < il; i ++ ) {
+
+			this.updateIKParametersDependingOnPhysicsEnabled( this.meshes[ i ], enabled );
+
+		}
+
+	},
+
+	updateIKParametersDependingOnPhysicsEnabled: function ( mesh, physicsEnabled ) {
+
+		var iks = mesh.geometry.iks;
+		var bones = mesh.geometry.bones;
+
+		for ( var j = 0, jl = iks.length; j < jl; j ++ ) {
+
+			var ik = iks[ j ];
+			var links = ik.links;
+
+			for ( var k = 0, kl = links.length; k < kl; k ++ ) {
+
+				var link = links[ k ];
+
+				if ( physicsEnabled === true ) {
+
+					// disable IK of the bone the corresponding rigidBody type of which is 1 or 2
+					// because its rotation will be overriden by physics
+					link.enabled = bones[ link.index ].rigidBodyType > 0 ? false : true;
+
+				} else {
+
+					link.enabled = true;
+
+				}
+
+			}
+
+		}
+
+	},
+
 	setAnimations: function () {
 
 		for ( var i = 0; i < this.meshes.length; i++ ) {