|
|
|
/*==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 "hsTypes.h"
|
|
|
|
#include "hsBitVector.h"
|
|
|
|
|
|
|
|
#include "Max.h"
|
|
|
|
#include "iparamb2.h"
|
|
|
|
#include "notify.h"
|
|
|
|
#include "notetrck.h"
|
|
|
|
|
|
|
|
#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 = TRACKED_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( hsBool update )
|
|
|
|
{
|
|
|
|
if( update )
|
|
|
|
IUpdateAnimNodes();
|
|
|
|
|
|
|
|
return fAnimPB->Count( (ParamID)kPBAnimStealthNodes );
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimStealthNode *plPassMtlBase::IGetStealth( int index, hsBool update )
|
|
|
|
{
|
|
|
|
if( update )
|
|
|
|
IUpdateAnimNodes();
|
|
|
|
|
|
|
|
return (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, index );
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimStealthNode *plPassMtlBase::IFindStealth( const char *segmentName )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < fAnimPB->Count( (ParamID)kPBAnimStealthNodes ); i++ )
|
|
|
|
{
|
|
|
|
plAnimStealthNode *node = (plAnimStealthNode *)fAnimPB->GetReferenceTarget( (ParamID)kPBAnimStealthNodes, 0, i );
|
|
|
|
const char *name = node->GetSegmentName();
|
|
|
|
|
|
|
|
if( node != nil && strcmp( name, segmentName ) == 0 )
|
|
|
|
{
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IVerifyStealthPresent ///////////////////////////////////////////////////
|
|
|
|
// Ensures that we have a stealth for the given segment.
|
|
|
|
|
|
|
|
plAnimStealthNode *plPassMtlBase::IVerifyStealthPresent( const char *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( ( strcmp(animName, ENTIRE_ANIMATION_NAME) != 0 ) ? animName : 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 );
|
|
|
|
|
|
|
|
const char *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 ];
|
|
|
|
else
|
|
|
|
hsAssert(false, "shit");
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// 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 );
|
|
|
|
const char *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( 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 ),
|
|
|
|
(char *)fAnimPB->GetStr( (ParamID)kPBAnimLoopName ) );
|
|
|
|
myNew->SetEaseIn( (UInt8)fAnimPB->GetInt( (ParamID)kPBAnimEaseInType ),
|
|
|
|
(hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseInLength ),
|
|
|
|
(hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseInMin ),
|
|
|
|
(hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseInMax ) );
|
|
|
|
myNew->SetEaseOut( (UInt8)fAnimPB->GetInt( (ParamID)kPBAnimEaseOutType ),
|
|
|
|
(hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseOutLength ),
|
|
|
|
(hsScalar)fAnimPB->GetFloat( (ParamID)kPBAnimEaseOutMin ),
|
|
|
|
(hsScalar)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 /////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
hsBool plPassMtlBase::SetupProperties( plMaxNode *node, plErrorMsg *pErrMsg )
|
|
|
|
{
|
|
|
|
hsBool 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 ///////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
hsBool plPassMtlBase::ConvertDeInit( plMaxNode *node, plErrorMsg *pErrMsg )
|
|
|
|
{
|
|
|
|
hsBool 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;
|
|
|
|
}
|