Torque3D Documentation / _generateds / forestDataFile.cpp

forestDataFile.cpp

Engine/source/forest/forestDataFile.cpp

More...

Detailed Description

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platform/platform.h"
 25#include "forest/forestDataFile.h"
 26
 27#include "forest/forest.h"
 28#include "forest/forestCell.h"
 29#include "T3D/physics/physicsBody.h"
 30#include "core/stream/fileStream.h"
 31#include "core/resource.h"
 32#include "math/mathIO.h"
 33#include "math/mPoint2.h"
 34#include "platform/profiler.h"
 35
 36
 37template<> ResourceBase::Signature Resource<ForestData>::signature()
 38{
 39   return MakeFourCC('f','k','d','f');
 40}
 41
 42template<>
 43void* Resource<ForestData>::create( const Torque::Path &path )
 44{
 45   FileStream stream;
 46   stream.open( path.getFullPath(), Torque::FS::File::Read );
 47   if ( stream.getStatus() != Stream::Ok )
 48      return NULL;
 49
 50   ForestData *file = new ForestData();
 51   if ( !file->read( stream ) )
 52   {
 53      delete file;
 54      return NULL;
 55   }
 56
 57   return file;
 58}
 59
 60
 61U32 ForestData::smNextItemId = 1;
 62
 63ForestData::ForestData()
 64   :  mIsDirty( false )
 65{
 66   ForestItemData::getReloadSignal().notify( this, &ForestData::_onItemReload );
 67}
 68
 69ForestData::~ForestData()
 70{
 71   ForestItemData::getReloadSignal().remove( this, &ForestData::_onItemReload );
 72   clear();
 73}
 74
 75void ForestData::clear()
 76{
 77   // We only have to delete the top level cells and ForestCell will
 78   // clean up its sub-cells in its destructor.   
 79
 80   BucketTable::Iterator iter = mBuckets.begin();
 81   for (; iter != mBuckets.end(); ++iter) delete iter->value;
 82   mBuckets.clear();
 83
 84   mIsDirty = true;
 85}
 86
 87bool ForestData::read( Stream &stream )
 88{
 89   // Read our identifier... so we know we're 
 90   // not reading in pure garbage.
 91   char id[4] = { 0 };
 92   stream.read( 4, id );
 93   if ( dMemcmp( id, "FKDF", 4 ) != 0 )
 94   {
 95      Con::errorf( "ForestDataFile::read() - This is not a Forest planting file!" );
 96      return false;
 97   }
 98
 99   // Empty ourselves before we really begin reading.
100   clear();
101
102   // Now the version number.
103   U8 version;
104   stream.read( &version );
105   if ( version > (U8)FILE_VERSION )
106   {
107      Con::errorf( "ForestDataFile::read() - This file was created with an newer version of Forest!" );
108      return false;
109   }
110
111   // Read in the names of the ForestItemData datablocks
112   // and recover the datablock.
113   Vector<ForestItemData*> allDatablocks;
114   U32 count;
115   stream.read( &count );
116   allDatablocks.setSize( count );
117   for ( U32 i=0; i < count; i++ )
118   {
119      StringTableEntry name = stream.readSTString();
120      ForestItemData* data = ForestItemData::find( name );
121      
122      // TODO: Change this to instead create a dummy forest data
123      // for each so that the user can swap it with the right one.
124      if ( data == NULL )
125      {
126         Con::warnf( "ForestData::read - ForestItemData named %s was not found.", name );
127         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." );
128      }
129      
130      allDatablocks[ i ] = data;
131   }
132
133   U8 dataIndex;
134   Point3F pos;
135   QuatF rot;
136   F32 scale;
137   ForestItemData* data;
138   MatrixF xfm;
139
140   U32 skippedItems = 0;
141
142   // Read in the items.
143   stream.read( &count );
144   for ( U32 i=0; i < count; i++ )
145   {
146      stream.read( &dataIndex );
147      mathRead( stream, &pos );
148      mathRead( stream, &rot );
149      stream.read( &scale );
150
151      data = allDatablocks[ dataIndex ];
152      if ( data )
153      {
154         rot.setMatrix( &xfm );
155         xfm.setPosition( pos );
156
157         addItem( smNextItemId++, data, xfm, scale );
158      }
159      else
160      {
161         skippedItems++;
162      }
163   }
164
165   if ( skippedItems > 0 )
166      Con::warnf( "ForestData::read - %i items were skipped because their datablocks were not found.", skippedItems );
167
168   // Clear the dirty flag.
169   mIsDirty = false;
170
171   return true;
172}
173
174bool ForestData::write( const char *path )
175{
176   // Open the stream.
177   FileStream stream;
178   if ( !stream.open( path, Torque::FS::File::Write ) )
179   {
180      Con::errorf( "ForestDataFile::write() - Failed opening stream!" );
181      return false;
182   }
183
184   // Write our identifier... so we have a better
185   // idea if we're reading pure garbage.
186   stream.write( 4, "FKDF" );
187
188   // Now the version number.
189   stream.write( (U8)FILE_VERSION );
190
191   // First gather all the ForestItemData datablocks
192   // used by the items in the forest.
193   Vector<ForestItemData*> allDatablocks;
194   getDatablocks( &allDatablocks );
195
196   // Write out the datablock list.
197   U32 count = allDatablocks.size();
198   stream.write( count );
199   for ( U32 i=0; i < count; i++ )
200   {
201      StringTableEntry localName = allDatablocks[i]->getInternalName();
202      AssertFatal( localName != NULL && localName[0] != '\0', "ForestData::write - ForestItemData had no internal name set!" );
203      stream.writeString( allDatablocks[i]->getInternalName() );
204   }
205
206   // Get a copy of all the items.
207   Vector<ForestItem> items;
208   getItems( &items );
209
210   // Save the item count.
211   stream.write( (U32)items.size() );
212
213   // Save the items.
214   Vector<ForestItem>::const_iterator iter = items.begin();
215   for ( ; iter != items.end(); iter++ )
216   {
217      U8 dataIndex = T3D::find( allDatablocks.begin(), allDatablocks.end(), iter->getData() ) - allDatablocks.begin();
218
219      stream.write( dataIndex );
220
221      mathWrite( stream, iter->getPosition() );
222
223      QuatF quat;       
224      quat.set( iter->getTransform() );
225      mathWrite( stream, quat );
226
227      stream.write( iter->getScale() );
228   }
229
230   // Clear the dirty flag.
231   mIsDirty = false;
232
233   return true;
234}
235
236void ForestData::regenCells()
237{
238   Vector<ForestItem> items;
239   getItems( &items );
240
241   clear();
242
243   for ( U32 i=0; i < items.size(); i++ )
244   {
245      const ForestItem &item = items[i];
246      addItem( item.getKey(), item.getData(), item.getTransform(), item.getScale() );
247   }
248
249   mIsDirty = true;
250}
251
252ForestCell* ForestData::_findBucket( const Point2I &key ) const
253{
254   BucketTable::ConstIterator iter = mBuckets.find( key );
255
256   if ( iter != mBuckets.end() )
257      return iter->value;
258   else
259      return NULL;
260}
261
262ForestCell* ForestData::_findOrCreateBucket( const Point3F &pos )
263{
264   // Look it up.
265   const Point2I key = _getBucketKey( pos );
266   BucketTable::Iterator iter = mBuckets.find( key );
267
268   ForestCell *bucket = NULL;
269   if ( iter != mBuckets.end() )
270      bucket = iter->value;
271   else
272   {
273      bucket = new ForestCell( RectF( key.x, key.y, BUCKET_DIM, BUCKET_DIM ) );
274      mBuckets.insertUnique( key, bucket );     
275      mIsDirty = true;
276   }
277
278   return bucket;
279}
280
281void ForestData::_onItemReload()
282{
283   // Invalidate cell batches and bounds so they
284   // will be regenerated next render.
285
286   Vector<ForestCell*> stack;
287   getCells( &stack );
288
289   ForestCell *pCell;
290
291   while ( !stack.empty() )
292   {
293      pCell = stack.last();
294      stack.pop_back();
295
296      if ( !pCell )
297         continue;
298
299      pCell->freeBatches();
300      pCell->invalidateBounds();
301
302      pCell->getChildren( &stack );
303   }
304}
305
306const ForestItem& ForestData::addItem( ForestItemData *data,
307                                       const Point3F &position,
308                                       F32 rotation,
309                                       F32 scale )
310{
311   MatrixF xfm;
312   xfm.set( EulerF( 0, 0, rotation ), position );
313
314   return addItem(   smNextItemId++,
315                     data,
316                     xfm, 
317                     scale );
318}
319
320const ForestItem& ForestData::addItem( ForestItemKey key,
321                                       ForestItemData *data,
322                                       const MatrixF &xfm,
323                                       F32 scale )
324{
325   ForestCell *bucket = _findOrCreateBucket( xfm.getPosition() );
326   
327   mIsDirty = true;
328
329   return bucket->insertItem( key, data, xfm, scale );
330}
331
332const ForestItem& ForestData::updateItem( ForestItemKey key,
333                                          const Point3F &keyPosition,
334                                          ForestItemData *newData,
335                                          const MatrixF &newXfm,
336                                          F32 newScale )
337{
338   Point2I bucketKey = _getBucketKey( keyPosition );
339
340   ForestCell *bucket = _findBucket( bucketKey );
341
342   if ( !bucket || !bucket->removeItem( key, keyPosition, true ) )
343      return ForestItem::Invalid;
344
345   if ( bucket->isEmpty() )
346   {
347      delete bucket;
348      mBuckets.erase( bucketKey );
349   }
350
351   return addItem( key, newData, newXfm, newScale );
352}
353
354const ForestItem& ForestData::updateItem( ForestItem &item )
355{
356   return updateItem( item.getKey(), 
357                      item.getPosition(), 
358                      item.getData(), 
359                      item.getTransform(), 
360                      item.getScale() );   
361}
362
363bool ForestData::removeItem( ForestItemKey key, const Point3F &keyPosition )
364{
365   Point2I bucketkey = _getBucketKey( keyPosition );
366
367   ForestCell *bucket = _findBucket( keyPosition );
368
369   if ( !bucket || !bucket->removeItem( key, keyPosition, true ) )
370      return false;
371
372   if ( bucket->isEmpty() )
373   {
374      delete bucket;
375      mBuckets.erase( bucketkey );
376   }      
377
378   mIsDirty = true;
379
380   return true;
381}
382
383const ForestItem& ForestData::findItem( ForestItemKey key, const Point3F &keyPos ) const
384{
385   PROFILE_SCOPE( ForestData_findItem );
386
387   AssertFatal( key != 0, "ForestCell::findItem() - Got null key!" );
388
389   ForestCell *cell = _findBucket( keyPos );
390
391   while ( cell && !cell->isLeaf() )
392      cell = cell->getChildAt( keyPos );
393
394   U32 index;
395   if ( cell && cell->findIndexByKey( key, &index ) )
396      return cell->getItems()[ index ];
397
398   return ForestItem::Invalid;
399}
400
401const ForestItem& ForestData::findItem( ForestItemKey key ) const
402{
403   PROFILE_SCOPE( ForestData_findItem_Slow );
404
405   AssertFatal( key != 0, "ForestData::findItem() - Got null key!" );
406
407   // Do an exhaustive search thru all the cells... this
408   // is really crappy... we shouldn't do this regularly.
409
410   Vector<const ForestCell*> stack;
411   BucketTable::ConstIterator iter = mBuckets.begin();
412   for (; iter != mBuckets.end(); ++iter)
413      stack.push_back( iter->value );
414
415   // Now loop till we run out of cells.
416   while ( !stack.empty() )
417   {
418      // Pop off the next cell.
419      const ForestCell *cell = stack.last();
420      stack.pop_back();
421
422      // Recurse thru non-leaf cells.
423      if ( !cell->isLeaf() )
424      {
425         cell->getChildren( &stack );
426         continue;
427      }
428
429      // Finally search for the item.
430      U32 index;
431      if ( cell->findIndexByKey( key, &index ) )
432         return cell->getItems()[ index ];
433   }
434
435   return ForestItem::Invalid;
436}
437
438U32 ForestData::getItems( Vector<ForestItem> *outItems ) const
439{
440   AssertFatal( outItems, "ForestData::getItems() - The output vector was NULL!" );
441
442   PROFILE_SCOPE( ForestData_getItems );
443
444   Vector<const ForestCell*> stack;
445   U32 count = 0;
446
447   BucketTable::ConstIterator iter = mBuckets.begin();
448   for (; iter != mBuckets.end(); ++iter)
449      stack.push_back( iter->value );
450
451   // Now loop till we run out of cells.
452   while ( !stack.empty() )
453   {
454      // Pop off the next cell.
455      const ForestCell *cell = stack.last();
456      stack.pop_back();
457
458      // Recurse thru non-leaf cells.
459      if ( !cell->isLeaf() )
460      {
461         cell->getChildren( &stack );
462         continue;
463      }
464
465      // Get the items.
466      count += cell->getItems().size();
467      outItems->merge( cell->getItems() );
468   }
469
470   return count;
471}
472
473U32 ForestData::getItems( const Frustum &culler, Vector<ForestItem> *outItems ) const
474{
475   AssertFatal( outItems, "ForestData::getItems() - The output vector was NULL!" );
476
477   PROFILE_SCOPE( ForestData_getItems_ByFrustum );
478
479   Vector<ForestCell*> stack;
480   getCells( &stack );
481   Vector<ForestItem>::const_iterator iter;
482   U32 count = 0;
483
484   // Now loop till we run out of cells.
485   while ( !stack.empty() )
486   {
487      // Pop off the next cell.
488      const ForestCell *cell = stack.last();
489      stack.pop_back();
490
491      if ( culler.isCulled( cell->getBounds() ) )
492         continue;
493
494      // Recurse thru non-leaf cells.
495      if ( cell->isBranch() )
496      {
497         cell->getChildren( &stack );
498         continue;
499      }
500
501      // Get the items.
502      iter = cell->getItems().begin();
503      for ( ; iter != cell->getItems().end(); iter++ )
504      {
505         if ( !culler.isCulled( iter->getWorldBox() ) )
506         {
507            outItems->merge( cell->getItems() );
508            count++;
509         }
510      }
511   }
512
513   return count;
514}
515
516U32 ForestData::getItems( const Box3F &box, Vector<ForestItem> *outItems ) const
517{
518   PROFILE_SCOPE( ForestData_getItems_ByBox );
519
520   Vector<const ForestCell*> stack;
521   U32 count = 0;
522
523   BucketTable::ConstIterator iter = mBuckets.begin();
524   for (; iter != mBuckets.end(); ++iter)
525      stack.push_back( iter->value );
526
527   // Now loop till we run out of cells.
528   while ( !stack.empty() )
529   {
530      // Pop off the next cell.
531      const ForestCell *cell = stack.last();
532      stack.pop_back();
533
534      // If the cell is empty or doesn't overlap the box... skip it.
535      if (  cell->isEmpty() || 
536            !cell->getBounds().isOverlapped( box ) )
537         continue;
538
539      // Recurse thru non-leaf cells.
540      if ( !cell->isLeaf() )
541      {
542         cell->getChildren( &stack );
543         continue;
544      }
545
546      // Finally look thru the items.
547      const Vector<ForestItem> &items = cell->getItems();
548      Vector<ForestItem>::const_iterator item = items.begin();
549      for ( ; item != items.end(); item++ )
550      {
551         if ( item->getWorldBox().isOverlapped( box ) )
552         {
553            // If we don't have an output vector then the user just
554            // wanted to know if any object existed... so early out.
555            if ( !outItems )
556               return 1;
557
558            ++count;
559            outItems->push_back( *item );
560         }
561      }
562   }
563
564   return count;
565}
566
567U32 ForestData::getItems( const Point3F &point, F32 radius, Vector<ForestItem> *outItems ) const
568{
569   PROFILE_SCOPE( ForestData_getItems_BySphere );
570
571   Vector<const ForestCell*> stack;
572   U32 count = 0;
573
574   BucketTable::ConstIterator iter = mBuckets.begin();
575   for (; iter != mBuckets.end(); ++iter)
576         stack.push_back( iter->value );
577
578   const F32 radiusSq = radius * radius;
579
580   // Now loop till we run out of cells.
581   while ( !stack.empty() )
582   {
583      // Pop off the next cell.
584      const ForestCell *cell = stack.last();
585      stack.pop_back();
586
587      // TODO: If we could know here that the cell is fully within
588      // the sphere... we could do a fast gather of all its elements
589      // without any further testing of it or its children.
590
591      // If the cell is empty or doesn't overlap the sphere... skip it.
592      if (  cell->isEmpty() || 
593            cell->getBounds().getSqDistanceToPoint( point ) > radiusSq )
594         continue;
595
596      // Recurse thru non-leaf cells.
597      if ( !cell->isLeaf() )
598      {
599         cell->getChildren( &stack );
600         continue;
601      }
602
603      // Finally look thru the items.
604      const Vector<ForestItem> &items = cell->getItems();
605      Vector<ForestItem>::const_iterator item = items.begin();
606      for ( ; item != items.end(); item++ )
607      {
608         if ( item->getWorldBox().getSqDistanceToPoint( point ) < radiusSq )
609         {
610            // If we don't have an output vector then the user just
611            // wanted to know if any object existed... so early out.
612            if ( !outItems )
613               return 1;
614
615            ++count;
616            outItems->push_back( *item );
617         }
618      }
619   }
620
621   return count;
622}
623
624
625U32 ForestData::getItems( const Point2F &point, F32 radius, Vector<ForestItem> *outItems ) const
626{
627   PROFILE_SCOPE( ForestData_getItems_ByCircle );
628
629   Vector<const ForestCell*> stack;
630   U32 count = 0;
631
632   BucketTable::ConstIterator iter = mBuckets.begin();
633   for (; iter != mBuckets.end(); ++iter)
634         stack.push_back( iter->value );
635
636   const F32 radiusSq = radius * radius;
637
638   // Now loop till we run out of cells.
639   while ( !stack.empty() )
640   {
641      // Pop off the next cell.
642      const ForestCell *cell = stack.last();
643      stack.pop_back();
644
645      // If the cell is empty or doesn't overlap the sphere... skip it.
646      if (  cell->isEmpty() || 
647            cell->getRect().getSqDistanceToPoint( point ) > radiusSq )
648         continue;
649
650      // Recurse thru non-leaf cells.
651      if ( !cell->isLeaf() )
652      {
653         cell->getChildren( &stack );
654         continue;
655      }
656
657      // Finally look thru the items.
658      const Vector<ForestItem> &items = cell->getItems();
659      Vector<ForestItem>::const_iterator item = items.begin();
660      F32 compareDist;
661      for ( ; item != items.end(); item++ )
662      {
663         compareDist = mSquared( radius + item->getData()->mRadius );
664         if ( item->getSqDistanceToPoint( point ) < compareDist )
665         {
666            // If we don't have an output vector then the user just
667            // wanted to know if any object existed... so early out.
668            if ( !outItems )
669               return 1;
670
671            ++count;
672            outItems->push_back( *item );
673         }
674      }
675   }
676
677   return count;
678}
679
680U32 ForestData::getItems( const ForestItemData *data, Vector<ForestItem> *outItems ) const
681{
682   AssertFatal( outItems, "ForestData::getItems() - The output vector was NULL!" );
683
684   PROFILE_SCOPE( ForestData_getItems_ByDatablock );
685
686   Vector<const ForestCell*> stack;
687   U32 count = 0;
688
689   BucketTable::ConstIterator iter = mBuckets.begin();
690   for (; iter != mBuckets.end(); ++iter)
691      stack.push_back( iter->value );
692
693   // Now loop till we run out of cells.
694   while ( !stack.empty() )
695   {
696      // Pop off the next cell.
697      const ForestCell *cell = stack.last();
698      stack.pop_back();
699
700      // Recurse thru non-leaf cells.
701      if ( !cell->isLeaf() )
702      {
703         cell->getChildren( &stack );
704         continue;
705      }
706
707      // Get the items.
708      const Vector<ForestItem> &items = cell->getItems();
709      Vector<ForestItem>::const_iterator item = items.begin();
710      for ( ; item != items.end(); item++ )
711      {
712         if ( item->getData() == data )
713         {
714            ++count;
715            outItems->push_back( *item );
716         }
717      }
718   }
719
720   return count;
721}
722
723void ForestData::getCells( const Frustum &frustum, Vector<ForestCell*> *outCells ) const
724{
725   PROFILE_SCOPE( ForestData_getCells_frustum );
726
727   BucketTable::ConstIterator iter = mBuckets.begin();
728   for (; iter != mBuckets.end(); ++iter)
729   {
730      if ( !frustum.isCulled( iter->value->getBounds() ) )
731         outCells->push_back( iter->value );
732   }
733}
734
735void ForestData::getCells( Vector<ForestCell*> *outCells ) const
736{
737   PROFILE_SCOPE( ForestData_getCells_nofrustum );
738
739   BucketTable::ConstIterator iter = mBuckets.begin();
740   for (; iter != mBuckets.end(); ++iter)
741      outCells->push_back( iter->value );
742}
743
744U32 ForestData::getDatablocks( Vector<ForestItemData*> *outVector ) const
745{
746   Vector<const ForestCell*> stack;
747   U32 count = 0;
748
749   BucketTable::ConstIterator iter = mBuckets.begin();
750   for (; iter != mBuckets.end(); ++iter)
751      stack.push_back( iter->value );
752
753   // Now loop till we run out of cells.
754   while ( !stack.empty() )
755   {
756      // Pop off the next cell.
757      const ForestCell *cell = stack.last();
758      stack.pop_back();
759
760      // Recurse thru non-leaf cells.
761      if ( !cell->isLeaf() )
762      {
763         cell->getChildren( &stack );
764         continue;
765      }
766
767      // Go thru the items.
768      const Vector<ForestItem> &items = cell->getItems();
769      Vector<ForestItem>::const_iterator item = items.begin();
770      for ( ; item != items.end(); item++ )
771      {
772         ForestItemData *data = item->getData();
773
774         if (T3D::find( outVector->begin(), outVector->end(), data ) != outVector->end() )
775            continue;
776
777         count++;
778         outVector->push_back( data );
779      }
780   }
781
782   return count;
783}
784
785void ForestData::clearPhysicsRep( Forest *forest )
786{
787   Vector<ForestCell*> stack;
788
789   BucketTable::Iterator iter = mBuckets.begin();
790   for (; iter != mBuckets.end(); ++iter)
791      stack.push_back( iter->value );
792
793   // Now loop till we run out of cells.
794   while ( !stack.empty() )
795   {
796      // Pop off the next cell.
797      ForestCell *cell = stack.last();
798      stack.pop_back();
799
800      // Recurse thru non-leaf cells.
801      if ( !cell->isLeaf() )
802      {
803         cell->getChildren( &stack );
804         continue;
805      }
806
807      cell->clearPhysicsRep( forest );      
808   }
809}
810
811void ForestData::buildPhysicsRep( Forest *forest )
812{
813   Vector<ForestCell*> stack;
814
815   BucketTable::Iterator iter = mBuckets.begin();
816   for (; iter != mBuckets.end(); ++iter)
817      stack.push_back( iter->value );
818
819   // Now loop till we run out of cells.
820   while ( !stack.empty() )
821   {
822      // Pop off the next cell.
823      ForestCell *cell = stack.last();
824      stack.pop_back();
825
826      // Recurse thru non-leaf cells.
827      if ( !cell->isLeaf() )
828      {
829         cell->getChildren( &stack );
830         continue;
831      }
832
833      cell->buildPhysicsRep( forest );      
834   }   
835}
836