/*==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==*/ #include "HeadSpin.h" #include "plAnimEventComponent.h" #include "plComponentReg.h" #include "resource.h" #include "../MaxMain/plMaxNode.h" #include "plAnimComponent.h" #include "plNotetrackAnim.h" #include "plAnimCompProc.h" #include "plPickNode.h" #include "../plModifier/plAnimEventModifier.h" #include "../plMessage/plAnimCmdMsg.h" #include "hsResMgr.h" #include "../pnMessage/plRefMsg.h" void DummyCodeIncludeFuncAnimDetector() {} CLASS_DESC(plAnimEventComponent, gAnimEventDesc, "Anim Event", "AnimEvent", COMP_TYPE_DETECTOR, ANIMEVENT_CID) class plAnimEventProc : public plAnimCompProc { protected: virtual void IPickComponent(IParamBlock2* pb); virtual void ILoadUser(HWND hWnd, IParamBlock2* pb); virtual bool IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID); public: plAnimEventProc(); }; static plAnimEventProc gAnimEventProc; enum { kAnimComp, kAnimNode, kAnimEvent_DEAD, kAnimName_DEAD, kAnimBegin, kAnimEnd, kAnimMarkers, }; enum { kAnimEventBegin, kAnimEventEnd, kAnimEventMarker, }; ParamBlockDesc2 gAnimEventBlock ( plComponent::kBlkComp, _T("animEvent"), 0, &gAnimEventDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, IDD_COMP_DETECTOR_ANIM, IDS_COMP_DETECTOR_ANIM, 0, 0, &gAnimEventProc, kAnimComp, _T("animComp"), TYPE_INODE, 0, 0, end, kAnimNode, _T("animNode"), TYPE_INODE, 0, 0, end, kAnimBegin, _T("animBegin"), TYPE_BOOL, 0, 0, end, kAnimEnd, _T("animEnd"), TYPE_BOOL, 0, 0, end, kAnimMarkers, _T("animMarkers"), TYPE_STRING_TAB, 0, 0, 0, end, end ); plAnimEventComponent::plAnimEventComponent() : fCanExport(false) { fClassDesc = &gAnimEventDesc; fClassDesc->MakeAutoParamBlocks(this); } hsBool plAnimEventComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) { plComponentBase* animComp; plMaxNode* animNode; if (!gAnimEventProc.GetCompAndNode(fCompPB, animComp, animNode)) { pErrMsg->Set(true, "Anim Event Component", "Component %s does not have a valid anim component and node selected.\n" "It will not be exported.", GetINode()->GetName()).Show(); pErrMsg->Set(false); fCanExport = false; } else fCanExport = true; return plActivatorBaseComponent::SetupProperties(node, pErrMsg); } hsBool plAnimEventComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) { if (!fCanExport) return false; plAnimEventModifier* mod = TRACKED_NEW plAnimEventModifier; plKey modKey = node->AddModifier(mod, IGetUniqueName(node)); fLogicModKeys[node] = modKey; return true; } plEventCallbackMsg* CreateCallbackMsg(plAnimCmdMsg* animMsg, plKey modKey) { plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg; eventMsg->AddReceiver(modKey); eventMsg->fRepeats = -1; animMsg->AddCallback(eventMsg); hsRefCnt_SafeUnRef(eventMsg); // AddCallback adds it's own ref, so remove ours (the default of 1) return eventMsg; } hsBool plAnimEventComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) { if (!fCanExport) return false; plKey modKey = fLogicModKeys[node]; plAnimEventModifier* mod = plAnimEventModifier::ConvertNoRef(modKey->GetObjectPtr()); plAnimComponentBase* animComp; plMaxNode* animNode; gAnimEventProc.GetCompAndNode(fCompPB, (plComponentBase*&)animComp, animNode); // // Create and setup the callback message // plKey animKey = animComp->GetModKey(animNode); const char* animName = animComp->GetAnimName(); plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg; animMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); animMsg->SetSender(modKey); animMsg->SetAnimName(animName); animMsg->AddReceiver(animKey); if (fCompPB->GetInt(kAnimBegin)) { plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); eventMsg->fEvent = kBegin; } if (fCompPB->GetInt(kAnimEnd)) { plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); eventMsg->fEvent = kEnd; } if (fCompPB->Count(kAnimMarkers) > 0) { plNotetrackAnim anim(animComp, nil); plAnimInfo info = anim.GetAnimInfo(animName); int numMarkers = fCompPB->Count(kAnimMarkers); for (int i = 0; i < numMarkers; i++) { const char* marker = fCompPB->GetStr(kAnimMarkers, 0, i); float time = info.GetMarkerTime(marker); plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); eventMsg->fEvent = kTime; eventMsg->fEventTime = time; } } mod->SetCallback(animMsg); hsTArray receivers; IGetReceivers(node, receivers); mod->SetReceivers(receivers); return true; } //////////////////////////////////////////////////////////////////////////////// plAnimEventProc::plAnimEventProc() { fCompButtonID = IDC_ANIM_BUTTON; fCompParamID = kAnimComp; fNodeButtonID = IDC_NODE_BUTTON; fNodeParamID = kAnimNode; } void plAnimEventProc::IPickComponent(IParamBlock2* pb) { std::vector cids; cids.push_back(ANIM_COMP_CID); cids.push_back(ANIM_GROUP_COMP_CID); plPick::Node(pb, kAnimComp, &cids, true, false); } static int ListBox_AddStringData(HWND hList, const char* text, int data) { int idx = ListBox_AddString(hList, text); ListBox_SetItemData(hList, idx, data); return idx; } static bool IsMarkerSelected(IParamBlock2* pb, int paramID, const char* marker, bool remove=false) { int numMarkers = pb->Count(paramID); for (int i = 0; i < numMarkers; i++) { if (hsStrEQ(marker, pb->GetStr(paramID, 0, i))) { if (remove) pb->Delete(paramID, i, 1); return true; } } return false; } // // Remove markers we had saved that aren't in the object's notetrack any more // static void RemoveDeadMarkers(IParamBlock2* pb, int paramID, plAnimInfo& info) { int numMarkers = pb->Count(paramID); for (int i = numMarkers-1; i >= 0; i--) { float time = info.GetMarkerTime(pb->GetStr(paramID, 0, i)); if (time == -1) { pb->Delete(paramID, i, 1); } } } void plAnimEventProc::ILoadUser(HWND hWnd, IParamBlock2* pb) { HWND hList = GetDlgItem(hWnd, IDC_EVENT_LIST); ListBox_ResetContent(hList); plAnimComponentBase* comp; plMaxNode* node; // // If we don't have a valid comp and node, we should be disabled // if (!GetCompAndNode(pb, (plComponentBase*&)comp, node)) { EnableWindow(hList, FALSE); return; } else EnableWindow(hList, TRUE); // // Load the events // int idx; idx = ListBox_AddStringData(hList, "(Begin)", kAnimEventBegin); if (pb->GetInt(kAnimBegin)) ListBox_SetSel(hList, TRUE, idx); idx = ListBox_AddStringData(hList, "(End)", kAnimEventEnd); if (pb->GetInt(kAnimEnd)) ListBox_SetSel(hList, TRUE, idx); if (comp) { // Get the shared animations for all the nodes this component is applied to plNotetrackAnim anim(comp, nil); plAnimInfo info = anim.GetAnimInfo(comp->GetAnimName()); RemoveDeadMarkers(pb, kAnimMarkers, info); // Get all the markers in this animation while (const char* marker = info.GetNextMarkerName()) { idx = ListBox_AddStringData(hList, marker, kAnimEventMarker); if (IsMarkerSelected(pb, kAnimMarkers, marker)) ListBox_SetSel(hList, TRUE, idx); } } } bool plAnimEventProc::IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID) { if (cmd == LBN_SELCHANGE && resID == IDC_EVENT_LIST) { HWND hList = GetDlgItem(hWnd, IDC_EVENT_LIST); int idx = ListBox_GetCurSel(hList); BOOL selected = ListBox_GetSel(hList, idx); int eventType = ListBox_GetItemData(hList, idx); if (eventType == kAnimEventBegin) pb->SetValue(kAnimBegin, 0, selected); else if (eventType == kAnimEventEnd) pb->SetValue(kAnimEnd, 0, selected); else if (eventType == kAnimEventMarker) { char buf[256]; ListBox_GetText(hList, idx, buf); if (selected) { if (!IsMarkerSelected(pb, kAnimMarkers, buf)) { TCHAR* name = buf; pb->Append(kAnimMarkers, 1, &name); } } else IsMarkerSelected(pb, kAnimMarkers, buf, true); } return true; } return false; } /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// CLASS_DESC(plMtlEventComponent, gMtlEventDesc, "Mtl Event", "MtlEvent", COMP_TYPE_DETECTOR, MTLEVENT_CID) class plMtlEventProc : public plMtlAnimProc { public: plMtlEventProc(); protected: virtual void ILoadUser(HWND hWnd, IParamBlock2* pb); virtual bool IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID); }; static plMtlEventProc gMtlEventProc; enum { kMtlMtl, kMtlNode, kMtlAnim, kMtlBegin, kMtlEnd, kMtlMarkers, }; ParamBlockDesc2 gMtlEventBlock ( plComponent::kBlkComp, _T("mtlEvent"), 0, &gMtlEventDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp, IDD_COMP_DETECTOR_MTL, IDS_COMP_DETECTOR_MTL, 0, 0, &gMtlEventProc, kMtlMtl, _T("mtl"), TYPE_MTL, 0, 0, end, kMtlNode, _T("node"), TYPE_INODE, 0, 0, end, kMtlAnim, _T("anim"), TYPE_STRING, 0, 0, end, kMtlBegin, _T("animBegin"), TYPE_BOOL, 0, 0, end, kMtlEnd, _T("animEnd"), TYPE_BOOL, 0, 0, end, kMtlMarkers,_T("markers"), TYPE_STRING_TAB, 0, 0, 0, end, end ); plMtlEventComponent::plMtlEventComponent() : fCanExport(false) { fClassDesc = &gMtlEventDesc; fClassDesc->MakeAutoParamBlocks(this); } hsBool plMtlEventComponent::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg) { Mtl* mtl = fCompPB->GetMtl(kMtlMtl); if (!mtl) { pErrMsg->Set(true, "Mtl Event Component", "Component %s does not have a valid material selected.\n" "It will not be exported.", GetINode()->GetName()).Show(); pErrMsg->Set(false); fCanExport = false; } else fCanExport = true; return plActivatorBaseComponent::SetupProperties(node, pErrMsg); } hsBool plMtlEventComponent::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg) { if (!fCanExport) return false; plAnimEventModifier* mod = TRACKED_NEW plAnimEventModifier; plKey modKey = node->AddModifier(mod, IGetUniqueName(node)); fLogicModKeys[node] = modKey; return true; } // KLUDGE - The material animation key getter is here, so we have to include all this crap #include "plResponderMtl.h" hsBool plMtlEventComponent::Convert(plMaxNode *node, plErrorMsg *pErrMsg) { if (!fCanExport) return false; plKey modKey = fLogicModKeys[node]; plAnimEventModifier* mod = plAnimEventModifier::ConvertNoRef(modKey->GetObjectPtr()); Mtl* mtl = fCompPB->GetMtl(kMtlMtl); plMaxNodeBase* mtlNode = (plMaxNodeBase*)fCompPB->GetINode(kMtlNode); const char* mtlAnim = fCompPB->GetStr(kMtlAnim); // // Create and setup the callback message // hsTArray animKeys; GetMatAnimModKey(mtl, mtlNode, mtlAnim, animKeys); plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg; animMsg->SetCmd(plAnimCmdMsg::kAddCallbacks); animMsg->SetSender(modKey); animMsg->SetAnimName(mtlAnim); animMsg->AddReceivers(animKeys); if (fCompPB->GetInt(kMtlBegin)) { plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); eventMsg->fEvent = kBegin; } if (fCompPB->GetInt(kMtlEnd)) { plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); eventMsg->fEvent = kEnd; } if (fCompPB->Count(kMtlMarkers) > 0) { plNotetrackAnim anim(mtl, nil); plAnimInfo info = anim.GetAnimInfo(mtlAnim); int numMarkers = fCompPB->Count(kMtlMarkers); for (int i = 0; i < numMarkers; i++) { const char* marker = fCompPB->GetStr(kMtlMarkers, 0, i); float time = info.GetMarkerTime(marker); plEventCallbackMsg *eventMsg = CreateCallbackMsg(animMsg, modKey); eventMsg->fEvent = kTime; eventMsg->fEventTime = time; } } mod->SetCallback(animMsg); hsTArray receivers; IGetReceivers(node, receivers); mod->SetReceivers(receivers); return true; } //////////////////////////////////////////////////////////////////////////////// plMtlEventProc::plMtlEventProc() { fMtlButtonID = IDC_MTL_BUTTON; fMtlParamID = kMtlMtl; fNodeButtonID = IDC_NODE_BUTTON; fNodeParamID = kMtlNode; fAnimComboID = IDC_ANIM_COMBO; fAnimParamID = kMtlAnim; } void plMtlEventProc::ILoadUser(HWND hWnd, IParamBlock2* pb) { HWND hList = GetDlgItem(hWnd, IDC_EVENT_LIST); ListBox_ResetContent(hList); // // If we don't have a valid material, we should be disabled // Mtl* mtl = pb->GetMtl(kMtlMtl); if (!mtl) { EnableWindow(hList, FALSE); return; } else EnableWindow(hList, TRUE); // // Load the events // int idx; idx = ListBox_AddStringData(hList, "(Begin)", kAnimEventBegin); if (pb->GetInt(kMtlBegin)) ListBox_SetSel(hList, TRUE, idx); idx = ListBox_AddStringData(hList, "(End)", kAnimEventEnd); if (pb->GetInt(kMtlEnd)) ListBox_SetSel(hList, TRUE, idx); if (mtl) { const char* mtlAnim = pb->GetStr(kMtlAnim); // Get the shared animations for all the nodes this component is applied to plNotetrackAnim anim(mtl, nil); plAnimInfo info = anim.GetAnimInfo(mtlAnim); RemoveDeadMarkers(pb, kMtlMarkers, info); // Get all the markers in this animation while (const char* marker = info.GetNextMarkerName()) { idx = ListBox_AddStringData(hList, marker, kAnimEventMarker); if (IsMarkerSelected(pb, kMtlMarkers, marker)) ListBox_SetSel(hList, TRUE, idx); } } } bool plMtlEventProc::IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID) { if (cmd == LBN_SELCHANGE && resID == IDC_EVENT_LIST) { HWND hList = GetDlgItem(hWnd, IDC_EVENT_LIST); int idx = ListBox_GetCurSel(hList); BOOL selected = ListBox_GetSel(hList, idx); int eventType = ListBox_GetItemData(hList, idx); if (eventType == kAnimEventBegin) pb->SetValue(kMtlBegin, 0, selected); else if (eventType == kAnimEventEnd) pb->SetValue(kMtlEnd, 0, selected); else if (eventType == kAnimEventMarker) { char buf[256]; ListBox_GetText(hList, idx, buf); if (selected) { if (!IsMarkerSelected(pb, kMtlMarkers, buf)) { TCHAR* name = buf; pb->Append(kMtlMarkers, 1, &name); } } else IsMarkerSelected(pb, kMtlMarkers, buf, true); } return true; } return false; }