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.
678 lines
23 KiB
678 lines
23 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==*/ |
|
////////////////////////////////////////////////////////////////////////////// |
|
// // |
|
// plPassMtlBase - Base class for all Plasma MAX materials // |
|
// // |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
#include "HeadSpin.h" |
|
#include "hsBitVector.h" |
|
#include "hsWindows.h" |
|
#include "../resource.h" |
|
|
|
#include <iparamb2.h> |
|
#include <max.h> |
|
#include <notetrck.h> |
|
#include <notify.h> |
|
#pragma hdrstop |
|
|
|
#include "plPassMtlBase.h" |
|
#include "plPassBaseParamIDs.h" |
|
#include "plNoteTrackWatcher.h" |
|
#include "plAnimStealthNode.h" |
|
|
|
#include "MaxComponent/plMaxAnimUtils.h" |
|
#include "MaxMain/plPlasmaRefMsgs.h" |
|
|
|
// For converting from a MAX Mtl |
|
#include "plPassMtl.h" |
|
#include "plBumpMtl.h" |
|
#include "plDecalMtl.h" |
|
|
|
using namespace plPassBaseParamIDs; |
|
|
|
IMtlParams *plPassMtlBase::fIMtlParams = nil; |
|
|
|
//// plPostLoadHandler /////////////////////////////////////////////////////// |
|
// Small class to keep track of all the materials to update after load |
|
|
|
class plPostLoadHandler |
|
{ |
|
static bool fLoading; |
|
static hsTArray<plPassMtlBase *> fPostLoads; |
|
|
|
public: |
|
static bool IsLoading() { return fLoading; } |
|
|
|
static void PostLoadFixupFunction( void *param, NotifyInfo *info ) |
|
{ |
|
fLoading = false; |
|
|
|
for( int i = 0; i < fPostLoads.GetCount(); i++ ) |
|
fPostLoads[ i ]->PostLoadAnimPBFixup(); |
|
|
|
fPostLoads.Reset(); |
|
UnRegisterNotification( PostLoadFixupFunction, param, NOTIFY_FILE_POST_OPEN ); |
|
UnRegisterNotification( PostLoadFixupFunction, param, NOTIFY_FILE_POST_MERGE ); |
|
} |
|
|
|
static void AddPostLoad( plPassMtlBase *mtl ) |
|
{ |
|
fLoading = true; |
|
|
|
if( fPostLoads.GetCount() == 0 ) |
|
{ |
|
RegisterNotification( PostLoadFixupFunction, mtl, NOTIFY_FILE_POST_OPEN ); |
|
RegisterNotification( PostLoadFixupFunction, mtl, NOTIFY_FILE_POST_MERGE ); |
|
} |
|
|
|
mtl->SetLoadingFlag( true ); |
|
fPostLoads.Append( mtl ); |
|
} |
|
|
|
static void RemovePostLoad( plPassMtlBase *mtl ) |
|
{ |
|
for( int i = 0; i < fPostLoads.GetCount(); i++ ) |
|
{ |
|
if( fPostLoads[ i ] == mtl ) |
|
{ |
|
fPostLoads.Remove( i ); |
|
break; |
|
} |
|
} |
|
} |
|
}; |
|
hsTArray<plPassMtlBase *> plPostLoadHandler::fPostLoads; |
|
bool plPostLoadHandler::fLoading = false; |
|
|
|
|
|
plPassMtlBase::plPassMtlBase( BOOL loading ) : fNTWatcher( nil ), fBasicPB(NULL), fAdvPB(NULL), fLayersPB(NULL), fAnimPB(NULL), |
|
fLoading( loading ) |
|
{ |
|
fNTWatcher = new plNoteTrackWatcher( this ); |
|
Reset(); |
|
} |
|
|
|
plPassMtlBase::~plPassMtlBase() |
|
{ |
|
if( fLoading ) |
|
plPostLoadHandler::RemovePostLoad( this ); |
|
|
|
// Force the watcher's parent pointer to nil, otherwise the de-ref will attempt to re-delete us |
|
fNTWatcher->SetReference( plNoteTrackWatcher::kRefParentMtl, nil ); |
|
delete fNTWatcher; |
|
fNTWatcher = nil; |
|
|
|
// Manually delete our notetrack refs, otherwise there'll be hell to pay |
|
for( int i = 0; i < fNotetracks.GetCount(); i++ ) |
|
{ |
|
if( fNotetracks[ i ] != nil ) |
|
DeleteReference( kRefNotetracks + i ); |
|
} |
|
} |
|
|
|
void plPassMtlBase::Reset( void ) |
|
{ |
|
fIValid.SetEmpty(); |
|
} |
|
|
|
//// Stealth Accessors /////////////////////////////////////////////////////// |
|
|
|
int plPassMtlBase::GetNumStealths( void ) |
|
{ |
|
return IGetNumStealths( true ); |
|
} |
|
|
|
plAnimStealthNode *plPassMtlBase::GetStealth( int index ) |
|
{ |
|
return IGetStealth( index, false ); |
|
} |
|
|
|
int plPassMtlBase::IGetNumStealths( bool update ) |
|
{ |
|
if( update ) |
|
IUpdateAnimNodes(); |
|
|
|
return fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); |
|
} |
|
|
|
plAnimStealthNode *plPassMtlBase::IGetStealth( int index, bool update ) |
|
{ |
|
if( update ) |
|
IUpdateAnimNodes(); |
|
|
|
return (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, index ); |
|
} |
|
|
|
plAnimStealthNode *plPassMtlBase::IFindStealth( const plString &segmentName ) |
|
{ |
|
int i; |
|
|
|
|
|
for( i = 0; i < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); i++ ) |
|
{ |
|
plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, i ); |
|
plString name = node->GetSegmentName(); |
|
|
|
if( node != nil && name.Compare( segmentName ) == 0 ) |
|
{ |
|
return node; |
|
} |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
//// IVerifyStealthPresent /////////////////////////////////////////////////// |
|
// Ensures that we have a stealth for the given segment. |
|
|
|
plAnimStealthNode *plPassMtlBase::IVerifyStealthPresent( const plString &animName ) |
|
{ |
|
// If we're in the middle of loading, don't check |
|
if (plPostLoadHandler::IsLoading()) |
|
return nil; |
|
|
|
plAnimStealthNode *stealth = IFindStealth( animName ); |
|
if( stealth == nil ) |
|
{ |
|
// New segment, add a new stealth node |
|
stealth = (plAnimStealthNode *)GetCOREInterface()->CreateInstance( HELPER_CLASS_ID, ANIMSTEALTH_CLASSID ); |
|
INode *node = GetCOREInterface()->CreateObjectNode( stealth ); |
|
stealth->SetSegment( ( animName.Compare(ENTIRE_ANIMATION_NAME) != 0 ) ? animName.c_str() : nil ); |
|
stealth->SetNodeName( GetName() ); |
|
node->Freeze( true ); |
|
|
|
// Skip the attach, since we might not find a valid INode. This will leave the node attached to the scene |
|
// root, which is fine. Since we just care about it being SOMEWHERE in the scene hierarchy |
|
/* |
|
if( fAnimPB->Count( (ParamID)kPBAnimStealthNodes ) > 0 ) |
|
{ |
|
plAnimStealthNode *first = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, 0 ); |
|
first->GetINode()->AttachChild( node ); |
|
} |
|
*/ |
|
|
|
fAnimPB->Append( (ParamID)kPBAnimStealthNodes, 1, (ReferenceTarget **)&stealth ); |
|
|
|
plString realName = stealth->GetSegmentName(); |
|
|
|
fStealthsChanged = true; |
|
} |
|
else |
|
{ |
|
// Exists already, we're ok |
|
stealth->SetParentMtl( this ); |
|
} |
|
return stealth; |
|
} |
|
|
|
//// Change Callbacks //////////////////////////////////////////////////////// |
|
|
|
void plPassMtlBase::RegisterChangeCallback( plMtlChangeCallback *callback ) |
|
{ |
|
if( fChangeCallbacks.Find( callback ) == fChangeCallbacks.kMissingIndex ) |
|
fChangeCallbacks.Append( callback ); |
|
} |
|
|
|
void plPassMtlBase::UnregisterChangeCallback( plMtlChangeCallback *callback ) |
|
{ |
|
int idx = fChangeCallbacks.Find( callback ); |
|
if( idx != fChangeCallbacks.kMissingIndex ) |
|
fChangeCallbacks.Remove( idx ); |
|
} |
|
|
|
//// IUpdateAnimNodes //////////////////////////////////////////////////////// |
|
// Updates the list of stealth nodes in the anim paramBlock to match our |
|
// list of anim segments. |
|
|
|
void plPassMtlBase::IUpdateAnimNodes( void ) |
|
{ |
|
// Our beautiful hack, to make sure we don't update until we actually are loaded |
|
if( fLoading ) |
|
return; |
|
|
|
SegmentMap *segMap = GetAnimSegmentMap( this, nil ); |
|
|
|
hsTArray<plAnimStealthNode *> goodNodes; |
|
|
|
|
|
// Keep track of whether we change anything |
|
fStealthsChanged = false; |
|
|
|
// Verify one for "entire animation" |
|
plAnimStealthNode *stealth = IVerifyStealthPresent( ENTIRE_ANIMATION_NAME ); |
|
goodNodes.Append( stealth ); |
|
|
|
// Verify segment nodes |
|
if( segMap != nil ) |
|
{ |
|
for( SegmentMap::iterator i = segMap->begin(); i != segMap->end(); i++ ) |
|
{ |
|
SegmentSpec *spec = (*i).second; |
|
|
|
if( spec->fType == SegmentSpec::kAnim ) |
|
{ |
|
plAnimStealthNode *stealth = IVerifyStealthPresent( spec->fName ); |
|
goodNodes.Append( stealth ); |
|
} |
|
} |
|
|
|
DeleteSegmentMap( segMap ); |
|
} |
|
|
|
// Remove nodes that no longer have segments |
|
int idx; |
|
for( idx = 0; idx < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); ) |
|
{ |
|
plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, idx ); |
|
|
|
if( node != nil && goodNodes.Find( node ) == goodNodes.kMissingIndex ) |
|
{ |
|
fAnimPB->Delete( (ParamID)kPBAnimStealthNodes, idx, 1 ); |
|
// GetCOREInterface()->DeleteNode( node->GetINode() ); |
|
fStealthsChanged = true; |
|
} |
|
else |
|
idx++; |
|
} |
|
|
|
if( fStealthsChanged ) |
|
{ |
|
// Yup, our list of stealths got updated. Notify everyone of such. |
|
for( idx = 0; idx < fChangeCallbacks.GetCount(); idx++ ) |
|
fChangeCallbacks[ idx ]->SegmentListChanged(); |
|
} |
|
} |
|
|
|
//// NameChanged ///////////////////////////////////////////////////////////// |
|
// Notify from NTWatcher so we can update the names of our stealth nodes |
|
|
|
void plPassMtlBase::NameChanged( void ) |
|
{ |
|
for( int idx = 0; idx < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); idx++ ) |
|
{ |
|
plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, idx ); |
|
if( node != nil ) |
|
node->SetNodeName( GetName() ); |
|
} |
|
} |
|
|
|
//// NoteTrackAdded/Removed ////////////////////////////////////////////////// |
|
// Notifies from NTWatcher so we can update our list of stealths |
|
|
|
void plPassMtlBase::NoteTrackAdded( void ) |
|
{ |
|
int i; |
|
|
|
|
|
// Make a ref to our new notetrack |
|
for( i = 0; i < NumNoteTracks(); i++ ) |
|
{ |
|
NoteTrack *track = GetNoteTrack( i ); |
|
|
|
if( fNotetracks.Find( track ) == fNotetracks.kMissingIndex ) |
|
{ |
|
ReplaceReference(kRefNotetracks + fNotetracks.GetCount(), track); |
|
break; |
|
} |
|
} |
|
|
|
for( i = 0; i < fChangeCallbacks.GetCount(); i++ ) |
|
fChangeCallbacks[ i ]->NoteTrackListChanged(); |
|
IUpdateAnimNodes(); |
|
} |
|
|
|
void plPassMtlBase::NoteTrackRemoved( void ) |
|
{ |
|
int i; |
|
hsBitVector stillThere; |
|
|
|
|
|
// Make a ref to our new notetrack |
|
for( i = 0; i < NumNoteTracks(); i++ ) |
|
{ |
|
NoteTrack *track = GetNoteTrack( i ); |
|
|
|
int idx = fNotetracks.Find( track ); |
|
if( idx != fNotetracks.kMissingIndex ) |
|
stillThere.Set( idx ); |
|
} |
|
|
|
for( i = 0; i < fNotetracks.GetCount(); i++ ) |
|
{ |
|
if( !stillThere.IsBitSet( i ) && fNotetracks[ i ] != nil ) |
|
{ |
|
// DeleteReference( kRefNotetracks + i ); |
|
SetReference( kRefNotetracks + i, nil ); |
|
} |
|
} |
|
|
|
for( i = 0; i < fChangeCallbacks.GetCount(); i++ ) |
|
fChangeCallbacks[ i ]->NoteTrackListChanged(); |
|
IUpdateAnimNodes(); |
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
//// MAX Ref Stuff /////////////////////////////////////////////////////////// |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
//// NumRefs ///////////////////////////////////////////////////////////////// |
|
|
|
int plPassMtlBase::NumRefs() |
|
{ |
|
return 4 + fNotetracks.GetCount(); |
|
} |
|
|
|
//// GetReference //////////////////////////////////////////////////////////// |
|
|
|
RefTargetHandle plPassMtlBase::GetReference( int i ) |
|
{ |
|
if( i >= kRefNotetracks && i < kRefNotetracks + fNotetracks.GetCount() ) |
|
return fNotetracks[ i - kRefNotetracks ]; |
|
return nullptr; |
|
} |
|
|
|
//// SetReference //////////////////////////////////////////////////////////// |
|
|
|
void plPassMtlBase::SetReference(int i, RefTargetHandle rtarg) |
|
{ |
|
if( i >= kRefNotetracks ) |
|
{ |
|
fNotetracks.ExpandAndZero(i - kRefNotetracks + 1); |
|
fNotetracks[i - kRefNotetracks] = (NoteTrack*)rtarg; |
|
} |
|
} |
|
|
|
//// NotifyRefChanged //////////////////////////////////////////////////////// |
|
|
|
RefResult plPassMtlBase::NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, |
|
PartID &partID, RefMessage message ) |
|
{ |
|
switch( message ) |
|
{ |
|
case REFMSG_CHANGE: |
|
fIValid.SetEmpty(); |
|
|
|
// see if this message came from a changing parameter in the pblock, |
|
// if so, limit rollout update to the changing item |
|
if (hTarget == fBasicPB || hTarget == fAdvPB || hTarget == fLayersPB || hTarget == fAnimPB) |
|
{ |
|
IParamBlock2 *pb = (IParamBlock2*)hTarget; |
|
|
|
ParamID changingParam = pb->LastNotifyParamID(); |
|
pb->GetDesc()->InvalidateUI( changingParam ); |
|
|
|
// And let the SceneWatcher know that the material on some of it's |
|
// referenced objects changed. |
|
NotifyDependents( FOREVER, PART_ALL, REFMSG_USER_MAT ); |
|
} |
|
else |
|
{ |
|
// Was it a notetrack ref? |
|
if( fNotetracks.Find( (NoteTrack *)hTarget ) != fNotetracks.kMissingIndex ) |
|
{ |
|
// Yup, so update our notetrack list |
|
IUpdateAnimNodes(); |
|
} |
|
} |
|
|
|
break; |
|
|
|
case REFMSG_TARGET_DELETED: |
|
NoteTrackRemoved(); |
|
break; |
|
|
|
} |
|
|
|
return REF_SUCCEED; |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
//// Standard IO (or non-standard, as the case may be) /////////////////////// |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
//// PostLoadAnimPBFixup ///////////////////////////////////////////////////// |
|
// Takes the old version of the anim paramblock and translates it into the |
|
// new version. |
|
// Note that there's an interesting (?) side effect of this: for new materials, |
|
// we'll incorrectly detect them as the "old" format and fix-up them as well. |
|
// This means that we'll end up with the same defaults for (Entire Animation) |
|
// that we had for the old materials. We can easily change the defaults by |
|
// changing the defaults for the old paramblock though. |
|
// Also, we go ahead and re-work the stealth parent pointers here, since it |
|
// appears to be the ONLY time we can do it and have it reliably work. ARRGH! |
|
// |
|
// History of the options that we CAN'T do and why (and hence why this): |
|
// - ParamBlock accessor doesn't work. For some reason, the accessor isn't |
|
// always called on load, even with P_CALLSETS_ON_LOAD specified |
|
// - Doing it on Load() doesn't work, because neither the ParamBlocks are |
|
// guaranteed to be fully loaded (with their tabs filled) nor are the |
|
// notetracks necessarily attached yet |
|
// - Notetracks can also possibly be attached BEFORE load, which doesn't |
|
// do us a damned bit of good, so we need to make sure we're fully |
|
// loaded before we run this function. Unfortunately, the only time |
|
// we're guaranteed THAT is by a FILE_POST_OPEN notify. (post-load |
|
// callbacks don't work because they're called right after our object |
|
// is loaded but not necessarily before the notetracks are attached) |
|
|
|
void plPassMtlBase::PostLoadAnimPBFixup( void ) |
|
{ |
|
SetLoadingFlag( false ); |
|
|
|
#ifdef MCN_UPGRADE_OLD_ANIM_BLOCKS |
|
if( fAnimPB->Count( (ParamID)kPBAnimStealthNodes ) == 0 ) |
|
{ |
|
// Yup, old style. So our update process looks like this: |
|
// 1) Create stealths for all our segments as we are now |
|
// 2) Set the parameters on all of them to our old defaults (no autostart, |
|
// loop on entire, no ease). |
|
// 3) Copy the old paramblock values to the single stealth indicated by |
|
// the old PB |
|
|
|
// Step 1... |
|
IUpdateAnimNodes(); |
|
|
|
// Step 2... |
|
for( int i = 0; i < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); i++ ) |
|
{ |
|
plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, i ); |
|
plString name = node->GetSegmentName(); |
|
node->SetAutoStart( false ); |
|
node->SetLoop( true, ENTIRE_ANIMATION_NAME ); |
|
node->SetEaseIn( plAnimEaseTypes::kNoEase, 1.f, 1.f, 1.f ); |
|
node->SetEaseOut( plAnimEaseTypes::kNoEase, 1.f, 1.f, 1.f ); |
|
} |
|
|
|
// Step 3... |
|
const char *oldSel = (const char *)fAnimPB->GetStr( (ParamID)kPBAnimName ); |
|
if( oldSel == nil ) |
|
oldSel = ENTIRE_ANIMATION_NAME; |
|
plAnimStealthNode *myNew = IFindStealth( plString::FromUtf8( oldSel ) ); |
|
if( myNew != nil ) |
|
{ |
|
#pragma warning( push ) // Forcing value to bool true or false (go figure, i'm even explicitly casting) |
|
#pragma warning( disable:4800 ) // Forcing value to bool true or false (go figure, i'm even explicitly casting) |
|
myNew->SetAutoStart( (bool)fAnimPB->GetInt( (ParamID)kPBAnimAutoStart ) ); |
|
myNew->SetLoop( (bool)fAnimPB->GetInt( (ParamID)kPBAnimLoop ), |
|
plString::FromUtf8( (char *)fAnimPB->GetStr( (ParamID)kPBAnimLoopName ) ) ); |
|
myNew->SetEaseIn( (uint8_t)fAnimPB->GetInt( (ParamID)kPBAnimEaseInType ), |
|
(float)fAnimPB->GetFloat( (ParamID)kPBAnimEaseInLength ), |
|
(float)fAnimPB->GetFloat( (ParamID)kPBAnimEaseInMin ), |
|
(float)fAnimPB->GetFloat( (ParamID)kPBAnimEaseInMax ) ); |
|
myNew->SetEaseOut( (uint8_t)fAnimPB->GetInt( (ParamID)kPBAnimEaseOutType ), |
|
(float)fAnimPB->GetFloat( (ParamID)kPBAnimEaseOutLength ), |
|
(float)fAnimPB->GetFloat( (ParamID)kPBAnimEaseOutMin ), |
|
(float)fAnimPB->GetFloat( (ParamID)kPBAnimEaseOutMax ) ); |
|
#pragma warning( pop ) |
|
} |
|
} |
|
#endif // MCN_UPGRADE_OLD_ANIM_BLOCKS |
|
|
|
|
|
// Make sure the parent is set tho. Note: we have to do this because, for some *(#$&(* reason, |
|
// when we're loading a file, MAX can somehow add the stealths to our tab list WITHOUT calling |
|
// the accessor for it (and the tab is empty on the CallSetsOnLoad() pass, for some reason). |
|
for( int i = 0; i < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); i++ ) |
|
{ |
|
plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, i ); |
|
node->SetParentMtl( this ); |
|
} |
|
} |
|
|
|
|
|
#define MTL_HDR_CHUNK 0x4000 |
|
|
|
//// Load //////////////////////////////////////////////////////////////////// |
|
// Our actual MAX load function |
|
|
|
IOResult plPassMtlBase::Load(ILoad *iload) |
|
{ |
|
plPostLoadHandler::AddPostLoad( this ); |
|
|
|
IOResult res; |
|
int id; |
|
while (IO_OK==(res=iload->OpenChunk())) |
|
{ |
|
switch(id = iload->CurChunkID()) |
|
{ |
|
case MTL_HDR_CHUNK: |
|
res = MtlBase::Load(iload); |
|
break; |
|
} |
|
iload->CloseChunk(); |
|
if (res!=IO_OK) |
|
return res; |
|
} |
|
|
|
return IO_OK; |
|
} |
|
|
|
//// Save //////////////////////////////////////////////////////////////////// |
|
// The MAX flip-side |
|
|
|
IOResult plPassMtlBase::Save(ISave *isave) |
|
{ |
|
IOResult res; |
|
isave->BeginChunk(MTL_HDR_CHUNK); |
|
res = MtlBase::Save(isave); |
|
if (res!=IO_OK) return res; |
|
isave->EndChunk(); |
|
|
|
return IO_OK; |
|
} |
|
|
|
//// ICloneBase ////////////////////////////////////////////////////////////// |
|
|
|
void plPassMtlBase::ICloneBase( plPassMtlBase *target, RemapDir &remap ) |
|
{ |
|
*((MtlBase*)target) = *((MtlBase*)this); |
|
ICloneRefs( target, remap ); |
|
|
|
for( int idx = 0; idx < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); idx++ ) |
|
{ |
|
IParamBlock2 *pb = target->fAnimPB; |
|
plAnimStealthNode *stealth = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, idx ); |
|
pb->SetValue( (ParamID)kPBAnimStealthNodes, 0, remap.CloneRef( stealth ), idx ); |
|
|
|
stealth = (plAnimStealthNode *)pb->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, idx ); |
|
INode *node = GetCOREInterface()->CreateObjectNode( stealth ); |
|
stealth->SetNodeName( GetName() ); |
|
node->Freeze( true ); |
|
} |
|
|
|
BaseClone(this, target, remap); |
|
target->fIValid.SetEmpty(); |
|
} |
|
|
|
//// ConvertToPassMtl //////////////////////////////////////////////////////// |
|
// Static convert to our plPassMtlBase type, if possible |
|
|
|
plPassMtlBase *plPassMtlBase::ConvertToPassMtl( Mtl *mtl ) |
|
{ |
|
if( mtl == nil ) |
|
return nil; |
|
|
|
if( mtl->ClassID() == PASS_MTL_CLASS_ID |
|
|| mtl->ClassID() == BUMP_MTL_CLASS_ID |
|
|| mtl->ClassID() == DECAL_MTL_CLASS_ID ) |
|
{ |
|
return (plPassMtlBase *)mtl; |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
//// SetupProperties ///////////////////////////////////////////////////////// |
|
|
|
bool plPassMtlBase::SetupProperties( plMaxNode *node, plErrorMsg *pErrMsg ) |
|
{ |
|
bool ret = true; |
|
|
|
// Call SetupProperties on all our animStealths if we have any |
|
int i, count = IGetNumStealths(); |
|
for( i = 0; i < count; i++ ) |
|
{ |
|
if( !IGetStealth( i, false )->SetupProperties( node, pErrMsg ) ) |
|
ret = false; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
//// ConvertDeInit /////////////////////////////////////////////////////////// |
|
|
|
bool plPassMtlBase::ConvertDeInit( plMaxNode *node, plErrorMsg *pErrMsg ) |
|
{ |
|
bool ret = true; |
|
|
|
// Call ConvertDeInit on all our animStealths if we have any |
|
int i, count = IGetNumStealths(); |
|
for( i = 0; i < count; i++ ) |
|
{ |
|
if( !IGetStealth( i, false )->ConvertDeInit( node, pErrMsg ) ) |
|
ret = false; |
|
} |
|
|
|
return ret; |
|
}
|
|
|