terrFile.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  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. #include "platform/platform.h"
  23. #include "terrain/terrFile.h"
  24. #include "core/stream/fileStream.h"
  25. #include "core/resourceManager.h"
  26. #include "terrain/terrMaterial.h"
  27. #include "gfx/gfxTextureHandle.h"
  28. #include "gfx/bitmap/gBitmap.h"
  29. #include "platform/profiler.h"
  30. #include "math/mPlane.h"
  31. template<>
  32. void* Resource<TerrainFile>::create( const Torque::Path &path )
  33. {
  34. return TerrainFile::load( path );
  35. }
  36. template<> ResourceBase::Signature Resource<TerrainFile>::signature()
  37. {
  38. return MakeFourCC('t','e','r','d');
  39. }
  40. TerrainFile::TerrainFile()
  41. : mSize( 256 ),
  42. mGridLevels(0),
  43. mFileVersion( FILE_VERSION ),
  44. mNeedsResaving( false )
  45. {
  46. mLayerMap.setSize( mSize * mSize );
  47. dMemset( mLayerMap.address(), 0, mLayerMap.memSize() );
  48. mHeightMap.setSize( mSize * mSize );
  49. dMemset( mHeightMap.address(), 0, mHeightMap.memSize() );
  50. }
  51. TerrainFile::~TerrainFile()
  52. {
  53. }
  54. static U16 calcDev( const PlaneF &pl, const Point3F &pt )
  55. {
  56. F32 z = (pl.d + pl.x * pt.x + pl.y * pt.y) / -pl.z;
  57. F32 diff = z - pt.z;
  58. if(diff < 0.0f)
  59. diff = -diff;
  60. if(diff > 0xFFFF)
  61. return 0xFFFF;
  62. else
  63. return U16(diff);
  64. }
  65. static U16 Umax( U16 u1, U16 u2 )
  66. {
  67. return u1 > u2 ? u1 : u2;
  68. }
  69. inline U32 getMostSignificantBit( U32 v )
  70. {
  71. U32 bit = 0;
  72. while ( v >>= 1 )
  73. bit++;
  74. return bit;
  75. }
  76. void TerrainFile::_buildGridMap()
  77. {
  78. // The grid level count is the same as the
  79. // most significant bit of the size. While
  80. // we loop we take the time to calculate the
  81. // grid memory pool size.
  82. mGridLevels = 0;
  83. U32 size = mSize;
  84. U32 poolSize = size * size;
  85. while ( size >>= 1 )
  86. {
  87. poolSize += size * size;
  88. mGridLevels++;
  89. }
  90. mGridMapPool.setSize( poolSize );
  91. mGridMapPool.compact();
  92. mGridMap.setSize( mGridLevels + 1 );
  93. mGridMap.compact();
  94. // Assign memory from the pool to each grid level.
  95. TerrainSquare *grid = mGridMapPool.address();
  96. for ( S32 i = mGridLevels; i >= 0; i-- )
  97. {
  98. mGridMap[i] = grid;
  99. grid += 1 << ( 2 * ( mGridLevels - i ) );
  100. }
  101. for( S32 i = mGridLevels; i >= 0; i-- )
  102. {
  103. S32 squareCount = 1 << ( mGridLevels - i );
  104. S32 squareSize = mSize / squareCount;
  105. for ( S32 squareX = 0; squareX < squareCount; squareX++ )
  106. {
  107. for ( S32 squareY = 0; squareY < squareCount; squareY++ )
  108. {
  109. U16 min = 0xFFFF;
  110. U16 max = 0;
  111. U16 mindev45 = 0;
  112. U16 mindev135 = 0;
  113. // determine max error for both possible splits.
  114. const Point3F p1(0, 0, getHeight(squareX * squareSize, squareY * squareSize));
  115. const Point3F p2(0, (F32)squareSize, getHeight(squareX * squareSize, squareY * squareSize + squareSize));
  116. const Point3F p3((F32)squareSize, (F32)squareSize, getHeight(squareX * squareSize + squareSize, squareY * squareSize + squareSize));
  117. const Point3F p4((F32)squareSize, 0, getHeight(squareX * squareSize + squareSize, squareY * squareSize));
  118. // pl1, pl2 = split45, pl3, pl4 = split135
  119. const PlaneF pl1(p1, p2, p3);
  120. const PlaneF pl2(p1, p3, p4);
  121. const PlaneF pl3(p1, p2, p4);
  122. const PlaneF pl4(p2, p3, p4);
  123. bool parentSplit45 = false;
  124. TerrainSquare *parent = NULL;
  125. if ( i < mGridLevels )
  126. {
  127. parent = findSquare( i+1, squareX * squareSize, squareY * squareSize );
  128. parentSplit45 = parent->flags & TerrainSquare::Split45;
  129. }
  130. bool empty = true;
  131. bool hasEmpty = false;
  132. for ( S32 sizeX = 0; sizeX <= squareSize; sizeX++ )
  133. {
  134. for ( S32 sizeY = 0; sizeY <= squareSize; sizeY++ )
  135. {
  136. S32 x = squareX * squareSize + sizeX;
  137. S32 y = squareY * squareSize + sizeY;
  138. if(sizeX != squareSize && sizeY != squareSize)
  139. {
  140. if ( !isEmptyAt( x, y ) )
  141. empty = false;
  142. else
  143. hasEmpty = true;
  144. }
  145. U16 ht = getHeight( x, y );
  146. if ( ht < min )
  147. min = ht;
  148. if( ht > max )
  149. max = ht;
  150. Point3F pt( (F32)sizeX, (F32)sizeY, (F32)ht );
  151. U16 dev;
  152. if(sizeX < sizeY)
  153. dev = calcDev(pl1, pt);
  154. else if(sizeX > sizeY)
  155. dev = calcDev(pl2, pt);
  156. else
  157. dev = Umax(calcDev(pl1, pt), calcDev(pl2, pt));
  158. if(dev > mindev45)
  159. mindev45 = dev;
  160. if(sizeX + sizeY < squareSize)
  161. dev = calcDev(pl3, pt);
  162. else if(sizeX + sizeY > squareSize)
  163. dev = calcDev(pl4, pt);
  164. else
  165. dev = Umax(calcDev(pl3, pt), calcDev(pl4, pt));
  166. if(dev > mindev135)
  167. mindev135 = dev;
  168. }
  169. }
  170. TerrainSquare *sq = findSquare( i, squareX * squareSize, squareY * squareSize );
  171. sq->minHeight = min;
  172. sq->maxHeight = max;
  173. sq->flags = empty ? TerrainSquare::Empty : 0;
  174. if ( hasEmpty )
  175. sq->flags |= TerrainSquare::HasEmpty;
  176. bool shouldSplit45 = ((squareX ^ squareY) & 1) == 0;
  177. bool split45;
  178. //split45 = shouldSplit45;
  179. if ( i == 0 )
  180. split45 = shouldSplit45;
  181. else if( i < 4 && shouldSplit45 == parentSplit45 )
  182. split45 = shouldSplit45;
  183. else
  184. split45 = mindev45 < mindev135;
  185. //split45 = shouldSplit45;
  186. if(split45)
  187. {
  188. sq->flags |= TerrainSquare::Split45;
  189. sq->heightDeviance = mindev45;
  190. }
  191. else
  192. sq->heightDeviance = mindev135;
  193. if( parent )
  194. if ( parent->heightDeviance < sq->heightDeviance )
  195. parent->heightDeviance = sq->heightDeviance;
  196. }
  197. }
  198. }
  199. /*
  200. for ( S32 y = 0; y < mSize; y += 2 )
  201. {
  202. for ( S32 x=0; x < mSize; x += 2 )
  203. {
  204. GridSquare *sq = findSquare(1, Point2I(x, y));
  205. GridSquare *s1 = findSquare(0, Point2I(x, y));
  206. GridSquare *s2 = findSquare(0, Point2I(x+1, y));
  207. GridSquare *s3 = findSquare(0, Point2I(x, y+1));
  208. GridSquare *s4 = findSquare(0, Point2I(x+1, y+1));
  209. sq->flags |= (s1->flags | s2->flags | s3->flags | s4->flags) & ~(GridSquare::MaterialStart -1);
  210. }
  211. }
  212. */
  213. }
  214. void TerrainFile::_initMaterialInstMapping()
  215. {
  216. mMaterialInstMapping.clearMatInstList();
  217. for( U32 i = 0; i < mMaterials.size(); ++ i )
  218. {
  219. mMaterialInstMapping.push_back(mMaterials[i]->getInternalName());
  220. }
  221. mMaterialInstMapping.mapMaterials();
  222. }
  223. bool TerrainFile::save( const char *filename )
  224. {
  225. FileStream stream;
  226. stream.open( filename, Torque::FS::File::Write );
  227. if ( stream.getStatus() != Stream::Ok )
  228. return false;
  229. stream.write( (U8)FILE_VERSION );
  230. stream.write( mSize );
  231. // Write out the height map.
  232. for ( U32 i=0; i < mHeightMap.size(); i++)
  233. stream.write( mHeightMap[i] );
  234. // Write out the layer map.
  235. for ( U32 i=0; i < mLayerMap.size(); i++)
  236. stream.write( mLayerMap[i] );
  237. // Write out the material names.
  238. stream.write( (U32)mMaterials.size() );
  239. for ( U32 i=0; i < mMaterials.size(); i++ )
  240. stream.write( String( mMaterials[i]->getInternalName() ) );
  241. return stream.getStatus() == FileStream::Ok;
  242. }
  243. TerrainFile* TerrainFile::load( const Torque::Path &path )
  244. {
  245. FileStream stream;
  246. stream.open( path.getFullPath(), Torque::FS::File::Read );
  247. if ( stream.getStatus() != Stream::Ok )
  248. {
  249. Con::errorf( "Resource<TerrainFile>::create - could not open '%s'", path.getFullPath().c_str() );
  250. return NULL;
  251. }
  252. U8 version;
  253. stream.read(&version);
  254. if (version > TerrainFile::FILE_VERSION)
  255. {
  256. Con::errorf( "Resource<TerrainFile>::create - file version '%i' is newer than engine version '%i'", version, TerrainFile::FILE_VERSION );
  257. return NULL;
  258. }
  259. TerrainFile *ret = new TerrainFile;
  260. ret->mFileVersion = version;
  261. ret->mFilePath = path;
  262. if ( version >= 7 )
  263. ret->_load( stream );
  264. else
  265. ret->_loadLegacy( stream );
  266. // Update the collision structures.
  267. ret->_buildGridMap();
  268. // Do the material mapping.
  269. ret->_initMaterialInstMapping();
  270. return ret;
  271. }
  272. void TerrainFile::_load( FileStream &stream )
  273. {
  274. // NOTE: We read using a loop instad of in one large chunk
  275. // because the stream will do endian conversions for us when
  276. // reading one type at a time.
  277. stream.read( &mSize );
  278. // Load the heightmap.
  279. mHeightMap.setSize( mSize * mSize );
  280. for ( U32 i=0; i < mHeightMap.size(); i++ )
  281. stream.read( &mHeightMap[i] );
  282. // Load the layer index map.
  283. mLayerMap.setSize( mSize * mSize );
  284. for ( U32 i=0; i < mLayerMap.size(); i++ )
  285. stream.read( &mLayerMap[i] );
  286. // Get the material name count.
  287. U32 materialCount;
  288. stream.read( &materialCount );
  289. Vector<String> materials;
  290. materials.setSize( materialCount );
  291. // Load the material names.
  292. for ( U32 i=0; i < materialCount; i++ )
  293. stream.read( &materials[i] );
  294. // Resolve the TerrainMaterial objects from the names.
  295. _resolveMaterials( materials );
  296. }
  297. void TerrainFile::_loadLegacy( FileStream &stream )
  298. {
  299. // Some legacy constants.
  300. enum
  301. {
  302. MaterialGroups = 8,
  303. BlockSquareWidth = 256,
  304. };
  305. const U32 sampleCount = BlockSquareWidth * BlockSquareWidth;
  306. mSize = BlockSquareWidth;
  307. // Load the heightmap.
  308. mHeightMap.setSize( sampleCount );
  309. for ( U32 i=0; i < mHeightMap.size(); i++ )
  310. stream.read( &mHeightMap[i] );
  311. // Prior to version 7 we stored this weird material struct.
  312. const U32 MATERIAL_GROUP_MASK = 0x7;
  313. struct Material
  314. {
  315. enum Flags
  316. {
  317. Plain = 0,
  318. Rotate = 1,
  319. FlipX = 2,
  320. FlipXRotate = 3,
  321. FlipY = 4,
  322. FlipYRotate = 5,
  323. FlipXY = 6,
  324. FlipXYRotate = 7,
  325. RotateMask = 7,
  326. Empty = 8,
  327. Modified = BIT(7),
  328. // must not clobber TerrainFile::MATERIAL_GROUP_MASK bits!
  329. PersistMask = BIT(7)
  330. };
  331. U8 flags;
  332. U8 index;
  333. };
  334. // Temp locals for loading before we convert to the new
  335. // version 7+ format.
  336. U8 baseMaterialMap[sampleCount] = { 0 };
  337. U8 *materialAlphaMap[MaterialGroups] = { 0 };
  338. Material materialMap[BlockSquareWidth * BlockSquareWidth];
  339. // read the material group map and flags...
  340. dMemset(materialMap, 0, sizeof(materialMap));
  341. AssertFatal(!(Material::PersistMask & MATERIAL_GROUP_MASK),
  342. "Doh! We have flag clobberage...");
  343. for (S32 j=0; j < sampleCount; j++)
  344. {
  345. U8 val;
  346. stream.read(&val);
  347. //
  348. baseMaterialMap[j] = val & MATERIAL_GROUP_MASK;
  349. materialMap[j].flags = val & Material::PersistMask;
  350. }
  351. // Load the material names.
  352. Vector<String> materials;
  353. for ( U32 i=0; i < MaterialGroups; i++ )
  354. {
  355. String matName;
  356. stream.read( &matName );
  357. if ( matName.isEmpty() )
  358. continue;
  359. if ( mFileVersion > 3 && mFileVersion < 6 )
  360. {
  361. // Between version 3 and 5 we store the texture file names
  362. // relative to the terrain file. We restore the full path
  363. // here so that we can create a TerrainMaterial from it.
  364. materials.push_back( Torque::Path::CompressPath( mFilePath.getRoot() + mFilePath.getPath() + '/' + matName ) );
  365. }
  366. else
  367. materials.push_back( matName );
  368. }
  369. if ( mFileVersion <= 3 )
  370. {
  371. GFXTexHandle terrainMat;
  372. Torque::Path matRelPath;
  373. // Try to automatically fix up our material file names
  374. for (U32 i = 0; i < materials.size(); i++)
  375. {
  376. if ( materials[i].isEmpty() )
  377. continue;
  378. terrainMat.set( materials[i], &GFXTexturePersistentSRGBProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
  379. if ( terrainMat )
  380. continue;
  381. matRelPath = materials[i];
  382. String path = matRelPath.getPath();
  383. String::SizeType n = path.find( '/', 0, String::NoCase );
  384. if ( n != String::NPos )
  385. {
  386. matRelPath.setPath( String(Con::getVariable( "$defaultGame" )) + path.substr( n, path.length() - n ) );
  387. terrainMat.set( matRelPath, &GFXTexturePersistentSRGBProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
  388. if ( terrainMat )
  389. {
  390. materials[i] = matRelPath.getFullPath();
  391. mNeedsResaving = true;
  392. }
  393. }
  394. } // for (U32 i = 0; i < TerrainBlock::MaterialGroups; i++)
  395. } // if ( mFileVersion <= 3 )
  396. if ( mFileVersion == 1 )
  397. {
  398. for( S32 j = 0; j < sampleCount; j++ )
  399. {
  400. if ( materialAlphaMap[baseMaterialMap[j]] == NULL )
  401. {
  402. materialAlphaMap[baseMaterialMap[j]] = new U8[sampleCount];
  403. dMemset(materialAlphaMap[baseMaterialMap[j]], 0, sampleCount);
  404. }
  405. materialAlphaMap[baseMaterialMap[j]][j] = 255;
  406. }
  407. }
  408. else
  409. {
  410. for( S32 k=0; k < materials.size(); k++ )
  411. {
  412. AssertFatal(materialAlphaMap[k] == NULL, "Bad assumption. There should be no alpha map at this point...");
  413. materialAlphaMap[k] = new U8[sampleCount];
  414. stream.read(sampleCount, materialAlphaMap[k]);
  415. }
  416. }
  417. // Throw away the old texture and heightfield scripts.
  418. if ( mFileVersion >= 3 )
  419. {
  420. U32 len;
  421. stream.read(&len);
  422. char *textureScript = (char *)dMalloc(len + 1);
  423. stream.read(len, textureScript);
  424. dFree( textureScript );
  425. stream.read(&len);
  426. char *heightfieldScript = (char *)dMalloc(len + 1);
  427. stream.read(len, heightfieldScript);
  428. dFree( heightfieldScript );
  429. }
  430. // Load and throw away the old edge terrain paths.
  431. if ( mFileVersion >= 5 )
  432. {
  433. stream.readSTString(true);
  434. stream.readSTString(true);
  435. }
  436. U32 layerCount = materials.size() - 1;
  437. // Ok... time to convert all this mess to the layer index map!
  438. for ( U32 i=0; i < sampleCount; i++ )
  439. {
  440. // Find the greatest layer.
  441. U32 layer = 0;
  442. U32 lastValue = 0;
  443. for ( U32 k=0; k < MaterialGroups; k++ )
  444. {
  445. if ( materialAlphaMap[k] && materialAlphaMap[k][i] > lastValue )
  446. {
  447. layer = k;
  448. lastValue = materialAlphaMap[k][i];
  449. }
  450. }
  451. // Set the layer index.
  452. mLayerMap[i] = getMin( layer, layerCount );
  453. }
  454. // Cleanup.
  455. for ( U32 i=0; i < MaterialGroups; i++ )
  456. delete [] materialAlphaMap[i];
  457. // Force resaving on these old file versions.
  458. //mNeedsResaving = false;
  459. // Resolve the TerrainMaterial objects from the names.
  460. _resolveMaterials( materials );
  461. }
  462. void TerrainFile::_resolveMaterials( const Vector<String> &materials )
  463. {
  464. mMaterials.clear();
  465. for ( U32 i=0; i < materials.size(); i++ )
  466. mMaterials.push_back( TerrainMaterial::findOrCreate( materials[i] ) );
  467. // If we didn't get any materials then at least
  468. // add a warning material so we will render.
  469. if ( mMaterials.empty() )
  470. mMaterials.push_back( TerrainMaterial::getWarningMaterial() );
  471. }
  472. void TerrainFile::setSize( U32 newSize, bool clear )
  473. {
  474. // Make sure the resolution is a power of two.
  475. newSize = getNextPow2( newSize );
  476. //
  477. if ( clear )
  478. {
  479. mLayerMap.setSize( newSize * newSize );
  480. mLayerMap.compact();
  481. dMemset( mLayerMap.address(), 0, mLayerMap.memSize() );
  482. // Initialize the elevation to something above
  483. // zero so that we have room to excavate by default.
  484. U16 elev = floatToFixed( 512.0f );
  485. mHeightMap.setSize( newSize * newSize );
  486. mHeightMap.compact();
  487. for ( U32 i = 0; i < mHeightMap.size(); i++ )
  488. mHeightMap[i] = elev;
  489. }
  490. else
  491. {
  492. // We're resizing here!
  493. }
  494. mSize = newSize;
  495. _buildGridMap();
  496. }
  497. void TerrainFile::smooth( F32 factor, U32 steps, bool updateCollision )
  498. {
  499. const U32 blockSize = mSize * mSize;
  500. // Grab some temp buffers for our smoothing results.
  501. Vector<F32> h1, h2;
  502. h1.setSize( blockSize );
  503. h2.setSize( blockSize );
  504. // Fill the first buffer with the current heights.
  505. for ( U32 i=0; i < blockSize; i++ )
  506. h1[i] = (F32)mHeightMap[i];
  507. // factor of 0.0 = NO Smoothing
  508. // factor of 1.0 = MAX Smoothing
  509. const F32 matrixM = 1.0f - getMax(0.0f, getMin(1.0f, factor));
  510. const F32 matrixE = (1.0f-matrixM) * (1.0f/12.0f) * 2.0f;
  511. const F32 matrixC = matrixE * 0.5f;
  512. // Now loop for our interations.
  513. F32 *src = h1.address();
  514. F32 *dst = h2.address();
  515. for ( U32 s=0; s < steps; s++ )
  516. {
  517. for ( S32 y=0; y < mSize; y++ )
  518. {
  519. for ( S32 x=0; x < mSize; x++ )
  520. {
  521. F32 samples[9];
  522. S32 c = 0;
  523. for (S32 i = y-1; i < y+2; i++)
  524. for (S32 j = x-1; j < x+2; j++)
  525. {
  526. if ( i < 0 || j < 0 || i >= mSize || j >= mSize )
  527. samples[c++] = src[ x + ( y * mSize ) ];
  528. else
  529. samples[c++] = src[ j + ( i * mSize ) ];
  530. }
  531. // 0 1 2
  532. // 3 x,y 5
  533. // 6 7 8
  534. dst[ x + ( y * mSize ) ] =
  535. ((samples[0]+samples[2]+samples[6]+samples[8]) * matrixC) +
  536. ((samples[1]+samples[3]+samples[5]+samples[7]) * matrixE) +
  537. (samples[4] * matrixM);
  538. }
  539. }
  540. // Swap!
  541. F32 *tmp = dst;
  542. dst = src;
  543. src = tmp;
  544. }
  545. // Copy the results back to the height map.
  546. for ( U32 i=0; i < blockSize; i++ )
  547. mHeightMap[i] = (U16)mCeil( (F32)src[i] );
  548. if ( updateCollision )
  549. _buildGridMap();
  550. }
  551. void TerrainFile::setHeightMap( const Vector<U16> &heightmap, bool updateCollision )
  552. {
  553. AssertFatal( mHeightMap.size() == heightmap.size(), "TerrainFile::setHeightMap - Incorrect heightmap size!" );
  554. dMemcpy( mHeightMap.address(), heightmap.address(), mHeightMap.size() );
  555. if ( updateCollision )
  556. _buildGridMap();
  557. }
  558. void TerrainFile::import( const GBitmap &heightMap,
  559. F32 heightScale,
  560. const Vector<U8> &layerMap,
  561. const Vector<String> &materials,
  562. bool flipYAxis )
  563. {
  564. AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainFile::import - Height map is not square!" );
  565. AssertFatal( isPow2( heightMap.getWidth() ), "TerrainFile::import - Height map is not power of two!" );
  566. const U32 newSize = heightMap.getWidth();
  567. if ( newSize != mSize )
  568. {
  569. mHeightMap.setSize( newSize * newSize );
  570. mHeightMap.compact();
  571. mSize = newSize;
  572. }
  573. // Convert the height map to heights.
  574. U16 *oBits = mHeightMap.address();
  575. if ( heightMap.getFormat() == GFXFormatL16)
  576. {
  577. const F32 toFixedPoint = ( 1.0f / (F32)U16_MAX ) * floatToFixed( heightScale );
  578. const U16 *iBits = (const U16*)heightMap.getBits();
  579. if ( flipYAxis )
  580. {
  581. for ( U32 i = 0; i < mSize * mSize; i++ )
  582. {
  583. U16 height = convertBEndianToHost( *iBits );
  584. *oBits = (U16)mCeil( (F32)height * toFixedPoint );
  585. ++oBits;
  586. ++iBits;
  587. }
  588. }
  589. else
  590. {
  591. for(S32 y = mSize - 1; y >= 0; y--) {
  592. for(U32 x = 0; x < mSize; x++) {
  593. U16 height = convertBEndianToHost( *iBits );
  594. mHeightMap[x + y * mSize] = (U16)mCeil( (F32)height * toFixedPoint );
  595. ++iBits;
  596. }
  597. }
  598. }
  599. }
  600. else
  601. {
  602. const F32 toFixedPoint = ( 1.0f / (F32)U8_MAX ) * floatToFixed( heightScale );
  603. const U8 *iBits = heightMap.getBits();
  604. if ( flipYAxis )
  605. {
  606. for ( U32 i = 0; i < mSize * mSize; i++ )
  607. {
  608. *oBits = (U16)mCeil( ((F32)*iBits) * toFixedPoint );
  609. ++oBits;
  610. iBits += heightMap.getBytesPerPixel();
  611. }
  612. }
  613. else
  614. {
  615. for(S32 y = mSize - 1; y >= 0; y--) {
  616. for(U32 x = 0; x < mSize; x++) {
  617. mHeightMap[x + y * mSize] = (U16)mCeil( ((F32)*iBits) * toFixedPoint );
  618. iBits += heightMap.getBytesPerPixel();
  619. }
  620. }
  621. }
  622. }
  623. // Copy over the layer map.
  624. AssertFatal( layerMap.size() == mHeightMap.size(), "TerrainFile::import - Layer map is the wrong size!" );
  625. mLayerMap = layerMap;
  626. mLayerMap.compact();
  627. // Resolve the materials.
  628. _resolveMaterials( materials );
  629. // Rebuild the collision grid map.
  630. _buildGridMap();
  631. }
  632. void TerrainFile::create( String *inOutFilename,
  633. U32 newSize,
  634. const Vector<String> &materials )
  635. {
  636. // Determine the path and basename
  637. Torque::Path basePath( *inOutFilename );
  638. if ( !basePath.getExtension().equal("ter") )
  639. {
  640. // Use the default path and filename
  641. String terrainDirectory( Con::getVariable( "$pref::Directories::Terrain" ) );
  642. if ( terrainDirectory.isEmpty() )
  643. {
  644. terrainDirectory = "data/terrains";
  645. }
  646. basePath.setPath( terrainDirectory );
  647. basePath.setFileName( "terrain" );
  648. }
  649. // Construct a default file name
  650. (*inOutFilename) = Torque::FS::MakeUniquePath( basePath.getRootAndPath(), basePath.getFileName(), "ter" );
  651. // Create the file
  652. TerrainFile *file = new TerrainFile;
  653. for ( U32 i=0; i < materials.size(); i++ )
  654. file->mMaterials.push_back( TerrainMaterial::findOrCreate( materials[i] ) );
  655. file->setSize( newSize, true );
  656. file->save( *inOutFilename );
  657. delete file;
  658. }
  659. inline void getMinMax( U16 &inMin, U16 &inMax, U16 height )
  660. {
  661. if ( height < inMin )
  662. inMin = height;
  663. if ( height > inMax )
  664. inMax = height;
  665. }
  666. inline void checkSquare( TerrainSquare *parent, const TerrainSquare *child )
  667. {
  668. if(parent->minHeight > child->minHeight)
  669. parent->minHeight = child->minHeight;
  670. if(parent->maxHeight < child->maxHeight)
  671. parent->maxHeight = child->maxHeight;
  672. if ( child->flags & (TerrainSquare::Empty | TerrainSquare::HasEmpty) )
  673. parent->flags |= TerrainSquare::HasEmpty;
  674. }
  675. void TerrainFile::updateGrid( const Point2I &minPt, const Point2I &maxPt )
  676. {
  677. // here's how it works:
  678. // for the current terrain renderer we only care about
  679. // the minHeight and maxHeight on the GridSquare
  680. // so we do one pass through, updating minHeight and maxHeight
  681. // on the level 0 squares, then we loop up the grid map from 1 to
  682. // the top, expanding the bounding boxes as necessary.
  683. // this should end up being way, way, way, way faster for the terrain
  684. // editor
  685. PROFILE_SCOPE( TerrainFile_UpdateGrid );
  686. for ( S32 y = minPt.y - 1; y < maxPt.y + 1; y++ )
  687. {
  688. for ( S32 x = minPt.x - 1; x < maxPt.x + 1; x++ )
  689. {
  690. S32 px = x;
  691. S32 py = y;
  692. if ( px < 0 )
  693. px += mSize;
  694. if ( py < 0 )
  695. py += mSize;
  696. TerrainSquare *sq = findSquare( 0, px, py );
  697. sq->minHeight = 0xFFFF;
  698. sq->maxHeight = 0;
  699. // Update the empty state.
  700. if ( isEmptyAt( x, y ) )
  701. sq->flags |= TerrainSquare::Empty;
  702. else
  703. sq->flags &= ~TerrainSquare::Empty;
  704. getMinMax( sq->minHeight, sq->maxHeight, getHeight( x, y ) );
  705. getMinMax( sq->minHeight, sq->maxHeight, getHeight( x+1, y ) );
  706. getMinMax( sq->minHeight, sq->maxHeight, getHeight( x, y+1 ) );
  707. getMinMax( sq->minHeight, sq->maxHeight, getHeight( x+1, y+1 ) );
  708. }
  709. }
  710. // ok, all the level 0 grid squares are updated:
  711. // now update all the parent grid squares that need to be updated:
  712. for( S32 level = 1; level <= mGridLevels; level++ )
  713. {
  714. S32 size = 1 << level;
  715. S32 halfSize = size >> 1;
  716. for( S32 y = (minPt.y - 1) >> level; y < (maxPt.y + size) >> level; y++ )
  717. {
  718. for ( S32 x = (minPt.x - 1) >> level; x < (maxPt.x + size) >> level; x++ )
  719. {
  720. S32 px = x << level;
  721. S32 py = y << level;
  722. TerrainSquare *sq = findSquare(level, px, py);
  723. sq->minHeight = 0xFFFF;
  724. sq->maxHeight = 0;
  725. sq->flags &= ~( TerrainSquare::Empty | TerrainSquare::HasEmpty );
  726. checkSquare( sq, findSquare( level - 1, px, py ) );
  727. checkSquare( sq, findSquare( level - 1, px + halfSize, py ) );
  728. checkSquare( sq, findSquare( level - 1, px, py + halfSize ) );
  729. checkSquare( sq, findSquare( level - 1, px + halfSize, py + halfSize ) );
  730. }
  731. }
  732. }
  733. }