forest.cpp
Engine/source/forest/forest.cpp
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