~etc-pgh-launchpad/wildpockets/trunk

« back to all changes in this revision

Viewing changes to scripts/Animation.lua

  • Committer: etc-pgh-launchpad at cmu
  • Date: 2010-11-30 20:56:30 UTC
  • Revision ID: etc-pgh-launchpad@lists.andrew.cmu.edu-20101130205630-0blbkcz28ovjl8wj
Committing the Wild Pockets code base to Launchpad.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
--[[
 
2
Wild Pockets
 
3
 
 
4
Copyright (c) 2010 Carnegie Mellon University
 
5
 
 
6
Permission is hereby granted, free of charge, to any person obtaining a copy
 
7
of this software and associated documentation files (the "Software"), to deal
 
8
in the Software without restriction, including without limitation the rights
 
9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
10
copies of the Software, and to permit persons to whom the Software is
 
11
furnished to do so, subject to the following conditions:
 
12
 
 
13
The above copyright notice and this permission notice shall be included in
 
14
all copies or substantial portions of the Software.
 
15
 
 
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
22
THE SOFTWARE.
 
23
 
 
24
]]
 
25
-- Animation System. Big Lua wrapper around Pose and MultiPose to play animations
 
26
-- on scene objects. 
 
27
 
 
28
animationTable = nil
 
29
lastTime = Timer.getTime()
 
30
ProfileTimerAnimationUpdate = ProfileTimer.new("Timer::runFrame::Animation Update")
 
31
 
 
32
docs([[
 
33
    returnChannel = SceneObject:playAnimation(animation,{speed (optional), channel (optional), uniqueChannel(optional), start (optional), stop (optional), time (optional), loop (optional), callback (optional), prePad (optional), postPad (optional), fadeInTime (optional), fadeOutTime (optional), weight (optional) } (optional) )
 
34
    
 
35
    - animation: (Animation) The animation to play
 
36
    - speed: (number) Playback speed relative to the animation's recorded speed. 1 is 100% speed, 2 is 200%, .5 is 50%
 
37
    - channel: (number/string) Playback channel. Animations in different channels may be played simultaneously, animation blending will average between them if they touch the same bones.
 
38
    - uniqueChannel: (boolean) if set to true it will create a random channel and not set channel to 1. (Overridden if channel is set)
 
39
    - start: (number) Start time, in seconds.
 
40
    - stop: (number) Stop time, in seconds.
 
41
    - time: (number) Initialization time, in seconds. If the animation is looping, the first cycle will start at this time. Otherwise, this option will override 'start'.
 
42
    - loop: (boolean) if true, animation will loop once it reaches the end. If false, animation will play once.
 
43
    - callback: (function ()) Function to be called when the animation completes a cycle.
 
44
    - callTime: (number) The amount of time before the end of the animation that the callback is called (default 0)
 
45
    - prePad: (number) Seconds to stay on the first frame of the animation before starting to play (default 0)
 
46
    - postPad: (number) Seconds to stay on the last frame of the animation after playing the full animation (default 0) (has no effect when loop = true)
 
47
    - fadeInTime: (number) Seconds including prePad to fade the weight from 0 to the set weight. (default 0)
 
48
    - fadeOutTime: (number) Seconds before the end, including postPad, to start fading from the set weight to 0 (default 0)
 
49
    - fadeAll: (boolean) if set to true fades all other animations out and then back in again (default 0)
 
50
    - weight: (number 0.0-1.0) The weight as compared to other animations currently playing on different channels. (default 1)
 
51
    
 
52
    Plays an animation on the object. Several parameters can be
 
53
    specified to control the animation. Normally, the animation will play
 
54
    until it completes. The loop argument may be specified to continue
 
55
    playback. The speed argument can be used to adjust the speed of the
 
56
    animation. A speed of 1 is the default speed of the animation, 2 is
 
57
    twice as fast, .5 is half as fast. If a negative speed is specified,
 
58
    the animation will play in reverse (and either stop or loop when it
 
59
    reaches the 'start' time).
 
60
    
 
61
    Animation blending allows multiple animations to be played simultaniously
 
62
    and the engine averages between the two of them based on their given weights.
 
63
    This allows one animation to be faded out while another is faded in and a semless
 
64
    transition to be seen between them.
 
65
    
 
66
    This function always returns the channel that this animation is set to
 
67
    
 
68
]])
 
69
 
 
70
function SceneObject:playAnimation(whichAnimation,extraArgs)
 
71
 
 
72
 
 
73
    --Animations are stored in a linked list of tables.
 
74
    --This is no longer needed and if anyone wants to change it to a table that's fine
 
75
    --This was implimented back when it was needed that animations be in order for blending
 
76
    --but that only lasted a couple of days, but by then it was written and worked.
 
77
    
 
78
    --Check if Animation is a String or an animation, and if string, make it an animation
 
79
    if type(whichAnimation) == type("") then
 
80
        whichAnimation = Animation.load(whichAnimation)
 
81
    end
 
82
    
 
83
    if type(whichAnimation) ~= "Animation" then
 
84
        error("SceneObject:playAnimation must be passed an animation or the filename of an animation.")
 
85
    end
 
86
    
 
87
    
 
88
    
 
89
    --local animations = animationTable[self]
 
90
    --if animations == nil then animations = {} animations["__n__"] = 0 end
 
91
    --local firstAnim = false
 
92
    --if animations["__n__"] == 0 then firstAnim = true end
 
93
    
 
94
    
 
95
    if extraArgs==nil then
 
96
        extraArgs={}
 
97
    end
 
98
    
 
99
    
 
100
    --[[if extraArgs.uniqueChannel and not extraArgs.channel then
 
101
        local channel = 1
 
102
        while animations[channel] do
 
103
            channel = channel + 1                                    XXXXXX  REPLACE THIS!!!!!!!!!!!!!!!!!!!  XXXXX
 
104
        end
 
105
        extraArgs.channel = channel
 
106
    end]]
 
107
    
 
108
    if extraArgs.uniqueChannel and not extraArgs.channel then
 
109
        extraArgs.channel = {}
 
110
    end
 
111
 
 
112
    
 
113
    --[[
 
114
    if extraArgs.channel=="__n__" then
 
115
        error("nope, you can't make a channel '__n__'. We use that.")
 
116
    end]]
 
