Coverage Report

Created: 2025-10-12 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/include/OgreAnimationTrack.h
Line
Count
Source
1
/*
2
-----------------------------------------------------------------------------
3
This source file is part of OGRE
4
    (Object-oriented Graphics Rendering Engine)
5
For the latest info, see http://www.ogre3d.org/
6
7
Copyright (c) 2000-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
29
#ifndef __AnimationTrack_H__
30
#define __AnimationTrack_H__
31
32
#include "OgrePrerequisites.h"
33
#include "OgreHeaderPrefix.h"
34
#include "OgreSimpleSpline.h"
35
#include "OgreRotationalSpline.h"
36
#include "OgrePose.h"
37
38
namespace Ogre 
39
{
40
    class VertexPoseKeyFrame;
41
    class KeyFrame;
42
43
    /** \addtogroup Core
44
    *  @{
45
    */
46
    /** \addtogroup Animation
47
    *  @{
48
    */
49
    /** Time index object used to search keyframe at the given position.
50
    */
51
    class _OgreExport TimeIndex
52
    {
53
    private:
54
        /** The time position (in relation to the whole animation sequence)
55
        */
56
        Real mTimePos;
57
        /** The global keyframe index (in relation to the whole animation sequence)
58
            that used to convert to local keyframe index, or INVALID_KEY_INDEX which
59
            means global keyframe index unavailable, and then slight slow method will
60
            used to search local keyframe index.
61
        */
62
        uint mKeyIndex;
63
64
        /** Indicate it's an invalid global keyframe index.
65
        */
66
        static const uint INVALID_KEY_INDEX = (uint)-1;
67
68
    public:
69
        /** Construct time index object by the given time position.
70
        */
71
        TimeIndex(Real timePos)
72
            : mTimePos(timePos)
73
            , mKeyIndex(INVALID_KEY_INDEX)
74
0
        {
75
0
        }
76
77
        /** Construct time index object by the given time position and
78
            global keyframe index.
79
        @note In normally, you don't need to use this constructor directly, use
80
            Animation::_getTimeIndex instead.
81
        */
82
        TimeIndex(Real timePos, uint keyIndex)
83
            : mTimePos(timePos)
84
            , mKeyIndex(keyIndex)
85
0
        {
86
0
        }
87
88
        bool hasKeyIndex(void) const
89
0
        {
90
0
            return mKeyIndex != INVALID_KEY_INDEX;
91
0
        }
92
93
        Real getTimePos(void) const
94
0
        {
95
0
            return mTimePos;
96
0
        }
97
98
        uint getKeyIndex(void) const
99
0
        {
100
0
            return mKeyIndex;
101
0
        }
102
    };
103
104
    /** A 'track' in an animation sequence, i.e. a sequence of keyframes which affect a
105
        certain type of animable object.
106
107
        This class is intended as a base for more complete classes which will actually
108
        animate specific types of object, e.g. a bone in a skeleton to affect
109
        skeletal animation. An animation will likely include multiple tracks each of which
110
        can be made up of many KeyFrame instances. Note that the use of tracks allows each animable
111
        object to have it's own number of keyframes, i.e. you do not have to have the
112
        maximum number of keyframes for all animable objects just to cope with the most
113
        animated one.
114
115
        Since the most common animable object is a Node, there are options in this class for associating
116
        the track with a Node which will receive keyframe updates automatically when the 'apply' method
117
        is called.
118
119
        By default rotation is done using shortest-path algorithm.
120
        It is possible to change this behaviour using
121
        setUseShortestRotationPath() method.
122
    */
123
    class _OgreExport AnimationTrack : public AnimationAlloc
