BlendCharacter.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /**
  2. * @author Michael Guerrero / http://realitymeltdown.com
  3. */
  4. THREE.BlendCharacter = function () {
  5. this.animations = {};
  6. this.weightSchedule = [];
  7. this.warpSchedule = [];
  8. this.load = function ( url, onLoad ) {
  9. var scope = this;
  10. var loader = new THREE.JSONLoader();
  11. loader.load( url, function( geometry, materials ) {
  12. var originalMaterial = materials[ 0 ];
  13. originalMaterial.skinning = true;
  14. THREE.SkinnedMesh.call( scope, geometry, originalMaterial );
  15. // Create the animations
  16. for ( var i = 0; i < geometry.animations.length; ++i ) {
  17. var animName = geometry.animations[ i ].name;
  18. scope.animations[ animName ] = new THREE.Animation( scope, geometry.animations[ i ] );
  19. }
  20. // Create the debug visualization
  21. scope.skeletonHelper = new THREE.SkeletonHelper( scope );
  22. scope.skeletonHelper.material.linewidth = 3;
  23. scope.add( scope.skeletonHelper );
  24. scope.showSkeleton( false );
  25. // Loading is complete, fire the callback
  26. if ( onLoad !== undefined ) onLoad();
  27. } );
  28. };
  29. this.update = function( dt ) {
  30. for ( var i = this.weightSchedule.length - 1; i >= 0; --i ) {
  31. var data = this.weightSchedule[ i ];
  32. data.timeElapsed += dt;
  33. // If the transition is complete, remove it from the schedule
  34. if ( data.timeElapsed > data.duration ) {
  35. data.anim.weight = data.endWeight;
  36. this.weightSchedule.splice( i, 1 );
  37. // If we've faded out completely, stop the animation
  38. if ( data.anim.weight == 0 ) {
  39. data.anim.stop( 0 );
  40. }
  41. } else {
  42. // interpolate the weight for the current time
  43. data.anim.weight = data.startWeight + (data.endWeight - data.startWeight) * data.timeElapsed / data.duration;
  44. }
  45. }
  46. this.updateWarps( dt );
  47. this.skeletonHelper.update();
  48. };
  49. this.updateWarps = function( dt ) {
  50. // Warping modifies the time scale over time to make 2 animations of different
  51. // lengths match. This is useful for smoothing out transitions that get out of
  52. // phase such as between a walk and run cycle
  53. for ( var i = this.warpSchedule.length - 1; i >= 0; --i ) {
  54. var data = this.warpSchedule[ i ];
  55. data.timeElapsed += dt;
  56. if ( data.timeElapsed > data.duration ) {
  57. data.to.weight = 1;
  58. data.to.timeScale = 1;
  59. data.from.weight = 0;
  60. data.from.timeScale = 1;
  61. data.from.stop( 0 );
  62. this.warpSchedule.splice( i, 1 );
  63. } else {
  64. var alpha = data.timeElapsed / data.duration;
  65. var fromLength = data.from.data.length;
  66. var toLength = data.to.data.length;
  67. var fromToRatio = fromLength / toLength;
  68. var toFromRatio = toLength / fromLength;
  69. // scale from each time proportionally to the other animation
  70. data.from.timeScale = ( 1 - alpha ) + fromToRatio * alpha;
  71. data.to.timeScale = alpha + toFromRatio * ( 1 - alpha );
  72. data.from.weight = 1 - alpha;
  73. data.to.weight = alpha;
  74. }
  75. }
  76. }
  77. this.play = function(animName, weight) {
  78. this.animations[ animName ].play( 0, weight );
  79. };
  80. this.crossfade = function( fromAnimName, toAnimName, duration ) {
  81. var fromAnim = this.animations[ fromAnimName ];
  82. var toAnim = this.animations[ toAnimName ];
  83. fromAnim.play( 0, 1 );
  84. toAnim.play( 0, 0 );
  85. this.weightSchedule.push( {
  86. anim: fromAnim,
  87. startWeight: 1,
  88. endWeight: 0,
  89. timeElapsed: 0,
  90. duration: duration
  91. } );
  92. this.weightSchedule.push( {
  93. anim: toAnim,
  94. startWeight: 0,
  95. endWeight: 1,
  96. timeElapsed: 0,
  97. duration: duration
  98. } );
  99. };
  100. this.warp = function( fromAnimName, toAnimName, duration ) {
  101. var fromAnim = this.animations[ fromAnimName ];
  102. var toAnim = this.animations[ toAnimName ];
  103. fromAnim.play( 0, 1 );
  104. toAnim.play( 0, 0 );
  105. this.warpSchedule.push( {
  106. from: fromAnim,
  107. to: toAnim,
  108. timeElapsed: 0,
  109. duration: duration
  110. } );
  111. };
  112. this.applyWeight = function(animName, weight) {
  113. this.animations[ animName ].weight = weight;
  114. };
  115. this.pauseAll = function() {
  116. for ( var a in this.animations ) {
  117. if ( this.animations[ a ].isPlaying ) {
  118. this.animations[ a ].stop();
  119. }
  120. }
  121. };
  122. this.unPauseAll = function() {
  123. for ( var a in this.animations ) {
  124. if ( this.animations[ a ].isPlaying && this.animations[ a ].isPaused ) {
  125. this.animations[ a ].pause();
  126. }
  127. }
  128. };
  129. this.stopAll = function() {
  130. for ( a in this.animations ) {
  131. if ( this.animations[ a ].isPlaying ) {
  132. this.animations[ a ].stop(0);
  133. }
  134. this.animations[ a ].weight = 0;
  135. }
  136. this.weightSchedule.length = 0;
  137. this.warpSchedule.length = 0;
  138. }
  139. this.showSkeleton = function( boolean ) {
  140. this.skeletonHelper.visible = boolean;
  141. }
  142. this.showModel = function( boolean ) {
  143. this.visible = boolean;
  144. }
  145. };
  146. THREE.BlendCharacter.prototype = Object.create( THREE.SkinnedMesh.prototype );
  147. THREE.BlendCharacter.prototype.getForward = function() {
  148. var forward = new THREE.Vector3();
  149. return function() {
  150. // pull the character's forward basis vector out of the matrix
  151. forward.set(
  152. -this.matrix.elements[ 8 ],
  153. -this.matrix.elements[ 9 ],
  154. -this.matrix.elements[ 10 ]
  155. );
  156. return forward;
  157. }
  158. }