117
    
 
118
    
 
119
    if extraArgs.channel==nil then
 
120
        extraArgs.channel=1
 
121
    end
 
122
    
 
123
    if extraArgs.speed==nil then
 
124
        extraArgs.speed=1
 
125
    end
 
126
    if extraArgs.start==nil then
 
127
        extraArgs.start=whichAnimation:getStartTime()
 
128
    end
 
129
 
 
130
    if extraArgs.stop==nil then
 
131
        extraArgs.stop=whichAnimation:getEndTime()
 
132
    end
 
133
    if extraArgs.loop==nil then
 
134
        extraArgs.loop=false
 
135
    end
 
136
    if extraArgs.weight == nil then
 
137
        extraArgs.weight = 1
 
138
    elseif extraArgs.weight > 1 then
 
139
        extraArgs.weight = 1
 
140
    elseif extraArgs.weight < 0 then
 
141
        extraArgs.weight = 0
 
142
    end
 
143
    if extraArgs.prePad == nil then
 
144
        extraArgs.prePad = 0
 
145
    end
 
146
    if extraArgs.postPad == nil then
 
147
        extraArgs.postPad = 0
 
148
    end
 
149
    if extraArgs.fadeInTime == nil then
 
150
        extraArgs.fadeInTime = 0
 
151
    end
 
152
    if extraArgs.fadeOutTime == nil then
 
153
        extraArgs.fadeOutTime = 0
 
154
    end
 
155
    if (extraArgs.fadeInTime + extraArgs.fadeOutTime) > (extraArgs.stop - extraArgs.start) + extraArgs.prePad + extraArgs.postPad then
 
156
        error("fadeInTime and fadeOutTime are too long, cannot fade more animation than there is.")
 
157
    end
 
158
    if extraArgs.callTime == nil then
 
159
        extraArgs.callTime = extraArgs.stop
 
160
    end
 
161
 
 
162
    
 
163
    if (extraArgs.speed > 0 and extraArgs.start > extraArgs.stop) or
 
164
       (extraArgs.speed < 0 and extraArgs.start < extraArgs.stop)
 
165
    then
 
166
        local swap=extraArgs.start
 
167
        extraArgs.start=extraArgs.stop
 
168
        extraArgs.stop=swap
 
169
        
 
170
    end
 
171
    
 
172
    if extraArgs.time==nil then
 
173
        extraArgs.time=extraArgs.start
 
174
    end
 
175
    
 
176
    if extraArgs.fadeAll == nil then
 
177
        extraArgs.fadeAll = false
 
178
    end
 
179
 
 
180
    if type(extraArgs.callTime) ~= "number" then error("callTime must be a number") end
 
181
    if (type(extraArgs.callback) ~= "function" and type(extraArgs.callback) ~= "nil" and type(extraArgs.callback) ~= "no value") then error("callback must be a function") end
 
182
    if type(extraArgs.speed) ~= "number" then error("speed must be a number") end
 
183
    if type(extraArgs.time) ~= "number" then error("time must be a number") end
 
184
    if type(extraArgs.start) ~= "number" then error("start must be a number") end
 
185
    if type(extraArgs.stop) ~= "number" then error("stop must be a number") end
 
186
    if type(extraArgs.prePad) ~= "number" then error("prePad must be a number") end
 
187
    if type(extraArgs.postPad) ~= "number" then error("postPad must be a number") end
 
188
    if type(extraArgs.fadeInTime) ~= "number" then error("fadeInTime must be a number") end
 
189
    if type(extraArgs.fadeOutTime) ~= "number" then error("fadeOutTime must be a number") end
 
190
    if type(extraArgs.loop) ~= "boolean" then error("loop must be a boolean") end
 
191
    if type(extraArgs.weight) ~= "number" then error("weight must be a number") end
 
192
    if type(extraArgs.fadeAll) ~= "boolean" then error("fadeAll must be a boolean") end
 
193
    
 
194
    if extraArgs.time < extraArgs.start or extraArgs.time > extraArgs.stop then
 
195
        extraArgs.time = extraArgs.start
 
196
    end
 
197
    
 
198
    
 
199
    --if not animations[extraArgs.channel] then animations["__n__"] = animations["__n__"] + 1 end
 
200
    local fadeInAnimation = false
 
201
    local fadeOutTime = 0
 
202
    if extraArgs.fadeOutTime > extraArgs.postPad then
 
203
        fadeInAnimation = true
 
204
        fadeOutTime = extraArgs.stop + extraArgs.postPad - extraArgs.fadeOutTime
 
205
    else
 
206
        fadeOutTime = extraArgs.postPad - extraArgs.fadeOutTime
 
207
    end
 
208
    
 
209
    local runBackwards = false
 
210
    
 
211
    local absoluteSpeed = extraArgs.speed
 
212
    if absoluteSpeed < 0 then
 
213
        runBackwards = true
 
214
        absoluteSpeed = -absoluteSpeed
 
215
    end
 
216
    
 
217
    local tempAnimTable = {
 
218
        runBackwards = runBackwards,
 
219
        anim = whichAnimation,
 
220
        prePadTime = 0,
 
221
        prePadTotal = extraArgs.prePad,
 
222
        postPadTime = 0,
 
223
        postPadTotal = extraArgs.postPad,
 
224
        fadeInAnimation = fadeInAnimation,
 
225
        fadeOutTime = fadeOutTime,
 
226
        time = extraArgs.time,
 
227
        fadeAll = extraArgs.fadeAll,
 
228
        speed = absoluteSpeed,
 
229
        rangeStart = extraArgs.start,
 
230
        rangeEnd = extraArgs.stop,
 
231
        loop = extraArgs.loop,
 
232
        callback = extraArgs.callback,
 
233
        callTime = extraArgs.callTime,
 
234
        weight = extraArgs.weight,
 
235
        originalArgs = extraArgs
 
236
    }
 
237
    
 
238
    if extraArgs.fadeInTime > 0 then
 
239
        --Setup the fade in.
 
