6e8fbca745
match the genesis editor version 1.3.0.653.
335 lines
9.9 KiB
C++
335 lines
9.9 KiB
C++
/****************************************************************************
|
||
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
|