/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #ifndef PLAGANIM_INC #define PLAGANIM_INC /** \file plAGAnim.h \brief The animation class for the AniGraph animation system \ingroup Avatar \ingroup AniGraph */ #pragma warning(disable: 4786) // don't care if mangled names are longer than 255 characters #include "../pnNetCommon/plSynchedObject.h" #include "hsStlUtils.h" #include "hsStlSortUtils.h" class plTMController; class hsAffineParts; class plAnimTimeConvert; struct hsMatrix44; class plEmoteAnim; class plAGApplicator; class plAGChannel; /** \class plAGAnim This class holds reusable animation data. A single plAGAnim can be instanced any number of times simultaneously. In order to use a plAGAnim, you need a plAGMasterMod, which is a special type of modifier which can be attached to multiple scene objects. A master mod is typically applied to the root of a scene branch, but there is no requirement that all the scene objects animated be children. Each plAGAnim has a number of channels, each of which can animate a single parameter of a single object. Channels are parametric, i.e. they can carry any data type. A plAGAnim carries a name for the animation, and each channel is named as well. Instancing a plAGAnim is done via a plAGAnimInstance. \sa plAGAnimInstance plAGMasterMod plAGModifier */ class plAGAnim : public plSynchedObject { public: /** How much of the body does this emote use? This is handy information for figuring out whether you can, say, wave while sitting down. */ enum BodyUsage { kBodyUnknown, kBodyUpper, kBodyFull, kBodyLower, kBodyMax, kForceSize = 0xff }; plAGAnim(); /** Construct with name, start time, and end time (within the max note track) */ plAGAnim(const char *name, double begin, double end); /** Destruct, freeing the underlying animation data. */ virtual ~plAGAnim(); /** Return the total of number of channels supplied by this animation. An object being animated by this animation does not have to have this many channels; any which are not available will be ignored. This is syntactic sugar: GetApplicatorCount will return the exact same number, but some code is only interested in the channels and not the applicators. */ int GetChannelCount() const; /** Return the ith channel of the animation. Ordering is arbitrary but consistent. It's currently breadth first base on the export algorithm, but don't count on this remaining true. */ plAGChannel * GetChannel(int i) const; /** Get the name of the channel having the given index. Useful for talking to an an animation before it is applied and finding out what channels it's going to affect. */ virtual const char * GetChannelName(int index); /** Get channel by name. This corresponds to the name of the scene object this channel will be attached to when the animation is applied. This function is fairly slow and shouldn't be used often. */ plAGChannel * GetChannel(const char *name) const; /** Return the number of applicators held by this animation. An applicator is used to attach a channel to a sceneobject. */ int GetApplicatorCount() const; /** Return the ith applicator in the channel. Order is arbitrary but consistent, corresponding to processing order in the exporter. */ plAGApplicator * GetApplicator(int i) const; // get applicator by index /** Add an applicator -- which must have a channel behind it. Applicators are translator object which take the output of a channel and apply it to a scene object. */ int AddApplicator(plAGApplicator * app); /** Remove the ith applicator and its associated channel. Existing applicators will be renumbered. */ hsBool RemoveApplicator(int appNum); /** The name of the animation. This name is used in the avatar manager to reference animations. Animations are generally indexed by name when they are loaded by the avatar or from script, but most of the functions which take an animation name (such as AttachAnimation on the plAGMasterMod) will also take an pointer to a plAGAnim. */ virtual const char * GetName() const { return fName; } /** Return the length of the animation; end - start. */ virtual hsScalar GetLength() const { return fEnd - fStart; } /** Hacky function to extend the length of the animation to some minimum length. Does nothing if the animation is already longer than this. */ void ExtendToLength(hsScalar length); /** Return the start time for the beginning of the animation. The animation contains no data prior to this time. In practice, this always returns 0.0f and this function may be deprecated. */ virtual hsScalar GetStart() const { return fStart; } void SetStart(hsScalar start) { fStart = start; } /** Return the end time of the animation. Since start is typically 0, this usually serves as the length of the animation as well. */ virtual hsScalar GetEnd() const { return fEnd; } void SetEnd(hsScalar end) { fEnd = end; } /** Returns true if any applicator on the arg anim tries to use the same pin (on the same object) as we do. */ hsBool SharesPinsWith(const plAGAnim *anim) const; // PLASMA PROTOCOL // rtti CLASSNAME_REGISTER( plAGAnim ); GETINTERFACE_ANY( plAGAnim, plSynchedObject ); // *** temp hack to manage animation instances /** Add the animation by name to a global static registry. This functionality will possibly be added to the resource manager. */ static void AddAnim(const char * name, plAGAnim *anim); /** See if there is an animation with the given name in the global animation registry. */ static plAGAnim *FindAnim(const char *name); /** Remove the given animation from the registry. */ static hsBool RemoveAnim(const char *name); /** Clear the animation cache. Used when resetting the client to a vanilla state, as when clearing the scene while exporting. */ static void ClearAnimationRegistry(); /** Debugging utility. Prints out a list of all the animations in the registry */ static void DumpAnimationRegistry(); // persistance virtual void Read(hsStream* stream, hsResMgr* mgr); virtual void Write(hsStream* stream, hsResMgr* mgr); protected: typedef std::vector ApplicatorVec; ApplicatorVec fApps; /// our animation channels float fBlend; /// requested blend factor hsScalar fStart; /// the start time of the beginning of the animation (usually 0) hsScalar fEnd; /// the end time of the animation char *fName; /// the name of our animation // ??? Can this be moved to the resource manager? If it can manage an efficient // string-based namespace per class, we could get rid of this. typedef std::map plAnimMap; // static plAnimMap fAllAnims; /// map of animation names to animations typedef std::map plEmoteMap; static plEmoteMap fAllEmotes; }; /////////////// // PLATCANIM /////////////// /** \class plATCAnim The most common subclass of plAGAnim. Represents an animation with a standard AnimTimeConvert (i.e. stop/start/loop/etc animation) */ class plATCAnim : public plAGAnim { public: plATCAnim(); /** Construct with name, start time, and end time (within the max note track) Default is to start automatically, not loop, with no ease curves. */ plATCAnim(const char *name, double begin, double end); /** Destruct, freeing the underlying animation data. */ virtual ~plATCAnim(); /** Returns the initial position of the "playback head" for this animation. Animations are not required to start at their actual beginning but can, for instance, start in the middle, play to the end, and then loop to the beginning or to their loop start point. */ virtual hsScalar GetInitial() const { return fInitial; } void SetInitial(hsScalar initial) { fInitial = initial; } /** Does this animation start automatically when it's applied? */ virtual bool GetAutoStart() const { return fAutoStart; } void SetAutoStart(hsBool start) { fAutoStart = (start != 0); } /** If the animation loops, this is where it will restart the loop. Note that loops do not have to start at the beginning of the animation. */ virtual hsScalar GetLoopStart() const { return fLoopStart; } void SetLoopStart(hsScalar start) { fLoopStart = start; } /** If the animation loops, this is the end point of the loop. After passing this point, the animation will cycle around to GetLoopStart */ virtual hsScalar GetLoopEnd() const { return fLoopEnd; } void SetLoopEnd(hsScalar end) { fLoopEnd = end; } /** Does this animation loop?. Note that there may be multiple loop segments defined within a given animation. */ virtual bool GetLoop() const { return fLoop; } void SetLoop(hsBool loop) { fLoop = (loop != 0); } /** Set the curve type for easing in. Easing is an optional feature which allows you to make an animation slow down gradually when you stop it. The types are defined in plAnimEaseTypes.h */ virtual UInt8 GetEaseInType() const { return fEaseInType; } void SetEaseInType(UInt8 type) { fEaseInType = type; } /** Set the length of time the ease-in should take. */ virtual hsScalar GetEaseInLength() const { return fEaseInLength; } /** Set the length of time the ease-in should take. */ void SetEaseInLength(hsScalar length) { fEaseInLength = length; } /** The minimum value used at the start of the ease in. */ virtual hsScalar GetEaseInMin() const { return fEaseInMin; } /** The minimum value used at the start of the ease in. */ void SetEaseInMin(hsScalar length) { fEaseInMin = length; } /** The maximum value reached at the end of the ease in. */ virtual hsScalar GetEaseInMax() const { return fEaseInMax; } /** The maximum value reached at the end of the ease in. */ void SetEaseInMax(hsScalar length) { fEaseInMax = length; } /** The curve type for the ease out. */ virtual UInt8 GetEaseOutType() const { return fEaseOutType; } /** The curve type for the ease out. */ void SetEaseOutType(UInt8 type) { fEaseOutType = type; } /** The length of time for the ease out. */ virtual hsScalar GetEaseOutLength() const { return fEaseOutLength; } /** The length of time for the ease out. */ void SetEaseOutLength(hsScalar length) { fEaseOutLength = length; } /** Minimum value reached in ease-out */ virtual hsScalar GetEaseOutMin() const { return fEaseOutMin; } /** Minimum value reached in ease-out */ void SetEaseOutMin(hsScalar length) { fEaseOutMin = length; } /** Maximum value reached in ease-in */ virtual hsScalar GetEaseOutMax() const { return fEaseOutMax; } /** Maximum value reached in ease-in */ void SetEaseOutMax(hsScalar length) { fEaseOutMax = length; } /** Animations can have multiple defined loop segments; these are selected using animation control messages. Each loop segment is named using markers in the notetrack. */ void AddLoop(const char *name, float start, float end); /** Get the loop having the given name. \param start will return the start time of the loop. \param end will hold the end time of the loop */ bool GetLoop(const char *name, float &start, float &end) const; /** Lets you get a loop by index instead of name. */ bool GetLoop(UInt32 num, float &start, float &end) const; /** Returns the number of loops defined on this anim. */ UInt32 GetNumLoops() const; /** Add a marker to the animation. Markers can be used for callbacks or for goto comands. A marker is a simple name/time tuple. */ void AddMarker(const char *name, float time); /** Returns the time value of the marker named by name. */ float GetMarker(const char *name) const; void CopyMarkerNames(std::vector &out); /** Add a stop point to the animation. A stop point is a "detent" for playback - if the animation is stopping near a stop point and fading out, the stop point will override the fade, so that the animation stops precisely at the defined time. */ void AddStopPoint(hsScalar time); /** Return the number of stop points defined for this animation. */ UInt32 NumStopPoints(); /** Get the time corresponding to the given stop point. Stop points are numbered in the order they were added. */ hsScalar GetStopPoint(UInt32 i); /** Function to check for a zero-length loop, and set it to the anim's start/end instead */ void CheckLoop(); // PLASMA PROTOCOL // rtti CLASSNAME_REGISTER( plATCAnim ); GETINTERFACE_ANY( plATCAnim, plAGAnim ); // persistance virtual void Read(hsStream* stream, hsResMgr* mgr); virtual void Write(hsStream* stream, hsResMgr* mgr); protected: hsScalar fInitial; /// the position of the playback head bool fAutoStart; /// does the animation start automatically? hsScalar fLoopStart; /// when wrapping a loop, start here hsScalar fLoopEnd; /// when you reach this point, loop back bool fLoop; /// do we loop? UInt8 fEaseInType; /// the type (none/linear/spline) of our ease-in curve, if any UInt8 fEaseOutType; /// the type (none/linear/spline) of our ease-out curve, if any hsScalar fEaseInLength; /// the length of time our ease-in curve takes hsScalar fEaseInMin; /// minimum (initial) value of our ease-in hsScalar fEaseInMax; /// maximum (final) value of our ease-in hsScalar fEaseOutLength; /// the length of time our ease-out curve takes hsScalar fEaseOutMin; /// minimum (final) value of our ease-out hsScalar fEaseOutMax; /// maximum (initial) value of our ease-out // a map from segment names to times typedef std::map MarkerMap; MarkerMap fMarkers; typedef std::map, stringSorter> LoopMap; LoopMap fLoops; typedef std::vector ScalarMap; ScalarMap fStopPoints; /// vector of stop points }; /** \class plEmoteAnim An animation to be used for emotes. Automatically registers so that it can be played from the chat field. */ class plEmoteAnim : public plATCAnim { public: plEmoteAnim(); plEmoteAnim(const char *animName, double begin, double end, float fadeIn, float fadeOut, BodyUsage bodyUsage); BodyUsage GetBodyUsage() const; hsScalar GetFadeIn() const; hsScalar GetFadeOut() const; CLASSNAME_REGISTER( plEmoteAnim ); GETINTERFACE_ANY( plEmoteAnim, plATCAnim ); virtual void Read(hsStream *stream, hsResMgr *mgr); virtual void Write(hsStream *stream, hsResMgr *mgr); protected: BodyUsage fBodyUsage; // how much of the body is used by this emote? hsScalar fFadeIn; // how fast to fade in the emote hsScalar fFadeOut; // how fast to fade out the emote }; ////////////////// // PLAGEGLOBALANIM ////////////////// /** \class plAgeGlobalAnim An animation that bases its current position on a variable that global to the age, like weather, time of day, etc. */ class plAgeGlobalAnim : public plAGAnim { public: plAgeGlobalAnim(); /** Construct with name, start time, and end time (within the max note track) */ plAgeGlobalAnim(const char *name, double begin, double end); /** Destruct, freeing the underlying animation data. */ virtual ~plAgeGlobalAnim(); const char * GetGlobalVarName() const { return fGlobalVarName; } void SetGlobalVarName(char *name); // PLASMA PROTOCOL // rtti CLASSNAME_REGISTER( plAgeGlobalAnim ); GETINTERFACE_ANY( plAgeGlobalAnim, plAGAnim ); // persistance virtual void Read(hsStream* stream, hsResMgr* mgr); virtual void Write(hsStream* stream, hsResMgr* mgr); protected: char *fGlobalVarName; // Name of the SDL variable we animate on. }; // USEFUL HELPER FUNCTIONS bool GetStartToEndTransform(const plAGAnim *anim, hsMatrix44 *startToEnd, hsMatrix44 *endToStart, const char *channelName); bool GetRelativeTransform(const plAGAnim *anim, double timeA, double timeB, hsMatrix44 *a2b, hsMatrix44 *b2a, const char *channelName); #endif