124
    {
125
    public:
126
127
        /** Listener allowing you to override certain behaviour of a track, 
128
            for example to drive animation procedurally.
129
        */
130
        class _OgreExport Listener
131
        {
132
        public:
133
0
            virtual ~Listener() {}
134
135
            /** Get an interpolated keyframe for this track at the given time.
136
            @return true if the KeyFrame was populated, false if not.
137
            */
138
            virtual bool getInterpolatedKeyFrame(const AnimationTrack* t, const TimeIndex& timeIndex, KeyFrame* kf) = 0;
139
        };
140
141
        /// Constructor
142
        AnimationTrack(Animation* parent, unsigned short handle);
143
144
        virtual ~AnimationTrack();
145
146
        /** Get the handle associated with this track. */
147
0
        unsigned short getHandle(void) const { return mHandle; }
148
149
        /** Returns the number of keyframes in this animation. */
150
0
        size_t getNumKeyFrames(void) const { return mKeyFrames.size(); }
151
152
        /** Returns the KeyFrame at the specified index. */
153
0
        KeyFrame* getKeyFrame(size_t index) const { return mKeyFrames.at(index); }
154
155
        /** Gets the 2 KeyFrame objects which are active at the time given, and the blend value between them.
156
157
            At any point in time  in an animation, there are either 1 or 2 keyframes which are 'active',
158
            1 if the time index is exactly on a keyframe, 2 at all other times i.e. the keyframe before
159
            and the keyframe after.
160
        @par
161
            This method returns those keyframes given a time index, and also returns a parametric
162
            value indicating the value of 't' representing where the time index falls between them.
163
            E.g. if it returns 0, the time index is exactly on keyFrame1, if it returns 0.5 it is
164
            half way between keyFrame1 and keyFrame2 etc.
165
        @param timeIndex The time index.
166
        @param keyFrame1 Pointer to a KeyFrame pointer which will receive the pointer to the 
167
            keyframe just before or at this time index.
168
        @param keyFrame2 Pointer to a KeyFrame pointer which will receive the pointer to the 
169
            keyframe just after this time index. 
170
        @param firstKeyIndex Pointer to an unsigned short which, if supplied, will receive the 
171
            index of the 'from' keyframe in case the caller needs it.
172
        @return Parametric value indicating how far along the gap between the 2 keyframes the timeIndex
173
            value is, e.g. 0.0 for exactly at 1, 0.25 for a quarter etc. By definition the range of this 
174
            value is:  0.0 <= returnValue < 1.0 .
175
        */
176
        float getKeyFramesAtTime(const TimeIndex& timeIndex, KeyFrame** keyFrame1, KeyFrame** keyFrame2,
177
            unsigned short* firstKeyIndex = 0) const;
178
179
        /** Creates a new KeyFrame and adds it to this animation at the given time index.
180
181
            It is better to create KeyFrames in time order.
182
        @param timePos The time from which this KeyFrame will apply.
183
        */
184
        virtual KeyFrame* createKeyFrame(Real timePos);
185
186
        /** Removes a KeyFrame by it's index. */
187
        virtual void removeKeyFrame(unsigned short index);
188
189
        /** Removes all the KeyFrames from this track. */
190
        void removeAllKeyFrames(void);
191
192
193
        /** Gets a KeyFrame object which contains the interpolated transforms at the time index specified.
194
195
            The KeyFrame objects held by this class are transformation snapshots at 
196
            discrete points in time. Normally however, you want to interpolate between these
197
            keyframes to produce smooth movement, and this method allows you to do this easily.
198
            In animation terminology this is called 'tweening'. 
199
        @param timeIndex The time (in relation to the whole animation sequence)
200
        @param kf Keyframe object to store results
201
        */
202
        virtual void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const = 0;
203
204
        /** Applies an animation track to the designated target.
205
        @param timeIndex The time position in the animation to apply.
206
        @param weight The influence to give to this track, 1.0 for full influence, less to blend with
207
          other animations.
208
        @param scale The scale to apply to translations and scalings, useful for 
209
            adapting an animation to a different size target.
210
        */
211
        virtual void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f) = 0;
212
213
        /** Internal method used to tell the track that keyframe data has been 
214
            changed, which may cause it to rebuild some internal data. */
215
0
        virtual void _keyFrameDataChanged(void) const {}
216
217
        /** Method to determine if this track has any KeyFrames which are
218
        doing anything useful - can be used to determine if this track
219
        can be optimised out.
220
        */
221
0
        virtual bool hasNonZeroKeyFrames(void) const { return true; }
222
223
        /** Optimise the current track by removing any duplicate keyframes. */
224
0
        virtual void optimise(void) {}
225
226
        /** Internal method to collect keyframe times, in unique, ordered format. */
227
        virtual void _collectKeyFrameTimes(std::vector<Real>& keyFrameTimes);
228
229
        /** Internal method to build keyframe time index map to translate global lower
230
            bound index to local lower bound index. */
231
        virtual void _buildKeyFrameIndexMap(const std::vector<Real>& keyFrameTimes);
232
        
233
        /** Internal method to re-base the keyframes relative to a given keyframe. */
234
        virtual void _applyBaseKeyFrame(const KeyFrame* base);
235
236
        /** Set a listener for this track. */
237
0
        virtual void setListener(Listener* l) { mListener = l; }
238
239
        /** Returns the parent Animation object for this track. */
240
0
        Animation *getParent() const { return mParent; }
241
    private:
242
        /// Map used to translate global keyframe time lower bound index to local lower bound index
243
        typedef std::vector<ushort> KeyFrameIndexMap;
