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.

616 lines
16 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==*/
#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<plKey> 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<Class_ID> 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<plKey> 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<plKey> 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;
}