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.
971 lines
30 KiB
971 lines
30 KiB
14 years ago
|
/*==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==*/
|
||
|
//////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// 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 "hsTypes.h"
|
||
|
#include "plAnimStealthNode.h"
|
||
|
#include "plPassMtlBase.h"
|
||
|
#include "resource.h"
|
||
|
|
||
|
#include "../MaxComponent/plMaxAnimUtils.h"
|
||
|
#include "../MaxComponent/plPickNodeBase.h"
|
||
|
|
||
|
#include "iparamm2.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 TRACKED_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:
|
||
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||
|
void DeleteThis() { IDeleteSegMap(); }
|
||
|
void SetThing(ReferenceTarget *m);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
const char *plAnimStealthNode::GetSegmentName( void ) const
|
||
|
{
|
||
|
const char *str = fParamBlock->GetStr( (ParamID)kPBName );
|
||
|
if( str == nil || str[ 0 ] == 0 )
|
||
|
return ENTIRE_ANIMATION_NAME;
|
||
|
return 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;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
hsBool 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();
|
||
|
|
||
|
RefList &refList = GetRefList();
|
||
|
RefListItem *item = refList.FirstItem();
|
||
|
while( item != nil )
|
||
|
{
|
||
|
TSTR s;
|
||
|
item->maker->GetClassName( s );
|
||
|
|
||
|
if( item->maker->SuperClassID() == BASENODE_CLASS_ID && !CanConvertToStealth( (INode *)( item->maker ) ) )
|
||
|
return true; // Horray, a node has a ref to us!
|
||
|
|
||
|
else if( item->maker->ClassID() == Class_ID(MULTI_CLASS_ID,0) )
|
||
|
{
|
||
|
// Multi-sub, run the refs on that guy (we only go one up)
|
||
|
Mtl *multisub = (Mtl *)item->maker;
|
||
|
RefList &refList2 = multisub->GetRefList();
|
||
|
RefListItem *item2 = refList.FirstItem();
|
||
|
while( item2 != nil )
|
||
|
{
|
||
|
if( item2->maker->SuperClassID() == BASENODE_CLASS_ID )
|
||
|
return true; // Horray, a node has a ref to us!
|
||
|
item2 = item2->next;
|
||
|
}
|
||
|
|
||
|
// No go, keep trying
|
||
|
}
|
||
|
else if( item->maker->SuperClassID() == MATERIAL_CLASS_ID )
|
||
|
{
|
||
|
int q = 0;
|
||
|
}
|
||
|
|
||
|
item = item->next;
|
||
|
}
|
||
|
|
||
|
// Enum dependents
|
||
|
int i;
|
||
|
|
||
|
plGetRefs callback;
|
||
|
GetParentMtl()->EnumDependents( &callback );
|
||
|
for( 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.
|
||
|
RefList &refList = GetRefList();
|
||
|
RefListItem *item = refList.FirstItem();
|
||
|
while( item )
|
||
|
{
|
||
|
if( item->maker->SuperClassID() == BASENODE_CLASS_ID )
|
||
|
return (INode *)item->maker;
|
||
|
|
||
|
item = item->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 );
|
||
|
|
||
|
const char *segName = pb->GetStr( (ParamID)plAnimStealthNode::kPBName );
|
||
|
if( segName == nil || 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 );
|
||
|
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( hsStrEQ( userType, kUseParamBlockNodeString ) )
|
||
|
{
|
||
|
ISetNodeValue(nil);
|
||
|
fPB->SetValue(fTypeID, 0, plAnimObjInterface::kUseParamBlockNode);
|
||
|
}
|
||
|
else if( hsStrEQ(userType, kUseOwnerNodeString ) )
|
||
|
{
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
const char *plAnimStealthNode::GetIfaceSegmentName( hsBool allowNil )
|
||
|
{
|
||
|
// When sending messages to material animations, they're already addressed for the right
|
||
|
// layer, no need for a segment name
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
//// 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 ); }
|
||
|
const char *plAnimStealthNode::GetLoopName( void ) const { return fParamBlock->GetStr( (ParamID)kPBLoopName ); }
|
||
|
void plAnimStealthNode::SetLoop( bool b, const char *name )
|
||
|
{
|
||
|
fParamBlock->SetValue( (ParamID)kPBLoop, 0, (int)b );
|
||
|
if( name == nil )
|
||
|
fParamBlock->SetValue( (ParamID)kPBLoopName, 0, "" );
|
||
|
else
|
||
|
fParamBlock->SetValue( (ParamID)kPBLoopName, 0, (char *)name );
|
||
|
}
|
||
|
|
||
|
UInt8 plAnimStealthNode::GetEaseInType( void ) const { return (UInt8)fParamBlock->GetInt( (ParamID)kPBEaseInType ); }
|
||
|
hsScalar plAnimStealthNode::GetEaseInLength( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseInLength ); }
|
||
|
hsScalar plAnimStealthNode::GetEaseInMin( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseInMin ); }
|
||
|
hsScalar plAnimStealthNode::GetEaseInMax( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseInMax ); }
|
||
|
void plAnimStealthNode::SetEaseIn( UInt8 type, hsScalar length, hsScalar min, hsScalar 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 plAnimStealthNode::GetEaseOutType( void ) const { return (UInt8)fParamBlock->GetInt( (ParamID)kPBEaseOutType ); }
|
||
|
hsScalar plAnimStealthNode::GetEaseOutLength( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseOutLength ); }
|
||
|
hsScalar plAnimStealthNode::GetEaseOutMin( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseOutMin ); }
|
||
|
hsScalar plAnimStealthNode::GetEaseOutMax( void ) const { return (hsScalar)fParamBlock->GetFloat( (ParamID)kPBEaseOutMax ); }
|
||
|
void plAnimStealthNode::SetEaseOut( UInt8 type, hsScalar length, hsScalar min, hsScalar 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 );
|
||
|
}
|
||
|
|