babylon.runtimeAnimation.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. module BABYLON {
  2. export class RuntimeAnimation {
  3. public currentFrame: number;
  4. private _animation: Animation;
  5. private _target: any;
  6. private _originalBlendValue: any;
  7. private _offsetsCache: {[key: string]: any} = {};
  8. private _highLimitsCache: {[key: string]: any} = {};
  9. private _stopped = false;
  10. private _blendingFactor = 0;
  11. public constructor(target: any, animation: Animation) {
  12. this._animation = animation;
  13. this._target = target;
  14. animation._runtimeAnimations.push(this);
  15. }
  16. public get animation(): Animation {
  17. return this._animation;
  18. }
  19. public reset(): void {
  20. this._offsetsCache = {};
  21. this._highLimitsCache = {};
  22. this.currentFrame = 0;
  23. this._blendingFactor = 0;
  24. this._originalBlendValue = null;
  25. }
  26. public isStopped(): boolean {
  27. return this._stopped;
  28. }
  29. public dispose(): void {
  30. let index = this._animation.runtimeAnimations.indexOf(this);
  31. if (index > -1) {
  32. this._animation.runtimeAnimations.splice(index, 1);
  33. }
  34. }
  35. private _getKeyValue(value: any): any {
  36. if (typeof value === "function") {
  37. return value();
  38. }
  39. return value;
  40. }
  41. private _interpolate(currentFrame: number, repeatCount: number, loopMode?: number, offsetValue?: any, highLimitValue?: any) {
  42. if (loopMode === Animation.ANIMATIONLOOPMODE_CONSTANT && repeatCount > 0) {
  43. return highLimitValue.clone ? highLimitValue.clone() : highLimitValue;
  44. }
  45. this.currentFrame = currentFrame;
  46. let keys = this._animation.getKeys();
  47. // Try to get a hash to find the right key
  48. var startKeyIndex = Math.max(0, Math.min(keys.length - 1, Math.floor(keys.length * (currentFrame - keys[0].frame) / (keys[keys.length - 1].frame - keys[0].frame)) - 1));
  49. if (keys[startKeyIndex].frame >= currentFrame) {
  50. while (startKeyIndex - 1 >= 0 && keys[startKeyIndex].frame >= currentFrame) {
  51. startKeyIndex--;
  52. }
  53. }
  54. for (var key = startKeyIndex; key < keys.length; key++) {
  55. var endKey = keys[key + 1];
  56. if (endKey.frame >= currentFrame) {
  57. var startKey = keys[key];
  58. var startValue = this._getKeyValue(startKey.value);
  59. var endValue = this._getKeyValue(endKey.value);
  60. var useTangent = startKey.outTangent !== undefined && endKey.inTangent !== undefined;
  61. var frameDelta = endKey.frame - startKey.frame;
  62. // gradient : percent of currentFrame between the frame inf and the frame sup
  63. var gradient = (currentFrame - startKey.frame) / frameDelta;
  64. // check for easingFunction and correction of gradient
  65. let easingFunction = this._animation.getEasingFunction();
  66. if (easingFunction != null) {
  67. gradient = easingFunction.ease(gradient);
  68. }
  69. switch (this._animation.dataType) {
  70. // Float
  71. case Animation.ANIMATIONTYPE_FLOAT:
  72. var floatValue = useTangent ? this._animation.floatInterpolateFunctionWithTangents(startValue, startKey.outTangent * frameDelta, endValue, endKey.inTangent * frameDelta, gradient) : this._animation.floatInterpolateFunction(startValue, endValue, gradient);
  73. switch (loopMode) {
  74. case Animation.ANIMATIONLOOPMODE_CYCLE:
  75. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  76. return floatValue;
  77. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  78. return offsetValue * repeatCount + floatValue;
  79. }
  80. break;
  81. // Quaternion
  82. case Animation.ANIMATIONTYPE_QUATERNION:
  83. var quatValue = useTangent ? this._animation.quaternionInterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient) : this._animation.quaternionInterpolateFunction(startValue, endValue, gradient);
  84. switch (loopMode) {
  85. case Animation.ANIMATIONLOOPMODE_CYCLE:
  86. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  87. return quatValue;
  88. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  89. return quatValue.add(offsetValue.scale(repeatCount));
  90. }
  91. return quatValue;
  92. // Vector3
  93. case Animation.ANIMATIONTYPE_VECTOR3:
  94. var vec3Value = useTangent ? this._animation.vector3InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient) : this._animation.vector3InterpolateFunction(startValue, endValue, gradient);
  95. switch (loopMode) {
  96. case Animation.ANIMATIONLOOPMODE_CYCLE:
  97. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  98. return vec3Value;
  99. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  100. return vec3Value.add(offsetValue.scale(repeatCount));
  101. }
  102. // Vector2
  103. case Animation.ANIMATIONTYPE_VECTOR2:
  104. var vec2Value = useTangent ? this._animation.vector2InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient) : this._animation.vector2InterpolateFunction(startValue, endValue, gradient);
  105. switch (loopMode) {
  106. case Animation.ANIMATIONLOOPMODE_CYCLE:
  107. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  108. return vec2Value;
  109. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  110. return vec2Value.add(offsetValue.scale(repeatCount));
  111. }
  112. // Size
  113. case Animation.ANIMATIONTYPE_SIZE:
  114. switch (loopMode) {
  115. case Animation.ANIMATIONLOOPMODE_CYCLE:
  116. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  117. return this._animation.sizeInterpolateFunction(startValue, endValue, gradient);
  118. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  119. return this._animation.sizeInterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
  120. }
  121. // Color3
  122. case Animation.ANIMATIONTYPE_COLOR3:
  123. switch (loopMode) {
  124. case Animation.ANIMATIONLOOPMODE_CYCLE:
  125. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  126. return this._animation.color3InterpolateFunction(startValue, endValue, gradient);
  127. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  128. return this._animation.color3InterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
  129. }
  130. // Matrix
  131. case Animation.ANIMATIONTYPE_MATRIX:
  132. switch (loopMode) {
  133. case Animation.ANIMATIONLOOPMODE_CYCLE:
  134. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  135. if (Animation.AllowMatricesInterpolation) {
  136. return this._animation.matrixInterpolateFunction(startValue, endValue, gradient);
  137. }
  138. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  139. return startValue;
  140. }
  141. default:
  142. break;
  143. }
  144. break;
  145. }
  146. }
  147. return this._getKeyValue(keys[keys.length - 1].value);
  148. }
  149. public setValue(currentValue: any, blend: boolean = false): void {
  150. // Set value
  151. var path: any;
  152. var destination: any;
  153. let targetPropertyPath = this._animation.targetPropertyPath
  154. if (targetPropertyPath.length > 1) {
  155. var property = this._target[targetPropertyPath[0]];
  156. for (var index = 1; index < targetPropertyPath.length - 1; index++) {
  157. property = property[targetPropertyPath[index]];
  158. }
  159. path = targetPropertyPath[targetPropertyPath.length - 1];
  160. destination = property;
  161. } else {
  162. path = targetPropertyPath[0];
  163. destination = this._target;
  164. }
  165. // Blending
  166. if (this._animation.enableBlending && this._blendingFactor <= 1.0) {
  167. if (!this._originalBlendValue) {
  168. if (destination[path].clone) {
  169. this._originalBlendValue = destination[path].clone();
  170. } else {
  171. this._originalBlendValue = destination[path];
  172. }
  173. }
  174. if (this._originalBlendValue.prototype) { // Complex value
  175. if (this._originalBlendValue.prototype.Lerp) { // Lerp supported
  176. destination[path] = this._originalBlendValue.construtor.prototype.Lerp(currentValue, this._originalBlendValue, this._blendingFactor);
  177. } else { // Blending not supported
  178. destination[path] = currentValue;
  179. }
  180. } else if (this._originalBlendValue.m) { // Matrix
  181. destination[path] = Matrix.Lerp(this._originalBlendValue, currentValue, this._blendingFactor);
  182. } else { // Direct value
  183. destination[path] = this._originalBlendValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
  184. }
  185. this._blendingFactor += this._animation.blendingSpeed;
  186. } else {
  187. destination[path] = currentValue;
  188. }
  189. if (this._target.markAsDirty) {
  190. this._target.markAsDirty(this._animation.targetProperty);
  191. }
  192. }
  193. public goToFrame(frame: number): void {
  194. let keys = this._animation.getKeys();
  195. if (frame < keys[0].frame) {
  196. frame = keys[0].frame;
  197. } else if (frame > keys[keys.length - 1].frame) {
  198. frame = keys[keys.length - 1].frame;
  199. }
  200. var currentValue = this._interpolate(frame, 0, this._animation.loopMode);
  201. this.setValue(currentValue);
  202. }
  203. public _prepareForSpeedRatioChange(newSpeedRatio: number): void {
  204. let newRatio = this._previousDelay * (this._animation.framePerSecond * newSpeedRatio) / 1000.0;
  205. this._ratioOffset = this._previousRatio - newRatio;
  206. }
  207. private _ratioOffset = 0;
  208. private _previousDelay: number;
  209. private _previousRatio: number;
  210. public animate(delay: number, from: number, to: number, loop: boolean, speedRatio: number, blend: boolean = false): boolean {
  211. let targetPropertyPath = this._animation.targetPropertyPath
  212. if (!targetPropertyPath || targetPropertyPath.length < 1) {
  213. this._stopped = true;
  214. return false;
  215. }
  216. var returnValue = true;
  217. let keys = this._animation.getKeys();
  218. // Adding a start key at frame 0 if missing
  219. if (keys[0].frame !== 0) {
  220. var newKey = { frame: 0, value: keys[0].value };
  221. keys.splice(0, 0, newKey);
  222. }
  223. // Check limits
  224. if (from < keys[0].frame || from > keys[keys.length - 1].frame) {
  225. from = keys[0].frame;
  226. }
  227. if (to < keys[0].frame || to > keys[keys.length - 1].frame) {
  228. to = keys[keys.length - 1].frame;
  229. }
  230. //to and from cannot be the same key
  231. if(from === to) {
  232. if (from > keys[0].frame) {
  233. from--;
  234. } else if (to < keys[keys.length - 1].frame) {
  235. to++;
  236. }
  237. }
  238. // Compute ratio
  239. var range = to - from;
  240. var offsetValue;
  241. // ratio represents the frame delta between from and to
  242. var ratio = (delay * (this._animation.framePerSecond * speedRatio) / 1000.0) + this._ratioOffset;
  243. var highLimitValue = 0;
  244. this._previousDelay = delay;
  245. this._previousRatio = ratio;
  246. if (((to > from && ratio > range) || (from > to && ratio < range)) && !loop) { // If we are out of range and not looping get back to caller
  247. returnValue = false;
  248. highLimitValue = this._getKeyValue(keys[keys.length - 1].value);
  249. } else {
  250. // Get max value if required
  251. if (this._animation.loopMode !== Animation.ANIMATIONLOOPMODE_CYCLE) {
  252. var keyOffset = to.toString() + from.toString();
  253. if (!this._offsetsCache[keyOffset]) {
  254. var fromValue = this._interpolate(from, 0, Animation.ANIMATIONLOOPMODE_CYCLE);
  255. var toValue = this._interpolate(to, 0, Animation.ANIMATIONLOOPMODE_CYCLE);
  256. switch (this._animation.dataType) {
  257. // Float
  258. case Animation.ANIMATIONTYPE_FLOAT:
  259. this._offsetsCache[keyOffset] = toValue - fromValue;
  260. break;
  261. // Quaternion
  262. case Animation.ANIMATIONTYPE_QUATERNION:
  263. this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
  264. break;
  265. // Vector3
  266. case Animation.ANIMATIONTYPE_VECTOR3:
  267. this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
  268. // Vector2
  269. case Animation.ANIMATIONTYPE_VECTOR2:
  270. this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
  271. // Size
  272. case Animation.ANIMATIONTYPE_SIZE:
  273. this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
  274. // Color3
  275. case Animation.ANIMATIONTYPE_COLOR3:
  276. this._offsetsCache[keyOffset] = toValue.subtract(fromValue);
  277. default:
  278. break;
  279. }
  280. this._highLimitsCache[keyOffset] = toValue;
  281. }
  282. highLimitValue = this._highLimitsCache[keyOffset];
  283. offsetValue = this._offsetsCache[keyOffset];
  284. }
  285. }
  286. if (offsetValue === undefined) {
  287. switch (this._animation.dataType) {
  288. // Float
  289. case Animation.ANIMATIONTYPE_FLOAT:
  290. offsetValue = 0;
  291. break;
  292. // Quaternion
  293. case Animation.ANIMATIONTYPE_QUATERNION:
  294. offsetValue = new Quaternion(0, 0, 0, 0);
  295. break;
  296. // Vector3
  297. case Animation.ANIMATIONTYPE_VECTOR3:
  298. offsetValue = Vector3.Zero();
  299. break;
  300. // Vector2
  301. case Animation.ANIMATIONTYPE_VECTOR2:
  302. offsetValue = Vector2.Zero();
  303. break;
  304. // Size
  305. case Animation.ANIMATIONTYPE_SIZE:
  306. offsetValue = Size.Zero();
  307. break;
  308. // Color3
  309. case Animation.ANIMATIONTYPE_COLOR3:
  310. offsetValue = Color3.Black();
  311. }
  312. }
  313. // Compute value
  314. var repeatCount = (ratio / range) >> 0;
  315. var currentFrame = returnValue ? from + ratio % range : to;
  316. var currentValue = this._interpolate(currentFrame, repeatCount, this._animation.loopMode, offsetValue, highLimitValue);
  317. // Set value
  318. this.setValue(currentValue);
  319. // Check events
  320. let events = this._animation.getEvents();
  321. for (var index = 0; index < events.length; index++) {
  322. // Make sure current frame has passed event frame and that event frame is within the current range
  323. // Also, handle both forward and reverse animations
  324. if (
  325. (range > 0 && currentFrame >= events[index].frame && events[index].frame >= from) ||
  326. (range < 0 && currentFrame <= events[index].frame && events[index].frame <= from)
  327. ){
  328. var event = events[index];
  329. if (!event.isDone) {
  330. // If event should be done only once, remove it.
  331. if (event.onlyOnce) {
  332. events.splice(index, 1);
  333. index--;
  334. }
  335. event.isDone = true;
  336. event.action();
  337. } // Don't do anything if the event has already be done.
  338. } else if (events[index].isDone && !events[index].onlyOnce) {
  339. // reset event, the animation is looping
  340. events[index].isDone = false;
  341. }
  342. }
  343. if (!returnValue) {
  344. this._stopped = true;
  345. }
  346. return returnValue;
  347. }
  348. }
  349. }