1
0
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-18 19:29:09 +00:00

Move AniGraph classes from plAvatar -> plAnimation.

This commit is contained in:
Darryl Pogue
2014-10-19 00:09:07 -07:00
parent 69bc106576
commit 4450d87947
81 changed files with 429 additions and 297 deletions

View File

@ -0,0 +1,43 @@
include_directories("../../CoreLib")
include_directories("../../FeatureLib")
include_directories("../../NucleusLib/inc")
include_directories("../../NucleusLib")
include_directories("../../PubUtilLib")
set(plAnimation_SOURCES
plAGAnim.cpp
plAGAnimInstance.cpp
plAGApplicator.cpp
plAGChannel.cpp
plAGMasterMod.cpp
plAGModifier.cpp
plMatrixChannel.cpp
plPointChannel.cpp
plQuatChannel.cpp
plScalarChannel.cpp
)
set(plAnimation_HEADERS
plAGAnim.h
plAGAnimInstance.h
plAGApplicator.h
plAGChannel.h
plAGDefs.h
plAGMasterMod.h
plAGModifier.h
plAnimationCreatable.h
plMatrixChannel.h
plPointChannel.h
plQuatChannel.h
plScalarChannel.h
)
add_library(plAnimation STATIC ${plAnimation_SOURCES} ${plAnimation_HEADERS})
target_link_libraries(plAnimation pnModifier)
target_link_libraries(plAnimation plInterp)
target_link_libraries(plAnimation plSDL)
target_link_libraries(plAnimation plTransform)
source_group("Source Files" FILES ${plAnimation_SOURCES})
source_group("Header Files" FILES ${plAnimation_HEADERS})

View File

