/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsTypes.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 n;
int i;
n = s->ReadSwap32();
fSceneObjects.Reset();
for( i = 0; i < n; i++ )
{
plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(GetKey(), plRefMsg::kOnCreate, i, plNodeRefMsg::kObject);
plKey key = mgr->ReadKeyNotifyMe(s, refMsg, plRefFlags::kActiveRef);
}
n = s->ReadSwap32();
fGenericPool.Reset();
for( i = 0; i < n; i++ )
{
plNodeRefMsg* refMsg = TRACKED_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->WriteSwap32(fSceneObjects.GetCount());
for( i = 0; i < fSceneObjects.GetCount(); i++ )
mgr->WriteKey(s,fSceneObjects[i]);
s->WriteSwap32(fGenericPool.GetCount());
for( i = 0; i < fGenericPool.GetCount(); i++ )
mgr->WriteKey(s, fGenericPool[i]);
}
void plSceneNode::Harvest(plVolumeIsect* isect, hsTArray& levList)
{
static hsTArray visList;
visList.SetCount(0);
GetSpaceTree()->HarvestLeaves(isect, visList);
static hsTArray 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& levList, plVisMgr* visMgr)
{
static hsTArray visList;
visList.SetCount(0);
pipe->HarvestVisible(GetSpaceTree(), visList);
static hsTArray 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;
bnd.Reset(&hsPoint3(0,0,0));
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();
}