/*==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 . 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 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_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(FindModifierByClass(SO, plAGModifier::Index())); plAGModifier * mod = const_cast(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; }