240
        tempAnimTable.fade = {
 
241
            startWeight = 0,
 
242
            goalWeight = extraArgs.weight,
 
243
            totalTime = (extraArgs.fadeInTime / math.abs(extraArgs.speed)),
 
244
            now = 0
 
245
        }
 
246
    end
 
247
    
 
248
    
 
249
    --Doing it with a linked list for easy removal addition and manipulation.
 
250
    
 
251
    
 
252
    --Finding the Object in the linked list
 
253
    local thisObject = animationTable
 
254
    local lastObject = {}
 
255
    lastObject.next = animationTable
 
256
    --print("")
 
257
    --print("")
 
258
    
 
259
    while thisObject do
 
260
        --print(1,"checking if object ", thisObject.object, " matches myself: ", self)
 
261
        if thisObject.object == self then break end
 
262
        lastObject = thisObject
 
263
        thisObject = thisObject.next
 
264
    end
 
265
    
 
266
    if thisObject == nil then
 
267
        --print(1,"Nothing Matched, creating a new object at the end.")
 
268
        lastObject.next = {next = nil,object=self,channelList=nil, lastRunTime = Timer.getTime()}
 
269
        
 
270
        thisObject=lastObject.next
 
271
        
 
272
        --print(1, "Setting ", thisObject.object, " s lastRunTime to ", thisObject.lastRunTime)
 
273
    end
 
274
    
 
275
    --print(1,"I am ", thisObject.object)
 
276
    
 
277
    if animationTable == nil then
 
278
        --print(1,"This was the first animation")
 
279
        animationTable = thisObject
 
280
    end
 
281
     
 
282
    
 
283
    
 
284
    --Fade All XXX THIS IS KINDA A HACK
 
285
    if extraArgs.fadeAll then
 
286
        local thisChannel = thisObject.channelList
 
287
        while thisChannel do
 
288
            --print(1, "Fading out channel ", thisChannel.channel ," because of fadeAll")
 
289
            local list = thisChannel.infoTable
 
290
            if not list.originalWeight then list.originalWeight = list.weight end
 
291
            list.fade = {
 
292
                startWeight = list.weight,
 
293
                goalWeight = 0,
 
294
                totalTime = (extraArgs.fadeInTime / absoluteSpeed),
 
295
                now = 0
 
296
            }       
 
297
            thisChannel = thisChannel.next
 
298
        end
 
299
    end
 
300
    
 
301
    --Finding a Channel on this object:
 
302
    
 
303
    
 
304
    local thisChannel = thisObject.channelList
 
305
    local lastChannel = {}
 
306
    lastChannel.next = thisObject.channelList
 
307
    
 
308
    while thisChannel do
 
309
        --print(1,"checking if channel ", thisChannel.channel, " matches myself: ", extraArgs.channel)
 
310
        if thisChannel.channel == extraArgs.channel then break end
 
311
        lastChannel = thisChannel
 
312
        thisChannel = thisChannel.next
 
313
    end
 
314
    if lastChannel.next == nil then
 
315
        --print(1,"Nothing Matched, creating a new channel at the end.")
 
316
        lastChannel.next = {next=nil,channel=extraArgs.channel}
 
317
        thisChannel = lastChannel.next
 
318
    end
 
319
    if thisObject.channelList == nil then
 
320
        --print(1,"This was the first Channel on this object")
 
321
        thisObject.channelList = thisChannel
 
322
    end
 
323
    
 
324
    
 
325
    --print(1,"My Channel is: \"", thisChannel.channel, "\"")
 
326
    
 
327
    
 
328
    thisChannel.infoTable = tempAnimTable
 
329
    thisObject.dirty = true
 
330
    syncObjectAnimations(thisObject)
 
331
    
 
332
    
 
333
    return extraArgs.channel
 
334
    
 
335
        --Fade all out
 
336
    --[[if (extraArgs.fadeAll) then
 
337
        for channel,animTable in pairs(animations) do
 
338
 
 
339
            if (channel ~= extraArgs.channel) and (channel ~= "__n__")  and (animTable.originalWeight == nil) then
 
340
                animTable.originalWeight = animTable.weightEnd
 
341
                animTable.timeToFade = extraArgs.fadeInTime
 
342
                animTable.fadeTime = 0
 
343
                animTable.weightStart = animTable.weight
 
344
                animTable.weightEnd = 0
 
345
                
 
346
            end
 
347
        end
 
348
    end]]
 
349
    
 
350
 end   
 
351
 
 
352
 
 
353
sandboxScope(SceneObject,"playAnimation","USER")
 
354
 
 
355
--------------------------------------------------------------------------
 
356
 
 
357
--This function takes the linked list table, not an object. 
 
358
function syncObjectAnimations(thisObject)
 
359
    --print(2,"Calculating animations on object: ", thisObject.object)
 
360
    
 
361
    if not thisObject.dirty then 
 
362
        --print(2,"The animation has already been run this frame, returning") 
 
363
        return 
 
364
    end
 
365
    local deltaTime = Timer.getTime() - thisObject.lastRunTime
 
366
    local poseList = {}
 
367
    local thisChannel = thisObject.channelList
 
368
    local lastChannel = nil
 
369
    while thisChannel do
 
370
        --print(2,"Calculating Channel: ", thisChannel.channel)
 
371
        local channel = thisChannel.channel
 
372
        local animTable = thisChannel.infoTable 
 
373
        local deltaT = deltaTime * animTable.speed
 
374
        local deadAnim = false
 
375
        --print("OH GOD HI")
 
376
        --Fading system runs regardless and independantly
 
377
        if animTable.fade then
 
378
            local fadeTable = animTable.fade
 
379
            fadeTable.now = fadeTable.now + deltaTime
 
380
            --print(2,"Fading from ", animTable.fade.startWeight, " to ", animTable.fade.goalWeight, " current at ", animTable.weight)
 
381
            if fadeTable.now >= fadeTable.totalTime or fadeTable.totalTime <= 0 then
 
382
                animTable.weight = fadeTable.goalWeight
 
383
                animTable.fade = nil
 
384
            else
 
385
                animTable.weight = ((fadeTable.now) * (fadeTable.goalWeight - fadeTable.startWeight)) / fadeTable.totalTime + fadeTable.startWeight
 
386
            end
 
