|
|
|
/*==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/>.
|
|
|
|
|
|
|
|
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==*/
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
#include "plResponderMtl.h"
|
|
|
|
#include "plResponderComponentPriv.h"
|
|
|
|
#include "resource.h"
|
|
|
|
#include "max.h"
|
|
|
|
|
|
|
|
#include "MaxMain/plMaxNode.h"
|
|
|
|
|
|
|
|
#include "MaxPlasmaMtls/Materials/plDecalMtl.h"
|
|
|
|
#include "MaxPlasmaMtls/Materials/plPassMtl.h"
|
|
|
|
|
|
|
|
#include "MaxConvert/plConvert.h"
|
|
|
|
#include "MaxConvert/hsMaterialConverter.h"
|
|
|
|
#include "plSurface/hsGMaterial.h"
|
|
|
|
#include "plSurface/plLayerAnimation.h"
|
|
|
|
|
|
|
|
#include "plMaxAnimUtils.h"
|
|
|
|
#include "plNotetrackAnim.h"
|
|
|
|
|
|
|
|
#include "plPickMaterialMap.h"
|
|
|
|
#include "MaxMain/plMtlCollector.h"
|
|
|
|
#include "plPickNode.h"
|
|
|
|
|
|
|
|
// Needed for convert
|
|
|
|
#include "plMessage/plAnimCmdMsg.h"
|
|
|
|
|
|
|
|
#include <set>
|
|
|
|
#include <vector>
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
#include "MaxMain/plPlasmaRefMsgs.h"
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
kMtlRef,
|
|
|
|
kMtlAnim,
|
|
|
|
kMtlLoop,
|
|
|
|
kMtlType,
|
|
|
|
kMtlOwner_DEAD,
|
|
|
|
kMtlNode,
|
|
|
|
kMtlNodeType,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum MtlNodeType
|
|
|
|
{
|
|
|
|
kNodePB, // Use the node in the PB
|
|
|
|
kNodeResponder // Use the node the responder is attached to
|
|
|
|
};
|
|
|
|
|
|
|
|
#include "plAnimCompProc.h"
|
|
|
|
|
|
|
|
class plResponderMtlProc : public plMtlAnimProc
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
plResponderMtlProc();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void IOnInitDlg(HWND hWnd, IParamBlock2* pb);
|
|
|
|
virtual void ILoadUser(HWND hWnd, IParamBlock2* pb);
|
|
|
|
virtual bool IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID);
|
|
|
|
|
|
|
|
virtual void IPickNode(IParamBlock2* pb);
|
|
|
|
|
|
|
|
virtual void ISetNodeButtonText(HWND hWnd, IParamBlock2* pb);
|
|
|
|
};
|
|
|
|
static plResponderMtlProc gResponderMtlProc;
|
|
|
|
|
|
|
|
ParamBlockDesc2 gResponderMtlBlock
|
|
|
|
(
|
|
|
|
kResponderMtlBlk, _T("mtlCmd"), 0, NULL, P_AUTO_UI,
|
|
|
|
|
|
|
|
IDD_COMP_RESPOND_MTL, IDS_COMP_CMD_PARAMS, 0, 0, &gResponderMtlProc,
|
|
|
|
|
|
|
|
kMtlRef, _T("mtl"), TYPE_REFTARG, 0, 0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
kMtlAnim, _T("anim"), TYPE_STRING, 0, 0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
kMtlLoop, _T("loop"), TYPE_STRING, 0, 0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
kMtlType, _T("type"), TYPE_INT, 0, 0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
kMtlNode, _T("node"), TYPE_INODE, 0, 0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
kMtlNodeType, _T("nodeType"), TYPE_INT, 0, 0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
end
|
|
|
|
);
|
|
|
|
|
|
|
|
plResponderCmdMtl& plResponderCmdMtl::Instance()
|
|
|
|
{
|
|
|
|
static plResponderCmdMtl theInstance;
|
|
|
|
return theInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
ParamBlockDesc2 *plResponderCmdMtl::GetDesc()
|
|
|
|
{
|
|
|
|
return &gResponderMtlBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use old types for backward compatibility
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
kRespondPlayMat=12,
|
|
|
|
kRespondStopMat,
|
|
|
|
kRespondToggleMat,
|
|
|
|
kRespondLoopMatOn,
|
|
|
|
kRespondLoopMatOff,
|
|
|
|
kRespondSetForeMat,
|
|
|
|
kRespondSetBackMat,
|
|
|
|
kRespondRewindMat,
|
|
|
|
|
|
|
|
kNumTypes=8
|
|
|
|
};
|
|
|
|
|
|
|
|
static int IndexToOldType(int idx)
|
|
|
|
{
|
|
|
|
static int oldTypes[] =
|
|
|
|
{
|
|
|
|
kRespondPlayMat,
|
|
|
|
kRespondStopMat,
|
|
|
|
kRespondToggleMat,
|
|
|
|
kRespondLoopMatOn,
|
|
|
|
kRespondLoopMatOff,
|
|
|
|
kRespondSetForeMat,
|
|
|
|
kRespondSetBackMat,
|
|
|
|
kRespondRewindMat
|
|
|
|
};
|
|
|
|
|
|
|
|
hsAssert(idx < kNumTypes, "Bad index");
|
|
|
|
return oldTypes[idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
int plResponderCmdMtl::NumTypes()
|
|
|
|
{
|
|
|
|
return kNumTypes;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *plResponderCmdMtl::GetCategory(int idx)
|
|
|
|
{
|
|
|
|
return "Material";
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *plResponderCmdMtl::GetName(int idx)
|
|
|
|
{
|
|
|
|
int type = IndexToOldType(idx);
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case kRespondPlayMat: return "Play";
|
|
|
|
case kRespondStopMat: return "Stop";
|
|
|
|
case kRespondToggleMat: return "Toggle";
|
|
|
|
case kRespondLoopMatOn: return "Set Looping On";
|
|
|
|
case kRespondLoopMatOff:return "Set Looping Off";
|
|
|
|
case kRespondSetForeMat:return "Set Forwards";
|
|
|
|
case kRespondSetBackMat:return "Set Backwards";
|
|
|
|
case kRespondRewindMat: return "Rewind";
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *GetShortName(int type)
|
|
|
|
{
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case kRespondPlayMat: return "Mat Play";
|
|
|
|
case kRespondStopMat: return "Mat Stop";
|
|
|
|
case kRespondToggleMat: return "Mat Toggle";
|
|
|
|
case kRespondLoopMatOn: return "Mat Loop On";
|
|
|
|
case kRespondLoopMatOff:return "Mat Loop Off";
|
|
|
|
case kRespondSetForeMat:return "Mat Set Fore";
|
|
|
|
case kRespondSetBackMat:return "Mat Set Back";
|
|
|
|
case kRespondRewindMat: return "Mat Rewind";
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
const char *plResponderCmdMtl::GetInstanceName(IParamBlock2 *pb)
|
|
|
|
{
|
|
|
|
static char name[256];
|
|
|
|
|
|
|
|
const char *shortName = GetShortName(pb->GetInt(kMtlType));
|
|
|
|
|
|
|
|
Mtl *mtl = (Mtl*)pb->GetReferenceTarget(kMtlRef);
|
|
|
|
sprintf(name, "%s (%s)", shortName, mtl ? mtl->GetName() : "none");
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
IParamBlock2 *plResponderCmdMtl::CreatePB(int idx)
|
|
|
|
{
|
|
|
|
int type = IndexToOldType(idx);
|
|
|
|
|
|
|
|
// Create the paramblock and save it's type
|
|
|
|
IParamBlock2 *pb = CreateParameterBlock2(&gResponderMtlBlock, nil);
|
|
|
|
pb->SetValue(kMtlType, 0, type);
|
|
|
|
|
|
|
|
return pb;
|
|
|
|
}
|
|
|
|
|
|
|
|
Mtl *plResponderCmdMtl::GetMtl(IParamBlock2 *pb)
|
|
|
|
{
|
|
|
|
return (Mtl*)pb->GetReferenceTarget(kMtlRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *plResponderCmdMtl::GetAnim(IParamBlock2 *pb)
|
|
|
|
{
|
|
|
|
return pb->GetStr(kMtlAnim);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ISearchLayerRecur(plLayerInterface *layer, const char *segName, hsTArray<plKey>& keys)
|
|
|
|
{
|
|
|
|
if (!layer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
plLayerAnimation *animLayer = plLayerAnimation::ConvertNoRef(layer);
|
|
|
|
if (animLayer)
|
|
|
|
{
|
|
|
|
char *ID = animLayer->GetSegmentID();
|
|
|
|
if (ID == nil)
|
|
|
|
ID = "";
|
|
|
|
if (!strcmp(ID, segName))
|
|
|
|
{
|
|
|
|
if( keys.kMissingIndex == keys.Find(animLayer->GetKey()) )
|
|
|
|
keys.Append(animLayer->GetKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ISearchLayerRecur(layer->GetAttached(), segName, keys);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ISearchLayerRecur(hsGMaterial* mat, const char *segName, hsTArray<plKey>& keys)
|
|
|
|
{
|
|
|
|
if (segName == nil)
|
|
|
|
segName = "";
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < mat->GetNumLayers(); i++ )
|
|
|
|
ISearchLayerRecur(mat->GetLayer(i), segName, keys);
|
|
|
|
return keys.GetCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetMatAnimModKey(Mtl* mtl, plMaxNodeBase* node, const char* segName, hsTArray<plKey>& keys)
|
|
|
|
{
|
|
|
|
int retVal = 0;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
//if( begin < 0 )
|
|
|
|
// begin = 0;
|
|
|
|
|
|
|
|
if( mtl->ClassID() == Class_ID(MULTI_CLASS_ID,0) )
|
|
|
|
{
|
|
|
|
for( i = 0; i < mtl->NumSubMtls(); i++ )
|
|
|
|
retVal += GetMatAnimModKey(mtl->GetSubMtl(i), node, segName, keys);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsTArray<hsGMaterial*> matList;
|
|
|
|
|
|
|
|
if (node)
|
|
|
|
hsMaterialConverter::Instance().GetMaterialArray(mtl, (plMaxNode*)node, matList);
|
|
|
|
else
|
|
|
|
hsMaterialConverter::Instance().CollectConvertedMaterials(mtl, matList);
|
|
|
|
|
|
|
|
for( i = 0; i < matList.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
retVal += ISearchLayerRecur(matList[i], segName, keys);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plResponderCmdMtl::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2* pb)
|
|
|
|
{
|
|
|
|
plMaxNode* mtlNode;
|
|
|
|
if (pb->GetInt(kMtlNodeType) == kNodeResponder)
|
|
|
|
mtlNode = node;
|
|
|
|
else
|
|
|
|
mtlNode = (plMaxNode*)pb->GetINode(kMtlNode);
|
|
|
|
|
|
|
|
if (mtlNode)
|
|
|
|
mtlNode->SetForceMaterialCopy(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
plMessage *plResponderCmdMtl::CreateMsg(plMaxNode* node, plErrorMsg *pErrMsg, IParamBlock2 *pb)
|
|
|
|
{
|
|
|
|
Mtl *maxMtl = (Mtl*)pb->GetReferenceTarget(kMtlRef);
|
|
|
|
if (!maxMtl)
|
|
|
|
throw "No material specified";
|
|
|
|
|
|
|
|
const char *animName = pb->GetStr(kMtlAnim);
|
|
|
|
float begin=-1.f;
|
|
|
|
float end = -1.f;
|
|
|
|
|
|
|
|
SegmentMap *segMap = GetAnimSegmentMap(maxMtl, pErrMsg);
|
|
|
|
|
|
|
|
hsTArray<plKey> keys;
|
|
|
|
|
|
|
|
if( segMap )
|
|
|
|
{
|
|
|
|
GetSegMapAnimTime(animName, segMap, SegmentSpec::kAnim, begin, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
plMaxNode* mtlNode;
|
|
|
|
if (pb->GetInt(kMtlNodeType) == kNodeResponder)
|
|
|
|
mtlNode = node;
|
|
|
|
else
|
|
|
|
mtlNode = (plMaxNode*)pb->GetINode(kMtlNode);
|
|
|
|
|
|
|
|
GetMatAnimModKey(maxMtl, mtlNode, animName, keys);
|
|
|
|
|
|
|
|
const char *loopName = nil;
|
|
|
|
loopName = pb->GetStr(kMtlLoop);
|
|
|
|
if (segMap && loopName)
|
|
|
|
GetSegMapAnimTime(loopName, segMap, SegmentSpec::kLoop, begin, end);
|
|
|
|
|
|
|
|
DeleteSegmentMap(segMap);
|
|
|
|
|
|
|
|
if (!keys.GetCount())
|
|
|
|
{
|
|
|
|
// We need the check here because "physicals only" export mode means that
|
|
|
|
// most of the materials won't be there, so we should ignore this warning. -Colin
|
|
|
|
if (plConvert::Instance().GetConvertSettings()->fPhysicalsOnly)
|
|
|
|
return nil;
|
|
|
|
else
|
|
|
|
throw "Material animation key(s) not found";
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimCmdMsg *msg = TRACKED_NEW plAnimCmdMsg;
|
|
|
|
msg->AddReceivers(keys);
|
|
|
|
|
|
|
|
switch (pb->GetInt(kMtlType))
|
|
|
|
{
|
|
|
|
case kRespondPlayMat:
|
|
|
|
msg->SetCmd(plAnimCmdMsg::kContinue);
|
|
|
|
break;
|
|
|
|
case kRespondStopMat:
|
|
|
|
msg->SetCmd(plAnimCmdMsg::kStop);
|
|
|
|
break;
|
|
|
|
case kRespondToggleMat:
|
|
|
|
msg->SetCmd(plAnimCmdMsg::kToggleState);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kRespondLoopMatOn:
|
|
|
|
msg->SetCmd(plAnimCmdMsg::kSetLooping);
|
|
|
|
|
|
|
|
// KLUDGE - We send the loop to play by name here, so anim grouped components
|
|
|
|
// could have loops with different begin and end points. However, apparently
|
|
|
|
// that functionality was never implemented, whoops. So, we'll take out the
|
|
|
|
// stuff that actually tries to set the begin and end points for now, so that
|
|
|
|
// anims with a loop set in advance will actually work with this. -Colin
|
|
|
|
//
|
|
|
|
// This KLUDGE has been copied from where Colin kludged it in plResponderAnim
|
|
|
|
// in the spirit of consistent hackage. Maybe when one gets unkludged, the
|
|
|
|
// other one will too. -mf
|
|
|
|
// msg->SetCmd(plAnimCmdMsg::kSetLoopBegin);
|
|
|
|
// msg->fLoopBegin = begin;
|
|
|
|
|
|
|
|
// msg->SetCmd(plAnimCmdMsg::kSetLoopEnd);
|
|
|
|
// msg->fLoopEnd = end;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kRespondLoopMatOff:
|
|
|
|
msg->SetCmd(plAnimCmdMsg::kUnSetLooping);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kRespondSetForeMat:
|
|
|
|
msg->SetCmd(plAnimCmdMsg::kSetForewards);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kRespondSetBackMat:
|
|
|
|
msg->SetCmd(plAnimCmdMsg::kSetBackwards);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kRespondRewindMat:
|
|
|
|
msg->SetCmd(plAnimCmdMsg::kGoToBegin);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
delete msg;
|
|
|
|
throw "Unknown material command";
|
|
|
|
}
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plResponderCmdMtl::IsWaitable(IParamBlock2 *pb)
|
|
|
|
{
|
|
|
|
int type = pb->GetInt(kMtlType);
|
|
|
|
if (type == kRespondPlayMat ||
|
|
|
|
type == kRespondToggleMat)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plResponderCmdMtl::GetWaitPoints(IParamBlock2 *pb, WaitPoints& waitPoints)
|
|
|
|
{
|
|
|
|
Mtl *mtl = GetMtl(pb);
|
|
|
|
const char *animName = GetAnim(pb);
|
|
|
|
|
|
|
|
if (mtl)
|
|
|
|
{
|
|
|
|
plNotetrackAnim notetrackAnim(mtl, nil);
|
|
|
|
plAnimInfo info = notetrackAnim.GetAnimInfo(animName);
|
|
|
|
while (const char *marker = info.GetNextMarkerName())
|
|
|
|
waitPoints.push_back(marker);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plResponderCmdMtl::CreateWait(plMaxNode* node, plErrorMsg* pErrMsg, IParamBlock2 *pb, ResponderWaitInfo& waitInfo)
|
|
|
|
{
|
|
|
|
plAnimCmdMsg *animMsg = plAnimCmdMsg::ConvertNoRef(waitInfo.msg);
|
|
|
|
if (animMsg)
|
|
|
|
animMsg->SetCmd(plAnimCmdMsg::kAddCallbacks);
|
|
|
|
|
|
|
|
plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg;
|
|
|
|
eventMsg->AddReceiver(waitInfo.receiver);
|
|
|
|
eventMsg->fRepeats = 0;
|
|
|
|
eventMsg->fEvent = kStop;
|
|
|
|
eventMsg->fUser = waitInfo.callbackUser;
|
|
|
|
|
|
|
|
if (waitInfo.point)
|
|
|
|
{
|
|
|
|
// FIXME COLIN - Error checking here?
|
|
|
|
Mtl *mtl = GetMtl(pb);
|
|
|
|
const char *animName = GetAnim(pb);
|
|
|
|
|
|
|
|
plNotetrackAnim notetrackAnim(mtl, nil);
|
|
|
|
plAnimInfo info = notetrackAnim.GetAnimInfo(animName);
|
|
|
|
|
|
|
|
eventMsg->fEvent = kTime;
|
|
|
|
eventMsg->fEventTime = info.GetMarkerTime(waitInfo.point);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eventMsg->fEvent = kStop;
|
|
|
|
}
|
|
|
|
|
|
|
|
plMessageWithCallbacks *callbackMsg = plMessageWithCallbacks::ConvertNoRef(waitInfo.msg);
|
|
|
|
callbackMsg->AddCallback(eventMsg);
|
|
|
|
hsRefCnt_SafeUnRef( eventMsg );
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
plResponderMtlProc::plResponderMtlProc()
|
|
|
|
{
|
|
|
|
fMtlButtonID = IDC_MTL_BUTTON;
|
|
|
|
fMtlParamID = kMtlRef;
|
|
|
|
fNodeButtonID = IDC_NODE_BUTTON;
|
|
|
|
fNodeParamID = kMtlNode;
|
|
|
|
fAnimComboID = IDC_ANIM_COMBO;
|
|
|
|
fAnimParamID = kMtlAnim;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plResponderMtlProc::IOnInitDlg(HWND hWnd, IParamBlock2* pb)
|
|
|
|
{
|
|
|
|
int type = pb->GetInt(kMtlType);
|
|
|
|
|
|
|
|
// Show the loop control only if this is a loop
|
|
|
|
int show = (type == kRespondLoopMatOn) ? SW_SHOW : SW_HIDE;
|
|
|
|
ShowWindow(GetDlgItem(hWnd, IDC_LOOP_COMBO), show);
|
|
|
|
ShowWindow(GetDlgItem(hWnd, IDC_LOOP_TEXT), show);
|
|
|
|
|
|
|
|
// Resize the dialog if we're not using the loop control
|
|
|
|
if (type != kRespondLoopMatOn)
|
|
|
|
{
|
|
|
|
RECT itemRect, clientRect;
|
|
|
|
GetWindowRect(GetDlgItem(hWnd, IDC_LOOP_TEXT), &itemRect);
|
|
|
|
GetWindowRect(hWnd, &clientRect);
|
|
|
|
SetWindowPos(hWnd, NULL, 0, 0, clientRect.right-clientRect.left,
|
|
|
|
itemRect.top-clientRect.top, SWP_NOMOVE | SWP_NOZORDER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plResponderMtlProc::ILoadUser(HWND hWnd, IParamBlock2 *pb)
|
|
|
|
{
|
|
|
|
HWND hLoop = GetDlgItem(hWnd, IDC_LOOP_COMBO);
|
|
|
|
|
|
|
|
const char *savedName = pb->GetStr(kMtlLoop);
|
|
|
|
if (!savedName)
|
|
|
|
savedName = "";
|
|
|
|
|
|
|
|
ComboBox_ResetContent(hLoop);
|
|
|
|
int sel = ComboBox_AddString(hLoop, ENTIRE_ANIMATION_NAME);
|
|
|
|
ComboBox_SetCurSel(hLoop, sel);
|
|
|
|
|
|
|
|
// Get the NoteTrack animations off the selected material
|
|
|
|
Mtl *mtl = (Mtl*)pb->GetReferenceTarget(kMtlRef);
|
|
|
|
if (!mtl)
|
|
|
|
{
|
|
|
|
ComboBox_Enable(hLoop, FALSE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ComboBox_Enable(hLoop, TRUE);
|
|
|
|
|
|
|
|
plNotetrackAnim anim(mtl, nil);
|
|
|
|
const char *animName = pb->GetStr(kMtlAnim);
|
|
|
|
plAnimInfo info = anim.GetAnimInfo(animName);
|
|
|
|
|
|
|
|
while (const char *loopName = info.GetNextLoopName())
|
|
|
|
{
|
|
|
|
sel = ComboBox_AddString(hLoop, loopName);
|
|
|
|
if (!strcmp(loopName, savedName))
|
|
|
|
ComboBox_SetCurSel(hLoop, sel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plResponderMtlProc::IUserCommand(HWND hWnd, IParamBlock2* pb, int cmd, int resID)
|
|
|
|
{
|
|
|
|
if (cmd == CBN_SELCHANGE && resID == IDC_LOOP_COMBO)
|
|
|
|
{
|
|
|
|
HWND hCombo = GetDlgItem(hWnd, IDC_LOOP_COMBO);
|
|
|
|
int idx = ComboBox_GetCurSel(hCombo);
|
|
|
|
|
|
|
|
if (idx != CB_ERR)
|
|
|
|
{
|
|
|
|
if (ComboBox_GetItemData(hCombo, idx) == 0)
|
|
|
|
pb->SetValue(kMtlLoop, 0, "");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Get the name of the animation and save it
|
|
|
|
char buf[256];
|
|
|
|
ComboBox_GetText(hCombo, buf, sizeof(buf));
|
|
|
|
pb->SetValue(kMtlLoop, 0, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "plPickNodeBase.h"
|
|
|
|
|
|
|
|
static const char* kUserTypeAll = "(All)";
|
|
|
|
static const char* kResponderNodeName = "(Responder Node)";
|
|
|
|
|
|
|
|
class plPickRespMtlNode : public plPickMtlNode
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
int fTypeID;
|
|
|
|
|
|
|
|
void IAddUserType(HWND hList)
|
|
|
|
{
|
|
|
|
int type = fPB->GetInt(fTypeID);
|
|
|
|
|
|
|
|
int idx = ListBox_AddString(hList, kUserTypeAll);
|
|
|
|
if (type == kNodePB && !fPB->GetINode(fNodeParamID))
|
|
|
|
ListBox_SetCurSel(hList, idx);
|
|
|
|
|
|
|
|
|
|
|
|
idx = ListBox_AddString(hList, kResponderNodeName);
|
|
|
|
if (type == kNodeResponder)
|
|
|
|
ListBox_SetCurSel(hList, idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ISetUserType(plMaxNode* node, const char* userType)
|
|
|
|
{
|
|
|
|
if (hsStrEQ(userType, kUserTypeAll))
|
|
|
|
{
|
|
|
|
ISetNodeValue(nil);
|
|
|
|
fPB->SetValue(fTypeID, 0, kNodePB);
|
|
|
|
}
|
|
|
|
else if (hsStrEQ(userType, kResponderNodeName))
|
|
|
|
{
|
|
|
|
ISetNodeValue(nil);
|
|
|
|
fPB->SetValue(fTypeID, 0, kNodeResponder);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fPB->SetValue(fTypeID, 0, kNodePB);
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
plPickRespMtlNode(IParamBlock2* pb, int nodeParamID, int typeID, Mtl* mtl) :
|
|
|
|
plPickMtlNode(pb, nodeParamID, mtl), fTypeID(typeID)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void plResponderMtlProc::IPickNode(IParamBlock2* pb)
|
|
|
|
{
|
|
|
|
plPickRespMtlNode pick(pb, kMtlNode, kMtlNodeType, IGetMtl(pb));
|
|
|
|
pick.DoPick();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plResponderMtlProc::ISetNodeButtonText(HWND hWnd, IParamBlock2* pb)
|
|
|
|
{
|
|
|
|
int type = pb->GetInt(kMtlNodeType);
|
|
|
|
HWND hButton = GetDlgItem(hWnd, IDC_NODE_BUTTON);
|
|
|
|
|
|
|
|
if (type == kNodeResponder)
|
|
|
|
SetWindowText(hButton, kResponderNodeName);
|
|
|
|
else if (type == kNodePB && !pb->GetINode(kMtlNode))
|
|
|
|
SetWindowText(hButton, kUserTypeAll);
|
|
|
|
else
|
|
|
|
plMtlAnimProc::ISetNodeButtonText(hWnd, pb);
|
|
|
|
}
|