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.
1206 lines
41 KiB
1206 lines
41 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/>. |
|
|
|
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 "pnKeyedObject/plKey.h" |
|
#include "hsMatrix44.h" |
|
#include "plRenderLevel.h" |
|
|
|
#include "plMaxNodeBase.h" |
|
#include "plMaxNodeData.h" |
|
#include "MaxComponent/plComponentBase.h" |
|
|
|
#include <guplib.h> |
|
#include <iparamm2.h> |
|
#include <dummy.h> |
|
#include <iskin.h> |
|
#include <modstack.h> |
|
#include <utilapi.h> |
|
|
|
#include <algorithm> |
|
#include <set> |
|
#include <vector> |
|
#pragma hdrstop |
|
|
|
// To support the new Plasma Light Objs, the classes are included below |
|
#include "MaxPlasmaLights/plRealTimeLightBase.h" |
|
|
|
#include "GlobalUtility.h" // Only needed for PLASMA_MAX_CLASSID, fix? |
|
|
|
#include "pnKeyedObject/plUoid.h" |
|
|
|
#include "pnModifier/plModifier.h" |
|
|
|
#include "MaxPlasmaMtls/Materials/plDecalMtl.h" |
|
|
|
CoreExport void *__cdecl MAX_new(size_t size); |
|
CoreExport void __cdecl MAX_delete(void* mem); |
|
|
|
void plMaxNodeBase::SetMaxNodeData(plMaxNodeData * pdat) |
|
{ |
|
const char* dbgNodeName = GetName(); |
|
|
|
// If object is a component, don't add node data |
|
if (IsComponent()) |
|
return; |
|
|
|
AppDataChunk *adc = GetAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaMaxNodeDataChunk); |
|
|
|
// If pointer is nil, remove any data |
|
if (!pdat) |
|
{ |
|
if( adc ) |
|
{ |
|
plMaxNodeData *pDataChunk = (plMaxNodeData*)adc->data; |
|
pDataChunk->DeInit(); |
|
} |
|
RemoveAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaMaxNodeDataChunk); |
|
return; |
|
} |
|
|
|
if (!adc) |
|
{ |
|
// Does not exist, create a new one... |
|
int len = sizeof(plMaxNodeData); |
|
plMaxNodeData *pDataChunk = (plMaxNodeData *)MAX_new(len); |
|
memcpy(pDataChunk, pdat, sizeof(*pdat)); |
|
pDataChunk->Init(); |
|
*pDataChunk = *pdat; |
|
AddAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaMaxNodeDataChunk, len, pDataChunk); |
|
} |
|
else |
|
{ |
|
plMaxNodeData *pDataChunk = (plMaxNodeData*)adc->data; |
|
// if someone does a GetMaxNodeData, they get a pointer to the data, |
|
// No need to set it, other wise set the Data chunk from the MaxNodeData passed in |
|
if (pDataChunk != pdat) |
|
*pDataChunk = *pdat; |
|
} |
|
} |
|
|
|
plMaxNodeData *plMaxNodeBase::GetMaxNodeData() |
|
{ |
|
AppDataChunk *adc = GetAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaMaxNodeDataChunk); |
|
if (adc) |
|
return (plMaxNodeData*)adc->data; |
|
|
|
return nil; |
|
} |
|
|
|
#define GetMD plMaxNodeData *pMD = GetMaxNodeData(); //hsAssert(pMD,"Missing MaxNodeData"); // Get MaxNode Data |
|
|
|
plKey plMaxNodeBase::GetKey() { GetMD; return (pMD) ? pMD->GetKey() : nil; } |
|
plSceneObject* plMaxNodeBase::GetSceneObject() { GetMD; return (pMD) ? pMD->GetSceneObject() : nil;} |
|
bool plMaxNodeBase::GetForceLocal() { GetMD; return (pMD) ? pMD->GetForceLocal() : false; } |
|
bool plMaxNodeBase::GetReverseSort() { GetMD; return (pMD) ? pMD->GetReverseSort() : false;} |
|
bool plMaxNodeBase::GetSortAsOpaque() { GetMD; return (pMD) ? pMD->GetSortAsOpaque() : false;} |
|
bool plMaxNodeBase::GetVS() { GetMD; return (pMD) ? pMD->GetVS() : false;} |
|
bool plMaxNodeBase::GetHasWaterHeight() { GetMD; return (pMD) ? pMD->GetHasWaterHeight() : false; } |
|
float plMaxNodeBase::GetWaterHeight() { GetMD; return (pMD) ? pMD->GetWaterHeight() : 0; } |
|
bool plMaxNodeBase::GetSmoothAll() { GetMD; return (pMD) ? pMD->GetSmoothAll() : false;} |
|
bool plMaxNodeBase::GetForceSortable() { GetMD; return (pMD) ? pMD->GetForceSortable() : false;} |
|
bool plMaxNodeBase::GetConcave() { GetMD; return (pMD) ? pMD->GetConcave() : false;} |
|
bool plMaxNodeBase::GetCalcEdgeLens() { GetMD; return (pMD) ? pMD->GetCalcEdgeLens() : false;} |
|
bool plMaxNodeBase::GetRunTimeLight() { GetMD; return (pMD) ? pMD->GetRunTimeLight() : false;} |
|
bool plMaxNodeBase::GetForceMatShade() { GetMD; return (pMD) ? pMD->GetForceMatShade() : false;} |
|
bool plMaxNodeBase::GetForceVisLOS() { GetMD; return (pMD) ? pMD->GetForceVisLOS() : false;} |
|
bool plMaxNodeBase::GetEnviron() { GetMD; return (pMD) ? pMD->GetEnviron() : false;} |
|
bool plMaxNodeBase::GetEnvironOnly() { GetMD; return (pMD) ? pMD->GetEnvironOnly() : false;} |
|
bool plMaxNodeBase::GetWaterDecEnv() { GetMD; return (pMD) ? pMD->GetWaterDecEnv() : false; } |
|
bool plMaxNodeBase::GetNoPreShade() { GetMD; return (pMD) ? pMD->GetNoPreShade() && !pMD->GetForcePreShade() : false;} |
|
bool plMaxNodeBase::GetForcePreShade() { GetMD; return (pMD) ? pMD->GetForcePreShade() : false;} |
|
plKey plMaxNodeBase::GetRoomKey() { GetMD; return (pMD) ? pMD->GetRoomKey() : nil; } |
|
bool plMaxNodeBase::GetDrawable() { GetMD; return (pMD) ? pMD->GetDrawable() : false; } |
|
bool plMaxNodeBase::GetPhysical() { GetMD; return (pMD) ? pMD->GetPhysical() : false; } |
|
bool plMaxNodeBase::GetItinerant() { GetMD; return (pMD) ? pMD->GetItinerant() : false; } |
|
bool plMaxNodeBase::GetUnBounded() { GetMD; return (pMD) ? pMD->GetUnBounded() : false; } |
|
bool plMaxNodeBase::GetDisableNormal() { GetMD; return (pMD) ? pMD->GetDisableNormal() : false; } |
|
uint32_t plMaxNodeBase::GetDecalLevel() { GetMD; return (pMD) ? pMD->GetDecalLevel() : 0; } |
|
bool plMaxNodeBase::GetMovable() { GetMD; return (pMD) ? pMD->GetMovable() : false; } |
|
bool plMaxNodeBase::GetIsBarney() { GetMD; return (pMD) ? pMD->GetIsBarney() : false; } |
|
bool plMaxNodeBase::GetForceShadow() { GetMD; return (pMD) ? pMD->GetForceShadow() : false; } |
|
bool plMaxNodeBase::GetAlphaTestHigh() { GetMD; return (pMD) ? pMD->GetAlphaTestHigh() : false; } |
|
bool plMaxNodeBase::GetFilterInherit() { GetMD; return (pMD) ? pMD->GetFilterInherit() : false; } |
|
bool plMaxNodeBase::GetNoShadow() { GetMD; return (pMD) ? pMD->GetNoShadow() : false; } |
|
bool plMaxNodeBase::GetNoSpanSort() { GetMD; return (pMD) ? pMD->GetNoSpanSort() : false; } |
|
bool plMaxNodeBase::GetNoSpanReSort() { GetMD; return (pMD) ? pMD->GetNoSpanReSort() : false; } |
|
bool plMaxNodeBase::GetNoFaceSort() { GetMD; return (pMD) ? pMD->GetNoFaceSort() : false; } |
|
bool plMaxNodeBase::GetNoDeferDraw() { GetMD; return (pMD) ? pMD->GetNoDeferDraw() : false; } |
|
bool plMaxNodeBase::GetBlendToFB() { GetMD; return (pMD) ? pMD->GetBlendToFB() : false; } |
|
bool plMaxNodeBase::GetForceMaterialCopy() { GetMD; return (pMD) ? pMD->GetForceMaterialCopy() : false; } |
|
bool plMaxNodeBase::GetInstanced() { GetMD; return (pMD) ? pMD->GetInstanced() : false; } |
|
bool plMaxNodeBase::GetParticleRelated() { GetMD; return (pMD) ? pMD->GetParticleRelated() : false; } |
|
uint32_t plMaxNodeBase::GetSoundIdxCounter() { GetMD; return (pMD) ? pMD->GetSoundIdxCounter() : 0; } |
|
plSceneObject* plMaxNodeBase::GetAvatarSO() { GetMD; return (pMD) ? pMD->GetAvatarSO() : nil; } |
|
BOOL plMaxNodeBase::HasFade() { GetMD; return (pMD) ? pMD->HasFade() : false; } |
|
Box3 plMaxNodeBase::GetFade() { GetMD; return (pMD) ? pMD->GetFade() : Box3(Point3(0,0,0), Point3(0,0,0)); } |
|
bool plMaxNodeBase::GetDup2Sided() { GetMD; return (pMD) ? pMD->GetDup2Sided() : false;} |
|
bool plMaxNodeBase::GetRadiateNorms() { GetMD; return (pMD) ? pMD->GetRadiateNorms() : false;} |
|
BOOL plMaxNodeBase::HasNormalChan() { GetMD; return (pMD) ? pMD->HasNormalChan() : false; } |
|
int plMaxNodeBase::GetNormalChan() { GetMD; return (pMD) ? pMD->GetNormalChan() : 0; } |
|
bool plMaxNodeBase::GetIsGUI() { GetMD; return (pMD) ? pMD->GetIsGUI() : false; } |
|
plSharedMesh* plMaxNodeBase::GetSwappableGeom() { GetMD; return (pMD) ? pMD->GetSwappableGeom() : nil; } |
|
uint32_t plMaxNodeBase::GetSwappableGeomTarget() { GetMD; return (pMD) ? pMD->GetSwappableGeomTarget() : -1; } |
|
plMaxBoneMap* plMaxNodeBase::GetBoneMap() { GetMD; return (pMD) ? pMD->GetBoneMap() : nil; } |
|
bool plMaxNodeBase::GetOverrideHighLevelSDL() { GetMD; return (pMD) ? pMD->GetOverrideHighLevelSDL() : false; } |
|
uint8_t plMaxNodeBase::GetAnimCompress() { GetMD; return (pMD) ? pMD->GetAnimCompress() : false; } |
|
float plMaxNodeBase::GetKeyReduceThreshold() { GetMD; return (pMD) ? pMD->GetKeyReduceThreshold() : 0; } |
|
int plMaxNodeBase::NumRenderDependencies() { GetMD; return (pMD) ? pMD->NumRenderDependencies() : 0; } |
|
plMaxNodeBase* plMaxNodeBase::GetRenderDependency(int i) { GetMD; return (pMD) ? pMD->GetRenderDependency(i) : nil; } |
|
|
|
int plMaxNodeBase::NumBones() { GetMD; return (pMD) ? pMD->NumBones() : 0; } |
|
plMaxNodeBase* plMaxNodeBase::GetBone(int i) { GetMD; return (pMD) ? pMD->GetBone(i) : nil; } |
|
|
|
|
|
//------------------------------ |
|
// Set Data from MaxNodeData |
|
//------------------------------ |
|
void plMaxNodeBase::SetCanConvert(bool b) { GetMD; pMD->SetCanConvert(b); } |
|
void plMaxNodeBase::SetMesh(hsGMesh *p) { GetMD; pMD->SetMesh(p); } |
|
void plMaxNodeBase::SetRoomKey(plKey p) { GetMD; pMD->SetRoomKey(p); } |
|
void plMaxNodeBase::SetDrawable(bool b) { GetMD; pMD->SetDrawable(b); } |
|
void plMaxNodeBase::SetPhysical(bool b) { GetMD; pMD->SetPhysical(b); } |
|
//void plMaxNodeBase::SetItinerant(bool b); |
|
void plMaxNodeBase::SetUnBounded(bool b) { GetMD; pMD->SetUnBounded(b); } |
|
void plMaxNodeBase::SetDisableNormal(bool b) { GetMD; pMD->SetDisableNormal(b); } |
|
void plMaxNodeBase::SetDecalLevel(uint32_t i) { GetMD; pMD->SetDecalLevel(i); } |
|
void plMaxNodeBase::SetMovable(bool b) { GetMD; pMD->SetMovable(b); pMD->SetRunTimeLight(b); pMD->SetNoPreShade(b); } |
|
void plMaxNodeBase::SetReverseSort(bool b) { GetMD; pMD->SetReverseSort(b); } |
|
void plMaxNodeBase::SetSortAsOpaque(bool b) { GetMD; pMD->SetSortAsOpaque(b); } |
|
void plMaxNodeBase::SetVS(bool b) { GetMD; pMD->SetVS(b); } |
|
void plMaxNodeBase::SetHasWaterHeight(bool b) { GetMD; pMD->SetHasWaterHeight(b); } |
|
void plMaxNodeBase::SetWaterHeight(float h) { GetMD; pMD->SetWaterHeight(h); } |
|
void plMaxNodeBase::SetSmoothAll(bool b) { GetMD; pMD->SetSmoothAll(b); } |
|
void plMaxNodeBase::SetForceSortable(bool b) { GetMD; pMD->SetForceSortable(b); } |
|
void plMaxNodeBase::SetConcave(bool b) { GetMD; pMD->SetConcave(b); } |
|
void plMaxNodeBase::SetCalcEdgeLens(bool b) { GetMD; pMD->SetCalcEdgeLens(b); } |
|
void plMaxNodeBase::SetRunTimeLight(bool b) { GetMD; pMD->SetRunTimeLight(b); } |
|
void plMaxNodeBase::SetForceMatShade(bool b) { GetMD; pMD->SetForceMatShade(b); } |
|
void plMaxNodeBase::SetForceVisLOS(bool b) { GetMD; pMD->SetForceVisLOS(b); } |
|
void plMaxNodeBase::SetEnviron(bool b) { GetMD; pMD->SetEnviron(b); } |
|
void plMaxNodeBase::SetEnvironOnly(bool b) { GetMD; pMD->SetEnvironOnly(b); } |
|
void plMaxNodeBase::SetWaterDecEnv(bool b) { GetMD; pMD->SetWaterDecEnv(b); } |
|
void plMaxNodeBase::SetNoPreShade(bool b) { GetMD; pMD->SetNoPreShade(b); } |
|
void plMaxNodeBase::SetForcePreShade(bool b) { GetMD; pMD->SetForcePreShade(b); } |
|
void plMaxNodeBase::SetForceLocal(bool b) { GetMD; pMD->SetForceLocal(b); } |
|
void plMaxNodeBase::SetIsBarney(bool b) { GetMD; pMD->SetIsBarney(b); } |
|
void plMaxNodeBase::SetForceShadow(bool b) { GetMD; pMD->SetForceShadow(b); } |
|
void plMaxNodeBase::SetAlphaTestHigh(bool b) { GetMD; pMD->SetAlphaTestHigh(b); } |
|
void plMaxNodeBase::SetFilterInherit(bool b) { GetMD; pMD->SetFilterInherit(b); } |
|
void plMaxNodeBase::SetNoShadow(bool b) { GetMD; pMD->SetNoShadow(b); } |
|
void plMaxNodeBase::SetNoSpanSort(bool b) { GetMD; pMD->SetNoSpanSort(b); } |
|
void plMaxNodeBase::SetNoSpanReSort(bool b) { GetMD; pMD->SetNoSpanReSort(b); } |
|
void plMaxNodeBase::SetNoFaceSort(bool b) { GetMD; pMD->SetNoFaceSort(b); } |
|
void plMaxNodeBase::SetNoDeferDraw(bool b) { GetMD; pMD->SetNoDeferDraw(b); } |
|
void plMaxNodeBase::SetBlendToFB(bool b) { GetMD; pMD->SetBlendToFB(b); } |
|
void plMaxNodeBase::SetForceMaterialCopy(bool b) { GetMD; pMD->SetForceMaterialCopy(b); } |
|
void plMaxNodeBase::SetInstanced(bool b) { GetMD; pMD->SetInstanced(b); } |
|
void plMaxNodeBase::SetParticleRelated(bool b) { GetMD; pMD->SetParticleRelated(b); } |
|
void plMaxNodeBase::SetSoundIdxCounter(uint32_t ctr) { GetMD; pMD->SetSoundIdxCounter(ctr); } |
|
void plMaxNodeBase::SetAvatarSO(plSceneObject *so) { GetMD; pMD->SetAvatarSO(so); } |
|
void plMaxNodeBase::SetFade(const Box3& b) { GetMD; pMD->SetFade(b); } |
|
void plMaxNodeBase::SetDup2Sided(bool b) { GetMD; pMD->SetDup2Sided(b); } |
|
void plMaxNodeBase::SetRadiateNorms(bool b) { GetMD; pMD->SetRadiateNorms(b); } |
|
void plMaxNodeBase::SetNormalChan(int n) { GetMD; pMD->SetNormalChan(n); } |
|
void plMaxNodeBase::SetIsGUI(bool b) { GetMD; pMD->SetIsGUI(b); } |
|
void plMaxNodeBase::SetSwappableGeom(plSharedMesh *sm) { GetMD; pMD->SetSwappableGeom(sm); } |
|
void plMaxNodeBase::SetSwappableGeomTarget(uint32_t id) { GetMD; pMD->SetSwappableGeomTarget(id); } |
|
void plMaxNodeBase::SetBoneMap(plMaxBoneMap *bones) { GetMD; pMD->SetBoneMap(bones); } |
|
void plMaxNodeBase::SetOverrideHighLevelSDL(bool b) { GetMD; pMD->SetOverrideHighLevelSDL(b); } |
|
void plMaxNodeBase::SetAnimCompress(uint8_t v) { GetMD; pMD->SetAnimCompress(v); } |
|
void plMaxNodeBase::SetKeyReduceThreshold(float v) { GetMD; pMD->SetKeyReduceThreshold(v); } |
|
void plMaxNodeBase::ClearRenderDependencies() { GetMD; pMD->ClearRenderDependencies(); } |
|
|
|
void plMaxNodeBase::AddBone(plMaxNodeBase* m) { GetMD; if(pMD) pMD->AddBone(m); } |
|
void plMaxNodeBase::ClearBones() { GetMD; if(pMD) pMD->ClearBones(); } |
|
|
|
plLocation plMaxNodeBase::GetLocation() |
|
{ |
|
plKey rmKey= GetRoomKey(); |
|
plLocation loc; |
|
loc.Invalidate(); |
|
if (rmKey) |
|
loc = rmKey->GetUoid().GetLocation(); |
|
return loc; |
|
} |
|
|
|
bool plMaxNodeBase::GetDirty(uint8_t i) |
|
{ |
|
uint8_t *dirty = IGetSceneViewerChunk(); |
|
return *dirty & i; |
|
} |
|
|
|
void plMaxNodeBase::SetDirty(uint8_t i, bool b) |
|
{ |
|
uint8_t *dirty = IGetSceneViewerChunk(); |
|
|
|
if (b) |
|
*dirty |= i; |
|
else |
|
*dirty &= ~i; |
|
} |
|
|
|
bool plMaxNodeBase::HasLoadMask() |
|
{ |
|
GetMD; |
|
return pMD->HasLoadMask(); |
|
} |
|
|
|
plLoadMask plMaxNodeBase::GetLoadMask() |
|
{ |
|
GetMD; |
|
return pMD->GetLoadMask(); |
|
} |
|
|
|
void plMaxNodeBase::AddLoadMask(const plLoadMask& m) |
|
{ |
|
GetMD; |
|
pMD->AddLoadMask(m); |
|
} |
|
|
|
bool plMaxNodeBase::RenderDependsOn(plMaxNodeBase* m) |
|
{ |
|
if( m == this ) |
|
return true; |
|
|
|
int i; |
|
for( i = 0; i < NumRenderDependencies(); i++ ) |
|
{ |
|
if( GetRenderDependency(i)->RenderDependsOn(m) ) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool plMaxNodeBase::AddRenderDependency(plMaxNodeBase* m) |
|
{ |
|
if( m->RenderDependsOn(this) ) |
|
return false; |
|
GetMD; |
|
pMD->AddRenderDependency(m); |
|
return true; |
|
} |
|
|
|
uint8_t *plMaxNodeBase::IGetSceneViewerChunk() |
|
{ |
|
uint8_t *SVChunk = nil; |
|
|
|
AppDataChunk *adc = GetAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaSceneViewerChunk); |
|
if (adc) |
|
SVChunk = (uint8_t*)adc->data; |
|
else |
|
{ |
|
// Does not exist, create a new one... |
|
SVChunk = (uint8_t*)MAX_new(1); |
|
*SVChunk = 0; |
|
AddAppDataChunk(PLASMA_MAX_CLASSID, GUP_CLASS_ID, kPlasmaSceneViewerChunk, 1, SVChunk); |
|
} |
|
|
|
hsAssert(SVChunk, "SceneViewer chunk not found or created"); |
|
return SVChunk; |
|
} |
|
|
|
bool plMaxNodeBase::CanConvert(bool recalculate) |
|
{ |
|
// Try and find a cached return value |
|
plMaxNodeData *md = GetMaxNodeData(); |
|
if (md && !recalculate) |
|
return md->CanConvert(); |
|
|
|
if (UserPropExists("IGNORE")) |
|
return false; |
|
|
|
Object *obj = EvalWorldState(0/*hsConverterUtils::Instance().GetTime(GetInterface())*/).obj; |
|
if (obj) |
|
{ |
|
if ( obj->CanConvertToType(triObjectClassID) // MeshObjs are accepted here |
|
|| obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) // Dummy boxes are accepted here |
|
|| obj->SuperClassID() == CAMERA_CLASS_ID // All Camera types are accepted here |
|
|| obj->ClassID() == Class_ID(UTILITY_CLASS_ID, 0) // All Camera targets are accepted here |
|
|| ( obj->ClassID() == RTOMNI_LIGHT_CLASSID |
|
|| obj->ClassID() == RTSPOT_LIGHT_CLASSID |
|
|| obj->ClassID() == RTDIR_LIGHT_CLASSID |
|
|| obj->ClassID() == RTPDIR_LIGHT_CLASSID ) |
|
|| ( obj->SuperClassID() == LIGHT_CLASS_ID // All run time lights are accepted here |
|
&& UserPropExists("RunTimeLight")) |
|
|
|
|| IsGroupMember() // Group objects are accepted here |
|
) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool plMaxNodeBase::IsTMAnimated() |
|
{ |
|
Control* tmControl = GetTMController(); |
|
return (tmControl && tmControl->IsAnimated()); |
|
} |
|
|
|
//// IsTMAnimatedRecur - test recursively up the chain /////////////////////////////////////////////////////////////// |
|
bool plMaxNodeBase::IsTMAnimatedRecur() |
|
{ |
|
const char* dbgNodeName = GetName(); |
|
bool shouldBe = false; |
|
|
|
if( !CanConvert() ) |
|
return false; |
|
if( IsTMAnimated() ) |
|
return true; |
|
|
|
return ((plMaxNodeBase*)GetParentNode())->IsTMAnimatedRecur(); |
|
} |
|
|
|
//// IsMovable /////////////////////////////////////////////////////////////// |
|
// Returns whether this node is "animated" (i.e. could move at runtime) |
|
bool plMaxNodeBase::IsMovable() |
|
{ |
|
const char* dbgNodeName = GetName(); |
|
bool shouldBe = false; |
|
|
|
|
|
if( !CanConvert() ) |
|
return false; |
|
|
|
if( GetMovable() ) |
|
return true; |
|
|
|
if( GetItinerant() ) |
|
shouldBe = true; |
|
else if( FindSkinModifier() ) |
|
shouldBe = true; |
|
|
|
// Moved this to plAnimComponent (so GetMovable() will reveal it) |
|
/* |
|
else if( IsTMAnimated() ) |
|
shouldBe = true; |
|
*/ |
|
if( shouldBe ) |
|
{ |
|
SetMovable( true ); |
|
return true; |
|
} |
|
|
|
return ((plMaxNodeBase*)GetParentNode())->IsMovable(); |
|
} |
|
|
|
// Recursively set so we don't have to recursively check. |
|
void plMaxNodeBase::SetItinerant(bool b) |
|
{ |
|
const char* dbgNodeName = GetName(); |
|
|
|
if( !CanConvert() ) |
|
return; |
|
|
|
GetMD; |
|
pMD->SetItinerant(b); |
|
|
|
int i; |
|
for( i = 0; i < NumChildren(); i++ ) |
|
{ |
|
((plMaxNodeBase*)GetChildNode(i))->SetItinerant(b); |
|
} |
|
} |
|
|
|
//// FindSkinModifier /////////////////////////////////////////////////////// |
|
// Given an INode, gets the ISkin object of that node, or nil if there is |
|
// none. Taken from the Max4 SDK, ISkin.h |
|
ISkin* plMaxNodeBase::FindSkinModifier() |
|
{ |
|
int modStackIndex; |
|
|
|
// Get object from node. Abort if no object. |
|
Object *pObj = GetObjectRef(); |
|
if( pObj == nil ) |
|
return nil; |
|
|
|
// Is derived object ? |
|
while( pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID ) |
|
{ |
|
IDerivedObject *pDerObj = (IDerivedObject *)pObj; |
|
|
|
// Iterate over all entries of the modifier stack. |
|
for( modStackIndex = 0; modStackIndex < pDerObj->NumModifiers(); modStackIndex++ ) |
|
{ |
|
// Get current modifier. |
|
Modifier *mod = pDerObj->GetModifier( modStackIndex ); |
|
|
|
// Is this Skin ? |
|
if( mod->ClassID() == SKIN_CLASSID ) |
|
{ |
|
ISkin* skin = (ISkin*)mod->GetInterface(I_SKIN); |
|
if( skin->GetNumBones() > 0 ) |
|
return skin; |
|
} |
|
} |
|
pObj = pDerObj->GetObjRef(); |
|
} |
|
|
|
// Not found. |
|
return nil; |
|
} |
|
|
|
bool plMaxNodeBase::IsXRef() |
|
{ |
|
// Is this an XRef'd object? |
|
Object *obj = GetObjectRef(); |
|
if (obj->SuperClassID() == SYSTEM_CLASS_ID && obj->ClassID() == XREFOBJ_CLASS_ID) |
|
return true; |
|
|
|
// |
|
// Is this part of an XRef'd scene? |
|
// |
|
// Walk up to our root node |
|
INode *root = GetParentNode(); |
|
while (!root->IsRootNode()) |
|
root = root->GetParentNode(); |
|
// If our root isn't the main root, we're in an XRef'd scene. |
|
if (root != GetCOREInterface()->GetRootNode()) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
bool plMaxNodeBase::IsComponent(Object *obj) |
|
{ |
|
if (!obj) |
|
obj = GetObjectRef(); |
|
|
|
if (obj && obj->CanConvertToType(COMPONENT_CLASSID)) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
bool plMaxNodeBase::IsExternComponent(Object *obj) |
|
{ |
|
if (!obj) |
|
obj = GetObjectRef(); |
|
|
|
if (obj && obj->CanConvertToType(EXT_COMPONENT_CLASSID)) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
plComponentBase *plMaxNodeBase::ConvertToComponent() |
|
{ |
|
if (IsComponent()) |
|
return (plComponentBase*)GetObjectRef(); |
|
|
|
return nil; |
|
} |
|
|
|
// There isn't an easy way to determine if there are any components attached to a node. |
|
// This method goes through the reflist of a node and backtracks up each ref (using |
|
// IRefMakerToComponent) to determine if it is a component. There are cases though |
|
// where a component can show up as two or more refs to a node, when it has a legitimate |
|
// target ref and some other ref like a proxy object. To catch this case we maintain a |
|
// list of the components found so far and ensure there aren't any duplicates. Maybe it |
|
// would be easier to just go through every component in the scene and check their target |
|
// lists... -Colin |
|
uint32_t plMaxNodeBase::NumAttachedComponents(bool all) |
|
{ |
|
uint32_t numComponents = 0; |
|
std::vector<plComponentBase*> comps; |
|
|
|
// Go through this item's reflist, looking for components |
|
DependentIterator di(this); |
|
ReferenceMaker* item = di.Next(); |
|
while (item) |
|
{ |
|
plComponentBase *comp = IRefMakerToComponent(item, all); |
|
if (comp && std::find(comps.begin(), comps.end(), comp) == comps.end()) |
|
{ |
|
comps.push_back(comp); |
|
numComponents++; |
|
} |
|
|
|
item = di.Next(); |
|
} |
|
|
|
return numComponents; |
|
} |
|
|
|
plComponentBase *plMaxNodeBase::GetAttachedComponent(uint32_t i, bool all) |
|
{ |
|
uint32_t numComponents = 0; |
|
std::vector<plComponentBase*> comps; |
|
|
|
// Go through this item's reflist, looking for components |
|
DependentIterator di(this); |
|
ReferenceMaker* item = di.Next(); |
|
while (item) |
|
{ |
|
plComponentBase *comp = IRefMakerToComponent(item, all); |
|
if (comp && std::find(comps.begin(), comps.end(), comp) == comps.end()) |
|
{ |
|
if (numComponents == i) |
|
return comp; |
|
|
|
comps.push_back(comp); |
|
numComponents++; |
|
} |
|
|
|
item = di.Next();; |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
plComponentBase *plMaxNodeBase::IRefMakerToComponent(ReferenceMaker *maker, bool all) |
|
{ |
|
if (!maker) |
|
return nil; |
|
|
|
// Is the refmaker a paramblock? If so, it may be the |
|
// targets block of a component |
|
if (maker->SuperClassID() == PARAMETER_BLOCK2_CLASS_ID) |
|
{ |
|
IParamBlock2 *pb = (IParamBlock2*)maker; |
|
ReferenceMaker *pbowner = pb->GetOwner(); |
|
|
|
// Is the owner of the paramblock a helper object (component superclass)? |
|
if (pbowner && pbowner->SuperClassID() == HELPER_CLASS_ID) |
|
{ |
|
Object *obj = (Object*)pbowner; |
|
// Is the owner of the paramblock a component? |
|
if (IsComponent(obj)) |
|
{ |
|
plComponentBase *comp = (plComponentBase*)obj; |
|
|
|
if (!all) |
|
{ |
|
// Does this component actually ref us? (A component can have other |
|
// refs to a node, like a proxy object, so we want to make sure this |
|
// node is actually in the target list.) |
|
for (uint32_t i = 0; i < comp->NumTargets(); i++) |
|
{ |
|
if (comp->GetTarget(i) == this) |
|
return comp; |
|
} |
|
} |
|
else |
|
return comp; |
|
} |
|
} |
|
} |
|
|
|
return nil; |
|
} |
|
|
|
bool plMaxNodeBase::IRenderLevelSet(bool forBlend) |
|
{ |
|
plMaxNodeData* md = GetMaxNodeData(); |
|
if( md ) |
|
return forBlend ? md->BlendLevelSet() : md->OpaqueLevelSet(); |
|
return false; |
|
} |
|
|
|
void plMaxNodeBase::ISetRenderLevel(const plRenderLevel& l, bool forBlend) |
|
{ |
|
plMaxNodeData* md = GetMaxNodeData(); |
|
if( md ) |
|
{ |
|
if( forBlend ) |
|
md->SetBlendLevel(l); |
|
else |
|
md->SetOpaqueLevel(l); |
|
} |
|
} |
|
|
|
const plRenderLevel& plMaxNodeBase::IGetRenderLevel(bool forBlend) |
|
{ |
|
plMaxNodeData* md = GetMaxNodeData(); |
|
if( !md ) |
|
{ |
|
static plRenderLevel defRenderLevel; |
|
return defRenderLevel; |
|
} |
|
return forBlend ? md->GetBlendLevel() : md->GetOpaqueLevel(); |
|
} |
|
|
|
uint32_t plMaxNodeBase::IGetMajorRenderLevel(bool forBlend) |
|
{ |
|
if( GetBlendToFB() ) |
|
return plRenderLevel::kFBMajorLevel; |
|
|
|
if( GetNoDeferDraw() || GetAvatarSO() ) |
|
forBlend = false; |
|
|
|
int numDep = NumRenderDependencies(); |
|
if( !numDep ) |
|
return forBlend ? plRenderLevel::kBlendRendMajorLevel : plRenderLevel::kDefRendMajorLevel; |
|
|
|
int iMaxDep = 0; |
|
const char* dbgNodeName = GetName(); |
|
|
|
int i; |
|
for( i = 0; i < numDep; i++ ) |
|
{ |
|
plMaxNodeBase* dep = GetRenderDependency(i); |
|
if( dep ) |
|
{ |
|
const char* depNodeName = dep->GetName(); |
|
int iDep = GetRenderDependency(i)->GetRenderLevel(forBlend).Major(); |
|
if( iDep > iMaxDep ) |
|
iMaxDep = iDep; |
|
} |
|
} |
|
|
|
return iMaxDep; |
|
} |
|
|
|
uint32_t plMaxNodeBase::IGetMinorRenderLevel(bool forBlend) |
|
{ |
|
if( GetAvatarSO() ) |
|
return plRenderLevel::kAvatarRendMinorLevel; |
|
|
|
int numDep = NumRenderDependencies(); |
|
if( !numDep ) |
|
return plRenderLevel::kDefRendMinorLevel; |
|
|
|
int iMaxDep = 0; |
|
|
|
const char* dbgNodeName = GetName(); |
|
|
|
int i; |
|
for( i = 0; i < numDep; i++ ) |
|
{ |
|
plMaxNodeBase* dep = GetRenderDependency(i); |
|
if( dep ) |
|
{ |
|
const char* depNodeName = dep->GetName(); |
|
int iDep = GetRenderDependency(i)->GetRenderLevel(forBlend).Minor(); |
|
if( iDep > iMaxDep ) |
|
iMaxDep = iDep; |
|
} |
|
} |
|
|
|
return iMaxDep + 4; |
|
} |
|
|
|
plRenderLevel plMaxNodeBase::ICalcRenderLevel(bool forBlend) |
|
{ |
|
if( GetBlendToFB() ) |
|
return plRenderLevel::kFBMajorLevel; |
|
|
|
if( GetAvatarSO() ) |
|
return plRenderLevel(plRenderLevel::kOpaqueMajorLevel, plRenderLevel::kAvatarRendMinorLevel); |
|
|
|
if( GetNoDeferDraw() ) |
|
forBlend = false; |
|
|
|
int numDep = NumRenderDependencies(); |
|
if( !numDep ) |
|
return forBlend |
|
? plRenderLevel(plRenderLevel::kBlendRendMajorLevel, plRenderLevel::kDefRendMinorLevel) |
|
: plRenderLevel(plRenderLevel::kDefRendMajorLevel, plRenderLevel::kDefRendMinorLevel); |
|
|
|
plRenderLevel maxLevel(plRenderLevel::kFBMajorLevel, plRenderLevel::kDefRendMinorLevel); |
|
|
|
const char* dbgNodeName = GetName(); |
|
|
|
int i; |
|
for( i = 0; i < numDep; i++ ) |
|
{ |
|
plMaxNodeBase* dep = GetRenderDependency(i); |
|
if( dep ) |
|
{ |
|
const char* depNodeName = dep->GetName(); |
|
|
|
plRenderLevel depLev = GetRenderDependency(i)->GetRenderLevel(forBlend); |
|
if( depLev > maxLevel ) |
|
maxLevel = depLev; |
|
} |
|
} |
|
maxLevel.Set(maxLevel.Level() + 4); |
|
|
|
return maxLevel; |
|
} |
|
|
|
const plRenderLevel& plMaxNodeBase::GetRenderLevel(bool forBlend) |
|
{ |
|
if( !CanConvert() ) |
|
{ |
|
static plRenderLevel retVal; |
|
return retVal; |
|
} |
|
|
|
if( !IRenderLevelSet(forBlend) ) |
|
{ |
|
#if 0 |
|
|
|
uint32_t major = IGetMajorRenderLevel(forBlend); |
|
uint32_t minor = IGetMinorRenderLevel(forBlend); |
|
|
|
ISetRenderLevel(plRenderLevel(major, minor), forBlend); |
|
#else |
|
ISetRenderLevel(ICalcRenderLevel(forBlend), forBlend); |
|
#endif |
|
} |
|
|
|
return IGetRenderLevel(forBlend); |
|
} |
|
|
|
BOOL plMaxNodeBase::GetGeoDice(int& maxFaces, float& maxSize, int& minFaces) |
|
{ |
|
plMaxNodeData* md = GetMaxNodeData(); |
|
if( md && md->GetGeoDice() ) |
|
{ |
|
maxFaces = md->GetGeoDiceMaxFaces(); |
|
maxSize = md->GetGeoDiceMaxSize(); |
|
minFaces = md->GetGeoDiceMinFaces(); |
|
return true; |
|
} |
|
maxFaces = 0; |
|
maxSize = 0; |
|
minFaces = 0; |
|
return false; |
|
} |
|
|
|
void plMaxNodeBase::SetGeoDice(BOOL on, int maxFaces, float maxSize, int minFaces) |
|
{ |
|
plMaxNodeData* md = GetMaxNodeData(); |
|
if( md ) |
|
{ |
|
md->SetGeoDice(on); |
|
md->SetGeoDiceMaxFaces(maxFaces); |
|
md->SetGeoDiceMaxSize(maxSize); |
|
md->SetGeoDiceMinFaces(minFaces); |
|
} |
|
} |
|
|
|
bool plMaxNodeBase::Contains(const Point3& worldPt) |
|
{ |
|
TimeValue currTime = 0;//hsConverterUtils::Instance().GetTime(GetInterface()); |
|
Object *obj = EvalWorldState(currTime).obj; |
|
if( !obj ) |
|
return false; |
|
|
|
Matrix3 l2w = GetObjectTM(currTime); |
|
Matrix3 w2l = Inverse(l2w); |
|
Point3 pt = w2l * worldPt; |
|
|
|
if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) ) |
|
{ |
|
DummyObject* dummy = (DummyObject*)obj; |
|
Box3 bnd = dummy->GetBox(); |
|
return bnd.Contains(pt); |
|
} |
|
if( obj->CanConvertToType(triObjectClassID) ) |
|
{ |
|
TriObject *meshObj = (TriObject *)obj->ConvertToType(currTime, triObjectClassID); |
|
if( !meshObj ) |
|
return false; |
|
|
|
Mesh& mesh = meshObj->mesh; |
|
Box3 bnd = mesh.getBoundingBox(); |
|
if( !bnd.Contains(pt) ) |
|
{ |
|
if( meshObj != obj ) |
|
meshObj->DeleteThis(); |
|
return false; |
|
} |
|
|
|
bool retVal = true; |
|
int i; |
|
for( i = 0; i < mesh.getNumFaces(); i++ ) |
|
{ |
|
Face& face = mesh.faces[i]; |
|
|
|
Point3 p0 = mesh.verts[face.v[0]]; |
|
Point3 p1 = mesh.verts[face.v[1]]; |
|
Point3 p2 = mesh.verts[face.v[2]]; |
|
|
|
Point3 n = CrossProd(p1 - p0, p2 - p0); |
|
|
|
if( DotProd(pt, n) > DotProd(p0, n) ) |
|
{ |
|
retVal = false; |
|
break; |
|
} |
|
} |
|
|
|
if( meshObj != obj ) |
|
meshObj->DeleteThis(); |
|
|
|
return retVal; |
|
} |
|
|
|
// If we can't figure out what it is, the point isn't inside it. |
|
return false; |
|
} |
|
|
|
bool plMaxNodeBase::Contains(const Box3& bnd, const Matrix3& l2w) |
|
{ |
|
int i; |
|
for( i = 0; i < 8; i++ ) |
|
{ |
|
if( !Contains(l2w * bnd[i]) ) |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
float plMaxNodeBase::BoxVolume(const Box3& bnd, const Matrix3& l2w) |
|
{ |
|
Point3 corner = l2w * bnd[0]; // min, min, min |
|
float len[3]; |
|
|
|
len[0] = Length((l2w * bnd[1]) - corner); // max, min, min |
|
len[1] = Length((l2w * bnd[2]) - corner); // min, max, min |
|
len[2] = Length((l2w * bnd[4]) - corner); // min, min, max |
|
|
|
return len[0] * len[1] * len[2]; |
|
} |
|
|
|
float plMaxNodeBase::RegionPriority() |
|
{ |
|
TimeValue currTime = 0;//hsConverterUtils::Instance().GetTime(GetInterface()); |
|
Object *obj = EvalWorldState(currTime).obj; |
|
if( !obj ) |
|
return 0; |
|
|
|
Matrix3 l2w = GetObjectTM(currTime); |
|
|
|
if( obj->ClassID() == Class_ID(DUMMY_CLASS_ID,0) ) |
|
{ |
|
DummyObject* dummy = (DummyObject*)obj; |
|
Box3 bnd = dummy->GetBox(); |
|
|
|
return BoxVolume(bnd, l2w); |
|
} |
|
|
|
if( obj->CanConvertToType(triObjectClassID) ) |
|
{ |
|
TriObject *meshObj = (TriObject *)obj->ConvertToType(currTime, triObjectClassID); |
|
if( !meshObj ) |
|
return 0; |
|
|
|
Mesh& mesh = meshObj->mesh; |
|
Box3 bnd = mesh.getBoundingBox(); |
|
|
|
if( meshObj != obj ) |
|
meshObj->DeleteThis(); |
|
|
|
return BoxVolume(bnd, l2w); |
|
} |
|
|
|
// Don't know how to interpret other, it's not contained. |
|
return 0; |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::GetLocalToParent44(TimeValue t) |
|
{ |
|
Matrix3 m3 = GetLocalToParent(t); |
|
hsMatrix44 m44 = Matrix3ToMatrix44(m3); |
|
return m44; |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::GetParentToLocal44(TimeValue t) |
|
{ |
|
Matrix3 m3 = GetParentToLocal(t); |
|
hsMatrix44 m44 = Matrix3ToMatrix44(m3); |
|
return m44; |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::GetLocalToWorld44(TimeValue t) |
|
{ |
|
Matrix3 m3 = GetLocalToWorld(t); |
|
hsMatrix44 m44 = Matrix3ToMatrix44(m3); |
|
return m44; |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::GetWorldToLocal44(TimeValue t) |
|
{ |
|
Matrix3 m3 = GetWorldToLocal(t); |
|
hsMatrix44 m44 = Matrix3ToMatrix44(m3); |
|
return m44; |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::GetOTM44(TimeValue t) |
|
{ |
|
Matrix3 m3 = GetOTM(t); |
|
hsMatrix44 m44 = Matrix3ToMatrix44(m3); |
|
return m44; |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::GetVertToLocal44(TimeValue t) |
|
{ |
|
Matrix3 m3 = GetVertToLocal(t); |
|
hsMatrix44 m44 = Matrix3ToMatrix44(m3); |
|
return m44; |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::GetLocalToVert44(TimeValue t) |
|
{ |
|
Matrix3 m3 = GetLocalToVert(t); |
|
hsMatrix44 m44 = Matrix3ToMatrix44(m3); |
|
return m44; |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::GetLocalToOBB44(TimeValue t) |
|
{ |
|
Matrix3 m3 = GetLocalToOBB(t); |
|
hsMatrix44 m44 = Matrix3ToMatrix44(m3); |
|
return m44; |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::GetOBBToLocal44(TimeValue t) |
|
{ |
|
Matrix3 m3 = GetOBBToLocal(t); |
|
hsMatrix44 m44 = Matrix3ToMatrix44(m3); |
|
return m44; |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetParentToWorld(TimeValue t) |
|
{ |
|
return Inverse(GetWorldToParent(t)); |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetWorldToParent(TimeValue t) |
|
{ |
|
// This may look back-ass-ward, but that's only because it |
|
// is. If we've got inheritance filtering on, then our localtoworld |
|
// is no longer parentl2w * l2p, because we'll be ignoring |
|
// some of our parent's transform. More precisely, we'll be |
|
// clobbering parts of the product of our parent's current transform |
|
// and our current local to parent. So we're going to calculate |
|
// a parent to world transform here that would get us to the |
|
// right point and orientation in space, even though it has |
|
// little or nothing to do with our parent's real transform. |
|
// Note that we only go through this charade if we've got |
|
// filtering of inheritance active for this node. |
|
plMaxNodeBase* parent = (plMaxNodeBase*)GetParentNode(); |
|
if( !GetFilterInherit() ) |
|
return parent->GetWorldToLocal(t); |
|
|
|
// l2w = l2p * parentL2W |
|
// l2w * parentW2L = l2p |
|
// parentW2L = w2l * l2p |
|
Point3 pos; |
|
float rot[4]; |
|
ScaleValue scl; |
|
Interval posInv; |
|
Interval rotInv; |
|
Interval sclInv; |
|
|
|
Matrix3Indirect parentMatrix(parent->GetNodeTM(t)); |
|
|
|
TMComponentsArg cmpts(&pos, &posInv, rot, &rotInv, &scl, &sclInv); |
|
GetTMController()->GetLocalTMComponents(t, cmpts, parentMatrix); |
|
|
|
Quat q; |
|
if( cmpts.rotRep == TMComponentsArg::kQuat ) |
|
q = Quat(rot); |
|
else |
|
EulerToQuat(rot, q, cmpts.rotRep); |
|
|
|
Matrix3 l2p(true); |
|
l2p.PreTranslate(pos); |
|
PreRotateMatrix(l2p, q); |
|
l2p.PreScale(scl.s); |
|
PreRotateMatrix(l2p, scl.q); |
|
|
|
Matrix3 w2l = GetWorldToLocal(t); |
|
|
|
return w2l * l2p; |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetLocalToParent(TimeValue t) |
|
{ |
|
// l2w = l2p * parentL2W |
|
// l2w * Inverse(parentL2W) = l2p |
|
// l2w * parentW2L = l2p |
|
Matrix3 l2w = GetLocalToWorld(t); |
|
Matrix3 w2p(true); |
|
if( !GetParentNode()->IsRootNode() ) |
|
w2p = GetWorldToParent(t); |
|
|
|
Matrix3 l2p = l2w * w2p; |
|
return l2p; |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetParentToLocal(TimeValue t) |
|
{ |
|
Matrix3 loc2Par = GetLocalToParent(t); |
|
Matrix3 par2Loc = Inverse(loc2Par); |
|
return par2Loc; |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetLocalToWorld(TimeValue t) |
|
{ |
|
if( GetForceLocal() ) |
|
{ |
|
// v2l * l2w = objectTM |
|
// l2w = Inverse(v2l) * objectTM; |
|
// l2w = l2v * objectTM |
|
Matrix3 objectTM = GetObjectTM(t); |
|
Matrix3 l2w = GetLocalToVert(t) * objectTM ; |
|
return l2w; |
|
} |
|
if( !GetParentNode()->IsRootNode() ) |
|
return GetParentToWorld(t); |
|
|
|
return Matrix3(true); |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetWorldToLocal(TimeValue t) |
|
{ |
|
Matrix3 l2w = GetLocalToWorld(t); |
|
Matrix3 w2l = Inverse(l2w); |
|
return w2l; |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetOTM(TimeValue t) |
|
{ |
|
// objectTM = otm * nodeTM |
|
// otm = objectTM * Inverse(nodeTM) |
|
Matrix3 objectTM = GetObjectTM(t); |
|
Matrix3 nodeTM = GetNodeTM(t); |
|
Matrix3 otm = objectTM * Inverse(nodeTM); |
|
otm.ValidateFlags(); |
|
return otm; |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetVertToLocal(TimeValue t) |
|
{ |
|
Matrix3 objectTM; |
|
|
|
// |
|
// If animated or we want forced into local space |
|
// still return OTM to fold into vertices |
|
// |
|
if( GetForceLocal() ) |
|
{ |
|
return GetOTM(t); |
|
} |
|
|
|
// otherwise flatten our local to parent into the verts |
|
// note that our parent may have flattened their l2p into |
|
// their v2l as well. |
|
// so |
|
// objectTM = v2l * l2p * parentL2W |
|
// objectTM * Inverse(parentL2W) = v2l * l2p |
|
// objectTM * parentW2L = v2l (w/ l2p folded in) |
|
// |
|
// Objects transformation ObjectTM = OTM * nodeTM |
|
objectTM = GetObjectTM(t); |
|
|
|
Matrix3 w2p(true); |
|
if( !GetParentNode()->IsRootNode() ) |
|
w2p = GetWorldToParent(t); |
|
|
|
Matrix3 v2l = objectTM * w2p; |
|
|
|
return v2l; |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetLocalToVert(TimeValue t) |
|
{ |
|
Matrix3 v2l = GetVertToLocal(t); |
|
Matrix3 l2v = Inverse(v2l); |
|
return l2v; |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetLocalToOBB(TimeValue t) |
|
{ |
|
return GetLocalToVert(t) * GetOTM(t); |
|
} |
|
|
|
Matrix3 plMaxNodeBase::GetOBBToLocal(TimeValue t) |
|
{ |
|
return Inverse(GetOTM(t)) * GetVertToLocal(t); |
|
} |
|
|
|
hsMatrix44 plMaxNodeBase::Matrix3ToMatrix44(const Matrix3& m3) |
|
{ |
|
const MRow* m = m3.GetAddr(); |
|
|
|
hsMatrix44 m44; |
|
m44.Reset(); |
|
m44.fMap[0][0] = m[0][0]; |
|
m44.fMap[0][1] = m[1][0]; |
|
m44.fMap[0][2] = m[2][0]; |
|
m44.fMap[0][3] = m[3][0]; |
|
|
|
m44.fMap[1][0] = m[0][1]; |
|
m44.fMap[1][1] = m[1][1]; |
|
m44.fMap[1][2] = m[2][1]; |
|
m44.fMap[1][3] = m[3][1]; |
|
|
|
m44.fMap[2][0] = m[0][2]; |
|
m44.fMap[2][1] = m[1][2]; |
|
m44.fMap[2][2] = m[2][2]; |
|
m44.fMap[2][3] = m[3][2]; |
|
|
|
m44.IsIdentity(); |
|
|
|
return m44; |
|
} |
|
|
|
Matrix3 plMaxNodeBase::Matrix44ToMatrix3(const hsMatrix44& m44) |
|
{ |
|
Matrix3 m3; |
|
|
|
MRow* m = m3.GetAddr(); |
|
|
|
m[0][0] = m44.fMap[0][0]; |
|
m[1][0] = m44.fMap[0][1]; |
|
m[2][0] = m44.fMap[0][2]; |
|
m[3][0] = m44.fMap[0][3]; |
|
|
|
m[0][1] = m44.fMap[1][0]; |
|
m[1][1] = m44.fMap[1][1]; |
|
m[2][1] = m44.fMap[1][2]; |
|
m[3][1] = m44.fMap[1][3]; |
|
|
|
m[0][2] = m44.fMap[2][0]; |
|
m[1][2] = m44.fMap[2][1]; |
|
m[2][2] = m44.fMap[2][2]; |
|
m[3][2] = m44.fMap[2][3]; |
|
|
|
m3.ValidateFlags(); |
|
|
|
return m3; |
|
}
|
|
|