forest.cpp

Engine/source/forest/forest.cpp

More...

Public Variables

bool

For frame signal.

Public Functions

ConsoleDocClass(Forest , "@brief %<a href="/coding/class/classforest/">Forest</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> global-bounds scene object provides collision and rendering <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "(.forest) data <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">file.\n\n</a>" "%<a href="/coding/class/classforest/">Forest</a> is designed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> efficiently <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> large number of static meshes: trees, rocks " " plants, etc. These cannot be moved at game-time or play animations but do support wind " "effects using vertex shader transformations guided by vertex color in the asset and " "user placed wind emitters(or weapon explosions).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "Script level manipulation of forest data is not possible through % Forest, it is only " "the rendering/collision. All editing is done through the world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">editor.\n\n</a>" " @see <a href="/coding/class/classtsforestitemdata/">TSForestItemData</a> Defines <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tree <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">type.\n</a>" " @see GuiForestEditorCtrl Used by the world editor <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> provide manipulation of forest <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">data.\n</a>" " @ingroup <a href="/coding/class/classforest/">Forest</a>" )
DefineEngineMethod(Forest , clear , void , () , "()" )
DefineEngineMethod(Forest , isDirty , bool , () , "()" )
DefineEngineMethod(Forest , regenCells , void , () , "()" )
DefineEngineMethod(Forest , saveDataFile , void , (const char *path) , ("") , "saveDataFile( [path] )" )

Detailed Description

Public Variables

bool gEditingMission 

For frame signal.

Public Functions

ConsoleDocClass(Forest , "@brief %<a href="/coding/class/classforest/">Forest</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> global-bounds scene object provides collision and rendering <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "(.forest) data <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">file.\n\n</a>" "%<a href="/coding/class/classforest/">Forest</a> is designed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> efficiently <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> large number of static meshes: trees, rocks " " plants, etc. These cannot be moved at game-time or play animations but do support wind " "effects using vertex shader transformations guided by vertex color in the asset and " "user placed wind emitters(or weapon explosions).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "Script level manipulation of forest data is not possible through % Forest, it is only " "the rendering/collision. All editing is done through the world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">editor.\n\n</a>" " @see <a href="/coding/class/classtsforestitemdata/">TSForestItemData</a> Defines <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tree <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">type.\n</a>" " @see GuiForestEditorCtrl Used by the world editor <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> provide manipulation of forest <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">data.\n</a>" " @ingroup <a href="/coding/class/classforest/">Forest</a>" )

DefineEngineMethod(Forest , clear , void , () , "()" )

DefineEngineMethod(Forest , isDirty , bool , () , "()" )

DefineEngineMethod(Forest , regenCells , void , () , "()" )

DefineEngineMethod(Forest , saveDataFile , void , (const char *path) , ("") , "saveDataFile( [path] )" )