@ -0,0 +1,756 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
// singular
#include "plAGAnim.h"
// local
#include "plMatrixChannel.h"
// global
#include "hsResMgr.h"
// other
#include "plInterp/plAnimEaseTypes.h"
#include "plMessage/plAnimCmdMsg.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// STATICS
//
/////////////////////////////////////////////////////////////////////////////////////////
plAGAnim::plAnimMap plAGAnim::fAllAnims;
/////////////////////////////////////////////////////////////////////////////////////////
//
// plAGAnim
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor ------------
// -----
plAGAnim::plAGAnim()
: plSynchedObject()
{
}
// ctor ------------------------------------------------------
// -----
plAGAnim::plAGAnim(const plString &name, double start, double end)
: fStart((float)start),
fEnd((float)end),
fName(name)
{
}
// dtor -------------
// -----
plAGAnim::~plAGAnim()
{
if (!fName.IsNull())
{
RemoveAnim(fName);
}
//int numChannels = fChannels.size();
int numApps = fApps.size();
for (int i = 0; i < numApps; i++)
{
plAGApplicator *app = fApps[i];
if (app)
{
plAGChannel *channel = app->GetChannel();
if(channel)
delete channel;
delete app;
}
}
}
// GetChannelCount ------------------
// ----------------
int plAGAnim::GetChannelCount() const
{
return fApps.size();
}
// GetChannel -------------------------------------
// -----------
plAGChannel * plAGAnim::GetChannel(int index) const
{
plAGApplicator *app = fApps[index];
return (app ? app->GetChannel() : nil);
}
// GetChannel --------------------------------------------
// -----------
plAGChannel * plAGAnim::GetChannel(const plString &name) const
{
int appCount = fApps.size();
for(int i = 0; i < appCount; i++)
{
plAGApplicator *app = fApps[i];
plAGChannel *channel = app->GetChannel();
plString channelName = app->GetChannelName();
if(name.Compare(channelName, plString::kCaseInsensitive) == 0)
{
return channel;
}
}
return nil;
}
// GetApplicatorCount ------------------
// -------------------
int plAGAnim::GetApplicatorCount() const
{
return fApps.size();
}
// GetApplicator -----------------------------------
// --------------
plAGApplicator *plAGAnim::GetApplicator(int i) const
{
return fApps[i];
}
// AddApplicator -------------------------------
// --------------
int plAGAnim::AddApplicator(plAGApplicator *app)
{
hsAssert(app->GetChannel(), "Adding an applicator with no channel");
fApps.push_back(app);
// return the index of the channel
return(fApps.size() - 1);
}
// RemoveApplicator ------------------------
// -----------------
bool plAGAnim::RemoveApplicator(int index)
{
hsAssert(index < fApps.size(), "Out of range index for plAGAnim::RemoveApp()");
if(index < fApps.size())
{
fApps.erase(fApps.begin() + index);
return true;
} else {
return false;
}
}
// ExtendToLength ----------------------------
// ---------------
void plAGAnim::ExtendToLength(float length)
{
if (length > GetEnd())
SetEnd(length);
}
// GetChannelName ------------------------------
// ---------------
plString plAGAnim::GetChannelName(int index)
{
hsAssert(index < fApps.size(), "Out of range index for plAGAnim::GetChannelName()");
if(index < fApps.size())
{
return fApps[index]->GetChannel()->GetName();
} else {
return plString::Null;
}
}
// Read --------------------------------------------
// -----
void plAGAnim::Read(hsStream *stream, hsResMgr *mgr)
{
plSynchedObject::Read(stream, mgr);
// read in the name of the animation itself
fName = stream->ReadSafeString();
fStart = stream->ReadLEScalar();
fEnd = stream->ReadLEScalar();
int numApps = stream->ReadLE32();
fApps.reserve(numApps); // pre-allocate for performance
int i;
for (i = 0; i < numApps; i++)
{
plAGApplicator * app = plAGApplicator::ConvertNoRef(mgr->ReadCreatable(stream));
app->SetChannel(plAGChannel::ConvertNoRef(mgr->ReadCreatable(stream)));
fApps.push_back(app);
}
plAGAnim::AddAnim(fName, this);
}
// Write --------------------------------------------
// ------
void plAGAnim::Write(hsStream *stream, hsResMgr *mgr)
{
plSynchedObject::Write(stream, mgr);
stream->WriteSafeString(fName);
stream->WriteLEScalar(fStart);
stream->WriteLEScalar(fEnd);
int numApps = fApps.size();
stream->WriteLE32(numApps);
int i;
for (i = 0; i < numApps; i++)
{
plAGApplicator *app = fApps[i];
hsAssert(app, "Missing applicator during write.");
plAGChannel *channel = nil;
if (app)
channel = app->GetChannel();
hsAssert(channel, "Missing channel during write.");
mgr->WriteCreatable(stream, app);
mgr->WriteCreatable(stream, channel);
}
}
void plAGAnim::ClearAnimationRegistry()
{
fAllAnims.clear();
}
// AddAnim ----------------------------------------------
// --------
void plAGAnim::AddAnim(const plString & name, plAGAnim *anim)
{
// Only register the animation if it's got a "real" name. Unnamed animations
// all get the same standard name.
if(name.Compare(ENTIRE_ANIMATION_NAME) != 0)
{
hsAssert(anim, "registering nil anim");
fAllAnims[name] = anim;
}
}
// FindAnim -----------------------------------
// ---------
plAGAnim * plAGAnim::FindAnim(const plString &name)
{
plAnimMap::iterator i = fAllAnims.find(name);
if(i != fAllAnims.end())
{
return (*i).second;
} else {
return nil;
}
}
// RemoveAnim -------------------------------
// -----------
bool plAGAnim::RemoveAnim(const plString &name)
{
plAnimMap::iterator i = fAllAnims.find(name);
if(i != fAllAnims.end())
{
fAllAnims.erase(i);
return true;
} else {
return false;
}
}
// DumpAnimationRegistry -------------
// ----------------------
void plAGAnim::DumpAnimationRegistry()
{
plAnimMap::iterator i = fAllAnims.begin();
int j = 0;
do {
plAGAnim *anim = (*i).second;
plString name = anim->GetName();
hsStatusMessageF("GLOBAL ANIMS [%d]: <%s>", j++, name.c_str());
} while(++i != fAllAnims.end());
}
// SharesPinsWith -----------------------------------------
// ---------------
bool plAGAnim::SharesPinsWith(const plAGAnim *anim) const
{
int i, j;
for (i = 0; i < fApps.size(); i++)
{
for (j = 0; j < anim->fApps.size(); j++)
{
if (!fApps[i]->GetChannelName().Compare(anim->fApps[j]->GetChannelName()) &&
fApps[i]->CanBlend(anim->fApps[j]))
{
return true;
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plATCAnim
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------
// -----
plATCAnim::plATCAnim()
: plAGAnim()
{
}
// ctor --------------------------------------------------------
// -----
plATCAnim::plATCAnim(const plString &name, double start, double end)
: plAGAnim(name, start, end),
fInitial(-1),
fAutoStart(true),
fLoopStart((float)start),
fLoopEnd((float)end),
fLoop(false),
fEaseInType(plAnimEaseTypes::kNoEase),
fEaseOutType(plAnimEaseTypes::kNoEase),
fEaseInLength(0),
fEaseOutLength(0),
fEaseInMin(0.f),
fEaseInMax(0.f),
fEaseOutMin(0.f),
fEaseOutMax(0.f)
{
}
// dtor ---------------
// -----
plATCAnim::~plATCAnim()
{
fMarkers.clear();
fLoops.clear();
fStopPoints.clear();
}
// Read ---------------------------------------------
// -----
void plATCAnim::Read(hsStream *stream, hsResMgr *mgr)
{
plAGAnim::Read(stream, mgr);
fInitial = stream->ReadLEScalar();
fAutoStart = stream->ReadBool();
fLoopStart = stream->ReadLEScalar();
fLoopEnd = stream->ReadLEScalar();
fLoop = stream->ReadBool();
fEaseInType = stream->ReadByte();
fEaseInMin = stream->ReadLEScalar();
fEaseInMax = stream->ReadLEScalar();
fEaseInLength = stream->ReadLEScalar();
fEaseOutType = stream->ReadByte();
fEaseOutMin = stream->ReadLEScalar();
fEaseOutMax = stream->ReadLEScalar();
fEaseOutLength = stream->ReadLEScalar();
int i;
int numMarkers = stream->ReadLE32();
for (i = 0; i < numMarkers; i++)
{
plString name = stream->ReadSafeString();
float time = stream->ReadLEFloat();
fMarkers[name] = time;
}
int numLoops = stream->ReadLE32();
for (i = 0; i < numLoops; i++)
{
plString name = stream->ReadSafeString();
float begin = stream->ReadLEScalar();
float end = stream->ReadLEScalar();
fLoops[name] = std::pair<float,float>(begin,end);
}
int numStops = stream->ReadLE32();
for (i = 0; i < numStops; i++)
fStopPoints.push_back(stream->ReadLEScalar());
}
// Write ---------------------------------------------
// ------
void plATCAnim::Write(hsStream *stream, hsResMgr *mgr)
{
plAGAnim::Write(stream, mgr);
stream->WriteLEScalar(fInitial);
stream->WriteBool(fAutoStart);
stream->WriteLEScalar(fLoopStart);
stream->WriteLEScalar(fLoopEnd);
stream->WriteBool(fLoop);
stream->WriteByte(fEaseInType);
stream->WriteLEScalar(fEaseInMin);
stream->WriteLEScalar(fEaseInMax);
stream->WriteLEScalar(fEaseInLength);
stream->WriteByte(fEaseOutType);
stream->WriteLEScalar(fEaseOutMin);
stream->WriteLEScalar(fEaseOutMax);
stream->WriteLEScalar(fEaseOutLength);
stream->WriteLE32(fMarkers.size());
for (MarkerMap::iterator it = fMarkers.begin(); it != fMarkers.end(); it++)
{
stream->WriteSafeString(it->first);
stream->WriteLEFloat(it->second);
}
stream->WriteLE32(fLoops.size());
for (LoopMap::iterator loopIt = fLoops.begin(); loopIt != fLoops.end(); loopIt++)
{
stream->WriteSafeString(loopIt->first);
std::pair<float,float>& loop = loopIt->second;
stream->WriteLEFloat(loop.first);
stream->WriteLEFloat(loop.second);
}
int i;
stream->WriteLE32(fStopPoints.size());
for (i = 0; i < fStopPoints.size(); i++)
stream->WriteLEScalar(fStopPoints[i]);
}
// CheckLoop --------------
// ----------
void plATCAnim::CheckLoop()
{
if (fLoopStart == fLoopEnd)
{
fLoopStart = fStart;
fLoopEnd = fEnd;
}
}
// AddLoop ------------------------------------------------------
// --------
void plATCAnim::AddLoop(const plString &name, float start, float end)
{
fLoops[name] = std::pair<float,float>(start, end);
}
// GetLoop --------------------------------------------------------------
// --------
bool plATCAnim::GetLoop(const plString &name, float &start, float &end) const
{
LoopMap::const_iterator it = fLoops.find(name);
if (it != fLoops.end())
{
const std::pair<float,float>& loop = (*it).second;
start = loop.first;
end = loop.second;
return true;
}
return false;
}
// GetLoop --------------------------------------------------------
// --------
bool plATCAnim::GetLoop(uint32_t num, float &start, float &end) const
{
if (num >= fLoops.size())
return false;
LoopMap::const_iterator it = fLoops.begin();
while (num > 0)
{
it++;
num--;
}
const std::pair<float,float>& loop = (*it).second;
start = loop.first;
end = loop.second;
return true;
}
// GetNumLoops ----------------------
// ------------
uint32_t plATCAnim::GetNumLoops() const
{
return fLoops.size();
}
// AddMarker ------------------------------------------
// ----------
void plATCAnim::AddMarker(const plString &name, float time)
{
fMarkers[name] = time;
}
// GetMarker -------------------------------------
// ----------
float plATCAnim::GetMarker(const plString &name) const
{
if (fMarkers.find(name) != fMarkers.end())
return (*fMarkers.find(name)).second;
return -1;
}
// CopyMarkerNames -------------------------------------
// ----------------
void plATCAnim::CopyMarkerNames(std::vector<plString> &out)
{
MarkerMap::iterator it = fMarkers.begin();
out.reserve(fMarkers.size());
for (; it != fMarkers.end(); it++)
{
out.push_back((*it).first);
}
}
// AddStopPoint ---------------------------
// -------------
void plATCAnim::AddStopPoint(float time)
{
fStopPoints.push_back(time);
}
// NumStopPoints ----------------
// --------------
uint32_t plATCAnim::NumStopPoints()
{
return fStopPoints.size();
}
// GetStopPoint --------------------------
// -------------
float plATCAnim::GetStopPoint(uint32_t i)
{
hsAssert(i < fStopPoints.size(), "Invalid index for GetStopPoint");
return fStopPoints[i];
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plEmoteAnim
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor ------------------
// -----
plEmoteAnim::plEmoteAnim()
: fBodyUsage(kBodyFull)
{
}
// ctor ------------------------------------------------------------------------------
// -----
plEmoteAnim::plEmoteAnim(const plString &animName, double begin, double end, float fadeIn,
float fadeOut, BodyUsage bodyUsage)
: plATCAnim(animName, begin, end),
fFadeIn(fadeIn),
fFadeOut(fadeOut),
fBodyUsage(bodyUsage)
{
}
// Read -----------------------------------------------
// -----
void plEmoteAnim::Read(hsStream *stream, hsResMgr *mgr)
{
plATCAnim::Read(stream, mgr);
// plAGAnim::RegisterEmote(fName, this);
fFadeIn = stream->ReadLEScalar();
fFadeOut = stream->ReadLEScalar();
fBodyUsage = static_cast<BodyUsage>(stream->ReadByte());
}
// Write -----------------------------------------------
// ------
void plEmoteAnim::Write(hsStream *stream, hsResMgr *mgr)
{
plATCAnim::Write(stream, mgr);
stream->WriteLEScalar(fFadeIn);
stream->WriteLEScalar(fFadeOut);
stream->WriteByte(static_cast<uint8_t>(fBodyUsage));
}
// GetBodyUsage ----------------------------------------
// -------------
plEmoteAnim::BodyUsage plEmoteAnim::GetBodyUsage() const
{
return fBodyUsage;
}
// GetFadeIn -----------------------
// ----------
float plEmoteAnim::GetFadeIn() const
{
return fFadeIn;
}
// GetFadeOut -----------------------
// -----------
float plEmoteAnim::GetFadeOut() const
{
return fFadeOut;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plAgeGlobalAnim
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------------------
// -----
plAgeGlobalAnim::plAgeGlobalAnim()
: plAGAnim()
{
}
// ctor --------------------------------------------------------------------
// -----
plAgeGlobalAnim::plAgeGlobalAnim(const plString &name, double start, double end)
: plAGAnim(name, start, end)
{
}
// Read ---------------------------------------------------
// -----
void plAgeGlobalAnim::Read(hsStream *stream, hsResMgr *mgr)
{
plAGAnim::Read(stream, mgr);
fGlobalVarName = stream->ReadSafeString();
}
// Write ---------------------------------------------------
// ------
void plAgeGlobalAnim::Write(hsStream *stream, hsResMgr *mgr)
{
plAGAnim::Write(stream, mgr);
stream->WriteSafeString(fGlobalVarName);
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// UTILITIES
//
/////////////////////////////////////////////////////////////////////////////////////////
// GetStartToEndTransform -----------------------------------------------
bool GetStartToEndTransform(const plAGAnim *anim, hsMatrix44 *startToEnd,
hsMatrix44 *endToStart, const plString &channelName)
{
double start = 0.0f; // assumed
double end = anim->GetEnd();
GetRelativeTransform(anim, start, end, startToEnd, endToStart, channelName);
return true;
}
// GetRelativeTransform ---------------------------------------------------
bool GetRelativeTransform(const plAGAnim *anim, double timeA, double timeB,
hsMatrix44 *a2b, hsMatrix44 *b2a, const plString &channelName)
{
bool result = false;
plAGChannel *maybeChannel = anim->GetChannel(channelName);
hsAssert(maybeChannel, "Couldn't find channel with given name.");
if(maybeChannel)
{
plMatrixChannel *channel = plMatrixChannel::ConvertNoRef(maybeChannel);
hsAssert(channel, "Found channel, but it's not a matrix channel.");
if(channel)
{
hsMatrix44 matA;
hsMatrix44 matB;
channel->Value(matA, timeA);
channel->Value(matB, timeB);
if(a2b) // requested a transform from point A to point B
{
hsMatrix44 invA;
matA.GetInverse(&invA);
*a2b = invA * matB;
}
if(b2a) // requested a transform from point B to point A
{
hsMatrix44 invB;
matB.GetInverse(&invB);
*b2a = invB * matA;
if(a2b)
{
hsMatrix44 invB2;
a2b->GetInverse(&invB2);
}
}
}
}
return result;
}

View File

@ -0,0 +1,443 @@
/*==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 <http://www.gnu.org/licenses/>.
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 <map>
#include "pnNetCommon/plSynchedObject.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 plString &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 plString 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 plString &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. */
bool 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 plString GetName() const { return fName; }
/** Return the length of the animation; end - start. */
virtual float 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(float 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 float GetStart() const { return fStart; }
void SetStart(float 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 float GetEnd() const { return fEnd; }
void SetEnd(float 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. */
bool 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 plString & name, plAGAnim *anim);
/** See if there is an animation with the given name in the
global animation registry. */
static plAGAnim *FindAnim(const plString &name);
/** Remove the given animation from the registry. */
static bool RemoveAnim(const plString &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<plAGApplicator*> ApplicatorVec;
ApplicatorVec fApps; /// our animation channels
float fBlend; /// requested blend factor
float fStart; /// the start time of the beginning of the animation (usually 0)
float fEnd; /// the end time of the animation
plString 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<plString, plAGAnim *, plString::less_i> plAnimMap; //
static plAnimMap fAllAnims; /// map of animation names to animations
typedef std::map<plString, plEmoteAnim *, plString::less_i> 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 plString &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 float GetInitial() const { return fInitial; }
void SetInitial(float initial) { fInitial = initial; }
/** Does this animation start automatically when it's applied? */
virtual bool GetAutoStart() const { return fAutoStart; }
void SetAutoStart(bool 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 float GetLoopStart() const { return fLoopStart; }
void SetLoopStart(float 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 float GetLoopEnd() const { return fLoopEnd; }
void SetLoopEnd(float 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(bool 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_t GetEaseInType() const { return fEaseInType; }
void SetEaseInType(uint8_t type) { fEaseInType = type; }
/** Set the length of time the ease-in should take. */
virtual float GetEaseInLength() const { return fEaseInLength; }
/** Set the length of time the ease-in should take. */
void SetEaseInLength(float length) { fEaseInLength = length; }
/** The minimum value used at the start of the ease in. */
virtual float GetEaseInMin() const { return fEaseInMin; }
/** The minimum value used at the start of the ease in. */
void SetEaseInMin(float length) { fEaseInMin = length; }
/** The maximum value reached at the end of the ease in. */
virtual float GetEaseInMax() const { return fEaseInMax; }
/** The maximum value reached at the end of the ease in. */
void SetEaseInMax(float length) { fEaseInMax = length; }
/** The curve type for the ease out. */
virtual uint8_t GetEaseOutType() const { return fEaseOutType; }
/** The curve type for the ease out. */
void SetEaseOutType(uint8_t type) { fEaseOutType = type; }
/** The length of time for the ease out. */
virtual float GetEaseOutLength() const { return fEaseOutLength; }
/** The length of time for the ease out. */
void SetEaseOutLength(float length) { fEaseOutLength = length; }
/** Minimum value reached in ease-out */
virtual float GetEaseOutMin() const { return fEaseOutMin; }
/** Minimum value reached in ease-out */
void SetEaseOutMin(float length) { fEaseOutMin = length; }
/** Maximum value reached in ease-in */
virtual float GetEaseOutMax() const { return fEaseOutMax; }
/** Maximum value reached in ease-in */
void SetEaseOutMax(float 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 plString &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 plString &name, float &start, float &end) const;
/** Lets you get a loop by index instead of name. */
bool GetLoop(uint32_t num, float &start, float &end) const;
/** Returns the number of loops defined on this anim. */
uint32_t 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 plString &name, float time);
/** Returns the time value of the marker named by name. */
float GetMarker(const plString &name) const;
void CopyMarkerNames(std::vector<plString> &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(float time);
/** Return the number of stop points defined for this animation. */
uint32_t NumStopPoints();
/** Get the time corresponding to the given stop point. Stop points
are numbered in the order they were added. */
float GetStopPoint(uint32_t 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:
float fInitial; /// the position of the playback head
bool fAutoStart; /// does the animation start automatically?
float fLoopStart; /// when wrapping a loop, start here
float fLoopEnd; /// when you reach this point, loop back
bool fLoop; /// do we loop?
uint8_t fEaseInType; /// the type (none/linear/spline) of our ease-in curve, if any
uint8_t fEaseOutType; /// the type (none/linear/spline) of our ease-out curve, if any
float fEaseInLength; /// the length of time our ease-in curve takes
float fEaseInMin; /// minimum (initial) value of our ease-in
float fEaseInMax; /// maximum (final) value of our ease-in
float fEaseOutLength; /// the length of time our ease-out curve takes
float fEaseOutMin; /// minimum (final) value of our ease-out
float fEaseOutMax; /// maximum (initial) value of our ease-out
// a map from segment names to times
typedef std::map<plString, float> MarkerMap;
MarkerMap fMarkers;
typedef std::map<plString, std::pair<float,float> > LoopMap;
LoopMap fLoops;
typedef std::vector<float> 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 plString &animName, double begin, double end, float fadeIn, float fadeOut, BodyUsage bodyUsage);
BodyUsage GetBodyUsage() const;
float GetFadeIn() const;
float 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?
float fFadeIn; // how fast to fade in the emote
float 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 plString &name, double begin, double end);
plString GetGlobalVarName() const { return fGlobalVarName; }
void SetGlobalVarName(const plString &name) { fGlobalVarName = 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:
plString fGlobalVarName; // Name of the SDL variable we animate on.
};
// USEFUL HELPER FUNCTIONS
bool GetStartToEndTransform(const plAGAnim *anim, hsMatrix44 *startToEnd, hsMatrix44 *endToStart, const plString &channelName);
bool GetRelativeTransform(const plAGAnim *anim, double timeA, double timeB, hsMatrix44 *a2b, hsMatrix44 *b2a, const plString &channelName);
#endif

View File

@ -0,0 +1,681 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
#include <algorithm>
// singular
#include "plAGAnimInstance.h"
// local
#include "plAGAnim.h"
#include "plAGModifier.h"
#include "plAGMasterMod.h"
// global
#include "hsTimer.h" // just when debugging for GetSysSeconds
// other
#include "pnNetCommon/plSDLTypes.h"
#include "plMessage/plAnimCmdMsg.h"
#include "plMessage/plOneShotCallbacks.h"
#include "plModifier/plSDLModifier.h"
#include "plSDL/plSDL.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// FLAGS
//
/////////////////////////////////////////////////////////////////////////////////////////
// enable this to show blend trees before and after attaches and detaches
// #define SHOW_AG_CHANGES
/////////////////////////////////////////////////////////////////////////////////////////
//
// STATIC
//
/////////////////////////////////////////////////////////////////////////////////////////
#ifdef TRACK_AG_ALLOCS
plString gGlobalAnimName;
plString gGlobalChannelName;
#endif // TRACK_AG_ALLOCS
/////////////////////////////////////////////////////////////////////////////////////////
//
// plAGAnimInstance
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor -------------------------------------------------------------------
// -----
plAGAnimInstance::plAGAnimInstance(plAGAnim * anim, plAGMasterMod * master,
float blend, uint16_t blendPriority, bool cache,
bool useAmplitude)
: fAnimation(anim),
fMaster(master),
fBlend(blend),
fAmplitude(useAmplitude ? 1.0f : -1.0f)
{
int i;
fTimeConvert = nil;
plScalarChannel *timeChan = nil;
#ifdef TRACK_AG_ALLOCS
gGlobalAnimName = anim->GetName(); // for debug tracking...
#endif // TRACK_AG_ALLOCS
plATCAnim *atcAnim = plATCAnim::ConvertNoRef(anim);
if (atcAnim)
{
fTimeConvert = new plAnimTimeConvert();
IInitAnimTimeConvert(fTimeConvert, atcAnim, master);
//fTimeConvert->Init(atcAnim, this, master);
timeChan = new plATCChannel(fTimeConvert);
}
else
{
timeChan = new plScalarSDLChannel(anim->GetLength());
fSDLChannels.push_back((plScalarSDLChannel *)timeChan);
}
int nInChannels = anim->GetChannelCount();
fCleanupChannels.push_back(timeChan);
#ifdef SHOW_AG_CHANGES
hsStatusMessageF("\nAbout to Attach anim <%s>", GetName().c_str());
fMaster->DumpAniGraph("bone_pelvis", false, hsTimer::GetSysSeconds());
#endif
for (i = 0; i < nInChannels; i++)
{
plAGApplicator * app = fAnimation->GetApplicator(i);
plAGChannel * inChannel = app->GetChannel();
plString channelName = app->GetChannelName();
plAGModifier * channelMod = master->GetChannelMod(channelName);
if(channelMod) {
#ifdef TRACK_AG_ALLOCS
gGlobalChannelName = channelName;
#endif // TRACK_AG_ALLOCS
// we're going to be accumulating a chain of channels.
// curChannel will always point to the top one...
plAGChannel *topNode = inChannel;
if(cache)
{
topNode = topNode->MakeCacheChannel(fTimeConvert);
IRegisterDetach(channelName, topNode);
}
if(useAmplitude)
{
// amplitude is rarely used and expensive, so only alloc if asked
// first build a static copy of the incoming channel...
plAGChannel *zeroState = inChannel->MakeZeroState();
IRegisterDetach(channelName, zeroState);
// now make a blend node to blend the anim with its static copy
topNode = zeroState->MakeBlend(topNode, &fAmplitude, -1);
}
// make a time scaler to localize time for this instance
topNode = topNode->MakeTimeScale(timeChan);
IRegisterDetach(channelName, topNode);
channelMod->MergeChannel(app, topNode, &fBlend, this, blendPriority);
}
else
hsAssert(false, "Adding an animation with an invalid channel.");
}
fFadeBlend = fFadeAmp = false;
#ifdef TRACK_AG_ALLOCS
gGlobalAnimName = "";
#endif // TRACK_AG_ALLOCS
}
// dtor -----------------------------
// -----
plAGAnimInstance::~plAGAnimInstance()
{
delete fTimeConvert;
}
void plAGAnimInstance::IInitAnimTimeConvert(plAnimTimeConvert* atc, plATCAnim* anim, plAGMasterMod* master)
{
// Set up our eval callbacks
plAGInstanceCallbackMsg* instMsg;
instMsg = new plAGInstanceCallbackMsg(master->GetKey(), kStart);
instMsg->fInstance = this;
atc->AddCallback(instMsg);
hsRefCnt_SafeUnRef(instMsg);
instMsg = new plAGInstanceCallbackMsg(master->GetKey(), kStop);
instMsg->fInstance = this;
atc->AddCallback(instMsg);
hsRefCnt_SafeUnRef(instMsg);
instMsg = new plAGInstanceCallbackMsg(master->GetKey(), kSingleFrameAdjust);
instMsg->fInstance = this;
atc->AddCallback(instMsg);
hsRefCnt_SafeUnRef(instMsg);
atc->SetOwner(master);
atc->ClearFlags();
for (size_t i = 0; i < anim->NumStopPoints(); i++)
{
atc->GetStopPoints().Append(anim->GetStopPoint(i));
}
atc->SetBegin(anim->GetStart());
atc->SetEnd(anim->GetEnd());
atc->SetInitialBegin(atc->GetBegin());
atc->SetInitialEnd(atc->GetEnd());
if (anim->GetInitial() != -1)
{
atc->SetCurrentAnimTime(anim->GetInitial());
}
else
{
atc->SetCurrentAnimTime(anim->GetStart());
}
atc->SetLoopPoints(anim->GetLoopStart(), anim->GetLoopEnd());
atc->Loop(anim->GetLoop());
atc->SetSpeed(1.f);
atc->SetEase(true, anim->GetEaseInType(), anim->GetEaseInMin(),
anim->GetEaseInMax(), anim->GetEaseInLength());
atc->SetEase(false, anim->GetEaseOutType(), anim->GetEaseOutMin(),
anim->GetEaseOutMax(), anim->GetEaseOutLength());
// set up our time converter based on the animation's specs...
// ... after we've set all of its other state values.
if (anim->GetAutoStart())
{
plSynchEnabler ps(true); // enable dirty tracking so that autostart will send out a state update
atc->Start();
}
else
{
atc->InitStop();
}
}
// SearchForGlobals ---------------------
// -----------------
void plAGAnimInstance::SearchForGlobals()
{
const plAgeGlobalAnim *ageAnim = plAgeGlobalAnim::ConvertNoRef(fAnimation);
if (ageAnim != nil && fSDLChannels.size() > 0)
{
extern const plSDLModifier *ExternFindAgeSDL();
const plSDLModifier *sdlMod = ExternFindAgeSDL();
if (!sdlMod)
return;
plSimpleStateVariable *var = sdlMod->GetStateCache()->FindVar(ageAnim->GetGlobalVarName());
if (!var)
return;
sdlMod->AddNotifyForVar(fMaster->GetKey(), ageAnim->GetGlobalVarName(), 0);
int i;
for (i = 0; i < fSDLChannels.size(); i++)
fSDLChannels[i]->SetVar(var);
}
}
void plAGAnimInstance::IRegisterDetach(const plString &channelName, plAGChannel *channel)
{
plDetachMap::value_type newPair(channelName, channel);
fManualDetachChannels.insert(newPair);
}
// SetCurrentTime ---------------------------------------------------------------
// ---------------
void plAGAnimInstance::SetCurrentTime(float localT, bool jump /* = false */)
{
if (fTimeConvert)
fTimeConvert->SetCurrentAnimTime(localT, jump);
}
// SeekRelative ------------------------------------
// -------------
void plAGAnimInstance::SeekRelative (float delta, bool jump)
{
if(fTimeConvert)
{
float now = fTimeConvert->CurrentAnimTime();
fTimeConvert->SetCurrentAnimTime (now + delta, jump);
}
}
// Detach ---------------------
// -------
void plAGAnimInstance::Detach()
{
fMaster->DetachAnimation(this);
}
// DetachChannels ---------------------
// ---------------
void plAGAnimInstance::DetachChannels()
{
#ifdef SHOW_AG_CHANGES
hsStatusMessageF("\nAbout to DETACH anim <%s>", GetName().c_str());
fMaster->DumpAniGraph("bone_pelvis", false, hsTimer::GetSysSeconds());
#endif
plDetachMap::iterator i = fManualDetachChannels.begin();
while(i != fManualDetachChannels.end())
{
plString channelName = (*i).first;
plAGModifier *channelMod = fMaster->GetChannelMod(channelName, true);
if(channelMod)
{
do {
plAGChannel *channel = (*i).second;
channelMod->DetachChannel(channel);
} while (++i != fManualDetachChannels.end() && i->first == channelName);
} else {
do {
} while (++i != fManualDetachChannels.end() && i->first == channelName);
}
}
int cleanCount = fCleanupChannels.size();
hsAssert(cleanCount, "No time controls when deleting animation");
for (int j = 0; j < cleanCount; j++)
{
delete fCleanupChannels[j];
}
fCleanupChannels.clear();
#ifdef SHOW_AG_CHANGES
hsStatusMessageF("\nFinished DETACHING anim <%s>", GetName().c_str());
fMaster->DumpAniGraph("bone_pelvis", false, hsTimer::GetSysSeconds());
#endif
}
// SetBlend ---------------------------------------
// ---------
float plAGAnimInstance::SetBlend(float blend)
{
float oldBlend = fBlend.Value(0.0, true);
if(oldBlend != blend &&
(oldBlend == 0.0f ||
blend == 0.0f ||
oldBlend == 1.0f ||
blend == 1.0f))
{
fMaster->SetNeedCompile(true);
}
fBlend.Set(blend);
return blend;
}
// GetBlend -------------------------
// ---------
float plAGAnimInstance::GetBlend()
{
return fBlend.Value(0);
}
// SetAmplitude -------------------------------------
// -------------
float plAGAnimInstance::SetAmplitude(float amp)
{
if(fAmplitude.Get() != -1.0f)
{
fAmplitude.Set(amp);
}
return amp;
}
// GetAmplitude -------------------------
// -------------
float plAGAnimInstance::GetAmplitude()
{
return fAmplitude.Value(0);
}
// GetName -----------------------------
// --------
plString plAGAnimInstance::GetName()
{
if(fAnimation)
return fAnimation->GetName();
else
return plString::Null;
}
// SetLoop ----------------------------------
// --------
void plAGAnimInstance::SetLoop(bool status)
{
if (fTimeConvert)
fTimeConvert->Loop(status);
}
// HandleCmd ----------------------------------------
// ----------
bool plAGAnimInstance::HandleCmd(plAnimCmdMsg *msg)
{
if (fTimeConvert)
return fTimeConvert->HandleCmd(msg);
return false;
}
// IsFinished -----------------------
// -----------
bool plAGAnimInstance::IsFinished()
{
if (fTimeConvert)
return fTimeConvert->IsStopped();
return false;
}
// IsAtEnd -----------------------
// --------
bool plAGAnimInstance::IsAtEnd()
{
if(fTimeConvert)
{
return fTimeConvert->CurrentAnimTime() == fTimeConvert->GetEnd();
}
else
return false;
}
// Start -----------------------------------
// ------
void plAGAnimInstance::Start(double timeNow)
{
if (fTimeConvert)
{
if (timeNow < 0)
fTimeConvert->Start();
else
fTimeConvert->Start(timeNow);
}
}
// Stop ---------------------
// -----
void plAGAnimInstance::Stop()
{
if (fTimeConvert)
fTimeConvert->Stop();
}
// AttachCallbacks --------------------------------------------------
// ----------------
void plAGAnimInstance::AttachCallbacks(plOneShotCallbacks *callbacks)
{
const plATCAnim *anim = plATCAnim::ConvertNoRef(fAnimation);
if (callbacks && anim)
{
plAnimCmdMsg animMsg;
animMsg.SetCmd(plAnimCmdMsg::kAddCallbacks);
for (int i = 0; i < callbacks->GetNumCallbacks(); i++)
{
plOneShotCallbacks::plOneShotCallback& cb = callbacks->GetCallback(i);
plEventCallbackMsg *eventMsg = new plEventCallbackMsg;
eventMsg->AddReceiver(cb.fReceiver);
eventMsg->fRepeats = 0;
eventMsg->fUser = cb.fUser;
if (!cb.fMarker.IsNull())
{
float marker = anim->GetMarker(cb.fMarker);
hsAssert(marker != -1, "Bad marker name");
eventMsg->fEventTime = marker;
eventMsg->fEvent = kTime;
}
else
{
eventMsg->fEvent = kStop;
}
animMsg.AddCallback(eventMsg);
hsRefCnt_SafeUnRef(eventMsg);
}
fTimeConvert->HandleCmd(&animMsg);
}
}
// ProcessFade -------------------------------------
// ------------
void plAGAnimInstance::ProcessFade(float elapsed)
{
if (fFadeBlend) {
float newBlend = ICalcFade(fFadeBlend, GetBlend(), fFadeBlendGoal, fFadeBlendRate, elapsed);
SetBlend(newBlend);
if(fFadeDetach && (newBlend == fFadeBlendGoal) && (fFadeBlendGoal == 0.0f) )
{
fMaster->DetachAnimation(this);
return;
}
}
if (fFadeAmp && fAmplitude.Get() != -1.0f) {
float curAmp = GetAmplitude();
float newAmp = ICalcFade(fFadeAmp, curAmp, fFadeAmpGoal, fFadeAmpRate, elapsed);
SetAmplitude(newAmp);
}
}
// ICalcFade ---------------------------------------------------------------------
// ----------
float plAGAnimInstance::ICalcFade(bool &fade, float curVal, float goal,
float rate, float elapsed)
{
float newVal;
float curStep = rate * elapsed;
if(rate > 0) {
newVal = std::min(goal, curVal + curStep);
} else {
newVal = std::max(goal, curVal + curStep);
}
if(newVal == goal)
{
fade = false;
fMaster->DirtySynchState(kSDLAGMaster, 0); // send SDL state update to server
}
return newVal;
}
// FadeAndDetach -------------------------------------------------
// --------------
void plAGAnimInstance::FadeAndDetach(float goal, float rate)
{
ISetupFade(goal, rate, true, kFadeBlend);
}
// Fade --------------------------------------------------------------------------------
// -----
void plAGAnimInstance::Fade(float goal, float rate, uint8_t type /* = kFadeBlend */)
{
ISetupFade(goal, rate, false, type);
}
// ISetupFade --------------------------------------------------------------------------
// -----------
void plAGAnimInstance::ISetupFade(float goal, float rate, bool detach, uint8_t type)
{
if (rate == 0)
{
if (type == kFadeBlend)
{
SetBlend(goal);
fFadeBlend = false;
if(detach) {
fMaster->DetachAnimation(this);
}
}
else if (type == kFadeAmp)
{
SetAmplitude(goal);
fFadeAmp = false;
}
return;
}
rate = (rate > 0 ? rate : -rate); // For old code that sends negative values
float curVal = 0;
switch (type)
{
case kFadeBlend:
curVal = GetBlend();
break;
case kFadeAmp:
curVal = GetAmplitude();
break;
}
if (curVal > goal)
rate = -rate;
switch (type)
{
case kFadeBlend:
fFadeBlend = true;
fFadeBlendGoal = goal;
fFadeBlendRate = rate;
fFadeDetach = detach;
break;
case kFadeAmp:
fFadeAmp = true;
fFadeAmpGoal = goal;
fFadeAmpRate = rate;
fFadeDetach = false; // only detach on blend fades, for the moment.
break;
}
}
class agAlloc
{
public:
agAlloc(plAGChannel *object, const char *chanName, const char *animName, uint16_t classIndex)
: fObject(object),
fClassIndex(classIndex)
{
fChannelName = hsStrcpy(chanName);
fAnimName = hsStrcpy(animName);
}
~agAlloc()
{
delete[] fChannelName;
delete[] fAnimName;
}
plAGChannel *fObject;
char *fChannelName;
char *fAnimName;
uint16_t fClassIndex;
};
typedef std::map<plAGChannel *, agAlloc *> agAllocMap;
static agAllocMap gAGAllocs;
void RegisterAGAlloc(plAGChannel *object, const char *chanName, const char *animName, uint16_t classIndex)
{
gAGAllocs[object] = new agAlloc(object, chanName, animName, classIndex);
}
void DumpAGAllocs()
{
agAllocMap::iterator i = gAGAllocs.begin();
agAllocMap::iterator theEnd = gAGAllocs.end();
hsStatusMessage("DUMPING AG ALLOCATIONS ================================================");
for ( ; i != theEnd; i++)
{
agAlloc * al = (*i).second;
uint16_t realClassIndex = al->fObject->ClassIndex();
hsStatusMessageF("agAlloc: an: %s ch: %s, cl: %s", al->fAnimName, al->fChannelName, plFactory::GetNameOfClass(realClassIndex));
}
// it's not fast but it's safe and simple..
i = gAGAllocs.begin();
while(i != gAGAllocs.end())
{
agAlloc * al = (*i).second;
delete al;
gAGAllocs.erase(i++);
}
hsStatusMessage("FINISHED DUMPING AG ALLOCATIONS *********************************************");
}
void UnRegisterAGAlloc(plAGChannel *object)
{
agAllocMap::iterator i = gAGAllocs.find(object);
if(i != gAGAllocs.end())
{
agAlloc * al = (*i).second;
gAGAllocs.erase(i);
delete al;
}
}

View File

@ -0,0 +1,274 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
/** \file plAGAnimInstance.h
\brief The animation class for the AniGraph animation system
\ingroup Avatar
\ingroup AniGraph
*/
#ifndef PLAGANIMINSTANCE_INC
#define PLAGANIMINSTANCE_INC
// disable warning C4503: dcorated name length exceeded, name was truncated
// disable warning C4786: symbol greater than 255 characters,
#pragma warning(disable: 4503 4786)
#include "HeadSpin.h"
#include <map>
// local
#include "plScalarChannel.h"
// other
#include "plInterp/plAnimTimeConvert.h"
// declarations
class plAGChannel;
class plAGAnim;
class plAGMasterMod;
class plAGChannelApplicator;
class plOneShotCallbacks;
/////////////////
// PLAGANIMINSTANCE
/////////////////
/** \class plAGAnimInstance
Whenever we attach an animation to a scene object hierarchy, we
create an activation record -- a plAGAnimInstance -- that remembers
all the ephemeral state associated with animation
Since animations have many channels and may involve blend operations,
one of the primary responsibilities of this class is to keep track of
all the animation node graphs that were created by the invocation of
this animation.
*/
class plAGAnimInstance {
public:
/** Used for the fade commands to select what to fade. */
enum
{
kFadeBlend, /// Fade the blend strength
kFadeAmp, /// Fade the amplitude
} FadeType;
/** Default constructor. */
plAGAnimInstance();
/** Construct from an animation and a master modifier.
This attaches the animation channels to the channels of
the master modifier and creates all the bookkeeping structures
necessary to undo it later. */
plAGAnimInstance(plAGAnim * anim, plAGMasterMod * master, float blend, uint16_t blendPriority, bool cache, bool useAmplitude);
/** Destructor. Removes the animation from the scene objects it's attached to. */
virtual ~plAGAnimInstance();
/** Returns the animation that this instance mediates. */
const plAGAnim * GetAnimation() { return fAnimation; };
/** Returns the timeconvert object that controls the progress of time
in this animation. */
plAnimTimeConvert *GetTimeConvert() { return fTimeConvert; }
/** Set the speed of the animation. This is expressed as a fraction of
the speed with which the animation was defined. */
void SetSpeed(float speed) { if (fTimeConvert) fTimeConvert->SetSpeed(speed); };
// \{
/**
The current blend factor of the animation. This indicates the
priority of this animation as opposed to other animations which
were attached before it. Conceptually it may help to think of this
as a layer in an stack of animations, where the blend value is the
'opacity' of this animation relative to the ones below it.
1.0 represents full strength.
You may use values higher than 1.0, but this has not
yet been seen to have any practical utility whatsoever. Note that
even if an animation has a blend strength of 1.0, it may have another
animation on top/downstream from it that is masking it completely. */
float SetBlend(float blend);
float GetBlend();
// \}
/** Set the strength of the animation with respect to its 0th frame.
This can be used to dampen the motion of the animation.
Animations must be designed to use this: frame 0 of the animation
must be a reasonable "default pose" as it will be blended with the
current frame of the animation to produce the result. */
float SetAmplitude(float amp);
/** Get the current strength of the animation. */
float GetAmplitude();
/** Make this animation loop (or not.) Note that the instance can loop
or not without regard to whether the plAGAnim it is based on loops. */
void SetLoop(bool status);
/** Interpret and respond to an animation command message. /sa plAnimCmdMsg */
bool HandleCmd(plAnimCmdMsg *msg);
/** Start playback of the animation. You may optionally provide the a world
time, which is needed for synchronizing the animation's timeline
with the global timeline. If timeNow is -1 (the default,) the system
time will be polled */
void Start(double worldTimeNow = -1);
/** Stop playback of the animation. */
void Stop();
/** Move the playback head of the animation to a specific time.
Note that this time is in animation local time, not global time.
The "jump" parameter specifies whether or not to fire callbacks
that occur between the current time and the target time. */
void SetCurrentTime(float newLocalTime, bool jump = false);
/** Move the playback head by the specified relative amount within
the animation. This may cause looping. If the beginning or end
of the animation is reached an looping is not on, the movement
will pin.
\param jump if true, don't look for callbacks between old time and TRACKED_NEW */
void SeekRelative(float delta, bool jump);
/** Gradually fade the blend strength or amplitude of the animation.
\param goal is the desired blend strength
\param rate is in blend units per second
\type is either kFadeBlend or kFadeAmp */
void Fade(float goal, float rate, uint8_t type = kFadeBlend);
/** Fade the animation and detach it after the fade is complete.
Extremely useful for situations where the controlling logic
is terminating immediately but you want the animation to fade
out gradually.
\deprecated
*/
void FadeAndDetach(float goal, float rate);
/** Has the animation terminated of natural causes?
Primarily used to see if an animation has played all the
way to the end, but will also return true if the animation
was stopped with a stop command */
bool IsFinished();
/** Is the animation playback head positioned at the end. */
bool IsAtEnd();
/** Get the name of the underlying animation. */
plString GetName();
/** Remove all channels from the master mode and remove us from
our master modifier.
Destructs the object! */
void Detach();
/** Remove all the instance's channels from the modifiers they're attached to.
Typically called by the master mod prior to destructing the instance. */
void DetachChannels();
/** Prune any unused branches out of the animation graph; add any
newly active branches back in. */
void Optimize();
/** Convert the given world time to local animation time.
May include the effects of looping or wraparound.
If the local time passes the end of the animation, the returned
time will be pinned appropriately. */
double WorldToAnimTime(double foo) { return (fTimeConvert ? fTimeConvert->WorldToAnimTimeNoUpdate(foo) : 0); };
/** Attach a sequence of callback messages to the animation instance.
Messages are each associated with a specific (local) time
in the animation and will be sent when playback passes that time. */
void AttachCallbacks(plOneShotCallbacks *callbacks);
void ProcessFade(float elapsed); // process any outstanding fades
void SearchForGlobals(); // Util function to setup SDL channels
protected:
/** Set up bookkeeping for a fade. */
void ISetupFade(float goal, float rate, bool detach, uint8_t type);
void IRegisterDetach(const plString &channelName, plAGChannel *channel);
void IInitAnimTimeConvert(plAnimTimeConvert* atc, plATCAnim* anim, plAGMasterMod* master);
const plAGAnim * fAnimation;
plAGMasterMod * fMaster;
std::map<plString, plAGChannelApplicator *, plString::less_i> fChannels;
typedef std::multimap<plString, plAGChannel *> plDetachMap;
plDetachMap fManualDetachChannels;
std::vector<plAGChannel*> fCleanupChannels;
std::vector<plScalarSDLChannel*> fSDLChannels;
plScalarConstant fBlend; // blend factor vs. previous animations
plScalarConstant fAmplitude; // for animation scaling
// Each activation gets its own timeline.
plAnimTimeConvert *fTimeConvert;
bool fFadeBlend; /// we are fading the blend
float fFadeBlendGoal; /// what blend level we're trying to reach
float fFadeBlendRate; /// how fast are we fading in blend units per second (1 blend unit = full)
bool fFadeDetach; /// detach after fade is finished? (only used for blend fades)
bool fFadeAmp; /// we are fading the amplitude
float fFadeAmpGoal; /// amplitude we're trying to reach
float fFadeAmpRate; /// how faster we're fading in blend units per second
float ICalcFade(bool &fade, float curVal, float goal, float rate, float elapsed);
};
//#ifdef _DEBUG
//#define TRACK_AG_ALLOCS // for now, automatically track AG allocations in debug
//#endif
#ifdef TRACK_AG_ALLOCS
extern plString gGlobalAnimName;
extern plString gGlobalChannelName;
void RegisterAGAlloc(plAGChannel *object, const char *chanName, const char *animName, uint16_t classIndex);
void UnRegisterAGAlloc(plAGChannel *object);
void DumpAGAllocs();
#endif // TRACK_AG_ALLOCS
#endif // PLAGANIMINSTANCE_INC

View File

@ -0,0 +1,178 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
#include "plAGApplicator.h"
#include "plAGModifier.h"
#include "hsResMgr.h"
// ctor --------
// -----
plAGApplicator::plAGApplicator()
: fChannel(nil),
fEnabled(true)
{
};
// ctor -------------------------------
// -----
plAGApplicator::plAGApplicator(const plString &channelName)
: fChannel(nil),
fEnabled(true),
fChannelName(channelName)
{
};
plAGApplicator::~plAGApplicator()
{
}
void plAGApplicator::Apply(const plAGModifier *mod, double time, bool force)
{
if (fEnabled || force)
IApply(mod, time);
}
void plAGApplicator::SetChannelName(const plString &name)
{
if(!name.IsNull())
fChannelName = name;
};
plString plAGApplicator::GetChannelName()
{
return fChannelName;
};
plAGChannel *plAGApplicator::MergeChannel(plAGApplicator *app, plAGChannel *channel,
plScalarChannel *blend, int blendPriority)
{
plAGChannel *result = nil;
if(fChannel)
{
if (CanCombine(app))
result = fChannel->MakeCombine(channel);
else if (CanBlend(app))
result = fChannel->MakeBlend(channel, blend, blendPriority);
} else {
result = channel;
}
if (result && result != fChannel)
SetChannel(result);
return result;
}
plAGApplicator *plAGApplicator::CloneWithChannel(plAGChannel *channel)
{
plAGApplicator *app = plAGApplicator::ConvertNoRef(plFactory::Create(ClassIndex()));
app->SetChannel(channel);
app->Enable(fEnabled);
app->SetChannelName(fChannelName);
return app;
}
bool plAGApplicator::CanBlend(plAGApplicator *app)
{
uint16_t ourClass = ClassIndex();
uint16_t theirClass = app->ClassIndex();
return(ourClass == theirClass);
// return(this->HasBaseClass(theirClass)
// || app->HasBaseClass(ourClass));
}
void plAGApplicator::Write(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Write(stream, mgr);
stream->WriteBool(fEnabled);
stream->WriteSafeString(fChannelName);
}
void plAGApplicator::Read(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Read(stream, mgr);
fEnabled = stream->ReadBool();
fChannel = nil; // Whatever is reading this applicator in should know what channel to assign it
fChannelName = stream->ReadSafeString();
}
// IGETxI
// Gain access to of our modifier's target's interfaces.
// This is technically in violation of the principle that only modifiers can get non-const
// reference to their target's interfaces,
// BUT since the plAGApplicator architecture is wholly "owned" by the AGModifier, this
// seemed the most graceful way to do it without const_cast or modifying plModifier or plSceneObject
// IGETAI
plAudioInterface * plAGApplicator::IGetAI(const plAGModifier *modifier) const
{
return modifier->LeakAI();
}
// IGETCI
plCoordinateInterface * plAGApplicator::IGetCI(const plAGModifier* modifier) const
{
return modifier->LeakCI();
}
// IGETDI
plDrawInterface * plAGApplicator::IGetDI(const plAGModifier * modifier) const
{
return modifier->LeakDI();
}
// IGETSI
plSimulationInterface * plAGApplicator::IGetSI(const plAGModifier * modifier) const
{
return modifier->LeakSI();
}
plObjInterface * plAGApplicator::IGetGI(const plAGModifier * modifier, uint16_t classIdx) const
{
return modifier->LeakGI(classIdx);
}

View File

@ -0,0 +1,174 @@
/*==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 <http://www.gnu.org/licenses/>.
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 plAGApplicator_h
#define plAGApplicator_h
/////////////////////////////////////////////////////////////////////////////////////////
//
// FORWARDS
//
/////////////////////////////////////////////////////////////////////////////////////////
class plAudioInterface;
class plCoordinateInterface;
class plDrawInterface;
class plSimulationInterface;
class plObjInterface;
class plAGModifier;
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
#include "pnFactory/plCreatable.h"
#include "plAGDefs.h"
#include "plString.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
class plAGChannel;
class plScalarChannel;
/////////////////////////////////////////////////////////////////////////////////////////
//
// DEFINITIONS
//
/////////////////////////////////////////////////////////////////////////////////////////
/** \class plAGApplicator
Takes the end of a channel tree and applies it to a scene object.
A transform applicator takes a matrix-typed-tree and applies it to
the transform of the scene object.
Other applicators might take floats and use them to animate alpha channels,
etc. */
class plAGApplicator : public plCreatable
{
public:
// -- methods --
/** Base constructor. */
plAGApplicator();
plAGApplicator(const plString &channelName);
virtual ~plAGApplicator();
/** Return our single input channel. Applicators only ever
have a single input channel; you can always use blend
or combine nodes to merge channels before routing
them into the applicator. */
plAGChannel *GetChannel() { return fChannel; }
/** Set our input channel. Does not free the previous input channel. */
void SetChannel(plAGChannel *channel) { fChannel = channel; }
void SetChannelName(const plString &name);
plString GetChannelName();
/** Optionally suppress the action of this applicator.
The applicator can still be forced to apply using the force
paramater of the Apply function. */
void Enable(bool on) { fEnabled = on; }
/** Make a shallow copy of the applicator. Keep the same input channel
but do not clone the input channel. */
virtual plAGApplicator *CloneWithChannel(plAGChannel *channel);
/** What animation type do we have? Used to determine whether two
applicators are trying to animate the same thing or whether they
can coexist peacefully. */
virtual plAGPinType GetPinType() { return kAGPinUnknown; }
// Join the incoming channel (if possible) to ours
/** Determine whether the given applicator can be blended to ours. If so, this
would be effected by pulling the input channel from the other applicator,
blending it with our input channel via a new blend node, attaching that blend
node as our new input, and throwing the other applicator away. */
virtual bool CanBlend(plAGApplicator *app);
/** Combine the two applicators if possible. \sa CanBlend */
virtual plAGChannel * MergeChannel(plAGApplicator *app, plAGChannel *channel,
plScalarChannel *blend, int blendPriority);
/** \bug It makes no sense for an applicator to combine because combination always
results in a different data type, which would require a different applicator. */
virtual bool CanCombine(plAGApplicator *app) { return false; }
/** Apply our channel's data to the scene object, via the modifier.
This is the only function that actually changes perceivable scene state. */
void Apply(const plAGModifier *mod, double time, bool force = false); // Apply our channel's data to the modifier
// this is pretty much a HACK to support applicators that want to stick around when
// their channel is gone so they can operate on the next channel that comes in
// the RIGHT way to do this is to make applicators support the Detach() protocol just
// like channels...
virtual bool AutoDelete() { return true; } // should we remove it when its input channel is gone?
// PlOP
CLASSNAME_REGISTER( plAGApplicator );
GETINTERFACE_ANY( plAGApplicator, plCreatable );
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream *s, hsResMgr *mgr);
protected:
// -- methods --
virtual void IApply(const plAGModifier *mod, double time) = 0;
// give derived classes access to the object interfaces
plAudioInterface * IGetAI(const plAGModifier *modifier) const;
plCoordinateInterface * IGetCI(const plAGModifier *modifier) const;
plDrawInterface * IGetDI(const plAGModifier *modifier) const;
plSimulationInterface * IGetSI(const plAGModifier *modifier) const;
plObjInterface * IGetGI(const plAGModifier *modifier, uint16_t classIdx) const;
// Allow plAGModifier to declare IGet?? as friends
friend class plAGModifier;
// -- members --
plAGChannel *fChannel;
bool fEnabled;
plString fChannelName;
};
#endif

View File

@ -0,0 +1,126 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
// singular
#include "plAGChannel.h"
// local
#include "plAGModifier.h"
// global
#include "HeadSpin.h"
#include "hsResMgr.h"
#include "plAGAnimInstance.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// plAGChannel
//
/////////////////////////////////////////////////////////////////////////////////////////
plAGChannel::plAGChannel()
{
#ifdef TRACK_AG_ALLOCS
fName = gGlobalAnimName;
RegisterAGAlloc(this, gGlobalChannelName.c_str(), gGlobalAnimName.c_str(), this->ClassIndex());
#endif // TRACK_AG_ALLOCS
}
// DTOR
plAGChannel::~plAGChannel()
{
// we do not own the "fName" string, so don't delete it!
#ifdef TRACK_AG_ALLOCS
UnRegisterAGAlloc(this);
#endif // TRACK_AG_ALLOCS
}
// MAKECOMBINE
plAGChannel * plAGChannel::MakeCombine(plAGChannel *channelA)
{
return nil;
}
// MAKEBLEND
plAGChannel * plAGChannel::MakeBlend(plAGChannel *channelA, plScalarChannel *blend, int blendPriority)
{
return nil;
}
// DETACH
// If the channel being detached is us, let our caller know to replace us
// by return NIL.
plAGChannel * plAGChannel::Detach(plAGChannel *channel)
{
if (this == channel)
{
return nil;
} else {
return this;
}
}
// OPTIMIZE
plAGChannel * plAGChannel::Optimize(double time)
{
// the basic channel can't optimize...
return this;
}
// WRITE
void plAGChannel::Write(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Write(stream, mgr);
stream->WriteSafeString(fName);
}
// READ
void plAGChannel::Read(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Read(stream, mgr);
fName = stream->ReadSafeString();
}
////////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,199 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
/** \file plAGChannel.h
\brief The animation class for the AniGraph animation system
WHAT'S AG STAND FOR?
AG stands for animation graph. It's a directed acyclic graph of animation data sources and operations.
WHAT'S A CHANNEL?
A channel is a stream of animation data.
A channel is a node in an animation graph.
Animation data can come from anything; keyframes, IK, physics, etc.
Channels are often grouped together using blending nodes.
Blending nodes are also a type of channel.
See where I'm going with this?
HOW IS THIS DIFFERENT FROM PLCONTROLLER?
It's very similar, but it's designed to be extremely lightweight; animation graphs are set up and torn down
quickly and with great frequency. They are not persistent, and their only state (besides cache)
is their interconnectedness.
HOW DO THEY GET SAVED?
Their client must recreate them at read/load time. Since they are stateless, all information necessary
to recreate them is necessarily held by the client.
ARE THEY REFERENCE COUNTED?
No. The entire graph is owned by the creator.
Deleting the top node of a graph deletes the entire graph.
If we decide we want to share subgraphs, we'll add reference counting -- easy since cycles are illegal
HOW DO THEY INTEGRATE WITH PLCONTROLLERS?
Once we decide the animation graph approach is workable and effective, we'll combine the plController
concept with the plAGChannel concept.
Until then, there will be a handful of channel types that adapt controllers into animation graphs.
WHAT DOES "COMBINE" MEAN?
Merging more than one types of animation data into a single output that preserves the value
of both.
WHAT DOES "BLEND" MEAN?
Merging two similar types of animation data using a weight to "fade" from one to the other
WHAT IS ZEROSTATE?
This is a static channel that always returns the value at time zero. It is used as a reference for animation
scaling (amplitude).
WHAT'S AN APPLICATOR?
In order to cut down on the number of derived channel classes for different Value() and Apply() behaviors,
applicators were created. At the end of each graph is an applicator, which specifies what should be done
with the result of a call to Value().
\ingroup Avatar
\ingroup AniGraph
*/
#ifndef PLAGCHANNEL_H
#define PLAGCHANNEL_H
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
#include "pnFactory/plCreatable.h"
#include "plString.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// FORWARDS
//
/////////////////////////////////////////////////////////////////////////////////////////
class plAGModifier;
class plAnimTimeConvert;
class plAGChannel;
class plScalarChannel;
/////////////////////////////////////////////////////////////////////////////////////////
//
// DEFINITIONS
//
/////////////////////////////////////////////////////////////////////////////////////////
/** \class plAGChannel
An object that emits data of a specific type. Fundamental building
block of the animation graph. */
class plAGChannel : public plCreatable
{
public:
// -- methods --
/** Default constructor for the base class. */
plAGChannel();
/** Free an allocated name. Does not release any upstream nodes. */
virtual ~plAGChannel();
// AG PROTOCOL
/** Combine the given channel with this channel, allocating and returning
a new node which does the combination. It's up to the caller to
manage the lifetime of the new node. */
virtual plAGChannel * MakeCombine(plAGChannel * channelB);
/** Blend the given channel with this channel, using a third channel (which
must output a float/scalar value) to crossfade between the two.
As the blendChannel varies, the blend will vary. */
virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * blendChannel, int blendPriority);
/** Create a "static clone" of this channel which always returns this channel's
value at time zero. */
virtual plAGChannel * MakeZeroState() = 0;
/** If we're potentially sharing this channel with other plAGMasterMods, we'll
want to insert a channel in the graph for cache info. This function returns
either the cache channel (replacing us) or ourself. */
virtual plAGChannel * MakeCacheChannel(plAnimTimeConvert *atc) { return this; }
/** Create a new channel which converts global time to local time
and attach it downstream from this channel. This allows you to
convert an animation from one timespace to another - critical for
blending.
local-time-animation <-- timescale <-- world-time-animation */
virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource) = 0;
/** Is the animation moving at the given world time? Takes into account
start/stop messages that haven't been applied yet, ease curves, etc. */
virtual bool IsStoppedAt(double wSecs) { return true; }
/** Detach the given channel from our graph. If this is the channel in
question, returns any upstream channels so they can be reattached.
If this is not the channel in question, passes the request upstream
and does any reattachment necessary. */
virtual plAGChannel * Detach(plAGChannel * channel);
/** Return the optimized version of this channel. May be a completely
different channel; will collapse out inactive subgraphs. */
virtual plAGChannel * Optimize(double time);
// \{
/** The name of the channel is used to dynamically attach to sub-parts of an
object. */
virtual plString GetName() { return fName; };
virtual void SetName(const plString & name) { fName = name; };
// \}
// PLASMA PROTOCOL
// rtti
CLASSNAME_REGISTER( plAGChannel );
GETINTERFACE_ANY( plAGChannel, plCreatable );
// persistence
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream *s, hsResMgr *mgr);
protected:
plString fName;
};
#endif //PLAGCHANNEL_H

View File

@ -0,0 +1,63 @@
/*==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 <http://www.gnu.org/licenses/>.
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 plAGDefs_h
#define plAGDefs_h
/** \enum PinType
Tells us what category of animation this channel affects.
Primarly used to determine if two channels are competing,
which you can't tell strictly from the type of data the
channel handles. */
enum plAGPinType
{
kAGPinUnknown, // this applicator hasn't decided its pin type
kAGPinTransform, // this applicator munches the entire transform
kNumPinTypes
};
#define kAGMaxBlendPriority 0x0fffffff
#define kAGMinBlendPriority 0x00000000
#define kAGMedBlendPriority 0x0000ffff
#endif

View File

@ -0,0 +1,823 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
// singular
#include "plAGMasterMod.h"
// local
#include "plAGAnim.h"
#include "plAGAnimInstance.h"
#include "plAGModifier.h"
// #include "plAvatarAnim.h"
#include "plModifier/plAGMasterSDLModifier.h"
#include "plMatrixChannel.h"
// global
#include "hsResMgr.h"
#include "plgDispatch.h"
// other
#include "plInterp/plAnimEaseTypes.h"
#include "plMessage/plAnimCmdMsg.h"
#include "pnMessage/plSDLModifierMsg.h"
#include "pnMessage/plSDLNotificationMsg.h"
#include "pnMessage/plTimeMsg.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plCoordinateInterface.h"
////////////////
// PLAGMASTERMOD
////////////////
// Coordinates the activities of a bunch of plAGModifiers
// std::map<char *, plAGMasterMod *, stringISorter> plAGMasterMod::fInstances;
// CTOR
plAGMasterMod::plAGMasterMod()
: fTarget(nil),
fNeedEval(false),
fFirstEval(true),
fAGMasterSDLMod(nil),
fNeedCompile(false),
fIsGrouped(false),
fIsGroupMaster(false),
fMsgForwarder(nil)
{
}
// DTOR
plAGMasterMod::~plAGMasterMod()
{
}
void plAGMasterMod::Write(hsStream *stream, hsResMgr *mgr)
{
plModifier::Write(stream, mgr);
int length = 0;
stream->WriteLE32(length); // backwards compatability. Nuke on next format change.
stream->WriteLE32(fPrivateAnims.size());
int i;
for (i = 0; i < fPrivateAnims.size(); i++)
{
mgr->WriteKey(stream, fPrivateAnims[i]->GetKey());
}
stream->WriteBool(fIsGrouped);
stream->WriteBool(fIsGroupMaster);
if (fIsGroupMaster)
mgr->WriteKey(stream, fMsgForwarder->GetKey());
// maybe later... WriteCachedMessages(stream, mgr);
}
void plAGMasterMod::Read(hsStream * stream, hsResMgr *mgr)
{
plModifier::Read(stream, mgr);
//////////////////////////////////////////
int nameLength = stream->ReadLE32(); // Unused. Nuke next format change.
char *junk = new char[nameLength+1]; //
stream->Read(nameLength, junk); //
junk[nameLength] = 0; //
delete [] junk; //
//////////////////////////////////////////
int numPrivateAnims = stream->ReadLE32();
fPrivateAnims.reserve(numPrivateAnims); // pre-allocate for performance
int i;
for (i = 0; i < numPrivateAnims; i++)
{
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPrivateAnim);
mgr->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef);
}
fIsGrouped = stream->ReadBool();
fIsGroupMaster = stream->ReadBool();
if (fIsGroupMaster)
{
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0);
mgr->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef);
}
// maybe later... ReadCachedMessages(stream, mgr);
}
// ADDTARGET
// Collect all the plAGModifiers from our children and attach private anims.
void plAGMasterMod::AddTarget(plSceneObject * object)
{
plSynchEnabler p(false); // turn off dirty tracking while in this function
fTarget = object;
int autoIdx = -1;
int initialIdx = -1;
int timeIdx = 0;
int i;
for (i = 0; i < fPrivateAnims.size(); i++)
{
plATCAnim *atcAnim = plATCAnim::ConvertNoRef(fPrivateAnims[i]);
if (!atcAnim)
continue;
if (atcAnim->GetAutoStart())
autoIdx = i;
if (atcAnim->GetInitial() != -1)
initialIdx = i;
if (atcAnim->GetStart() < fPrivateAnims[timeIdx]->GetStart())
timeIdx = i;
}
int masterIdx;
if (autoIdx != -1)
masterIdx = autoIdx; // If something autostarts, it wins.
else if (initialIdx != -1)
masterIdx = initialIdx; // Otherwise, the fellow with the @initial point wins
else
masterIdx = timeIdx; // Default case: the earliest anim wins
for (i = 0; i < fPrivateAnims.size(); i++)
{
AttachAnimationBlended(fPrivateAnims[i], i == masterIdx ? 1.f : 0.f);
}
// Force one eval after we init
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
if (!fIsGrouped || fIsGroupMaster)
{
// add sdl modifier
delete fAGMasterSDLMod;
fAGMasterSDLMod = new plAGMasterSDLModifier;
object->AddModifier(fAGMasterSDLMod);
}
}
void plAGMasterMod::RemoveTarget(plSceneObject* o)
{
hsAssert(o == fTarget, "Removing target I don't have");
DetachAllAnimations();
// remove sdl modifier
if (o)
{
if (fAGMasterSDLMod)
o->RemoveModifier(fAGMasterSDLMod);
}
delete fAGMasterSDLMod;
fAGMasterSDLMod=nil;
fTarget = nil;
}
#include "plProfile.h"
plProfile_CreateTimer("ApplyAnimation", "Animation", ApplyAnimation);
plProfile_CreateTimer(" AffineValue", "Animation", AffineValue);
plProfile_CreateTimer(" AffineInterp", "Animation", AffineInterp);
plProfile_CreateTimer(" AffineBlend", "Animation", AffineBlend);
plProfile_CreateTimer(" AffineCompose", "Animation", AffineCompose);
plProfile_CreateTimer(" AffineApplicator", "Animation", MatrixApplicator);
plProfile_CreateTimer("AnimatingPhysicals", "Animation", AnimatingPhysicals);
plProfile_CreateTimer("StoppedAnimPhysicals", "Animation", StoppedAnimPhysicals);
// IEVAL
bool plAGMasterMod::IEval(double secs, float del, uint32_t dirty)
{
if (fFirstEval)
{
int i;
for (i = 0; i < fAnimInstances.size(); i++)
fAnimInstances[i]->SearchForGlobals();
fFirstEval = false;
}
ApplyAnimations(secs, del);
// We might get registered for just a single eval. If we don't need to eval anymore, unregister
if (!fNeedEval)
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
return true;
}
// APPLYANIMATIONS
void plAGMasterMod::ApplyAnimations(double time, float elapsed)
{
plProfile_BeginLap(ApplyAnimation, this->GetKey()->GetUoid().GetObjectName().c_str());
// update any fades
for (int i = 0; i < fAnimInstances.size(); i++)
{
fAnimInstances[i]->ProcessFade(elapsed);
}
AdvanceAnimsToTime(time);
plProfile_EndLap(ApplyAnimation,this->GetKey()->GetUoid().GetObjectName().c_str());
}
void plAGMasterMod::AdvanceAnimsToTime(double time)
{
if(fNeedCompile)
Compile(time);
for(plChannelModMap::iterator j = fChannelMods.begin(); j != fChannelMods.end(); j++)
{
plAGModifier *mod = (*j).second;
mod->Apply(time);
}
}
void plAGMasterMod::SetNeedCompile(bool needCompile)
{
fNeedCompile = true;
}
void plAGMasterMod::Compile(double time)
{
plChannelModMap::iterator end = fChannelMods.end();
fNeedCompile = false;
for(plChannelModMap::iterator j = fChannelMods.begin(); j != end; j++)
{
plAGModifier *mod = (*j).second;
plAGApplicator *app = mod->GetApplicator(kAGPinTransform);
if(app) {
plAGChannel *channel = app->GetChannel();
if(channel)
{
plMatrixChannel *topChannel = plMatrixChannel::ConvertNoRef(channel);
if(topChannel)
topChannel->Optimize(time);
}
}
}
}
void plAGMasterMod::DumpAniGraph(const char *justThisChannel, bool optimized, double time)
{
plChannelModMap::iterator end = fChannelMods.end();
fNeedCompile = false;
for(plChannelModMap::iterator j = fChannelMods.begin(); j != end; j++)
{
plAGModifier *mod = (*j).second;
if(!justThisChannel || mod->GetChannelName().Compare(justThisChannel, plString::kCaseInsensitive) == 0)
{
plAGApplicator *app = mod->GetApplicator(kAGPinTransform);
if(app) {
plAGChannel *channel = app->GetChannel();
if(channel)
{
plMatrixChannel *topChannel = plMatrixChannel::ConvertNoRef(channel);
if(topChannel)
{
hsStatusMessageF("AGModifier: <%s>", mod->GetChannelName().c_str());
topChannel->Dump(1, optimized, time);
}
}
}
if(justThisChannel)
break;
}
}
}
// GETCHANNELMOD(name)
// Get the modifier that controls the channel with the given name
plAGModifier * plAGMasterMod::GetChannelMod(const plString & name, bool dontCache ) const
{
plAGModifier * result = nil;
std::map<plString, plAGModifier *>::const_iterator i = fChannelMods.find(name);
if (i != fChannelMods.end()) {
result = (*i).second;
} else {
plSceneObject *SO = GetTarget(0);
if(SO) {
result = IFindChannelMod(SO, name);
if(result && !dontCache) {
ICacheChannelMod(result);
}
}
}
return result;
}
// CACHECHANNELMOD
plAGModifier * plAGMasterMod::ICacheChannelMod(plAGModifier *mod) const
{
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0);
hsgResMgr::ResMgr()->SendRef(mod, msg, plRefFlags::kActiveRef);
return mod;
}
// IFINDAGMOD (sceneObject)
// See if there's an ag modifier on this sceneobject.
// Doesn't check for multiples; just returns the first one.
plAGModifier * plAGMasterMod::IFindChannelMod(const plSceneObject *SO, const plString &name) const
{
const plCoordinateInterface * CI = SO->GetCoordinateInterface();
const plAGModifier * constMod = static_cast<const plAGModifier *>(FindModifierByClass(SO, plAGModifier::Index()));
plAGModifier * mod = const_cast<plAGModifier *>(constMod);
if(mod)
{
plString modName = mod->GetChannelName();
if(modName.Compare(name, plString::kCaseInsensitive) == 0)
return mod;
}
if(CI)
{
int childCount = CI->GetNumChildren();
for (int i = 0; i < childCount; i++)
{
const plSceneObject * subChild = CI->GetChild(i)->GetOwner();
plAGModifier * mod = IFindChannelMod(subChild, name);
if(mod)
return mod;
}
}
return nil;
}
// ATTACHANIMATIONBLENDED(anim, blend)
plAGAnimInstance * plAGMasterMod::AttachAnimationBlended(plAGAnim *anim,
float blendFactor /* = 0 */,
uint16_t blendPriority /* plAGMedBlendPriority */,
bool cache /* = false */)
{
plAGAnimInstance *instance = nil;
plAnimVector::iterator i;
if(anim)
{
fNeedCompile = true; // need to recompile the graph since we're editing it...
for (i = fPrivateAnims.begin(); i != fPrivateAnims.end(); i++)
{
if (*i == anim)
break;
}
if (i == fPrivateAnims.end()) // Didn't find it. Ref it!
{
plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPublicAnim);
hsgResMgr::ResMgr()->SendRef(anim, msg, plRefFlags::kActiveRef);
}
instance = new plAGAnimInstance(anim, this, blendFactor, blendPriority, cache, false);
fAnimInstances.push_back(instance);
plATCAnim *atcAnim = plATCAnim::ConvertNoRef(anim);
if (atcAnim)
{
fATCAnimInstances.push_back(instance);
ISetupMarkerCallbacks(atcAnim, instance->GetTimeConvert());
}
IRegForEval(HasRunningAnims());
}
return instance;
}
// ATTACHANIMATIONBLENDED(name, blend)
plAGAnimInstance * plAGMasterMod::AttachAnimationBlended(const plString &name, float blendFactor /* = 0 */, uint16_t blendPriority, bool cache /* = false */)
{
plAGAnimInstance *instance = nil;
plAGAnim *anim = plAGAnim::FindAnim(name);
if(anim)
{
instance = AttachAnimationBlended(anim, blendFactor, blendPriority, cache);
}
return instance;
}
void plAGMasterMod::PlaySimpleAnim(const plString &name)
{
plATCAnim *anim = plATCAnim::ConvertNoRef(plAGAnim::FindAnim(name));
plAGAnimInstance *instance = nil;
if (anim)
{
if (FindAnimInstance(name))
return;
instance = AttachAnimationBlended(anim, 1.f, (uint16_t)kAGMaxBlendPriority, false);
}
if (instance)
{
instance->SetLoop(false);
instance->Start();
plAGDetachCallbackMsg *msg = new plAGDetachCallbackMsg(GetKey(), kStop);
msg->SetAnimName(name);
instance->GetTimeConvert()->AddCallback(msg);
hsRefCnt_SafeUnRef(msg);
}
}
// FINDANIMINSTANCE
// Look for an animation instance of the given name on the modifier.
// If we need this to be fast, should make it a map rather than a vector
plAGAnimInstance * plAGMasterMod::FindAnimInstance(const plString &name)
{
plAGAnimInstance *result = nil;
if (!name.IsNull())
{
for (int i = 0; i < fAnimInstances.size(); i++)
{
plAGAnimInstance *act = fAnimInstances[i];
plString eachName = act->GetName();
if( eachName.Compare(name, plString::kCaseInsensitive) == 0)
{
result = act;
break;
}
}
}
return result;
}
// FINDORATTACHINSTANCE
plAGAnimInstance * plAGMasterMod::FindOrAttachInstance(const plString &name, float blendFactor)
{
plAGAnimInstance *result = FindAnimInstance(name);
if(result)
{
// if it's already attached, we need to set the blend
result->SetBlend(blendFactor);
} else {
result = AttachAnimationBlended(name, blendFactor);
}
return result;
}
// GETANIMINSTANCE
plAGAnimInstance * plAGMasterMod::GetAnimInstance(int i)
{
return fAnimInstances[i];
}
// GETNUMANIMATIONS
int plAGMasterMod::GetNumAnimations()
{
return fAnimInstances.size();
}
// GETNUMPRIVATEANIMATIONS
int plAGMasterMod::GetNumPrivateAnimations()
{
return fPrivateAnims.size();
}
int plAGMasterMod::GetNumATCAnimations()
{
return fATCAnimInstances.size();
}
plAGAnimInstance *plAGMasterMod::GetATCAnimInstance(int i)
{
return fATCAnimInstances[i];
}
void plAGMasterMod::DetachAllAnimations()
{
int nInstances = fAnimInstances.size();
for (int i = nInstances - 1; i >= 0; i--)
{
plAGAnimInstance * instance = fAnimInstances[i];
if(instance)
{
DetachAnimation(instance);
// delete instance;
}
}
fAnimInstances.clear();
fPrivateAnims.clear();
fATCAnimInstances.clear();
}
// DETACHANIMATION(plAGAnimInstance *)
void plAGMasterMod::DetachAnimation(plAGAnimInstance *anim)
{
plInstanceVector::iterator i;
plAnimVector::iterator j;
fNeedCompile = true; // need to recompile the graph since we're editing it...
for ( i = fAnimInstances.begin(); i != fAnimInstances.end(); i++)
{
plAGAnimInstance *instance = *i;
if(instance == anim)
{
// DetachAnimation(instance);
instance->DetachChannels();
// Need to release it if it's not a private anim
const plAGAnim *agAnim = instance->GetAnimation();
for (j = fPrivateAnims.begin(); j != fPrivateAnims.end(); j++)
{
if (*j == agAnim)
break;
}
if (j == fPrivateAnims.end()) // We didn't find it
GetKey()->Release(agAnim->GetKey());
delete instance;
i = fAnimInstances.erase(i);
break;
}
}
for ( i = fATCAnimInstances.begin(); i != fATCAnimInstances.end(); i++)
{
plAGAnimInstance *instance = *i;
if(instance == anim)
{
i = fATCAnimInstances.erase(i);
break;
}
}
}
// DETACHANIMATION(name)
void plAGMasterMod::DetachAnimation(const plString &name)
{
plAGAnimInstance *anim = FindAnimInstance(name);
if(anim) {
DetachAnimation(anim);
}
}
void plAGMasterMod::DumpCurrentAnims(const char *header)
{
if(header)
hsStatusMessageF("Dumping Armature Anim Stack: %s", header);
int nAnims = fAnimInstances.size();
for(int i = nAnims - 1; i >= 0; i--)
{
plAGAnimInstance *inst = fAnimInstances[i];
plString name = inst->GetName();
float blend = inst->GetBlend();
hsStatusMessageF("%d: %s with blend of %f\n", i, name.c_str(), blend);
}
}
// MSGRECEIVE
// receive trigger messages
bool plAGMasterMod::MsgReceive(plMessage* msg)
{
plSDLNotificationMsg* nMsg = plSDLNotificationMsg::ConvertNoRef(msg);
if (nMsg)
{
// Force a single eval
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
return true;
}
plAnimCmdMsg* cmdMsg = plAnimCmdMsg::ConvertNoRef(msg);
if (cmdMsg)
{
plString targetName = cmdMsg->GetAnimName();
if (targetName.IsNull())
targetName = ENTIRE_ANIMATION_NAME;
plAGAnimInstance *inst = FindAnimInstance(targetName);
if (inst != nil)
{
if (cmdMsg->CmdChangesAnimTime())
{
for (int i = 0; i < GetNumAnimations(); i++)
{
plAGAnimInstance *currInst = GetAnimInstance(i);
if (currInst != inst && currInst->GetAnimation()->SharesPinsWith(inst->GetAnimation()))
currInst->SetBlend(0);
}
inst->SetBlend(1);
}
inst->HandleCmd(cmdMsg);
}
return true;
}
plAGCmdMsg* agMsg = plAGCmdMsg::ConvertNoRef(msg);
if (agMsg)
{
if (agMsg->Cmd(plAGCmdMsg::kSetAnimTime))
{
for (int i = 0; i < fAnimInstances.size(); i++)
{
plAGAnimInstance *inst = fAnimInstances[i];
inst->SetCurrentTime(agMsg->fAnimTime, true);
}
return true;
}
plAGAnimInstance *inst = FindAnimInstance(agMsg->GetAnimName());
if (inst != nil)
{
if (agMsg->Cmd(plAGCmdMsg::kSetBlend))
inst->Fade(agMsg->fBlend, agMsg->fBlendRate, plAGAnimInstance::kFadeBlend);
if (agMsg->Cmd(plAGCmdMsg::kSetAmp))
inst->Fade(agMsg->fAmp, agMsg->fAmpRate, plAGAnimInstance::kFadeAmp);
}
return true;
}
plAGInstanceCallbackMsg *agicMsg = plAGInstanceCallbackMsg::ConvertNoRef(msg);
if (agicMsg)
{
if (agicMsg->fEvent == kStart)
{
IRegForEval(true);
}
else if (agicMsg->fEvent == kStop)
{
if (!HasRunningAnims())
IRegForEval(false);
}
else // Just force a single eval
{
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
}
return true;
}
plAGDetachCallbackMsg *detachMsg = plAGDetachCallbackMsg::ConvertNoRef(msg);
if (detachMsg)
{
DetachAnimation(detachMsg->GetAnimName());
}
plGenRefMsg *genRefMsg = plGenRefMsg::ConvertNoRef(msg);
if (genRefMsg)
{
plAGAnim *anim = plAGAnim::ConvertNoRef(genRefMsg->GetRef());
if (anim)
{
if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
{
if (genRefMsg->fType == kPrivateAnim)
fPrivateAnims.push_back(anim);
}
else
{
if (genRefMsg->fType == kPrivateAnim)
{
plAnimVector::iterator i = fPrivateAnims.begin();
for ( ; i != fPrivateAnims.end(); i++)
{
plAGAnim *currAnim = *i;
if(currAnim == anim)
{
i = fPrivateAnims.erase(i);
break;
}
}
}
}
return true;
}
plAGModifier *agmod = plAGModifier::ConvertNoRef(genRefMsg->GetRef());
if (agmod)
{
if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
fChannelMods[agmod->GetChannelName()] = agmod;
else
fChannelMods.erase(agmod->GetChannelName());
return true;
}
plMsgForwarder *msgfwd = plMsgForwarder::ConvertNoRef(genRefMsg->GetRef());
if (msgfwd)
{
if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
fMsgForwarder = msgfwd;
else
fMsgForwarder = nil;
return true;
}
}
plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg);
if (refMsg)
{
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
AddTarget(plSceneObject::ConvertNoRef(refMsg->GetRef()));
else
RemoveTarget(plSceneObject::ConvertNoRef(refMsg->GetRef()));
return true;
}
return plModifier::MsgReceive(msg);
}
void plAGMasterMod::IRegForEval(bool val)
{
if (fNeedEval == val)
return;
fNeedEval = val;
if (val)
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
else
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
}
bool plAGMasterMod::HasRunningAnims()
{
int i;
bool needEval = false;
for (i = 0; i < fAnimInstances.size(); i++)
{
if (!fAnimInstances[i]->IsFinished())
{
needEval = true;
break;
}
}
return needEval;
}
//
// Send SDL sendState msg to object's plAGMasterSDLModifier
//
bool plAGMasterMod::DirtySynchState(const plString& SDLStateName, uint32_t synchFlags)
{
if(GetNumTargets() > 0 && (!fIsGrouped || fIsGroupMaster))
{
plSceneObject *sObj = GetTarget(0);
if(sObj)
return sObj->DirtySynchState(SDLStateName, synchFlags);
}
return false;
}
void plAGMasterMod::SetIsGroupMaster(bool master, plMsgForwarder* msgForwarder)
{
fIsGroupMaster = master;
fMsgForwarder = msgForwarder;
}
void plAGMasterMod::SetIsGrouped(bool grouped)
{
fIsGrouped = true;
}

View File

@ -0,0 +1,263 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
/** \file plAGMasterMod.h
\brief The animation class for the AniGraph animation system
\ingroup Avatar
\ingroup AniGraph
*/
#ifndef PLAGMASTERMOD_INC
#define PLAGMASTERMOD_INC
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
#include "pnModifier/plModifier.h"
#include "plAGChannel.h"
#include "plAGDefs.h"
#include "pnKeyedObject/plMsgForwarder.h"
class plAGModifier;
class plAGAnimInstance;
class plAGAnim;
class plATCAnim;
class plAGMasterSDLModifier;
////////////////
//
// PLAGMASTERMOD
//
////////////////
/** \class plAGMasterMod
A modifier which can apply animations to scene objects.
Works together with a plAGModifier, which can apply animation to a
single scene object. A master modifier hooks up to a family of
ag modifiers to do its job.
An animation (plAGAnim) can have many different channels, but the
user can just pass it to a single master mod to apply it to all the
channels owned by that master. The master will take care of hooking
each channel up to an ag modifier.
The goal is to make an animation a conceptually (and practically) simple
object to work with, whether or not it has multiple channels.
\sa plAGAnim, plAGAnimInstance, plAGModifier
*/
class plAGMasterMod : public plModifier
{
friend class plAGMasterSDLModifier;
public:
/** Default constructor. Primarily for use by the class factory. */
plAGMasterMod();
/** Free the name and any other miscellany. */
virtual ~plAGMasterMod();
/** Find an individual plAGModifier of the given name under our control. */
plAGModifier * GetChannelMod(const plString & name, bool dontCache = false) const;
/** \name Managing Animations */
// \{
// AGANIM PROTOCOL
/** Attach the given animation object with the given blend factor.
If there's no animation already attached to blend with, the
animation will be attached at full strength. */
plAGAnimInstance *AttachAnimationBlended(plAGAnim *anim, float blendFactor = 0,
uint16_t blendPriority = kAGMedBlendPriority,
bool cache = false);
/** Look up the given animation by name and attach it
with the given blend factor. */
plAGAnimInstance *AttachAnimationBlended(const plString &name, float blendFactor = 0,
uint16_t blendPriority = kAGMedBlendPriority,
bool cache = false);
/** Play a simple anim (one that doesn't affect root) once and auto detach.
Intended for Zandi's facial animations that run seperate from the behaviors. */
void PlaySimpleAnim(const plString &name);
/** Detach the given animation instance. Does nothing
if the instance is not managed by this master mod. */
void DetachAnimation(plAGAnimInstance *instance);
void DetachAllAnimations();
/** Detach the given animation by name. Searches for
any instances derived from animations with the
given name and removes them. */
void DetachAnimation(const plString &name);
// \}
/** Print the current animation stack to the console.
Will list all the animations and their blend strengths.
Animations later in the list will mask animations earlier
in the list. */
void DumpCurrentAnims(const char *header);
/** Find and return any animation instances with the
given name on this master modifer. */
plAGAnimInstance *FindAnimInstance(const plString &name);
/** Return the Ith animation instance, based on blend
order. Of dubious utility, but, y'know. */
plAGAnimInstance *GetAnimInstance(int i);
/** Attach the animation if it's not already attached. If
it is attached, return the instance.
Note that if it's attached by this function, it
will be on top of the stack, but if it was already
attached, it could be anywhere, including buried under
a bunch of other animations. If it's important that it be
on top of the stack, you may need to detach it first. */
plAGAnimInstance *FindOrAttachInstance(const plString &name, float blendFactor);
/** Return the number of animations available. */
int GetNumAnimations();
/** Return the number of animations that are privately
owned by this modifier.
Animations may be either shared in a general pool,
or privately owned by a mastermod. */
int GetNumPrivateAnimations();
int GetNumATCAnimations();
plAGAnimInstance *GetATCAnimInstance(int i);
/** Apply all our animations to all our parts.
\param timeNow is the current world time
\param elapsed is the time since the previous frame */
void ApplyAnimations(double timeNow, float elapsed);
/** Runs through our anims and applies them, without
processing fades. This is used when we load in anim
state from the server, and need to advance it to a
certain point before enabling callbacks */
void AdvanceAnimsToTime(double time);
/** Change the connectivity in the graph so that inactive animations are bypassed.
The original connectivity information is kept, so if the activity of different
animations is changed (such as by changing blend biases or adding new animations,
the graph can be compiled again to the correct state. */
void Compile(double time);
/** We've done something that invalidates the cached connectivity in the graph.
Mark this for fixup. */
void SetNeedCompile(bool needCompile);
/** List the animationg graph to stdOut, with a ASCII representation of the tree
structure. Done by recursively dumping the graph; some types of nodes will have
more output information than others.
*/
void DumpAniGraph(const char *channel, bool optimized, double time);
/** Set whether or not this is the "group master" so grouped animations will only have
one member getting/setting sdl animation state in order to synch the anims
*/
void SetIsGrouped(bool grouped);
void SetIsGroupMaster(bool master, plMsgForwarder* msgForwarder);
// PLASMA PROTOCOL
virtual int GetNumTargets() const { return fTarget ? 1 : 0; }
virtual plSceneObject* GetTarget(int w) const { /* hsAssert(w < GetNumTargets(), "Bad target"); */ return fTarget; }
virtual void AddTarget(plSceneObject * object);
virtual void RemoveTarget(plSceneObject * object);
bool MsgReceive(plMessage* msg);
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream * stream, hsResMgr *mgr);
bool HasRunningAnims();
bool DirtySynchState(const plString& SDLStateName, uint32_t synchFlags);
CLASSNAME_REGISTER( plAGMasterMod );
GETINTERFACE_ANY( plAGMasterMod, plModifier );
protected:
// -- methods --
plAGModifier * ICacheChannelMod(plAGModifier *mod) const;
plAGModifier * IFindChannelMod(const plSceneObject *obj, const plString &name) const;
virtual bool IEval(double secs, float del, uint32_t dirty);
virtual void IApplyDynamic() {}; // dummy function required by base class
// Find markers in an anim for environment effects (footsteps)
virtual void ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc) {}
// -- members
plSceneObject* fTarget;
// a map from channel names to ag modifiers within our domain
typedef std::map<plString, plAGModifier*> plChannelModMap;
plChannelModMap fChannelMods;
// instances which are currently attached to this master
typedef std::vector<plAGAnimInstance*> plInstanceVector;
plInstanceVector fAnimInstances; // animation instances
// animations which are owned exclusively by this master
typedef std::vector<plAGAnim*> plAnimVector;
plAnimVector fPrivateAnims;
// animations that require AnimTimeConvert state to be synched
plInstanceVector fATCAnimInstances;
bool fFirstEval;
bool fNeedEval;
void IRegForEval(bool val);
// SDL modifier which sends/recvs dynamics state
plAGMasterSDLModifier *fAGMasterSDLMod;
bool fNeedCompile;
bool fIsGrouped;
bool fIsGroupMaster;
plMsgForwarder* fMsgForwarder;
enum {
kPrivateAnim,
kPublicAnim,
};
};
#endif // PLAGMASTERMOD_INC

