CCDIKSolver.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /**
  2. * @author takahiro / https://github.com/takahirox
  3. *
  4. * CCD Algorithm
  5. * https://sites.google.com/site/auraliusproject/ccd-algorithm
  6. *
  7. * mesh.geometry needs to have iks array.
  8. *
  9. * ik parameter example
  10. *
  11. * // target, effector, index in links are bone index in skeleton.
  12. * ik = {
  13. * target: 1,
  14. * effector: 2,
  15. * links: [ { index: 5 }, { index: 4, limitation: new THREE.Vector3( 1, 0, 0 ) }, { index : 3 } ],
  16. * iteration: 10,
  17. * minAngle: 0.0,
  18. * maxAngle: 1.0,
  19. * };
  20. */
  21. THREE.CCDIKSolver = function ( mesh ) {
  22. this.mesh = mesh;
  23. };
  24. THREE.CCDIKSolver.prototype = {
  25. constructor: THREE.CCDIKSolver,
  26. update: function () {
  27. var effectorVec = new THREE.Vector3();
  28. var targetVec = new THREE.Vector3();
  29. var axis = new THREE.Vector3();
  30. var q = new THREE.Quaternion();
  31. var bones = this.mesh.skeleton.bones;
  32. var iks = this.mesh.geometry.iks;
  33. // for reference overhead reduction in loop
  34. var math = Math;
  35. for ( var i = 0, il = iks.length; i < il; i++ ) {
  36. var ik = iks[ i ];
  37. var effector = bones[ ik.effector ];
  38. var target = bones[ ik.target ];
  39. var targetPos = target.getWorldPosition();
  40. var links = ik.links;
  41. var iteration = ik.iteration !== undefined ? ik.iteration : 1;
  42. for ( var j = 0; j < iteration; j++ ) {
  43. for ( var k = 0, kl = links.length; k < kl; k++ ) {
  44. var link = bones[ links[ k ].index ];
  45. var limitation = links[ k ].limitation;
  46. var linkPos = link.getWorldPosition();
  47. var invLinkQ = link.getWorldQuaternion().inverse();
  48. var effectorPos = effector.getWorldPosition();
  49. // work in link world
  50. effectorVec.subVectors( effectorPos, linkPos );
  51. effectorVec.applyQuaternion( invLinkQ );
  52. effectorVec.normalize();
  53. targetVec.subVectors( targetPos, linkPos );
  54. targetVec.applyQuaternion( invLinkQ );
  55. targetVec.normalize();
  56. var angle = targetVec.dot( effectorVec );
  57. if ( angle > 1.0 ) {
  58. angle = 1.0;
  59. } else if ( angle < -1.0 ) {
  60. angle = -1.0;
  61. }
  62. angle = math.acos( angle );
  63. // skip if changing angle is too small to prevent vibration of bone
  64. // Refer to http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
  65. if ( angle < 1e-5 ) {
  66. continue;
  67. }
  68. if ( ik.minAngle !== undefined && angle < ik.minAngle ) {
  69. angle = ik.minAngle;
  70. }
  71. if ( ik.maxAngle !== undefined && angle > ik.maxAngle ) {
  72. angle = ik.maxAngle;
  73. }
  74. axis.crossVectors( effectorVec, targetVec );
  75. axis.normalize();
  76. q.setFromAxisAngle( axis, angle );
  77. link.quaternion.multiply( q );
  78. // TODO: re-consider the limitation specification
  79. if ( limitation !== undefined ) {
  80. var c = link.quaternion.w;
  81. if ( c > 1.0 ) {
  82. c = 1.0;
  83. }
  84. var c2 = math.sqrt( 1 - c * c );
  85. link.quaternion.set( limitation.x * c2,
  86. limitation.y * c2,
  87. limitation.z * c2,
  88. c );
  89. }
  90. link.updateMatrixWorld( true );
  91. }
  92. }
  93. this.mesh.updateMatrixWorld( true );
  94. }
  95. }
  96. };