387
        end
 
388
        
 
389
        
 
390
        
 
391
        
 
392
        --Ending fade
 
393
        if ((animTable.fadeInAnimation and animTable.time > animTable.fadeOutTime) or (animTable.postPadTime > animTable.fadeOutTime)) and not animTable.finalFadeout and not animTable.loop then
 
394
            --print(1,"we're fading out now")
 
395
            --print(channel)
 
396
            --print(animTable.loop)
 
397
            animTable.fade = {
 
398
                startWeight = animTable.weight,
 
399
                goalWeight = 0,
 
400
                totalTime = (animTable.originalArgs.fadeOutTime),
 
401
                now = 0,
 
402
                
 
403
            }
 
404
            if animTable.fadeAll then
 
405
                local fadeChannels = thisObject.channelList
 
406
                while fadeChannels do
 
407
                    if fadeChannels.channel ~= channel then
 
408
                        --print(1, "Fading in channel ", fadeChannels.channel ," because of fadeAll")
 
409
                        local list = fadeChannels.infoTable
 
410
                        if list.originalWeight then 
 
411
 
 
412
                            list.fade = {
 
413
                                startWeight = list.weight,
 
414
                                goalWeight = list.originalWeight,
 
415
                                now = 0,
 
416
                                totalTime = (animTable.originalArgs.fadeOutTime)
 
417
                            }  
 
418
                            list.originalWeight = nil
 
419
                        end
 
420
                    end
 
421
                    fadeChannels = fadeChannels.next
 
422
                end
 
423
            end
 
424
            animTable.finalFadeout = true
 
425
        end
 
426
        --print("OH GOD HI AGAIN")
 
427
        
 
428
        --The Meat and/or Potatoes
 
429
        
 
430
        if animTable.prePadTime < animTable.prePadTotal then
 
431
            --we're in prepad
 
432
            animTable.prePadTime = animTable.prePadTime + deltaTime
 
433
            
 
434
        elseif (animTable.time < animTable.rangeEnd and not animTable.runBackwards ) or (animTable.time > animTable.rangeEnd and animTable.runBackwards )then
 
435
            --we're playing the animation
 
436
            --print("Animation on Channel ", channel, " is playing")
 
437
            local timePassed = nil
 
438
            if animTable.runBackwards then
 
439
                animTable.time = animTable.time - deltaT
 
440
                timePassed = function (a,b) return a < b end
 
441
            else
 
442
                animTable.time = animTable.time + deltaT
 
443
                timePassed = function (a,b) return a > b end
 
444
            end
 
445
            
 
446
            --Callback System
 
447
            
 
448
            if animTable.callback and not animTable.callbackCalled and timePassed(animTable.time,animTable.callTime) then
 
449
            
 
450
                if type(animTable.callback) == "function" then
 
451
                    --print(1,"running callback")
 
452
                    animTable.callback()
 
453
                    if not thisObject.dirty then 
 
454
                        --print(1,"The callback has changed this animations status to clean.") 
 
455
                        return 
 
456
                    end
 
457
                end
 
458
                animTable.callbackCalled = true
 
459
                
 
460
            end
 
461
            --print("Animation on Channel ", channel, " is still playing")
 
462
            if timePassed(animTable.time, animTable.rangeEnd) then
 
463
                if animTable.loop then
 
464
                    if animTable.callback and not animTable.callbackCalled then
 
465
                        if type(animTable.callback) == "function" then
 
466
                            --print(1,"running callback")
 
467
                            animTable.callback()
 
468
                            animTable.callbackCalled = true
 
469
                            if not thisObject.dirty then 
 
470
                                --print(1,"The callback has changed this animations status to clean.") 
 
471
                                return 
 
472
                            end
 
473
                        end
 
474
                    end
 
475
                    animTable.callbackCalled = false
 
476
                    animTable.time = animTable.rangeStart
 
477
                else
 
478
                    animTable.time = animTable.rangeEnd
 
479
                end
 
480
                
 
481
          
 
482
            end  
 
483
            --print("Animation on Channel ", channel, " is still alive playing")
 
484
        elseif animTable.postPadTime < animTable.postPadTotal then
 
485
            --we're on the postPad
 
486
            if animTable.callback and not animTable.callbackCalled and ((animTable.time + animTable.postPadTime) > animTable.callTime) then
 
487
                if type(animTable.callback) == "function" then
 
488
                    --print(1,"running callback")
 
489
                    animTable.callback()
 
490
                    if not thisObject.dirty then 
 
491
                        --print(1,"The callback has changed this animations status to clean.") 
 
492
                        return 
 
493
                    end
 
494
                end
 
495
                animTable.callbackCalled = true
 
496
            end
 
497
            animTable.postPadTime = animTable.postPadTime + deltaTime
 
498
            
 
499
        else
 
500
            --if none of those things are true, this animation is finished.
 
501
            
 
502
            --Call the callback if it hasn't been called yet, I'm sure they'd want us to. 
 
503
            if animTable.callback and not animTable.callbackCalled then
 
504
                if type(animTable.callback) == "function" then
 
505
                    --print(1,"running callback")
 
506
                    animTable.callback()
 
507
                    animTable.callbackCalled = true
 
508
                    if not thisObject.dirty then 
 
509
                        --print(1,"The callback has changed this animations status to clean.") 
 
510
                        return 
 
511
                    end
 
512
                end
 
513
            end
 
514
            --We're completely done with this channel
 
515
            if lastChannel == nil then
 
516
                thisObject.channelList = thisChannel.next
 
517
            else
 
518
                lastChannel.next = thisChannel.next
 
519
            end
 
520
            deadAnim = true
 
521
            
 
522
        end
 
523
        
 
524
        poseList[channel] = {anim = animTable.anim, time = animTable.time, weight = animTable.weight}
 
525
        if not deadAnim then
 
526
            lastChannel = thisChannel
 
527
        end
 
528
        
 
529
        thisChannel = thisChannel.next
 
530
    end
 
531
    
 
532
    local weightList = {}
 
533
    local animationList = {}
 
534
    local timeList = {}    
 
535
    local numAnimations = 0
 
536
    for k,v in pairs(poseList) do
 