View File

@ -0,0 +1,313 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
// local
#include "plAGModifier.h"
#include "plMatrixChannel.h"
// global
#include "hsResMgr.h"
#include "hsTimer.h"
// other
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plCoordinateInterface.h"
#include "pnSceneObject/plSimulationInterface.h"
/////////////////
//
// PLAGMODIFIER
//
/////////////////
// Applies animation graph output to a single scene object.
// CTOR
plAGModifier::plAGModifier()
: plSingleModifier()
{
fAutoApply = true;
fEnabled = true;
}
// CTOR(name)
plAGModifier::plAGModifier(const plString &name, bool autoApply)
: plSingleModifier(), fAutoApply(autoApply)
{
fChannelName = name;
fEnabled = true;
}
// DTOR
plAGModifier::~plAGModifier()
{
int i;
for (i = 0; i < fApps.size(); i++)
{
delete fApps[i];
}
}
// GETCHANNELNAME
plString plAGModifier::GetChannelName() const
{
return fChannelName;
}
// ENABLE
void plAGModifier::Enable(bool val)
{
fEnabled = val;
}
// SETCHANNELNAME
void plAGModifier::SetChannelName(const plString & name)
{
fChannelName = name;
}
// IAPPLYCHANNELS (time)
// One AGModifier, although applied to a single scene object, can have
// multiple channels on it affecting, say, position, color, aroma, etc.
//
// There are cases where we want to call this and won't know the delta,
// we don't seem to ever need it for this function, so I'm taking it out.
// If you run into a case where you think it's necessary, see me. -Bob
void plAGModifier::Apply(double time) const
{
if (!fEnabled)
return;
for (int i = 0; i < fApps.size(); i++)
{
plAGApplicator *app = fApps[i];
app->Apply(this, time);
}
}
// IEVAL
// Apply our channels to our scene object
bool plAGModifier::IEval(double time, float delta, uint32_t dirty)
{
if(fAutoApply) {
// Apply(time, delta);
}
return true;
}
// GETAPPLICATOR
plAGApplicator * plAGModifier::GetApplicator(plAGPinType pinType) const
{
int numApps = fApps.size();
for (int i = 0; i < numApps; i++)
{
plAGApplicator *app = fApps[i];
plAGPinType otherType = app->GetPinType();
if(otherType == pinType)
return app;
}
return nil;
}
// SETAPPLICATOR
void plAGModifier::SetApplicator(plAGApplicator *newApp)
{
int numApps = fApps.size();
plAGPinType newPinType = newApp->GetPinType();
// *** NOTE: this code is completely untested. Since I happened to be here
// I sketched out how it *should* work and implemented the base protocol.
// In reality, most of these code paths are not accessed now...
// -- mm
for(int i = 0; i < numApps; i++)
{
plAGApplicator *existingApp = fApps[i];
plAGPinType extPinType = existingApp->GetPinType();
if(extPinType == newPinType)
{
hsStatusMessage("Two applicators accessing same pin type...congratulations for being the first to test this.");
// these applicators both try to set the same thing; try to merge them
plAGChannel *newChannel = newApp->GetChannel();
hsAssert(!newChannel, "Trying to merge in new applicator which already has channel. Incomplete.");
// *** right now I just want to support the case of putting in a new applicator - not merging animations
plAGChannel *extChannel = existingApp->GetChannel();
newApp->SetChannel(extChannel);
existingApp->SetChannel(nil);
fApps[i] = newApp;
delete existingApp;
return;
// NOTE: we should make these arbitrate, but I'm not going to right now because
// there's not currently an (easy) way to merge two applicators without allowing a blend.
// if(existingApp->CanCombine(newApp))
// {
// // the existing applicator promises to provide the functionality we need...merge into it.
// existingApp->MergeChannel(newApp);
// } else {
// // couldn't merge into the existing channel; can we merge it into us instead?
// if(newApp->CanCombine(extApp))
// {
// // okay, WE can provide the functionality of the existing applicator.
// fApps[i] = newApp; // take over its spot in the applicators
// newApp->MergeChannel(existingApp); // and merge it into us
// }
// }
}
}
// didn't find any conflicts; just add our app on the end
fApps.push_back(newApp);
}
// MERGECHANNEL
// Intended as a replacement for attach/blend channel. You want to add a channel to this node,
// we do that for you. Don't ask us how, you shouldn't have to know.
plAGChannel * plAGModifier::MergeChannel(plAGApplicator *app,
plAGChannel *channel,
plScalarChannel *blend,
plAGAnimInstance *anim,
int priority)
{
int numApps = fApps.size();
plAGChannel * result = nil;
for (int i = 0; i < numApps; i++)
{
plAGApplicator *existingApp = fApps[i];
result = existingApp->MergeChannel(app, channel, blend, priority);
if (result)
return result;
}
if (!result)
{
// didn't blend or combine with an existing channel; add a new channel
plAGApplicator *newApp = app->CloneWithChannel(channel);
fApps.push_back(newApp);
}
return result;
}
// DETACHCHANNEL
bool plAGModifier::DetachChannel(plAGChannel * channel)
{
plAppTable::iterator i = fApps.begin();
while( i != fApps.end() )
{
plAGApplicator *app = *i;
plAGChannel *existingChannel = app->GetChannel();
if(existingChannel)
{
plAGChannel *replacementChannel = existingChannel->Detach(channel);
if (existingChannel != replacementChannel)
{
app->SetChannel(replacementChannel);
if( ! replacementChannel && app->AutoDelete())
{
// Don't need to adjust the iterator since we're about to exit the loop
fApps.erase(i);
delete app;
}
return true;
}
}
++i;
}
return false;
}
// READ
void plAGModifier::Read(hsStream *stream, hsResMgr *mgr)
{
plSingleModifier::Read(stream, mgr);
// read in the name of the modifier
fChannelName = stream->ReadSafeString();
}
// WRITE
void plAGModifier::Write(hsStream *stream, hsResMgr *mgr)
{
plSingleModifier::Write(stream, mgr);
// write out the name of the modifier
stream->WriteSafeString(fChannelName);
}
/////////////////////////////////////////
/////////////////////////////////////////
//
// MOVE
//
/////////////////////////////////////////
/////////////////////////////////////////
const plModifier * FindModifierByClass(const plSceneObject *obj, int classID)
{
if(obj)
{
int modCount = obj->GetNumModifiers();
for (int i = 0; i < modCount; i++)
{
const plModifier *mod = obj->GetModifier(i);
if(mod) // modifier might not be loaded yet
{
if(mod->ClassIndex() == classID)
{
return mod;
}
}
}
}
return nil;
}

