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.
988 lines
33 KiB
988 lines
33 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/>. |
|
|
|
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==*/ |
|
////////////////////////////////////////////////////////////////////////////// |
|
// // |
|
// plAnimStealthNode - Stealthy hidden INode that represents a single // |
|
// segment's worth of animation info for a material. // |
|
// Stored as an INode so they can be "selected" // |
|
// by components as targets of animation messages. // |
|
// // |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
#include "HeadSpin.h" |
|
#include "pnKeyedObject/plKey.h" |
|
#include "hsWindows.h" |
|
#include <windowsx.h> |
|
#include "../resource.h" |
|
|
|
#include "MaxMain/MaxCompat.h" |
|
#include <iparamm2.h> |
|
#pragma hdrstop |
|
|
|
#include "plAnimStealthNode.h" |
|
#include "plPassMtlBase.h" |
|
|
|
#include "MaxComponent/plMaxAnimUtils.h" |
|
#include "MaxComponent/plPickNodeBase.h" |
|
|
|
extern TCHAR *GetString( int id ); |
|
extern HINSTANCE hInstance; |
|
|
|
|
|
//// Stealthy Class Desc ///////////////////////////////////////////////////// |
|
|
|
class plStealthClassDesc : public ClassDesc2 |
|
{ |
|
public: |
|
int IsPublic() { return FALSE; } |
|
void* Create(BOOL loading) { return new plAnimStealthNode(loading); } |
|
const TCHAR* ClassName() { return GetString( IDS_STEALTH_NAME ); } |
|
SClass_ID SuperClassID() { return HELPER_CLASS_ID; } |
|
Class_ID ClassID() { return ANIMSTEALTH_CLASSID; } |
|
const TCHAR* Category() { return NULL; } |
|
const TCHAR* InternalName() { return _T("PlasmaAnimStealthInfo"); } |
|
HINSTANCE HInstance() { return hInstance; } |
|
}; |
|
static plStealthClassDesc sStealthClassDesc; |
|
ClassDesc2* GetStealthClassDesc() { return &sStealthClassDesc; } |
|
|
|
//// plStealthDlgProc ///////////////////////////////////////////////////////// |
|
// Dialog proc for the anim stealth child dialog |
|
|
|
class plStealthDlgProc : public ParamMap2UserDlgProc |
|
{ |
|
protected: |
|
// Combo itemdata values |
|
enum |
|
{ |
|
kName, // Name of an animation/loop |
|
kDefault, // Default combo value |
|
kInvalid, // Invalid entry (couldn't find) |
|
}; |
|
|
|
SegmentMap *fSegMap; |
|
|
|
HWND fhWnd; |
|
|
|
public: |
|
virtual BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); |
|
virtual void DeleteThis() { IDeleteSegMap(); } |
|
void SetThing(ReferenceTarget *m); |
|
|
|
virtual void Update( TimeValue t, Interval &valid, IParamMap2 *pmap ); |
|
|
|
protected: |
|
// Set all the controls to their stored value |
|
void IInitControls( plAnimStealthNode *stealth, IParamBlock2 *pb); |
|
|
|
// Deletes all the allocated memory |
|
void IDeleteSegMap(); |
|
|
|
void ILoadLoops(IParamBlock2 *pb); |
|
|
|
void ISetSel(HWND hCombo, const char *name); |
|
}; |
|
|
|
const char *kAnimNameNone = ENTIRE_ANIMATION_NAME; |
|
|
|
static plStealthDlgProc sStealthDlgProc; |
|
|
|
|
|
|
|
|
|
|
|
//// Stealthy ParamBlock Desc //////////////////////////////////////////////// |
|
|
|
static plEaseAccessor sEaseAccessor( plAnimStealthNode::kBlockPB, plAnimStealthNode::kPBEaseInMin, |
|
plAnimStealthNode::kPBEaseInMax, plAnimStealthNode::kPBEaseInLength, |
|
plAnimStealthNode::kPBEaseOutMin, plAnimStealthNode::kPBEaseOutMax, |
|
plAnimStealthNode::kPBEaseOutLength ); |
|
|
|
ParamBlockDesc2 plAnimStealthNode::sAnimStealthPB |
|
( |
|
kBlockPB, _T( "animStealth" ), IDS_STEALTH_NAME, GetStealthClassDesc(),//NULL, |
|
P_AUTO_CONSTRUCT + P_AUTO_UI, kRefParamBlock, |
|
|
|
// UI |
|
IDD_STEALTH_ANIM, IDS_STEALTH_NAME, 0, 0, &sStealthDlgProc, |
|
|
|
kPBName, _T("animName"), TYPE_STRING, 0, 0, |
|
end, |
|
|
|
kPBAutoStart, _T("autoStart"), TYPE_BOOL, 0, 0, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_AUTO_START, |
|
p_default, FALSE, |
|
end, |
|
|
|
kPBLoop, _T("loop"), TYPE_BOOL, 0, 0, |
|
p_ui, TYPE_SINGLECHEKBOX, IDC_LOOP, |
|
p_default, TRUE, |
|
end, |
|
kPBLoopName, _T("loopName"), TYPE_STRING, 0, 0, |
|
end, |
|
|
|
// Anim Ease |
|
kPBEaseInType, _T("easeInType"), TYPE_INT, 0, 0, |
|
p_ui, TYPE_RADIO, 3, IDC_PASS_ANIM_EASE_IN_NONE, IDC_PASS_ANIM_EASE_IN_CONST_ACCEL, IDC_PASS_ANIM_EASE_IN_SPLINE, |
|
p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, |
|
p_default, plAnimEaseTypes::kNoEase, |
|
end, |
|
kPBEaseInLength, _T("easeInLength"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_IN_TIME, IDC_PASS_ANIM_EASE_IN_TIME_SPIN, 1.0, |
|
p_accessor, &sEaseAccessor, |
|
end, |
|
kPBEaseInMin, _T("easeInMin"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_IN_MIN, IDC_PASS_ANIM_EASE_IN_MIN_SPIN, 1.0, |
|
p_accessor, &sEaseAccessor, |
|
end, |
|
kPBEaseInMax, _T("easeInMax"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_IN_MAX, IDC_PASS_ANIM_EASE_IN_MAX_SPIN, 1.0, |
|
p_accessor, &sEaseAccessor, |
|
end, |
|
|
|
kPBEaseOutType, _T("easeOutType"), TYPE_INT, 0, 0, |
|
p_ui, TYPE_RADIO, 3, IDC_PASS_ANIM_EASE_OUT_NONE, IDC_PASS_ANIM_EASE_OUT_CONST_ACCEL, IDC_PASS_ANIM_EASE_OUT_SPLINE, |
|
p_vals, plAnimEaseTypes::kNoEase, plAnimEaseTypes::kConstAccel, plAnimEaseTypes::kSpline, |
|
p_default, plAnimEaseTypes::kNoEase, |
|
end, |
|
kPBEaseOutLength, _T("easeOutLength"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_OUT_TIME, IDC_PASS_ANIM_EASE_OUT_TIME_SPIN, 1.0, |
|
p_accessor, &sEaseAccessor, |
|
end, |
|
kPBEaseOutMin, _T("easeOutMin"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_OUT_MIN, IDC_PASS_ANIM_EASE_OUT_MIN_SPIN, 1.0, |
|
p_accessor, &sEaseAccessor, |
|
end, |
|
kPBEaseOutMax, _T("easeOutMax"), TYPE_FLOAT, 0, 0, |
|
p_default, 1.0, |
|
p_range, 0.1, 99.0, |
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT, IDC_PASS_ANIM_EASE_OUT_MAX, IDC_PASS_ANIM_EASE_OUT_MAX_SPIN, 1.0, |
|
p_accessor, &sEaseAccessor, |
|
end, |
|
|
|
end |
|
); |
|
|
|
plAnimStealthNode::plAnimStealthNode( BOOL loading ) : fClassDesc(nil), fParamBlock(nil), fParentMtl(nil) |
|
{ |
|
fCachedSegMap = nil; |
|
fClassDesc = &sStealthClassDesc; |
|
fClassDesc->MakeAutoParamBlocks( this ); |
|
} |
|
|
|
plAnimStealthNode::~plAnimStealthNode() |
|
{ |
|
// DeleteAllRefsFromMe(); |
|
} |
|
|
|
CreateMouseCallBack *plAnimStealthNode::GetCreateMouseCallBack() |
|
{ |
|
return nil; |
|
} |
|
|
|
void plAnimStealthNode::SetParentMtl( plPassMtlBase *parent ) |
|
{ |
|
fParentMtl = parent; |
|
} |
|
|
|
bool plAnimStealthNode::CanConvertToStealth( INode *objNode ) |
|
{ |
|
return ( ConvertToStealth( objNode ) != nil ); |
|
} |
|
|
|
plAnimStealthNode *plAnimStealthNode::ConvertToStealth( INode *objNode ) |
|
{ |
|
if( objNode == nil ) |
|
return nil; |
|
|
|
Object *obj = objNode->GetObjectRef(); |
|
if( obj == nil ) |
|
return nil; |
|
|
|
if( obj->CanConvertToType( ANIMSTEALTH_CLASSID ) ) |
|
return (plAnimStealthNode *)obj; |
|
|
|
return nil; |
|
} |
|
|
|
|
|
plString plAnimStealthNode::GetSegmentName( void ) const |
|
{ |
|
const char *str = fParamBlock->GetStr( (ParamID)kPBName ); |
|
if( str == nil || str[ 0 ] == 0 ) |
|
return ENTIRE_ANIMATION_NAME; |
|
return plString::FromUtf8(str); |
|
} |
|
|
|
void plAnimStealthNode::SetSegment( const char *name ) |
|
{ |
|
if( name == nil || strcmp(name, ENTIRE_ANIMATION_NAME) == 0 || name[ 0 ] == 0 ) |
|
fParamBlock->SetValue( (ParamID)kPBName, 0, "" ); |
|
else |
|
fParamBlock->SetValue( (ParamID)kPBName, 0, (char *)name ); |
|
} |
|
|
|
void plAnimStealthNode::SetNodeName( const char *parentName ) |
|
{ |
|
INode *node = GetINode(); |
|
if( node != nil ) |
|
{ |
|
char name[ 512 ], newName[ 512 ]; |
|
sprintf( name, "%s : %s", parentName, GetSegmentName() ); |
|
|
|
if( GetCOREInterface()->GetINodeByName( name ) != nil ) |
|
{ |
|
// For whatever reason, MakeNameUnique() doesn't ACTUALLY make a name unique! |
|
// So we just need to more or less do it ourselves... |
|
int i; |
|
for( i = 1; i < 1024; i++ ) |
|
{ |
|
sprintf( newName, "%s(%d)", name, i ); |
|
if( GetCOREInterface()->GetINodeByName( newName ) == nil ) |
|
break; |
|
} |
|
if( i == 1024 ) |
|
{ |
|
// You've got to be kidding me... |
|
char msg[ 2048 ]; |
|
sprintf( msg, "WARNING: For some reason, we cannot find a unique name for the node '%s'. This" |
|
" will most likely cause export problems. Exactly how many of these do we HAVE??", |
|
name ); |
|
hsMessageBox( msg, "WARNING!", hsMessageBoxNormal ); |
|
} |
|
} |
|
else |
|
strcpy( newName, name ); |
|
|
|
|
|
node->SetName( newName ); |
|
} |
|
} |
|
|
|
int plAnimStealthNode::NumParamBlocks() |
|
{ |
|
return 1; |
|
} |
|
|
|
IParamBlock2 *plAnimStealthNode::GetParamBlock( int i ) |
|
{ |
|
if( i == kRefParamBlock ) |
|
return fParamBlock; |
|
|
|
return nil; |
|
} |
|
|
|
IParamBlock2 *plAnimStealthNode::GetParamBlockByID( BlockID id ) |
|
{ |
|
if( fParamBlock && fParamBlock->ID() == id ) |
|
return fParamBlock; |
|
|
|
return nil; |
|
} |
|
|
|
RefTargetHandle plAnimStealthNode::Clone(RemapDir &remap) |
|
{ |
|
plAnimStealthNode *obj = (plAnimStealthNode *)fClassDesc->Create( false ); |
|
// Do the base clone |
|
BaseClone(this, obj, remap); |
|
// Copy our references |
|
if (fParamBlock) |
|
obj->ReplaceReference( kRefParamBlock, fParamBlock->Clone( remap ) ); |
|
|
|
return obj; |
|
} |
|
|
|
void plAnimStealthNode::BuildMesh(TimeValue t) |
|
{ |
|
} |
|
|
|
void plAnimStealthNode::FreeCaches() |
|
{ |
|
} |
|
|
|
void plAnimStealthNode::GetLocalBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box) |
|
{ |
|
box.MakeCube(Point3(0,0,0), 0); |
|
} |
|
|
|
void plAnimStealthNode::GetWorldBoundBox(TimeValue t, INode *node, ViewExp *vpt, Box3 &box) |
|
{ |
|
box.MakeCube(Point3(0,0,0), 0); |
|
} |
|
|
|
int plAnimStealthNode::Display(TimeValue t, INode *node, ViewExp *vpt, int flags) |
|
{ |
|
return 0; |
|
} |
|
|
|
int plAnimStealthNode::HitTest(TimeValue t, INode *node, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt) |
|
{ |
|
return 0; |
|
} |
|
|
|
int plAnimStealthNode::NumRefs() |
|
{ |
|
return 1; |
|
} |
|
|
|
RefTargetHandle plAnimStealthNode::GetReference( int i ) |
|
{ |
|
if( i == kRefParamBlock ) |
|
return fParamBlock; |
|
else if( i == kRefParentMtl ) |
|
return fParentMtl; |
|
|
|
return nil; |
|
} |
|
|
|
void plAnimStealthNode::SetReference( int i, RefTargetHandle rtarg ) |
|
{ |
|
if( i == kRefParamBlock ) |
|
fParamBlock = (IParamBlock2 *)rtarg; |
|
else if( i == kRefParentMtl ) |
|
fParentMtl = (plPassMtlBase *)rtarg; |
|
} |
|
|
|
RefResult plAnimStealthNode::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) |
|
{ |
|
return REF_SUCCEED; |
|
} |
|
|
|
IOResult plAnimStealthNode::Save(ISave* isave) |
|
{ |
|
return IO_OK; |
|
} |
|
|
|
IOResult plAnimStealthNode::Load(ILoad* iload) |
|
{ |
|
return IO_OK; |
|
} |
|
|
|
plPassMtlBase *plAnimStealthNode::GetParentMtl( void ) |
|
{ |
|
return fParentMtl; |
|
} |
|
|
|
class plGetRefs : public DependentEnumProc |
|
{ |
|
public: |
|
|
|
hsTArray<ReferenceMaker *> fList; |
|
|
|
plGetRefs() { } |
|
|
|
virtual int proc( ReferenceMaker *rmaker ) |
|
{ |
|
fList.Append( rmaker ); |
|
return DEP_ENUM_CONTINUE; |
|
} |
|
}; |
|
|
|
bool plAnimStealthNode::IsParentUsedInScene( void ) |
|
{ |
|
if( GetParentMtl() == nil ) |
|
return false; |
|
|
|
// There are two possibilities: either a node uses us and thus has a ref to us, |
|
// or a multi-sub uses us that a node has a ref to us. |
|
|
|
// Note: we could do the loop as a helper function, but we only do it twice, |
|
// so it's not *really* worth the effort... |
|
//// NOTE: the following doesn't seem to work, but keeping here in case it ever does. |
|
//// What really actually finds something is the enum dependents loop below |
|
const char *mtlName = GetParentMtl()->GetName(); |
|
|
|
DependentIterator di(this); |
|
ReferenceMaker* item = di.Next(); |
|
while( item != nil ) |
|
{ |
|
TSTR s; |
|
item->GetClassName( s ); |
|
|
|
if( item->SuperClassID() == BASENODE_CLASS_ID && !CanConvertToStealth( (INode *)( item ) ) ) |
|
return true; // Horray, a node has a ref to us! |
|
|
|
else if( item->ClassID() == Class_ID(MULTI_CLASS_ID,0) ) |
|
{ |
|
// Multi-sub, run the refs on that guy (we only go one up) |
|
Mtl *multisub = (Mtl *)item; |
|
|
|
DependentIterator sub(multisub); |
|
ReferenceMaker* item2 = sub.Next(); |
|
while( item2 != nil ) |
|
{ |
|
if( item2->SuperClassID() == BASENODE_CLASS_ID ) |
|
return true; // Horray, a node has a ref to us! |
|
item2 = sub.Next(); |
|
} |
|
|
|
// No go, keep trying |
|
} |
|
else if( item->SuperClassID() == MATERIAL_CLASS_ID ) |
|
{ |
|
int q = 0; |
|
} |
|
|
|
item = di.Next(); |
|
} |
|
|
|
// Enum dependents |
|
plGetRefs callback; |
|
ENUMDEPENDENTS(GetParentMtl(), &callback); |
|
for(int i = 0; i < callback.fList.GetCount(); i++ ) |
|
{ |
|
ReferenceMaker *maker = callback.fList[ i ]; |
|
|
|
TSTR s; |
|
maker->GetClassName( s ); |
|
|
|
if( maker->SuperClassID() == BASENODE_CLASS_ID && !CanConvertToStealth( (INode *)maker ) ) |
|
return true; // Horray, a node has a ref to us! |
|
} |
|
return false; |
|
} |
|
|
|
INode *plAnimStealthNode::GetINode() |
|
{ |
|
// Go through the reflist looking for RefMakers with a ref to this component. |
|
// There should only be one INode in this list. |
|
DependentIterator di(this); |
|
ReferenceMaker* item = di.Next(); |
|
while( item ) |
|
{ |
|
if( item->SuperClassID() == BASENODE_CLASS_ID ) |
|
return (INode *)item; |
|
|
|
item = di.Next(); |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
void plStealthDlgProc::Update(TimeValue t, Interval& valid, IParamMap2* pmap) |
|
{ |
|
// Does the pmap match our pmap? |
|
|
|
} |
|
|
|
//// plStealthMouseOverrideProc ////////////////////////////////////////////// |
|
// Because of wonderful linking problems with the MAX libraries, we can't |
|
// actually use CreateChildMParamMap2 like we should. So instead, we use |
|
// CreateChildCPParamMap2. However, *that* function calls the wrong interface |
|
// to handle untrapped mouse messages, with the result that clicking and |
|
// dragging scrolls the command pane (where components are displayed) instead |
|
// of the material editor pane. |
|
// To override this, we subclass each dialog so that we can capture the mouse |
|
// messages before MAX processes them and then reroute them appropriately. |
|
// Note: because MAX already uses the window long of the given window, we can't |
|
// store the old proc of the window. However, since we always use |
|
// CreateChildCPParamMap2, and because the MAX source code shows us that it |
|
// always uses the same dialog proc for all windows created with that function, |
|
// we can simply store the address of that proc the first time we subclass and |
|
// use it for restoring every time thereafter (see the following DlgProc) |
|
|
|
static WNDPROC sOldStealthDlgProc = nil; |
|
|
|
static INT_PTR CALLBACK plStealthMouseOverrideProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) |
|
{ |
|
IParamMap2 *map = (IParamMap2 *)GetWindowLongPtr( hWnd, GWLP_USERDATA ); |
|
|
|
switch( msg ) |
|
{ |
|
case WM_LBUTTONDOWN: |
|
case WM_LBUTTONUP: |
|
case WM_MOUSEMOVE: |
|
{ |
|
// We don't want the COREInterface to process our mouse messages with RollupMouseMessage; |
|
// rather, we want IMtlParams to do it just like it would if we could actually call |
|
// CreateChildMParamMap2 |
|
IParamBlock2 *pb = map->GetParamBlock(); |
|
if( pb != nil ) |
|
{ |
|
plAnimStealthNode *stealth = (plAnimStealthNode *)pb->GetOwner(); |
|
if( stealth != nil ) |
|
{ |
|
plPassMtlBase *mtl = (plPassMtlBase *)stealth->GetParentMtl(); |
|
mtl->fIMtlParams->RollupMouseMessage( hWnd, msg, wParam, lParam ); |
|
} |
|
} |
|
return 0; |
|
} |
|
} |
|
|
|
if( sOldStealthDlgProc != nil ) |
|
return CallWindowProc( sOldStealthDlgProc, hWnd, msg, wParam, lParam ); |
|
else |
|
return 0; |
|
} |
|
|
|
BOOL plStealthDlgProc::DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
IParamBlock2 *pb = map->GetParamBlock(); |
|
plAnimStealthNode *stealth = (plAnimStealthNode *)pb->GetOwner(); |
|
|
|
switch (msg) |
|
{ |
|
case WM_INITDIALOG: |
|
{ |
|
// Install our override proc so we can capture mouse messages ourselves. |
|
// Note that the first time, we grab the old proc so we can restore with that |
|
// one every time after, since they should always be the same proc |
|
WNDPROC old = (WNDPROC)SetWindowLongPtr( hWnd, DWLP_DLGPROC, (LONG_PTR)plStealthMouseOverrideProc ); |
|
if( sOldStealthDlgProc == nil ) |
|
sOldStealthDlgProc = old; |
|
|
|
fhWnd = hWnd; |
|
IInitControls( stealth, pb ); |
|
|
|
return TRUE; |
|
} |
|
|
|
case WM_DESTROY: |
|
// Restore our old proc |
|
SetWindowLongPtr( hWnd, DWLP_DLGPROC, (LONG_PTR)sOldStealthDlgProc ); |
|
break; |
|
|
|
case WM_ENABLE: |
|
// The entire dialog was either enabled or disabled. |
|
break; |
|
|
|
case WM_COMMAND: |
|
// Loop selection changed |
|
if( LOWORD( wParam ) == IDC_LOOPS && HIWORD( wParam ) == CBN_SELCHANGE ) |
|
{ |
|
// If a loop is selected, save it |
|
HWND hCombo = (HWND)lParam; |
|
int sel = SendMessage( hCombo, CB_GETCURSEL, 0, 0 ); |
|
if( sel != CB_ERR ) |
|
{ |
|
if( SendMessage( hCombo, CB_GETITEMDATA, sel, 0 ) == kName ) |
|
{ |
|
char buf[256]; |
|
SendMessage( hCombo, CB_GETLBTEXT, sel, (LPARAM)buf ); |
|
pb->SetValue( (ParamID)plAnimStealthNode::kPBLoopName, 0, buf ); |
|
} |
|
else |
|
pb->SetValue( (ParamID)plAnimStealthNode::kPBLoopName, 0, "" ); |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
// Auto-start or loop checkbox checked |
|
if( LOWORD( wParam ) == IDC_LOOP && HIWORD( wParam ) == BN_CLICKED ) |
|
{ |
|
BOOL checked = ( SendMessage( (HWND)lParam, BM_GETCHECK, 0, 0 ) == BST_CHECKED ); |
|
|
|
pb->SetValue( plAnimStealthNode::kPBLoop, 0, checked ); |
|
EnableWindow( GetDlgItem( hWnd, IDC_LOOPS ), checked ); |
|
return TRUE; |
|
} |
|
|
|
// Refresh clicked |
|
else if( LOWORD( wParam ) == IDC_REFRESH_ANIMS && HIWORD( wParam ) == BN_CLICKED ) |
|
{ |
|
IInitControls( stealth, pb ); |
|
return TRUE; |
|
} |
|
|
|
break; |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
void plStealthDlgProc::SetThing(ReferenceTarget *m) |
|
{ |
|
plAnimStealthNode *stealth = (plAnimStealthNode *)m; |
|
IParamBlock2 *pb = stealth->GetParamBlockByID( plAnimStealthNode::kBlockPB ); |
|
|
|
IInitControls( stealth, pb ); |
|
} |
|
|
|
void plStealthDlgProc::IDeleteSegMap() |
|
{ |
|
// If we have a segment map, delete the memory associated with it |
|
DeleteSegmentMap( fSegMap ); |
|
fSegMap = nil; |
|
} |
|
|
|
void plStealthDlgProc::ISetSel(HWND hCombo, const char *name) |
|
{ |
|
// If there is a name, try and set that |
|
if( name && strcmp( name, "" ) ) |
|
{ |
|
int idx = SendMessage( hCombo, CB_FINDSTRINGEXACT, -1, (LPARAM)name ); |
|
// If we can't find the saved name add a "not found" entry, so they know what it was |
|
if( idx == -1 ) |
|
{ |
|
char buf[256]; |
|
sprintf( buf, "(not found) %s", name ); |
|
idx = SendMessage( hCombo, CB_ADDSTRING, 0, (LPARAM)buf ); |
|
SendMessage( hCombo, CB_SETITEMDATA, idx, kInvalid ); |
|
} |
|
|
|
SendMessage( hCombo, CB_SETCURSEL, idx, 0 ); |
|
} |
|
// No name, set it to none |
|
else |
|
{ |
|
int count = SendMessage( hCombo, CB_GETCOUNT, 0, 0 ); |
|
for( int i = 0; i < count; i++ ) |
|
{ |
|
if( SendMessage( hCombo, CB_GETITEMDATA, i, 0 ) == kDefault ) |
|
SendMessage( hCombo, CB_SETCURSEL, i, 0 ); |
|
} |
|
} |
|
} |
|
|
|
void plStealthDlgProc::IInitControls( plAnimStealthNode *stealth, IParamBlock2 *pb ) |
|
{ |
|
IDeleteSegMap(); |
|
|
|
if( stealth->GetParentMtl() != nil ) |
|
{ |
|
fSegMap = GetAnimSegmentMap( stealth->GetParentMtl(), nil ); |
|
|
|
ILoadLoops( pb ); |
|
} |
|
else |
|
{ |
|
// ?? What should we do? |
|
fSegMap = nil; |
|
hsStatusMessage( "No parent material yet in plStealthDlgProc::IInitControls()...not good..." ); |
|
} |
|
|
|
// Enable/disable the loop dropdown |
|
EnableWindow( GetDlgItem( fhWnd, IDC_LOOPS ), pb->GetInt( (ParamID)plAnimStealthNode::kPBLoop ) ); |
|
} |
|
|
|
void plStealthDlgProc::ILoadLoops(IParamBlock2 *pb) |
|
{ |
|
HWND hLoops = GetDlgItem( fhWnd, IDC_LOOPS ); |
|
SendMessage( hLoops, CB_RESETCONTENT, 0, 0 ); |
|
|
|
// Add the default option |
|
int defIdx = SendMessage( hLoops, CB_ADDSTRING, 0, (LPARAM)ENTIRE_ANIMATION_NAME ); |
|
SendMessage( hLoops, CB_SETITEMDATA, defIdx, kDefault ); |
|
|
|
plString segName = plString::FromUtf8( pb->GetStr( (ParamID)plAnimStealthNode::kPBName ) ); |
|
if( segName.IsNull() || fSegMap == nil ) |
|
{ |
|
// Default of "entire animation", no other loop options |
|
SendMessage( hLoops, CB_SETCURSEL, defIdx, 0 ); |
|
return; |
|
} |
|
|
|
SegmentSpec *animSpec = (*fSegMap)[ segName ]; |
|
if( animSpec && fSegMap ) |
|
{ |
|
// for each segment we found: |
|
for( SegmentMap::iterator i = fSegMap->begin(); i != fSegMap->end(); i++ ) |
|
{ |
|
SegmentSpec *spec = (*i).second; |
|
|
|
if( spec->fType == SegmentSpec::kLoop ) |
|
{ |
|
// If the loop is contained by the animation, add it |
|
if( (spec->fStart == -1 || spec->fStart >= animSpec->fStart) && |
|
(spec->fEnd == -1 || spec->fEnd <= animSpec->fEnd) ) |
|
{ |
|
// Add the name |
|
int idx = SendMessage( hLoops, CB_ADDSTRING, 0, (LPARAM)spec->fName.c_str() ); |
|
SendMessage( hLoops, CB_SETITEMDATA, idx, kName ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
ISetSel( hLoops, pb->GetStr( (ParamID)plAnimStealthNode::kPBLoopName ) ); |
|
} |
|
|
|
void plAnimStealthNode::BeginEditParams(IObjParam *ip, ULONG flags, Animatable *prev) |
|
{ |
|
fClassDesc->BeginEditParams(ip, this, flags, prev); |
|
} |
|
|
|
void plAnimStealthNode::EndEditParams(IObjParam *ip, ULONG flags, Animatable *next) |
|
{ |
|
fClassDesc->EndEditParams(ip, this, flags, next); |
|
} |
|
|
|
//// ReleaseDlg ////////////////////////////////////////////////////////////// |
|
|
|
void plAnimStealthNode::ReleaseDlg( void ) |
|
{ |
|
IParamMap2 *map = fParamBlock->GetMap(); |
|
fParamBlock->SetMap( nil ); |
|
if( map != nil ) |
|
DestroyChildCPParamMap2( map ); |
|
} |
|
|
|
//// SwitchDlg /////////////////////////////////////////////////////////////// |
|
// Switch underlying objects in the dialog (to avoid unnecessary deletion/ |
|
// recreations) |
|
|
|
void plAnimStealthNode::SwitchDlg( plAnimStealthNode *toSwitchTo ) |
|
{ |
|
IParamMap2 *map = fParamBlock->GetMap(); |
|
|
|
fParamBlock->SetMap( nil ); |
|
toSwitchTo->fParamBlock->SetMap( map ); |
|
|
|
map->SetParamBlock( toSwitchTo->fParamBlock ); |
|
map->SetThing( (ReferenceTarget *)toSwitchTo ); |
|
map->Invalidate(); |
|
map->UpdateUI( 0 ); |
|
} |
|
|
|
//// CreateAndEmbedDlg /////////////////////////////////////////////////////// |
|
// Create the dialog for this object and place it inside the given dialog, |
|
// centering it in the given control if any. |
|
|
|
bool plAnimStealthNode::CreateAndEmbedDlg( IParamMap2 *parentMap, IMtlParams *parentParams, HWND frameCtrl ) |
|
{ |
|
IParamMap2 *map = CreateChildCPParamMap2( fParamBlock, GetCOREInterface(), hInstance, |
|
parentMap, MAKEINTRESOURCE( IDD_STEALTH_ANIM ), |
|
nil, &sStealthDlgProc ); |
|
fParamBlock->SetMap( map ); |
|
|
|
if( frameCtrl != nil ) |
|
{ |
|
HWND child = fParamBlock->GetMap()->GetHWnd(); |
|
RECT childFrame, centerFrame; |
|
|
|
::GetClientRect( child, &childFrame ); |
|
::GetWindowRect( frameCtrl, ¢erFrame ); |
|
::MapWindowPoints( nil, parentMap->GetHWnd(), (POINT *)¢erFrame, 2 ); |
|
|
|
int frameWidth = centerFrame.right - centerFrame.left; |
|
int frameHeight = centerFrame.bottom - centerFrame.top; |
|
int childWidth = childFrame.right - childFrame.left; |
|
int childHeight = childFrame.bottom - childFrame.top; |
|
|
|
::OffsetRect( &childFrame, ( frameWidth - childWidth ) >> 1, ( frameHeight - childHeight ) >> 1 ); |
|
::OffsetRect( &childFrame, centerFrame.left, centerFrame.top ); |
|
|
|
::SetWindowPos( child, nil, childFrame.left, childFrame.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//// GetWinDlg /////////////////////////////////////////////////////////////// |
|
// Get the actual window handle of the currently active dialog displaying us |
|
|
|
HWND plAnimStealthNode::GetWinDlg( void ) const |
|
{ |
|
IParamMap2 *map = fParamBlock->GetMap(); |
|
if( map != nil ) |
|
return map->GetHWnd(); |
|
|
|
return nil; |
|
} |
|
|
|
//// Picker Dialog for Restricted Animation Components ////////////////////////////////////////// |
|
|
|
class plPickAnimStealthNode : public plPickMtlNode |
|
{ |
|
protected: |
|
ParamID fTypeID; |
|
|
|
void IAddUserType(HWND hList) |
|
{ |
|
int type = fPB->GetInt(fTypeID); |
|
|
|
int idx = ListBox_AddString( hList, kUseParamBlockNodeString ); |
|
if (type == plAnimObjInterface::kUseParamBlockNode && !fPB->GetINode(fNodeParamID)) |
|
ListBox_SetCurSel(hList, idx); |
|
|
|
|
|
idx = ListBox_AddString( hList, kUseOwnerNodeString ); |
|
if (type == plAnimObjInterface::kUseOwnerNode) |
|
ListBox_SetCurSel(hList, idx); |
|
} |
|
|
|
void ISetUserType(plMaxNode* node, const char* userType) |
|
{ |
|
if( strcmp( userType, kUseParamBlockNodeString ) == 0 ) |
|
{ |
|
ISetNodeValue(nil); |
|
fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseParamBlockNode); |
|
} |
|
else if( strcmp(userType, kUseOwnerNodeString ) == 0 ) |
|
{ |
|
ISetNodeValue(nil); |
|
fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseOwnerNode); |
|
} |
|
else |
|
fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseParamBlockNode); |
|
} |
|
|
|
public: |
|
plPickAnimStealthNode(IParamBlock2* pb, ParamID nodeParamID, ParamID typeID, Mtl *mtl) : |
|
plPickMtlNode(pb, nodeParamID, mtl), fTypeID(typeID) |
|
{ |
|
} |
|
}; |
|
|
|
//// plAnimObjInterface Functions //////////////////////////////////////////// |
|
|
|
void plAnimStealthNode::PickTargetNode( IParamBlock2 *destPB, ParamID destParamID, ParamID typeID ) |
|
{ |
|
plPickAnimStealthNode pick( destPB, destParamID, typeID, (Mtl *)GetParentMtl() ); |
|
pick.DoPick(); |
|
} |
|
|
|
plString plAnimStealthNode::GetIfaceSegmentName( bool allowNil ) |
|
{ |
|
// When sending messages to material animations, they're already addressed for the right |
|
// layer, no need for a segment name |
|
return plString::Null; |
|
} |
|
|
|
//// Parameter Access Functions ////////////////////////////////////////////// |
|
|
|
#pragma warning( push ) |
|
#pragma warning( disable:4800 ) // Forcing value to bool true or false (go figure, i'm even explicitly casting) |
|
bool plAnimStealthNode::GetAutoStart( void ) const { return (bool)fParamBlock->GetInt( (ParamID)kPBAutoStart ); } |
|
void plAnimStealthNode::SetAutoStart( bool b ) { fParamBlock->SetValue( (ParamID)kPBAutoStart, 0, (int)b ); }; |
|
|
|
bool plAnimStealthNode::GetLoop( void ) const { return fParamBlock->GetInt( (ParamID)kPBLoop ); } |
|
plString plAnimStealthNode::GetLoopName( void ) const { return plString::FromUtf8( fParamBlock->GetStr( (ParamID)kPBLoopName ) ); } |
|
void plAnimStealthNode::SetLoop( bool b, const plString &name ) |
|
{ |
|
fParamBlock->SetValue( (ParamID)kPBLoop, 0, (int)b ); |
|
fParamBlock->SetValue( (ParamID)kPBLoopName, 0, (char *)name.c_str() ); |
|
} |
|
|
|
uint8_t plAnimStealthNode::GetEaseInType( void ) const { return (uint8_t)fParamBlock->GetInt( (ParamID)kPBEaseInType ); } |
|
float plAnimStealthNode::GetEaseInLength( void ) const { return (float)fParamBlock->GetFloat( (ParamID)kPBEaseInLength ); } |
|
float plAnimStealthNode::GetEaseInMin( void ) const { return (float)fParamBlock->GetFloat( (ParamID)kPBEaseInMin ); } |
|
float plAnimStealthNode::GetEaseInMax( void ) const { return (float)fParamBlock->GetFloat( (ParamID)kPBEaseInMax ); } |
|
void plAnimStealthNode::SetEaseIn( uint8_t type, float length, float min, float max ) |
|
{ |
|
fParamBlock->SetValue( (ParamID)kPBEaseInType, 0, (int)type ); |
|
fParamBlock->SetValue( (ParamID)kPBEaseInLength, 0, (float)length ); |
|
fParamBlock->SetValue( (ParamID)kPBEaseInMin, 0, (float)min ); |
|
fParamBlock->SetValue( (ParamID)kPBEaseInMax, 0, (float)max ); |
|
} |
|
|
|
uint8_t plAnimStealthNode::GetEaseOutType( void ) const { return (uint8_t)fParamBlock->GetInt( (ParamID)kPBEaseOutType ); } |
|
float plAnimStealthNode::GetEaseOutLength( void ) const { return (float)fParamBlock->GetFloat( (ParamID)kPBEaseOutLength ); } |
|
float plAnimStealthNode::GetEaseOutMin( void ) const { return (float)fParamBlock->GetFloat( (ParamID)kPBEaseOutMin ); } |
|
float plAnimStealthNode::GetEaseOutMax( void ) const { return (float)fParamBlock->GetFloat( (ParamID)kPBEaseOutMax ); } |
|
void plAnimStealthNode::SetEaseOut( uint8_t type, float length, float min, float max ) |
|
{ |
|
fParamBlock->SetValue( (ParamID)kPBEaseOutType, 0, (int)type ); |
|
fParamBlock->SetValue( (ParamID)kPBEaseOutLength, 0, (float)length ); |
|
fParamBlock->SetValue( (ParamID)kPBEaseOutMin, 0, (float)min ); |
|
fParamBlock->SetValue( (ParamID)kPBEaseOutMax, 0, (float)max ); |
|
} |
|
#pragma warning( pop ) // Forcing value to bool true or false (go figure, i'm even explicitly casting) |
|
|
|
//// Parent Accessor Functions /////////////////////////////////////////////// |
|
|
|
plStealthNodeAccessor &plStealthNodeAccessor::GetInstance( void ) |
|
{ |
|
static plStealthNodeAccessor instance; |
|
return instance; |
|
} |
|
|
|
void plStealthNodeAccessor::ISetParent( ReferenceTarget *target, plPassMtlBase *parent ) |
|
{ |
|
if( target != nil && target->ClassID() == ANIMSTEALTH_CLASSID ) |
|
{ |
|
( (plAnimStealthNode *)target )->SetParentMtl( parent ); |
|
} |
|
} |
|
|
|
void plStealthNodeAccessor::TabChanged( tab_changes changeCode, Tab<PB2Value> *tab, ReferenceMaker *owner, |
|
ParamID id, int tabIndex, int count ) |
|
{ |
|
if( changeCode == tab_insert || changeCode == tab_append ) |
|
{ |
|
if( owner->SuperClassID() != MATERIAL_CLASS_ID ) |
|
return; |
|
plPassMtlBase *mtl = (plPassMtlBase *)owner; |
|
|
|
while( count > 0 ) |
|
{ |
|
ISetParent( (*tab)[ tabIndex ].r, mtl ); |
|
tabIndex++; |
|
count--; |
|
} |
|
} |
|
else if( changeCode == tab_delete || changeCode == tab_ref_deleted ) |
|
{ |
|
// How are we supposed to handle this if we don't even get a stinkin pointer?? |
|
} |
|
} |
|
|
|
void plStealthNodeAccessor::Set( PB2Value &v, ReferenceMaker *owner, ParamID id, int tabIndex, TimeValue t ) |
|
{ |
|
// Bit of error checking |
|
if( owner->SuperClassID() != MATERIAL_CLASS_ID ) |
|
return; |
|
|
|
plPassMtlBase *mtl = (plPassMtlBase *)owner; |
|
|
|
IParamBlock2 *pb = mtl->fAnimPB; |
|
|
|
// A stealth node paramBlock value just got set. First make sure we |
|
// un-set the old stealth's parent |
|
ISetParent( pb->GetReferenceTarget( id, tabIndex ), nil ); |
|
|
|
// So make sure that the stealth node that was just added gets its parent mtl set properly |
|
ISetParent( v.r, mtl ); |
|
} |
|
|
|
|