537
        numAnimations = numAnimations + 1
 
538
        weightList[numAnimations] = v.weight
 
539
        animationList[numAnimations] = v.anim
 
540
        timeList[numAnimations] = v.time
 
541
    end
 
542
    thisObject.object:multiPose(numAnimations,animationList,timeList,weightList)
 
543
        
 
544
  
 
545
end
 
546
 
 
547
--------------------------------------------------------------------------
 
548
 
 
549
 
 
550
function timeRemaining(thisTable)
 
551
    local totalTime = thisTable.prePad + thisTable.postPad + ((thisTable.stop - thisTable.start) / thisTable.speed)
 
552
    if thisTable.status=="pausedBegin" then
 
553
        return (thisTable.prePad - thisTable.pauseTime) + thisTable.postPad + ((thisTable.stop - thisTable.start) / thisTable.speed)
 
554
    elseif thisTable.status=="playing" then
 
555
        return ((thisTable.stop - thisTable.time) / thisTable.speed) + thisTable.postPad
 
556
    else
 
557
        return (thisTable.postPad - thisTable.pauseTime)
 
558
    end
 
559
    return 0
 
560
end
 
561
 
 
562
 function doAnimation()
 
563
 
 
564
 end
 
565
 
 
566
 
 
567
--------------------------------------------------------------------------
 
568
 
 
569
docs([[
 
570
    table = SceneObject:getAnimationInfo(channel)
 
571
    channel - defaults to 1
 
572
    
 
573
    table contains{
 
574
    - speed: (number) Playback speed relative to the animation's recorded speed. 1 is 100% speed, 2 is 200%, .5 is 50%
 
575
    - start: (number) Start time, in seconds.
 
576
    - stop: (number) Stop time, in seconds.
 
577
    - time: (number) Initialization time, in seconds. If the animation is looping, the first cycle will start at this time. Otherwise, this option will override 'start'.
 
578
    - loop: (boolean) if true, animation will loop once it reaches the end. If false, animation will play once.
 
579
    - callback: (function ()) Function to be called when the animation completes a cycle.
 
580
    - callTime: (number) The amount of time before the end of the animation that the callback is called (default 0)
 
581
    - prePad: (number) Seconds to stay on the first frame of the animation before starting to play (default 0)
 
582
    - postPad: (number) Seconds to stay on the last frame of the animation after playing the full animation (default 0) (has no effect when loop = true)
 
583
    - fadeInTime: (number) Seconds including prePad to fade the weight from 0 to the set weight. (default 0)
 
584
    - fadeOutTime: (number) Seconds before the end, including postPad, to start fading from the set weight to 0 (default 0)
 
585
    - weight: (number 0.0-1.0) The weight as compared to other animations currently playing on different channels. (default 1)
 
586
    }
 
587
    Returns data about the animation on a given channel    
 
588
]])
 
589
 
 
590
function SceneObject:getAnimationInfo(channel)
 
591
    if animationTable[self] ~= nil then
 
592
        if animationTable[self][channel] then
 
593
            returnTable = {}
 
594
            returnTable.speed = animationTable[self][channel].speed
 
595
            returnTable.start = animationTable[self][channel].start
 
596
            returnTable.stop = animationTable[self][channel].stop
 
597
            returnTable.time = animationTable[self][channel].time
 
598
            returnTable.loop = animationTable[self][channel].loop
 
599
            returnTable.callback = animationTable[self][channel].callback
 
600
            returnTable.callTime = animationTable[self][channel].callTime
 
601
            returnTable.prePad = animationTable[self][channel].prePad
 
602
            returnTable.postPad = animationTable[self][channel].postPad
 
603
            returnTable.fadeInTime = animationTable[self][channel].fadeInTime
 
604
            returnTable.fadeOutTime = animationTable[self][channel].fadeOutTime
 
605
            returnTable.weight = animationTable[self][channel].weight
 
606
            return returnTable
 
607
        end
 
608
    end
 
609
end
 
610
        
 
611
sandboxScope(SceneObject,"getAnimationInfo","USER")
 
612
 
 
613
 
 
614
 
 
615
 
 
616
--------------------------------------------------------------------------
 
617
 
 
618
docs([[
 
619
    SceneObject:stopAnimation(channel (optional))
 
620
    
 
621
    - channel: (number) The channel to stop
 
622
    
 
623
    Stops the currently playing animation. A channel may be
 
624
    specified. If a channel is not specified, channel 1 is stopped.
 
625
]])
 
626
 
 
627
function SceneObject:stopAnimation(channel)
 
628
    --print(1, "calling stop animation on channel ", channel, " on object ", self)
 
629
    if channel==nil then
 
630
        channel=1
 
631
    end
 
632
    local thisObject = animationTable
 
633
    while thisObject do
 
634
        if thisObject.object == self then break end
 
635
        lastObject = thisObject
 
636
        thisObject = thisObject.next
 
637
    end
 
638
    
 
639
    if not thisObject then warning("This object has no animations playing on it.") return  end
 
640
    local thisChannel = thisObject.channelList
 
641
    local lastChannel = nil
 
642
    while thisChannel do
 
643
        if thisChannel.channel == channel then 
 
644
 
 
645
            if lastChannel == nil then
 
646
                thisObject.channelList = thisChannel.next
 
647
            else
 
648
                lastChannel.next = thisChannel.next
 
649
            end
 
650
            break
 
651
        end
 
652
        lastChannel = thisChannel
 
653
        thisChannel = thisChannel.next
 
654
    end
 
655
    if thisChannel == nil then warning("no channel: ", channel, " is playing on this object.") end
 
656
 
 
657
end
 
658
 
 
659
sandboxScope(SceneObject,"stopAnimation","USER")
 
660
 
 
661
--------------------------------------------------------------------------
 
662
 
 
663
--------------------------------------------------------------------------
 
664
 
 
665
docs([[
 
666
    SceneObject:setAnimationLoop(flag,channel)
 
667
    
 
668
    - loop: (boolean) If true, animation should loop when it reaches the end
 
669
    - channel: (number, optional) The channel to loop. If uspecified, channel 1 is set to loop.
 
670
    
 
671
    Sets the animation to loop when it reaches the end of its timeline.
 
672
]])
 