IMPLEMENT_CO_NETOBJECT_V1(Forest )

  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/forest.h"
 26
 27#include "forest/forestCell.h"
 28#include "forest/forestCollision.h"
 29#include "forest/forestDataFile.h"
 30#include "forest/forestWindMgr.h"
 31#include "forest/forestWindAccumulator.h"
 32
 33#include "core/resourceManager.h"
 34#include "core/volume.h"
 35#include "T3D/gameBase/gameConnection.h"
 36#include "console/consoleInternal.h"
 37#include "core/stream/bitStream.h"
 38#include "math/mathIO.h"
 39#include "environment/sun.h"
 40#include "scene/sceneManager.h"
 41#include "math/mathUtils.h"
 42#include "math/mTransform.h"
 43#include "T3D/physics/physicsBody.h"
 44#include "forest/editor/forestBrushElement.h"
 45#include "console/engineAPI.h"
 46
 47/// For frame signal
 48#include "gui/core/guiCanvas.h"
 49
 50
 51extern bool gEditingMission;
 52
 53ForestCreatedSignal Forest::smCreatedSignal;
 54ForestCreatedSignal Forest::smDestroyedSignal;
 55
 56bool Forest::smForceImposters = false;
 57bool Forest::smDisableImposters = false;
 58bool Forest::smDrawCells = false;
 59bool Forest::smDrawBounds = false;
 60
 61
 62IMPLEMENT_CO_NETOBJECT_V1(Forest);
 63
 64ConsoleDocClass( Forest,
 65   
 66   "@brief %Forest is a global-bounds scene object provides collision and rendering for a "
 67   "(.forest) data file.\n\n"
 68   
 69   "%Forest is designed to efficiently render a large number of static meshes: trees, rocks "
 70   "plants, etc. These cannot be moved at game-time or play animations but do support wind "
 71   "effects using vertex shader transformations guided by vertex color in the asset and "
 72   "user placed wind emitters ( or weapon explosions ).\n\n"
 73   
 74   "Script level manipulation of forest data is not possible through %Forest, it is only "
 75   "the rendering/collision. All editing is done through the world editor.\n\n"
 76   
 77   "@see TSForestItemData Defines a tree type.\n"
 78   "@see GuiForestEditorCtrl Used by the world editor to provide manipulation of forest data.\n" 
 79   "@ingroup Forest"
 80);
 81
 82
 83Forest::Forest()
 84   :  mDataFileName( NULL ),
 85      mConvexList( new Convex() ),
 86      mReflectionLodScalar( 2.0f ),
 87      mZoningDirty( false )
 88{
 89   mTypeMask |= EnvironmentObjectType | StaticShapeObjectType | StaticObjectType;
 90   mNetFlags.set(Ghostable | ScopeAlways);
 91}
 92
 93Forest::~Forest()
 94{
 95   delete mConvexList;
 96   mConvexList = NULL;
 97}
 98
 99
