aboutsummaryrefslogtreecommitdiff
path: root/js/helper-classes/RDGE/src/core/script/animation.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/helper-classes/RDGE/src/core/script/animation.js')
-rw-r--r--js/helper-classes/RDGE/src/core/script/animation.js322
1 files changed, 322 insertions, 0 deletions
diff --git a/js/helper-classes/RDGE/src/core/script/animation.js b/js/helper-classes/RDGE/src/core/script/animation.js
new file mode 100644
index 00000000..63eca0a2
--- /dev/null
+++ b/js/helper-classes/RDGE/src/core/script/animation.js
@@ -0,0 +1,322 @@
1/* <copyright>
2This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
3No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/>
4(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved.
5</copyright> */
6
7var g_numChannels = new stat( "animation", "numChannels", 0, null, false );
8var g_numTracks = new stat( "animation", "numTracks", 0, null, false );
9
10/**
11 * channelController
12 * The channel controller is really the workhorse of the RDGE animation system. It handles timing,
13 * interpolation, and sampling of attributes over the lifetime of an animation. Each channel controller
14 * is responsible for animating a single attribute. The current implementation supports animating vector,
15 * boolean, or quaternion attributes. This class is used internally by the animation system.
16 *
17 * @param _animation - the animation resource
18 * @param _channel - the channel id
19 *
20 */
21channelController = function(_animation, _channel) {
22 /**
23 * this.interpolate - Enable/Disable interpolation between animation frames.
24 * Typically this should be enabled for smoother looking animation. However,
25 * there may be applications where interpolation is undesireable.
26 */
27 this.interpolate = false;
28
29 /**
30 * this.animation - the animation resource.
31 * This is where the keyframes for the channel are stored.
32 */
33 this.animation = _animation;
34
35 /**
36 * this.channel - the channel id. This is used to look up the keyframe data for this channel.
37 */
38 this.channel = _channel;
39
40 /**
41 * this.localTime - the current time, relative to the start time.
42 */
43 this.localTime = 0.0;
44
45 /**
46 * this.startTime - the start time of the animation clip window.
47 */
48 this.startTime = this.animation.clipStart / this.animation.framesPerSec;
49
50 /**
51 * this.endTime - the end time of the animation clip window.
52 */
53 this.endTime = this.animation.clipEnd / this.animation.framesPerSec;
54
55 /**
56 * this.cachedFrame - cached frame index, this optimizes best case scenario computeFrame calls.
57 */
58 this.cachedFrame = -1;
59
60 /**
61 * oneFrameInSecs - stores the interval of a single frame in seconds. This is used for internal calculations.
62 */
63 oneFrameInSecs = 1.0 / _animation.framesPerSec;
64
65 /**
66 * this.channel.timeline - stores the animation timeline.
67 * Currently this is calculated based on the framePerSec settings of the animation.
68 * Eventually the timeline should be exported with the animation. Individual channels
69 * may have different timelines depending on which frames are keyed.
70 */
71 this.channel.timeline = new Array(this.channel.numKeys + 1);
72 for (i = 0; i <= this.channel.numKeys; ++i) {
73 this.channel.timeline[i] = i / this.animation.framesPerSec;
74 }
75
76 /** this.computeFrame
77 * Calculates the current frame index of the animation at the current time.
78 * In the worst case, this function will perform a binary search for the frame
79 * whose time is closest to and less than the current time. In the best case,
80 * the current frame is near the most recently cached frame, or it remains unchanged.
81 */
82 this.computeFrame = function() {
83 var absTime = this.localTime + this.startTime;
84 var start = this.animation.clipStart;
85 var end = this.animation.clipEnd;
86
87 if (this.cachedFrame != -1) {
88 // if the current time is reasonably close to the last frame processed try searching
89 // forward or backward. best case it is the next frame.
90 if (Math.abs(absTime - this.channel.timeline[this.cachedFrame]) < 5 * oneFrameInSecs) {
91 if (this.channel.timeline[this.cachedFrame] < absTime) {
92 while (this.channel.timeline[this.cachedFrame + 1] <= absTime) {
93 this.cachedFrame++;
94 if (this.animation.looping) {
95 if (this.cachedFrame > this.numFrames - 1) {
96 this.cachedFrame -= this.numFrames - 1;
97 }
98 } else {
99 if (this.cachedFrame > this.numFrames - 1) {
100 this.cachedFrame = this.numFrames - 1;
101 }
102 }
103 }
104 return this.cachedFrame;
105
106 } else {
107 while (this.channel.timeline[this.cachedFrame] > absTime) {
108 this.cachedFrame--;
109 if (this.animation.looping) {
110 if (this.cachedFrame < 0) {
111 this.cachedFrame += this.numFrames - 1;
112 }
113 } else {
114 if (this.cachedFrame > this.numFrames - 1) {
115 this.cachedFrame = this.numFrames - 1;
116 }
117 }
118 }
119 return this.cachedFrame;
120 }
121 }
122 }
123
124 // binary search...
125 while (start + 1 < end) {
126 var mid = Math.floor((start + end) / 2);
127 if (absTime > this.channel.timeline[mid]) {
128 start = mid + 1;
129 } else {
130 end = mid - 1;
131 }
132 }
133
134 this.cachedFrame = start;
135
136 return start;
137 }
138
139 /* this.sampleBool - Sample a boolean at the current frame, booleans are not interpolated.
140 * This function is used internally.
141 */
142 this.sampleBool = function() {
143 // no interpolation on flags...
144 var index = this.computeFrame();
145 return this.channel.keys[index];
146 }
147
148 /* this.sampleQuat - Sample a quaternion at the current frame.
149 * if this.interpolate == true, quaternions are interpolated inbetween frames using spherical linear interpolation (SLERP).
150 */
151 this.sampleQuat = function() {
152 var frame0 = this.computeFrame();
153 var frame1 = frame0 + 1;
154
155 var k0 = this.channel.timeline[frame0];
156 var k1 = this.channel.timeline[frame1];
157 var index0 = frame0 * 4;
158 var index1 = frame1 * 4;
159
160 if (this.interpolate) {
161 var absTime = this.localTime + this.startTime;
162 var t = (absTime - k0) / (k1 - k0);
163 var a = [this.channel.keys[index0 + 0], this.channel.keys[index0 + 1], this.channel.keys[index0 + 2], this.channel.keys[index0 + 3]];
164 var b = [this.channel.keys[index1 + 0], this.channel.keys[index1 + 1], this.channel.keys[index1 + 2], this.channel.keys[index1 + 3]];
165 return quat.slerp(a, b, t);
166 }
167
168 return [this.channel.keys[index0 + 0], this.channel.keys[index0 + 1], this.channel.keys[index0 + 2], this.channel.keys[index0 + 3]];
169 }
170
171 /* this.sampleVec3 - Sample a vector3 at the current frame.
172 * if this.interpolate == true, vectors are interpolated inbetween frames using linear interpolation (LERP).
173 */
174 this.sampleVec3 = function() {
175 var frame0 = this.computeFrame();
176 var frame1 = frame0 + 1;
177
178 var k0 = this.channel.timeline[frame0];
179 var k1 = this.channel.timeline[frame1];
180 var index0 = frame0 * 3;
181 var index1 = frame1 * 3;
182
183 if (this.interpolate) {
184 var absTime = this.localTime + this.startTime;
185 var t = (absTime - k0) / (k1 - k0);
186 var a = [this.channel.keys[index0 + 0], this.channel.keys[index0 + 1], this.channel.keys[index0 + 2]];
187 var b = [this.channel.keys[index1 + 0], this.channel.keys[index1 + 1], this.channel.keys[index1 + 2]];
188
189 return vec3.lerp(a, b, t);
190 }
191 return [this.channel.keys[index0 + 0], this.channel.keys[index0 + 1], this.channel.keys[index0 + 2]];
192 }
193
194 /* this.setTime - set the current time.
195 */
196 this.setTime = function(t) {
197 this.localTime = t;
198 if (this.localTime < 0.0) {
199 this.localTime = 0.0;
200 }
201 if (this.localTime > this.animation.duration - oneFrameInSecs) {
202 this.localTime = this.animation.duration - oneFrameInSecs;
203 }
204 }
205
206 /* this.setProgress - set the current time as a percentage of the duration.
207 */
208 this.setProgress = function(f) {
209 this.setTime(f * this.animation.duration);
210 }