KeyframeTrack.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /**
  2. *
  3. * A timed sequence of keyframes for a specific property.
  4. *
  5. *
  6. * @author Ben Houston / http://clara.io/
  7. * @author David Sarno / http://lighthaus.us/
  8. * @author tschw
  9. */
  10. THREE.KeyframeTrack = function ( name, times, values, interpolation ) {
  11. if( name === undefined ) throw new Error( "track name is undefined" );
  12. if( times === undefined || times.length === 0 ||
  13. values === undefined || values.length === 0 ) {
  14. throw new Error( "no keyframes in track named " + name );
  15. }
  16. this.name = name;
  17. this.times = THREE.AnimationUtils.convertArray( times, this.TimeBufferType );
  18. this.values = THREE.AnimationUtils.convertArray( values, this.ValueBufferType );
  19. this.setInterpolation( interpolation || this.DefaultInterpolation );
  20. this.validate();
  21. this.optimize();
  22. };
  23. THREE.KeyframeTrack.prototype = {
  24. constructor: THREE.KeyframeTrack,
  25. TimeBufferType: Float64Array,
  26. ValueBufferType: Float64Array,
  27. DefaultInterpolation: THREE.InterpolateLinear,
  28. InterpolantFactoryMethodDiscrete: function( result ) {
  29. return new THREE.DiscreteInterpolant(
  30. this.times, this.values, this.getValueSize(), result );
  31. },
  32. InterpolantFactoryMethodLinear: function( result ) {
  33. return new THREE.LinearInterpolant(
  34. this.times, this.values, this.getValueSize(), result );
  35. },
  36. InterpolantFactoryMethodSmooth: function( result ) {
  37. return new THREE.SmoothInterpolant(
  38. this.times, this.values, this.getValueSize(), result );
  39. },
  40. setInterpolation: function( interpolation ) {
  41. var factoryMethod = undefined;
  42. switch ( interpolation ) {
  43. case THREE.InterpolateDiscrete:
  44. factoryMethod = this.InterpolantFactoryMethodDiscrete;
  45. break;
  46. case THREE.InterpolateLinear:
  47. factoryMethod = this.InterpolantFactoryMethodLinear;
  48. break;
  49. case THREE.InterpolateSmooth:
  50. factoryMethod = this.InterpolantFactoryMethodSmooth;
  51. break;
  52. }
  53. if ( factoryMethod === undefined ) {
  54. var message = "unsupported interpolation for " +
  55. this.ValueTypeName + " keyframe track named " + this.name;
  56. if ( this.createInterpolant === undefined ) {
  57. // fall back to default, unless the default itself is messed up
  58. if ( interpolation !== this.DefaultInterpolation ) {
  59. this.setInterpolation( this.DefaultInterpolation );
  60. } else {
  61. throw new Error( message ); // fatal, in this case
  62. }
  63. }
  64. console.warn( message );
  65. return;
  66. }
  67. this.createInterpolant = factoryMethod;
  68. },
  69. getInterpolation: function() {
  70. switch ( this.createInterpolant ) {
  71. case this.InterpolantFactoryMethodDiscrete:
  72. return THREE.InterpolateDiscrete;
  73. case this.InterpolantFactoryMethodLinear:
  74. return THREE.InterpolateLinear;
  75. case this.InterpolantFactoryMethodSmooth:
  76. return THREE.InterpolateSmooth;
  77. }
  78. },
  79. getValueSize: function() {
  80. return this.values.length / this.times.length;
  81. },
  82. // move all keyframes either forwards or backwards in time
  83. shift: function( timeOffset ) {
  84. if( timeOffset !== 0.0 ) {
  85. var times = this.times;
  86. for( var i = 0, n = times.length; i !== n; ++ i ) {
  87. times[ i ] += timeOffset;
  88. }
  89. }
  90. return this;
  91. },
  92. // scale all keyframe times by a factor (useful for frame <-> seconds conversions)
  93. scale: function( timeScale ) {
  94. if( timeScale !== 1.0 ) {
  95. var times = this.times;
  96. for( var i = 0, n = times.length; i !== n; ++ i ) {
  97. times[ i ] *= timeScale;
  98. }
  99. }
  100. return this;
  101. },
  102. // removes keyframes before and after animation without changing any values within the range [startTime, endTime].
  103. // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values
  104. trim: function( startTime, endTime ) {
  105. var times = this.times;
  106. var nKeys = times.length;
  107. var firstKeysToRemove = 0;
  108. for ( var i = 1; i !== nKeys; ++ i ) {
  109. if ( times[i] <= startTime ) ++ firstKeysToRemove;
  110. }
  111. var lastKeysToRemove = 0;
  112. for ( var i = nKeys - 2; i !== 0; -- i ) {
  113. if ( times[i] >= endTime ) ++ lastKeysToRemove;
  114. else break;
  115. }
  116. // remove last keys first because it doesn't affect the position of the first keys (the otherway around doesn't work as easily)
  117. if( ( firstKeysToRemove + lastKeysToRemove ) !== 0 ) {
  118. var from = firstKeysToRemove;
  119. var to = nKeys - lastKeysToRemove - firstKeysToRemove;
  120. this.times = THREE.AnimationUtils.arraySlice( times, from, to );
  121. var values = this.values;
  122. var stride = this.getValueSize();
  123. this.values = THREE.AnimationUtils.arraySlice( values, from * stride, to * stride );
  124. }
  125. return this;
  126. },
  127. // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable
  128. // One could eventually ensure that all key.values in a track are all of the same type (otherwise interpolation makes no sense.)
  129. validate: function() {
  130. if ( this.getValueSize() % 1 !== 0 ) {
  131. throw new Error( "invalid value size for track named " + this.name );
  132. }
  133. var times = this.times;
  134. var values = this.values;
  135. var stride = this.getValueSize();
  136. var nKeys = times.length;
  137. if( nKeys === 0 ) {
  138. console.error( " track is empty, no keys", this );
  139. return;
  140. }
  141. var prevTime = null;
  142. for( var i = 0; i !== nKeys; i ++ ) {
  143. var currTime = times[ i ];
  144. if( currTime === undefined || currTime === null ) {
  145. console.error( " time is null in track", this, i );
  146. return;
  147. }
  148. if( ( typeof currTime ) !== 'number' || Number.isNaN( currTime ) ) {
  149. console.error( " time is not a valid number", this, i, currTime );
  150. return;
  151. }
  152. var offset = i * stride;
  153. for ( var j = offset, e = offset + stride; j !== e; ++ j ) {
  154. var value = values[ j ];
  155. if( value === undefined || value === null) {
  156. console.error( " value is null in track", this, j, value );
  157. return;
  158. }
  159. }
  160. if( prevTime !== null && prevTime > currTime ) {
  161. console.error( " time is less than previous key time, out of order keys", this, i, currTime, prevTime );
  162. return;
  163. }
  164. prevTime = currTime;
  165. }
  166. return this;
  167. },
  168. // removes equivalent sequential keys as common in morph target sequences
  169. // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)
  170. optimize: function() {
  171. var times = this.times;
  172. var values = this.values;
  173. var stride = this.getValueSize();
  174. var writeIndex = 1;
  175. for( var i = 1, n = times.length - 1; i <= n; ++ i ) {
  176. var keep = false;
  177. var time = times[ i ];
  178. var timeNext = times[ i + 1 ];
  179. // remove adjacent keyframes scheduled at the same time
  180. if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {
  181. // remove unnecessary keyframes same as their neighbors
  182. var offset = i * stride;
  183. var offsetP = offset - stride;
  184. var offsetN = offset + stride;
  185. for ( var j = 0; j !== stride; ++ j ) {
  186. var value = values[ offset + j ];
  187. if ( value !== values[ offsetP + j ] ||
  188. value !== values[ offsetN + j ] ) {
  189. keep = true;
  190. break;
  191. }
  192. }
  193. }
  194. // in-place compaction
  195. if ( keep ) {
  196. if ( i !== writeIndex ) {
  197. times[ writeIndex ] = times[ i ];
  198. var readOffset = i * stride;
  199. var writeOffset = writeIndex * stride;
  200. for ( var j = 0; j !== stride; ++ j ) {
  201. values[ writeOffset + j ] = values[ readOffset + j ];
  202. }
  203. }
  204. ++ writeIndex;
  205. }
  206. }
  207. if ( writeIndex !== times.length ) {
  208. this.times = THREE.AnimationUtils.arraySlice( times, 0, writeIndex );
  209. this.values = THREE.AnimationUtils.arraySlice( values, 0, writeIndex * stride );
  210. }
  211. return this;
  212. }
  213. };
  214. // Serialization (in static context, because of constructor invocation
  215. // and automatic invocation of .toJSON):
  216. THREE.KeyframeTrack.parse = function( json ) {
  217. if( json[ 'type' ] === undefined ) {
  218. throw new Error( "track type undefined, can not parse" );
  219. }
  220. var trackType = THREE.KeyframeTrack.GetTrackTypeForValueTypeName( json.type );
  221. if ( json[ 'times' ] === undefined ) {
  222. console.warn( "legacy JSON format detected, converting" );
  223. var times = [], values = [];
  224. THREE.AnimationUtils.flattenJSON( json, times, values, 'value' );
  225. json[ 'times' ] = times;
  226. json[ 'values' ] = values;
  227. }
  228. // derived classes can define a static parse method
  229. if ( trackType.parse !== undefined ) {
  230. return trackType.parse( json );
  231. } else {
  232. // by default, we asssume a constructor compatible with the base
  233. return new trackType(
  234. json[ 'name' ], json[ 'times' ], json[ 'values' ], json[ 'interpolation' ] );
  235. }
  236. };
  237. THREE.KeyframeTrack.toJSON = function( track ) {
  238. var trackType = track.constructor;
  239. var json;
  240. // derived classes can define a static toJSON method
  241. if ( trackType.toJSON !== undefined ) {
  242. json = trackType.toJSON( track );
  243. } else {
  244. // by default, we assume the data can be serialized as-is
  245. json = {
  246. 'name': track.name,
  247. 'times': THREE.AnimationUtils.convertArray( track.times, Array ),
  248. 'values': THREE.AnimationUtils.convertArray( track.values, Array )
  249. };
  250. var interpolation = track.getInterpolation();
  251. if ( interpolation !== track.DefaultInterpolation ) {
  252. json[ 'interpolation' ] = interpolation;
  253. }
  254. }
  255. json[ 'type' ] = track.ValueTypeName; // mandatory
  256. return json;
  257. };
  258. THREE.KeyframeTrack.GetTrackTypeForValueTypeName = function( typeName ) {
  259. switch( typeName.toLowerCase() ) {
  260. case "scalar":
  261. case "double":
  262. case "float":
  263. case "number":
  264. case "integer":
  265. return THREE.NumberKeyframeTrack;
  266. case "vector":
  267. case "vector2":
  268. case "vector3":
  269. case "vector4":
  270. return THREE.VectorKeyframeTrack;
  271. case "color":
  272. return THREE.ColorKeyframeTrack;
  273. case "quaternion":
  274. return THREE.QuaternionKeyframeTrack;
  275. case "bool":
  276. case "boolean":
  277. return THREE.BooleanKeyframeTrack;
  278. case "string":
  279. return THREE.StringKeyframeTrack;
  280. };
  281. throw new Error( "Unsupported typeName: " + typeName );
  282. <<<<<<< HEAD
  283. =======
  284. >>>>>>> Animation: Interpolants & extensibility overhaul.
  285. };
粤ICP备19079148号