View File

@ -0,0 +1,166 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
/** \file plAGMasterMod.h
\ingroup Avatar
\ingroup AniGraph
*/
#ifndef PLAGMODIFIER_H
#define PLAGMODIFIER_H
#include "HeadSpin.h" // need for plSingleModifier
#include "pnModifier/plSingleModifier.h" // inherited
// local
#include "plScalarChannel.h"
class plSceneObject;
class plAGAnimInstance;
class plAGAnim;
class plAvatarAnim;
class plAnimCmdMsg;
class plOneShotCallbacks;
///////////////
//
// PLAGMODIFIER
//
///////////////
/** \class plAGModifier
Animates a single scene object.
May apply multiple animations to that scene object, animating
the transform with one, colors with another, etc.
The basic model is that the ag modifier holds an array of
applicators, each of which is the root of a tree of animation
operations, such as a blend tree, etc. Each frame it fires
all its applicators, or (ideally) only those that need to be fired.
*/
class plAGModifier : public plSingleModifier
{
public:
/** Default constructor, primarily for the class factory. */
plAGModifier();
/** Construct given a name. This name will be used to match up
incoming channels with this modifier. You may also supply an
autoApply parameter, which indicates whether this modifier
should apply itself every frame, or only when explicitly asked to. */
plAGModifier(const plString &name, bool autoApply = true);
/** It's a destructor. Destroys the name passed into the constructor,
and a bunch of other stuff you don't need to know anything about. */
virtual ~plAGModifier();
/** Get the name of the channel controlled by this modifier. */
plString GetChannelName() const;
/** Change the channel name of the modifier. Will delete the previous
name. Will NOT remove any channels that are already attached, so
you could wind up with a modifier named "Fred" and a bunch of
channels attached to it that were intended for "Lamont." */
void SetChannelName(const plString & name);
/** Attach a new applicator to our modifier. Will arbitrate with existing
modifiers if necessary, based on pin type. May destruct existing applicators. */
plAGChannel *MergeChannel(plAGApplicator *app, plAGChannel *chan, plScalarChannel *blend,
plAGAnimInstance *anim, int priority);
/** Remove the given channel. Will also remove the channel's applicator. */
bool DetachChannel(plAGChannel * channel);
/** Set the applicator for a modifier.
\deprecated */
void SetApplicator(plAGApplicator *app);
/** Get the applicator for a given pintype. Note that an ag modifier can
only have one applicator of a given pin type attached at a time.
The exception is for the unknown pin type, which never conflicts
with any other pin type, including itself. */
plAGApplicator *GetApplicator(plAGPinType pin) const;
/** Apply the animation for our scene object. */
void Apply(double time) const;
/** Get the channel tied to our ith applicator */
plAGChannel * GetChannel(int i) { return fApps[i]->GetChannel(); }
void Enable(bool val);
// PERSISTENCE
virtual void Read(hsStream *stream, hsResMgr *mgr);
virtual void Write(hsStream *stream, hsResMgr *mgr);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plAGModifier );
GETINTERFACE_ANY( plAGModifier, plSingleModifier );
protected:
typedef std::vector<plAGApplicator*> plAppTable;
plAppTable fApps; // the applicators (with respective channels) that we're applying to our scene object
plString fChannelName; // name used for matching animation channels to this modifier
bool fAutoApply; // evaluate animation automatically during IEval call
bool fEnabled; // if not enabled, we don't eval any of our anims
// APPLYING THE ANIMATION
virtual bool IEval(double secs, float del, uint32_t dirty);
virtual bool IHandleCmd(plAnimCmdMsg* modMsg) { return false; } // only plAGMasterMod should handle these
virtual void IApplyDynamic() {}; // dummy function required by base class
// INTERNAL ACCESSORS FOR SCENE OBJECT INTERFACES
plAudioInterface * LeakAI() const { return IGetTargetAudioInterface(0); };
plCoordinateInterface * LeakCI() const { return IGetTargetCoordinateInterface(0); };
plDrawInterface * LeakDI() const { return IGetTargetDrawInterface(0); };
plSimulationInterface * LeakSI() const { return IGetTargetSimulationInterface(0); };
plObjInterface * LeakGI(uint32_t classIdx) const { return IGetTargetGenericInterface(0, classIdx); }
friend plAudioInterface * plAGApplicator::IGetAI(const plAGModifier * modifier) const;
friend plCoordinateInterface * plAGApplicator::IGetCI(const plAGModifier * modifier) const;
friend plDrawInterface * plAGApplicator::IGetDI(const plAGModifier * modifier) const;
friend plSimulationInterface * plAGApplicator::IGetSI(const plAGModifier * modifier) const;
friend plObjInterface * plAGApplicator::IGetGI(const plAGModifier * modifier, uint16_t classIdx) const;
};
const plModifier * FindModifierByClass(const plSceneObject *obj, int classID);
#endif

