AnimationUtils.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /**
  2. * @author tschw
  3. * @author Ben Houston / http://clara.io/
  4. * @author David Sarno / http://lighthaus.us/
  5. */
  6. import { Quaternion } from '../math/Quaternion.js';
  7. import { AdditiveAnimationBlendMode } from '../constants.js';
  8. var AnimationUtils = {
  9. // same as Array.prototype.slice, but also works on typed arrays
  10. arraySlice: function ( array, from, to ) {
  11. if ( AnimationUtils.isTypedArray( array ) ) {
  12. // in ios9 array.subarray(from, undefined) will return empty array
  13. // but array.subarray(from) or array.subarray(from, len) is correct
  14. return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );
  15. }
  16. return array.slice( from, to );
  17. },
  18. // converts an array to a specific type
  19. convertArray: function ( array, type, forceClone ) {
  20. if ( ! array || // let 'undefined' and 'null' pass
  21. ! forceClone && array.constructor === type ) return array;
  22. if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {
  23. return new type( array ); // create typed array
  24. }
  25. return Array.prototype.slice.call( array ); // create Array
  26. },
  27. isTypedArray: function ( object ) {
  28. return ArrayBuffer.isView( object ) &&
  29. ! ( object instanceof DataView );
  30. },
  31. // returns an array by which times and values can be sorted
  32. getKeyframeOrder: function ( times ) {
  33. function compareTime( i, j ) {
  34. return times[ i ] - times[ j ];
  35. }
  36. var n = times.length;
  37. var result = new Array( n );
  38. for ( var i = 0; i !== n; ++ i ) result[ i ] = i;
  39. result.sort( compareTime );
  40. return result;
  41. },
  42. // uses the array previously returned by 'getKeyframeOrder' to sort data
  43. sortedArray: function ( values, stride, order ) {
  44. var nValues = values.length;
  45. var result = new values.constructor( nValues );
  46. for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {
  47. var srcOffset = order[ i ] * stride;
  48. for ( var j = 0; j !== stride; ++ j ) {
  49. result[ dstOffset ++ ] = values[ srcOffset + j ];
  50. }
  51. }
  52. return result;
  53. },
  54. // function for parsing AOS keyframe formats
  55. flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {
  56. var i = 1, key = jsonKeys[ 0 ];
  57. while ( key !== undefined && key[ valuePropertyName ] === undefined ) {
  58. key = jsonKeys[ i ++ ];
  59. }
  60. if ( key === undefined ) return; // no data
  61. var value = key[ valuePropertyName ];
  62. if ( value === undefined ) return; // no data
  63. if ( Array.isArray( value ) ) {
  64. do {
  65. value = key[ valuePropertyName ];
  66. if ( value !== undefined ) {
  67. times.push( key.time );
  68. values.push.apply( values, value ); // push all elements
  69. }
  70. key = jsonKeys[ i ++ ];
  71. } while ( key !== undefined );
  72. } else if ( value.toArray !== undefined ) {
  73. // ...assume THREE.Math-ish
  74. do {
  75. value = key[ valuePropertyName ];
  76. if ( value !== undefined ) {
  77. times.push( key.time );
  78. value.toArray( values, values.length );
  79. }
  80. key = jsonKeys[ i ++ ];
  81. } while ( key !== undefined );
  82. } else {
  83. // otherwise push as-is
  84. do {
  85. value = key[ valuePropertyName ];
  86. if ( value !== undefined ) {
  87. times.push( key.time );
  88. values.push( value );
  89. }
  90. key = jsonKeys[ i ++ ];
  91. } while ( key !== undefined );
  92. }
  93. },
  94. subclip: function ( sourceClip, name, startFrame, endFrame, fps ) {
  95. fps = fps || 30;
  96. var clip = sourceClip.clone();
  97. clip.name = name;
  98. var tracks = [];
  99. for ( var i = 0; i < clip.tracks.length; ++ i ) {
  100. var track = clip.tracks[ i ];
  101. var valueSize = track.getValueSize();
  102. var times = [];
  103. var values = [];
  104. for ( var j = 0; j < track.times.length; ++ j ) {
  105. var frame = track.times[ j ] * fps;
  106. if ( frame < startFrame || frame >= endFrame ) continue;
  107. times.push( track.times[ j ] );
  108. for ( var k = 0; k < valueSize; ++ k ) {
  109. values.push( track.values[ j * valueSize + k ] );
  110. }
  111. }
  112. if ( times.length === 0 ) continue;
  113. track.times = AnimationUtils.convertArray( times, track.times.constructor );
  114. track.values = AnimationUtils.convertArray( values, track.values.constructor );
  115. tracks.push( track );
  116. }
  117. clip.tracks = tracks;
  118. // find minimum .times value across all tracks in the trimmed clip
  119. var minStartTime = Infinity;
  120. for ( var i = 0; i < clip.tracks.length; ++ i ) {
  121. if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) {
  122. minStartTime = clip.tracks[ i ].times[ 0 ];
  123. }
  124. }
  125. // shift all tracks such that clip begins at t=0
  126. for ( var i = 0; i < clip.tracks.length; ++ i ) {
  127. clip.tracks[ i ].shift( - 1 * minStartTime );
  128. }
  129. clip.resetDuration();
  130. return clip;
  131. },
  132. makeClipAdditive: function ( targetClip, referenceFrame, referenceClip, fps ) {
  133. if ( referenceFrame === undefined ) referenceFrame = 0;
  134. if ( referenceClip === undefined ) referenceClip = targetClip;
  135. if ( fps === undefined || fps <= 0 ) fps = 30;
  136. var numTracks = targetClip.tracks.length;
  137. var referenceTime = referenceFrame / fps;
  138. // Make each track's values relative to the values at the reference frame
  139. for ( var i = 0; i < numTracks; ++ i ) {
  140. var referenceTrack = referenceClip.tracks[ i ];
  141. var referenceTrackType = referenceTrack.ValueTypeName;
  142. // Skip this track if it's non-numeric
  143. if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue;
  144. // Find the track in the target clip whose name and type matches the reference track
  145. var targetTrack = targetClip.tracks.find( function ( track ) {
  146. return track.name === referenceTrack.name
  147. && track.ValueTypeName === referenceTrackType;
  148. } );
  149. if ( targetTrack === undefined ) continue;
  150. var valueSize = referenceTrack.getValueSize();
  151. var lastIndex = referenceTrack.times.length - 1;
  152. var referenceValue;
  153. // Find the value to subtract out of the track
  154. if ( referenceTime <= referenceTrack.times[ 0 ] ) {
  155. // Reference frame is earlier than the first keyframe, so just use the first keyframe
  156. referenceValue = AnimationUtils.arraySlice( referenceTrack.values, 0, referenceTrack.valueSize );
  157. } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) {
  158. // Reference frame is after the last keyframe, so just use the last keyframe
  159. var startIndex = lastIndex * valueSize;
  160. referenceValue = AnimationUtils.arraySlice( referenceTrack.values, startIndex );
  161. } else {
  162. // Interpolate to the reference value
  163. var interpolant = referenceTrack.createInterpolant();
  164. interpolant.evaluate( referenceTime );
  165. referenceValue = interpolant.resultBuffer;
  166. }
  167. // Conjugate the quaternion
  168. if ( referenceTrackType === 'quaternion' ) {
  169. var referenceQuat = new Quaternion(
  170. referenceValue[ 0 ],
  171. referenceValue[ 1 ],
  172. referenceValue[ 2 ],
  173. referenceValue[ 3 ]
  174. ).normalize().conjugate();
  175. referenceQuat.toArray( referenceValue );
  176. }
  177. // Subtract the reference value from all of the track values
  178. var numTimes = targetTrack.times.length;
  179. for ( var j = 0; j < numTimes; ++ j ) {
  180. var valueStart = j * valueSize;
  181. if ( referenceTrackType === 'quaternion' ) {
  182. // Multiply the conjugate for quaternion track types
  183. Quaternion.multiplyQuaternionsFlat(
  184. targetTrack.values,
  185. valueStart,
  186. referenceValue,
  187. 0,
  188. targetTrack.values,
  189. valueStart
  190. );
  191. } else {
  192. // Subtract each value for all other numeric track types
  193. for ( var k = 0; k < valueSize; ++ k ) {
  194. targetTrack.values[ valueStart + k ] -= referenceValue[ k ];
  195. }
  196. }
  197. }
  198. }
  199. targetClip.blendMode = AdditiveAnimationBlendMode;
  200. return targetClip;
  201. }
  202. };
  203. export { AnimationUtils };
粤ICP备19079148号