673
 
 
674
function SceneObject:setAnimationLoop(loop,channel)
 
675
    --print(1, "calling loop animation on channel ", channel, " setting it to ", loop, " on object ", self)
 
676
    -- loop an animation
 
677
    if channel==nil then
 
678
        channel=1
 
679
    end
 
680
    local thisObject = animationTable
 
681
    while thisObject do
 
682
        if thisObject.object == self then break end
 
683
        lastObject = thisObject
 
684
        thisObject = thisObject.next
 
685
    end
 
686
    
 
687
    if not thisObject then warning("This object has no animations playing on it.") return  end
 
688
    local thisChannel = thisObject.channelList
 
689
    local lastChannel = nil
 
690
    while thisChannel do
 
691
        if thisChannel.channel == channel then 
 
692
            thisChannel.infoTable.loop = loop
 
693
            break
 
694
        end
 
695
        lastChannel = thisChannel
 
696
        thisChannel = thisChannel.next
 
697
    end
 
698
    if thisChannel == nil then warning("no channel: ", channel, " is playing on this object.") end
 
699
end
 
700
sandboxScope(SceneObject,"setAnimationLoop","USER")
 
701
 
 
702
 
 
703
--------------------------------------------------------------------------
 
704
 
 
705
docs([[
 
706
    speed = SceneObject:getAnimationSpeed(channel)
 
707
    
 
708
    - speed: (boolean) If true, animation should loop when it reaches the end
 
709
    - channel: (number, optional) The channel to loop. If uspecified, channel 1 is set to loop.
 
710
    
 
711
    Sets the animation to loop when it reaches the end of its timeline.
 
712
]])
 
713
 
 
714
function SceneObject:getAnimationSpeed(channel)
 
715
    
 
716
    --print(1, "getting animation speed on channel ", channel, " on object ", self)
 
717
    if channel==nil then
 
718
        channel=1
 
719
    end
 
720
    local thisObject = animationTable
 
721
    while thisObject do
 
722
        if thisObject.object == self then break end
 
723
        lastObject = thisObject
 
724
        thisObject = thisObject.next
 
725
    end
 
726
    
 
727
    if not thisObject then warning("This object has no animations playing on it.") return end
 
728
    local thisChannel = thisObject.channelList
 
729
    local lastChannel = nil
 
730
    while thisChannel do
 
731
        if thisChannel.channel == channel then
 
732
            if thisChannel.infoTable.runBackwards then
 
733
                return -thisChannel.infoTable.speed
 
734
            else
 
735
                return thisChannel.infoTable.speed
 
736
            end
 
737
        end
 
738
        lastChannel = thisChannel
 
739
        thisChannel = thisChannel.next
 
740
    end
 
741
    if thisChannel == nil then warning("no channel: ", channel, " is playing on this object.") end
 
742
end
 
743
 
 
744
sandboxScope(SceneObject,"getAnimationSpeed","USER")
 
745
 
 
746
 
 
747
--------------------------------------------------------------------------
 
748
 
 
749
docs([[
 
750
    SceneObject:setAnimationSpeed(speed,channel)
 
751
    
 
752
    - speed: (number) The rate to play the animation. 
 
753
    - channel: (number, optional) The channel to loop. If uspecified, channel 1's speed is set.
 
754
    
 
755
    Sets the play rate of the animation. 1 is the original rate; 2 is
 
756
    twice as fast, 0.5 is half as fast.
 
757
]])
 
758
 
 
759
function SceneObject:setAnimationSpeed(newSpeed,channel)
 
760
 
 
761
    --print(1, "setting animation speed to ", newSpeed, " on channel ", channel, " on object ", self)
 
762
    -- set the speed of the animation
 
763
    if channel==nil then
 
764
        channel=1
 
765
    end
 
766
    local thisObject = animationTable
 
767
    while thisObject do
 
768
        if thisObject.object == self then break end
 
769
        lastObject = thisObject
 
770
        thisObject = thisObject.next
 
771
    end
 
772
    
 
773
    if not thisObject then error("This object has no animations playing on it.") return end
 
774
    local thisChannel = thisObject.channelList
 
775
    local lastChannel = nil
 
776
    while thisChannel do
 
777
        if thisChannel.channel == channel then 
 
778
            animTable = thisChannel.infoTable
 
779
            
 
780
            if newSpeed < 0 then 
 
781
                animTable.runBackwards = true 
 
782
                newSpeed = -newSpeed
 
783
            else
 
784
                animTable.runBackwards = false
 
785
            end
 
786
            animTable.speed = newSpeed
 
787
            return
 
788
        end
 
789
        lastChannel = thisChannel
 
790
        thisChannel = thisChannel.next
 
791
    end
 
792
    if thisChannel == nil then error("no channel: ", channel, " is playing on this object.") end
 
793
 
 
794
end
 
795
 
 
796
sandboxScope(SceneObject,"setAnimationSpeed","USER")
 
797
 
 
798
--------------------------------------------------------------------------
 
799
 
 
800
docs([[
 
801
    loop = SceneObject:getAnimationLoop(channel)
 
802
    
 
803
    - loop: (boolean) If true, animation should loop when it reaches the end
 
804
    - channel: (number, optional) The channel to loop. If uspecified, channel 1 is set to loop.
 
805
    
 
806
    Sets the animation to loop when it reaches the end of its timeline.
 
807
]])
 
808
 
 
809
function SceneObject:getAnimationLoop(channel)
 
810
    
 
811
    --print(1, "getting animation loop on channel ", channel, " on object ", self)
 
812
    if channel==nil then
 
813
        channel=1
 
814
    end
 
815
    local thisObject = animationTable
 
816
    while thisObject do
 
817
        if thisObject.object == self then break end
 
818
        lastObject = thisObject
 
819
        thisObject = thisObject.next
 
820
    end
 
821
    
 
822
    if not thisObject then warning("This object has no animations playing on it.") return end
 
823
    local thisChannel = thisObject.channelList
 
824
    local lastChannel = nil
 
825
    while thisChannel do
 
826
        if thisChannel.channel == channel then 
 
827
            return thisChannel.infoTable.loop
 