View File

@ -0,0 +1,132 @@
/*==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 <http://www.gnu.org/licenses/>.
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 plAnimationCreatable_inc
#define plAnimationCreatable_inc
#include "pnFactory/plCreator.h"
#include "plAGAnim.h"
REGISTER_CREATABLE(plAGAnim);
REGISTER_CREATABLE(plATCAnim);
REGISTER_CREATABLE(plEmoteAnim);
REGISTER_CREATABLE(plAgeGlobalAnim);
#include "plAGChannel.h"
REGISTER_NONCREATABLE(plAGChannel);
#include "plAGApplicator.h"
REGISTER_NONCREATABLE(plAGApplicator);
#include "plMatrixChannel.h"
REGISTER_CREATABLE(plMatrixChannel);
REGISTER_CREATABLE(plMatrixConstant);
REGISTER_CREATABLE(plMatrixTimeScale);
REGISTER_CREATABLE(plMatrixBlend);
REGISTER_CREATABLE(plMatrixControllerChannel);
REGISTER_CREATABLE(plMatrixControllerCacheChannel);
REGISTER_CREATABLE(plQuatPointCombine);
REGISTER_CREATABLE(plMatrixChannelApplicator);
REGISTER_CREATABLE(plMatrixDelayedCorrectionApplicator);
REGISTER_CREATABLE(plMatrixDifferenceApp);
#include "plPointChannel.h"
REGISTER_CREATABLE(plPointChannel);
REGISTER_CREATABLE(plPointConstant);
REGISTER_CREATABLE(plPointBlend);
REGISTER_CREATABLE(plPointTimeScale);
REGISTER_CREATABLE(plPointControllerChannel);
REGISTER_CREATABLE(plPointControllerCacheChannel);
REGISTER_CREATABLE(plPointChannelApplicator);
REGISTER_CREATABLE(plLightDiffuseApplicator);
REGISTER_CREATABLE(plLightAmbientApplicator);
REGISTER_CREATABLE(plLightSpecularApplicator);
#include "plQuatChannel.h"
REGISTER_CREATABLE(plQuatChannel);
REGISTER_CREATABLE(plQuatConstant);
REGISTER_CREATABLE(plQuatBlend);
REGISTER_CREATABLE(plQuatTimeScale);
REGISTER_CREATABLE(plQuatChannelApplicator);
#include "plScalarChannel.h"
REGISTER_CREATABLE(plScalarChannel);
REGISTER_CREATABLE(plScalarConstant);
REGISTER_CREATABLE(plScalarTimeScale);
REGISTER_CREATABLE(plScalarBlend);
REGISTER_CREATABLE(plScalarControllerChannel);
REGISTER_CREATABLE(plScalarControllerCacheChannel);
REGISTER_CREATABLE(plScalarChannelApplicator);
REGISTER_CREATABLE(plSpotInnerApplicator);
REGISTER_CREATABLE(plSpotOuterApplicator);
REGISTER_CREATABLE(plATCChannel);
REGISTER_CREATABLE(plScalarSDLChannel);
REGISTER_CREATABLE(plOmniApplicator);
REGISTER_CREATABLE(plOmniSqApplicator);
REGISTER_CREATABLE(plOmniCutoffApplicator);
#include "plAGModifier.h"
REGISTER_CREATABLE(plAGModifier);
#include "plAGMasterMod.h"
REGISTER_CREATABLE(plAGMasterMod);
#endif // plAnimationCreatable_inc

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,384 @@
/*==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 <http://www.gnu.org/licenses/>.
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 PLMATRIXCHANNEL_INC
#define PLMATRIXCHANNEL_INC
// local
#include "plScalarChannel.h"
// global
#include "HeadSpin.h" // you need types to include Matrix
#include "hsMatrix44.h"
#include "plTransform/hsAffineParts.h"
// local prototypes
class plQuatChannel;
class plPointChannel;
// external prototypes
class plController;
class hsAffineParts;
class plAnimTimeConvert;
class plMatrixChannelApplicator;
class plControllerCacheInfo;
//////////////////
// PLMATRIXCHANNEL
//////////////////
// an animation channel that outputs matrices
class plMatrixChannel : public plAGChannel
{
protected:
hsMatrix44 fResult;
hsAffineParts fAP;
public:
plMatrixChannel();
virtual ~plMatrixChannel();
// AG PROTOCOL
virtual const hsMatrix44 & Value(double time, bool peek = false);
virtual void Value(hsMatrix44 &matrix, double time, bool peek = false);
virtual const hsAffineParts & AffineValue(double time, bool peek = false);
// combine it (allocates combine object)
virtual plAGChannel * MakeCombine(plAGChannel * channelB);
// blend it (allocates blend object)
virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority);
// const eval at time zero
virtual plAGChannel * MakeZeroState();
// make a timeScale instance
virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource);
virtual plAGPinType GetPinType() { return kAGPinTransform; };
virtual void Dump(int indent, bool optimized, double time);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plMatrixChannel );
GETINTERFACE_ANY( plMatrixChannel, plAGChannel );
};
///////////////////
// PLMATRIXCONSTANT
///////////////////
// A matrix source that just keeps handing out the same value
class plMatrixConstant : public plMatrixChannel
{
public:
plMatrixConstant();
plMatrixConstant(const hsMatrix44 &value);
virtual ~plMatrixConstant();
void Set(const hsMatrix44 &value);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plMatrixConstant );
GETINTERFACE_ANY( plMatrixConstant, plMatrixChannel );
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream *s, hsResMgr *mgr);
};
////////////////////
// PLMATRIXTIMESCALE
////////////////////
// Adapts the time scale before passing it to the next channel in line.
// Use to instance animations while allowing each instance to run at different speeds.
class plMatrixTimeScale : public plMatrixChannel
{
protected:
plScalarChannel *fTimeSource;
plMatrixChannel *fChannelIn;
public:
plMatrixTimeScale();
plMatrixTimeScale(plMatrixChannel *channel, plScalarChannel *timeSource);
virtual ~plMatrixTimeScale();
virtual bool IsStoppedAt(double time);
virtual const hsMatrix44 & Value(double time, bool peek = false);
virtual const hsAffineParts & AffineValue(double time, bool peek = false);
virtual plAGChannel * Detach(plAGChannel * channel);
virtual void Dump(int indent, bool optimized, double time);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plMatrixTimeScale );
GETINTERFACE_ANY( plMatrixTimeScale, plMatrixChannel );
};
////////////////
// PLMATRIXBLEND
////////////////
// blends two matrices into one with weighting
class plMatrixBlend : public plMatrixChannel
{
protected:
plMatrixChannel * fChannelA;
plMatrixChannel * fOptimizedA;
plMatrixChannel * fChannelB;
plMatrixChannel * fOptimizedB;
plScalarChannel * fChannelBias;
int fPriority;
public:
// xTORs
plMatrixBlend();
plMatrixBlend(plMatrixChannel * channelA, plMatrixChannel * channelB, plScalarChannel * channelBias, int priority);
virtual ~plMatrixBlend();
virtual plAGChannel * MakeBlend(plAGChannel *newChannel, plScalarChannel *channelBias, int blendPriority);
// you cannot blend on top of a channel that has higher priority than you do.
virtual uint16_t GetPriority();
// SPECIFICS
const plMatrixChannel * GetChannelA() const { return fChannelA; }
void SetChannelA(plMatrixChannel * channel) { fChannelA = channel; }
const plMatrixChannel * GetChannelB() const { return fChannelB; }
void SetChannelB(plMatrixChannel * channel) { fChannelB = channel; }
const plScalarChannel * GetChannelBias() const { return fChannelBias; }
void SetChannelBias(plScalarChannel * channel) { fChannelBias = channel; }
//virtual void SetBlend(float blend) { fBlend = blend; };
//virtual float GetBlend() { return fBlend; };
virtual bool IsStoppedAt(double time);
// AG PROTOCOL
virtual const hsMatrix44 & Value(double time, bool peek = false);
virtual const hsAffineParts & AffineValue(double time, bool peek = false);
// remove the specified channel from our graph
virtual plAGChannel * Detach(plAGChannel * channel);
virtual void Dump(int indent, bool optimized, double time);
plAGChannel* Optimize(double time);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plMatrixBlend );
GETINTERFACE_ANY( plMatrixBlend, plMatrixChannel );
};
/////////////////////
// PLMATRIXCONTROLLER
/////////////////////
// converts a plController-style animation into a plMatrixChannel
class plMatrixControllerChannel : public plMatrixChannel
{
protected:
plController *fController;
public:
// xTORs
plMatrixControllerChannel();
plMatrixControllerChannel(plController *controller, hsAffineParts *parts);
virtual ~plMatrixControllerChannel();
// AG PROTOCOL
virtual const hsAffineParts & AffineValue(double time, bool peek = false);
virtual const hsAffineParts & AffineValue(double time, bool peek, plControllerCacheInfo *cache);
virtual const hsMatrix44 & Value(double time, bool peek = false);
virtual const hsMatrix44 & Value(double time, bool peek, plControllerCacheInfo *cache);
virtual plAGChannel * MakeCacheChannel(plAnimTimeConvert *atc);
virtual void Dump(int indent, bool optimized, double time);
// PLASMA PROTOCOL
// rtti
CLASSNAME_REGISTER( plMatrixControllerChannel );
GETINTERFACE_ANY( plMatrixControllerChannel, plMatrixChannel );
// persistence
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream *s, hsResMgr *mgr);
};
/////////////////////////////////
// PLMATRIXCONTROLLERCACHECHANNEL
/////////////////////////////////
// Same as plMatrixController, but with caching info
class plMatrixControllerCacheChannel : public plMatrixChannel
{
protected:
plControllerCacheInfo *fCache;
plMatrixControllerChannel *fControllerChannel;
public:
plMatrixControllerCacheChannel();
plMatrixControllerCacheChannel(plMatrixControllerChannel *channel, plControllerCacheInfo *cache);
virtual ~plMatrixControllerCacheChannel();
virtual const hsMatrix44 & Value(double time, bool peek = false);
virtual const hsAffineParts & AffineValue(double time, bool peek = false);
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plMatrixControllerCacheChannel );
GETINTERFACE_ANY( plMatrixControllerCacheChannel, plMatrixChannel );
// Created at runtime only, so no Read/Write
};
/////////////////////
// PLQUATPOINTCOMBINE
/////////////////////
// combines a quaternion and a point into a matrix
class plQuatPointCombine : public plMatrixChannel
{
protected:
plQuatChannel *fQuatChannel;
plPointChannel *fPointChannel;
public:
// xTORs
plQuatPointCombine();
plQuatPointCombine(plQuatChannel *quatChannel, plPointChannel *pointChannel);
virtual ~plQuatPointCombine();
// SPECIFICS
const plQuatChannel * GetQuatChannel() const { return fQuatChannel; }
void SetQuatChannel(plQuatChannel * channel) { fQuatChannel = channel; }
plPointChannel * GetPointChannel() const { return fPointChannel; }
void SetPointChannel(plPointChannel * channel) { fPointChannel = channel; }
// AG PROTOCOL
virtual const hsMatrix44 & Value(double time);
virtual const hsAffineParts & AffineValue(double time);
// remove the specified channel from our graph
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plQuatPointCombine );
GETINTERFACE_ANY( plQuatPointCombine, plMatrixChannel );
};
/////////////////////
//
// Applicator classes
class plMatrixChannelApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plMatrixChannelApplicator );
GETINTERFACE_ANY( plMatrixChannelApplicator, plAGApplicator );
virtual bool CanCombine(plAGApplicator *app) { return false; }
virtual plAGPinType GetPinType() { return kAGPinTransform; }
};
// PLMATRIXDELAYEDCORRECTIONAPPLICATOR
// Used for blending in sudden location changes due to synch messages.
// This app tacks on a correction to the l2p transform
// (so l2p is set as animL2P*correction)
// interpolating this to the identity matrix over time.
class plMatrixDelayedCorrectionApplicator : public plMatrixChannelApplicator
{
protected:
hsAffineParts fCorAP; // AP of the correction.
double fDelayStart; // Start time of the delayed correction
// apply our animation * our correction to the node
virtual void IApply(const plAGModifier *mod, double time);
public:
plMatrixDelayedCorrectionApplicator() : fDelayStart(-1000.f), fIgnoreNextCorrection(true) { fCorAP.Reset(); }
CLASSNAME_REGISTER( plMatrixDelayedCorrectionApplicator );
GETINTERFACE_ANY( plMatrixDelayedCorrectionApplicator, plMatrixChannelApplicator );
void SetCorrection(hsMatrix44 &correction);
virtual bool AutoDelete() { return false; } // should we remove it when its input channel is gone?
// applicator arbitration...
virtual plAGPinType GetPinType() { return kAGPinTransform; }
virtual bool CanBlend(plAGApplicator *app);
bool fIgnoreNextCorrection;
static const float fDelayLength; // static var for now.
};
// PLMATRIXDIFFERENCEAPP
// Each frame, this guy takes the difference between his current value
// and his last value and applies that to the current world
// transform of the target.
// You could also call it the Temporal Matrix Difference Applicator,
// but that sucks to type.
class plMatrixDifferenceApp : public plMatrixChannelApplicator
{
public:
/** Forget the previous cached transform and start again */
void Reset(double time);
/** Should this applicator be automatically removed when its channel goes away? */
virtual bool AutoDelete() { return false; }
// applicator arbitration
virtual plAGPinType GetPinType() { return kAGPinTransform; }
virtual bool CanBlend(plAGApplicator *app);
CLASSNAME_REGISTER(plMatrixDifferenceApp);
GETINTERFACE_ANY(plMatrixDifferenceApp, plMatrixChannelApplicator);
protected:
virtual void IApply(const plAGModifier *mod, double time);
hsMatrix44 fLastL2A; // local to animation space
hsMatrix44 fLastA2L; // animation space to local
bool fNew; // true if we haven't cached anything yet
};
#endif