244
        KeyFrameIndexMap mKeyFrameIndexMap;
245
246
        /// Create a keyframe implementation - must be overridden
247
        virtual KeyFrame* createKeyFrameImpl(Real time) = 0;
248
    protected:
249
        typedef std::vector<KeyFrame*> KeyFrameList;
250
        KeyFrameList mKeyFrames;
251
        Animation* mParent;
252
        Listener* mListener;
253
        unsigned short mHandle;
254
255
        /// Internal method for clone implementation
256
        virtual void populateClone(AnimationTrack* clone) const;
257
    };
258
259
    /** Specialised AnimationTrack for dealing with generic animable values.
260
    */
261
    class _OgreExport NumericAnimationTrack : public AnimationTrack
262
    {
263
    public:
264
        /// Constructor, associates with an AnimableValue
265
        NumericAnimationTrack(Animation* parent, unsigned short handle, 
266
            const AnimableValuePtr& target);
267
268
        /// @copydoc AnimationTrack::createKeyFrame
269
        virtual NumericKeyFrame* createNumericKeyFrame(Real timePos);
270
271
        /// @copydoc AnimationTrack::getInterpolatedKeyFrame
272
        void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const override;
273
274
        /// @copydoc AnimationTrack::apply
275
        void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f) override;
276
277
        /** Applies an animation track to a given animable value.
278
        @param anim The AnimableValue to which to apply the animation
279
        @param timeIndex The time position in the animation to apply.
280
        @param weight The influence to give to this track, 1.0 for full influence, less to blend with
281
          other animations.
282
        @param scale The scale to apply to translations and scalings, useful for 
283
            adapting an animation to a different size target.
284
        */
285
        void applyToAnimable(const AnimableValuePtr& anim, const TimeIndex& timeIndex, 
286
            Real weight = 1.0, Real scale = 1.0f);
287
288
        /** Returns a pointer to the associated animable object (if any). */
289
0
        const AnimableValuePtr& getAssociatedAnimable(void) const { return mTargetAnim; }
290
291
        /** Sets the associated animable object which will be automatically 
292
            affected by calls to 'apply'. */
293
0
        void setAssociatedAnimable(const AnimableValuePtr& val) { mTargetAnim = val; }
294
295
        /** Returns the KeyFrame at the specified index. */
296
        NumericKeyFrame* getNumericKeyFrame(unsigned short index) const;
297
298
        /** Clone this track (internal use only) */
299
        NumericAnimationTrack* _clone(Animation* newParent) const;
300
301
302
    private:
303
        /// Target to animate
304
        AnimableValuePtr mTargetAnim;
305
306
        /// @copydoc AnimationTrack::createKeyFrameImpl
307
        KeyFrame* createKeyFrameImpl(Real time) override;
308
309
310
    };
311
312
    /** Specialised AnimationTrack for dealing with node transforms.
313
    */
314
    class _OgreExport NodeAnimationTrack : public AnimationTrack
315
    {
316
    public:
317
        /// Constructor
318
        NodeAnimationTrack(Animation* parent, unsigned short handle);
319
        /// Constructor, associates with a Node
320
        NodeAnimationTrack(Animation* parent, unsigned short handle, 
321
            Node* targetNode);
322
        /// Destructor
323
        virtual ~NodeAnimationTrack();
324
        /// @copydoc AnimationTrack::createKeyFrame
325
        virtual TransformKeyFrame* createNodeKeyFrame(Real timePos);
326
        /** Returns a pointer to the associated Node object (if any). */
327
        virtual Node* getAssociatedNode(void) const;
328
329
        /** Sets the associated Node object which will be automatically affected by calls to 'apply'. */
330
        virtual void setAssociatedNode(Node* node);
331
332
        /** As the 'apply' method but applies to a specified Node instead of associated node. */
333
        virtual void applyToNode(Node* node, const TimeIndex& timeIndex, Real weight = 1.0, 
334
            Real scale = 1.0f);
335
336
        /** Sets the method of rotation calculation */
337
        virtual void setUseShortestRotationPath(bool useShortestPath);
338
339
        /** Gets the method of rotation calculation */
340
        virtual bool getUseShortestRotationPath() const;
341
342
        /// @copydoc AnimationTrack::getInterpolatedKeyFrame
343
        void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const override;
344
345
        /// @copydoc AnimationTrack::apply
346
        void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f) override;
347
348
        /// @copydoc AnimationTrack::_keyFrameDataChanged
349
        void _keyFrameDataChanged(void) const override;
350
351
        /** Returns the KeyFrame at the specified index. */
