|
|
|
/*==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 "dummy.h"
|
|
|
|
#include "resource.h"
|
|
|
|
#include "plComponent.h"
|
|
|
|
#include "plComponentReg.h"
|
|
|
|
#include "plMiscComponents.h"
|
|
|
|
#include "plSoftVolumeComponent.h"
|
|
|
|
|
|
|
|
#include "../MaxMain/plPlasmaRefMsgs.h"
|
|
|
|
#include "../MaxMain/plMaxNode.h"
|
|
|
|
|
|
|
|
#include "../plIntersect/plSoftVolumeTypes.h"
|
|
|
|
#include "../plIntersect/plVolumeIsect.h"
|
|
|
|
#include "../pnKeyedObject/plKey.h"
|
|
|
|
#include "../pnSceneObject/plSceneObject.h"
|
|
|
|
#include "../pnMessage/plObjRefMsg.h"
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
|
|
|
|
#include "../plGLight/plLightInfo.h"
|
|
|
|
#include "../plScene/plOccluder.h"
|
|
|
|
|
|
|
|
#include "../pnSceneObject/plDrawInterface.h"
|
|
|
|
#include "../plScene/plVisRegion.h"
|
|
|
|
#include "../plScene/plRelevanceRegion.h"
|
|
|
|
|
|
|
|
void DummyCodeIncludeFuncSoftVolume() {}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class plVolumeHitCallback : public HitByNameDlgCallback
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
INode* fOwner;
|
|
|
|
IParamBlock2* fPB;
|
|
|
|
ParamID fNodeListID;
|
|
|
|
BOOL fSingleSel;
|
|
|
|
TCHAR fTitle[ 128 ];
|
|
|
|
|
|
|
|
public:
|
|
|
|
plVolumeHitCallback(INode* owner, IParamBlock2 *pb, ParamID nodeListID, TCHAR *title = nil, BOOL singleSel=false );
|
|
|
|
|
|
|
|
virtual TCHAR *dialogTitle() { return fTitle; }
|
|
|
|
virtual TCHAR *buttonText() { return "OK"; }
|
|
|
|
virtual int filter(INode *node);
|
|
|
|
virtual void proc(INodeTab &nodeTab);
|
|
|
|
virtual BOOL showHiddenAndFrozen() { return TRUE; }
|
|
|
|
virtual BOOL singleSelect() { return fSingleSel; }
|
|
|
|
};
|
|
|
|
|
|
|
|
plVolumeHitCallback::plVolumeHitCallback(INode* owner, IParamBlock2 *pb, ParamID nodeListID, TCHAR *title, BOOL singleSel)
|
|
|
|
: fOwner(owner),
|
|
|
|
fPB(pb),
|
|
|
|
fNodeListID(nodeListID),
|
|
|
|
fSingleSel(singleSel)
|
|
|
|
{
|
|
|
|
strcpy( fTitle, title );
|
|
|
|
}
|
|
|
|
|
|
|
|
int plVolumeHitCallback::filter(INode *node)
|
|
|
|
{
|
|
|
|
if( node == fOwner )
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent();
|
|
|
|
|
|
|
|
// If this is an activator type component
|
|
|
|
if( comp )
|
|
|
|
{
|
|
|
|
if( (comp->ClassID() == SOFTVOLUME_CID)
|
|
|
|
|| (comp->ClassID() == SOFTVOLUME_UNION_CID)
|
|
|
|
|| (comp->ClassID() == SOFTVOLUME_ISECT_CID)
|
|
|
|
|| (comp->ClassID() == SOFTVOLUME_NEGATE_CID) )
|
|
|
|
{
|
|
|
|
|
|
|
|
if( !fSingleSel )
|
|
|
|
{
|
|
|
|
// And we don't already reference it
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fPB->Count(fNodeListID); i++ )
|
|
|
|
{
|
|
|
|
if( fPB->GetINode(fNodeListID, 0, i) == node )
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// And this wouldn't create a cyclical reference (Max doesn't like those)
|
|
|
|
if (comp->TestForLoop(FOREVER, fPB) == REF_FAIL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plVolumeHitCallback::proc(INodeTab &nodeTab)
|
|
|
|
{
|
|
|
|
if( fSingleSel )
|
|
|
|
{
|
|
|
|
if( nodeTab.Count() )
|
|
|
|
fPB->SetValue(fNodeListID, TimeValue(0), nodeTab[0]);
|
|
|
|
else
|
|
|
|
fPB->SetValue(fNodeListID, TimeValue(0), (INode*)nil);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fPB->Append(fNodeListID, nodeTab.Count(), &nodeTab[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//// plSingleCompSelProc Functions //////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
plSingleCompSelProc::plSingleCompSelProc(ParamID nodeID, int dlgItem, TCHAR *title) : fNodeID(nodeID), fDlgItem(dlgItem)
|
|
|
|
{
|
|
|
|
strcpy( fTitle, title );
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL plSingleCompSelProc::DlgProc(TimeValue t, IParamMap2 *paramMap, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
switch (msg)
|
|
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
{
|
|
|
|
IParamBlock2 *pb = paramMap->GetParamBlock();
|
|
|
|
INode* node = pb->GetINode(fNodeID);
|
|
|
|
TSTR newName(node ? node->GetName() : "Pick");
|
|
|
|
::SetWindowText(::GetDlgItem(hWnd, fDlgItem), newName);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
if( (HIWORD(wParam) == BN_CLICKED) && (LOWORD(wParam) == fDlgItem) )
|
|
|
|
{
|
|
|
|
// Adding a volume. Set it and refresh the UI to show it in our list.
|
|
|
|
IParamBlock2 *pb = paramMap->GetParamBlock();
|
|
|
|
plVolumeHitCallback hitCB((INode*)pb->GetOwner(), pb, fNodeID, fTitle, true );
|
|
|
|
GetCOREInterface()->DoHitByNameDialog(&hitCB);
|
|
|
|
INode* node = pb->GetINode(fNodeID);
|
|
|
|
TSTR newName(node ? node->GetName() : "Pick");
|
|
|
|
::SetWindowText(::GetDlgItem(hWnd, fDlgItem), newName);
|
|
|
|
paramMap->Invalidate(fNodeID);
|
|
|
|
ShowWindow(hWnd, SW_HIDE);
|
|
|
|
ShowWindow(hWnd, SW_SHOW);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Moved class declaration to .h -mcn
|
|
|
|
|
|
|
|
|
|
|
|
hsBool plSoftVolBaseComponent::SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg)
|
|
|
|
{
|
|
|
|
fSoftKey = nil;
|
|
|
|
fValid = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolBaseComponent::GetSoftVolume()
|
|
|
|
{
|
|
|
|
if( !fSoftKey )
|
|
|
|
ICreateSoftVolume();
|
|
|
|
return fSoftKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
plSoftVolBaseComponent* plSoftVolBaseComponent::GetSoftComponent(INode* node)
|
|
|
|
{
|
|
|
|
if( node == nil )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
plComponentBase *comp = ((plMaxNodeBase*)node)->ConvertToComponent();
|
|
|
|
if( comp == nil )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
return GetSoftComponent( comp );
|
|
|
|
}
|
|
|
|
|
|
|
|
plSoftVolBaseComponent* plSoftVolBaseComponent::GetSoftComponent(plComponentBase *comp)
|
|
|
|
{
|
|
|
|
if( comp != nil &&
|
|
|
|
(comp->ClassID() == SOFTVOLUME_CID)
|
|
|
|
|| (comp->ClassID() == SOFTVOLUME_UNION_CID)
|
|
|
|
|| (comp->ClassID() == SOFTVOLUME_ISECT_CID)
|
|
|
|
|| (comp->ClassID() == SOFTVOLUME_NEGATE_CID) )
|
|
|
|
{
|
|
|
|
return (plSoftVolBaseComponent*)comp;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSoftVolBaseComponent::IAddSubVolume(plKey masterKey, plKey subKey)
|
|
|
|
{
|
|
|
|
if( masterKey && subKey )
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(subKey, TRACKED_NEW plGenRefMsg(masterKey, plRefMsg::kOnCreate, 0, plSoftVolume::kSubVolume), plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolBaseComponent::ISetVolumeKey(plSoftVolume* vol)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < NumTargets(); i++ )
|
|
|
|
{
|
|
|
|
if( GetTarget(i) )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
hsAssert(i < NumTargets(), "We're not attached to anything?");
|
|
|
|
plKey key = hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), vol, GetTarget(i)->GetLocation());
|
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolBaseComponent::IInvertVolume(plKey subKey)
|
|
|
|
{
|
|
|
|
if( !subKey )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
plSoftVolumeInvert* invert = TRACKED_NEW plSoftVolumeInvert;
|
|
|
|
plKey invertKey = ISetVolumeKey(invert);
|
|
|
|
|
|
|
|
IAddSubVolume(invertKey, subKey);
|
|
|
|
|
|
|
|
return invertKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plSoftVolBaseComponent::DeInit(plMaxNode *node, plErrorMsg *pErrMsg)
|
|
|
|
{
|
|
|
|
fSoftKey = nil;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// SoftVolume Component
|
|
|
|
//
|
|
|
|
//Class that accesses the paramblock below.
|
|
|
|
class plSoftVolComponent : public plSoftVolBaseComponent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum {
|
|
|
|
kSoftDistance,
|
|
|
|
kPartialEnabled,
|
|
|
|
kInsidePower,
|
|
|
|
kOutsidePower
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
plKey ISetFromIsect(plMaxNodeBase* pNode, plVolumeIsect* isect);
|
|
|
|
plKey ICreateSoftVolume();
|
|
|
|
plKey ICreateFromNode(plMaxNodeBase* pNode);
|
|
|
|
plKey ICreateFromDummyObject(plMaxNodeBase* pNode, Object* obj);
|
|
|
|
plKey ICreateFromTriObject(plMaxNodeBase* pNode, Object* obj);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void ICreateVolume();
|
|
|
|
public:
|
|
|
|
plSoftVolComponent();
|
|
|
|
void DeleteThis() { delete this; }
|
|
|
|
|
|
|
|
hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg);
|
|
|
|
|
|
|
|
virtual void CollectNonDrawables(INodeTab& nonDrawables) { AddTargetsToList(nonDrawables); }
|
|
|
|
};
|
|
|
|
|
|
|
|
//
|
|
|
|
// When one of our parameters that is a ref changes, send out the component ref
|
|
|
|
// changed message. Normally, messages from component refs are ignored since
|
|
|
|
// they pass along all the messages of the ref, which generates a lot of false
|
|
|
|
// converts.
|
|
|
|
class plSoftVolObjAccessor : public PBAccessor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
plSoftVolObjAccessor gSoftVolObjAccessor;
|
|
|
|
|
|
|
|
|
|
|
|
class plSoftVolComponentProc : public ParamMap2UserDlgProc
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
switch (msg)
|
|
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
{
|
|
|
|
IParamBlock2 *pb = map->GetParamBlock();
|
|
|
|
map->SetTooltip(plSoftVolComponent::kSoftDistance, TRUE, "Distance effect fades in and out." );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void DeleteThis() {}
|
|
|
|
};
|
|
|
|
static plSoftVolComponentProc gSoftVolProc;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CLASS_DESC(plSoftVolComponent, gSoftVolDesc, "Soft Region", "SoftRegion", COMP_TYPE_VOLUME, SOFTVOLUME_CID)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ParamBlockDesc2 gSoftVolBk
|
|
|
|
(
|
|
|
|
plComponent::kBlkComp, _T("SoftRegion"), 0, &gSoftVolDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_SOFTVOLUME, IDS_COMP_SOFTVOLUMES, 0, 0, &gSoftVolProc,
|
|
|
|
|
|
|
|
plSoftVolComponent::kSoftDistance, _T("Soft Distance"), TYPE_FLOAT, 0, 0,
|
|
|
|
p_default, 0.0,
|
|
|
|
p_range, 0.0, 500.0,
|
|
|
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
|
|
|
|
IDC_COMP_SOFTVOL_SOFT, IDC_COMP_SOFTVOL_SOFT_SPIN, 1.0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolComponent::kPartialEnabled, _T("EnablePartial"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, FALSE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SOFTVOL_ENABLEPARTIAL,
|
|
|
|
p_enable_ctrls, 2, plSoftVolComponent::kInsidePower, plSoftVolComponent::kOutsidePower,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolComponent::kInsidePower, _T("PowerInside"), TYPE_FLOAT, 0, 0,
|
|
|
|
p_default, 100.0,
|
|
|
|
p_range, 0.0, 100.0,
|
|
|
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
|
|
|
|
IDC_COMP_SOFTVOL_INSIDE, IDC_COMP_SOFTVOL_INSIDE_SPIN, 1.0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolComponent::kOutsidePower, _T("PowerOutside"), TYPE_FLOAT, 0, 0,
|
|
|
|
p_default, 0.0,
|
|
|
|
p_range, 0.0, 100.0,
|
|
|
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
|
|
|
|
IDC_COMP_SOFTVOL_OUTSIDE, IDC_COMP_SOFTVOL_OUTSIDE_SPIN, 1.0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
plSoftVolComponent::plSoftVolComponent()
|
|
|
|
{
|
|
|
|
fClassDesc = &gSoftVolDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plSoftVolComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
if( !plSoftVolBaseComponent::SetupProperties(pNode, errMsg) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < pNode->NumAttachedComponents(); i++ )
|
|
|
|
{
|
|
|
|
plComponentBase* comp = pNode->GetAttachedComponent(i);
|
|
|
|
if( comp
|
|
|
|
&& (comp != this)
|
|
|
|
&& (comp->ClassID() == ClassID()) )
|
|
|
|
{
|
|
|
|
errMsg->Set(true, pNode->GetName(), "Multiple SoftRegion components attached, there can be only one").CheckAndAsk();
|
|
|
|
errMsg->Set(false);
|
|
|
|
|
|
|
|
fValid = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// The node must either point to a Mesh or a dummy box, otherwise skip it.
|
|
|
|
//
|
|
|
|
Object *obj = pNode->EvalWorldState(TimeValue(0)).obj;
|
|
|
|
if( !obj )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) )
|
|
|
|
{
|
|
|
|
fValid = true;
|
|
|
|
}
|
|
|
|
else if( obj->CanConvertToType(triObjectClassID) )
|
|
|
|
{
|
|
|
|
fValid = true;
|
|
|
|
}
|
|
|
|
pNode->SetDrawable(false);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolComponent::ICreateSoftVolume()
|
|
|
|
{
|
|
|
|
if( !fValid )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if( !NumTargets() )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if( NumTargets() < 2 )
|
|
|
|
{
|
|
|
|
return fSoftKey = ICreateFromNode(GetTarget(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
plSoftVolumeUnion* compound = TRACKED_NEW plSoftVolumeUnion;
|
|
|
|
fSoftKey = ISetVolumeKey(compound);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < NumTargets(); i++ )
|
|
|
|
{
|
|
|
|
if( GetTarget(i) )
|
|
|
|
{
|
|
|
|
plKey subKey = ICreateFromNode(GetTarget(i));
|
|
|
|
IAddSubVolume(fSoftKey, subKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( !compound->GetNumSubs() )
|
|
|
|
{
|
|
|
|
delete compound;
|
|
|
|
compound = nil;
|
|
|
|
fSoftKey = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fSoftKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolComponent::ICreateFromNode(plMaxNodeBase* pNode)
|
|
|
|
{
|
|
|
|
if( !pNode )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if( !fValid )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if( !pNode->GetSceneObject() )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
// Go ahead and make it here, so it'll be available for aggregaters in the Convert pass
|
|
|
|
Object *obj = pNode->EvalWorldState(TimeValue(0)).obj;
|
|
|
|
|
|
|
|
if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) )
|
|
|
|
{
|
|
|
|
return ICreateFromDummyObject(pNode, obj);
|
|
|
|
}
|
|
|
|
else if( obj->CanConvertToType(triObjectClassID) )
|
|
|
|
{
|
|
|
|
return ICreateFromTriObject(pNode, obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolComponent::ICreateFromDummyObject(plMaxNodeBase* pNode, Object* obj)
|
|
|
|
{
|
|
|
|
DummyObject* dummy = (DummyObject*)obj;
|
|
|
|
Box3 bnd = dummy->GetBox();
|
|
|
|
|
|
|
|
plParallelIsect* isect = TRACKED_NEW plParallelIsect;
|
|
|
|
isect->SetNumPlanes(3);
|
|
|
|
|
|
|
|
hsMatrix44 v2l = pNode->GetVertToLocal44();
|
|
|
|
hsMatrix44 l2v = pNode->GetLocalToVert44();
|
|
|
|
|
|
|
|
hsPoint3 corner(bnd.pmin.x, bnd.pmin.y, bnd.pmin.z);
|
|
|
|
hsVector3 axis(bnd.pmax.x - bnd.pmin.x, bnd.pmax.y - bnd.pmin.y, bnd.pmax.z - bnd.pmin.z);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
|
|
{
|
|
|
|
hsPoint3 bot = v2l * corner;
|
|
|
|
hsPoint3 top = corner;
|
|
|
|
top[i] += axis[i];
|
|
|
|
top = v2l * top;
|
|
|
|
|
|
|
|
isect->SetPlane(i, bot, top);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ISetFromIsect(pNode, isect);
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolComponent::ICreateFromTriObject(plMaxNodeBase* pNode, Object* obj)
|
|
|
|
{
|
|
|
|
TriObject *meshObj = (TriObject *)obj->ConvertToType(TimeValue(0), triObjectClassID);
|
|
|
|
|
|
|
|
Mesh* mesh = &meshObj->mesh;
|
|
|
|
|
|
|
|
hsMatrix44 v2l = pNode->GetVertToLocal44();
|
|
|
|
hsMatrix44 l2v = pNode->GetLocalToVert44();
|
|
|
|
|
|
|
|
plConvexIsect* isect = TRACKED_NEW plConvexIsect;
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < mesh->getNumFaces(); i++ )
|
|
|
|
{
|
|
|
|
Face *maxFace = &mesh->faces[ i ];
|
|
|
|
|
|
|
|
Point3 v0 = mesh->verts[ maxFace->v[ 0 ] ];
|
|
|
|
Point3 v1 = mesh->verts[ maxFace->v[ 1 ] ];
|
|
|
|
Point3 v2 = mesh->verts[ maxFace->v[ 2 ] ];
|
|
|
|
|
|
|
|
hsPoint3 p0(v0.x, v0.y, v0.z);
|
|
|
|
hsPoint3 p1(v1.x, v1.y, v1.z);
|
|
|
|
hsPoint3 p2(v2.x, v2.y, v2.z);
|
|
|
|
|
|
|
|
p0 = v2l * p0;
|
|
|
|
p1 = v2l * p1;
|
|
|
|
p2 = v2l * p2;
|
|
|
|
|
|
|
|
hsVector3 n = hsVector3(&p1, &p0) % hsVector3(&p2, &p0);
|
|
|
|
|
|
|
|
isect->AddPlane(n, p0);
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey retVal = ISetFromIsect(pNode, isect);
|
|
|
|
|
|
|
|
if( meshObj != obj )
|
|
|
|
meshObj->DeleteThis();
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolComponent::ISetFromIsect(plMaxNodeBase* pNode, plVolumeIsect* isect)
|
|
|
|
{
|
|
|
|
isect->SetTransform(pNode->GetLocalToWorld44(), pNode->GetWorldToLocal44());
|
|
|
|
|
|
|
|
plSoftVolumeSimple* simple = TRACKED_NEW plSoftVolumeSimple;
|
|
|
|
simple->SetVolume(isect);
|
|
|
|
simple->SetDistance(fCompPB->GetFloat(kSoftDistance));
|
|
|
|
|
|
|
|
if( fCompPB->GetInt(kPartialEnabled) )
|
|
|
|
{
|
|
|
|
simple->SetInsideStrength(fCompPB->GetFloat(kInsidePower) * 0.01f);
|
|
|
|
simple->SetOutsideStrength(fCompPB->GetFloat(kOutsidePower) * 0.01f);
|
|
|
|
}
|
|
|
|
|
|
|
|
plSceneObject* sceneObj = pNode->GetSceneObject();
|
|
|
|
|
|
|
|
plKey retVal = ISetVolumeKey(simple);
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(simple->GetKey(), TRACKED_NEW plObjRefMsg(sceneObj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Union of volumes
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//Class that accesses the paramblock below.
|
|
|
|
class plSoftVolUnionComponent : public plSoftVolBaseComponent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum {
|
|
|
|
kSubVolumes,
|
|
|
|
kPartialEnabled,
|
|
|
|
kInsidePower,
|
|
|
|
kOutsidePower
|
|
|
|
};
|
|
|
|
protected:
|
|
|
|
|
|
|
|
plKey ICreateSoftVolume();
|
|
|
|
|
|
|
|
public:
|
|
|
|
plSoftVolUnionComponent();
|
|
|
|
void DeleteThis() { delete this; }
|
|
|
|
|
|
|
|
hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg);
|
|
|
|
};
|
|
|
|
|
|
|
|
// When one of our parameters that is a ref changes, send out the component ref
|
|
|
|
// changed message. Normally, messages from component refs are ignored since
|
|
|
|
// they pass along all the messages of the ref, which generates a lot of false
|
|
|
|
// converts.
|
|
|
|
class plSoftVolUnionAccessor : public PBAccessor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t)
|
|
|
|
{
|
|
|
|
if (id == plSoftVolUnionComponent::kSubVolumes)
|
|
|
|
{
|
|
|
|
plSoftVolUnionComponent *comp = (plSoftVolUnionComponent*)owner;
|
|
|
|
comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
plSoftVolUnionAccessor gSoftVolUnionAccessor;
|
|
|
|
|
|
|
|
|
|
|
|
class plSoftVolUnionComponentProc : public ParamMap2UserDlgProc
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
switch (msg)
|
|
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
{
|
|
|
|
IParamBlock2 *pb = map->GetParamBlock();
|
|
|
|
map->SetTooltip(plSoftVolUnionComponent::kSubVolumes, TRUE, "Select sub-volumes to combine into larger." );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_VOLUME)
|
|
|
|
{
|
|
|
|
// Adding a volume. Set it and refresh the UI to show it in our list.
|
|
|
|
plVolumeHitCallback hitCB((INode*)map->GetParamBlock()->GetOwner(), map->GetParamBlock(), plSoftVolUnionComponent::kSubVolumes, "Select sub-volumes");
|
|
|
|
GetCOREInterface()->DoHitByNameDialog(&hitCB);
|
|
|
|
map->Invalidate(plSoftVolUnionComponent::kSubVolumes);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void DeleteThis() {}
|
|
|
|
};
|
|
|
|
static plSoftVolUnionComponentProc gSoftVolUnionProc;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CLASS_DESC(plSoftVolUnionComponent, gSoftVolUnionDesc, "Soft Region Union", "SoftRegionUnion", COMP_TYPE_VOLUME, SOFTVOLUME_UNION_CID)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ParamBlockDesc2 gSoftVolUnionBk
|
|
|
|
(
|
|
|
|
plComponent::kBlkComp, _T("SoftRegionUnion"), 0, &gSoftVolUnionDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_SOFTVOLUME_UNION, IDS_COMP_SOFTVOLUME_UNION, 0, 0, &gSoftVolUnionProc,
|
|
|
|
|
|
|
|
plSoftVolUnionComponent::kSubVolumes, _T("SubRegions"), TYPE_INODE_TAB, 0, P_CAN_CONVERT, 0,
|
|
|
|
p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS,
|
|
|
|
p_classID, SOFTVOLUME_BASE_CID,
|
|
|
|
p_accessor, &gSoftVolUnionAccessor,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolUnionComponent::kPartialEnabled, _T("EnablePartial"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, FALSE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SOFTUNION_ENABLEPARTIAL,
|
|
|
|
p_enable_ctrls, 2, plSoftVolUnionComponent::kInsidePower, plSoftVolUnionComponent::kOutsidePower,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolUnionComponent::kInsidePower, _T("PowerInside"), TYPE_FLOAT, 0, 0,
|
|
|
|
p_default, 100.0,
|
|
|
|
p_range, 0.0, 100.0,
|
|
|
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
|
|
|
|
IDC_COMP_SOFTUNION_INSIDE, IDC_COMP_SOFTUNION_INSIDE_SPIN, 1.0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolComponent::kOutsidePower, _T("PowerOutside"), TYPE_FLOAT, 0, 0,
|
|
|
|
p_default, 0.0,
|
|
|
|
p_range, 0.0, 100.0,
|
|
|
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
|
|
|
|
IDC_COMP_SOFTUNION_OUTSIDE, IDC_COMP_SOFTUNION_OUTSIDE_SPIN, 1.0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
plSoftVolUnionComponent::plSoftVolUnionComponent()
|
|
|
|
{
|
|
|
|
fClassDesc = &gSoftVolUnionDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plSoftVolUnionComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
return plSoftVolBaseComponent::SetupProperties(pNode, errMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolUnionComponent::ICreateSoftVolume()
|
|
|
|
{
|
|
|
|
int numSubs = fCompPB->Count(kSubVolumes);
|
|
|
|
if( numSubs < 0 )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if( numSubs < 2 )
|
|
|
|
return fSoftKey = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolumes, 0, 0))->GetSoftVolume();
|
|
|
|
|
|
|
|
plSoftVolumeUnion* compound = TRACKED_NEW plSoftVolumeUnion;
|
|
|
|
fSoftKey = ISetVolumeKey(compound);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < numSubs; i++ )
|
|
|
|
{
|
|
|
|
plSoftVolBaseComponent *comp = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolumes, 0, i));
|
|
|
|
if (comp)
|
|
|
|
{
|
|
|
|
plKey subKey = comp->GetSoftVolume();
|
|
|
|
IAddSubVolume(fSoftKey, subKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( fCompPB->GetInt(kPartialEnabled) )
|
|
|
|
{
|
|
|
|
compound->SetInsideStrength(fCompPB->GetFloat(kInsidePower) * 0.01f);
|
|
|
|
compound->SetOutsideStrength(fCompPB->GetFloat(kOutsidePower) * 0.01f);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fSoftKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Intersection of volumes
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Class that accesses the paramblock below.
|
|
|
|
class plSoftVolIsectComponent : public plSoftVolBaseComponent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum {
|
|
|
|
kSubVolumes,
|
|
|
|
kPartialEnabled,
|
|
|
|
kInsidePower,
|
|
|
|
kOutsidePower
|
|
|
|
};
|
|
|
|
protected:
|
|
|
|
|
|
|
|
plKey ICreateSoftVolume();
|
|
|
|
|
|
|
|
public:
|
|
|
|
plSoftVolIsectComponent();
|
|
|
|
void DeleteThis() { delete this; }
|
|
|
|
|
|
|
|
hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg);
|
|
|
|
};
|
|
|
|
|
|
|
|
// When one of our parameters that is a ref changes, send out the component ref
|
|
|
|
// changed message. Normally, messages from component refs are ignored since
|
|
|
|
// they pass along all the messages of the ref, which generates a lot of false
|
|
|
|
// converts.
|
|
|
|
class plSoftVolIsectAccessor : public PBAccessor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t)
|
|
|
|
{
|
|
|
|
if (id == plSoftVolIsectComponent::kSubVolumes)
|
|
|
|
{
|
|
|
|
plSoftVolIsectComponent *comp = (plSoftVolIsectComponent*)owner;
|
|
|
|
comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
plSoftVolIsectAccessor gSoftVolIsectAccessor;
|
|
|
|
|
|
|
|
|
|
|
|
class plSoftVolIsectComponentProc : public ParamMap2UserDlgProc
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
switch (msg)
|
|
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
{
|
|
|
|
IParamBlock2 *pb = map->GetParamBlock();
|
|
|
|
map->SetTooltip(plSoftVolIsectComponent::kSubVolumes, TRUE, "Select sub-volumes to combine into larger." );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case WM_COMMAND:
|
|
|
|
if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_ADD_VOLUME)
|
|
|
|
{
|
|
|
|
// Adding a volume. Set it and refresh the UI to show it in our list.
|
|
|
|
plVolumeHitCallback hitCB((INode*)map->GetParamBlock()->GetOwner(), map->GetParamBlock(), plSoftVolIsectComponent::kSubVolumes, "Select sub-volumes");
|
|
|
|
GetCOREInterface()->DoHitByNameDialog(&hitCB);
|
|
|
|
map->Invalidate(plSoftVolIsectComponent::kSubVolumes);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void DeleteThis() {}
|
|
|
|
};
|
|
|
|
static plSoftVolIsectComponentProc gSoftVolIsectProc;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CLASS_DESC(plSoftVolIsectComponent, gSoftVolIsectDesc, "Soft Region Intersection", "SoftRegionIsect", COMP_TYPE_VOLUME, SOFTVOLUME_ISECT_CID)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ParamBlockDesc2 gSoftVolIsectBk
|
|
|
|
(
|
|
|
|
plComponent::kBlkComp, _T("SoftRegionIsect"), 0, &gSoftVolIsectDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_SOFTVOLUME_ISECT, IDS_COMP_SOFTVOLUME_ISECT, 0, 0, &gSoftVolIsectProc,
|
|
|
|
|
|
|
|
plSoftVolIsectComponent::kSubVolumes, _T("SubRegions"), TYPE_INODE_TAB, 0, P_CAN_CONVERT, 0,
|
|
|
|
p_ui, TYPE_NODELISTBOX, IDC_LIST_TARGS, 0, 0, IDC_DEL_TARGS,
|
|
|
|
p_classID, SOFTVOLUME_BASE_CID,
|
|
|
|
p_accessor, &gSoftVolIsectAccessor,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolIsectComponent::kPartialEnabled, _T("EnablePartial"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, FALSE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SOFTISECT_ENABLEPARTIAL,
|
|
|
|
p_enable_ctrls, 2, plSoftVolIsectComponent::kInsidePower, plSoftVolIsectComponent::kOutsidePower,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolIsectComponent::kInsidePower, _T("PowerInside"), TYPE_FLOAT, 0, 0,
|
|
|
|
p_default, 100.0,
|
|
|
|
p_range, 0.0, 100.0,
|
|
|
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
|
|
|
|
IDC_COMP_SOFTISECT_INSIDE, IDC_COMP_SOFTISECT_INSIDE_SPIN, 1.0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolIsectComponent::kOutsidePower, _T("PowerOutside"), TYPE_FLOAT, 0, 0,
|
|
|
|
p_default, 0.0,
|
|
|
|
p_range, 0.0, 100.0,
|
|
|
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
|
|
|
|
IDC_COMP_SOFTISECT_OUTSIDE, IDC_COMP_SOFTISECT_OUTSIDE_SPIN, 1.0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
plSoftVolIsectComponent::plSoftVolIsectComponent()
|
|
|
|
{
|
|
|
|
fClassDesc = &gSoftVolIsectDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plSoftVolIsectComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
return plSoftVolBaseComponent::SetupProperties(pNode, errMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolIsectComponent::ICreateSoftVolume()
|
|
|
|
{
|
|
|
|
int numSubs = fCompPB->Count(kSubVolumes);
|
|
|
|
if( numSubs < 0 )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
if( numSubs < 2 )
|
|
|
|
return fSoftKey = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolumes, 0, 0))->GetSoftVolume();
|
|
|
|
|
|
|
|
plSoftVolumeIntersect* compound = TRACKED_NEW plSoftVolumeIntersect;
|
|
|
|
fSoftKey = ISetVolumeKey(compound);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < numSubs; i++ )
|
|
|
|
{
|
|
|
|
plKey subKey = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolumes, 0, i))->GetSoftVolume();
|
|
|
|
IAddSubVolume(fSoftKey, subKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( fCompPB->GetInt(kPartialEnabled) )
|
|
|
|
{
|
|
|
|
compound->SetInsideStrength(fCompPB->GetFloat(kInsidePower) * 0.01f);
|
|
|
|
compound->SetOutsideStrength(fCompPB->GetFloat(kOutsidePower) * 0.01f);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fSoftKey;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Inversion of a (possibly complex) volume
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//Class that accesses the paramblock below.
|
|
|
|
class plSoftVolNegateComponent : public plSoftVolBaseComponent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum {
|
|
|
|
kSubVolume,
|
|
|
|
kPartialEnabled,
|
|
|
|
kInsidePower,
|
|
|
|
kOutsidePower
|
|
|
|
};
|
|
|
|
protected:
|
|
|
|
|
|
|
|
plKey ICreateSoftVolume();
|
|
|
|
|
|
|
|
public:
|
|
|
|
plSoftVolNegateComponent();
|
|
|
|
void DeleteThis() { delete this; }
|
|
|
|
|
|
|
|
hsBool SetupProperties(plMaxNode* pNode, plErrorMsg* errMsg);
|
|
|
|
};
|
|
|
|
|
|
|
|
// When one of our parameters that is a ref changes, send out the component ref
|
|
|
|
// changed message. Normally, messages from component refs are ignored since
|
|
|
|
// they pass along all the messages of the ref, which generates a lot of false
|
|
|
|
// converts.
|
|
|
|
class plSoftVolNegateAccessor : public PBAccessor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t)
|
|
|
|
{
|
|
|
|
if (id == plSoftVolNegateComponent::kSubVolume)
|
|
|
|
{
|
|
|
|
plSoftVolNegateComponent *comp = (plSoftVolNegateComponent*)owner;
|
|
|
|
comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
plSoftVolNegateAccessor gSoftVolNegateAccessor;
|
|
|
|
|
|
|
|
|
|
|
|
static plSingleCompSelProc gSoftVolNegateSingleSel(plSoftVolNegateComponent::kSubVolume, IDC_COMP_SOFTVOLUME_NEGATE_CHOOSE_SUB, "Select sub-volumes");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CLASS_DESC(plSoftVolNegateComponent, gSoftVolNegateDesc, "Soft Region Inverted", "SoftRegionInvert", COMP_TYPE_VOLUME, SOFTVOLUME_NEGATE_CID)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ParamBlockDesc2 gSoftVolNegateBk
|
|
|
|
(
|
|
|
|
plComponent::kBlkComp, _T("SoftRegionNegate"), 0, &gSoftVolNegateDesc, P_AUTO_CONSTRUCT+P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_SOFTVOLUME_NEGATE, IDS_COMP_SOFTVOLUME_NEGATE, 0, 0, &gSoftVolNegateSingleSel,
|
|
|
|
|
|
|
|
plSoftVolNegateComponent::kSubVolume, _T("SubRegion"), TYPE_INODE, 0, 0,
|
|
|
|
p_prompt, IDS_COMP_SOFTVOLUME_NEGATE,
|
|
|
|
p_accessor, &gSoftVolNegateAccessor,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolNegateComponent::kPartialEnabled, _T("EnablePartial"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, FALSE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_SOFTNEGATE_ENABLEPARTIAL,
|
|
|
|
p_enable_ctrls, 2, plSoftVolComponent::kInsidePower, plSoftVolComponent::kOutsidePower,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolNegateComponent::kInsidePower, _T("PowerInside"), TYPE_FLOAT, 0, 0,
|
|
|
|
p_default, 100.0,
|
|
|
|
p_range, 0.0, 100.0,
|
|
|
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
|
|
|
|
IDC_COMP_SOFTNEGATE_INSIDE, IDC_COMP_SOFTNEGATE_INSIDE_SPIN, 1.0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plSoftVolNegateComponent::kOutsidePower, _T("PowerOutside"), TYPE_FLOAT, 0, 0,
|
|
|
|
p_default, 0.0,
|
|
|
|
p_range, 0.0, 100.0,
|
|
|
|
p_ui, TYPE_SPINNER, EDITTYPE_POS_FLOAT,
|
|
|
|
IDC_COMP_SOFTNEGATE_OUTSIDE, IDC_COMP_SOFTNEGATE_OUTSIDE_SPIN, 1.0,
|
|
|
|
end,
|
|
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
plSoftVolNegateComponent::plSoftVolNegateComponent()
|
|
|
|
{
|
|
|
|
fClassDesc = &gSoftVolNegateDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plSoftVolNegateComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
return plSoftVolBaseComponent::SetupProperties(pNode, errMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
plKey plSoftVolNegateComponent::ICreateSoftVolume()
|
|
|
|
{
|
|
|
|
if( NumTargets() < 1 )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
INode* subNode = fCompPB->GetINode(kSubVolume);
|
|
|
|
if( subNode )
|
|
|
|
{
|
|
|
|
plKey subKey = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSubVolume))->GetSoftVolume();
|
|
|
|
if( subKey )
|
|
|
|
{
|
|
|
|
fSoftKey = IInvertVolume(subKey);
|
|
|
|
|
|
|
|
if( fCompPB->GetInt(kPartialEnabled) )
|
|
|
|
{
|
|
|
|
plSoftVolumeInvert* invert = plSoftVolumeInvert::ConvertNoRef(fSoftKey->GetObjectPtr());
|
|
|
|
if( invert )
|
|
|
|
{
|
|
|
|
invert->SetInsideStrength(fCompPB->GetFloat(kInsidePower) * 0.01f);
|
|
|
|
invert->SetOutsideStrength(fCompPB->GetFloat(kOutsidePower) * 0.01f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fSoftKey;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Soft Light Region
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//Class that accesses the paramblock below.
|
|
|
|
class plLightRegionComponent : public plComponent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum {
|
|
|
|
kSoftVolume
|
|
|
|
};
|
|
|
|
public:
|
|
|
|
plLightRegionComponent();
|
|
|
|
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.
|
|
|
|
hsBool SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg);
|
|
|
|
|
|
|
|
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *errMsg);
|
|
|
|
hsBool Convert(plMaxNode *node, plErrorMsg *errMsg);
|
|
|
|
};
|
|
|
|
|
|
|
|
// When one of our parameters that is a ref changes, send out the component ref
|
|
|
|
// changed message. Normally, messages from component refs are ignored since
|
|
|
|
// they pass along all the messages of the ref, which generates a lot of false
|
|
|
|
// converts.
|
|
|
|
class plLightRegionAccessor : public PBAccessor
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t)
|
|
|
|
{
|
|
|
|
if (id == plLightRegionComponent::kSoftVolume)
|
|
|
|
{
|
|
|
|
plLightRegionComponent *comp = (plLightRegionComponent*)owner;
|
|
|
|
comp->NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_COMP_REF_CHANGED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
plLightRegionAccessor gLightRegionAccessor;
|
|
|
|
|
|
|
|
|
|
|
|
static plSingleCompSelProc gLightRegionSingleSel(plLightRegionComponent::kSoftVolume, IDC_COMP_LIGHTREGION_CHOOSE_VOLUME, "Select soft region for light");
|
|
|
|
|
|
|
|
|
|
|
|
//Max desc stuff necessary below.
|
|
|
|
CLASS_DESC(plLightRegionComponent, gLightRegionDesc, "Light Region", "LightRegion", COMP_TYPE_VOLUME, LIGHTREGION_CID)
|
|
|
|
|
|
|
|
|
|
|
|
ParamBlockDesc2 gLightRegionBk
|
|
|
|
( // KLUDGE: not the defined block ID, but kept for backwards compat.
|
|
|
|
plComponent::kBlkComp, _T("LightRegion"), 0, &gLightRegionDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_LIGHTREGION, IDS_COMP_LIGHTREGION, 0, 0, &gLightRegionSingleSel,
|
|
|
|
|
|
|
|
plLightRegionComponent::kSoftVolume, _T("Region"), TYPE_INODE, 0, 0,
|
|
|
|
p_accessor, &gLightRegionAccessor,
|
|
|
|
end,
|
|
|
|
end
|
|
|
|
);
|
|
|
|
|
|
|
|
plLightRegionComponent::plLightRegionComponent()
|
|
|
|
{
|
|
|
|
fClassDesc = &gLightRegionDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plLightRegionComponent::Convert(plMaxNode *node, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
if( !fCompPB->GetINode(kSoftVolume) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
plSceneObject* sceneObj = node->GetSceneObject();
|
|
|
|
if( !sceneObj )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
plLightInfo* li = plLightInfo::ConvertNoRef(sceneObj->GetGenericInterface(plLightInfo::Index()));
|
|
|
|
if( !li )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSoftVolume));
|
|
|
|
if( !softComp )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
plKey softKey = softComp->GetSoftVolume();
|
|
|
|
if( !softKey )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(softKey, TRACKED_NEW plGenRefMsg(li->GetKey(), plRefMsg::kOnCreate, 0, plLightInfo::kSoftVolume), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plLightRegionComponent::PreConvert(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
|
|
|
|
// of properties on the MaxNode, as it's still indeterminant.
|
|
|
|
hsBool plLightRegionComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Soft Visibility Region
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//Class that accesses the paramblock below.
|
|
|
|
class plVisRegionSingleSel : public plSingleCompSelProc
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
plVisRegionSingleSel(ParamID nodeID, int dlgItem, TCHAR *title)
|
|
|
|
: plSingleCompSelProc(nodeID, dlgItem, title)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
BOOL DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
|
|
{
|
|
|
|
switch (msg)
|
|
|
|
{
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
{
|
|
|
|
IParamBlock2 *pb = map->GetParamBlock();
|
|
|
|
if( pb->GetInt(plVisRegionComponent::kExcludes) )
|
|
|
|
{
|
|
|
|
map->Enable(plVisRegionComponent::kDisableNormal, FALSE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectDraw, TRUE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectLight, TRUE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectOcc, TRUE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
map->Enable(plVisRegionComponent::kDisableNormal, TRUE);
|
|
|
|
if( pb->GetInt(plVisRegionComponent::kDisableNormal) )
|
|
|
|
{
|
|
|
|
map->Enable(plVisRegionComponent::kAffectDraw, FALSE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectLight, FALSE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectOcc, FALSE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
map->Enable(plVisRegionComponent::kAffectDraw, TRUE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectLight, TRUE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectOcc, TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
|
|
{
|
|
|
|
if( (LOWORD(wParam) == IDC_COMP_VISREGION_NOT) || (LOWORD(wParam) == IDC_COMP_VISREGION_DIS) )
|
|
|
|
{
|
|
|
|
IParamBlock2 *pb = map->GetParamBlock();
|
|
|
|
if( pb->GetInt(plVisRegionComponent::kExcludes) )
|
|
|
|
{
|
|
|
|
map->Enable(plVisRegionComponent::kDisableNormal, FALSE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectDraw, TRUE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectLight, TRUE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectOcc, TRUE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
map->Enable(plVisRegionComponent::kDisableNormal, TRUE);
|
|
|
|
if( pb->GetInt(plVisRegionComponent::kDisableNormal) )
|
|
|
|
{
|
|
|
|
map->Enable(plVisRegionComponent::kAffectDraw, FALSE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectLight, FALSE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectOcc, FALSE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
map->Enable(plVisRegionComponent::kAffectDraw, TRUE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectLight, TRUE);
|
|
|
|
map->Enable(plVisRegionComponent::kAffectOcc, TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return plSingleCompSelProc::DlgProc(t, map, hWnd, msg, wParam, lParam);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static plVisRegionSingleSel gVisRegionSingleSel(plVisRegionComponent::kSoftVolume, IDC_COMP_VISREGION_CHOOSE_VOLUME, "Select region for visibility");
|
|
|
|
|
|
|
|
|
|
|
|
//Max desc stuff necessary below.
|
|
|
|
CLASS_DESC(plVisRegionComponent, gVisRegionDesc, "Visibility Region", "VisRegion", COMP_TYPE_VOLUME, VISREGION_CID)
|
|
|
|
|
|
|
|
|
|
|
|
ParamBlockDesc2 gVisRegionBk
|
|
|
|
( // KLUDGE: not the defined block ID, but kept for backwards compat.
|
|
|
|
plComponent::kBlkComp, _T("VisRegion"), 0, &gVisRegionDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_VISREGION, IDS_COMP_VISREGION, 0, 0, &gVisRegionSingleSel,
|
|
|
|
|
|
|
|
plVisRegionComponent::kSoftVolume, _T("Region"), TYPE_INODE, 0, 0,
|
|
|
|
p_accessor, nil,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plVisRegionComponent::kAffectDraw, _T("AffectDraw"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, TRUE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_DRAW,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plVisRegionComponent::kAffectLight, _T("AffectLight"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, TRUE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_LIGHT,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plVisRegionComponent::kAffectOcc, _T("AffectOcc"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, FALSE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_OCC,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plVisRegionComponent::kExcludes, _T("Excludes"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, FALSE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_NOT,
|
|
|
|
end,
|
|
|
|
|
|
|
|
plVisRegionComponent::kDisableNormal, _T("DisableNormal"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, FALSE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_VISREGION_DIS,
|
|
|
|
end,
|
|
|
|
|
|
|
|
end
|
|
|
|
);
|
|
|
|
|
|
|
|
plVisRegionComponent::plVisRegionComponent()
|
|
|
|
{
|
|
|
|
fClassDesc = &gVisRegionDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plVisRegionComponent::ICheckVisRegion(const plLocation& loc)
|
|
|
|
{
|
|
|
|
if( !fVisReg )
|
|
|
|
{
|
|
|
|
if( !fCompPB->GetINode(kSoftVolume) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSoftVolume));
|
|
|
|
if( !softComp )
|
|
|
|
return;
|
|
|
|
|
|
|
|
plKey softKey = softComp->GetSoftVolume();
|
|
|
|
if( !softKey )
|
|
|
|
return;
|
|
|
|
|
|
|
|
fVisReg = TRACKED_NEW plVisRegion;
|
|
|
|
plKey key = hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), fVisReg, loc);
|
|
|
|
|
|
|
|
|
|
|
|
hsBool excludes = fCompPB->GetInt(kExcludes);
|
|
|
|
hsBool disableNormal = excludes ? false : fCompPB->GetInt(kDisableNormal);
|
|
|
|
|
|
|
|
fVisReg->SetProperty(plVisRegion::kIsNot, excludes);
|
|
|
|
fVisReg->SetProperty(plVisRegion::kReplaceNormal, true);
|
|
|
|
fVisReg->SetProperty(plVisRegion::kDisableNormal, disableNormal);
|
|
|
|
|
|
|
|
plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(fVisReg->GetKey(), plRefMsg::kOnCreate, 0, plVisRegion::kRefRegion);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(softKey, refMsg, plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plVisRegionComponent::Convert(plMaxNode *node, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
const char* dbgNodeName = node->GetName();
|
|
|
|
plSceneObject* obj = node->GetSceneObject();
|
|
|
|
if( !obj )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
hsBool excludes = fCompPB->GetInt(kExcludes);
|
|
|
|
hsBool disableNormal = excludes ? false : fCompPB->GetInt(kDisableNormal);
|
|
|
|
hsBool affectDraw = disableNormal ? true : fCompPB->GetInt(kAffectDraw);
|
|
|
|
hsBool affectOcc = disableNormal ? true : fCompPB->GetInt(kAffectOcc);
|
|
|
|
hsBool affectLight = disableNormal ? true : fCompPB->GetInt(kAffectLight);
|
|
|
|
|
|
|
|
const plDrawInterface* di = affectDraw ? obj->GetDrawInterface() : nil;
|
|
|
|
plOccluder* occ = affectOcc ? (plOccluder*)obj->GetGenericInterface(plOccluder::Index()) : nil;
|
|
|
|
plLightInfo* li = affectLight ? (plLightInfo*)obj->GetGenericInterface(plLightInfo::Index()) : nil;
|
|
|
|
if( !(disableNormal || di || occ || li) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
ICheckVisRegion(node->GetLocation());
|
|
|
|
if( !fVisReg )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if( di )
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(di->GetKey(), plRefMsg::kOnCreate, 0, plDrawInterface::kRefVisRegion), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
if( occ )
|
|
|
|
{
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(occ->GetKey(), plRefMsg::kOnCreate, 0, plOccluder::kRefVisRegion), plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( li )
|
|
|
|
{
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(li->GetKey(), plRefMsg::kOnCreate, 0, plLightInfo::kVisRegion), plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !(di || occ || li) )
|
|
|
|
{
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, -1, plObjRefMsg::kInterface), plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plVisRegionComponent::PreConvert(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
fVisReg = nil;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
|
|
|
|
// of properties on the MaxNode, as it's still indeterminant.
|
|
|
|
hsBool plVisRegionComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plVisRegionComponent::CollectRegions(plMaxNode* node, hsTArray<plVisRegion*>& regions)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < node->NumAttachedComponents(); i++ )
|
|
|
|
{
|
|
|
|
plComponentBase* comp = node->GetAttachedComponent(i);
|
|
|
|
if( comp && comp->ClassID() == VISREGION_CID )
|
|
|
|
{
|
|
|
|
plVisRegionComponent* regComp = (plVisRegionComponent*)comp;
|
|
|
|
if( regComp )
|
|
|
|
{
|
|
|
|
regComp->ICheckVisRegion(node->GetLocation());
|
|
|
|
if( regComp->fVisReg )
|
|
|
|
regions.Append(regComp->fVisReg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Relevance Region
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
//Class that accesses the paramblock below.
|
|
|
|
class plRelevanceRegionComponent : public plComponent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum {
|
|
|
|
kSoftVolume,
|
|
|
|
kName,
|
|
|
|
};
|
|
|
|
protected:
|
|
|
|
plRelevanceRegion* fRegion;
|
|
|
|
|
|
|
|
public:
|
|
|
|
plRelevanceRegionComponent();
|
|
|
|
void DeleteThis() { delete this; }
|
|
|
|
|
|
|
|
hsBool PreConvert(plMaxNode *pNode, plErrorMsg *errMsg);
|
|
|
|
hsBool Convert(plMaxNode *node, plErrorMsg *errMsg);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static plSingleCompSelProc gRelevanceRegionSingleSel(plRelevanceRegionComponent::kSoftVolume, IDC_COMP_RELREGION_CHOOSE_VOLUME, "Select region");
|
|
|
|
|
|
|
|
|
|
|
|
//Max desc stuff necessary below.
|
|
|
|
CLASS_DESC(plRelevanceRegionComponent, gRelevanceRegionDesc, "Relevance Region", "RelevanceRegion", COMP_TYPE_VOLUME, RELREGION_CID)
|
|
|
|
|
|
|
|
|
|
|
|
ParamBlockDesc2 gRelevanceRegionBk
|
|
|
|
(
|
|
|
|
plComponent::kBlkComp, _T("RelevanceRegion"), 0, &gRelevanceRegionDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_RELREGION, IDS_COMP_RELREGION, 0, 0, &gRelevanceRegionSingleSel,
|
|
|
|
|
|
|
|
plRelevanceRegionComponent::kSoftVolume, _T("Region"), TYPE_INODE, 0, 0,
|
|
|
|
p_accessor, nil,
|
|
|
|
end,
|
|
|
|
|
|
|
|
end
|
|
|
|
);
|
|
|
|
|
|
|
|
plRelevanceRegionComponent::plRelevanceRegionComponent()
|
|
|
|
{
|
|
|
|
fClassDesc = &gRelevanceRegionDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plRelevanceRegionComponent::Convert(plMaxNode *node, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
const char* dbgNodeName = node->GetName();
|
|
|
|
plSceneObject* obj = node->GetSceneObject();
|
|
|
|
if( !obj )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if( !fRegion )
|
|
|
|
{
|
|
|
|
if( !fCompPB->GetINode(kSoftVolume) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
plSoftVolBaseComponent* softComp = plSoftVolBaseComponent::GetSoftComponent(fCompPB->GetINode(kSoftVolume));
|
|
|
|
if( !softComp )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
plKey softKey = softComp->GetSoftVolume();
|
|
|
|
if( !softKey )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
fRegion = TRACKED_NEW plRelevanceRegion;
|
|
|
|
plKey key = hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), fRegion, node->GetLocation());
|
|
|
|
|
|
|
|
plGenRefMsg* refMsg = TRACKED_NEW plGenRefMsg(fRegion->GetKey(), plRefMsg::kOnCreate, 0, 0);
|
|
|
|
hsgResMgr::ResMgr()->SendRef(softKey, refMsg, plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fRegion->GetKey(), TRACKED_NEW plObjRefMsg(obj->GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plRelevanceRegionComponent::PreConvert(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
fRegion = nil;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Special Effects Visibility Set
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Max desc stuff necessary below.
|
|
|
|
CLASS_DESC(plEffVisSetComponent, gEffVisSetDesc, "Effect Vis Set", "EffVisSet", COMP_TYPE_VOLUME, EFFVISSET_CID)
|
|
|
|
|
|
|
|
|
|
|
|
ParamBlockDesc2 gEffVisSetBk
|
|
|
|
( // KLUDGE: not the defined block ID, but kept for backwards compat.
|
|
|
|
plComponent::kBlkComp, _T("EffVisSet"), 0, &gEffVisSetDesc, P_AUTO_CONSTRUCT + P_AUTO_UI, plComponent::kRefComp,
|
|
|
|
|
|
|
|
IDD_COMP_EFFVISSET, IDS_COMP_EFFVISSET, 0, 0, NULL,
|
|
|
|
|
|
|
|
plEffVisSetComponent::kHideNormal, _T("HideNormal"), TYPE_BOOL, 0, 0,
|
|
|
|
p_default, FALSE,
|
|
|
|
p_ui, TYPE_SINGLECHEKBOX, IDC_COMP_EFFVISSET_HIDENORMAL,
|
|
|
|
end,
|
|
|
|
|
|
|
|
end
|
|
|
|
);
|
|
|
|
|
|
|
|
plEffVisSetComponent::plEffVisSetComponent()
|
|
|
|
{
|
|
|
|
fClassDesc = &gEffVisSetDesc;
|
|
|
|
fClassDesc->MakeAutoParamBlocks(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plEffVisSetComponent::Convert(plMaxNode *node, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
const char* dbgNodeName = node->GetName();
|
|
|
|
plSceneObject* obj = node->GetSceneObject();
|
|
|
|
if( !obj )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
const plDrawInterface* di = obj->GetDrawInterface();
|
|
|
|
plOccluder* occ = (plOccluder*)obj->GetGenericInterface(plOccluder::Index());
|
|
|
|
plLightInfo* li = (plLightInfo*)obj->GetGenericInterface(plLightInfo::Index());
|
|
|
|
if( !(di || occ || li) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if( !GetVisRegion(node) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( di )
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(di->GetKey(), plRefMsg::kOnCreate, 0, plDrawInterface::kRefVisRegion), plRefFlags::kActiveRef);
|
|
|
|
|
|
|
|
if( occ )
|
|
|
|
{
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(occ->GetKey(), plRefMsg::kOnCreate, 0, plOccluder::kRefVisRegion), plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( li )
|
|
|
|
{
|
|
|
|
hsgResMgr::ResMgr()->AddViaNotify(fVisReg->GetKey(), TRACKED_NEW plGenRefMsg(li->GetKey(), plRefMsg::kOnCreate, 0, plLightInfo::kVisRegion), plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetupProperties - Internal setup and write-only set properties on the MaxNode. No reading
|
|
|
|
// of properties on the MaxNode, as it's still indeterminant.
|
|
|
|
hsBool plEffVisSetComponent::SetupProperties(plMaxNode *pNode, plErrorMsg *errMsg)
|
|
|
|
{
|
|
|
|
fVisReg = nil;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plVisRegion* plEffVisSetComponent::GetVisRegion(plMaxNode* node)
|
|
|
|
{
|
|
|
|
if( !fVisReg )
|
|
|
|
{
|
|
|
|
fVisReg = TRACKED_NEW plVisRegion;
|
|
|
|
plKey key = hsgResMgr::ResMgr()->NewKey(GetINode()->GetName(), fVisReg, node->GetLocation());
|
|
|
|
|
|
|
|
fVisReg->SetProperty(plVisRegion::kIsNot, false);
|
|
|
|
fVisReg->SetProperty(plVisRegion::kReplaceNormal, fCompPB->GetInt(kHideNormal));
|
|
|
|
fVisReg->SetProperty(plVisRegion::kDisable, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fVisReg;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
plEffVisSetComponent* plEffVisSetComponent::ConvertToEffVisSetComponent(plMaxNode* node)
|
|
|
|
{
|
|
|
|
if( !node )
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
plComponentBase *comp = node->ConvertToComponent();
|
|
|
|
|
|
|
|
// If this is an activator type component
|
|
|
|
if( comp )
|
|
|
|
{
|
|
|
|
if( comp->ClassID() == EFFVISSET_CID )
|
|
|
|
{
|
|
|
|
return (plEffVisSetComponent*)comp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plEffVisSetComponent::CollectRegions(plMaxNode* node, hsTArray<plVisRegion*>& regions)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < node->NumAttachedComponents(); i++ )
|
|
|
|
{
|
|
|
|
plComponentBase* comp = node->GetAttachedComponent(i);
|
|
|
|
if( comp && comp->ClassID() == EFFVISSET_CID )
|
|
|
|
{
|
|
|
|
plEffVisSetComponent* regComp = (plEffVisSetComponent*)comp;
|
|
|
|
if( regComp )
|
|
|
|
{
|
|
|
|
if( regComp->fVisReg )
|
|
|
|
regions.Append(regComp->fVisReg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|