/*==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==*/ #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(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(); }