|
|
|
/*==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 "plSceneNode.h"
|
|
|
|
#include "pnDispatch/plDispatch.h"
|
|
|
|
#include "plMessage/plNodeCleanupMsg.h"
|
|
|
|
#include "pnMessage/plNodeRefMsg.h"
|
|
|
|
|
|
|
|
#include "hsStream.h"
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
|
|
|
|
#include "pnSceneObject/plSceneObject.h"
|
|
|
|
#include "plDrawable.h"
|
|
|
|
#include "plPhysical.h"
|
|
|
|
#include "plAudible.h"
|
|
|
|
#include "plGLight/plLightInfo.h"
|
|
|
|
#include "pnMessage/plRefMsg.h"
|
|
|
|
#include "plPipeline.h"
|
|
|
|
#include "pnKeyedObject/plKey.h"
|
|
|
|
#include "plDrawable/plSpaceTreeMaker.h"
|
|
|
|
#include "plDrawable/plSpaceTree.h"
|
|
|
|
#include "plPageTreeMgr.h"
|
|
|
|
#include "plOccluder.h"
|
|
|
|
|
|
|
|
//MFHORSE
|
|
|
|
//BLACK
|
|
|
|
// temp hack for debugging
|
|
|
|
#include "plDrawable/plDrawableSpans.h"
|
|
|
|
#include "pnKeyedObject/plKeyImp.h"
|
|
|
|
|
|
|
|
plSceneNode::plSceneNode()
|
|
|
|
: fDepth(0),
|
|
|
|
fSpaceTree(nil),
|
|
|
|
fFilterGenerics(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
plSceneNode::~plSceneNode()
|
|
|
|
{
|
|
|
|
plgDispatch::Dispatch()->UnRegisterForExactType(plNodeCleanupMsg::Index(), GetKey());
|
|
|
|
|
|
|
|
delete fSpaceTree;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// Init ////////////////////////////////////////////////////////////////////
|
|
|
|
// Because we can't register for messages on construction. Doh.
|
|
|
|
|
|
|
|
void plSceneNode::Init()
|
|
|
|
{
|
|
|
|
/// :P
|
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plNodeCleanupMsg::Index(), GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::Read(hsStream* s, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
hsKeyedObject::Read(s, mgr);
|
|
|
|
|
|
|
|
uint32_t n;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
n = s->ReadLE32();
|
|
|
|
fSceneObjects.Reset();
|
|
|
|
for( i = 0; i < n; i++ )
|
|
|
|
{
|
|
|
|
plNodeRefMsg* refMsg = new plNodeRefMsg(GetKey(), plRefMsg::kOnCreate, i, plNodeRefMsg::kObject);
|
|
|
|
plKey key = mgr->ReadKeyNotifyMe(s, refMsg, plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
|
|
|
|
n = s->ReadLE32();
|
|
|
|
fGenericPool.Reset();
|
|
|
|
for( i = 0; i < n; i++ )
|
|
|
|
{
|
|
|
|
plNodeRefMsg* refMsg = new plNodeRefMsg(GetKey(), plRefMsg::kOnCreate, -1, plNodeRefMsg::kGeneric);
|
|
|
|
mgr->ReadKeyNotifyMe(s, refMsg, plRefFlags::kActiveRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::Write(hsStream* s, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
hsKeyedObject::Write(s, mgr);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
s->WriteLE32(fSceneObjects.GetCount());
|
|
|
|
for( i = 0; i < fSceneObjects.GetCount(); i++ )
|
|
|
|
mgr->WriteKey(s,fSceneObjects[i]);
|
|
|
|
|
|
|
|
s->WriteLE32(fGenericPool.GetCount());
|
|
|
|
for( i = 0; i < fGenericPool.GetCount(); i++ )
|
|
|
|
mgr->WriteKey(s, fGenericPool[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::Harvest(plVolumeIsect* isect, hsTArray<plDrawVisList>& levList)
|
|
|
|
{
|
|
|
|
static hsTArray<int16_t> visList;
|
|
|
|
visList.SetCount(0);
|
|
|
|
GetSpaceTree()->HarvestLeaves(isect, visList);
|
|
|
|
static hsTArray<int16_t> visSpans;
|
|
|
|
visSpans.SetCount(0);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < visList.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
int idx = visList[i];
|
|
|
|
fDrawPool[idx]->GetSpaceTree()->HarvestLeaves(isect, visSpans);
|
|
|
|
if( visSpans.GetCount() )
|
|
|
|
{
|
|
|
|
plDrawVisList* drawVis = levList.Push();
|
|
|
|
drawVis->fDrawable = fDrawPool[idx];
|
|
|
|
drawVis->fVisList.Swap(visSpans);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::CollectForRender(plPipeline* pipe, hsTArray<plDrawVisList>& levList, plVisMgr* visMgr)
|
|
|
|
{
|
|
|
|
static hsTArray<int16_t> visList;
|
|
|
|
visList.SetCount(0);
|
|
|
|
pipe->HarvestVisible(GetSpaceTree(), visList);
|
|
|
|
static hsTArray<int16_t> visSpans;
|
|
|
|
visSpans.SetCount(0);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < visList.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
int idx = visList[i];
|
|
|
|
if( pipe->PreRender(fDrawPool[idx], visSpans, visMgr) )
|
|
|
|
{
|
|
|
|
plDrawVisList* drawVis = levList.Push();
|
|
|
|
drawVis->fDrawable = fDrawPool[idx];
|
|
|
|
drawVis->fVisList.Swap(visSpans);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::SubmitOccluders(plPageTreeMgr* pageMgr) const
|
|
|
|
{
|
|
|
|
pageMgr->AddOccluderList(fOccluders);
|
|
|
|
}
|
|
|
|
|
|
|
|
plSpaceTree* plSceneNode::IBuildSpaceTree()
|
|
|
|
{
|
|
|
|
plSpaceTreeMaker maker;
|
|
|
|
maker.Reset();
|
|
|
|
|
|
|
|
hsBounds3Ext bnd;
|
|
|
|
hsPoint3 zero(0, 0, 0);
|
|
|
|
bnd.Reset(&zero);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fDrawPool.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fDrawPool[i] )
|
|
|
|
maker.AddLeaf(fDrawPool[i]->GetSpaceTree()->GetWorldBounds());
|
|
|
|
else
|
|
|
|
maker.AddLeaf(bnd, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
fSpaceTree = maker.MakeTree();
|
|
|
|
fSpaceTree->MakeDirty();
|
|
|
|
|
|
|
|
return fSpaceTree;
|
|
|
|
}
|
|
|
|
|
|
|
|
plSpaceTree* plSceneNode::ITrashSpaceTree()
|
|
|
|
{
|
|
|
|
delete fSpaceTree;
|
|
|
|
return fSpaceTree = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::IDirtySpaceTree()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fDrawPool.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fDrawPool[i] && fDrawPool[i]->GetSpaceTree()->IsDirty() )
|
|
|
|
{
|
|
|
|
fDrawPool[i]->GetSpaceTree()->Refresh();
|
|
|
|
fSpaceTree->MoveLeaf(i, fDrawPool[i]->GetSpaceTree()->GetWorldBounds());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plSpaceTree* plSceneNode::GetSpaceTree()
|
|
|
|
{
|
|
|
|
if( !fSpaceTree )
|
|
|
|
{
|
|
|
|
IBuildSpaceTree();
|
|
|
|
}
|
|
|
|
IDirtySpaceTree();
|
|
|
|
return fSpaceTree;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::ISetDrawable(plDrawable* d)
|
|
|
|
{
|
|
|
|
if( !d )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (fDrawPool.Find(d) == fDrawPool.kMissingIndex)
|
|
|
|
{
|
|
|
|
fDrawPool.Append(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
ITrashSpaceTree();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::ISetAudible(plAudible* a)
|
|
|
|
{
|
|
|
|
if( !a )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( fAudioPool.kMissingIndex == fAudioPool.Find(a) )
|
|
|
|
{
|
|
|
|
fAudioPool.Append(a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::ISetPhysical(plPhysical* p)
|
|
|
|
{
|
|
|
|
if( !p )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if( fSimulationPool.kMissingIndex == fSimulationPool.Find(p) )
|
|
|
|
{
|
|
|
|
fSimulationPool.Append(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::ISetObject(plSceneObject* o)
|
|
|
|
{
|
|
|
|
if( o && (fSceneObjects.kMissingIndex == fSceneObjects.Find(o)) )
|
|
|
|
{
|
|
|
|
fSceneObjects.Append(o);
|
|
|
|
|
|
|
|
// MF_NET_GROUPS_TEST
|
|
|
|
// This will have no effect on members of NetGroupConstants
|
|
|
|
o->SetNetGroup(o->SelectNetGroup(GetKey()));
|
|
|
|
|
|
|
|
o->SetSceneNode(GetKey());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::ISetLight(plLightInfo* l)
|
|
|
|
{
|
|
|
|
if( fLightPool.kMissingIndex == fLightPool.Find(l) )
|
|
|
|
fLightPool.Append( l );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::ISetOccluder(plOccluder* o)
|
|
|
|
{
|
|
|
|
if( fOccluders.kMissingIndex == fOccluders.Find(o) )
|
|
|
|
{
|
|
|
|
fOccluders.Append(o);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::ISetGeneric(hsKeyedObject* k)
|
|
|
|
{
|
|
|
|
if( fGenericPool.kMissingIndex == fGenericPool.Find(k) )
|
|
|
|
fGenericPool.Append(k);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::IRemoveDrawable(plDrawable* d)
|
|
|
|
{
|
|
|
|
int idx = fDrawPool.Find(d);
|
|
|
|
if( idx != fDrawPool.kMissingIndex )
|
|
|
|
fDrawPool.Remove(idx);
|
|
|
|
|
|
|
|
ITrashSpaceTree();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::IRemoveAudible(plAudible* a)
|
|
|
|
{
|
|
|
|
int idx = fAudioPool.Find(a);
|
|
|
|
if( idx != fAudioPool.kMissingIndex )
|
|
|
|
fAudioPool.Remove(idx);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::IRemovePhysical(plPhysical* p)
|
|
|
|
{
|
|
|
|
hsAssert(p, "Removing nil physical");
|
|
|
|
|
|
|
|
#ifdef HS_DEBUGGING
|
|
|
|
if (p)
|
|
|
|
{
|
|
|
|
plKey oldNodeKey = p->GetSceneNode();
|
|
|
|
if (oldNodeKey && oldNodeKey != GetKey())
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
sprintf(buf, "Trying to remove physical %s from scenenode %s,\nbut it's actually in %s",
|
|
|
|
p->GetKeyName(), GetKeyName(), oldNodeKey->GetName());
|
|
|
|
hsAssert(0, buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int idx = fSimulationPool.Find(p);
|
|
|
|
if( idx != fSimulationPool.kMissingIndex )
|
|
|
|
fSimulationPool.Remove(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::IRemoveObject(plSceneObject* o)
|
|
|
|
{
|
|
|
|
int idx = fSceneObjects.Find(o);
|
|
|
|
if( idx != fSceneObjects.kMissingIndex )
|
|
|
|
fSceneObjects.Remove(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::IRemoveLight(plLightInfo* l)
|
|
|
|
{
|
|
|
|
hsAssert(l, "Removing nil light");
|
|
|
|
|
|
|
|
int idx = fLightPool.Find(l);
|
|
|
|
if( idx != fLightPool.kMissingIndex )
|
|
|
|
{
|
|
|
|
fLightPool.Remove(idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::IRemoveOccluder(plOccluder* o)
|
|
|
|
{
|
|
|
|
int idx = fOccluders.Find(o);
|
|
|
|
if( idx != fOccluders.kMissingIndex )
|
|
|
|
fOccluders.Remove(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSceneNode::IRemoveGeneric(hsKeyedObject* k)
|
|
|
|
{
|
|
|
|
int idx = fGenericPool.Find(k);
|
|
|
|
if( idx != fGenericPool.kMissingIndex )
|
|
|
|
fGenericPool.Remove(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plSceneNode::IOnRemove(plNodeRefMsg* refMsg)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch( refMsg->fType )
|
|
|
|
{
|
|
|
|
case plNodeRefMsg::kDrawable:
|
|
|
|
IRemoveDrawable(plDrawable::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
break;
|
|
|
|
case plNodeRefMsg::kPhysical:
|
|
|
|
IRemovePhysical(plPhysical::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
break;
|
|
|
|
case plNodeRefMsg::kAudible:
|
|
|
|
IRemoveAudible(plAudible::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
break;
|
|
|
|
case plNodeRefMsg::kObject:
|
|
|
|
IRemoveObject(plSceneObject::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
break;
|
|
|
|
case plNodeRefMsg::kLight:
|
|
|
|
IRemoveLight(plLightInfo::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
break;
|
|
|
|
case plNodeRefMsg::kOccluder:
|
|
|
|
IRemoveOccluder(plOccluder::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
break;
|
|
|
|
case plNodeRefMsg::kGeneric:
|
|
|
|
IRemoveGeneric(refMsg->GetRef());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if( refMsg->GetRef() && (refMsg->GetContext() & plRefMsg::kOnRemove) )
|
|
|
|
GetKey()->Release(refMsg->GetRef()->GetKey());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plSceneNode::IOnAdd(plNodeRefMsg* refMsg)
|
|
|
|
{
|
|
|
|
int which = refMsg->fWhich;
|
|
|
|
|
|
|
|
switch( refMsg->fType )
|
|
|
|
{
|
|
|
|
case plNodeRefMsg::kDrawable:
|
|
|
|
ISetDrawable(plDrawable::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
return true;
|
|
|
|
case plNodeRefMsg::kPhysical:
|
|
|
|
ISetPhysical(plPhysical::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
return true;
|
|
|
|
case plNodeRefMsg::kAudible:
|
|
|
|
ISetAudible(plAudible::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
return true;
|
|
|
|
case plNodeRefMsg::kObject:
|
|
|
|
ISetObject(plSceneObject::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
return true;
|
|
|
|
case plNodeRefMsg::kLight:
|
|
|
|
ISetLight(plLightInfo::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
return true;
|
|
|
|
case plNodeRefMsg::kOccluder:
|
|
|
|
ISetOccluder(plOccluder::ConvertNoRef(refMsg->GetRef()));
|
|
|
|
return true;
|
|
|
|
case plNodeRefMsg::kGeneric:
|
|
|
|
ISetGeneric(refMsg->GetRef());
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
hsBool plSceneNode::MsgReceive(plMessage* msg)
|
|
|
|
{
|
|
|
|
plNodeCleanupMsg *cleanMsg = plNodeCleanupMsg::ConvertNoRef( msg );
|
|
|
|
|
|
|
|
if( cleanMsg )
|
|
|
|
{
|
|
|
|
ICleanUp();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
plNodeRefMsg* refMsg = plNodeRefMsg::ConvertNoRef(msg);
|
|
|
|
|
|
|
|
if( refMsg )
|
|
|
|
{
|
|
|
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
|
|
|
|
return IOnAdd(refMsg);
|
|
|
|
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
|
|
|
|
return IOnRemove(refMsg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return hsKeyedObject::MsgReceive(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
//// ICleanUp ////////////////////////////////////////////////////////////////
|
|
|
|
// Export only: Clean up the scene node (i.e. make sure drawables optimize)
|
|
|
|
|
|
|
|
void plSceneNode::ICleanUp( void )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
/// Go find drawables to delete
|
|
|
|
for( i = 0; i < fDrawPool.GetCount(); i++ )
|
|
|
|
fDrawPool[ i ]->Optimize();
|
|
|
|
|
|
|
|
if (fFilterGenerics)
|
|
|
|
{
|
|
|
|
for( i = fSceneObjects.GetCount() - 1; i >= 0; i--)
|
|
|
|
GetKey()->Release(fSceneObjects[i]->GetKey());
|
|
|
|
for( i = fDrawPool.GetCount() - 1; i >= 0; i--)
|
|
|
|
GetKey()->Release(fDrawPool[i]->GetKey());
|
|
|
|
for( i = fSimulationPool.GetCount() - 1; i >= 0; i--)
|
|
|
|
GetKey()->Release(fSimulationPool[i]->GetKey());
|
|
|
|
for( i = fAudioPool.GetCount() - 1; i >= 0; i--)
|
|
|
|
GetKey()->Release(fAudioPool[i]->GetKey());
|
|
|
|
for( i = fOccluders.GetCount() - 1; i >= 0; i--)
|
|
|
|
GetKey()->Release(fOccluders[i]->GetKey());
|
|
|
|
for( i = fLightPool.GetCount() - 1; i >= 0; i--)
|
|
|
|
GetKey()->Release(fLightPool[i]->GetKey());
|
|
|
|
}
|
|
|
|
|
|
|
|
ITrashSpaceTree();
|
|
|
|
}
|
|
|
|
|
|
|
|
//// GetMatchingDrawable /////////////////////////////////////////////////////
|
|
|
|
// Export only: Query for a given drawable.
|
|
|
|
|
|
|
|
plDrawable *plSceneNode::GetMatchingDrawable( const plDrawableCriteria& crit )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < fDrawPool.GetCount(); i++ )
|
|
|
|
{
|
|
|
|
if( fDrawPool[ i ]->DoIMatch( crit ) )
|
|
|
|
return fDrawPool[ i ];
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// OptimizeDrawables ///////////////////////////////////////////////////////
|
|
|
|
// Loops through all the drawables and calls Optimize on each one. For the
|
|
|
|
// export side, to be called right before writing the drawables to disk.
|
|
|
|
|
|
|
|
void plSceneNode::OptimizeDrawables( void )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < fDrawPool.GetCount(); i++ )
|
|
|
|
fDrawPool[ i ]->Optimize();
|
|
|
|
}
|
|
|
|
|