terrData.cpp 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  23. // Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  24. // Copyright (C) 2015 Faust Logic, Inc.
  25. //~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  26. #include "platform/platform.h"
  27. #include "terrain/terrData.h"
  28. #include "terrain/terrCollision.h"
  29. #include "terrain/terrCell.h"
  30. #include "terrain/terrRender.h"
  31. #include "terrain/terrMaterial.h"
  32. #include "terrain/terrCellMaterial.h"
  33. #include "gui/worldEditor/terrainEditor.h"
  34. #include "math/mathIO.h"
  35. #include "core/stream/fileStream.h"
  36. #include "core/stream/bitStream.h"
  37. #include "console/consoleTypes.h"
  38. #include "sim/netConnection.h"
  39. #include "core/util/safeDelete.h"
  40. #include "T3D/objectTypes.h"
  41. #include "renderInstance/renderPassManager.h"
  42. #include "scene/sceneRenderState.h"
  43. #include "materials/materialManager.h"
  44. #include "materials/baseMatInstance.h"
  45. #include "gfx/gfxTextureManager.h"
  46. #include "gfx/gfxCardProfile.h"
  47. #include "gfx/gfxAPI.h"
  48. #include "core/resourceManager.h"
  49. #include "T3D/physics/physicsPlugin.h"
  50. #include "T3D/physics/physicsBody.h"
  51. #include "T3D/physics/physicsCollision.h"
  52. #include "console/engineAPI.h"
  53. #include "core/util/safeRelease.h"
  54. #include "T3D/assets/TerrainMaterialAsset.h"
  55. using namespace Torque;
  56. IMPLEMENT_CO_NETOBJECT_V1(TerrainBlock);
  57. ConsoleDocClass( TerrainBlock,
  58. "@brief Represent a terrain object in a Torque 3D level\n\n"
  59. "@tsexample\n"
  60. "new TerrainBlock(theTerrain)\n"
  61. "{\n"
  62. " terrainFile = \"art/terrains/Deathball Desert_0.ter\";\n"
  63. " squareSize = \"2\";\n"
  64. " tile = \"0\";\n"
  65. " baseTexSize = \"1024\";\n"
  66. " screenError = \"16\";\n"
  67. " position = \"-1024 -1024 179.978\";\n"
  68. " rotation = \"1 0 0 0\";\n"
  69. " scale = \"1 1 1\";\n"
  70. " isRenderEnabled = \"true\";\n"
  71. " canSaveDynamicFields = \"1\";\n"
  72. "};\n"
  73. "@endtsexample\n\n"
  74. "@see TerrainMaterial\n\n"
  75. "@ingroup Terrain\n"
  76. );
  77. Signal<void(U32,TerrainBlock*,const Point2I& ,const Point2I&)> TerrainBlock::smUpdateSignal;
  78. F32 TerrainBlock::smLODScale = 1.0f;
  79. F32 TerrainBlock::smDetailScale = 1.0f;
  80. //RBP - Global function declared in Terrdata.h
  81. TerrainBlock* getTerrainUnderWorldPoint(const Point3F & wPos)
  82. {
  83. // Cast a ray straight down from the world position and see which
  84. // Terrain is the closest to our starting point
  85. Point3F startPnt = wPos;
  86. Point3F endPnt = wPos + Point3F(0.0f, 0.0f, -10000.0f);
  87. S32 blockIndex = -1;
  88. F32 nearT = 1.0f;
  89. SimpleQueryList queryList;
  90. gServerContainer.findObjects( TerrainObjectType, SimpleQueryList::insertionCallback, &queryList);
  91. for (U32 i = 0; i < queryList.mList.size(); i++)
  92. {
  93. Point3F tStartPnt, tEndPnt;
  94. TerrainBlock* terrBlock = dynamic_cast<TerrainBlock*>(queryList.mList[i]);
  95. terrBlock->getWorldTransform().mulP(startPnt, &tStartPnt);
  96. terrBlock->getWorldTransform().mulP(endPnt, &tEndPnt);
  97. RayInfo ri;
  98. if (terrBlock->castRayI(tStartPnt, tEndPnt, &ri, true))
  99. {
  100. if (ri.t < nearT)
  101. {
  102. blockIndex = i;
  103. nearT = ri.t;
  104. }
  105. }
  106. }
  107. if (blockIndex > -1)
  108. return (TerrainBlock*)(queryList.mList[blockIndex]);
  109. return NULL;
  110. }
  111. ConsoleDocFragment _getTerrainUnderWorldPoint1(
  112. "@brief Gets the terrain block that is located under the given world point\n\n"
  113. "@param position The world space coordinate you wish to query at. Formatted as (\"x y z\")\n\n"
  114. "@return Returns the ID of the requested terrain block (0 if not found).\n\n"
  115. "@ingroup Terrain",
  116. NULL,
  117. "bool getTerrainUnderWorldPoint( Point3F position );"
  118. );
  119. ConsoleDocFragment _getTerrainUnderWorldPoint2(
  120. "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n"
  121. "@param x The X coordinate in world space\n"
  122. "@param y The Y coordinate in world space\n\n"
  123. "@param z The Z coordinate in world space\n\n"
  124. "@return Returns the ID of the requested terrain block (0 if not found).\n\n"
  125. "@ingroup Terrain",
  126. NULL,
  127. "bool getTerrainUnderWorldPoint( F32 x, F32 y, F32 z);"
  128. );
  129. DefineEngineFunction( getTerrainUnderWorldPoint, S32, (const char* ptOrX, const char* y, const char* z), ("", ""),
  130. "(Point3F x/y/z) Gets the terrain block that is located under the given world point.\n"
  131. "@param x/y/z The world coordinates (floating point values) you wish to query at. "
  132. "These can be formatted as either a string (\"x y z\") or separately as (x, y, z)\n"
  133. "@return Returns the ID of the requested terrain block (0 if not found).\n\n"
  134. "@hide")
  135. {
  136. Point3F pos;
  137. if(!String::isEmpty(ptOrX) && String::isEmpty(y) && String::isEmpty(z))
  138. dSscanf(ptOrX, "%f %f %f", &pos.x, &pos.y, &pos.z);
  139. else if(!String::isEmpty(ptOrX) && !String::isEmpty(y) && !String::isEmpty(z))
  140. {
  141. pos.x = dAtof(ptOrX);
  142. pos.y = dAtof(y);
  143. pos.z = dAtof(z);
  144. }
  145. TerrainBlock* terrain = getTerrainUnderWorldPoint(pos);
  146. if(terrain != NULL)
  147. {
  148. return terrain->getId();
  149. }
  150. return 0;
  151. }
  152. typedef TerrainBlock::BaseTexFormat baseTexFormat;
  153. DefineEnumType(baseTexFormat);
  154. ImplementEnumType(baseTexFormat,
  155. "Description\n"
  156. "@ingroup ?\n\n")
  157. { TerrainBlock::NONE, "NONE", "No cached terrain.\n" },
  158. { TerrainBlock::DDS, "DDS", "Cache the terrain in a DDS format.\n" },
  159. { TerrainBlock::PNG, "PNG", "Cache the terrain in a PNG format.\n" },
  160. EndImplementEnumType;
  161. TerrainBlock::TerrainBlock()
  162. : mLightMap( NULL ),
  163. mLightMapSize( 256 ),
  164. mCRC( 0 ),
  165. mMaxDetailDistance( 0.0f ),
  166. mBaseTexScaleConst( NULL ),
  167. mBaseTexIdConst( NULL ),
  168. mBaseLayerSizeConst(NULL),
  169. mDetailsDirty( false ),
  170. mLayerTexDirty( false ),
  171. mBaseTexSize( 1024 ),
  172. mBaseTexFormat( TerrainBlock::DDS ),
  173. mDetailTexSize(0),
  174. mDetailTexFormat(GFXFormat_COUNT),
  175. mMacroTexSize(0),
  176. mMacroTexFormat(GFXFormat_COUNT),
  177. mNormalTexSize(0),
  178. mNormalTexFormat(GFXFormat_COUNT),
  179. mOrmTexSize(0),
  180. mOrmTexFormat(GFXFormat_COUNT),
  181. mCell( NULL ),
  182. mBaseMaterial( NULL ),
  183. mDefaultMatInst( NULL ),
  184. mSquareSize( 1.0f ),
  185. mPhysicsRep( NULL ),
  186. mScreenError( 16 ),
  187. mCastShadows( true ),
  188. mZoningDirty( false ),
  189. mUpdateBasetex ( true ),
  190. mDetailTextureArray( NULL ),
  191. mMacroTextureArray( NULL ),
  192. mOrmTextureArray( NULL ),
  193. mNormalTextureArray( NULL )
  194. {
  195. mTypeMask = TerrainObjectType | StaticObjectType | StaticShapeObjectType;
  196. mNetFlags.set(Ghostable | ScopeAlways);
  197. mIgnoreZodiacs = false;
  198. zode_primBuffer = 0;
  199. mTerrainAsset = StringTable->EmptyString();
  200. mTerrainAssetId = StringTable->EmptyString();
  201. }
  202. extern Convex sTerrainConvexList;
  203. TerrainBlock::~TerrainBlock()
  204. {
  205. // Kill collision
  206. sTerrainConvexList.nukeList();
  207. SAFE_DELETE(mLightMap);
  208. mLightMapTex = NULL;
  209. #ifdef TORQUE_TOOLS
  210. TerrainEditor* editor = dynamic_cast<TerrainEditor*>(Sim::findObject("ETerrainEditor"));
  211. if (editor)
  212. editor->detachTerrain(this);
  213. #endif
  214. deleteZodiacPrimitiveBuffer();
  215. SAFE_RELEASE(mDetailTextureArray);
  216. SAFE_RELEASE(mMacroTextureArray);
  217. SAFE_RELEASE(mNormalTextureArray);
  218. SAFE_RELEASE(mOrmTextureArray);
  219. }
  220. void TerrainBlock::_onTextureEvent( GFXTexCallbackCode code )
  221. {
  222. if ( code == GFXZombify )
  223. {
  224. if ( mBaseTex.isValid() &&
  225. mBaseTex->isRenderTarget() )
  226. mBaseTex = NULL;
  227. mLayerTex = NULL;
  228. mLightMapTex = NULL;
  229. }
  230. }
  231. bool TerrainBlock::_setSquareSize( void *obj, const char *index, const char *data )
  232. {
  233. TerrainBlock *terrain = static_cast<TerrainBlock*>( obj );
  234. F32 newSqaureSize = dAtof( data );
  235. if ( !mIsEqual( terrain->mSquareSize, newSqaureSize ) )
  236. {
  237. terrain->mSquareSize = newSqaureSize;
  238. if ( terrain->isServerObject() && terrain->isProperlyAdded() )
  239. terrain->_updateBounds();
  240. terrain->setMaskBits( HeightMapChangeMask | SizeMask );
  241. }
  242. return false;
  243. }
  244. bool TerrainBlock::_setBaseTexSize( void *obj, const char *index, const char *data )
  245. {
  246. TerrainBlock *terrain = static_cast<TerrainBlock*>( obj );
  247. // NOTE: We're limiting the base texture size to
  248. // 2048 as anything greater in size becomes too
  249. // large to generate for many cards.
  250. //
  251. // If you want to remove this limit feel free, but
  252. // prepare for problems if you don't ship the baked
  253. // base texture with your installer.
  254. //
  255. S32 texSize = mClamp( dAtoi( data ), 0, 2048 );
  256. if ( terrain->mBaseTexSize != texSize )
  257. {
  258. terrain->mBaseTexSize = texSize;
  259. terrain->setMaskBits( MaterialMask );
  260. }
  261. return false;
  262. }
  263. bool TerrainBlock::_setBaseTexFormat(void *obj, const char *index, const char *data)
  264. {
  265. TerrainBlock *terrain = static_cast<TerrainBlock*>(obj);
  266. EngineEnumTable eTable = _baseTexFormat::_sEnumTable;
  267. for (U8 i = 0; i < eTable.getNumValues(); i++)
  268. {
  269. if (strcasecmp(eTable[i].mName, data) == 0)
  270. {
  271. terrain->mBaseTexFormat = (BaseTexFormat)eTable[i].mInt;
  272. terrain->_updateMaterials();
  273. if (terrain->isServerObject()) return false;
  274. terrain->_updateLayerTexture();
  275. // If the cached base texture is older that the terrain file or
  276. // it doesn't exist then generate and cache it.
  277. String baseCachePath = terrain->_getBaseTexCacheFileName();
  278. if (Platform::compareModifiedTimes(baseCachePath, terrain->mTerrainAsset->getTerrainFilePath()) < 0 && terrain->mUpdateBasetex)
  279. terrain->_updateBaseTexture(true);
  280. break;
  281. }
  282. }
  283. return false;
  284. }
  285. bool TerrainBlock::_setLightMapSize( void *obj, const char *index, const char *data )
  286. {
  287. TerrainBlock *terrain = static_cast<TerrainBlock*>(obj);
  288. // Handle inspector value decrements correctly
  289. U32 mapSize = dAtoi( data );
  290. if ( mapSize == terrain->mLightMapSize-1 )
  291. mapSize = terrain->mLightMapSize/2;
  292. // Limit the lightmap size, and ensure it is a power of 2
  293. const U32 maxTextureSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 1024 );
  294. mapSize = mClamp( getNextPow2( mapSize ), 0, maxTextureSize );
  295. if ( terrain->mLightMapSize != mapSize )
  296. {
  297. terrain->mLightMapSize = mapSize;
  298. terrain->setMaskBits( MaterialMask );
  299. }
  300. return false;
  301. }
  302. bool TerrainBlock::_setDetailTexSize(void* obj, const char* index, const char* data)
  303. {
  304. TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
  305. S32 size;
  306. castConsoleTypeFromString(size, data);
  307. if (terrain->mDetailTexSize != size)
  308. {
  309. terrain->mDetailTexSize = size;
  310. terrain->_updateMaterials();
  311. terrain->setMaskBits(MaterialMask);
  312. }
  313. return false;
  314. }
  315. bool TerrainBlock::_setDetailTexFormat(void* obj, const char* index, const char* data)
  316. {
  317. TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
  318. GFXFormat format;
  319. castConsoleTypeFromString(format, data);
  320. if (terrain->mDetailTexFormat != format)
  321. {
  322. terrain->mDetailTexFormat = format;
  323. terrain->_updateMaterials();
  324. terrain->setMaskBits(MaterialMask);
  325. }
  326. return false;
  327. }
  328. bool TerrainBlock::_setMacroTexSize(void* obj, const char* index, const char* data)
  329. {
  330. TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
  331. S32 size;
  332. castConsoleTypeFromString(size, data);
  333. if (terrain->mMacroTexSize != size)
  334. {
  335. terrain->mMacroTexSize = size;
  336. terrain->_updateMaterials();
  337. terrain->setMaskBits(MaterialMask);
  338. }
  339. return false;
  340. }
  341. bool TerrainBlock::_setMacroTexFormat(void* obj, const char* index, const char* data)
  342. {
  343. TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
  344. GFXFormat format;
  345. castConsoleTypeFromString(format, data);
  346. if (terrain->mMacroTexFormat != format)
  347. {
  348. terrain->mMacroTexFormat = format;
  349. terrain->_updateMaterials();
  350. terrain->setMaskBits(MaterialMask);
  351. }
  352. return false;
  353. }
  354. bool TerrainBlock::_setNormalTexSize(void* obj, const char* index, const char* data)
  355. {
  356. TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
  357. S32 size;
  358. castConsoleTypeFromString(size, data);
  359. if (terrain->mNormalTexSize != size)
  360. {
  361. terrain->mNormalTexSize = size;
  362. terrain->_updateMaterials();
  363. terrain->setMaskBits(MaterialMask);
  364. }
  365. return false;
  366. }
  367. bool TerrainBlock::_setNormalTexFormat(void* obj, const char* index, const char* data)
  368. {
  369. TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
  370. GFXFormat format;
  371. castConsoleTypeFromString(format, data);
  372. if (terrain->mNormalTexFormat != format)
  373. {
  374. terrain->mNormalTexFormat = format;
  375. terrain->_updateMaterials();
  376. terrain->setMaskBits(MaterialMask);
  377. }
  378. return false;
  379. }
  380. bool TerrainBlock::_setOrmTexSize(void* obj, const char* index, const char* data)
  381. {
  382. TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
  383. S32 size;
  384. castConsoleTypeFromString(size, data);
  385. if (terrain->mOrmTexSize != size)
  386. {
  387. terrain->mOrmTexSize = size;
  388. terrain->_updateMaterials();
  389. terrain->setMaskBits(MaterialMask);
  390. }
  391. return false;
  392. }
  393. bool TerrainBlock::_setOrmTexFormat(void* obj, const char* index, const char* data)
  394. {
  395. TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
  396. GFXFormat format;
  397. castConsoleTypeFromString(format, data);
  398. if (terrain->mOrmTexFormat != format)
  399. {
  400. terrain->mOrmTexFormat = format;
  401. terrain->_updateMaterials();
  402. terrain->setMaskBits(MaterialMask);
  403. }
  404. return false;
  405. }
  406. bool TerrainBlock::setFile( const FileName &terrFileName )
  407. {
  408. if ( mTerrainAsset && mTerrainAsset->getTerrainFilePath() == terrFileName )
  409. return mFile != NULL;
  410. Resource<TerrainFile> file = ResourceManager::get().load( terrFileName );
  411. if( !file )
  412. return false;
  413. setFile( file );
  414. setMaskBits( FileMask | HeightMapChangeMask );
  415. return true;
  416. }
  417. void TerrainBlock::setFile(const Resource<TerrainFile>& terr)
  418. {
  419. if (mFile)
  420. {
  421. GFXTextureManager::removeEventDelegate(this, &TerrainBlock::_onTextureEvent);
  422. MATMGR->getFlushSignal().remove(this, &TerrainBlock::_onFlushMaterials);
  423. }
  424. mFile = terr;
  425. if (!mFile)
  426. {
  427. Con::errorf("TerrainBlock::setFile() - No valid terrain file!");
  428. return;
  429. }
  430. if (terr->mNeedsResaving)
  431. {
  432. if (Platform::messageBox("Update Terrain File", "You appear to have a Terrain file in an older format. Do you want Torque to update it?", MBOkCancel, MIQuestion) == MROk)
  433. {
  434. mFile->save(terr->mFilePath.getFullPath());
  435. mFile->mNeedsResaving = false;
  436. }
  437. }
  438. if (terr->mFileVersion != TerrainFile::FILE_VERSION || terr->mNeedsResaving)
  439. {
  440. Con::errorf(" *********************************************************");
  441. Con::errorf(" *********************************************************");
  442. Con::errorf(" *********************************************************");
  443. Con::errorf(" PLEASE RESAVE THE TERRAIN FILE FOR THIS MISSION! THANKS!");
  444. Con::errorf(" *********************************************************");
  445. Con::errorf(" *********************************************************");
  446. Con::errorf(" *********************************************************");
  447. }
  448. _updateBounds();
  449. resetWorldBox();
  450. setRenderTransform(mObjToWorld);
  451. if (isClientObject())
  452. {
  453. if (mCRC != terr.getChecksum())
  454. {
  455. NetConnection::setLastError("Your terrain file doesn't match the version that is running on the server.");
  456. return;
  457. }
  458. clearLightMap();
  459. // Init the detail layer rendering helper.
  460. _updateMaterials();
  461. _updateLayerTexture();
  462. // If the cached base texture is older that the terrain file or
  463. // it doesn't exist then generate and cache it.
  464. String baseCachePath = _getBaseTexCacheFileName();
  465. if (Platform::compareModifiedTimes(baseCachePath, mTerrainAsset->getTerrainFilePath()) < 0 && mUpdateBasetex)
  466. _updateBaseTexture(true);
  467. // The base texture should have been cached by now... so load it.
  468. mBaseTex.set(baseCachePath, &GFXStaticTextureSRGBProfile, "TerrainBlock::mBaseTex");
  469. GFXTextureManager::addEventDelegate(this, &TerrainBlock::_onTextureEvent);
  470. MATMGR->getFlushSignal().notify(this, &TerrainBlock::_onFlushMaterials);
  471. // Build the terrain quadtree.
  472. _rebuildQuadtree();
  473. // Preload all the materials.
  474. mCell->preloadMaterials();
  475. mZoningDirty = true;
  476. SceneZoneSpaceManager::getZoningChangedSignal().notify(this, &TerrainBlock::_onZoningChanged);
  477. }
  478. else
  479. mCRC = terr.getChecksum();
  480. }
  481. bool TerrainBlock::setTerrainAsset(const StringTableEntry terrainAssetId)
  482. {
  483. if (TerrainAsset::getAssetById(terrainAssetId, &mTerrainAsset))
  484. {
  485. //Special exception case. If we've defaulted to the 'no shape' mesh, don't save it out, we'll retain the original ids/paths so it doesn't break
  486. //the TSStatic
  487. if (!mTerrainAsset.isNull())
  488. {
  489. mTerrFileName = StringTable->EmptyString();
  490. }
  491. setFile(mTerrainAsset->getTerrainResource());
  492. setMaskBits(-1);
  493. return true;
  494. }
  495. return false;
  496. }
  497. bool TerrainBlock::save(const char *filename)
  498. {
  499. return mFile->save(filename);
  500. }
  501. bool TerrainBlock::saveAsset()
  502. {
  503. if (!mTerrainAsset.isNull() && mTerrainAsset->isAssetValid())
  504. {
  505. mTerrainAsset->clearAssetDependencyFields("terrainMaterailAsset");
  506. AssetQuery* pAssetQuery = new AssetQuery();
  507. pAssetQuery->registerObject();
  508. AssetDatabase.findAssetType(pAssetQuery, "TerrainMaterialAsset");
  509. TerrainBlock* clientTerr = static_cast<TerrainBlock*>(getClientObject());
  510. for (U32 i = 0; i < pAssetQuery->mAssetList.size(); i++)
  511. {
  512. //Acquire it so we can check it for matches
  513. AssetPtr<TerrainMaterialAsset> terrMatAsset = pAssetQuery->mAssetList[i];
  514. for (U32 m = 0; m < clientTerr->mFile->mMaterials.size(); m++)
  515. {
  516. StringTableEntry intMatName = clientTerr->mFile->mMaterials[m]->getInternalName();
  517. StringTableEntry assetMatDefName = terrMatAsset->getMaterialDefinitionName();
  518. if (assetMatDefName == intMatName)
  519. {
  520. mTerrainAsset->addAssetDependencyField("terrainMaterailAsset", terrMatAsset.getAssetId());
  521. }
  522. }
  523. terrMatAsset.clear();
  524. }
  525. pAssetQuery->destroySelf();
  526. bool saveAssetSuccess = mTerrainAsset->saveAsset();
  527. if (!saveAssetSuccess)
  528. return false;
  529. return mFile->save(mTerrainAsset->getTerrainFilePath());
  530. }
  531. return false;
  532. }
  533. bool TerrainBlock::_setTerrainFile( void *obj, const char *index, const char *data )
  534. {
  535. //TerrainBlock* terrain = static_cast<TerrainBlock*>( obj )->setFile( FileName( data ) );
  536. TerrainBlock* terrain = static_cast<TerrainBlock*>(obj);
  537. StringTableEntry file = StringTable->insert(data);
  538. if (file != StringTable->EmptyString())
  539. {
  540. StringTableEntry assetId = TerrainAsset::getAssetIdByFilename(file);
  541. if (assetId != StringTable->EmptyString())
  542. {
  543. if (terrain->setTerrainAsset(assetId))
  544. {
  545. terrain->mTerrainAssetId = assetId;
  546. terrain->mTerrFileName = StringTable->EmptyString();
  547. return false;
  548. }
  549. }
  550. else
  551. {
  552. terrain->mTerrainAsset = StringTable->EmptyString();
  553. }
  554. }
  555. return true;
  556. }
  557. bool TerrainBlock::_setTerrainAsset(void* obj, const char* index, const char* data)
  558. {
  559. TerrainBlock* terr = static_cast<TerrainBlock*>(obj);// ->setFile(FileName(data));
  560. terr->mTerrainAssetId = StringTable->insert(data);
  561. return terr->setTerrainAsset(terr->mTerrainAssetId);
  562. }
  563. void TerrainBlock::_updateBounds()
  564. {
  565. if ( !mFile )
  566. return; // quick fix to stop crashing when deleting terrainblocks
  567. // Setup our object space bounds.
  568. mBounds.minExtents.set( 0.0f, 0.0f, 0.0f );
  569. mBounds.maxExtents.set( getWorldBlockSize(), getWorldBlockSize(), 0.0f );
  570. getMinMaxHeight( &mBounds.minExtents.z, &mBounds.maxExtents.z );
  571. // Set our mObjBox to be equal to mBounds
  572. if ( mObjBox.maxExtents != mBounds.maxExtents ||
  573. mObjBox.minExtents != mBounds.minExtents )
  574. {
  575. mObjBox = mBounds;
  576. resetWorldBox();
  577. }
  578. }
  579. void TerrainBlock::_onZoningChanged( SceneZoneSpaceManager *zoneManager )
  580. {
  581. const SceneManager* sm = getSceneManager();
  582. if (mCell == NULL || (sm != NULL && sm->getZoneManager() != NULL && zoneManager != sm->getZoneManager()))
  583. return;
  584. mZoningDirty = true;
  585. }
  586. void TerrainBlock::setHeight( const Point2I &pos, F32 height )
  587. {
  588. U16 ht = floatToFixed( height );
  589. mFile->setHeight( pos.x, pos.y, ht );
  590. // Note: We do not update the grid here as this could
  591. // be called several times in a loop. We depend on the
  592. // caller doing a grid update when he is done.
  593. }
  594. F32 TerrainBlock::getHeight( const Point2I &pos )
  595. {
  596. U16 ht = mFile->getHeight( pos.x, pos.y );
  597. return fixedToFloat( ht );
  598. }
  599. void TerrainBlock::updateGridMaterials( const Point2I &minPt, const Point2I &maxPt )
  600. {
  601. if ( mCell )
  602. {
  603. // Tell the terrain cell that something changed.
  604. const RectI gridRect( minPt, maxPt - minPt );
  605. mCell->updateGrid( gridRect, true );
  606. }
  607. // We mark us as dirty... it will be updated
  608. // before the next time we render the terrain.
  609. mLayerTexDirty = true;
  610. // Signal anyone that cares that the opacity was changed.
  611. smUpdateSignal.trigger( LayersUpdate, this, minPt, maxPt );
  612. }
  613. Point2I TerrainBlock::getGridPos( const Point3F &worldPos ) const
  614. {
  615. Point3F terrainPos = worldPos;
  616. getWorldTransform().mulP( terrainPos );
  617. F32 squareSize = ( F32 ) getSquareSize();
  618. F32 halfSquareSize = squareSize / 2.0;
  619. F32 x = ( terrainPos.x + halfSquareSize ) / squareSize;
  620. F32 y = ( terrainPos.y + halfSquareSize ) / squareSize;
  621. Point2I gridPos( ( S32 ) mFloor( x ), ( S32 ) mFloor( y ) );
  622. return gridPos;
  623. }
  624. void TerrainBlock::updateGrid( const Point2I &minPt, const Point2I &maxPt, bool updateClient )
  625. {
  626. // On the client we just signal everyone that the height
  627. // map has changed... the server does the actual changes.
  628. if ( isClientObject() )
  629. {
  630. PROFILE_SCOPE( TerrainBlock_updateGrid_Client );
  631. // This depends on the client getting this call 'after' the server.
  632. // Which is currently the case.
  633. _updateBounds();
  634. mZoningDirty = true;
  635. smUpdateSignal.trigger( HeightmapUpdate, this, minPt, maxPt );
  636. // Tell the terrain cell that the height changed.
  637. const RectI gridRect( minPt, maxPt - minPt );
  638. mCell->updateGrid( gridRect );
  639. // Rebuild the physics representation.
  640. if ( mPhysicsRep )
  641. {
  642. // Delay the update by a few milliseconds so
  643. // that we're not rebuilding during an active
  644. // editing operation.
  645. mPhysicsRep->queueCallback( 500, Delegate<void()>( this, &TerrainBlock::_updatePhysics ) );
  646. }
  647. return;
  648. }
  649. // Now on the server we rebuild the
  650. // affected area of the grid map.
  651. mFile->updateGrid( minPt, maxPt );
  652. // Fix up the bounds.
  653. _updateBounds();
  654. // Rebuild the physics representation.
  655. if ( mPhysicsRep )
  656. {
  657. // Delay the update by a few milliseconds so
  658. // that we're not rebuilding during an active
  659. // editing operation.
  660. mPhysicsRep->queueCallback( 500, Delegate<void()>( this, &TerrainBlock::_updatePhysics ) );
  661. }
  662. // Signal again here for any server side observers.
  663. smUpdateSignal.trigger( HeightmapUpdate, this, minPt, maxPt );
  664. // If this is a server object and the client update
  665. // was requested then try to use the local connection
  666. // pointer to do it.
  667. if ( updateClient && getClientObject() )
  668. ((TerrainBlock*)getClientObject())->updateGrid( minPt, maxPt, false );
  669. }
  670. bool TerrainBlock::getHeight( const Point2F &pos, F32 *height ) const
  671. {
  672. PROFILE_SCOPE( TerrainBlock_getHeight );
  673. F32 invSquareSize = 1.0f / mSquareSize;
  674. F32 xp = pos.x * invSquareSize;
  675. F32 yp = pos.y * invSquareSize;
  676. S32 x = S32(xp);
  677. S32 y = S32(yp);
  678. xp -= (F32)x;
  679. yp -= (F32)y;
  680. const U32 blockMask = mFile->mSize - 1;
  681. if ( x & ~blockMask || y & ~blockMask )
  682. return false;
  683. x &= blockMask;
  684. y &= blockMask;
  685. const TerrainSquare *sq = mFile->findSquare( 0, x, y );
  686. if ( sq->flags & TerrainSquare::Empty )
  687. return false;
  688. F32 zBottomLeft = fixedToFloat( mFile->getHeight( x, y ) );
  689. F32 zBottomRight = fixedToFloat( mFile->getHeight( x + 1, y ) );
  690. F32 zTopLeft = fixedToFloat( mFile->getHeight( x, y + 1 ) );
  691. F32 zTopRight = fixedToFloat( mFile->getHeight( x + 1, y + 1 ) );
  692. if ( sq->flags & TerrainSquare::Split45 )
  693. {
  694. if (xp>yp)
  695. // bottom half
  696. *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight);
  697. else
  698. // top half
  699. *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft);
  700. }
  701. else
  702. {
  703. if (1.0f-xp>yp)
  704. // bottom half
  705. *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft);
  706. else
  707. // top half
  708. *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight);
  709. }
  710. return true;
  711. }
  712. bool TerrainBlock::getNormal( const Point2F &pos, Point3F *normal, bool normalize, bool skipEmpty ) const
  713. {
  714. PROFILE_SCOPE( TerrainBlock_getNormal );
  715. F32 invSquareSize = 1.0f / mSquareSize;
  716. F32 xp = pos.x * invSquareSize;
  717. F32 yp = pos.y * invSquareSize;
  718. S32 x = S32(xp);
  719. S32 y = S32(yp);
  720. xp -= (F32)x;
  721. yp -= (F32)y;
  722. const U32 blockMask = mFile->mSize - 1;
  723. if ( x & ~blockMask || y & ~blockMask )
  724. return false;
  725. x &= blockMask;
  726. y &= blockMask;
  727. const TerrainSquare *sq = mFile->findSquare( 0, x, y );
  728. if ( skipEmpty && sq->flags & TerrainSquare::Empty )
  729. return false;
  730. F32 zBottomLeft = fixedToFloat( mFile->getHeight( x, y ) );
  731. F32 zBottomRight = fixedToFloat( mFile->getHeight( x + 1, y ) );
  732. F32 zTopLeft = fixedToFloat( mFile->getHeight( x, y + 1 ) );
  733. F32 zTopRight = fixedToFloat( mFile->getHeight( x + 1, y + 1 ) );
  734. if ( sq->flags & TerrainSquare::Split45 )
  735. {
  736. if (xp>yp)
  737. // bottom half
  738. normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize);
  739. else
  740. // top half
  741. normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize);
  742. }
  743. else
  744. {
  745. if (1.0f-xp>yp)
  746. // bottom half
  747. normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize);
  748. else
  749. // top half
  750. normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize);
  751. }
  752. if (normalize)
  753. normal->normalize();
  754. return true;
  755. }
  756. bool TerrainBlock::getSmoothNormal( const Point2F &pos,
  757. Point3F *normal,
  758. bool normalize,
  759. bool skipEmpty ) const
  760. {
  761. PROFILE_SCOPE( TerrainBlock_getSmoothNormal );
  762. F32 invSquareSize = 1.0f / mSquareSize;
  763. F32 xp = pos.x * invSquareSize;
  764. F32 yp = pos.y * invSquareSize;
  765. S32 x = S32(xp);
  766. S32 y = S32(yp);
  767. const U32 blockMask = mFile->mSize - 1;
  768. if ( x & ~blockMask || y & ~blockMask )
  769. return false;
  770. x &= blockMask;
  771. y &= blockMask;
  772. const TerrainSquare *sq = mFile->findSquare( 0, x, y );
  773. if ( skipEmpty && sq->flags & TerrainSquare::Empty )
  774. return false;
  775. F32 h1 = fixedToFloat( mFile->getHeight( x + 1, y ) );
  776. F32 h2 = fixedToFloat( mFile->getHeight( x, y + 1 ) );
  777. F32 h3 = fixedToFloat( mFile->getHeight( x - 1, y ) );
  778. F32 h4 = fixedToFloat( mFile->getHeight( x, y - 1 ) );
  779. normal->set( h3 - h1, h4 - h2, mSquareSize * 2.0f );
  780. if ( normalize )
  781. normal->normalize();
  782. return true;
  783. }
  784. bool TerrainBlock::getNormalAndHeight( const Point2F &pos, Point3F *normal, F32 *height, bool normalize ) const
  785. {
  786. PROFILE_SCOPE( TerrainBlock_getNormalAndHeight );
  787. F32 invSquareSize = 1.0f / mSquareSize;
  788. F32 xp = pos.x * invSquareSize;
  789. F32 yp = pos.y * invSquareSize;
  790. S32 x = S32(xp);
  791. S32 y = S32(yp);
  792. xp -= (F32)x;
  793. yp -= (F32)y;
  794. const U32 blockMask = mFile->mSize - 1;
  795. if ( x & ~blockMask || y & ~blockMask )
  796. return false;
  797. x &= blockMask;
  798. y &= blockMask;
  799. const TerrainSquare *sq = mFile->findSquare( 0, x, y );
  800. if ( sq->flags & TerrainSquare::Empty )
  801. return false;
  802. F32 zBottomLeft = fixedToFloat( mFile->getHeight(x, y) );
  803. F32 zBottomRight = fixedToFloat( mFile->getHeight(x + 1, y) );
  804. F32 zTopLeft = fixedToFloat( mFile->getHeight(x, y + 1) );
  805. F32 zTopRight = fixedToFloat( mFile->getHeight(x + 1, y + 1) );
  806. if ( sq->flags & TerrainSquare::Split45 )
  807. {
  808. if (xp>yp)
  809. {
  810. // bottom half
  811. normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize);
  812. *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight);
  813. }
  814. else
  815. {
  816. // top half
  817. normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize);
  818. *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft);
  819. }
  820. }
  821. else
  822. {
  823. if (1.0f-xp>yp)
  824. {
  825. // bottom half
  826. normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize);
  827. *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft);
  828. }
  829. else
  830. {
  831. // top half
  832. normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize);
  833. *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight);
  834. }
  835. }
  836. if (normalize)
  837. normal->normalize();
  838. return true;
  839. }
  840. bool TerrainBlock::getNormalHeightMaterial( const Point2F &pos,
  841. Point3F *normal,
  842. F32 *height,
  843. StringTableEntry &matName ) const
  844. {
  845. PROFILE_SCOPE( TerrainBlock_getNormalHeightMaterial );
  846. F32 invSquareSize = 1.0f / mSquareSize;
  847. F32 xp = pos.x * invSquareSize;
  848. F32 yp = pos.y * invSquareSize;
  849. S32 x = S32(xp);
  850. S32 y = S32(yp);
  851. S32 xm = S32(mFloor( xp + 0.5f ));
  852. S32 ym = S32(mFloor( yp + 0.5f ));
  853. xp -= (F32)x;
  854. yp -= (F32)y;
  855. const U32 blockMask = mFile->mSize - 1;
  856. if ( x & ~blockMask || y & ~blockMask )
  857. return false;
  858. x &= blockMask;
  859. y &= blockMask;
  860. const TerrainSquare *sq = mFile->findSquare( 0, x, y );
  861. if ( sq->flags & TerrainSquare::Empty )
  862. return false;
  863. F32 zBottomLeft = fixedToFloat( mFile->getHeight(x, y) );
  864. F32 zBottomRight = fixedToFloat( mFile->getHeight(x + 1, y) );
  865. F32 zTopLeft = fixedToFloat( mFile->getHeight(x, y + 1) );
  866. F32 zTopRight = fixedToFloat( mFile->getHeight(x + 1, y + 1) );
  867. matName = mFile->getMaterialName( xm, ym );
  868. if ( sq->flags & TerrainSquare::Split45 )
  869. {
  870. if (xp>yp)
  871. {
  872. // bottom half
  873. normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize);
  874. *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight);
  875. }
  876. else
  877. {
  878. // top half
  879. normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize);
  880. *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft);
  881. }
  882. }
  883. else
  884. {
  885. if (1.0f-xp>yp)
  886. {
  887. // bottom half
  888. normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize);
  889. *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft);
  890. }
  891. else
  892. {
  893. // top half
  894. normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize);
  895. *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight);
  896. }
  897. }
  898. normal->normalize();
  899. return true;
  900. }
  901. U32 TerrainBlock::getMaterialCount() const
  902. {
  903. return mFile->mMaterials.size();
  904. }
  905. void TerrainBlock::addMaterial( const String &name, U32 insertAt )
  906. {
  907. TerrainMaterial *mat = TerrainMaterial::findOrCreate( name );
  908. if ( insertAt == -1 )
  909. {
  910. mFile->mMaterials.push_back( mat );
  911. mFile->_initMaterialInstMapping();
  912. bool isSrv = isServerObject();
  913. //now we update our asset
  914. if (mTerrainAsset)
  915. {
  916. StringTableEntry terrMatName = StringTable->insert(name.c_str());
  917. AssetQuery* aq = new AssetQuery();
  918. U32 foundCount = AssetDatabase.findAssetType(aq, "TerrainMaterialAsset");
  919. for (U32 i = 0; i < foundCount; i++)
  920. {
  921. TerrainMaterialAsset* terrMatAsset = AssetDatabase.acquireAsset<TerrainMaterialAsset>(aq->mAssetList[i]);
  922. if (terrMatAsset && terrMatAsset->getMaterialDefinitionName() == terrMatName)
  923. {
  924. //Do iterative logic to find the next available slot and write to it with our new mat field
  925. mTerrainAsset->setDataField(StringTable->insert("terrainMaterialAsset"), nullptr, aq->mAssetList[i]);
  926. }
  927. }
  928. }
  929. }
  930. else
  931. {
  932. // TODO: Insert and reindex!
  933. }
  934. mDetailsDirty = true;
  935. mLayerTexDirty = true;
  936. }
  937. void TerrainBlock::removeMaterial( U32 index )
  938. {
  939. // Cannot delete if only one layer.
  940. if ( mFile->mMaterials.size() == 1 )
  941. return;
  942. mFile->mMaterials.erase( index );
  943. mFile->_initMaterialInstMapping();
  944. for ( S32 i = 0; i < mFile->mLayerMap.size(); i++ )
  945. {
  946. if ( mFile->mLayerMap[i] >= index &&
  947. mFile->mLayerMap[i] != 0 )
  948. {
  949. mFile->mLayerMap[i]--;
  950. }
  951. }
  952. mDetailsDirty = true;
  953. mLayerTexDirty = true;
  954. }
  955. void TerrainBlock::updateMaterial( U32 index, const String &name )
  956. {
  957. if ( index >= mFile->mMaterials.size() )
  958. return;
  959. mFile->mMaterials[ index ] = TerrainMaterial::findOrCreate( name );
  960. mFile->_initMaterialInstMapping();
  961. mDetailsDirty = true;
  962. mLayerTexDirty = true;
  963. }
  964. TerrainMaterial* TerrainBlock::getMaterial( U32 index ) const
  965. {
  966. if ( index >= mFile->mMaterials.size() )
  967. return NULL;
  968. return mFile->mMaterials[ index ];
  969. }
  970. void TerrainBlock::deleteAllMaterials()
  971. {
  972. mFile->mMaterials.clear();
  973. mFile->mMaterialInstMapping.clearMatInstList();
  974. }
  975. const char* TerrainBlock::getMaterialName( U32 index ) const
  976. {
  977. if ( index < mFile->mMaterials.size() )
  978. return mFile->mMaterials[ index ]->getInternalName();
  979. return NULL;
  980. }
  981. void TerrainBlock::setLightMap( GBitmap *newLightMap )
  982. {
  983. SAFE_DELETE( mLightMap );
  984. mLightMap = newLightMap;
  985. mLightMapTex = NULL;
  986. }
  987. void TerrainBlock::clearLightMap()
  988. {
  989. if ( !mLightMap )
  990. mLightMap = new GBitmap( mLightMapSize, mLightMapSize, 0, GFXFormatR8G8B8 );
  991. mLightMap->fillWhite();
  992. mLightMapTex = NULL;
  993. }
  994. GFXTextureObject* TerrainBlock::getLightMapTex()
  995. {
  996. if ( mLightMapTex.isNull() && mLightMap )
  997. {
  998. mLightMapTex.set( mLightMap,
  999. &GFXStaticTextureProfile,
  1000. false,
  1001. "TerrainBlock::getLightMapTex()" );
  1002. }
  1003. return mLightMapTex;
  1004. }
  1005. void TerrainBlock::onEditorEnable()
  1006. {
  1007. }
  1008. void TerrainBlock::onEditorDisable()
  1009. {
  1010. }
  1011. bool TerrainBlock::onAdd()
  1012. {
  1013. if(!Parent::onAdd())
  1014. return false;
  1015. Resource<TerrainFile> terr;
  1016. if (!mTerrainAsset.isNull())
  1017. {
  1018. terr = mTerrainAsset->getTerrainResource();
  1019. if (terr == NULL)
  1020. {
  1021. if (isClientObject())
  1022. NetConnection::setLastError("Unable to load terrain asset: %s", mTerrainAsset.getAssetId());
  1023. return false;
  1024. }
  1025. setFile(terr);
  1026. }
  1027. addToScene();
  1028. _updatePhysics();
  1029. return true;
  1030. }
  1031. String TerrainBlock::_getBaseTexCacheFileName() const
  1032. {
  1033. Torque::Path basePath( mTerrainAsset->getTerrainFilePath() );
  1034. basePath.setFileName( basePath.getFileName() + "_basetex" );
  1035. basePath.setExtension( formatToExtension(mBaseTexFormat) );
  1036. return basePath.getFullPath();
  1037. }
  1038. void TerrainBlock::_rebuildQuadtree()
  1039. {
  1040. SAFE_DELETE( mCell );
  1041. // Recursively build the cells.
  1042. mCell = TerrCell::init( this );
  1043. // Build the shared PrimitiveBuffer.
  1044. mCell->createPrimBuffer( &mPrimBuffer );
  1045. deleteZodiacPrimitiveBuffer();
  1046. }
  1047. void TerrainBlock::_updatePhysics()
  1048. {
  1049. if ( !PHYSICSMGR )
  1050. return;
  1051. SAFE_DELETE( mPhysicsRep );
  1052. PhysicsCollision *colShape;
  1053. // If we can steal the collision shape from the local server
  1054. // object then do so as it saves us alot of cpu time and memory.
  1055. //
  1056. // TODO: We should move this sharing down into TerrFile where
  1057. // it probably belongs.
  1058. //
  1059. if ( getServerObject() )
  1060. {
  1061. TerrainBlock *serverTerrain = (TerrainBlock*)getServerObject();
  1062. colShape = serverTerrain->mPhysicsRep->getColShape();
  1063. }
  1064. else
  1065. {
  1066. // Get empty state of each vert
  1067. bool *holes = new bool[ getBlockSize() * getBlockSize() ];
  1068. for ( U32 row = 0; row < getBlockSize(); row++ )
  1069. for ( U32 column = 0; column < getBlockSize(); column++ )
  1070. holes[ row + (column * getBlockSize()) ] = mFile->isEmptyAt( row, column );
  1071. colShape = PHYSICSMGR->createCollision();
  1072. colShape->addHeightfield( mFile->getHeightMap().address(), holes, getBlockSize(), mSquareSize, MatrixF::Identity );
  1073. delete [] holes;
  1074. }
  1075. PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
  1076. mPhysicsRep = PHYSICSMGR->createBody();
  1077. mPhysicsRep->init( colShape, 0, 0, this, world );
  1078. mPhysicsRep->setTransform( getTransform() );
  1079. }
  1080. void TerrainBlock::onRemove()
  1081. {
  1082. removeFromScene();
  1083. SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &TerrainBlock::_onZoningChanged );
  1084. SAFE_DELETE( mPhysicsRep );
  1085. if ( isClientObject() )
  1086. {
  1087. mBaseTex = NULL;
  1088. mLayerTex = NULL;
  1089. SAFE_DELETE( mBaseMaterial );
  1090. SAFE_DELETE( mDefaultMatInst );
  1091. SAFE_DELETE( mCell );
  1092. mPrimBuffer = NULL;
  1093. mBaseShader = NULL;
  1094. GFXTextureManager::removeEventDelegate( this, &TerrainBlock::_onTextureEvent );
  1095. MATMGR->getFlushSignal().remove( this, &TerrainBlock::_onFlushMaterials );
  1096. }
  1097. Parent::onRemove();
  1098. }
  1099. void TerrainBlock::prepRenderImage( SceneRenderState* state )
  1100. {
  1101. PROFILE_SCOPE(TerrainBlock_prepRenderImage);
  1102. // If we need to update our cached
  1103. // zone state then do it now.
  1104. if ( mZoningDirty )
  1105. {
  1106. mZoningDirty = false;
  1107. mCell->updateZoning( getSceneManager()->getZoneManager() );
  1108. }
  1109. _renderBlock( state );
  1110. }
  1111. void TerrainBlock::setTransform(const MatrixF & mat)
  1112. {
  1113. Parent::setTransform( mat );
  1114. // Update world-space OBBs.
  1115. if( mCell )
  1116. {
  1117. mCell->updateOBBs();
  1118. mZoningDirty = true;
  1119. }
  1120. if ( mPhysicsRep )
  1121. mPhysicsRep->setTransform( mat );
  1122. setRenderTransform( mat );
  1123. setMaskBits( TransformMask );
  1124. if(isClientObject())
  1125. smUpdateSignal.trigger( HeightmapUpdate, this, Point2I::Zero, Point2I::Max );
  1126. }
  1127. void TerrainBlock::setScale( const VectorF &scale )
  1128. {
  1129. // We disable scaling... we never scale!
  1130. Parent::setScale( VectorF::One );
  1131. }
  1132. void TerrainBlock::initPersistFields()
  1133. {
  1134. addGroup( "Media" );
  1135. addProtectedField("terrainAsset", TypeTerrainAssetId, Offset(mTerrainAssetId, TerrainBlock),
  1136. &TerrainBlock::_setTerrainAsset, &defaultProtectedGetFn,
  1137. "The source terrain data asset.");
  1138. addProtectedField( "terrainFile", TypeStringFilename, Offset( mTerrFileName, TerrainBlock ),
  1139. &TerrainBlock::_setTerrainFile, &defaultProtectedGetFn,
  1140. "The source terrain data file." );
  1141. endGroup( "Media" );
  1142. addGroup( "Misc" );
  1143. addField( "castShadows", TypeBool, Offset( mCastShadows, TerrainBlock ),
  1144. "Allows the terrain to cast shadows onto itself and other objects.");
  1145. addProtectedField( "squareSize", TypeF32, Offset( mSquareSize, TerrainBlock ),
  1146. &TerrainBlock::_setSquareSize, &defaultProtectedGetFn,
  1147. "Indicates the spacing between points on the XY plane on the terrain." );
  1148. addProtectedField( "baseTexSize", TypeS32, Offset( mBaseTexSize, TerrainBlock ),
  1149. &TerrainBlock::_setBaseTexSize, &defaultProtectedGetFn,
  1150. "Size of base texture size per meter." );
  1151. addProtectedField("baseTexFormat", TYPEID<baseTexFormat>(), Offset(mBaseTexFormat, TerrainBlock),
  1152. &TerrainBlock::_setBaseTexFormat, &defaultProtectedGetFn,
  1153. "");
  1154. addProtectedField( "lightMapSize", TypeS32, Offset( mLightMapSize, TerrainBlock ),
  1155. &TerrainBlock::_setLightMapSize, &defaultProtectedGetFn,
  1156. "Light map dimensions in pixels." );
  1157. addProtectedField("detailTexSize", TypeS32, Offset(mDetailTexSize, TerrainBlock),
  1158. &TerrainBlock::_setDetailTexSize, &defaultProtectedGetFn,
  1159. "");
  1160. addProtectedField("detailTexFormat", TypeGFXFormat, Offset(mDetailTexFormat, TerrainBlock),
  1161. &TerrainBlock::_setDetailTexFormat, &defaultProtectedGetFn,
  1162. "");
  1163. addProtectedField("macroTexSize", TypeS32, Offset(mMacroTexSize, TerrainBlock),
  1164. &TerrainBlock::_setMacroTexSize, &defaultProtectedGetFn,
  1165. "");
  1166. addProtectedField("macroTexFormat", TypeGFXFormat, Offset(mMacroTexFormat, TerrainBlock),
  1167. &TerrainBlock::_setMacroTexFormat, &defaultProtectedGetFn,
  1168. "");
  1169. addProtectedField("normalTexSize", TypeS32, Offset(mNormalTexSize, TerrainBlock),
  1170. &TerrainBlock::_setNormalTexSize, &defaultProtectedGetFn,
  1171. "");
  1172. addProtectedField("normalTexFormat", TypeGFXFormat, Offset(mNormalTexFormat, TerrainBlock),
  1173. &TerrainBlock::_setNormalTexFormat, &defaultProtectedGetFn,
  1174. "");
  1175. addProtectedField("ormTexSize", TypeS32, Offset(mOrmTexSize, TerrainBlock),
  1176. &TerrainBlock::_setOrmTexSize, &defaultProtectedGetFn,
  1177. "");
  1178. addProtectedField("ormTexFormat", TypeGFXFormat, Offset(mOrmTexFormat, TerrainBlock),
  1179. &TerrainBlock::_setOrmTexFormat, &defaultProtectedGetFn,
  1180. "");
  1181. addField( "screenError", TypeS32, Offset( mScreenError, TerrainBlock ), "Not yet implemented." );
  1182. addField( "updateBasetex", TypeBool, Offset( mUpdateBasetex, TerrainBlock ), "Whether or not to update the Base Texture" );
  1183. endGroup( "Misc" );
  1184. addGroup("AFX");
  1185. addField("ignoreZodiacs", TypeBool, Offset(mIgnoreZodiacs, TerrainBlock));
  1186. endGroup("AFX");
  1187. Parent::initPersistFields();
  1188. removeField( "scale" );
  1189. Con::addVariable( "$TerrainBlock::debugRender", TypeBool, &smDebugRender, "Triggers debug rendering of terrain cells\n\n"
  1190. "@ingroup Terrain");
  1191. Con::addVariable( "$pref::Terrain::lodScale", TypeF32, &smLODScale, "A global LOD scale used to tweak the default terrain screen error value.\n\n"
  1192. "@ingroup Terrain");
  1193. Con::addVariable( "$pref::Terrain::detailScale", TypeF32, &smDetailScale, "A global detail scale used to tweak the material detail distances.\n\n"
  1194. "@ingroup Terrain");
  1195. }
  1196. void TerrainBlock::inspectPostApply()
  1197. {
  1198. Parent::inspectPostApply();
  1199. setMaskBits( MiscMask );
  1200. }
  1201. U32 TerrainBlock::packUpdate(NetConnection* con, U32 mask, BitStream *stream)
  1202. {
  1203. U32 retMask = Parent::packUpdate( con, mask, stream );
  1204. if ( stream->writeFlag( mask & TransformMask ) )
  1205. mathWrite( *stream, getTransform() );
  1206. if ( stream->writeFlag( mask & FileMask ) )
  1207. {
  1208. S32 idasdasdf = getId();
  1209. stream->write(mCRC);
  1210. stream->writeString( mTerrainAsset.getAssetId() );
  1211. }
  1212. if ( stream->writeFlag( mask & SizeMask ) )
  1213. stream->write( mSquareSize );
  1214. stream->writeFlag( mCastShadows );
  1215. if ( stream->writeFlag( mask & MaterialMask ) )
  1216. {
  1217. stream->write( mBaseTexSize );
  1218. stream->write( mLightMapSize );
  1219. stream->write( mDetailTexSize );
  1220. stream->write( static_cast<S32>(mDetailTexFormat) );
  1221. stream->write( mMacroTexSize );
  1222. stream->write( static_cast<S32>(mMacroTexFormat) );
  1223. stream->write( mNormalTexSize );
  1224. stream->write( static_cast<S32>(mNormalTexFormat) );
  1225. stream->write( mOrmTexSize );
  1226. stream->write( static_cast<S32>(mOrmTexFormat) );
  1227. }
  1228. stream->writeFlag( mask & HeightMapChangeMask );
  1229. if ( stream->writeFlag( mask & MiscMask ) )
  1230. stream->write( mScreenError );
  1231. stream->writeInt(mBaseTexFormat, 32);
  1232. stream->writeFlag(mUpdateBasetex);
  1233. stream->writeFlag(mIgnoreZodiacs);
  1234. return retMask;
  1235. }
  1236. void TerrainBlock::unpackUpdate(NetConnection* con, BitStream *stream)
  1237. {
  1238. Parent::unpackUpdate( con, stream );
  1239. if ( stream->readFlag() ) // TransformMask
  1240. {
  1241. MatrixF mat;
  1242. mathRead( *stream, &mat );
  1243. setTransform( mat );
  1244. }
  1245. if ( stream->readFlag() ) // FileMask
  1246. {
  1247. stream->read(&mCRC);
  1248. char buffer[256];
  1249. stream->readString(buffer);
  1250. bool validAsset = setTerrainAsset(StringTable->insert(buffer));
  1251. }
  1252. if ( stream->readFlag() ) // SizeMask
  1253. stream->read( &mSquareSize );
  1254. mCastShadows = stream->readFlag();
  1255. if ( stream->readFlag() ) // MaterialMask
  1256. {
  1257. U32 baseTexSize;
  1258. stream->read( &baseTexSize );
  1259. if ( mBaseTexSize != baseTexSize )
  1260. {
  1261. mBaseTexSize = baseTexSize;
  1262. if ( isProperlyAdded() )
  1263. _updateBaseTexture( NONE );
  1264. }
  1265. U32 lightMapSize;
  1266. stream->read( &lightMapSize );
  1267. if ( mLightMapSize != lightMapSize )
  1268. {
  1269. mLightMapSize = lightMapSize;
  1270. if ( isProperlyAdded() )
  1271. {
  1272. SAFE_DELETE( mLightMap );
  1273. clearLightMap();
  1274. }
  1275. }
  1276. bool updateMaterials = false;
  1277. U32 detailTexSize;
  1278. stream->read(&detailTexSize);
  1279. if (mDetailTexSize != detailTexSize)
  1280. {
  1281. mDetailTexSize = detailTexSize;
  1282. updateMaterials = true;
  1283. }
  1284. S32 detailTexFormat;
  1285. stream->read(&detailTexFormat);
  1286. if (mDetailTexFormat != detailTexFormat)
  1287. {
  1288. mDetailTexFormat = static_cast<GFXFormat>(detailTexFormat);
  1289. updateMaterials = true;
  1290. }
  1291. U32 macroTexSize;
  1292. stream->read(&macroTexSize);
  1293. if (mMacroTexSize != macroTexSize)
  1294. {
  1295. mMacroTexSize = macroTexSize;
  1296. updateMaterials = true;
  1297. }
  1298. S32 macroTexFormat;
  1299. stream->read(&macroTexFormat);
  1300. if (mMacroTexFormat != macroTexFormat)
  1301. {
  1302. mMacroTexFormat = static_cast<GFXFormat>(macroTexFormat);
  1303. updateMaterials = true;
  1304. }
  1305. U32 normalTexSize;
  1306. stream->read(&normalTexSize);
  1307. if (mNormalTexSize != normalTexSize)
  1308. {
  1309. mNormalTexSize = normalTexSize;
  1310. updateMaterials = true;
  1311. }
  1312. S32 normalTexFormat;
  1313. stream->read(&normalTexFormat);
  1314. if (mNormalTexFormat != normalTexFormat)
  1315. {
  1316. mNormalTexFormat = static_cast<GFXFormat>(normalTexFormat);
  1317. updateMaterials = true;
  1318. }
  1319. U32 ormTexSize;
  1320. stream->read(&ormTexSize);
  1321. if (mOrmTexSize != ormTexSize)
  1322. {
  1323. mOrmTexSize = ormTexSize;
  1324. updateMaterials = true;
  1325. }
  1326. S32 ormTexFormat;
  1327. stream->read(&ormTexFormat);
  1328. if (mOrmTexFormat != ormTexFormat)
  1329. {
  1330. mOrmTexFormat = static_cast<GFXFormat>(ormTexFormat);
  1331. updateMaterials = true;
  1332. }
  1333. if (updateMaterials && isProperlyAdded())
  1334. {
  1335. _updateMaterials();
  1336. }
  1337. }
  1338. if ( stream->readFlag() && isProperlyAdded() ) // HeightMapChangeMask
  1339. {
  1340. _updateBounds();
  1341. _rebuildQuadtree();
  1342. _updatePhysics();
  1343. mDetailsDirty = true;
  1344. mLayerTexDirty = true;
  1345. }
  1346. if ( stream->readFlag() ) // MiscMask
  1347. stream->read( &mScreenError );
  1348. mBaseTexFormat = (BaseTexFormat)stream->readInt(32);
  1349. mUpdateBasetex = stream->readFlag();
  1350. mIgnoreZodiacs = stream->readFlag();
  1351. }
  1352. void TerrainBlock::getMinMaxHeight( F32 *minHeight, F32 *maxHeight ) const
  1353. {
  1354. // We can get the bound height from the last grid level.
  1355. const TerrainSquare *sq = mFile->findSquare( mFile->mGridLevels, 0, 0 );
  1356. *minHeight = fixedToFloat( sq->minHeight );
  1357. *maxHeight = fixedToFloat( sq->maxHeight );
  1358. }
  1359. void TerrainBlock::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
  1360. {
  1361. if (!mTerrainAsset.isNull())
  1362. usedAssetsList->push_back_unique(mTerrainAsset->getAssetId());
  1363. }
  1364. //-----------------------------------------------------------------------------
  1365. // Console Methods
  1366. //-----------------------------------------------------------------------------
  1367. DefineEngineMethod( TerrainBlock, save, bool, ( const char* fileName),,
  1368. "@brief Saves the terrain block's terrain file to the specified file name.\n\n"
  1369. "@param fileName Name and path of file to save terrain data to.\n\n"
  1370. "@return True if file save was successful, false otherwise")
  1371. {
  1372. char filename[256];
  1373. dStrcpy(filename,fileName,256);
  1374. char *ext = dStrrchr(filename, '.');
  1375. if (!ext || dStricmp(ext, ".ter") != 0)
  1376. dStrcat(filename, ".ter", 256);
  1377. return static_cast<TerrainBlock*>(object)->save(filename);
  1378. }
  1379. DefineEngineMethod(TerrainBlock, saveAsset, bool, (), ,
  1380. "@brief Saves the terrain block's terrain file to the specified file name.\n\n"
  1381. "@param fileName Name and path of file to save terrain data to.\n\n"
  1382. "@return True if file save was successful, false otherwise")
  1383. {
  1384. return static_cast<TerrainBlock*>(object)->saveAsset();
  1385. }
  1386. DefineEngineMethod( TerrainBlock, setMaterialsDirty, void, (),, "")
  1387. {
  1388. static_cast<TerrainBlock*>(object)->setMaterialsDirty();
  1389. }
  1390. //ConsoleMethod(TerrainBlock, save, bool, 3, 3, "(string fileName) - saves the terrain block's terrain file to the specified file name.")
  1391. //{
  1392. // char filename[256];
  1393. // dStrcpy(filename,argv[2],256);
  1394. // char *ext = dStrrchr(filename, '.');
  1395. // if (!ext || dStricmp(ext, ".ter") != 0)
  1396. // dStrcat(filename, ".ter", 256);
  1397. // return static_cast<TerrainBlock*>(object)->save(filename);
  1398. //}
  1399. ConsoleDocFragment _getTerrainHeight1(
  1400. "@brief Gets the terrain height at the specified position\n\n"
  1401. "@param position The world space point, minus the z (height) value. Formatted as (\"x y\")\n\n"
  1402. "@return Returns the terrain height at the given point as an F32 value.\n\n"
  1403. "@ingroup Terrain",
  1404. NULL,
  1405. "bool getTerrainHeight( Point2I position );"
  1406. );
  1407. ConsoleDocFragment _getTerrainHeight2(
  1408. "@brief Gets the terrain height at the specified position\n\n"
  1409. "@param x The X coordinate in world space\n"
  1410. "@param y The Y coordinate in world space\n\n"
  1411. "@return Returns the terrain height at the given point as an F32 value.\n\n"
  1412. "@ingroup Terrain",
  1413. NULL,
  1414. "bool getTerrainHeight( F32 x, F32 y);"
  1415. );
  1416. DefineEngineFunction( getTerrainHeight, F32, (const char* ptOrX, const char* y), (""), "(Point2 pos) - gets the terrain height at the specified position."
  1417. "@param pos The world space point, minus the z (height) value\n Can be formatted as either (\"x y\") or (x,y)\n"
  1418. "@return Returns the terrain height at the given point as an F32 value.\n"
  1419. "@hide")
  1420. {
  1421. F32 height = 0.0f;
  1422. Point2F pos;
  1423. if(!String::isEmpty(ptOrX) && String::isEmpty(y))
  1424. dSscanf(ptOrX, "%f %f", &pos.x, &pos.y);
  1425. else if(!String::isEmpty(ptOrX) && !String::isEmpty(y))
  1426. {
  1427. pos.x = dAtof(ptOrX);
  1428. pos.y = dAtof(y);
  1429. }
  1430. TerrainBlock * terrain = getTerrainUnderWorldPoint(Point3F(pos.x, pos.y, 5000.0f));
  1431. if(terrain && terrain->isServerObject())
  1432. {
  1433. Point3F offset;
  1434. terrain->getTransform().getColumn(3, &offset);
  1435. pos -= Point2F(offset.x, offset.y);
  1436. terrain->getHeight(pos, &height);
  1437. }
  1438. return height;
  1439. }
  1440. ConsoleDocFragment _getTerrainHeightBelowPosition1(
  1441. "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n"
  1442. "@param position The world space point, minus the z (height) value. Formatted as (\"x y\")\n\n"
  1443. "@return Returns the closest terrain height below the given point as an F32 value.\n\n"
  1444. "@ingroup Terrain",
  1445. NULL,
  1446. "bool getTerrainHeightBelowPosition( Point2I position );"
  1447. );
  1448. ConsoleDocFragment _getTerrainHeightBelowPosition2(
  1449. "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n"
  1450. "@param x The X coordinate in world space\n"
  1451. "@param y The Y coordinate in world space\n\n"
  1452. "@return Returns the closest terrain height below the given point as an F32 value.\n\n"
  1453. "@ingroup Terrain",
  1454. NULL,
  1455. "bool getTerrainHeightBelowPosition( F32 x, F32 y);"
  1456. );
  1457. DefineEngineFunction( getTerrainHeightBelowPosition, F32, (const char* ptOrX, const char* y, const char* z), ("", ""),
  1458. "(Point3F pos) - gets the terrain height at the specified position."
  1459. "@param pos The world space point. Can be formatted as either (\"x y z\") or (x,y,z)\n"
  1460. "@note This function is useful if you simply want to grab the terrain height underneath an object.\n"
  1461. "@return Returns the terrain height at the given point as an F32 value.\n"
  1462. "@hide")
  1463. {
  1464. F32 height = 0.0f;
  1465. Point3F pos;
  1466. if(!String::isEmpty(ptOrX) && String::isEmpty(y) && String::isEmpty(z))
  1467. dSscanf(ptOrX, "%f %f %f", &pos.x, &pos.y, &pos.z);
  1468. else if(!String::isEmpty(ptOrX) && !String::isEmpty(y) && !String::isEmpty(z))
  1469. {
  1470. pos.x = dAtof(ptOrX);
  1471. pos.y = dAtof(y);
  1472. pos.z = dAtof(z);
  1473. }
  1474. TerrainBlock * terrain = getTerrainUnderWorldPoint(pos);
  1475. Point2F nohghtPos(pos.x, pos.y);
  1476. if(terrain)
  1477. {
  1478. if(terrain->isServerObject())
  1479. {
  1480. Point3F offset;
  1481. terrain->getTransform().getColumn(3, &offset);
  1482. nohghtPos -= Point2F(offset.x, offset.y);
  1483. terrain->getHeight(nohghtPos, &height);
  1484. }
  1485. }
  1486. return height;
  1487. }
  1488. const U16* TerrainBlock::getZodiacPrimitiveBuffer()
  1489. {
  1490. if (!zode_primBuffer && !mIgnoreZodiacs)
  1491. TerrCell::createZodiacPrimBuffer(&zode_primBuffer);
  1492. return zode_primBuffer;
  1493. }
  1494. void TerrainBlock::deleteZodiacPrimitiveBuffer()
  1495. {
  1496. if (zode_primBuffer != 0)
  1497. {
  1498. delete [] zode_primBuffer;
  1499. zode_primBuffer = 0;
  1500. }
  1501. }