352
        virtual TransformKeyFrame* getNodeKeyFrame(unsigned short index) const;
353
354
355
        /** Method to determine if this track has any KeyFrames which are
356
            doing anything useful - can be used to determine if this track
357
            can be optimised out.
358
        */
359
        bool hasNonZeroKeyFrames(void) const override;
360
361
        /** Optimise the current track by removing any duplicate keyframes. */
362
        void optimise(void) override;
363
364
        /** Clone this track (internal use only) */
365
        NodeAnimationTrack* _clone(Animation* newParent) const;
366
        
367
        void _applyBaseKeyFrame(const KeyFrame* base) override;
368
        
369
    private:
370
        /// Specialised keyframe creation
371
        KeyFrame* createKeyFrameImpl(Real time) override;
372
        // Flag indicating we need to rebuild the splines next time
373
        virtual void buildInterpolationSplines(void) const;
374
375
        // Struct for store splines, allocate on demand for better memory footprint
376
        struct Splines
377
        {
378
            SimpleSpline positionSpline;
379
            SimpleSpline scaleSpline;
380
            RotationalSpline rotationSpline;
381
        };
382
383
        mutable bool mSplineBuildNeeded;
384
        /// Defines if rotation is done using shortest path
385
        mutable bool mUseShortestRotationPath;
386
        Node* mTargetNode;
387
        // Prebuilt splines, must be mutable since lazy-update in const method
388
        mutable Splines* mSplines;
389
    };
390
391
    /** Type of vertex animation.
392
        Vertex animation comes in 2 types, morph and pose. The reason
393
        for the 2 types is that we have 2 different potential goals - to encapsulate
394
        a complete, flowing morph animation with multiple keyframes (a typical animation,
395
        but implemented by having snapshots of the vertex data at each keyframe), 
396
        or to represent a single pose change, for example a facial expression. 
397
        Whilst both could in fact be implemented using the same system, we choose
398
        to separate them since the requirements and limitations of each are quite
399
        different.
400
    @par
401
        Morph animation is a simple approach where we have a whole series of 
402
        snapshots of vertex data which must be interpolated, e.g. a running 
403
        animation implemented as morph targets. Because this is based on simple
404
        snapshots, it's quite fast to use when animating an entire mesh because 
405
        it's a simple linear change between keyframes. However, this simplistic 
406
        approach does not support blending between multiple morph animations. 
407
        If you need animation blending, you are advised to use skeletal animation
408
        for full-mesh animation, and pose animation for animation of subsets of 
409
        meshes or where skeletal animation doesn't fit - for example facial animation.
410
        For animating in a vertex shader, morph animation is quite simple and 
411
        just requires the 2 vertex buffers (one the original position buffer) 
412
        of absolute position data, and an interpolation factor. Each track in 
413
        a morph animation references a unique set of vertex data.
414
    @par
415
        Pose animation is more complex. Like morph animation each track references
416
        a single unique set of vertex data, but unlike morph animation, each 
417
        keyframe references 1 or more 'poses', each with an influence level. 
418
        A pose is a series of offsets to the base vertex data, and may be sparse - ie it
419
        may not reference every vertex. Because they're offsets, they can be 
420
        blended - both within a track and between animations. This set of features
421
        is very well suited to facial animation.
422
    @par
423
        For example, let's say you modelled a face (one set of vertex data), and 
424
        defined a set of poses which represented the various phonetic positions 
425
        of the face. You could then define an animation called 'SayHello', containing
426
        a single track which referenced the face vertex data, and which included 
427
        a series of keyframes, each of which referenced one or more of the facial 
428
        positions at different influence levels - the combination of which over
429
        time made the face form the shapes required to say the word 'hello'. Since
430
        the poses are only stored once, but can be referenced may times in 
431
        many animations, this is a very powerful way to build up a speech system.
432
    @par
433
        The downside of pose animation is that it can be more difficult to set up.
434
        Also, since it uses more buffers (one for the base data, and one for each
435
        active pose), if you're animating in hardware using vertex shaders you need
436
        to keep an eye on how many poses you're blending at once. You define a
437
        maximum supported number in your vertex program definition, see the 
438
        includes_pose_animation material script entry. 
439
    @par
440
        So, by partitioning the vertex animation approaches into 2, we keep the
441
        simple morph technique easy to use, whilst still allowing all 
442
        the powerful techniques to be used. Note that morph animation cannot
443
        be blended with other types of vertex animation (pose animation or other
444
        morph animation); pose animation can be blended with other pose animation
445
        though, and both types can be combined with skeletal animation. Also note
446
        that all morph animation can be expressed as pose animation, but not vice
447
        versa.
448
    */
