KeyframeTrackPrototype.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. import { InterpolateLinear } from '../constants.js';
  2. import { AnimationUtils } from './AnimationUtils.js';
  3. import { InterpolateSmooth, InterpolateDiscrete } from '../constants.js';
  4. import { CubicInterpolant } from '../math/interpolants/CubicInterpolant.js';
  5. import { LinearInterpolant } from '../math/interpolants/LinearInterpolant.js';
  6. import { DiscreteInterpolant } from '../math/interpolants/DiscreteInterpolant.js';
  7. var KeyframeTrackPrototype;
  8. KeyframeTrackPrototype = {
  9. TimeBufferType: Float32Array,
  10. ValueBufferType: Float32Array,
  11. DefaultInterpolation: InterpolateLinear,
  12. InterpolantFactoryMethodDiscrete: function ( result ) {
  13. return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );
  14. },
  15. InterpolantFactoryMethodLinear: function ( result ) {
  16. return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );
  17. },
  18. InterpolantFactoryMethodSmooth: function ( result ) {
  19. return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );
  20. },
  21. setInterpolation: function ( interpolation ) {
  22. var factoryMethod;
  23. switch ( interpolation ) {
  24. case InterpolateDiscrete:
  25. factoryMethod = this.InterpolantFactoryMethodDiscrete;
  26. break;
  27. case InterpolateLinear:
  28. factoryMethod = this.InterpolantFactoryMethodLinear;
  29. break;
  30. case InterpolateSmooth:
  31. factoryMethod = this.InterpolantFactoryMethodSmooth;
  32. break;
  33. }
  34. if ( factoryMethod === undefined ) {
  35. var message = "unsupported interpolation for " +
  36. this.ValueTypeName + " keyframe track named " + this.name;
  37. if ( this.createInterpolant === undefined ) {
  38. // fall back to default, unless the default itself is messed up
  39. if ( interpolation !== this.DefaultInterpolation ) {
  40. this.setInterpolation( this.DefaultInterpolation );
  41. } else {
  42. throw new Error( message ); // fatal, in this case
  43. }
  44. }
  45. console.warn( 'THREE.KeyframeTrackPrototype:', message );
  46. return;
  47. }
  48. this.createInterpolant = factoryMethod;
  49. },
  50. getInterpolation: function () {
  51. switch ( this.createInterpolant ) {
  52. case this.InterpolantFactoryMethodDiscrete:
  53. return InterpolateDiscrete;
  54. case this.InterpolantFactoryMethodLinear:
  55. return InterpolateLinear;
  56. case this.InterpolantFactoryMethodSmooth:
  57. return InterpolateSmooth;
  58. }
  59. },
  60. getValueSize: function () {
  61. return this.values.length / this.times.length;
  62. },
  63. // move all keyframes either forwards or backwards in time
  64. shift: function ( timeOffset ) {
  65. if ( timeOffset !== 0.0 ) {
  66. var times = this.times;
  67. for ( var i = 0, n = times.length; i !== n; ++ i ) {
  68. times[ i ] += timeOffset;
  69. }
  70. }
  71. return this;
  72. },
  73. // scale all keyframe times by a factor (useful for frame <-> seconds conversions)
  74. scale: function ( timeScale ) {
  75. if ( timeScale !== 1.0 ) {
  76. var times = this.times;
  77. for ( var i = 0, n = times.length; i !== n; ++ i ) {
  78. times[ i ] *= timeScale;
  79. }
  80. }
  81. return this;
  82. },
  83. // removes keyframes before and after animation without changing any values within the range [startTime, endTime].
  84. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
  85. trim: function ( startTime, endTime ) {
  86. var times = this.times,
  87. nKeys = times.length,
  88. from = 0,
  89. to = nKeys - 1;
  90. while ( from !== nKeys && times[ from ] < startTime ) ++ from;
  91. while ( to !== - 1 && times[ to ] > endTime ) -- to;
  92. ++ to; // inclusive -> exclusive bound
  93. if ( from !== 0 || to !== nKeys ) {
  94. // empty tracks are forbidden, so keep at least one keyframe
  95. if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;
  96. var stride = this.getValueSize();
  97. this.times = AnimationUtils.arraySlice( times, from, to );
  98. this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );
  99. }
  100. return this;
  101. },
  102. // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
  103. validate: function () {
  104. var valid = true;
  105. var valueSize = this.getValueSize();
  106. if ( valueSize - Math.floor( valueSize ) !== 0 ) {
  107. console.error( 'THREE.KeyframeTrackPrototype: Invalid value size in track.', this );
  108. valid = false;
  109. }
  110. var times = this.times,
  111. values = this.values,
  112. nKeys = times.length;
  113. if ( nKeys === 0 ) {
  114. console.error( 'THREE.KeyframeTrackPrototype: Track is empty.', this );
  115. valid = false;
  116. }
  117. var prevTime = null;
  118. for ( var i = 0; i !== nKeys; i ++ ) {
  119. var currTime = times[ i ];
  120. if ( typeof currTime === 'number' && isNaN( currTime ) ) {
  121. console.error( 'THREE.KeyframeTrackPrototype: Time is not a valid number.', this, i, currTime );
  122. valid = false;
  123. break;
  124. }
  125. if ( prevTime !== null && prevTime > currTime ) {
  126. console.error( 'THREE.KeyframeTrackPrototype: Out of order keys.', this, i, currTime, prevTime );
  127. valid = false;
  128. break;
  129. }
  130. prevTime = currTime;
  131. }
  132. if ( values !== undefined ) {
  133. if ( AnimationUtils.isTypedArray( values ) ) {
  134. for ( var i = 0, n = values.length; i !== n; ++ i ) {
  135. var value = values[ i ];
  136. if ( isNaN( value ) ) {
  137. console.error( 'THREE.KeyframeTrackPrototype: Value is not a valid number.', this, i, value );
  138. valid = false;
  139. break;
  140. }
  141. }
  142. }
  143. }
  144. return valid;
  145. },
  146. // removes equivalent sequential keys as common in morph target sequences
  147. // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
  148. optimize: function () {
  149. var times = this.times,
  150. values = this.values,
  151. stride = this.getValueSize(),
  152. smoothInterpolation = this.getInterpolation() === InterpolateSmooth,
  153. writeIndex = 1,
  154. lastIndex = times.length - 1;
  155. for ( var i = 1; i < lastIndex; ++ i ) {
  156. var keep = false;
  157. var time = times[ i ];
  158. var timeNext = times[ i + 1 ];
  159. // remove adjacent keyframes scheduled at the same time
  160. if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
  161. if ( ! smoothInterpolation ) {
  162. // remove unnecessary keyframes same as their neighbors
  163. var offset = i * stride,
  164. offsetP = offset - stride,
  165. offsetN = offset + stride;
  166. for ( var j = 0; j !== stride; ++ j ) {
  167. var value = values[ offset + j ];
  168. if ( value !== values[ offsetP + j ] ||
  169. value !== values[ offsetN + j ] ) {
  170. keep = true;
  171. break;
  172. }
  173. }
  174. } else keep = true;
  175. }
  176. // in-place compaction
  177. if ( keep ) {
  178. if ( i !== writeIndex ) {
  179. times[ writeIndex ] = times[ i ];
  180. var readOffset = i * stride,
  181. writeOffset = writeIndex * stride;
  182. for ( var j = 0; j !== stride; ++ j )
  183. values[ writeOffset + j ] = values[ readOffset + j ];
  184. }
  185. ++ writeIndex;
  186. }
  187. }
  188. // flush last keyframe (compaction looks ahead)
  189. if ( lastIndex > 0 ) {
  190. times[ writeIndex ] = times[ lastIndex ];
  191. for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j )
  192. values[ writeOffset + j ] = values[ readOffset + j ];
  193. ++ writeIndex;
  194. }
  195. if ( writeIndex !== times.length ) {
  196. this.times = AnimationUtils.arraySlice( times, 0, writeIndex );
  197. this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );
  198. }
  199. return this;
  200. }
  201. };
  202. export { KeyframeTrackPrototype };