genesis-3d_engine/Engine/addons/vis/vissystems/viscell.cc

335 lines
9.9 KiB
C++
Raw Normal View History

/****************************************************************************
Copyright (c) 2010,Radon Labs GmbH
Copyright (c) 2011-2013,WebJet Business Division,CYOU
http://www.genesis-3d.com.cn
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "stdneb.h"
#include "vis/vissystems/viscell.h"
namespace Vis
{
__ImplementClass(Vis::VisCell, 'VICL', Core::RefCounted);
using namespace Math;
using namespace Util;
//------------------------------------------------------------------------------
/**
*/
VisCell::VisCell()
:mNumEntitiesInHierarchy(0)
{
}
//------------------------------------------------------------------------------
/**
*/
VisCell::~VisCell()
{
// make sure we've been properly cleaned up
n_assert(!this->mParentCell.isvalid());
n_assert(this->mChildCells.IsEmpty());
n_assert(this->mEntities.empty());
}
//------------------------------------------------------------------------------
/**
*/
void
VisCell::OnAttach()
{
n_assert(this->mEntities.empty());
mNumEntitiesInHierarchy = 0;
// recurse into child cells
IndexT i;
for (i = 0; i < this->mChildCells.Size(); i++)
{
this->mChildCells[i]->OnAttach();
}
}
//------------------------------------------------------------------------------
/**
*/
void
VisCell::OnRemove()
{
// first recurse to child cells
IndexT i;
for (i = 0; i < this->mChildCells.Size(); i++)
{
this->mChildCells[i]->OnRemove();
}
mNumEntitiesInHierarchy = 0;
// cleanup
this->mParentCell = 0;
this->mChildCells.Clear();
this->mEntities.clear();
}
//------------------------------------------------------------------------------
/**
NOTE: the cell hierarchy may only be built during the setup phase while
the cell hierarchy haven't been added to the stage yet.
*/
void
VisCell::AttachChildCell(const GPtr<VisCell>& cell)
{
n_assert(cell.isvalid())
n_assert(!cell->GetParentCell().isvalid());
cell->mParentCell = this;
this->mChildCells.Append(cell);
}
//------------------------------------------------------------------------------
/**
Attach an context to this VisCell. This will happen when a visEntity
moves through the world, leaving and entering cells as necessary.
*/
void
VisCell::AttachEntity(const GPtr<VisEntity>& ent)
{
n_assert(ent.isvalid());
this->mEntities.push_back(ent);
UpdateNumEntitiesInHierarchy(+1);
}
//------------------------------------------------------------------------------
/**
*/
void
VisCell::RemoveEntity(const GPtr<VisEntity>& ent)
{
n_assert(ent.isvalid());
this->mEntities.remove(ent );
UpdateNumEntitiesInHierarchy(-1);
}
//------------------------------------------------------------------------------
/**
Starting from this cell, try to find the smallest cell which completely
contains the given entity:
- starting from initial cell:
- if the entity does not fit into the cell, move up the
tree until the first cell is found which the entity completely fits into
- if the entity fits into a cell, check each child cell if the
entity fits completely into the cell
The entity will not be attached! If the entity does not fit into the
root cell, the root cell will be returned, not 0.
@param entity pointer of entity to find new cell for
@return cell which completely encloses the entity (the root cell is an exception)
*/
VisCell*
VisCell::FindEntityContainmentCell(const GPtr<VisEntity>& entity)
{
// get global bounding box of entity
const bbox& entityBox = entity->GetBoundingBox();
// find the first upward cell which completely contains the entity,
// stop at tree root
GPtr<VisCell> curCell = this;
while ( (curCell->GetParentCell().isvalid()) && (!curCell->GetBoundingBox().contains(entityBox)) )
{
curCell = curCell->GetParentCell();
}
// find smallest downward cell which completely contains the entity
IndexT cellIndex;
SizeT numCells;
do
{
const Array<GPtr<VisCell> >& curChildren = curCell->GetChildCells();
numCells = curChildren.Size();
for (cellIndex = 0; cellIndex < numCells; cellIndex++)
{
const GPtr<VisCell>& childCell = curChildren[cellIndex];
if (childCell->GetBoundingBox().contains(entityBox))
{
curCell = childCell;
break;
}
}
// check for loop fallthrough: this means that the current cell either has
// no children, or that none of the children completely contains the entity
}
while (cellIndex != numCells);
return curCell.get_unsafe();
}
//------------------------------------------------------------------------------
/**
Insert a dynamic graphics entity into the cell tree. The entity
will correctly be inserted into the smallest enclosing cell in the tree.
The cell may not be currently attached to a cell, the refcount of the
entity will be incremented.
@param entity pointer to a graphics entity
*/
VisCell*
VisCell::InsertEntity(const GPtr<VisEntity>& entity)
{
VisCell* cell = this->FindEntityContainmentCell(entity);
cell->AttachEntity(entity);
return cell;
}
//------------------------------------------------------------------------------
/**
Recursively update visibility links. This method is called by the
top level method CollectVisibleContexts().
NOTE: This is the core visibility detection method and must be FAST.
*/
void
VisCell::RecurseCollectVisibleEntities(const GPtr<ObserverContext>& observerContext,
Util::Array<GPtr<VisEntity> >& visibilityEntities,
Math::ClipStatus::Type clipStatus) const
{
n_assert(observerContext.isvalid());
// break immediately if no context of wanted type in this cell or below
if (this->mNumEntitiesInHierarchy == 0)
{
return;
}
// if clip status unknown or clipped, get clip status of this cell against observer context
if ((ClipStatus::Invalid == clipStatus) || (ClipStatus::Clipped == clipStatus))
{
const bbox& cellBox = this->GetBoundingBox();
clipStatus = observerContext->ComputeClipStatus(cellBox);
}
/// BUGFIX: Cell<6C>İ<EFBFBD>Χ<EFBFBD><CEA7><EFBFBD>ǹ̶<C7B9><CCB6>ģ<EFBFBD>û<EFBFBD>и<EFBFBD><D0B8>ݰ<EFBFBD><DDB0><EFBFBD><EFBFBD><EFBFBD>Entity<74><79>С<EFBFBD><D0A1><EFBFBD><EFBFBD>չ<EFBFBD><D5B9><EFBFBD><EFBFBD>Ϊ<EFBFBD><CEAA><EFBFBD>ܰ<EFBFBD><DCB0><EFBFBD><EFBFBD><EFBFBD>Entity<74><79><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>
/// <20><><EFBFBD><EFBFBD><EFBFBD>ڸ<EFBFBD><DAB8>ڵ㣬<DAB5><E3A3AC><EFBFBD><EFBFBD>Χ<EFBFBD>е<EFBFBD><D0B5>ڳ<EFBFBD>ʼ<EFBFBD><CABC>ʱ<EFBFBD><CAB1><EFBFBD>ü<EFBFBD><C3BC><EFBFBD><EFBFBD>Ĵ<EFBFBD>С<EFBFBD><D0A1><EFBFBD><EFBFBD><EFBFBD>ڲü<DAB2><C3BC><EFBFBD><EFBFBD><EFBFBD>Χ<EFBFBD><CEA7><EFBFBD><EFBFBD>Entity<74><79><EFBFBD><EFBFBD><EFBFBD>ܻᱻ<DCBB><E1B1BB><EFBFBD><EFBFBD><EFBFBD>ü<EFBFBD>
if ( mParentCell )
{
// proceed depending on clip status of cell against observer context
if (ClipStatus::Outside == clipStatus)
{
// cell isn't visible by observer context
return;
}
else if (ClipStatus::Inside == clipStatus)
{
EntityList::const_iterator itor = this->mEntities.begin();
EntityList::const_iterator end = this->mEntities.end();
while( itor != end )
{
visibilityEntities.Append( *itor );
++itor;
}
}
else
{
EntityList::const_iterator itor = this->mEntities.begin();
EntityList::const_iterator end = this->mEntities.end();
while( itor != end )
{
if ( observerContext->ComputeClipStatus( (*itor)->GetBoundingBox() ) != ClipStatus::Outside )
{
visibilityEntities.Append(*itor);
}
++itor;
}
}
}
else
{
// ֱ<>Ӳü<D3B2><C3BC><EFBFBD><EFBFBD>е<EFBFBD>Entity
EntityList::const_iterator itor = this->mEntities.begin();
EntityList::const_iterator end = this->mEntities.end();
while( itor != end )
{
if ( observerContext->ComputeClipStatus( (*itor)->GetBoundingBox() ) != ClipStatus::Outside )
{
visibilityEntities.Append(*itor);
}
++itor;
}
}
// recurse into child cells (if this cell is fully or partially visible)
IndexT childIndex;
SizeT numChildren = this->mChildCells.Size();
for (childIndex = 0; childIndex < numChildren; ++childIndex)
{
this->mChildCells[childIndex]->RecurseCollectVisibleEntities(observerContext, visibilityEntities, clipStatus);
}
}
//------------------------------------------------------------------------------
/**
Frontend method for updating visibility links. This method
simply calls RecurseCollectVisibleContexts() which recurses into child
cells if necessary.
*/
void
VisCell::QueryVisibleEntities(const GPtr<ObserverContext>& observerContext, Util::Array<GPtr<VisEntity> >& visibilityEntities ) const
{
this->RecurseCollectVisibleEntities(observerContext, visibilityEntities, ClipStatus::Invalid);
}
//------------------------------------------------------------------------------
/**
Update the number of entities in hierarchy. Must be called when
entities are added or removed from this cell.
*/
void
VisCell::UpdateNumEntitiesInHierarchy( int num)
{
this->mNumEntitiesInHierarchy += num;
n_assert(this->mNumEntitiesInHierarchy >= 0);
VisCell* p = this->mParentCell.get_unsafe();
if (p)
{
do
{
p->mNumEntitiesInHierarchy += num;
n_assert(p->mNumEntitiesInHierarchy >= 0);
}
while (0 != (p = p->mParentCell.get_unsafe()));
}
}
} // namespace Vis