prefab.cpp

Engine/source/T3D/prefab.cpp

More...

Public Variables

bool

For frame signal.

Public Functions

ConsoleDocClass(Prefab , "@brief A collection of arbitrary objects which can be allocated and manipulated as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">group.\n\n</a>" "%<a href="/coding/class/classprefab/">Prefab</a> always points <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> (.prefab) <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> which defines its objects. In " "fact more than one %<a href="/coding/class/classprefab/">Prefab</a> can reference this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> and both will update " "<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">modified.\n\n</a>" "%<a href="/coding/class/classprefab/">Prefab</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> very simple object and only exists on the server. When it is " "created it allocates children objects by reading the (.prefab) <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> like " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> list of instructions. It then sets their transform relative <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the %<a href="/coding/class/classprefab/">Prefab</a> " "and Torque networking handles the rest by ghosting the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> clients. " "%<a href="/coding/class/classprefab/">Prefab</a> itself is not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ghosted.\n\n</a>" "@ingroup enviroMisc" )
IMPLEMENT_CALLBACK(Prefab , onLoad , void , (SimGroup *children) , (children) , "Called when the prefab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> is loaded and children objects are <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">created.\n</a>" "@param children <a href="/coding/class/classsimgroup/">SimGroup</a> containing all children <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n</a>" )

Detailed Description

Public Variables

bool gEditingMission 

For frame signal.

Vector< String > sPrefabFileStack 

Public Functions

ConsoleDocClass(Prefab , "@brief A collection of arbitrary objects which can be allocated and manipulated as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">group.\n\n</a>" "%<a href="/coding/class/classprefab/">Prefab</a> always points <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> (.prefab) <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> which defines its objects. In " "fact more than one %<a href="/coding/class/classprefab/">Prefab</a> can reference this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> and both will update " "<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">modified.\n\n</a>" "%<a href="/coding/class/classprefab/">Prefab</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> very simple object and only exists on the server. When it is " "created it allocates children objects by reading the (.prefab) <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> like " "<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> list of instructions. It then sets their transform relative <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the %<a href="/coding/class/classprefab/">Prefab</a> " "and Torque networking handles the rest by ghosting the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> clients. " "%<a href="/coding/class/classprefab/">Prefab</a> itself is not <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ghosted.\n\n</a>" "@ingroup enviroMisc" )

IMPLEMENT_CALLBACK(Prefab , onLoad , void , (SimGroup *children) , (children) , "Called when the prefab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> is loaded and children objects are <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">created.\n</a>" "@param children <a href="/coding/class/classsimgroup/">SimGroup</a> containing all children <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">objects.\n</a>" )

IMPLEMENT_CO_NETOBJECT_V1(Prefab )

  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 "T3D/prefab.h"
 26
 27#include "math/mathIO.h"
 28#include "core/stream/bitStream.h"
 29#include "scene/sceneRenderState.h"
 30#include "gfx/gfxTransformSaver.h"
 31#include "renderInstance/renderPassManager.h"
 32#include "console/consoleTypes.h"
 33#include "core/volume.h"
 34#include "console/engineAPI.h"
 35#include "T3D/physics/physicsShape.h"
 36#include "core/util/path.h"
 37
 38#include "T3D/Scene.h"
 39
 40// We use this locally ( within this file ) to prevent infinite recursion
 41// while loading prefab files that contain other prefabs.
 42static Vector<String> sPrefabFileStack;
 43
 44Map<SimObjectId,SimObjectId> Prefab::smChildToPrefabMap;
 45
 46IMPLEMENT_CO_NETOBJECT_V1(Prefab);
 47
 48ConsoleDocClass( Prefab,
 49   "@brief A collection of arbitrary objects which can be allocated and manipulated as a group.\n\n"
 50   
 51   "%Prefab always points to a (.prefab) file which defines its objects. In "
 52   "fact more than one %Prefab can reference this file and both will update "
 53   "if the file is modified.\n\n"
 54   
 55   "%Prefab is a very simple object and only exists on the server. When it is "
 56   "created it allocates children objects by reading the (.prefab) file like "
 57   "a list of instructions.  It then sets their transform relative to the %Prefab "
 58   "and Torque networking handles the rest by ghosting the new objects to clients. "
 59   "%Prefab itself is not ghosted.\n\n"
 60   
 61   "@ingroup enviroMisc"   
 62);
 63
 64IMPLEMENT_CALLBACK( Prefab, onLoad, void, ( SimGroup *children ), ( children ),
 65   "Called when the prefab file is loaded and children objects are created.\n"
 66   "@param children SimGroup containing all children objects.\n"
 67);
 68
 69Prefab::Prefab()
 70{
 71   // Not ghosted unless we're editing
 72   mNetFlags.clear(Ghostable);
 73
 74   mTypeMask |= StaticObjectType;
 75}
 76
 77Prefab::~Prefab()
 78{
 79}
 80
 81void Prefab::initPersistFields()
 82{
 83   addGroup( "Prefab" );
 84
 85      addProtectedField( "filename", TypePrefabFilename, Offset( mFilename, Prefab ),         
 86                         &protectedSetFile, &defaultProtectedGetFn,
 87                         "(.prefab) File describing objects within this prefab." );
 88
 89   endGroup( "Prefab" );
 90
 91   Parent::initPersistFields();
 92}
 93
 94extern bool gEditingMission;
 95
 96bool Prefab::onAdd()
 97{
 98   if ( !Parent::onAdd() )
 99      return false;
100
101   mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ),
102      Point3F(  0.5f,  0.5f,  0.5f ) );
103
104   resetWorldBox();
105   
106   // Not added to the scene unless we are editing.
107   if ( gEditingMission )
108      onEditorEnable();
109
110   // Only the server-side prefab needs to create/update child objects.
111   // We rely on regular Torque ghosting of the individual child objects
112   // to take care of the rest.
113   if ( isServerObject() )
114   {
115      _loadFile( true );
116      _updateChildren();
117   }
118
119   return true;
120}
121
122void Prefab::onRemove()
123{
124   if ( isServerObject() )
125      _closeFile( true );
126
127   removeFromScene();
128   Parent::onRemove();
129}
130
131void Prefab::onEditorEnable()
132{
133   if ( isClientObject() )
134      return;
135
136   // Just in case we are already in the scene, lets not cause an assert.   
137   if ( mContainer != NULL )
138      return;
139
140   // Enable ghosting so we can see this on the client.
141   mNetFlags.set(Ghostable);
142   setScopeAlways();
143   addToScene();
144
145   Parent::onEditorEnable();
146}
147
148void Prefab::onEditorDisable()
149{   
150   if ( isClientObject() )
151      return;
152
153   // Just in case we are not in the scene, lets not cause an assert.   
154   if ( mContainer == NULL )
155      return;
156
157   // Do not need this on the client if we are not editing.
158   removeFromScene();
159   mNetFlags.clear(Ghostable);
160   clearScopeAlways();
161
162   Parent::onEditorDisable();
163}
164
165void Prefab::inspectPostApply()
166{
167   Parent::inspectPostApply();
168}
169
170void Prefab::setTransform(const MatrixF & mat)
171{
172   Parent::setTransform( mat ); 
173
174   if ( isServerObject() )
175   {
176      setMaskBits( TransformMask );
177      _updateChildren();
178   }
179}
180
181void Prefab::setScale(const VectorF & scale)
182{
183   Parent::setScale( scale ); 
184
185   if ( isServerObject() )
186   {
187      setMaskBits( TransformMask );
188      _updateChildren();
189   }
190}
191
192U32 Prefab::packUpdate( NetConnection *conn, U32 mask, BitStream *stream )
193{
194   U32 retMask = Parent::packUpdate( conn, mask, stream );
195
196   mathWrite(*stream,mObjBox);
197
198   if ( stream->writeFlag( mask & FileMask ) )
199   {
200      stream->write( mFilename );
201   }
202
203   if ( stream->writeFlag( mask & TransformMask ) )
204   {
205      mathWrite(*stream, getTransform());
206      mathWrite(*stream, getScale());
207   }
208
209   return retMask;
210}
211
212void Prefab::unpackUpdate(NetConnection *conn, BitStream *stream)
213{
214   Parent::unpackUpdate(conn, stream);
215
216   mathRead(*stream, &mObjBox);
217   resetWorldBox();
218
219   // FileMask
220   if ( stream->readFlag() ) 
221   {
222      stream->read( &mFilename );
223   }
224
225   // TransformMask
226   if ( stream->readFlag() )  
227   {
228      mathRead(*stream, &mObjToWorld);
229      mathRead(*stream, &mObjScale);
230
231      setTransform( mObjToWorld );
232   }
233}
234
235bool Prefab::protectedSetFile( void *object, const char *index, const char *data )
236{
237   Prefab *prefab = static_cast<Prefab*>(object);
238   
239   String file = String( Platform::makeRelativePathName(data, Platform::getMainDotCsDir()) );
240
241   prefab->setFile( file );
242
243   return false;
244}
245
246void Prefab::setFile( String file )
247{  
248   AssertFatal( isServerObject(), "Prefab-bad" );
249
250   if ( !isProperlyAdded() )
251   {
252      mFilename = file;
253      return;
254   }
255   
256   // Client-side Prefab(s) do not create/update/reference children, everything
257   // is handled on the server-side. In normal usage this will never actually
258   // be called for the client-side prefab but maybe the user did so accidentally.
259   if ( isClientObject() )
260   {
261      Con::errorf( "Prefab::setFile( %s ) - Should not be called on a client-side Prefab.", file.c_str() );
262      return;
263   }
264
265   _closeFile( true );
266
267   mFilename = file;
268
269   if ( isProperlyAdded() )
270      _loadFile( true );
271}
272
273SimGroup* Prefab::explode()
274{
275   Scene* scene = Scene::getRootScene();
276
277   if ( !scene)
278   {
279      Con::errorf( "Prefab::explode, Scene was not found." );
280      return NULL;
281   }
282
283   if ( !mChildGroup )
284      return NULL;
285
286   SimGroup *group = mChildGroup;
287   Vector<SceneObject*> foundObjects;
288
289   group->findObjectByType( foundObjects );
290
291   if ( foundObjects.empty() )
292      return NULL;
293
294   for ( S32 i = 0; i < foundObjects.size(); i++ )
295   {
296      SceneObject *child = foundObjects[i];
297      _updateChildTransform( child );
298      smChildToPrefabMap.erase( child->getId() );
299   }
300   
301   scene->addObject(group);
302   mChildGroup = NULL;
303   mChildMap.clear();
304
305   return group;
306}
307
308void Prefab::_closeFile( bool removeFileNotify )
309{
310   AssertFatal( isServerObject(), "Prefab-bad" );
311
312   mChildMap.clear();
313
314   if ( mChildGroup )
315   {
316      // Get a flat vector of all our children.
317      Vector<SceneObject*> foundObjects;
318      mChildGroup->findObjectByType( foundObjects );
319
320      // Remove them all from the ChildToPrefabMap.
321      for ( S32 i = 0; i < foundObjects.size(); i++ )
322         smChildToPrefabMap.erase( foundObjects[i]->getId() );
323
324      mChildGroup->deleteObject();
325      mChildGroup = NULL;
326   }
327
328   if ( removeFileNotify )
329      Torque::FS::RemoveChangeNotification( mFilename, this, &Prefab::_onFileChanged );
330
331   // Back to a default bounding box size.
332   mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ), Point3F(  0.5f,  0.5f,  0.5f ) );
333   resetWorldBox();
334}
335
336void Prefab::_loadFile( bool addFileNotify )
337{
338   AssertFatal( isServerObject(), "Prefab-bad" );
339
340   if ( mFilename.isEmpty() )
341      return;
342
343   if ( !Platform::isFile( mFilename ) )
344   {
345      Con::errorf( "Prefab::_loadFile() - file %s was not found.", mFilename.c_str() );
346      return;
347   }
348
349   if ( sPrefabFileStack.contains(mFilename) )
350   {
351      Con::errorf( 
352         "Prefab::_loadFile - failed loading prefab file (%s). \n"
353         "File was referenced recursively by both a Parent and Child prefab.", mFilename.c_str() );
354      return;
355   }
356
357   sPrefabFileStack.push_back(mFilename);
358
359   String command = String::ToString( "exec( \"%s\" );", mFilename.c_str() );
360   Con::evaluate( command );
361
362   SimGroup *group;
363   if ( !Sim::findObject( Con::getVariable( "$ThisPrefab" ), group ) )
364   {
365      Con::errorf( "Prefab::_loadFile() - file %s did not create $ThisPrefab.", mFilename.c_str() );
366      return;
367   }
368
369   if ( addFileNotify )
370      Torque::FS::AddChangeNotification( mFilename, this, &Prefab::_onFileChanged );
371
372   mChildGroup = group;
373
374   Vector<SceneObject*> foundObjects;
375   mChildGroup->findObjectByType( foundObjects );
376
377   if ( !foundObjects.empty() )
378   {
379      mWorldBox = Box3F::Invalid;
380
381      for ( S32 i = 0; i < foundObjects.size(); i++ )
382      {
383         SceneObject *child = foundObjects[i];
384         mChildMap.insert( child->getId(), Transform( child->getTransform(), child->getScale() ) );
385         smChildToPrefabMap.insert( child->getId(), getId() );
386
387         _updateChildTransform( child );
388
389         mWorldBox.intersect( child->getWorldBox() );
390      }
391
392      resetObjectBox();
393   }
394
395   sPrefabFileStack.pop_back();
396
397   onLoad_callback( mChildGroup );
398}
399
400void Prefab::_updateChildTransform( SceneObject* child )
401{
402   ChildToMatMap::Iterator itr = mChildMap.find(child->getId());
403   AssertFatal( itr != mChildMap.end(), "Prefab, mChildMap out of synch with mChildGroup." );
404
405   MatrixF mat( itr->value.mat );
406   Point3F pos = mat.getPosition();
407   pos.convolve( mObjScale );
408   mat.setPosition( pos );
409   mat.mulL( mObjToWorld );
410
411   child->setTransform( mat );
412   child->setScale( itr->value.scale * mObjScale );
413
414   // Hack for PhysicsShape... need to store the "editor" position to return to
415   // when a physics reset event occurs. Normally this would be where it is 
416   // during onAdd, but in this case it is not because the prefab stores its
417   // child objects in object space...
418
419   PhysicsShape *childPS = dynamic_cast<PhysicsShape*>( child );
420   if ( childPS )
421      childPS->storeRestorePos();
422
423}
424
425void Prefab::_updateChildren()
426{
427   if ( !mChildGroup )
428      return;
429
430   Vector<SceneObject*> foundObjects;
431   mChildGroup->findObjectByType( foundObjects );
432
433   for ( S32 i = 0; i < foundObjects.size(); i++ )
434   {
435      SceneObject *child = foundObjects[i];
436
437      _updateChildTransform( child );
438
439      if ( child->getClientObject() )
440      {
441         ((SceneObject*)child->getClientObject())->setTransform( child->getTransform() );
442         ((SceneObject*)child->getClientObject())->setScale( child->getScale() );
443      }
444   }
445}
446
447void Prefab::_onFileChanged( const Torque::Path &path )
448{
449   AssertFatal( path == mFilename, "Prefab::_onFileChanged - path does not match filename." );
450
451   _closeFile(false);
452   _loadFile(false);
453   setMaskBits(U32_MAX);
454}
455
456Prefab* Prefab::getPrefabByChild( SimObject *child )
457{
458   ChildToPrefabMap::Iterator itr = smChildToPrefabMap.find( child->getId() );
459   if ( itr == smChildToPrefabMap.end() )
460      return NULL;
461
462   Prefab *prefab;
463   if ( !Sim::findObject( itr->value, prefab ) )
464   {
465      Con::errorf( "Prefab::getPrefabByChild - child object mapped to a prefab that no longer exists." );
466      return NULL;
467   }
468
469   return prefab;
470}
471
472bool Prefab::isValidChild( SimObject *simobj, bool logWarnings )
473{
474   if ( simobj->getName() && simobj == Scene::getRootScene() )
475   {
476      if ( logWarnings )
477         Con::warnf( "root Scene is not valid within a Prefab." );
478      return false;
479   }
480
481   if ( simobj->getClassRep()->isClass( AbstractClassRep::findClassRep("LevelInfo") ) )
482   {
483      if ( logWarnings )
484         Con::warnf( "LevelInfo objects are not valid within a Prefab" );
485      return false;
486   }
487
488   if ( simobj->getClassRep()->isClass( AbstractClassRep::findClassRep("TimeOfDay") ) )
489   {
490      if ( logWarnings )
491         Con::warnf( "TimeOfDay objects are not valid within a Prefab" );
492      return false;
493   }
494
495   SceneObject *sceneobj = dynamic_cast<SceneObject*>(simobj);
496
497   if ( !sceneobj )
498      return false;
499   
500   if ( sceneobj->isGlobalBounds() )
501   {
502      if ( logWarnings )
503         Con::warnf( "SceneObject's with global bounds are not valid within a Prefab." );
504      return false;
505   }
506   
507   if ( sceneobj->getClassRep()->isClass( AbstractClassRep::findClassRep("TerrainBlock") ) )
508   {
509      if ( logWarnings )
510         Con::warnf( "TerrainBlock objects are not valid within a Prefab" );
511      return false;
512   }
513
514   if ( sceneobj->getClassRep()->isClass( AbstractClassRep::findClassRep("Player") ) )
515   {
516      if ( logWarnings )
517         Con::warnf( "Player objects are not valid within a Prefab" );
518      return false;
519   }
520
521   if ( sceneobj->getClassRep()->isClass( AbstractClassRep::findClassRep("DecalRoad") ) )
522   {
523      if ( logWarnings )
524         Con::warnf( "DecalRoad objects are not valid within a Prefab" );
525      return false;
526   }
527
528   return true;
529}
530
531bool Prefab::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF& sphere)
532{
533   Vector<SceneObject*> foundObjects;
534   if (mChildGroup.isNull() || mChildGroup->empty())
535   {
536      Con::warnf("Bad Prefab Config! %s has no valid entries!", getName());
537      return false;
538   }
539   mChildGroup->findObjectByType(foundObjects);
540
541   for (S32 i = 0; i < foundObjects.size(); i++)
542   {
543      foundObjects[i]->buildPolyList(context, polyList, box, sphere);
544   }
545
546   return true;
547}
548
549bool Prefab::buildExportPolyList(ColladaUtils::ExportData* exportData, const Box3F &box, const SphereF &sphere)
550{
551   Vector<SceneObject*> foundObjects;
552   mChildGroup->findObjectByType(foundObjects);
553
554   for (S32 i = 0; i < foundObjects.size(); i++)
555   {
556      foundObjects[i]->buildExportPolyList(exportData, box, sphere);
557   }
558
559   return true;
560}
561
562void Prefab::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
563{
564   Vector<SceneObject*> foundObjects;
565   mChildGroup->findObjectByType(foundObjects);
566
567   for (S32 i = 0; i < foundObjects.size(); i++)
568   {
569      SceneObject* child = foundObjects[i];
570
571      child->getUtilizedAssets(usedAssetsList);
572   }
573}
574
575ExplodePrefabUndoAction::ExplodePrefabUndoAction( Prefab *prefab )
576: UndoAction( "Explode Prefab" )
577{
578   mPrefabId = prefab->getId();
579   mGroup = NULL;
580
581   // Do the action.
582   redo();
583}
584
585void ExplodePrefabUndoAction::undo()
586{
587   if ( !mGroup )   
588   {
589      Con::errorf( "ExplodePrefabUndoAction::undo - NULL Group" );
590      return;
591   }
592
593   mGroup->deleteObject();
594   mGroup = NULL;   
595}
596
597void ExplodePrefabUndoAction::redo()
598{
599   Prefab *prefab;
600   if ( !Sim::findObject( mPrefabId, prefab ) )
601   {
602      Con::errorf( "ExplodePrefabUndoAction::redo - Prefab (%i) not found.", mPrefabId );
603      return;
604   }
605
606   mGroup = prefab->explode();
607
608   String name;
609
610   if ( prefab->getName() && prefab->getName()[0] != '\0' )   
611      name = prefab->getName();   
612   else
613      name = "prefab";
614
615   name += "_exploded";
616   name = Sim::getUniqueName( name );
617   mGroup->assignName( name );   
618}
619