/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ ////////////////////////////////////////////////////////////////////////////// // // // 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 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 *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 ); }