mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
834 lines
22 KiB
C++
834 lines
22 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Copyright (c) 2012 GarageGames, LLC
|
|
//
|
|
// 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 "platform/platform.h"
|
|
#include "forest/forestDataFile.h"
|
|
|
|
#include "forest/forest.h"
|
|
#include "forest/forestCell.h"
|
|
#include "T3D/physics/physicsBody.h"
|
|
#include "core/stream/fileStream.h"
|
|
#include "core/resource.h"
|
|
#include "math/mathIO.h"
|
|
#include "math/mPoint2.h"
|
|
#include "platform/profiler.h"
|
|
|
|
|
|
template<> ResourceBase::Signature Resource<ForestData>::signature()
|
|
{
|
|
return MakeFourCC('f','k','d','f');
|
|
}
|
|
|
|
template<>
|
|
void* Resource<ForestData>::create( const Torque::Path &path )
|
|
{
|
|
FileStream stream;
|
|
stream.open( path.getFullPath(), Torque::FS::File::Read );
|
|
if ( stream.getStatus() != Stream::Ok )
|
|
return NULL;
|
|
|
|
ForestData *file = new ForestData();
|
|
if ( !file->read( stream ) )
|
|
{
|
|
delete file;
|
|
return NULL;
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
|
|
U32 ForestData::smNextItemId = 1;
|
|
|
|
ForestData::ForestData()
|
|
: mIsDirty( false )
|
|
{
|
|
ForestItemData::getReloadSignal().notify( this, &ForestData::_onItemReload );
|
|
}
|
|
|
|
ForestData::~ForestData()
|
|
{
|
|
ForestItemData::getReloadSignal().remove( this, &ForestData::_onItemReload );
|
|
clear();
|
|
}
|
|
|
|
void ForestData::clear()
|
|
{
|
|
// We only have to delete the top level cells and ForestCell will
|
|
// clean up its sub-cells in its destructor.
|
|
|
|
BucketTable::Iterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ ) delete iter->value;
|
|
mBuckets.clear();
|
|
|
|
mIsDirty = true;
|
|
}
|
|
|
|
bool ForestData::read( Stream &stream )
|
|
{
|
|
// Read our identifier... so we know we're
|
|
// not reading in pure garbage.
|
|
char id[4] = { 0 };
|
|
stream.read( 4, id );
|
|
if ( dMemcmp( id, "FKDF", 4 ) != 0 )
|
|
{
|
|
Con::errorf( "ForestDataFile::read() - This is not a Forest planting file!" );
|
|
return false;
|
|
}
|
|
|
|
// Empty ourselves before we really begin reading.
|
|
clear();
|
|
|
|
// Now the version number.
|
|
U8 version;
|
|
stream.read( &version );
|
|
if ( version > (U8)FILE_VERSION )
|
|
{
|
|
Con::errorf( "ForestDataFile::read() - This file was created with an newer version of Forest!" );
|
|
return false;
|
|
}
|
|
|
|
// Read in the names of the ForestItemData datablocks
|
|
// and recover the datablock.
|
|
Vector<ForestItemData*> allDatablocks;
|
|
U32 count;
|
|
stream.read( &count );
|
|
allDatablocks.setSize( count );
|
|
for ( U32 i=0; i < count; i++ )
|
|
{
|
|
StringTableEntry name = stream.readSTString();
|
|
ForestItemData* data = ForestItemData::find( name );
|
|
|
|
// TODO: Change this to instead create a dummy forest data
|
|
// for each so that the user can swap it with the right one.
|
|
if ( data == NULL )
|
|
{
|
|
Con::warnf( "ForestData::read - ForestItemData named %s was not found.", name );
|
|
Con::warnf( "Note this can occur if you have deleted or renamed datablocks prior to loading this forest and is not an 'error' in this scenario." );
|
|
}
|
|
|
|
allDatablocks[ i ] = data;
|
|
}
|
|
|
|
U8 dataIndex;
|
|
Point3F pos;
|
|
QuatF rot;
|
|
F32 scale;
|
|
ForestItemData* data;
|
|
MatrixF xfm;
|
|
|
|
U32 skippedItems = 0;
|
|
|
|
// Read in the items.
|
|
stream.read( &count );
|
|
for ( U32 i=0; i < count; i++ )
|
|
{
|
|
stream.read( &dataIndex );
|
|
mathRead( stream, &pos );
|
|
mathRead( stream, &rot );
|
|
stream.read( &scale );
|
|
|
|
data = allDatablocks[ dataIndex ];
|
|
if ( data )
|
|
{
|
|
rot.setMatrix( &xfm );
|
|
xfm.setPosition( pos );
|
|
|
|
addItem( smNextItemId++, data, xfm, scale );
|
|
}
|
|
else
|
|
{
|
|
skippedItems++;
|
|
}
|
|
}
|
|
|
|
if ( skippedItems > 0 )
|
|
Con::warnf( "ForestData::read - %i items were skipped because their datablocks were not found.", skippedItems );
|
|
|
|
// Clear the dirty flag.
|
|
mIsDirty = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ForestData::write( const char *path )
|
|
{
|
|
// Open the stream.
|
|
FileStream stream;
|
|
if ( !stream.open( path, Torque::FS::File::Write ) )
|
|
{
|
|
Con::errorf( "ForestDataFile::write() - Failed opening stream!" );
|
|
return false;
|
|
}
|
|
|
|
// Write our identifier... so we have a better
|
|
// idea if we're reading pure garbage.
|
|
stream.write( 4, "FKDF" );
|
|
|
|
// Now the version number.
|
|
stream.write( (U8)FILE_VERSION );
|
|
|
|
// First gather all the ForestItemData datablocks
|
|
// used by the items in the forest.
|
|
Vector<ForestItemData*> allDatablocks;
|
|
getDatablocks( &allDatablocks );
|
|
|
|
// Write out the datablock list.
|
|
U32 count = allDatablocks.size();
|
|
stream.write( count );
|
|
for ( U32 i=0; i < count; i++ )
|
|
{
|
|
StringTableEntry localName = allDatablocks[i]->getInternalName();
|
|
AssertFatal( localName != NULL && localName[0] != '\0', "ForestData::write - ForestItemData had no internal name set!" );
|
|
stream.writeString( allDatablocks[i]->getInternalName() );
|
|
}
|
|
|
|
// Get a copy of all the items.
|
|
Vector<ForestItem> items;
|
|
getItems( &items );
|
|
|
|
// Save the item count.
|
|
stream.write( (U32)items.size() );
|
|
|
|
// Save the items.
|
|
Vector<ForestItem>::const_iterator iter = items.begin();
|
|
for ( ; iter != items.end(); iter++ )
|
|
{
|
|
U8 dataIndex = find( allDatablocks.begin(), allDatablocks.end(), iter->getData() ) - allDatablocks.begin();
|
|
|
|
stream.write( dataIndex );
|
|
|
|
mathWrite( stream, iter->getPosition() );
|
|
|
|
QuatF quat;
|
|
quat.set( iter->getTransform() );
|
|
mathWrite( stream, quat );
|
|
|
|
stream.write( iter->getScale() );
|
|
}
|
|
|
|
// Clear the dirty flag.
|
|
mIsDirty = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ForestData::regenCells()
|
|
{
|
|
Vector<ForestItem> items;
|
|
getItems( &items );
|
|
|
|
clear();
|
|
|
|
for ( U32 i=0; i < items.size(); i++ )
|
|
{
|
|
const ForestItem &item = items[i];
|
|
addItem( item.getKey(), item.getData(), item.getTransform(), item.getScale() );
|
|
}
|
|
|
|
mIsDirty = true;
|
|
}
|
|
|
|
ForestCell* ForestData::_findBucket( const Point2I &key ) const
|
|
{
|
|
BucketTable::ConstIterator iter = mBuckets.find( key );
|
|
|
|
if ( iter != mBuckets.end() )
|
|
return iter->value;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
ForestCell* ForestData::_findOrCreateBucket( const Point3F &pos )
|
|
{
|
|
// Look it up.
|
|
const Point2I key = _getBucketKey( pos );
|
|
BucketTable::Iterator iter = mBuckets.find( key );
|
|
|
|
ForestCell *bucket = NULL;
|
|
if ( iter != mBuckets.end() )
|
|
bucket = iter->value;
|
|
else
|
|
{
|
|
bucket = new ForestCell( RectF( key.x, key.y, BUCKET_DIM, BUCKET_DIM ) );
|
|
mBuckets.insertUnique( key, bucket );
|
|
mIsDirty = true;
|
|
}
|
|
|
|
return bucket;
|
|
}
|
|
|
|
void ForestData::_onItemReload()
|
|
{
|
|
// Invalidate cell batches and bounds so they
|
|
// will be regenerated next render.
|
|
|
|
Vector<ForestCell*> stack;
|
|
getCells( &stack );
|
|
|
|
ForestCell *pCell;
|
|
|
|
while ( !stack.empty() )
|
|
{
|
|
pCell = stack.last();
|
|
stack.pop_back();
|
|
|
|
if ( !pCell )
|
|
continue;
|
|
|
|
pCell->freeBatches();
|
|
pCell->invalidateBounds();
|
|
|
|
pCell->getChildren( &stack );
|
|
}
|
|
}
|
|
|
|
const ForestItem& ForestData::addItem( ForestItemData *data,
|
|
const Point3F &position,
|
|
F32 rotation,
|
|
F32 scale )
|
|
{
|
|
MatrixF xfm;
|
|
xfm.set( EulerF( 0, 0, rotation ), position );
|
|
|
|
return addItem( smNextItemId++,
|
|
data,
|
|
xfm,
|
|
scale );
|
|
}
|
|
|
|
const ForestItem& ForestData::addItem( ForestItemKey key,
|
|
ForestItemData *data,
|
|
const MatrixF &xfm,
|
|
F32 scale )
|
|
{
|
|
ForestCell *bucket = _findOrCreateBucket( xfm.getPosition() );
|
|
|
|
mIsDirty = true;
|
|
|
|
return bucket->insertItem( key, data, xfm, scale );
|
|
}
|
|
|
|
const ForestItem& ForestData::updateItem( ForestItemKey key,
|
|
const Point3F &keyPosition,
|
|
ForestItemData *newData,
|
|
const MatrixF &newXfm,
|
|
F32 newScale )
|
|
{
|
|
Point2I bucketKey = _getBucketKey( keyPosition );
|
|
|
|
ForestCell *bucket = _findBucket( bucketKey );
|
|
|
|
if ( !bucket || !bucket->removeItem( key, keyPosition, true ) )
|
|
return ForestItem::Invalid;
|
|
|
|
if ( bucket->isEmpty() )
|
|
{
|
|
delete bucket;
|
|
mBuckets.erase( bucketKey );
|
|
}
|
|
|
|
return addItem( key, newData, newXfm, newScale );
|
|
}
|
|
|
|
const ForestItem& ForestData::updateItem( ForestItem &item )
|
|
{
|
|
return updateItem( item.getKey(),
|
|
item.getPosition(),
|
|
item.getData(),
|
|
item.getTransform(),
|
|
item.getScale() );
|
|
}
|
|
|
|
bool ForestData::removeItem( ForestItemKey key, const Point3F &keyPosition )
|
|
{
|
|
Point2I bucketkey = _getBucketKey( keyPosition );
|
|
|
|
ForestCell *bucket = _findBucket( keyPosition );
|
|
|
|
if ( !bucket || !bucket->removeItem( key, keyPosition, true ) )
|
|
return false;
|
|
|
|
if ( bucket->isEmpty() )
|
|
{
|
|
delete bucket;
|
|
mBuckets.erase( bucketkey );
|
|
}
|
|
|
|
mIsDirty = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
const ForestItem& ForestData::findItem( ForestItemKey key, const Point3F &keyPos ) const
|
|
{
|
|
PROFILE_SCOPE( ForestData_findItem );
|
|
|
|
AssertFatal( key != 0, "ForestCell::findItem() - Got null key!" );
|
|
|
|
ForestCell *cell = _findBucket( keyPos );
|
|
|
|
while ( cell && !cell->isLeaf() )
|
|
cell = cell->getChildAt( keyPos );
|
|
|
|
U32 index;
|
|
if ( cell && cell->findIndexByKey( key, &index ) )
|
|
return cell->getItems()[ index ];
|
|
|
|
return ForestItem::Invalid;
|
|
}
|
|
|
|
const ForestItem& ForestData::findItem( ForestItemKey key ) const
|
|
{
|
|
PROFILE_SCOPE( ForestData_findItem_Slow );
|
|
|
|
AssertFatal( key != 0, "ForestData::findItem() - Got null key!" );
|
|
|
|
// Do an exhaustive search thru all the cells... this
|
|
// is really crappy... we shouldn't do this regularly.
|
|
|
|
Vector<const ForestCell*> stack;
|
|
BucketTable::ConstIterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
stack.push_back( iter->value );
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
const ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( !cell->isLeaf() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
// Finally search for the item.
|
|
U32 index;
|
|
if ( cell->findIndexByKey( key, &index ) )
|
|
return cell->getItems()[ index ];
|
|
}
|
|
|
|
return ForestItem::Invalid;
|
|
}
|
|
|
|
U32 ForestData::getItems( Vector<ForestItem> *outItems ) const
|
|
{
|
|
AssertFatal( outItems, "ForestData::getItems() - The output vector was NULL!" );
|
|
|
|
PROFILE_SCOPE( ForestData_getItems );
|
|
|
|
Vector<const ForestCell*> stack;
|
|
U32 count = 0;
|
|
|
|
BucketTable::ConstIterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
stack.push_back( iter->value );
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
const ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( !cell->isLeaf() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
// Get the items.
|
|
count += cell->getItems().size();
|
|
outItems->merge( cell->getItems() );
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
U32 ForestData::getItems( const Frustum &culler, Vector<ForestItem> *outItems ) const
|
|
{
|
|
AssertFatal( outItems, "ForestData::getItems() - The output vector was NULL!" );
|
|
|
|
PROFILE_SCOPE( ForestData_getItems_ByFrustum );
|
|
|
|
Vector<ForestCell*> stack;
|
|
getCells( &stack );
|
|
Vector<ForestItem>::const_iterator iter;
|
|
U32 count = 0;
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
const ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
if ( culler.isCulled( cell->getBounds() ) )
|
|
continue;
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( cell->isBranch() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
// Get the items.
|
|
iter = cell->getItems().begin();
|
|
for ( ; iter != cell->getItems().end(); iter++ )
|
|
{
|
|
if ( !culler.isCulled( iter->getWorldBox() ) )
|
|
{
|
|
outItems->merge( cell->getItems() );
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
U32 ForestData::getItems( const Box3F &box, Vector<ForestItem> *outItems ) const
|
|
{
|
|
PROFILE_SCOPE( ForestData_getItems_ByBox );
|
|
|
|
Vector<const ForestCell*> stack;
|
|
U32 count = 0;
|
|
|
|
BucketTable::ConstIterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
stack.push_back( iter->value );
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
const ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
// If the cell is empty or doesn't overlap the box... skip it.
|
|
if ( cell->isEmpty() ||
|
|
!cell->getBounds().isOverlapped( box ) )
|
|
continue;
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( !cell->isLeaf() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
// Finally look thru the items.
|
|
const Vector<ForestItem> &items = cell->getItems();
|
|
Vector<ForestItem>::const_iterator item = items.begin();
|
|
for ( ; item != items.end(); item++ )
|
|
{
|
|
if ( item->getWorldBox().isOverlapped( box ) )
|
|
{
|
|
// If we don't have an output vector then the user just
|
|
// wanted to know if any object existed... so early out.
|
|
if ( !outItems )
|
|
return 1;
|
|
|
|
++count;
|
|
outItems->push_back( *item );
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
U32 ForestData::getItems( const Point3F &point, F32 radius, Vector<ForestItem> *outItems ) const
|
|
{
|
|
PROFILE_SCOPE( ForestData_getItems_BySphere );
|
|
|
|
Vector<const ForestCell*> stack;
|
|
U32 count = 0;
|
|
|
|
BucketTable::ConstIterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
stack.push_back( iter->value );
|
|
|
|
const F32 radiusSq = radius * radius;
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
const ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
// TODO: If we could know here that the cell is fully within
|
|
// the sphere... we could do a fast gather of all its elements
|
|
// without any further testing of it or its children.
|
|
|
|
// If the cell is empty or doesn't overlap the sphere... skip it.
|
|
if ( cell->isEmpty() ||
|
|
cell->getBounds().getSqDistanceToPoint( point ) > radiusSq )
|
|
continue;
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( !cell->isLeaf() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
// Finally look thru the items.
|
|
const Vector<ForestItem> &items = cell->getItems();
|
|
Vector<ForestItem>::const_iterator item = items.begin();
|
|
for ( ; item != items.end(); item++ )
|
|
{
|
|
if ( item->getWorldBox().getSqDistanceToPoint( point ) < radiusSq )
|
|
{
|
|
// If we don't have an output vector then the user just
|
|
// wanted to know if any object existed... so early out.
|
|
if ( !outItems )
|
|
return 1;
|
|
|
|
++count;
|
|
outItems->push_back( *item );
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
U32 ForestData::getItems( const Point2F &point, F32 radius, Vector<ForestItem> *outItems ) const
|
|
{
|
|
PROFILE_SCOPE( ForestData_getItems_ByCircle );
|
|
|
|
Vector<const ForestCell*> stack;
|
|
U32 count = 0;
|
|
|
|
BucketTable::ConstIterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
stack.push_back( iter->value );
|
|
|
|
const F32 radiusSq = radius * radius;
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
const ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
// If the cell is empty or doesn't overlap the sphere... skip it.
|
|
if ( cell->isEmpty() ||
|
|
cell->getRect().getSqDistanceToPoint( point ) > radiusSq )
|
|
continue;
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( !cell->isLeaf() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
// Finally look thru the items.
|
|
const Vector<ForestItem> &items = cell->getItems();
|
|
Vector<ForestItem>::const_iterator item = items.begin();
|
|
F32 compareDist;
|
|
for ( ; item != items.end(); item++ )
|
|
{
|
|
compareDist = mSquared( radius + item->getData()->mRadius );
|
|
if ( item->getSqDistanceToPoint( point ) < compareDist )
|
|
{
|
|
// If we don't have an output vector then the user just
|
|
// wanted to know if any object existed... so early out.
|
|
if ( !outItems )
|
|
return 1;
|
|
|
|
++count;
|
|
outItems->push_back( *item );
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
U32 ForestData::getItems( const ForestItemData *data, Vector<ForestItem> *outItems ) const
|
|
{
|
|
AssertFatal( outItems, "ForestData::getItems() - The output vector was NULL!" );
|
|
|
|
PROFILE_SCOPE( ForestData_getItems_ByDatablock );
|
|
|
|
Vector<const ForestCell*> stack;
|
|
U32 count = 0;
|
|
|
|
BucketTable::ConstIterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
stack.push_back( iter->value );
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
const ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( !cell->isLeaf() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
// Get the items.
|
|
const Vector<ForestItem> &items = cell->getItems();
|
|
Vector<ForestItem>::const_iterator item = items.begin();
|
|
for ( ; item != items.end(); item++ )
|
|
{
|
|
if ( item->getData() == data )
|
|
{
|
|
++count;
|
|
outItems->push_back( *item );
|
|
}
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void ForestData::getCells( const Frustum &frustum, Vector<ForestCell*> *outCells ) const
|
|
{
|
|
PROFILE_SCOPE( ForestData_getCells_frustum );
|
|
|
|
BucketTable::ConstIterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
{
|
|
if ( !frustum.isCulled( iter->value->getBounds() ) )
|
|
outCells->push_back( iter->value );
|
|
}
|
|
}
|
|
|
|
void ForestData::getCells( Vector<ForestCell*> *outCells ) const
|
|
{
|
|
PROFILE_SCOPE( ForestData_getCells_nofrustum );
|
|
|
|
BucketTable::ConstIterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
outCells->push_back( iter->value );
|
|
}
|
|
|
|
U32 ForestData::getDatablocks( Vector<ForestItemData*> *outVector ) const
|
|
{
|
|
Vector<const ForestCell*> stack;
|
|
U32 count = 0;
|
|
|
|
BucketTable::ConstIterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
stack.push_back( iter->value );
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
const ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( !cell->isLeaf() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
// Go thru the items.
|
|
const Vector<ForestItem> &items = cell->getItems();
|
|
Vector<ForestItem>::const_iterator item = items.begin();
|
|
for ( ; item != items.end(); item++ )
|
|
{
|
|
ForestItemData *data = item->getData();
|
|
|
|
if ( find( outVector->begin(), outVector->end(), data ) != outVector->end() )
|
|
continue;
|
|
|
|
count++;
|
|
outVector->push_back( data );
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void ForestData::clearPhysicsRep( Forest *forest )
|
|
{
|
|
Vector<ForestCell*> stack;
|
|
|
|
BucketTable::Iterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
stack.push_back( iter->value );
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( !cell->isLeaf() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
cell->clearPhysicsRep( forest );
|
|
}
|
|
}
|
|
|
|
void ForestData::buildPhysicsRep( Forest *forest )
|
|
{
|
|
Vector<ForestCell*> stack;
|
|
|
|
BucketTable::Iterator iter = mBuckets.begin();
|
|
for ( ; iter != mBuckets.end(); iter++ )
|
|
stack.push_back( iter->value );
|
|
|
|
// Now loop till we run out of cells.
|
|
while ( !stack.empty() )
|
|
{
|
|
// Pop off the next cell.
|
|
ForestCell *cell = stack.last();
|
|
stack.pop_back();
|
|
|
|
// Recurse thru non-leaf cells.
|
|
if ( !cell->isLeaf() )
|
|
{
|
|
cell->getChildren( &stack );
|
|
continue;
|
|
}
|
|
|
|
cell->buildPhysicsRep( forest );
|
|
}
|
|
} |