828
            
 
829
        end
 
830
        lastChannel = thisChannel
 
831
        thisChannel = thisChannel.next
 
832
    end
 
833
    if thisChannel == nil then warning("no channel: ", channel, " is playing on this object.") end
 
834
 
 
835
end
 
836
 
 
837
sandboxScope(SceneObject,"getAnimationLoop","USER")
 
838
 
 
839
 
 
840
 
 
841
--------------------------------------------------------------------------
 
842
 
 
843
--------------------------------------------------------------------------
 
844
docs([[
 
845
    channels = SceneObject:getAllAnimations()
 
846
    
 
847
    channels - table of all channels of animations currently playing in any way
 
848
    
 
849
    Returns a list of all animations playing in a table. Usefull for looping through.
 
850
    ]])
 
851
    
 
852
function SceneObject:getAllAnimations()
 
853
    
 
854
    --print(1, "getting animation list on object ", self)
 
855
    local animations = {}
 
856
    local i = 1
 
857
 
 
858
    local thisObject = animationTable
 
859
    while thisObject do
 
860
        if thisObject.object == self then break end
 
861
        lastObject = thisObject
 
862
        thisObject = thisObject.next
 
863
    end
 
864
    
 
865
    if not thisObject then warning("This object has no animations playing on it.") return end
 
866
    local thisChannel = thisObject.channelList
 
867
    while thisChannel do
 
868
        animations[i] = thisChannel.channel
 
869
        i=i+1
 
870
        thisChannel = thisChannel.next
 
871
    end
 
872
    return animations
 
873
    
 
874
end
 
875
sandboxScope(SceneObject,"getAllAnimations","USER")
 
876
 
 
877
----------------------------------------------------------------------------------
 
878
 
 
879
 
 
880
--------------------------------------------------------------------------
 
881
 
 
882
docs([[
 
883
    playing = SceneObject:isAnimationPlaying(channel)
 
884
    
 
885
    - playing: (boolean) If true, the animation is currently playing.
 
886
    - channel: (number, optional) The channel to check. If uspecified, channel 1 is checked.
 
887
    
 
888
    Checks whether an animation is playing on the specified channel. 
 
889
 
 
890
]])
 
891
 
 
892
function SceneObject:isAnimationPlaying(channel)
 
893
    
 
894
    --print(1, "Checking if animation is playing on channel ", channel, " on object ", self)
 
895
   if channel==nil then
 
896
        channel=1
 
897
    end
 
898
    local thisObject = animationTable
 
899
    while thisObject do
 
900
        if thisObject.object == self then break end
 
901
        lastObject = thisObject
 
902
        thisObject = thisObject.next
 
903
    end
 
904
    
 
905
    if not thisObject then return false end
 
906
    local thisChannel = thisObject.channelList
 
907
    local lastChannel = nil
 
908
    while thisChannel do
 
909
        if thisChannel.channel == channel then 
 
910
            return true
 
911
            
 
912
        end
 
913
        lastChannel = thisChannel
 
914
        thisChannel = thisChannel.next
 
915
    end
 
916
    return false
 
917
 
 
918
end
 
919
 
 
920
sandboxScope(SceneObject,"isAnimationPlaying","USER")
 
921
 
 
922
 
 
923
--------------------------------------------------------------------------
 
924
 
 
925
docs([[
 
926
    SceneObject:animationFade(endWeight,time,channel)
 
927
    
 
928
    endWeight - The weight to fade to
 
929
    time - how much time in seconds to fade to
 
930
    channel - What channel the animation is playing on (default 1)
 
931
    
 
932
    Fades the weights of an animation up and down. Default weight
 
933
    is 1, a weight of 0 results in no animation movement. Weights
 
934
    are all compared to other animations currently playing.
 
935
    
 
936
]])
 
937
 
 
938
function SceneObject:animationFade(endWeight,time,channel,callback)
 
939
    --print(1, "called animationFade to ", endWeight, " in ", time, " seconds on channel ", channel, " on object ", self)
 
940
 
 
941
    if type(endWeight) ~= "number" then error("The Weight must be a number") end
 
942
    if type(time) ~= "number" then error("The time to fade must be a number") end
 
943
    if channel==nil then
 
944
        channel=1
 
945
    end
 
946
    local thisObject = animationTable
 
947
    while thisObject do
 
948
        if thisObject.object == self then break end
 
949
        lastObject = thisObject
 
950
        thisObject = thisObject.next
 
951
    end
 
952
    
 
953
    if not thisObject then warning("This object has no animations playing on it.") return end
 
954
    local thisChannel = thisObject.channelList
 
955
    local lastChannel = nil
 
956
    while thisChannel do
 
957
        if thisChannel.channel == channel then 
 
958
            thisChannel.infoTable.fade = {
 
959
                startWeight = thisChannel.infoTable.weight,
 
960
                goalWeight = endWeight,
 
961
                now = 0,
 
962
                totalTime = time
 
963
            }
 
964
            break
 
965
        end
 
966
        lastChannel = thisChannel
 
967
        thisChannel = thisChannel.next
 
968
    end
 
969
    if thisChannel == nil then warning("no channel: ".. tostring(channel) .. " is playing on this object.") end
 
970
 
 
971
end
 
972
 
 
973
 
 
974
sandboxScope(SceneObject,"animationFade","USER")
 
975
 
 
976
 
 
977
--------------------------------------------------------------------------
 
978
--Main animation looping functions
 
979
function animationLoop()
 
980
    local start = ProfileTimer.queryPerformanceCounter()
 
981
    --print(2,"looping through every object and every animation and doing it.")
 
982
  --[[  --print()
 
983
    --print()
 
984
    --print()
 
985
    --print()
 
986
    --print("Syncing animations for this frame.")]]
 
987
    local thisObject = animationTable
 
988
    local lastObject = nil
 
989
    
 
990
    while thisObject do
 
991
        --print("Doing animations on object: ", thisObject.object)
 
992
        if SceneManager.isDeletedObject(thisObject.object) or thisObject.channelList == nil then
 
993
             --This Object has been deleted or has noo channels on it. 
 
994
            if lastObject == nil then
 
995
                animationTable = thisObject.next
 