100void Forest::initPersistFields()
101{
102   Parent::initPersistFields();
103
104   addField( "dataFile",  TypeFilename, Offset( mDataFileName, Forest ),
105      "The source forest data file." );
106
107   addGroup( "Lod" );
108      
109      addField( "lodReflectScalar",      TypeF32,       Offset( mReflectionLodScalar, Forest ),
110         "Scalar applied to the farclip distance when Forest renders into a reflection." );
111
112   endGroup( "Lod" );
113}
114
115void Forest::consoleInit()
116{
117   // Some stats exposed to the console.
118   Con::addVariable("$Forest::totalCells", TypeS32, &Forest::smTotalCells, "@internal" );
119   Con::addVariable("$Forest::cellsRendered", TypeS32, &Forest::smCellsRendered, "@internal" );
120   Con::addVariable("$Forest::cellItemsRendered", TypeS32, &Forest::smCellItemsRendered, "@internal" );
121   Con::addVariable("$Forest::cellsBatched", TypeS32, &Forest::smCellsBatched, "@internal" );
122   Con::addVariable("$Forest::cellItemsBatched", TypeS32, &Forest::smCellItemsBatched, "@internal" );
123   Con::addVariable("$Forest::averageCellItems", TypeF32, &Forest::smAverageItemsPerCell, "@internal" );
124
125   // Some debug flags.
126   Con::addVariable("$Forest::forceImposters", TypeBool, &Forest::smForceImposters,
127      "A debugging aid which will force all forest items to be rendered as imposters.\n"
128      "@ingroup Forest\n" );
129   Con::addVariable("$Forest::disableImposters", TypeBool, &Forest::smDisableImposters,
130      "A debugging aid which will disable rendering of all imposters in the forest.\n"
131      "@ingroup Forest\n" );
132   Con::addVariable("$Forest::drawCells", TypeBool, &Forest::smDrawCells,
133      "A debugging aid which renders the forest cell bounds.\n"
134      "@ingroup Forest\n" );
135   Con::addVariable("$Forest::drawBounds", TypeBool, &Forest::smDrawBounds,
136      "A debugging aid which renders the forest bounds.\n"
137      "@ingroup Forest\n" );
138
139   // The canvas signal lets us know to clear the rendering stats.
140   GuiCanvas::getGuiCanvasFrameSignal().notify( &Forest::_clearStats );
141}
142
143bool Forest::onAdd()
144{
145   if (!Parent::onAdd())
146      return false;
147
148   const char *name = getName();
149   if(name && name[0] && getClassRep())
150   {
151      Namespace *parent = getClassRep()->getNameSpace();
152      Con::linkNamespaces(parent->mName, name);
153      mNameSpace = Con::lookupNamespace(name);
154   }
155
156   setGlobalBounds();
157   resetWorldBox();
158
159   // TODO: Make sure this calls the script "onAdd" which will
160   // populate the object with forest entries before creation.
161   addToScene();
162
163   // If we don't have a file name and the editor is 
164   // enabled then create an empty forest data file.
165   if ( isServerObject() && ( !mDataFileName || !mDataFileName[0] ) )
166      createNewFile();
167   else
168   {
169      // Try to load the forest file.
170      mData = ResourceManager::get().load( mDataFileName );
171      if ( !mData )
172      {
173         if ( isClientObject() )
174            NetConnection::setLastError( "You are missing a file needed to play this mission: %s", mDataFileName );
175
176         return false;
177      }
178   }
179
180   updateCollision();
181
182   smCreatedSignal.trigger( this );
183
184   if ( isClientObject() )
185   {
186      mZoningDirty = true;
187      SceneZoneSpaceManager::getZoningChangedSignal().notify( this, &Forest::_onZoningChanged );
188
189      ForestWindMgr::getAdvanceSignal().notify( this, &Forest::getLocalWindTrees );
190   }
191
192   return true;
193}
194
195void Forest::onRemove()
196{
197   Parent::onRemove();
198
199   smDestroyedSignal.trigger( this );
200
201   if ( mData )
202      mData->clearPhysicsRep( this );
203
204   mData = NULL;
205   
206   if ( isClientObject() )
207   {
208      SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &Forest::_onZoningChanged );
209      ForestWindMgr::getAdvanceSignal().remove( this, &Forest::getLocalWindTrees );
210   }
211
212   mConvexList->nukeList();
213
214   removeFromScene();
215}
216
217U32 Forest::packUpdate( NetConnection *connection, U32 mask, BitStream *stream )
218{
219   U32 retMask = Parent::packUpdate( connection, mask, stream );
220   
221   if ( stream->writeFlag( mask & MediaMask ) ) 
222      stream->writeString( mDataFileName );
223
224   if ( stream->writeFlag( mask & LodMask ) )
225   {
226      stream->write( mReflectionLodScalar );
227   }
228
229   return retMask;
230}
231
232void Forest::unpackUpdate(NetConnection *connection, BitStream *stream)
233{
234   Parent::unpackUpdate(connection,stream);
235   
236   if ( stream->readFlag() )
237   {
238      mDataFileName = stream->readSTString();
239   }
240
241   if ( stream->readFlag() ) // LodMask
242   {
243      stream->read( &mReflectionLodScalar );
244   }
245}
246
247void Forest::inspectPostApply()
248{
249   Parent::inspectPostApply();
250
251   // Update the client... note that this
252   // doesn't cause a regen of the forest.
253   setMaskBits( LodMask );
254}
255
256void Forest::setTransform( const MatrixF &mat )
257{
258   // Note: We do not use the position of the forest at all.
259   Parent::setTransform( mat );
260}
261
262void Forest::_onZoningChanged( SceneZoneSpaceManager *zoneManager )
263{
264   const SceneManager* sm = getSceneManager();
265
266   if (mData == NULL || (sm != NULL && sm->getZoneManager() != NULL && zoneManager != sm->getZoneManager()))
267      return;
268
269   mZoningDirty = true;
270}
271
272void Forest::getLocalWindTrees( const Point3F &camPos, F32 radius, Vector<TreePlacementInfo> *placementInfo )
273{
274   PROFILE_SCOPE( Forest_getLocalWindTrees );
275
276   Vector<ForestItem> items;
277   items.reserve( placementInfo->capacity() );
278   mData->getItems( camPos, radius, &items );
279
280   TreePlacementInfo treeInfo;
281   dMemset( &treeInfo, 0, sizeof ( TreePlacementInfo ) );
282
283   // Reserve some space in the output.
284   placementInfo->reserve( items.size() );
285
286   // Build an info struct for each returned item.
287   Vector<ForestItem>::const_iterator iter = items.begin();
288   for ( ; iter != items.end(); iter++ )
289   {
290      // Skip over any zero wind elements here and
291      // just keep them out of the final list.
292      treeInfo.dataBlock = iter->getData();
293      if ( treeInfo.dataBlock->mWindScale < 0.001f )
294         continue;
295
296      treeInfo.pos = iter->getPosition();
297      treeInfo.scale = iter->getScale();
298      treeInfo.itemKey = iter->getKey();
299      placementInfo->push_back( treeInfo );
300   }
301}
302
303void Forest::applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude )
304{   
305   if ( isServerObject() )
306      return;   
307
308   // Find all the trees in the radius
309   // then get their accumulators and 
310   // push our impulse into them.
311   VectorF impulse( 0, 0, 0 );
312   ForestWindAccumulator *accumulator = NULL;
313
314   Vector<TreePlacementInfo> trees;
315   getLocalWindTrees( origin, radius, &trees );
316   for ( U32 i = 0; i < trees.size(); i++ )
317   {
318      const TreePlacementInfo &treeInfo = trees[i];
319      accumulator = WINDMGR->getLocalWind( treeInfo.itemKey );
320      if ( !accumulator )
321         continue;
322
323      impulse = treeInfo.pos - origin;
324      impulse.normalize();
325      impulse *= magnitude;
326
327      accumulator->applyImpulse( impulse );
328   }
329}
330
331void Forest::createNewFile()
332{
333   // Release the current file if we have one.
334   mData = NULL;
335
336   // We need to construct a default file name
337   String missionName( Con::getVariable( "$Client::MissionFile" ) );
338   String levelDirectory( Con::getVariable( "$pref::Directories::Level" ) );
339   if ( levelDirectory.isEmpty() )
340   {
341      levelDirectory = "levels";
342   }
343   missionName.replace( "tools/levels", levelDirectory );
344   missionName = Platform::makeRelativePathName(missionName, Platform::getMainDotCsDir());
345
346   Torque::Path basePath( missionName );
347   String fileName = Torque::FS::MakeUniquePath( basePath.getPath(), basePath.getFileName(), "forest" );
348   mDataFileName = StringTable->insert( fileName );
349
350   ForestData *file = new ForestData;
351   file->write( mDataFileName );
352   delete file;
353
354   mData = ResourceManager::get().load( mDataFileName );
355   mZoningDirty = true;
356}
357
358void Forest::saveDataFile( const char *path )
359{
360   if ( path && !String::isEmpty(path))
361      mDataFileName = StringTable->insert( path );
362
363   if ( mData )
364      mData->write( mDataFileName );
365}
366
367DefineEngineMethod( Forest, saveDataFile, void, (const char * path), (""), "saveDataFile( [path] )" )
368{   
369   object->saveDataFile( path );
370}
371
372DefineEngineMethod(Forest, isDirty, bool, (), , "()")
373{
374   return object->getData() && object->getData()->isDirty();
375}
376
377DefineEngineMethod(Forest, regenCells, void, (), , "()")
378{
379   object->getData()->regenCells();
380}
381
382DefineEngineMethod(Forest, clear, void, (), , "()" )
383{
384   object->clear();
385}
386