449
    enum VertexAnimationType : uint8
450
    {
451
        /// No animation
452
        VAT_NONE = 0,
453
        /// Morph animation is made up of many interpolated snapshot keyframes
454
        VAT_MORPH = 1,
455
        /// Pose animation is made up of a single delta pose keyframe
456
        VAT_POSE = 2
457
    };
458
459
    /** Specialised AnimationTrack for dealing with changing vertex position information.
460
    @see VertexAnimationType
461
    */
462
    class _OgreExport VertexAnimationTrack : public AnimationTrack
463
    {
464
    public:
465
        /** The target animation mode */
466
        enum TargetMode : uint8
467
        {
468
            /// Interpolate vertex positions in software
469
            TM_SOFTWARE, 
470
            /** Bind keyframe 1 to position, and keyframe 2 to a texture coordinate
471
                for interpolation in hardware */
472
            TM_HARDWARE
473
        };
474
        /// Constructor
475
        VertexAnimationTrack(Animation* parent, unsigned short handle, VertexAnimationType animType);
476
        /// Constructor, associates with target VertexData and temp buffer (for software)
477
        VertexAnimationTrack(Animation* parent, unsigned short handle, VertexAnimationType animType, 
478
            VertexData* targetData, TargetMode target = TM_SOFTWARE);
479
480
        /** Get the type of vertex animation we're performing. */
481
0
        VertexAnimationType getAnimationType(void) const { return mAnimationType; }
482
        
483
        /** Whether the vertex animation (if present) includes normals */
484
        bool getVertexAnimationIncludesNormals() const;
485
486
        /// @copydoc AnimationTrack::createKeyFrame
487
        virtual VertexMorphKeyFrame* createVertexMorphKeyFrame(Real timePos);
488
489
        /** Creates the single pose KeyFrame and adds it to this animation.
490
        */
491
        virtual VertexPoseKeyFrame* createVertexPoseKeyFrame(Real timePos);
492
493
        /** @copydoc AnimationTrack::getInterpolatedKeyFrame
494
        */
495
        void getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const override;
496
497
        /// @copydoc AnimationTrack::apply
498
        void apply(const TimeIndex& timeIndex, Real weight = 1.0, Real scale = 1.0f) override;
499
500
        /** As the 'apply' method but applies to specified VertexData instead of 
501
            associated data. */
502
        virtual void applyToVertexData(VertexData* data, 
503
            const TimeIndex& timeIndex, float weight = 1.0,
504
            const PoseList* poseList = 0);
505
506
507
        /** Returns the morph KeyFrame at the specified index. */
508
        VertexMorphKeyFrame* getVertexMorphKeyFrame(unsigned short index) const;
509
510
        /** Returns the pose KeyFrame at the specified index. */
511
        VertexPoseKeyFrame* getVertexPoseKeyFrame(unsigned short index) const;
512
513
        /** Sets the associated VertexData which this track will update. */
514
0
        void setAssociatedVertexData(VertexData* data) { mTargetVertexData = data; }
515
        /** Gets the associated VertexData which this track will update. */
516
0
        VertexData* getAssociatedVertexData(void) const { return mTargetVertexData; }
517
518
        /// Set the target mode
519
0
        void setTargetMode(TargetMode m) { mTargetMode = m; }
520
        /// Get the target mode
521
0
        TargetMode getTargetMode(void) const { return mTargetMode; }
522
523
        /** Method to determine if this track has any KeyFrames which are
524
        doing anything useful - can be used to determine if this track
525
        can be optimised out.
526
        */
527
        bool hasNonZeroKeyFrames(void) const override;
528
529
        /** Optimise the current track by removing any duplicate keyframes. */
530
        void optimise(void) override;
531
532
        /** Clone this track (internal use only) */
533
        VertexAnimationTrack* _clone(Animation* newParent) const;
534
        
535
        void _applyBaseKeyFrame(const KeyFrame* base) override;
536
537
    private:
538
        /// Animation type
539
        VertexAnimationType mAnimationType;
540
        /// Mode to apply
541
        TargetMode mTargetMode;
542
        /// Target to animate
543
        VertexData* mTargetVertexData;
544
545
        /// @copydoc AnimationTrack::createKeyFrameImpl
546
        KeyFrame* createKeyFrameImpl(Real time) override;
547
548
        /// Utility method for applying pose animation
549
        void applyPoseToVertexData(const Pose* pose, VertexData* data, float influence);
550
551
552
    };
553
    /** @} */
554
    /** @} */
555
}
556
557
#include "OgreHeaderSuffix.h"
558
559
#endif