/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ // 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; }