Animation.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. /**
  2. * @author mikael emtinger / http://gomo.se/
  3. * @author mrdoob / http://mrdoob.com/
  4. * @author alteredq / http://alteredqualia.com/
  5. */
  6. THREE.Animation = function( root, data, interpolationType, JITCompile ) {
  7. this.root = root;
  8. this.data = THREE.AnimationHandler.get( data );
  9. this.hierarchy = THREE.AnimationHandler.parse( root );
  10. this.currentTime = 0;
  11. this.timeScale = 1;
  12. this.isPlaying = false;
  13. this.isPaused = true;
  14. this.loop = true;
  15. this.interpolationType = interpolationType !== undefined ? interpolationType : THREE.AnimationHandler.LINEAR;
  16. this.JITCompile = JITCompile !== undefined ? JITCompile : true;
  17. this.points = [];
  18. this.target = new THREE.Vector3();
  19. };
  20. // Play
  21. THREE.Animation.prototype.play = function( loop, startTimeMS ) {
  22. if( !this.isPlaying ) {
  23. this.isPlaying = true;
  24. this.loop = loop !== undefined ? loop : true;
  25. this.currentTime = startTimeMS !== undefined ? startTimeMS : 0;
  26. // reset key cache
  27. var h, hl = this.hierarchy.length,
  28. object;
  29. for ( h = 0; h < hl; h++ ) {
  30. object = this.hierarchy[ h ];
  31. if ( this.interpolationType !== THREE.AnimationHandler.CATMULLROM_FORWARD ) {
  32. object.useQuaternion = true;
  33. }
  34. object.matrixAutoUpdate = true;
  35. if ( object.animationCache === undefined ) {
  36. object.animationCache = {};
  37. object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 };
  38. object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 };
  39. object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix;
  40. }
  41. var prevKey = object.animationCache.prevKey;
  42. var nextKey = object.animationCache.nextKey;
  43. prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ];
  44. prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ];
  45. prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ];
  46. nextKey.pos = this.getNextKeyWith( "pos", h, 1 );
  47. nextKey.rot = this.getNextKeyWith( "rot", h, 1 );
  48. nextKey.scl = this.getNextKeyWith( "scl", h, 1 );
  49. }
  50. this.update( 0 );
  51. }
  52. this.isPaused = false;
  53. THREE.AnimationHandler.addToUpdate( this );
  54. };
  55. // Pause
  56. THREE.Animation.prototype.pause = function() {
  57. if( this.isPaused ) {
  58. THREE.AnimationHandler.addToUpdate( this );
  59. } else {
  60. THREE.AnimationHandler.removeFromUpdate( this );
  61. }
  62. this.isPaused = !this.isPaused;
  63. };
  64. // Stop
  65. THREE.Animation.prototype.stop = function() {
  66. this.isPlaying = false;
  67. this.isPaused = false;
  68. THREE.AnimationHandler.removeFromUpdate( this );
  69. // reset JIT matrix and remove cache
  70. for ( var h = 0; h < this.hierarchy.length; h++ ) {
  71. if ( this.hierarchy[ h ].animationCache !== undefined ) {
  72. if( this.hierarchy[ h ] instanceof THREE.Bone ) {
  73. this.hierarchy[ h ].skinMatrix = this.hierarchy[ h ].animationCache.originalMatrix;
  74. } else {
  75. this.hierarchy[ h ].matrix = this.hierarchy[ h ].animationCache.originalMatrix;
  76. }
  77. delete this.hierarchy[ h ].animationCache;
  78. }
  79. }
  80. };
  81. // Update
  82. THREE.Animation.prototype.update = function( deltaTimeMS ) {
  83. // early out
  84. if( !this.isPlaying ) return;
  85. // vars
  86. var types = [ "pos", "rot", "scl" ];
  87. var type;
  88. var scale;
  89. var vector;
  90. var prevXYZ, nextXYZ;
  91. var prevKey, nextKey;
  92. var object;
  93. var animationCache;
  94. var frame;
  95. var JIThierarchy = this.data.JIT.hierarchy;
  96. var currentTime, unloopedCurrentTime;
  97. var currentPoint, forwardPoint, angle;
  98. // update
  99. this.currentTime += deltaTimeMS * this.timeScale;
  100. unloopedCurrentTime = this.currentTime;
  101. currentTime = this.currentTime = this.currentTime % this.data.length;
  102. frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 );
  103. // update
  104. for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) {
  105. object = this.hierarchy[ h ];
  106. animationCache = object.animationCache;
  107. // use JIT?
  108. if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) {
  109. if( object instanceof THREE.Bone ) {
  110. object.skinMatrix = JIThierarchy[ h ][ frame ];
  111. object.matrixAutoUpdate = false;
  112. object.matrixWorldNeedsUpdate = false;
  113. } else {
  114. object.matrix = JIThierarchy[ h ][ frame ];
  115. object.matrixAutoUpdate = false;
  116. object.matrixWorldNeedsUpdate = true;
  117. }
  118. // use interpolation
  119. } else {
  120. // make sure so original matrix and not JIT matrix is set
  121. if ( this.JITCompile ) {
  122. if( object instanceof THREE.Bone ) {
  123. object.skinMatrix = object.animationCache.originalMatrix;
  124. } else {
  125. object.matrix = object.animationCache.originalMatrix;
  126. }
  127. }
  128. // loop through pos/rot/scl
  129. for ( var t = 0; t < 3; t++ ) {
  130. // get keys
  131. type = types[ t ];
  132. prevKey = animationCache.prevKey[ type ];
  133. nextKey = animationCache.nextKey[ type ];
  134. // switch keys?
  135. if ( nextKey.time <= unloopedCurrentTime ) {
  136. // did we loop?
  137. if ( currentTime < unloopedCurrentTime ) {
  138. if ( this.loop ) {
  139. prevKey = this.data.hierarchy[ h ].keys[ 0 ];
  140. nextKey = this.getNextKeyWith( type, h, 1 );
  141. while( nextKey.time < currentTime ) {
  142. prevKey = nextKey;
  143. nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
  144. }
  145. } else {
  146. this.stop();
  147. return;
  148. }
  149. } else {
  150. do {
  151. prevKey = nextKey;
  152. nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
  153. } while( nextKey.time < currentTime )
  154. }
  155. animationCache.prevKey[ type ] = prevKey;
  156. animationCache.nextKey[ type ] = nextKey;
  157. }
  158. object.matrixAutoUpdate = true;
  159. object.matrixWorldNeedsUpdate = true;
  160. scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
  161. prevXYZ = prevKey[ type ];
  162. nextXYZ = nextKey[ type ];
  163. // check scale error
  164. if ( scale < 0 || scale > 1 ) {
  165. console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h );
  166. scale = scale < 0 ? 0 : 1;
  167. }
  168. // interpolate
  169. if ( type === "pos" ) {
  170. vector = object.position;
  171. if( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
  172. vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
  173. vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
  174. vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
  175. } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
  176. this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
  177. this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
  178. this.points[ 1 ] = prevXYZ;
  179. this.points[ 2 ] = nextXYZ;
  180. this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
  181. scale = scale * 0.33 + 0.33;
  182. currentPoint = this.interpolateCatmullRom( this.points, scale );
  183. vector.x = currentPoint[ 0 ];
  184. vector.y = currentPoint[ 1 ];
  185. vector.z = currentPoint[ 2 ];
  186. if( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
  187. forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 );
  188. this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
  189. this.target.subSelf( vector );
  190. this.target.y = 0;
  191. this.target.normalize();
  192. angle = Math.atan2( this.target.x, this.target.z );
  193. object.rotation.set( 0, angle, 0 );
  194. }
  195. }
  196. } else if ( type === "rot" ) {
  197. THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale );
  198. } else if( type === "scl" ) {
  199. vector = object.scale;
  200. vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
  201. vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
  202. vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
  203. }
  204. }
  205. }
  206. }
  207. // update JIT?
  208. if ( this.JITCompile ) {
  209. if ( JIThierarchy[ 0 ][ frame ] === undefined ) {
  210. this.hierarchy[ 0 ].update( undefined, true );
  211. for ( var h = 0; h < this.hierarchy.length; h++ ) {
  212. if( this.hierarchy[ h ] instanceof THREE.Bone ) {
  213. JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone();
  214. } else {
  215. JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone();
  216. }
  217. }
  218. }
  219. }
  220. };
  221. // Catmull-Rom spline
  222. THREE.Animation.prototype.interpolateCatmullRom = function ( points, scale ) {
  223. var c = [], v3 = [],
  224. point, intPoint, weight, w2, w3,
  225. pa, pb, pc, pd;
  226. point = ( points.length - 1 ) * scale;
  227. intPoint = Math.floor( point );
  228. weight = point - intPoint;
  229. c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1;
  230. c[ 1 ] = intPoint;
  231. c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
  232. c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
  233. pa = points[ c[ 0 ] ];
  234. pb = points[ c[ 1 ] ];
  235. pc = points[ c[ 2 ] ];
  236. pd = points[ c[ 3 ] ];
  237. w2 = weight * weight;
  238. w3 = weight * w2;
  239. v3[ 0 ] = this.interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
  240. v3[ 1 ] = this.interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
  241. v3[ 2 ] = this.interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
  242. return v3;
  243. };
  244. THREE.Animation.prototype.interpolate = function( p0, p1, p2, p3, t, t2, t3 ) {
  245. var v0 = ( p2 - p0 ) * 0.5,
  246. v1 = ( p3 - p1 ) * 0.5;
  247. return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
  248. };
  249. // Get next key with
  250. THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) {
  251. var keys = this.data.hierarchy[ h ].keys;
  252. if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
  253. this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
  254. key = key < keys.length - 1 ? key : keys.length - 1;
  255. } else {
  256. key = key % keys.length;
  257. }
  258. for ( ; key < keys.length; key++ ) {
  259. if ( keys[ key ][ type ] !== undefined ) {
  260. return keys[ key ];
  261. }
  262. }
  263. return this.data.hierarchy[ h ].keys[ 0 ];
  264. };
  265. // Get previous key with
  266. THREE.Animation.prototype.getPrevKeyWith = function( type, h, key ) {
  267. var keys = this.data.hierarchy[ h ].keys;
  268. if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
  269. this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
  270. key = key > 0 ? key : 0;
  271. } else {
  272. key = key >= 0 ? key : key + keys.length;
  273. }
  274. for ( ; key >= 0; key-- ) {
  275. if ( keys[ key ][ type ] !== undefined ) {
  276. return keys[ key ];
  277. }
  278. }
  279. return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
  280. };