You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1594 lines
51 KiB
1594 lines
51 KiB
/*==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/>. |
|
|
|
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); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|