View File

@ -0,0 +1,498 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
#include "plPointChannel.h"
#include "plScalarChannel.h"
#include "hsResMgr.h"
#include "pnSceneObject/plDrawInterface.h"
#include "pnSceneObject/plSimulationInterface.h"
#include "pnSceneObject/plCoordinateInterface.h"
#include "pnSceneObject/plAudioInterface.h"
#include "plInterp/plController.h"
#include "plInterp/plAnimTimeConvert.h"
#include "plGLight/plLightInfo.h"
//////////////
// PLPOINTSRCE
//////////////
// CTOR
plPointChannel::plPointChannel()
: plAGChannel(), fResult(0, 0, 0)
{
}
// DTOR
plPointChannel::~plPointChannel()
{
}
// VALUE
// default behaviour is just to return our result (constant value)
const hsPoint3 & plPointChannel::Value(double time)
{
return fResult;
}
// VALUE(point, time)
void plPointChannel::Value(hsPoint3 &point, double time)
{
point = Value(time);
}
// MAKECOMBINE
plAGChannel * plPointChannel::MakeCombine(plAGChannel *channelA)
{
return nil;
}
// MAKEBLEND
plAGChannel * plPointChannel::MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority)
{
return new plPointBlend(this, (plPointChannel *)channelB, channelBias);
}
// MAKEZEROSTATE
plAGChannel * plPointChannel::MakeZeroState()
{
return new plPointConstant(Value(0));
}
// MAKETIMESCALE
plAGChannel * plPointChannel::MakeTimeScale(plScalarChannel *timeSource)
{
return new plPointTimeScale(this, timeSource);
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plPointConstant
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------------------
// -----
plPointConstant::plPointConstant()
: plPointChannel()
{
}
// ctor -----------------------------------------------
// -----
plPointConstant::plPointConstant(const hsPoint3 &point)
{
fResult = point;
}
// dtor ---------------------------
// -----
plPointConstant::~plPointConstant()
{
}
void plPointConstant::Read(hsStream *stream, hsResMgr *mgr)
{
plPointChannel::Read(stream, mgr);
fResult.Read(stream);
}
void plPointConstant::Write(hsStream *stream, hsResMgr *mgr)
{
plPointChannel::Write(stream, mgr);
fResult.Write(stream);
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// PLPOINTTIMESCALE
//
// Insert into the graph when you need to change the speed or direction of time
// Also serves as a handy instancing node, since it just passes its data through.
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor ----------------------------
// -----
plPointTimeScale::plPointTimeScale()
: fTimeSource(nil), fChannelIn(nil)
{
}
// ctor --------------------------------------------------------------------------------
// -----
plPointTimeScale::plPointTimeScale(plPointChannel *channel, plScalarChannel *timeSource)
: fChannelIn(channel),
fTimeSource(timeSource)
{
}
// dtor -----------------------------
// -----
plPointTimeScale::~plPointTimeScale()
{
}
// IsStoppedAt ---------------------------
// ------------
bool plPointTimeScale::IsStoppedAt(double time)
{
return fTimeSource->IsStoppedAt(time);
}
// Value --------------------------------------------
// ------
const hsPoint3 & plPointTimeScale::Value(double time)
{
fResult = fChannelIn->Value(fTimeSource->Value(time));
return fResult;
}
// Detach ---------------------------------------------------
// -------
plAGChannel * plPointTimeScale::Detach(plAGChannel * channel)
{
plAGChannel *result = this;
fChannelIn = plPointChannel::ConvertNoRef(fChannelIn->Detach(channel));
if(!fChannelIn || channel == this)
result = nil;
if(result != this)
delete this;
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plPointBlend
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------------
// -----
plPointBlend::plPointBlend()
: fPointA(nullptr),
fPointB(nullptr),
fChannelBias(nullptr)
{
}
// ctor ---------------------------------------------------------------------------------
// -----
plPointBlend::plPointBlend(plPointChannel *channelA, plPointChannel *channelB,
plScalarChannel *channelBias)
: fPointA(channelA),
fPointB(channelB),
fChannelBias(channelBias)
{
}
// dtor ---------------------
// -----
plPointBlend::~plPointBlend()
{
fPointA = nil;
fPointB = nil;
fChannelBias = nil;
}
// IsStoppedAt ------------------------------
// ------------
bool plPointBlend::IsStoppedAt(double time)
{
float blend = fChannelBias->Value(time);
if (blend == 0)
return fPointA->IsStoppedAt(time);
if (blend == 1)
return fPointB->IsStoppedAt(time);
return (fPointA->IsStoppedAt(time) && fPointB->IsStoppedAt(time));
}
// Value ---------------------------------------
// ------
const hsPoint3 &plPointBlend::Value(double time)
{
if (fPointA && fPointB)
{
float curBlend = fChannelBias->Value(time);
if(curBlend == 0) {
fPointA->Value(fResult, time);
} else {
if(curBlend == 1) {
fPointB->Value(fResult, time);
} else {
const hsPoint3 &pointA = fPointA->Value(time);
const hsPoint3 &pointB = fPointB->Value(time);
hsPoint3 difference = pointB - pointA;
difference *= curBlend;
fResult = pointA + difference;
}
}
} else {
if (fPointA) {
fResult = fPointA->Value(time);
} else if (fPointB) {
fResult = fPointB->Value(time);
}
}
return fResult;
}
// Detach ------------------------------------------------------
// -------
plAGChannel * plPointBlend::Detach(plAGChannel *remove)
{
plAGChannel *result = this;
if (remove == this)
{
result = nil;
} else {
// it's possible that the incoming channel could reside down *all* of our
// branches (it's a graph, not a tree,) so we always pass down all limbs
fChannelBias = plScalarChannel::ConvertNoRef(fChannelBias->Detach(remove));
fPointA = plPointChannel::ConvertNoRef(fPointA->Detach(remove));
fPointB = plPointChannel::ConvertNoRef(fPointB->Detach(remove));
if (!fChannelBias)
{
// No more bias channel, assume it's zero from now on, (a.k.a. We just want channelA)
result = fPointA;
}
else
{
if(!fChannelBias)
result = fPointA;
else if(fPointA && !fPointB)
result = fPointA;
else if(fPointB && !fPointA)
result = fPointB;
else if(!fPointA && !fPointB)
result = nil;
if(result != this)
{
delete this;
}
}
}
return result;
}
///////////////////////////
// PLPOINTCONTROLLERCHANNEL
///////////////////////////
// CTOR
plPointControllerChannel::plPointControllerChannel()
: fController(nil)
{
}
// CTOR(name, controller)
plPointControllerChannel::plPointControllerChannel(plController *controller)
: fController(controller)
{
}
// ~DTOR()
plPointControllerChannel::~plPointControllerChannel()
{
if(fController) {
delete fController;
fController = nil;
}
}
// VALUE(time)
const hsPoint3 & plPointControllerChannel::Value(double time)
{
return Value(time, nil);
}
// VALUE(time)
const hsPoint3 & plPointControllerChannel::Value(double time, plControllerCacheInfo *cache)
{
fController->Interp((float)time, &fResult, cache);
return fResult;
}
plAGChannel *plPointControllerChannel::MakeCacheChannel(plAnimTimeConvert *atc)
{
plControllerCacheInfo *cache = fController->CreateCache();
cache->SetATC(atc);
return new plPointControllerCacheChannel(this, cache);
}
// WRITE(stream, mgr)
void plPointControllerChannel::Write(hsStream *stream, hsResMgr *mgr)
{
plPointChannel::Write(stream, mgr);
hsAssert(fController, "Trying to write plPointControllerChannel with nil controller. File will not be importable.");
mgr->WriteCreatable(stream, fController);
}
// READ(stream, mgr)
void plPointControllerChannel::Read(hsStream *stream, hsResMgr *mgr)
{
plPointChannel::Read(stream, mgr);
fController = plController::ConvertNoRef(mgr->ReadCreatable(stream));
}
/////////////////////////////////
// PLPOINTCONTROLLERCACHECHANNEL
/////////////////////////////////
// CTOR
plPointControllerCacheChannel::plPointControllerCacheChannel()
: fControllerChannel(nil),
fCache(nil)
{
}
// CTOR(name, controller)
plPointControllerCacheChannel::plPointControllerCacheChannel(plPointControllerChannel *controller, plControllerCacheInfo *cache)
: fControllerChannel(controller),
fCache(cache)
{
}
// ~DTOR()
plPointControllerCacheChannel::~plPointControllerCacheChannel()
{
delete fCache;
fControllerChannel = nil;
}
// VALUE(time)
const hsPoint3 & plPointControllerCacheChannel::Value(double time, bool peek)
{
return fControllerChannel->Value(time, fCache);
}
// DETACH
plAGChannel * plPointControllerCacheChannel::Detach(plAGChannel * channel)
{
if(channel == this)
{
return nil;
} else {
plAGChannel *result = fControllerChannel->Detach(channel);
if(result == fControllerChannel)
{
return this;
} else {
// if our controller channel has been detached, then detach ourselves as well.
return result;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// Channel applicators
void plPointChannelApplicator::IApply(const plAGModifier *modifier, double time)
{
plPointChannel *pointChan = plPointChannel::ConvertNoRef(fChannel);
hsAssert(pointChan, "Invalid channel given to plPointChannelApplicator");
plCoordinateInterface *CI = IGetCI(modifier);
hsMatrix44 l2p = CI->GetLocalToParent();
const hsPoint3 &point = pointChan->Value(time);
l2p.SetTranslate(&point);
hsMatrix44 p2l;
l2p.GetInverse(&p2l);
CI->SetLocalToParent(l2p, p2l);
}
void plLightDiffuseApplicator::IApply(const plAGModifier *modifier, double time)
{
plPointChannel *pointChan = plPointChannel::ConvertNoRef(fChannel);
hsAssert(pointChan, "Invalid channel given to plLightDiffuseApplicator");
plLightInfo *li = plLightInfo::ConvertNoRef(IGetGI(modifier, plLightInfo::Index()));
const hsPoint3 &point = pointChan->Value(time);
hsColorRGBA color;
color.Set(point.fX, point.fY, point.fZ, 1.0f);
li->SetDiffuse(color);
}
void plLightAmbientApplicator::IApply(const plAGModifier *modifier, double time)
{
plPointChannel *pointChan = plPointChannel::ConvertNoRef(fChannel);
hsAssert(pointChan, "Invalid channel given to plLightAmbientApplicator");
plLightInfo *li = plLightInfo::ConvertNoRef(IGetGI(modifier, plLightInfo::Index()));
const hsPoint3 &point = pointChan->Value(time);
hsColorRGBA color;
color.Set(point.fX, point.fY, point.fZ, 1.0f);
li->SetAmbient(color);
}
void plLightSpecularApplicator::IApply(const plAGModifier *modifier, double time)
{
plPointChannel *pointChan = plPointChannel::ConvertNoRef(fChannel);
hsAssert(pointChan, "Invalid channel given to plLightSpecularApplicator");
plLightInfo *li = plLightInfo::ConvertNoRef(IGetGI(modifier, plLightInfo::Index()));
const hsPoint3 &point = pointChan->Value(time);
hsColorRGBA color;
color.Set(point.fX, point.fY, point.fZ, 1.0f);
li->SetSpecular(color);
}

View File

@ -0,0 +1,302 @@
/*==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 <http://www.gnu.org/licenses/>.
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 PLPOINTCHANNEL_H
#define PLPOINTCHANNEL_H
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
#include "plAGApplicator.h"
#include "plAGChannel.h"
#include "hsGeometry3.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// FORWARDS
//
/////////////////////////////////////////////////////////////////////////////////////////
class plController;
class plControllerCacheInfo;
/////////////////////////////////////////////////////////////////////////////////////////
//
// DEFINITIONS
//
/////////////////////////////////////////////////////////////////////////////////////////
// PLPOINTCHANNEL
/////////////////
// A source of animated hsPoint3 data
class plPointChannel : public plAGChannel
{
protected:
hsPoint3 fResult;
public:
plPointChannel();
virtual ~plPointChannel();
// AG PROTOCOL
virtual const hsPoint3 & Value(double time);
virtual void Value(hsPoint3 &point, double time);
// combine it (allocates combine object)
virtual plAGChannel * MakeCombine(plAGChannel * channelB);
// blend it (allocates blend object)
virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority);
// const eval at time zero
virtual plAGChannel * MakeZeroState();
// make a timeScale instance
virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plPointChannel );
GETINTERFACE_ANY( plPointChannel, plAGChannel );
};
//////////////////
// PLPOINTCONSTANT
//////////////////
// A point source that just keeps handing out the same point
class plPointConstant : public plPointChannel
{
public:
plPointConstant();
plPointConstant(const hsPoint3 &point);
virtual ~plPointConstant();
void Set(const hsPoint3 &the_Point) { fResult = the_Point; }
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plPointConstant );
GETINTERFACE_ANY( plPointConstant, plPointChannel );
void Read(hsStream *stream, hsResMgr *mgr);
void Write(hsStream *stream, hsResMgr *mgr);
};
////////////////////
// PLPOINTTIMESCALE
////////////////////
// Adapts the time scale before passing it to the next channel in line.
// Use to instance animations while allowing each instance to run at different speeds.
class plPointTimeScale : public plPointChannel
{
protected:
plScalarChannel *fTimeSource;
plPointChannel *fChannelIn;
public:
plPointTimeScale();
plPointTimeScale(plPointChannel *channel, plScalarChannel *timeSource);
virtual ~plPointTimeScale();
virtual bool IsStoppedAt(double time);
virtual const hsPoint3 & Value(double time);
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plPointTimeScale );
GETINTERFACE_ANY( plPointTimeScale, plPointChannel );
};
// PLPOINTBLEND
///////////////
// Combines two point sources into one
class plPointBlend : public plPointChannel
{
public:
plPointBlend();
plPointBlend(plPointChannel *channelA, plPointChannel *channelB, plScalarChannel *channelBias);
virtual ~plPointBlend();
plAGChannel * Remove(plAGChannel *srceToRemove);
const plPointChannel * GetPointChannelA() const { return fPointA; }
void SetPointChannelA(plPointChannel *the_PointA) { fPointA = the_PointA; }
const plPointChannel * GetPointChannelB() const { return fPointB; }
void SetPointChannelB(plPointChannel *the_PointB) { fPointB = the_PointB; }
const plScalarChannel * GetChannelBias() const { return fChannelBias; }
void SetChannelBias(plScalarChannel * channel) { fChannelBias = channel; }
//float GetBlend() const { return fBlend; }
//void SetBlend(float the_blend) { fBlend = the_blend; }
virtual bool IsStoppedAt(double time);
// AG PROTOCOL
virtual const hsPoint3 &Value(double time);
// remove the specified channel from our graph
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plPointBlend );
GETINTERFACE_ANY( plPointBlend, plPointChannel );
protected:
plPointChannel *fPointA;
plPointChannel *fPointB;
plScalarChannel *fChannelBias;
};
// BLENDPOINTS
// Handy little function to share with others.
hsPoint3 BlendPoints(hsPoint3 &pointA, hsPoint3 &pointB, float blend);
///////////////////////////
// PLPOINTCONTROLLERCHANNEL
///////////////////////////
// converts a plController-style animation into a plPointChannel
class plPointControllerChannel : public plPointChannel
{
protected:
plController *fController;
public:
// xTORs
plPointControllerChannel();
plPointControllerChannel(plController *controller);
virtual ~plPointControllerChannel();
// AG PROTOCOL
virtual const hsPoint3 & Value(double time);
virtual const hsPoint3 & Value(double time, plControllerCacheInfo *cache);
virtual plAGChannel * MakeCacheChannel(plAnimTimeConvert *atc);
// PLASMA PROTOCOL
// rtti
CLASSNAME_REGISTER( plPointControllerChannel );
GETINTERFACE_ANY( plPointControllerChannel, plPointChannel );
// persistence
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream *s, hsResMgr *mgr);
};
////////////////////////////////
// PLPOINTCONTROLLERCACHECHANNEL
////////////////////////////////
// Same as plPointController, but with caching info
class plPointControllerCacheChannel : public plPointChannel
{
protected:
plControllerCacheInfo *fCache;
plPointControllerChannel *fControllerChannel;
public:
plPointControllerCacheChannel();
plPointControllerCacheChannel(plPointControllerChannel *channel, plControllerCacheInfo *cache);
virtual ~plPointControllerCacheChannel();
virtual const hsPoint3 & Value(double time, bool peek = false);
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plPointControllerCacheChannel );
GETINTERFACE_ANY( plPointControllerCacheChannel, plPointChannel );
// Created at runtime only, so no Read/Write
};
////////////////////////////
//
// Channel Applicator classes
class plPointChannelApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plPointChannelApplicator );
GETINTERFACE_ANY( plPointChannelApplicator, plAGApplicator );
};
class plLightDiffuseApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plLightDiffuseApplicator );
GETINTERFACE_ANY( plLightDiffuseApplicator, plAGApplicator );
};
class plLightAmbientApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plLightAmbientApplicator );
GETINTERFACE_ANY( plLightAmbientApplicator, plAGApplicator );
};
class plLightSpecularApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plLightSpecularApplicator );
GETINTERFACE_ANY( plLightSpecularApplicator, plAGApplicator );
};
#endif // PLPOINTCHANNEL_H

View File

@ -0,0 +1,326 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
#include "plQuatChannel.h"
#include "plPointChannel.h"
#include "plMatrixChannel.h"
#include "pnSceneObject/plDrawInterface.h"
#include "pnSceneObject/plSimulationInterface.h"
#include "pnSceneObject/plCoordinateInterface.h"
#include "pnSceneObject/plAudioInterface.h"
#include "plInterp/plAnimTimeConvert.h"
#include "hsMatrix44.h"
////////////////
// PLQUATCHANNEL
////////////////
// CTOR
plQuatChannel::plQuatChannel()
: plAGChannel()
{
fResult.Identity();
}
// DTOR
plQuatChannel::~plQuatChannel()
{
}
// VALUE (time)
const hsQuat &plQuatChannel::Value(double time)
{
return fResult;
}
// VALUE (quaternion, time)
void plQuatChannel::Value(hsQuat &quat, double time)
{
quat = Value(time);
}
// CANCOMBINE
bool plQuatChannel::CanCombine(plAGChannel *channelA)
{
if(plPointChannel::ConvertNoRef(channelA))
return true;
else
return false;
}
// MAKECOMBINE
plAGChannel * plQuatChannel::MakeCombine(plAGChannel *channelA)
{
plPointChannel* channel = plPointChannel::ConvertNoRef(channelA);
if(channel)
return new plQuatPointCombine(this, channel);
else
return nullptr;
}
// MAKEBLEND
plAGChannel *plQuatChannel::MakeBlend(plAGChannel *channelB, plScalarChannel *channelBias, int blendPriority)
{
plQuatChannel *chanB = plQuatChannel::ConvertNoRef(channelB);
plScalarChannel *chanBias = plScalarChannel::ConvertNoRef(channelBias);
if(chanB && chanBias)
{
return new plQuatBlend(this, chanB, chanBias);
} else {
hsStatusMessageF("Blend operation failed.");
return this;
}
}
// MAKEZEROSTATE
plAGChannel * plQuatChannel::MakeZeroState()
{
return new plQuatConstant(Value(0));
}
// MAKETIMESCALE
plAGChannel * plQuatChannel::MakeTimeScale(plScalarChannel *timeSource)
{
return new plQuatTimeScale(this, timeSource);
}
/////////////////
// PLQUATCONSTANT
/////////////////
// CTOR
plQuatConstant::plQuatConstant()
: plQuatChannel()
{
}
// CTOR(name, quaternion)
plQuatConstant::plQuatConstant(const hsQuat &quaternion)
{
fResult = quaternion;
}
// DTOR
plQuatConstant::~plQuatConstant()
{
}
void plQuatConstant::Read(hsStream *stream, hsResMgr *mgr)
{
plQuatChannel::Read(stream, mgr);
fResult.Read(stream);
}
void plQuatConstant::Write(hsStream *stream, hsResMgr *mgr)
{
plQuatChannel::Write(stream, mgr);
fResult.Write(stream);
}
////////////////////
// PLQUATTIMESCALE
////////////////////
// Insert into the graph when you need to change the speed or direction of time
// Also serves as a handy instancing node, since it just passes its data through.
// CTOR
plQuatTimeScale::plQuatTimeScale()
: fTimeSource(nil),
fChannelIn(nil)
{
}
// CTOR (channel, converter)
plQuatTimeScale::plQuatTimeScale(plQuatChannel *channel, plScalarChannel *timeSource)
: fChannelIn(channel),
fTimeSource(timeSource)
{
}
// DTOR
plQuatTimeScale::~plQuatTimeScale()
{
}
bool plQuatTimeScale::IsStoppedAt(double time)
{
return fTimeSource->IsStoppedAt(time);
}
// VALUE
const hsQuat & plQuatTimeScale::Value(double time)
{
fResult = fChannelIn->Value(fTimeSource->Value(time));
return fResult;
}
// DETACH
plAGChannel * plQuatTimeScale::Detach(plAGChannel * channel)
{
plAGChannel *result = this;
fChannelIn = plQuatChannel::ConvertNoRef(fChannelIn->Detach(channel));
if(!fChannelIn || channel == this)
result = nil;
if (result != this)
delete this;
return result;
}
//////////////
// PLQUATBLEND
//////////////
// CTOR
plQuatBlend::plQuatBlend()
: fQuatA(nil),
fQuatB(nil),
fChannelBias(nil)
{
}
// CTOR(channelA, channelB, blend)
plQuatBlend::plQuatBlend(plQuatChannel *channelA, plQuatChannel *channelB, plScalarChannel *channelBias)
: fQuatA(channelA),
fQuatB(channelB),
fChannelBias(channelBias)
{
}
// DTOR
plQuatBlend::~plQuatBlend()
{
//if (fQuatA) delete fQuatA;
//if (fQuatB) delete fQuatB;
fQuatA = fQuatB = nil;
fChannelBias = nil;
}
bool plQuatBlend::IsStoppedAt(double time)
{
float blend = fChannelBias->Value(time);
if (blend == 0)
return fQuatA->IsStoppedAt(time);
if (blend == 1)
return fQuatB->IsStoppedAt(time);
return (fQuatA->IsStoppedAt(time) && fQuatB->IsStoppedAt(time));
}
// VALUE(time)
const hsQuat &plQuatBlend::Value(double time)
{
hsQuat quatA = fQuatA->Value(time);
hsQuat quatB = fQuatB->Value(time);
fResult.SetFromSlerp(quatA, quatB, fChannelBias->Value(time));
return fResult;
}
// REMOVE
// Remove the given channel wherever it may be in the graph (including this node)
plAGChannel * plQuatBlend::Detach(plAGChannel *remove)
{
plAGChannel *result = this;
hsAssert(remove != this, "Cannot remove blenders explicitly. Remove blended source instead.");
if (remove != this)
{
fChannelBias = plScalarChannel::ConvertNoRef(fChannelBias->Detach(remove));
if (!fChannelBias)
{
// No more bias channel, assume it's zero from now on, (a.k.a. We just want channelA)
result = fQuatA;
}
else
{
fQuatA = (plQuatChannel *)fQuatA->Detach(remove);
if(fQuatA)
{
// channel a still here(although children may be gone); try channel b
fQuatB = (plQuatChannel *)fQuatB->Detach(remove);
if(!fQuatB)
{
result = fQuatA; // channel b is gone: return channel a as blender's replacement
}
} else {
result = fQuatB; // channel a is gone: return channel b
}
if (result != this)
{
delete this; // lost one of our channels: kill the blender.
}
}
}
return result;
}
//////////////////////////////////////////////////////////////////////////////////////
// Applicators
void plQuatChannelApplicator::IApply(const plAGModifier *mod, double time)
{
plQuatChannel *quatChan = plQuatChannel::ConvertNoRef(fChannel);
hsAssert(quatChan, "Invalid channel in plQuatChannelApplicator");
const hsQuat &rotate = quatChan->Value(time);
plCoordinateInterface *CI = IGetCI(mod);
hsMatrix44 l2w;
hsMatrix44 w2l;
rotate.MakeMatrix(&l2w);
l2w.GetInverse(&w2l);
CI->SetLocalToParent(l2w, w2l);
}

View File

@ -0,0 +1,187 @@
/*==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 <http://www.gnu.org/licenses/>.
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 PLQUATCHANNEL_INC
#define PLQUATCHANNEL_INC
#include "hsQuat.h"
#include "plAGChannel.h"
#include "plAGApplicator.h"
// PLQUATCHANNEL
/////////////
// A source of animated hsQuat data
class plQuatChannel : public plAGChannel
{
protected:
hsQuat fResult;
public:
plQuatChannel();
virtual ~plQuatChannel();
// AG PROTOCOL
virtual const hsQuat & Value(double time);
virtual void Value(hsQuat &quaternion, double time);
// can this channel combine with the given channel?
virtual bool CanCombine(plAGChannel * channelB);
// combine it (allocates combine object)
virtual plAGChannel * MakeCombine(plAGChannel * channelB);
// const eval at time zero
virtual plAGChannel * MakeZeroState();
// make a timeScale instance
virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource);
// blend it (allocates blend object)
virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plQuatChannel );
GETINTERFACE_ANY( plQuatChannel, plAGChannel );
};
// PLQUATCONSTANT
////////////
// A quat channel that just keeps handing out the same quaternion
class plQuatConstant : public plQuatChannel
{
public:
plQuatConstant();
plQuatConstant(const hsQuat &quaternion);
virtual ~plQuatConstant();
void Set(const hsQuat &the_Quat) { fResult = the_Quat; }
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plQuatConstant );
GETINTERFACE_ANY( plQuatConstant, plQuatChannel );
void Read(hsStream *stream, hsResMgr *mgr);
void Write(hsStream *stream, hsResMgr *mgr);
};
////////////////////
// PLQUATTIMESCALE
////////////////////
// Adapts the time scale before passing it to the next channel in line.
// Use to instance animations while allowing each instance to run at different speeds.
class plQuatTimeScale : public plQuatChannel
{
protected:
plScalarChannel *fTimeSource;
plQuatChannel *fChannelIn;
public:
plQuatTimeScale();
plQuatTimeScale(plQuatChannel *channel, plScalarChannel *timeSource);
virtual ~plQuatTimeScale();
virtual bool IsStoppedAt(double time);
virtual const hsQuat & Value(double time);
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plQuatTimeScale );
GETINTERFACE_ANY( plQuatTimeScale, plQuatChannel );
};
// PLQUATBLEND
//////////////
// Combines two quaternion sources into one
class plQuatBlend : public plQuatChannel
{
public:
plQuatBlend();
plQuatBlend(plQuatChannel *channelA, plQuatChannel *channelB, plScalarChannel *channelBias);
virtual ~plQuatBlend();
// GETTERS AND SETTERS FOR CHANNELS
const plQuatChannel * GetQuatA() const { return fQuatA; }
void SetQuatA(plQuatChannel *the_QuatA) { fQuatA = the_QuatA; }
const plQuatChannel * GetQuatB() const { return fQuatB; }
void SetQuatB(plQuatChannel *the_QuatB) { fQuatB = the_QuatB; }
const plScalarChannel * GetChannelBias() const { return fChannelBias; }
void SetChannelBias(plScalarChannel *channel) { fChannelBias = channel; }
//float GetBlend() const { return fBlend; }
//void SetBlend(float the_blend) { fBlend = the_blend; }
virtual bool IsStoppedAt(double time);
// AG PROTOCOL
virtual const hsQuat &Value(double time);
// remove the specified channel from our graph
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plQuatBlend );
GETINTERFACE_ANY( plQuatBlend, plQuatChannel );
protected:
plQuatChannel *fQuatA;
plQuatChannel *fQuatB;
plScalarChannel *fChannelBias;
};
////////////////////////////
//
// Channel Applicator classes
class plQuatChannelApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plQuatChannelApplicator );
GETINTERFACE_ANY( plQuatChannelApplicator, plAGApplicator );
};
#endif // PLQUATCHANNEL_INC

View File

@ -0,0 +1,606 @@
/*==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 <http://www.gnu.org/licenses/>.
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==*/
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
// singular
#include "plScalarChannel.h"
// global
#include "hsResMgr.h"
// other
#include "plGLight/plLightInfo.h"
#include "plInterp/plController.h"
#include "plInterp/plAnimTimeConvert.h"
#include "plSDL/plSDL.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// plScalarChannel
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------------------
// -----
plScalarChannel::plScalarChannel()
: plAGChannel()
{
}
// dtor ---------------------------
// -----
plScalarChannel::~plScalarChannel()
{
}
// value --------------------------------------------------------
// ------
const float & plScalarChannel::Value(double time, bool peek)
{
return fResult;
}
// value --------------------------------------------------------------
// ------
void plScalarChannel::Value(float &scalar, double time, bool peek)
{
scalar = Value(time, peek);
}
// MakeCombine -----------------------------------------------
// ------------
plAGChannel * plScalarChannel::MakeCombine(plAGChannel *other)
{
return nil;
}
// MakeBlend ---------------------------------------------------
// ----------
plAGChannel * plScalarChannel::MakeBlend(plAGChannel * channelB,
plScalarChannel * channelBias,
int blendPriority)
{
plScalarChannel * chanB = plScalarChannel::ConvertNoRef(channelB);
plScalarChannel * chanBias = plScalarChannel::ConvertNoRef(channelBias);
plAGChannel * result = this;
if (chanB)
{
result = new plScalarBlend(this, chanB, chanBias);
} else {
hsStatusMessage("Blend operation failed.");
}
return result;
}
// MakeZeroState -----------------------------
// --------------
plAGChannel * plScalarChannel::MakeZeroState()
{
return new plScalarConstant(Value(0));
}
// MakeTimeScale --------------------------------------------------------
// --------------
plAGChannel * plScalarChannel::MakeTimeScale(plScalarChannel *timeSource)
{
return new plScalarTimeScale(this, timeSource);
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plScalarConstant
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor ----------------------------
// -----
plScalarConstant::plScalarConstant()
{
}
// ctor ------------------------------------------
// -----
plScalarConstant::plScalarConstant(float value)
{
fResult = value;
}
// dtor -----------------------------
// -----
plScalarConstant::~plScalarConstant()
{
}
void plScalarConstant::Read(hsStream *stream, hsResMgr *mgr)
{
plScalarChannel::Read(stream, mgr);
fResult = stream->ReadLEScalar();
}
void plScalarConstant::Write(hsStream *stream, hsResMgr *mgr)
{
plScalarChannel::Write(stream, mgr);
stream->WriteLEScalar(fResult);
}
////////////////////
// PLSCALARTIMESCALE
////////////////////
// Insert into the graph when you need to change the speed or direction of time
// Also serves as a handy instancing node, since it just passes its data through.
// CTOR
plScalarTimeScale::plScalarTimeScale()
: fTimeSource(nil),
fChannelIn(nil)
{
}
// CTOR (channel, converter)
plScalarTimeScale::plScalarTimeScale(plScalarChannel *channel, plScalarChannel *timeSource)
: fChannelIn(channel),
fTimeSource(timeSource)
{
}
// DTOR
plScalarTimeScale::~plScalarTimeScale()
{
}
bool plScalarTimeScale::IsStoppedAt(double time)
{
return fTimeSource->IsStoppedAt(time);
}
// VALUE
const float & plScalarTimeScale::Value(double time, bool peek)
{
fResult = fChannelIn->Value(fTimeSource->Value(time, peek));
return fResult;
}
// DETACH
plAGChannel * plScalarTimeScale::Detach(plAGChannel * detach)
{
plAGChannel *result = this;
fChannelIn = plScalarChannel::ConvertNoRef(fChannelIn->Detach(detach));
if(!fChannelIn || detach == this)
result = nil;
if(result != this)
delete this;
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plScalarBlend
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor ----------------------
// -----
plScalarBlend::plScalarBlend()
: fChannelA(nil),
fChannelB(nil),
fChannelBias(nil)
{
}
// ctor ----------------------------------------------------------------------------
// -----
plScalarBlend::plScalarBlend(plScalarChannel * channelA, plScalarChannel * channelB,
plScalarChannel * channelBias)
: fChannelA(channelA),
fChannelB(channelB),
fChannelBias(channelBias)
{
}
// dtor -----------------------
// -----
plScalarBlend::~plScalarBlend()
{
fChannelA = nil;
fChannelB = nil;
fChannelBias = nil;
}
// IsStoppedAt -------------------------------
// ------------
bool plScalarBlend::IsStoppedAt(double time)
{
float blend = fChannelBias->Value(time);
if (blend == 0)
return fChannelA->IsStoppedAt(time);
if (blend == 1)
return fChannelB->IsStoppedAt(time);
return (fChannelA->IsStoppedAt(time) && fChannelB->IsStoppedAt(time));
}
// Value ------------------------------------------------------
// ------
const float & plScalarBlend::Value(double time, bool peek)
{
float curBlend = fChannelBias->Value(time, peek);
if(curBlend == 0) {
fChannelA->Value(fResult, time, peek);
} else {
if(curBlend == 1) {
fChannelB->Value(fResult, time, peek);
} else {
const float &scalarA = fChannelA->Value(time, peek);
const float &scalarB = fChannelB->Value(time, peek);
fResult = scalarA + curBlend * (scalarB - scalarA);
}
}
return fResult;
}
// Detach ----------------------------------------------
// -------
plAGChannel * plScalarBlend::Detach(plAGChannel *remove)
{
plAGChannel *result = this;
// it's possible that the incoming channel could reside down *all* of our
// branches (it's a graph, not a tree,) so we always pass down all limbs
fChannelBias = plScalarChannel::ConvertNoRef(fChannelBias->Detach(remove));
fChannelA = plScalarChannel::ConvertNoRef(fChannelA->Detach(remove));
fChannelB = plScalarChannel::ConvertNoRef(fChannelB->Detach(remove));
if(!fChannelBias)
result = fChannelA;
else if(fChannelA && !fChannelB)
result = fChannelA;
else if(fChannelB && !fChannelA)
result = fChannelB;
else if(!fChannelA && !fChannelB)
result = nil;
if(result != this)
delete this;
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// PLSCALARCONTROLLERCHANNEL
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor ----------------------------------------------
// -----
plScalarControllerChannel::plScalarControllerChannel()
: fController(nil)
{
}
// ctor ----------------------------------------------------------------------------
// -----
plScalarControllerChannel::plScalarControllerChannel(plController *controller)
: fController(controller)
{
}
// dtor -----------------------------------------------
// -----
plScalarControllerChannel::~plScalarControllerChannel()
{
if(fController) {
delete fController;
fController = nil;
}
}
// Value ------------------------------------------------------------------
// ------
const float & plScalarControllerChannel::Value(double time, bool peek)
{
return Value(time, peek, nil);
}
// Value ------------------------------------------------------------------
// ------
const float & plScalarControllerChannel::Value(double time, bool peek,
plControllerCacheInfo *cache)
{
fController->Interp((float)time, &fResult, cache);
return fResult;
}
// MakeCacheChannel ------------------------------------------------------------
// -----------------
plAGChannel *plScalarControllerChannel::MakeCacheChannel(plAnimTimeConvert *atc)
{
plControllerCacheInfo *cache = fController->CreateCache();
cache->SetATC(atc);
return new plScalarControllerCacheChannel(this, cache);
}
// Write -------------------------------------------------------------
// ------
void plScalarControllerChannel::Write(hsStream *stream, hsResMgr *mgr)
{
plScalarChannel::Write(stream, mgr);
hsAssert(fController, "Trying to write plScalarControllerChannel with nil controller. File will not be importable.");
mgr->WriteCreatable(stream, fController);
}
// Read -------------------------------------------------------------
// -----
void plScalarControllerChannel::Read(hsStream *stream, hsResMgr *mgr)
{
plScalarChannel::Read(stream, mgr);
fController = plController::ConvertNoRef(mgr->ReadCreatable(stream));
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// PLSCALARCONTROLLERCACHECHANNEL
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------------------------------------------------
// -----
plScalarControllerCacheChannel::plScalarControllerCacheChannel()
: fControllerChannel(nil),
fCache(nil)
{
}
// ctor ---------------------------------------------------------------------------------------------
// -----
plScalarControllerCacheChannel::plScalarControllerCacheChannel(plScalarControllerChannel *controller,
plControllerCacheInfo *cache)
: fControllerChannel(controller),
fCache(cache)
{
}
// dtor ---------------------------------------------------------
// -----
plScalarControllerCacheChannel::~plScalarControllerCacheChannel()
{
delete fCache;
fControllerChannel = nil;
}
// Value ---------------------------------------------------------------------
// ------
const float & plScalarControllerCacheChannel::Value(double time, bool peek)
{
return fControllerChannel->Value(time, peek, fCache);
}
// Detach -----------------------------------------------------------------
// -------
plAGChannel * plScalarControllerCacheChannel::Detach(plAGChannel * channel)
{
plAGChannel *result = this;
if(channel == this)
{
result = nil;
} else {
fControllerChannel = plScalarControllerChannel::ConvertNoRef(fControllerChannel->Detach(channel));
if(!fControllerChannel)
result = nil;
}
if(result != this)
delete this;
return result;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// PLATCCHANNEL
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------------
plATCChannel::plATCChannel()
: fConvert(nil)
{
}
// ctor ----------------------------------------------
plATCChannel::plATCChannel(plAnimTimeConvert *convert)
: fConvert(convert)
{
}
// dtor ---------------------
plATCChannel::~plATCChannel()
{
}
// IsStoppedAt ------------------------------
// ------------
bool plATCChannel::IsStoppedAt(double time)
{
return fConvert->IsStoppedAt(time);
}
// Value -----------------------------------------------------
// ------
const float & plATCChannel::Value(double time, bool peek)
{
fResult = (peek ? fConvert->WorldToAnimTimeNoUpdate(time) : fConvert->WorldToAnimTime(time));
return fResult;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// PLSCALARSDLCHANNEL
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------------------------
// -----
plScalarSDLChannel::plScalarSDLChannel()
: fLength(1), fVar(nil)
{
fResult = 0;
}
plScalarSDLChannel::plScalarSDLChannel(float length)
: fLength(length), fVar(nil)
{
fResult = 0;
}
// dtor ---------------------------------
plScalarSDLChannel::~plScalarSDLChannel()
{
}
// IsStoppedAt ------------------------------------
// ------------
bool plScalarSDLChannel::IsStoppedAt(double time)
{
return false;
}
// Value -----------------------------------------------------------
// ------
const float & plScalarSDLChannel::Value(double time, bool peek)
{
if (fVar)
fVar->Get(&fResult);
// the var will return a 0-1 value, scale to match our anim length.
fResult *= fLength;
return fResult;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// APPLICATORS
//
/////////////////////////////////////////////////////////////////////////////////////////
// IApply ------------------------------------------------------------------
// -------
void plScalarChannelApplicator::IApply(const plAGModifier *mod, double time)
{
}
// IApply --------------------------------------------------------------
// -------
void plSpotInnerApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel);
hsAssert(scalarChan, "Invalid channel given to plSpotInnerApplicator");
plSpotLightInfo *sli = plSpotLightInfo::ConvertNoRef(IGetGI(mod, plSpotLightInfo::Index()));
const float &scalar = scalarChan->Value(time);
sli->SetSpotInner(hsDegreesToRadians(scalar)*0.5f);
}
// IApply --------------------------------------------------------------
// -------
void plSpotOuterApplicator::IApply(const plAGModifier *mod, double time)
{
plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel);
hsAssert(scalarChan, "Invalid channel given to plSpotInnerApplicator");
plSpotLightInfo *sli = plSpotLightInfo::ConvertNoRef(IGetGI(mod, plSpotLightInfo::Index()));
const float &scalar = scalarChan->Value(time);
sli->SetSpotOuter(hsDegreesToRadians(scalar)*0.5f);
}
void plOmniApplicator::IApply(const plAGModifier *modifier, double time)
{
plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel);
hsAssert(scalarChan, "Invalid channel given to plLightOmniApplicator");
plOmniLightInfo *oli = plOmniLightInfo::ConvertNoRef(IGetGI(modifier, plOmniLightInfo::Index()));
oli->SetLinearAttenuation(scalarChan->Value(time));
}
void plOmniSqApplicator::IApply(const plAGModifier *modifier, double time)
{
plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel);
hsAssert(scalarChan, "Invalid channel given to plLightOmniApplicator");
plOmniLightInfo *oli = plOmniLightInfo::ConvertNoRef(IGetGI(modifier, plOmniLightInfo::Index()));
oli->SetQuadraticAttenuation(scalarChan->Value(time));
}
void plOmniCutoffApplicator::IApply(const plAGModifier *modifier, double time)
{
plScalarChannel *scalarChan = plScalarChannel::ConvertNoRef(fChannel);
hsAssert(scalarChan, "Invalid channel given to plOmniCutoffApplicator");
plOmniLightInfo *oli = plOmniLightInfo::ConvertNoRef(IGetGI(modifier, plOmniLightInfo::Index()));
oli->SetCutoffAttenuation( scalarChan->Value( time ) );
}

View File

@ -0,0 +1,362 @@
/*==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 <http://www.gnu.org/licenses/>.
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 PLSCALARCHANNEL_INC
#define PLSCALARCHANNEL_INC
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
// base
#include "plAGChannel.h"
#include "plAGApplicator.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// FORWARDS
//
/////////////////////////////////////////////////////////////////////////////////////////
class plController;
class plAnimTimeConvert;
class plSimpleStateVariable;
class plControllerCacheInfo;
/////////////////////////////////////////////////////////////////////////////////////////
//
// DEFINITIONS
//
/////////////////////////////////////////////////////////////////////////////////////////
//////////////////
// PLSCALARCHANNEL
//////////////////
// an animation channel that outputs a scalar value
class plScalarChannel : public plAGChannel
{
protected:
float fResult;
public:
plScalarChannel();
virtual ~plScalarChannel();
// AG PROTOCOL
virtual const float & Value(double time, bool peek = false);
virtual void Value(float &result, double time, bool peek = false);
// combine it (allocates combine object)
virtual plAGChannel * MakeCombine(plAGChannel * channelB);
// blend it (allocates blend object)
virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * channelBias, int blendPriority);
// const eval at time zero
virtual plAGChannel * MakeZeroState();
// make a timeScale instance
virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plScalarChannel );
GETINTERFACE_ANY( plScalarChannel, plAGChannel );
};
///////////////////
// PLSCALARCONSTANT
///////////////////
// A scalar source that just keeps handing out the same value
class plScalarConstant : public plScalarChannel
{
public:
plScalarConstant();
plScalarConstant(float value);
virtual ~plScalarConstant();
void Set(float value) { fResult = value; }
float Get() { return fResult; }
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plScalarConstant );
GETINTERFACE_ANY( plScalarConstant, plScalarChannel );
void Read(hsStream *stream, hsResMgr *mgr);
void Write(hsStream *stream, hsResMgr *mgr);
};
////////////////////
// PLSCALARTIMESCALE
////////////////////
// Adapts the time scale before passing it to the next channel in line.
// Use to instance animations while allowing each instance to run at different speeds.
class plScalarTimeScale : public plScalarChannel
{
protected:
plScalarChannel *fTimeSource;
plScalarChannel *fChannelIn;
public:
plScalarTimeScale();
plScalarTimeScale(plScalarChannel *channel, plScalarChannel *timeSource);
virtual ~plScalarTimeScale();
virtual bool IsStoppedAt(double time);
virtual const float & Value(double time, bool peek = false);
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plScalarTimeScale );
GETINTERFACE_ANY( plScalarTimeScale, plScalarChannel );
};
////////////////
// PLSCALARBLEND
////////////////
// blends two scalars into one with weighting
class plScalarBlend : public plScalarChannel
{
protected:
plScalarChannel * fChannelA;
plScalarChannel * fChannelB;
plScalarChannel * fChannelBias;
public:
// xTORs
plScalarBlend();
plScalarBlend(plScalarChannel * channelA, plScalarChannel * channelB, plScalarChannel * channelBias);
virtual ~plScalarBlend();
// SPECIFICS
const plScalarChannel * GetChannelA() const { return fChannelA; }
void SetChannelA(plScalarChannel * channel) { fChannelA = channel; }
const plScalarChannel * GetChannelB() const { return fChannelB; }
void SetChannelB(plScalarChannel * channel) { fChannelB = channel; }
const plScalarChannel * GetChannelBias() const { return fChannelBias; }
void SetChannelBias(plScalarChannel * channel) { fChannelBias = channel; }
virtual bool IsStoppedAt(double time);
// AG PROTOCOL
virtual const float & Value(double time, bool peek = false);
// remove the specified channel from our graph
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plScalarBlend );
GETINTERFACE_ANY( plScalarBlend, plScalarChannel );
};
////////////////////////////
// PLSCALARCONTROLLERCHANNEL
////////////////////////////
// converts a plController-style animation into a plScalarChannel
class plScalarControllerChannel : public plScalarChannel
{
protected:
plController *fController;
public:
// xTORs
plScalarControllerChannel();
plScalarControllerChannel(plController *controller);
virtual ~plScalarControllerChannel();
// AG PROTOCOL
virtual const float & Value(double time, bool peek = false);
virtual const float & Value(double time, bool peek, plControllerCacheInfo *cache);
virtual plAGChannel *MakeCacheChannel(plAnimTimeConvert *atc);
// PLASMA PROTOCOL
// rtti
CLASSNAME_REGISTER( plScalarControllerChannel );
GETINTERFACE_ANY( plScalarControllerChannel, plScalarChannel );
// persistence
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream *s, hsResMgr *mgr);
};
/////////////////////////////////
// PLSCALARCONTROLLERCACHECHANNEL
/////////////////////////////////
// Same as plScalarController, but with caching info
class plScalarControllerCacheChannel : public plScalarChannel
{
protected:
plControllerCacheInfo *fCache;
plScalarControllerChannel *fControllerChannel;
public:
plScalarControllerCacheChannel();
plScalarControllerCacheChannel(plScalarControllerChannel *channel, plControllerCacheInfo *cache);
virtual ~plScalarControllerCacheChannel();
virtual const float & Value(double time, bool peek = false);
virtual plAGChannel * Detach(plAGChannel * channel);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plScalarControllerCacheChannel );
GETINTERFACE_ANY( plScalarControllerCacheChannel, plScalarChannel );
// Created at runtime only, so no Read/Write
};
////////////////////
// PLATCChannel
////////////////////
// Channel interface for a plAnimTimeConvert object
class plATCChannel : public plScalarChannel
{
protected:
plAnimTimeConvert *fConvert;
public:
plATCChannel();
plATCChannel(plAnimTimeConvert *convert);
virtual ~plATCChannel();
virtual bool IsStoppedAt(double time);
virtual const float & Value(double time, bool peek = false);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plATCChannel );
GETINTERFACE_ANY( plATCChannel, plScalarChannel );
};
////////////////////
// PLSCALARSDLCHANNEL
////////////////////
// Returns the value of an SDL scalar variable
class plScalarSDLChannel : public plScalarChannel
{
protected:
plSimpleStateVariable *fVar;
float fLength;
public:
plScalarSDLChannel();
plScalarSDLChannel(float length);
virtual ~plScalarSDLChannel();
virtual bool IsStoppedAt(double time);
virtual const float & Value(double time, bool peek = false);
void SetVar(plSimpleStateVariable *var) { fVar = var; }
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plScalarSDLChannel );
GETINTERFACE_ANY( plScalarSDLChannel, plScalarChannel );
};
////////////////////////////
//
// Channel Applicator classes
class plScalarChannelApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plScalarChannelApplicator );
GETINTERFACE_ANY( plScalarChannelApplicator, plAGApplicator );
};
class plSpotInnerApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plSpotInnerApplicator );
GETINTERFACE_ANY( plSpotInnerApplicator, plAGApplicator );
};
class plSpotOuterApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plSpotOuterApplicator );
GETINTERFACE_ANY( plSpotOuterApplicator, plAGApplicator );
};
class plOmniApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plOmniApplicator );
GETINTERFACE_ANY( plOmniApplicator, plAGApplicator );
};
class plOmniSqApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plOmniSqApplicator );
GETINTERFACE_ANY( plOmniSqApplicator, plAGApplicator );
};
class plOmniCutoffApplicator : public plAGApplicator
{
protected:
virtual void IApply(const plAGModifier *mod, double time);
public:
CLASSNAME_REGISTER( plOmniCutoffApplicator );
GETINTERFACE_ANY( plOmniCutoffApplicator, plAGApplicator );
};
#endif