|
|
|
/*==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==*/
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
|
|
|
|
#include "max.h"
|
|
|
|
#include "resource.h"
|
|
|
|
#include "plComponent.h"
|
|
|
|
#include "plComponentReg.h"
|
|
|
|
#include "MaxMain/plPlasmaRefMsgs.h"
|
|
|
|
|
|
|
|
#include "MaxMain/plMaxNode.h"
|
|
|
|
#include "MaxExport/plExportProgressBar.h"
|
|
|
|
|
|
|
|
#include "plXImposter.h"
|
|
|
|
|
|
|
|
#include "pfAnimation/plFilterCoordInterface.h"
|
|
|
|
|
|
|
|
#include "pnSceneObject/plSimulationInterface.h"
|
|
|
|
#include "plPhysical.h"
|
|
|
|
|
|
|
|
const Class_ID FILTERINHERIT_COMP_CID(0x263928d8, 0x548456da);
|
|
|
|
|
|
|
|
void DummyCodeIncludeFuncXImposter()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//Class that accesses the paramblock below.
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//Max desc stuff necessary below.
|
|
|
|
CLASS_DESC(plXImposterComp, gXImposterDesc, "X-Form", "X-Form", COMP_TYPE_DISTRIBUTOR, XIMPOSTER_COMP_CID)
|
|
|
|
|
|
|
|
ParamBlockDesc2 gXImposterBk
|
|
|
|
(
|
|
|
|
plComponent::kBlkComp, _T("X-Form"), 0, &gXImposterDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_XFORM, IDS_COMP_XFORMS, 0, 0, NULL,
|
|
|
|
|
|
|
|
end
|
|
|
|
);
|
|
|
|
|
|
|
|
plXImposterComp::plXImposterComp()
|
|
|
|
{
|
|
|
|
fClassDesc = &gXImposterDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plXImposterComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
|
|
|
|
{
|
|
|
|
node->SetRadiateNorms(true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const Class_ID FORCE_CTT_COMP_CID(0x30ee73b7, 0x4cdd551b);
|
|
|
|
|
|
|
|
class plForceCTTComp : public plComponent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
plForceCTTComp();
|
|
|
|
void DeleteThis() { delete this; }
|
|
|
|
|
|
|
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
|
|
|
|
// of properties on the MaxNode, as it's still indeterminant.
|
|
|
|
virtual hsBool SetupProperties(plMaxNode *node, plErrorMsg *pErrMsg);
|
|
|
|
virtual hsBool PreConvert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; }
|
|
|
|
virtual hsBool Convert(plMaxNode *node, plErrorMsg *pErrMsg) { return true; }
|
|
|
|
};
|
|
|
|
|
|
|
|
//Max desc stuff necessary below.
|
|
|
|
CLASS_DESC(plForceCTTComp, gForceCTTDesc, "ForceClick2Turn", "ForceCTT", COMP_TYPE_MISC, FORCE_CTT_COMP_CID)
|
|
|
|
|
|
|
|
ParamBlockDesc2 gForceCTTBk
|
|
|
|
(
|
|
|
|
plComponent::kBlkComp, _T("ForceCTT"), 0, &gForceCTTDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_FORCE_CTT, IDS_COMP_FORCE_CTT, 0, 0, NULL,
|
|
|
|
|
|
|
|
end
|
|
|
|
);
|
|
|
|
|
|
|
|
plForceCTTComp::plForceCTTComp()
|
|
|
|
{
|
|
|
|
fClassDesc = &gForceCTTDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plForceCTTComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
|
|
|
|
{
|
|
|
|
node->SetForceVisLOS(true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// The filter inheritance component doesn't have anything to do with the XImposter, but there's
|
|
|
|
// plenty of space here, so blow me.
|
|
|
|
|
|
|
|
class plFilterInheritComp : public plComponent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
kActive,
|
|
|
|
kNoX,
|
|
|
|
kNoY,
|
|
|
|
kNoZ
|
|
|
|
};
|
|
|
|
public:
|
|
|
|
plFilterInheritComp();
|
|
|
|
void DeleteThis() { delete this; }
|
|
|
|
|
|
|
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
|
|
|
|
// of properties on the MaxNode, as it's still indeterminant.
|
|
|
|
virtual hsBool SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg);
|
|
|
|
virtual hsBool PreConvert(plMaxNode* node, plErrorMsg* pErrMsg);
|
|
|
|
virtual hsBool Convert(plMaxNode* node, plErrorMsg* pErrMsg);
|
|
|
|
|
|
|
|
hsBool SetMaxInherit();
|
|
|
|
hsBool SetMaxInherit(plMaxNodeBase* targ);
|
|
|
|
hsBool KillMaxInherit();
|
|
|
|
hsBool KillMaxInherit(plMaxNodeBase* targ);
|
|
|
|
|
|
|
|
hsBool Bail(plMaxNode* node, const char* msg, plErrorMsg* pErrMsg);
|
|
|
|
|
|
|
|
virtual void AddTarget(plMaxNodeBase *target);
|
|
|
|
virtual void DeleteTarget(plMaxNodeBase *target);
|
|
|
|
virtual void DeleteAllTargets();
|
|
|
|
};
|
|
|
|
|
|
|
|
class FilterInheritCompDlgProc : public ParamMap2UserDlgProc
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
void ISetTransEnable(IParamMap2* map)
|
|
|
|
{
|
|
|
|
IParamBlock2* pb = map->GetParamBlock();
|
|
|
|
if( !pb->GetInt(plFilterInheritComp::kActive) )
|
|
|
|
{
|
|
|
|
map->Enable(plFilterInheritComp::kNoX, FALSE);
|
|
|
|
map->Enable(plFilterInheritComp::kNoY, FALSE);
|
|
|
|
map->Enable(plFilterInheritComp::kNoZ, FALSE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
map->Enable(plFilterInheritComp::kNoX, TRUE);
|
|
|
|
map->Enable(plFilterInheritComp::kNoY, TRUE);
|
|
|
|
map->Enable(plFilterInheritComp::kNoZ, TRUE);
|
|
|
|
}
|
|
|
|
plFilterInheritComp* comp = (plFilterInheritComp*)map->GetParamBlock()->GetOwner();
|
|
|
|
comp->SetMaxInherit();
|
|
|
|
}
|
|
|
|
public:
|
|
|
|
FilterInheritCompDlgProc() {}
|
|
|
|
~FilterInheritCompDlgProc() {}
|
|
|
|
|
|
|
|
BOOL DlgProc(TimeValue t, IParamMap2* map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
switch (msg)
|
|
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
ISetTransEnable(map);
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
ISetTransEnable(map);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
void DeleteThis() {}
|
|
|
|
};
|
|
|
|
static FilterInheritCompDlgProc gFilterInheritCompDlgProc;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CLASS_DESC(plFilterInheritComp, gFilterInheritDesc, "Filter Inherit", "FiltInherit", COMP_TYPE_MISC, FILTERINHERIT_COMP_CID)
|
|
|
|
|
|
|
|
ParamBlockDesc2 gFilterInheritBk
|
|
|
|
(
|
|
|
|
plComponent::kBlkComp, _T("FilterInherit"), 0, &gFilterInheritDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_FILTERINHERIT, IDS_COMP_FILTER, 0, 0, &gFilterInheritCompDlgProc,
|
|
|
|
|
|
|
|
plFilterInheritComp::kActive, _T("Active"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, TRUE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FILTER_ACTIVE,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plFilterInheritComp::kNoX, _T("NoX"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, TRUE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FILTER_NOX,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plFilterInheritComp::kNoY, _T("NoY"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, TRUE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FILTER_NOY,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plFilterInheritComp::kNoZ, _T("NoZ"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, TRUE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_FILTER_NOZ,
|
|
|
|
end,
|
|
|
|
|
|
|
|
end
|
|
|
|
);
|
|
|
|
|
|
|
|
plFilterInheritComp::plFilterInheritComp()
|
|
|
|
{
|
|
|
|
fClassDesc = &gFilterInheritDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plFilterInheritComp::SetupProperties(plMaxNode* node, plErrorMsg* pErrMsg)
|
|
|
|
{
|
|
|
|
if( !fCompPB->GetInt(kActive) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if( node->GetParentNode()->IsRootNode() )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
node->SetFilterInherit(true);
|
|
|
|
node->SetForceLocal(true);
|
|
|
|
plMaxNode* parent = (plMaxNode*)node->GetParentNode();
|
|
|
|
parent->SetForceLocal(true);
|
|
|
|
|
|
|
|
// Okay, everything works fine as long as you set up your heirarchy, and THEN
|
|
|
|
// add this component. But if you put this component on the as yet unlinked child,
|
|
|
|
// and THEN link the child to the parent, Max starts reporting bogus local TM info.
|
|
|
|
// If you turn off the component (uncheck the disable rotation checkbox), and turn
|
|
|
|
// it back on, Max is happy again.
|
|
|
|
// However, if I just turn off the component and turn it right back on again here,
|
|
|
|
// (a KillMaxInherit() followed by a SetMaxInherit()), apparently Max hasn't had
|
|
|
|
// enough time to think in between, and so stays confused.
|
|
|
|
// Soooo, here at the last minute before conversion, we kill all those inherit
|
|
|
|
// checkboxes for this node, then after we've finished converting, we turn them
|
|
|
|
// back on. Note that we don't currently give a rat's patooties whether the check
|
|
|
|
// boxes are set or not, we just want to turn them off and turn them back on
|
|
|
|
// sometime before export, with enough time in between for Max to get on to the fact.
|
|
|
|
// See the matching SetMaxInherit() at the end of Convert().
|
|
|
|
KillMaxInherit(node);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plFilterInheritComp::PreConvert(plMaxNode* node, plErrorMsg* pErrMsg)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plFilterInheritComp::Bail(plMaxNode* node, const char* msg, plErrorMsg* pErrMsg)
|
|
|
|
{
|
|
|
|
pErrMsg->Set(true, node->GetName(), msg).CheckAndAsk();
|
|
|
|
pErrMsg->Set(false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plFilterInheritComp::Convert(plMaxNode* node, plErrorMsg* pErrMsg)
|
|
|
|
{
|
|
|
|
if( !fCompPB->GetInt(kActive) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if( node->GetParentNode()->IsRootNode() )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
plSceneObject* so = node->GetSceneObject();
|
|
|
|
if( !so )
|
|
|
|
{
|
|
|
|
return Bail(node, "Error finding scene object for filtered inheritance", pErrMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
const plCoordinateInterface* co = so->GetCoordinateInterface();
|
|
|
|
if( !co )
|
|
|
|
{
|
|
|
|
return Bail(node, "Error setting filtered inheritance - no coordinate interface", pErrMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
plFilterCoordInterface* filt = plFilterCoordInterface::ConvertNoRef(const_cast<plCoordinateInterface*>(co));
|
|
|
|
if( !filt )
|
|
|
|
{
|
|
|
|
return Bail(node, "Error setting filtered inheritance - wrong coordinate interface", pErrMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
const plSimulationInterface* si = so->GetSimulationInterface();
|
|
|
|
if (si)
|
|
|
|
{
|
|
|
|
plPhysical* phys = si->GetPhysical();
|
|
|
|
// tell the physical not to send transforms back -- they'll be wrong if it tries to compose w/a subworld
|
|
|
|
// this rules out using transform filters on dynamically simulated objects....
|
|
|
|
phys->SetProperty(plSimulationInterface::kPassive, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
UInt32 mask = plFilterCoordInterface::kNoRotation;
|
|
|
|
if( fCompPB->GetInt(kNoX) )
|
|
|
|
mask |= plFilterCoordInterface::kNoTransX;
|
|
|
|
if( fCompPB->GetInt(kNoY) )
|
|
|
|
mask |= plFilterCoordInterface::kNoTransY;
|
|
|
|
if( fCompPB->GetInt(kNoZ) )
|
|
|
|
mask |= plFilterCoordInterface::kNoTransZ;
|
|
|
|
|
|
|
|
plMaxNode* parent = (plMaxNode*)node->GetParentNode();
|
|
|
|
hsMatrix44 parL2W = parent->GetLocalToWorld44(TimeValue(0));
|
|
|
|
|
|
|
|
filt->SetFilterMask(mask);
|
|
|
|
filt->SetRefLocalToWorld(parL2W);
|
|
|
|
|
|
|
|
// See the matching KillMaxInherit() in SetupProperties().
|
|
|
|
SetMaxInherit(node);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plFilterInheritComp::SetMaxInherit(plMaxNodeBase* targ)
|
|
|
|
{
|
|
|
|
if( !fCompPB->GetInt(kActive) )
|
|
|
|
return KillMaxInherit(targ);
|
|
|
|
|
|
|
|
DWORD mask = INHERIT_ROT_X
|
|
|
|
| INHERIT_ROT_Y
|
|
|
|
| INHERIT_ROT_Z
|
|
|
|
| INHERIT_SCL_X
|
|
|
|
| INHERIT_SCL_Y
|
|
|
|
| INHERIT_SCL_Z;
|
|
|
|
|
|
|
|
if( fCompPB->GetInt(kNoX) )
|
|
|
|
mask |= INHERIT_POS_X;
|
|
|
|
if( fCompPB->GetInt(kNoY) )
|
|
|
|
mask |= INHERIT_POS_Y;
|
|
|
|
if( fCompPB->GetInt(kNoZ) )
|
|
|
|
mask |= INHERIT_POS_Z;
|
|
|
|
|
|
|
|
// Max documentation is a big fat liar
|
|
|
|
mask = ~mask;
|
|
|
|
|
|
|
|
if( targ )
|
|
|
|
{
|
|
|
|
targ->GetTMController()->SetInheritanceFlags(mask, true);
|
|
|
|
targ->GetTMController()->NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plFilterInheritComp::SetMaxInherit()
|
|
|
|
{
|
|
|
|
if( !fCompPB->GetInt(kActive) )
|
|
|
|
return KillMaxInherit();
|
|
|
|
|
|
|
|
DWORD mask = INHERIT_ROT_X
|
|
|
|
| INHERIT_ROT_Y
|
|
|
|
| INHERIT_ROT_Z
|
|
|
|
| INHERIT_SCL_X
|
|
|
|
| INHERIT_SCL_Y
|
|
|
|
| INHERIT_SCL_Z;
|
|
|
|
|
|
|
|
if( fCompPB->GetInt(kNoX) )
|
|
|
|
mask |= INHERIT_POS_X;
|
|
|
|
if( fCompPB->GetInt(kNoY) )
|
|
|
|
mask |= INHERIT_POS_Y;
|
|
|
|
if( fCompPB->GetInt(kNoZ) )
|
|
|
|
mask |= INHERIT_POS_Z;
|
|
|
|
|
|
|
|
// Max documentation is a big fat liar
|
|
|
|
mask = ~mask;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < NumTargets(); i++ )
|
|
|
|
{
|
|
|
|
plMaxNodeBase* targ = GetTarget(i);
|
|
|
|
if( targ )
|
|
|
|
{
|
|
|
|
targ->GetTMController()->SetInheritanceFlags(mask, true);
|
|
|
|
targ->GetTMController()->NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plFilterInheritComp::KillMaxInherit(plMaxNodeBase* targ)
|
|
|
|
{
|
|
|
|
// Max documentation is a big fat liar
|
|
|
|
DWORD mask = ~0;
|
|
|
|
|
|
|
|
targ->GetTMController()->SetInheritanceFlags(mask, true);
|
|
|
|
targ->GetTMController()->NotifyDependents(FOREVER,0,REFMSG_CHANGE);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plFilterInheritComp::KillMaxInherit()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < NumTargets(); i++ )
|
|
|
|
{
|
|
|
|
plMaxNodeBase* targ = GetTarget(i);
|
|
|
|
if( targ )
|
|
|
|
{
|
|
|
|
KillMaxInherit(targ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plFilterInheritComp::AddTarget(plMaxNodeBase *target)
|
|
|
|
{
|
|
|
|
plComponentBase::AddTarget(target);
|
|
|
|
|
|
|
|
SetMaxInherit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plFilterInheritComp::DeleteTarget(plMaxNodeBase *target)
|
|
|
|
{
|
|
|
|
plComponentBase::DeleteTarget(target);
|
|
|
|
|
|
|
|
KillMaxInherit(target);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plFilterInheritComp::DeleteAllTargets()
|
|
|
|
{
|
|
|
|
KillMaxInherit();
|
|
|
|
|
|
|
|
plComponentBase::DeleteAllTargets();
|
|
|
|
}
|
|
|
|
|
|
|
|
|