// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #pragma once #pragma warning( push, 3 ) #include #include #pragma warning( pop ) namespace frantic { namespace max3d { /** * This template class provides the necessary bootstrapping for a new object to be inserted in the 3ds Max reference * target heirarchy at any point. Its first template parameter defines *where* in the heirarchy to insert the new * object, and its second template parameter is used in the "Curiously Recursive Template Pattern" in order to implement * functions (like Clone) that need to know the concrete type of the object. * * This class defines a single IParamBlock2* that should be defined and created in the implementing class. All of the * Animatable, ReferenceMaker, and ReferenceTarget class member functions are defined assuming this single parameter * block. * * The implementing class is responsible for implementing any methods from classes in the heirarchy below * ReferenceTarget (ex. If you use GenericReferenceTarget you need to implement methods from BaseObject & * Object), as well as ReferenceMaker::NotifyRefChanged(). * * @tparam BaseClass The 3ds Max class you are inheriting from. Must be ReferenceTarget, or a subclass of * ReferenceTarget. * @tparam ChildClass The concrete type you are creating by inheriting from GenericReferenceTarget, for use in the * Curiously Recursive Template Pattern. This is class T such that you are doing: class T : public * GenericReferenceTarget{}; */ template class GenericReferenceTarget : public BaseClass { protected: IParamBlock2* m_pblock; protected: virtual ClassDesc2* GetClassDesc() = 0; public: GenericReferenceTarget(); virtual ~GenericReferenceTarget(); // From Animatable /** * @return the Class_ID as defined in GetClassDesc()->ClassID() */ virtual Class_ID ClassID(); /** * @param s A string that will be assigned GetClassDesc()->ClassName() */ #if MAX_VERSION_MAJOR < 24 virtual void GetClassName( MSTR& s ); #else virtual void GetClassName( MSTR& s, bool localized ); #endif /** * @return 1 Since the single reference held is the parameter block. */ virtual int NumRefs(); virtual ReferenceTarget* GetReference( int i ); virtual void SetReference( int i, ReferenceTarget* r ); /** * @return 1 Since the single subanim is also the parameter block. */ virtual int NumSubs(); virtual Animatable* SubAnim( int i ); #if MAX_VERSION_MAJOR >= 24 virtual TSTR SubAnimName( int i, bool localized ); #else virtual TSTR SubAnimName( int i ); #endif /** * @return 1 Since we hold a single parameter block */ virtual int NumParamBlocks(); virtual IParamBlock2* GetParamBlock( int i ); virtual IParamBlock2* GetParamBlockByID( BlockID i ); /** * Directly forwards to GetClassDesc()->BeginEditParams() */ virtual void BeginEditParams( IObjParam* ip, ULONG flags, Animatable* prev = NULL ); /** * Directly forwards to GetClassDesc()->EndEditParams() */ virtual void EndEditParams( IObjParam* ip, ULONG flags, Animatable* next = NULL ); /** * Calls 'operator delete' on 'this' */ virtual void DeleteThis(); // From ReferenceMaker // NOTE: This is the Max 2015 signature for NotifyRefChanged. All older versions should just forward to this // signature so we keep // the implementations consistent. virtual RefResult NotifyRefChanged( const Interval& changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message, BOOL propagate ) = 0; #if MAX_VERSION_MAJOR < 17 virtual RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message ); #endif // From ReferenceTarget /** * Copies the held parameter block from 'from' to 'to' if both objects are valid. This is generally a deep copy of * the parameter block, except for any INode objects referenced (I think but you should test this if it matters to * you). */ virtual void BaseClone( ReferenceTarget* from, ReferenceTarget* to, RemapDir& remap ); /** * Creates a new ChildClass instance and uses ChildClass::BaseClone() to copy 'this' into the new object. */ virtual ReferenceTarget* Clone( RemapDir& remap ); }; #pragma region Implementation template GenericReferenceTarget::GenericReferenceTarget() { m_pblock = NULL; } template GenericReferenceTarget::~GenericReferenceTarget() {} template Class_ID GenericReferenceTarget::ClassID() { return GetClassDesc()->ClassID(); } template #if MAX_VERSION_MAJOR >= 24 void GenericReferenceTarget::GetClassName( MSTR& s, bool localized ) { #else void GenericReferenceTarget::GetClassName( MSTR& s ) { #endif s = GetClassDesc()->ClassName(); } template int GenericReferenceTarget::NumRefs() { return 1; } template ReferenceTarget* GenericReferenceTarget::GetReference( int i ) { return ( i == 0 ) ? m_pblock : NULL; } template void GenericReferenceTarget::SetReference( int i, ReferenceTarget* r ) { if( i == 0 ) m_pblock = static_cast( r ); } template int GenericReferenceTarget::NumSubs() { return 1; } template Animatable* GenericReferenceTarget::SubAnim( int i ) { return ( i == 0 ) ? m_pblock : NULL; } template #if MAX_VERSION_MAJOR < 24 TSTR GenericReferenceTarget::SubAnimName( int i ) { #else TSTR GenericReferenceTarget::SubAnimName( int i, bool localized ) { #endif return ( i == 0 && m_pblock ) ? m_pblock->GetLocalName() : _T(""); } template int GenericReferenceTarget::NumParamBlocks() { return 1; } template IParamBlock2* GenericReferenceTarget::GetParamBlock( int i ) { return ( i == 0 && m_pblock ) ? m_pblock : NULL; } template IParamBlock2* GenericReferenceTarget::GetParamBlockByID( BlockID i ) { return ( m_pblock && i == m_pblock->ID() ) ? m_pblock : NULL; } template void GenericReferenceTarget::BeginEditParams( IObjParam* ip, ULONG flags, Animatable* prev ) { GetClassDesc()->BeginEditParams( ip, this, flags, prev ); } template void GenericReferenceTarget::EndEditParams( IObjParam* ip, ULONG flags, Animatable* next ) { GetClassDesc()->EndEditParams( ip, this, flags, next ); } template void GenericReferenceTarget::DeleteThis() { delete this; } #if MAX_VERSION_MAJOR < 17 template RefResult GenericReferenceTarget::NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message ) { return this->NotifyRefChanged( changeInt, hTarget, partID, message, TRUE ); } #endif template void GenericReferenceTarget::BaseClone( ReferenceTarget* from, ReferenceTarget* to, RemapDir& remap ) { if( !to || !from || to == from ) return; BaseClass::BaseClone( from, to, remap ); for( int i = 0, iEnd = from->NumRefs(); i < iEnd; ++i ) to->ReplaceReference( i, remap.CloneRef( from->GetReference( i ) ) ); } template ReferenceTarget* GenericReferenceTarget::Clone( RemapDir& remap ) { ReferenceTarget* result = (ReferenceTarget*)GetClassDesc()->Create( FALSE ); this->BaseClone( this, result, remap ); return result; } #pragma endregion } // namespace max3d } // namespace frantic // Add it to the global namespace for backwards compatibility. TODO: Update GenomeProject to make this irrelevent. using frantic::max3d::GenericReferenceTarget;