996
            else
 
997
                lastObject.next = thisObject.next
 
998
            end
 
999
        else
 
1000
            --We're not yet finished with this object
 
1001
            lastObject = thisObject
 
1002
            syncObjectAnimations(thisObject)
 
1003
        end 
 
1004
        thisObject = thisObject.next
 
1005
 
 
1006
    end
 
1007
    ProfileTimerAnimationUpdate:addFromStart(start)
 
1008
end
 
1009
--------------------------------------------------------------------------
 
1010
docs([[
 
1011
SceneObject:setAllAnimDirty()
 
1012
 
 
1013
This function sets all the dirty flags on all the animations so they will be calculated next frame.
 
1014
]])
 
1015
function SceneObject.setAllAnimDirty(now)
 
1016
    local thisObject = animationTable
 
1017
    
 
1018
    while thisObject do
 
1019
        thisObject.dirty = true
 
1020
        thisObject.lastRunTime = now
 
1021
        --print(2, "Setting ", thisObject.object, " s lastRunTime to ", thisObject.lastRunTime)
 
1022
        thisObject = thisObject.next
 
1023
    end
 
1024
    
 
1025
end
 
1026
 sandboxScope(SceneObject,"setAllAnimDirty","KERNEL")
 
1027
 
 
1028
 
 
1029
 
 
1030
--------------------------------------------------------------------------
 
1031
docs([[
 
1032
SceneObject:syncAnimations()
 
1033
 
 
1034
This function is a state changing function that calls into the lua layer and forcefully makes sure that the bones
 
1035
in this scene object are updated 
 
1036
]])
 
1037
function SceneObject:syncAnimations()
 
1038
    --print(2,"running Animation Sync")
 
1039
    local thisObject = animationTable
 
1040
    
 
1041
    while thisObject do
 
1042
        if thisObject.object == self then
 
1043
            syncObjectAnimations(thisObject)
 
1044
        end
 
1045
        thisObject = thisObject.next
 
1046
    end
 
1047
    
 
1048
end
 
1049
sandboxScope(SceneObject,"syncAnimations","USER")
 
1050
 
 
1051
 
 
1052
--------------------------------------------------------------------------
 
1053
 
 
1054
docs([[
 
1055
    table = SceneObject:getAnimationInfo(channel)
 
1056
    channel - defaults to 1
 
1057
    
 
1058
    table contains{
 
1059
    - speed: (number) Playback speed relative to the animation's recorded speed. 1 is 100% speed, 2 is 200%, .5 is 50%
 
1060
    - start: (number) Start time, in seconds.
 
1061
    - stop: (number) Stop time, in seconds.
 
1062
    - time: (number) Initialization time, in seconds. If the animation is looping, the first cycle will start at this time. Otherwise, this option will override 'start'.
 
1063
    - loop: (boolean) if true, animation will loop once it reaches the end. If false, animation will play once.
 
1064
    - callback: (function ()) Function to be called when the animation completes a cycle.
 
1065
    - callTime: (number) The amount of time before the end of the animation that the callback is called (default 0)
 
1066
    - prePad: (number) Seconds to stay on the first frame of the animation before starting to play (default 0)
 
1067
    - postPad: (number) Seconds to stay on the last frame of the animation after playing the full animation (default 0) (has no effect when loop = true)
 
1068
    - fadeInTime: (number) Seconds including prePad to fade the weight from 0 to the set weight. (default 0)
 
1069
    - fadeOutTime: (number) Seconds before the end, including postPad, to start fading from the set weight to 0 (default 0)
 
1070
    - weight: (number 0.0-1.0) The weight as compared to other animations currently playing on different channels. (default 1)
 
1071
    }
 
1072
    Returns data about the animation on a given channel    
 
1073
]])
 
1074
 
 
1075
function SceneObject:getAnimationInfo(channel)
 
1076
    --print(1, "getting animation info on channel " , channel)
 
1077
    if channel==nil then
 
1078
        channel=1
 
1079
    end
 
1080
    local thisObject = animationTable
 
1081
    while thisObject do
 
1082
        if thisObject.object == self then break end
 
1083
        lastObject = thisObject
 
1084
        thisObject = thisObject.next
 
1085
    end
 
1086
    
 
1087
    if not thisObject then warning("This object has no animations playing on it.") return end
 
1088
    local thisChannel = thisObject.channelList
 
1089
    local lastChannel = nil
 
1090
    while thisChannel do
 
1091
        
 
1092
        if thisChannel.channel == channel then 
 
1093
        
 
1094
            returnTable = {}
 
1095
            if thisChannel.infoTable.runBackwards then
 
1096
                returnTable.speed = -thisChannel.infoTable.speed
 
1097
            else
 
1098
                returnTable.speed = thisChannel.infoTable.speed
 
1099
            end
 
1100
            returnTable.start = thisChannel.infoTable.rangeStart
 
1101
            returnTable.stop = thisChannel.infoTable.rangeEnd
 
1102
            returnTable.time = thisChannel.infoTable.time
 
1103
            returnTable.loop = thisChannel.infoTable.loop
 
1104
            returnTable.callback = thisChannel.infoTable.callback
 
1105
            returnTable.callTime = thisChannel.infoTable.originalArgs.callTime
 
1106
            returnTable.prePad = thisChannel.infoTable.originalArgs.prePad
 
1107
            returnTable.postPad = thisChannel.infoTable.originalArgs.postPad
 
1108
            returnTable.fadeInTime = thisChannel.infoTable.originalArgs.fadeInTime
 
1109
            returnTable.fadeOutTime = thisChannel.infoTable.originalArgs.fadeOutTime
 
1110
            returnTable.weight = thisChannel.infoTable.weight
 
1111
            return returnTable
 
1112
            
 
1113
        end
 
1114
        lastChannel = thisChannel
 
1115
        thisChannel = thisChannel.next
 
1116
    end
 
1117
    if thisChannel == nil then error("no channel: ", channel, " is playing on this object.") end
 
1118
 
 
1119
end
 
1120
        
 
1121
sandboxScope(SceneObject,"getAnimationInfo","USER")
 
1122
 
 
1123
Timer.every(animationLoop,0,"__animationLoop")