You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

807 lines
20 KiB

/*==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/>.
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 "plAGMasterSDLModifier.h"
#include "plMatrixChannel.h"
// global
#include "hsResMgr.h"
#include "plgDispatch.h"
// other
// #include "../pnMessage/plRefMsg.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->WriteSwap32(length); // backwards compatability. Nuke on next format change.
stream->WriteSwap32(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->ReadSwap32(); // Unused. Nuke next format change.
char *junk = TRACKED_NEW char[nameLength+1]; //
stream->Read(nameLength, junk); //
junk[nameLength] = 0; //
delete [] junk; //
//////////////////////////////////////////
int numPrivateAnims = stream->ReadSwap32();
fPrivateAnims.reserve(numPrivateAnims); // pre-allocate for performance
int i;
for (i = 0; i < numPrivateAnims; i++)
{
plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPrivateAnim);
mgr->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef);
}
fIsGrouped = stream->Readbool();
fIsGroupMaster = stream->Readbool();
if (fIsGroupMaster)
{
plGenRefMsg* msg = TRACKED_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 = TRACKED_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
hsBool plAGMasterMod::IEval(double secs, hsScalar del, UInt32 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, hsScalar elapsed)
{
plProfile_BeginLap(ApplyAnimation, this->GetKey()->GetUoid().GetObjectName());
// update any fades
for (int i = 0; i < fAnimInstances.size(); i++)
{
fAnimInstances[i]->ProcessFade(elapsed);
}
AdvanceAnimsToTime(time);
plProfile_EndLap(ApplyAnimation,this->GetKey()->GetUoid().GetObjectName());
}
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 || stricmp(justThisChannel, mod->GetChannelName()) == 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());
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 char * name, hsBool dontCache ) const
{
plAGModifier * result = nil;
std::map<const char *, plAGModifier *, stringSorter>::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 = TRACKED_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 char *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)
{
const char *modName = mod->GetChannelName();
if(stricmp(name, modName) == 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,
hsScalar blendFactor /* = 0 */,
UInt16 blendPriority /* plAGMedBlendPriority */,
hsBool 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 = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPublicAnim);
hsgResMgr::ResMgr()->SendRef(anim, msg, plRefFlags::kActiveRef);
}
instance = TRACKED_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 char *name, hsScalar blendFactor /* = 0 */, UInt16 blendPriority, hsBool cache /* = false */)
{
plAGAnimInstance *instance = nil;
plAGAnim *anim = plAGAnim::FindAnim(name);
if(anim)
{
instance = AttachAnimationBlended(anim, blendFactor, blendPriority, cache);
}
return instance;
}
void plAGMasterMod::PlaySimpleAnim(const char *name)
{
plATCAnim *anim = plATCAnim::ConvertNoRef(plAGAnim::FindAnim(name));
plAGAnimInstance *instance = nil;
if (anim)
{
if (FindAnimInstance(name))
return;
instance = AttachAnimationBlended(anim, 1.f, (UInt16)kAGMaxBlendPriority, false);
}
if (instance)
{
instance->SetLoop(false);
instance->Start();
plAGDetachCallbackMsg *msg = TRACKED_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 char *name)
{
plAGAnimInstance *result = nil;
if (name)
{
for (int i = 0; i < fAnimInstances.size(); i++)
{
plAGAnimInstance *act = fAnimInstances[i];
const char *eachName = act->GetName();
if( stricmp(eachName, name) == 0)
{
result = act;
break;
}
}
}
return result;
}
// FINDORATTACHINSTANCE
plAGAnimInstance * plAGMasterMod::FindOrAttachInstance(const char *name, hsScalar 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 char *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];
const char *name = inst->GetName();
float blend = inst->GetBlend();
hsStatusMessageF("%d: %s with blend of %f\n", i, name, blend);
}
}
// MSGRECEIVE
// receive trigger messages
hsBool plAGMasterMod::MsgReceive(plMessage* msg)
{
plAnimCmdMsg* cmdMsg;
plAGCmdMsg* agMsg;
int i;
plSDLNotificationMsg* nMsg = plSDLNotificationMsg::ConvertNoRef(msg);
if (nMsg)
{
// Force a single eval
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
return true;
}
if (cmdMsg = plAnimCmdMsg::ConvertNoRef(msg))
{
const char *targetName = cmdMsg->GetAnimName();
if (!targetName)
targetName = ENTIRE_ANIMATION_NAME;
plAGAnimInstance *inst = FindAnimInstance(targetName);
if (inst != nil)
{
if (cmdMsg->CmdChangesAnimTime())
{
for (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;
}
if (agMsg = plAGCmdMsg::ConvertNoRef(msg))
{
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 != nil)
{
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 != nil)
{
DetachAnimation(detachMsg->GetAnimName());
}
plGenRefMsg *genRefMsg;
if (genRefMsg = plGenRefMsg::ConvertNoRef(msg))
{
plAGAnim *anim;
if (anim = plAGAnim::ConvertNoRef(genRefMsg->GetRef()))
{
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; i != fPrivateAnims.end(); i++)
{
plAGAnim *currAnim = *i;
if(currAnim == anim)
{
i = fPrivateAnims.erase(i);
break;
}
}
}
}
return true;
}
plAGModifier *agmod;
if (agmod = plAGModifier::ConvertNoRef(genRefMsg->GetRef()))
{
if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
fChannelMods[agmod->GetChannelName()] = agmod;
else
fChannelMods.erase(agmod->GetChannelName());
return true;
}
plMsgForwarder *msgfwd;
if (msgfwd = plMsgForwarder::ConvertNoRef(genRefMsg->GetRef()))
{
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(hsBool val)
{
if (fNeedEval == val)
return;
fNeedEval = val;
if (val)
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
else
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
}
hsBool plAGMasterMod::HasRunningAnims()
{
int i;
hsBool 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
//
hsBool plAGMasterMod::DirtySynchState(const char* SDLStateName, UInt32 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;
}