terrFile.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  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. Torque::Path path( mMaterials[ i ]->getDiffuseMap());
  220. mMaterialInstMapping.push_back( path.getFileName() );
  221. }
  222. mMaterialInstMapping.mapMaterials();
  223. }
  224. bool TerrainFile::save( const char *filename )
  225. {
  226. FileStream stream;
  227. stream.open( filename, Torque::FS::File::Write );
  228. if ( stream.getStatus() != Stream::Ok )
  229. return false;
  230. stream.write( (U8)FILE_VERSION );
  231. stream.write( mSize );
  232. // Write out the height map.
  233. for ( U32 i=0; i < mHeightMap.size(); i++)
  234. stream.write( mHeightMap[i] );
  235. // Write out the layer map.
  236. for ( U32 i=0; i < mLayerMap.size(); i++)
  237. stream.write( mLayerMap[i] );
  238. // Write out the material names.
  239. stream.write( (U32)mMaterials.size() );
  240. for ( U32 i=0; i < mMaterials.size(); i++ )
  241. stream.write( String( mMaterials[i]->getInternalName() ) );
  242. return stream.getStatus() == FileStream::Ok;
  243. }
  244. TerrainFile* TerrainFile::load( const Torque::Path &path )
  245. {
  246. FileStream stream;
  247. stream.open( path.getFullPath(), Torque::FS::File::Read );
  248. if ( stream.getStatus() != Stream::Ok )
  249. {
  250. Con::errorf( "Resource<TerrainFile>::create - could not open '%s'", path.getFullPath().c_str() );
  251. return NULL;
  252. }
  253. U8 version;
  254. stream.read(&version);
  255. if (version > TerrainFile::FILE_VERSION)
  256. {
  257. Con::errorf( "Resource<TerrainFile>::create - file version '%i' is newer than engine version '%i'", version, TerrainFile::FILE_VERSION );
  258. return NULL;
  259. }
  260. TerrainFile *ret = new TerrainFile;
  261. ret->mFileVersion = version;
  262. ret->mFilePath = path;
  263. if ( version >= 7 )
  264. ret->_load( stream );
  265. else
  266. ret->_loadLegacy( stream );
  267. // Update the collision structures.
  268. ret->_buildGridMap();
  269. // Do the material mapping.
  270. ret->_initMaterialInstMapping();
  271. return ret;
  272. }
  273. void TerrainFile::_load( FileStream &stream )
  274. {
  275. // NOTE: We read using a loop instad of in one large chunk
  276. // because the stream will do endian conversions for us when
  277. // reading one type at a time.
  278. stream.read( &mSize );
  279. // Load the heightmap.
  280. mHeightMap.setSize( mSize * mSize );
  281. for ( U32 i=0; i < mHeightMap.size(); i++ )
  282. stream.read( &mHeightMap[i] );
  283. // Load the layer index map.
  284. mLayerMap.setSize( mSize * mSize );
  285. for ( U32 i=0; i < mLayerMap.size(); i++ )
  286. stream.read( &mLayerMap[i] );
  287. // Get the material name count.
  288. U32 materialCount;
  289. stream.read( &materialCount );
  290. Vector<String> materials;
  291. materials.setSize( materialCount );
  292. // Load the material names.
  293. for ( U32 i=0; i < materialCount; i++ )
  294. stream.read( &materials[i] );
  295. // Resolve the TerrainMaterial objects from the names.
  296. _resolveMaterials( materials );
  297. }
  298. void TerrainFile::_loadLegacy( FileStream &stream )
  299. {
  300. // Some legacy constants.
  301. enum
  302. {
  303. MaterialGroups = 8,
  304. BlockSquareWidth = 256,
  305. };
  306. const U32 sampleCount = BlockSquareWidth * BlockSquareWidth;
  307. mSize = BlockSquareWidth;
  308. // Load the heightmap.
  309. mHeightMap.setSize( sampleCount );
  310. for ( U32 i=0; i < mHeightMap.size(); i++ )
  311. stream.read( &mHeightMap[i] );
  312. // Prior to version 7 we stored this weird material struct.
  313. const U32 MATERIAL_GROUP_MASK = 0x7;
  314. struct Material
  315. {
  316. enum Flags
  317. {
  318. Plain = 0,
  319. Rotate = 1,
  320. FlipX = 2,
  321. FlipXRotate = 3,
  322. FlipY = 4,
  323. FlipYRotate = 5,
  324. FlipXY = 6,
  325. FlipXYRotate = 7,
  326. RotateMask = 7,
  327. Empty = 8,
  328. Modified = BIT(7),
  329. // must not clobber TerrainFile::MATERIAL_GROUP_MASK bits!
  330. PersistMask = BIT(7)
  331. };
  332. U8 flags;
  333. U8 index;
  334. };
  335. // Temp locals for loading before we convert to the new
  336. // version 7+ format.
  337. U8 baseMaterialMap[sampleCount] = { 0 };
  338. U8 *materialAlphaMap[MaterialGroups] = { 0 };
  339. Material materialMap[BlockSquareWidth * BlockSquareWidth];
  340. // read the material group map and flags...
  341. dMemset(materialMap, 0, sizeof(materialMap));
  342. AssertFatal(!(Material::PersistMask & MATERIAL_GROUP_MASK),
  343. "Doh! We have flag clobberage...");
  344. for (S32 j=0; j < sampleCount; j++)
  345. {
  346. U8 val;
  347. stream.read(&val);
  348. //
  349. baseMaterialMap[j] = val & MATERIAL_GROUP_MASK;
  350. materialMap[j].flags = val & Material::PersistMask;
  351. }
  352. // Load the material names.
  353. Vector<String> materials;
  354. for ( U32 i=0; i < MaterialGroups; i++ )
  355. {
  356. String matName;
  357. stream.read( &matName );
  358. if ( matName.isEmpty() )
  359. continue;
  360. if ( mFileVersion > 3 && mFileVersion < 6 )
  361. {
  362. // Between version 3 and 5 we store the texture file names
  363. // relative to the terrain file. We restore the full path
  364. // here so that we can create a TerrainMaterial from it.
  365. materials.push_back( Torque::Path::CompressPath( mFilePath.getRoot() + mFilePath.getPath() + '/' + matName ) );
  366. }
  367. else
  368. materials.push_back( matName );
  369. }
  370. if ( mFileVersion <= 3 )
  371. {
  372. GFXTexHandle terrainMat;
  373. Torque::Path matRelPath;
  374. // Try to automatically fix up our material file names
  375. for (U32 i = 0; i < materials.size(); i++)
  376. {
  377. if ( materials[i].isEmpty() )
  378. continue;
  379. terrainMat.set( materials[i], &GFXTexturePersistentSRGBProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
  380. if ( terrainMat )
  381. continue;
  382. matRelPath = materials[i];
  383. String path = matRelPath.getPath();
  384. String::SizeType n = path.find( '/', 0, String::NoCase );
  385. if ( n != String::NPos )
  386. {
  387. matRelPath.setPath( String(Con::getVariable( "$defaultGame" )) + path.substr( n, path.length() - n ) );
  388. terrainMat.set( matRelPath, &GFXTexturePersistentSRGBProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
  389. if ( terrainMat )
  390. {
  391. materials[i] = matRelPath.getFullPath();
  392. mNeedsResaving = true;
  393. }
  394. }
  395. } // for (U32 i = 0; i < TerrainBlock::MaterialGroups; i++)
  396. } // if ( mFileVersion <= 3 )
  397. if ( mFileVersion == 1 )
  398. {
  399. for( S32 j = 0; j < sampleCount; j++ )
  400. {
  401. if ( materialAlphaMap[baseMaterialMap[j]] == NULL )
  402. {
  403. materialAlphaMap[baseMaterialMap[j]] = new U8[sampleCount];
  404. dMemset(materialAlphaMap[baseMaterialMap[j]], 0, sampleCount);
  405. }
  406. materialAlphaMap[baseMaterialMap[j]][j] = 255;
  407. }
  408. }
  409. else
  410. {
  411. for( S32 k=0; k < materials.size(); k++ )
  412. {
  413. AssertFatal(materialAlphaMap[k] == NULL, "Bad assumption. There should be no alpha map at this point...");
  414. materialAlphaMap[k] = new U8[sampleCount];
  415. stream.read(sampleCount, materialAlphaMap[k]);
  416. }
  417. }
  418. // Throw away the old texture and heightfield scripts.
  419. if ( mFileVersion >= 3 )
  420. {
  421. U32 len;
  422. stream.read(&len);
  423. char *textureScript = (char *)dMalloc(len + 1);
  424. stream.read(len, textureScript);
  425. dFree( textureScript );
  426. stream.read(&len);
  427. char *heightfieldScript = (char *)dMalloc(len + 1);
  428. stream.read(len, heightfieldScript);
  429. dFree( heightfieldScript );
  430. }
  431. // Load and throw away the old edge terrain paths.
  432. if ( mFileVersion >= 5 )
  433. {
  434. stream.readSTString(true);
  435. stream.readSTString(true);
  436. }
  437. U32 layerCount = materials.size() - 1;
  438. // Ok... time to convert all this mess to the layer index map!
  439. for ( U32 i=0; i < sampleCount; i++ )
  440. {
  441. // Find the greatest layer.
  442. U32 layer = 0;
  443. U32 lastValue = 0;
  444. for ( U32 k=0; k < MaterialGroups; k++ )
  445. {
  446. if ( materialAlphaMap[k] && materialAlphaMap[k][i] > lastValue )
  447. {
  448. layer = k;
  449. lastValue = materialAlphaMap[k][i];
  450. }
  451. }
  452. // Set the layer index.
  453. mLayerMap[i] = getMin( layer, layerCount );
  454. }
  455. // Cleanup.
  456. for ( U32 i=0; i < MaterialGroups; i++ )
  457. delete [] materialAlphaMap[i];
  458. // Force resaving on these old file versions.
  459. //mNeedsResaving = false;
  460. // Resolve the TerrainMaterial objects from the names.
  461. _resolveMaterials( materials );
  462. }
  463. void TerrainFile::_resolveMaterials( const Vector<String> &materials )
  464. {
  465. mMaterials.clear();
  466. for ( U32 i=0; i < materials.size(); i++ )
  467. mMaterials.push_back( TerrainMaterial::findOrCreate( materials[i] ) );
  468. // If we didn't get any materials then at least
  469. // add a warning material so we will render.
  470. if ( mMaterials.empty() )
  471. mMaterials.push_back( TerrainMaterial::getWarningMaterial() );
  472. }
  473. void TerrainFile::setSize( U32 newSize, bool clear )
  474. {
  475. // Make sure the resolution is a power of two.
  476. newSize = getNextPow2( newSize );
  477. //
  478. if ( clear )
  479. {
  480. mLayerMap.setSize( newSize * newSize );
  481. mLayerMap.compact();
  482. dMemset( mLayerMap.address(), 0, mLayerMap.memSize() );
  483. // Initialize the elevation to something above
  484. // zero so that we have room to excavate by default.
  485. U16 elev = floatToFixed( 512.0f );
  486. mHeightMap.setSize( newSize * newSize );
  487. mHeightMap.compact();
  488. for ( U32 i = 0; i < mHeightMap.size(); i++ )
  489. mHeightMap[i] = elev;
  490. }
  491. else
  492. {
  493. // We're resizing here!
  494. }
  495. mSize = newSize;
  496. _buildGridMap();
  497. }
  498. void TerrainFile::smooth( F32 factor, U32 steps, bool updateCollision )
  499. {
  500. const U32 blockSize = mSize * mSize;
  501. // Grab some temp buffers for our smoothing results.
  502. Vector<F32> h1, h2;
  503. h1.setSize( blockSize );
  504. h2.setSize( blockSize );
  505. // Fill the first buffer with the current heights.
  506. for ( U32 i=0; i < blockSize; i++ )
  507. h1[i] = (F32)mHeightMap[i];
  508. // factor of 0.0 = NO Smoothing
  509. // factor of 1.0 = MAX Smoothing
  510. const F32 matrixM = 1.0f - getMax(0.0f, getMin(1.0f, factor));
  511. const F32 matrixE = (1.0f-matrixM) * (1.0f/12.0f) * 2.0f;
  512. const F32 matrixC = matrixE * 0.5f;
  513. // Now loop for our interations.
  514. F32 *src = h1.address();
  515. F32 *dst = h2.address();
  516. for ( U32 s=0; s < steps; s++ )
  517. {
  518. for ( S32 y=0; y < mSize; y++ )
  519. {
  520. for ( S32 x=0; x < mSize; x++ )
  521. {
  522. F32 samples[9];
  523. S32 c = 0;
  524. for (S32 i = y-1; i < y+2; i++)
  525. for (S32 j = x-1; j < x+2; j++)
  526. {
  527. if ( i < 0 || j < 0 || i >= mSize || j >= mSize )
  528. samples[c++] = src[ x + ( y * mSize ) ];
  529. else
  530. samples[c++] = src[ j + ( i * mSize ) ];
  531. }
  532. // 0 1 2
  533. // 3 x,y 5
  534. // 6 7 8
  535. dst[ x + ( y * mSize ) ] =
  536. ((samples[0]+samples[2]+samples[6]+samples[8]) * matrixC) +
  537. ((samples[1]+samples[3]+samples[5]+samples[7]) * matrixE) +
  538. (samples[4] * matrixM);
  539. }
  540. }
  541. // Swap!
  542. F32 *tmp = dst;
  543. dst = src;
  544. src = tmp;
  545. }
  546. // Copy the results back to the height map.
  547. for ( U32 i=0; i < blockSize; i++ )
  548. mHeightMap[i] = (U16)mCeil( (F32)src[i] );
  549. if ( updateCollision )
  550. _buildGridMap();
  551. }
  552. void TerrainFile::setHeightMap( const Vector<U16> &heightmap, bool updateCollision )
  553. {
  554. AssertFatal( mHeightMap.size() == heightmap.size(), "TerrainFile::setHeightMap - Incorrect heightmap size!" );
  555. dMemcpy( mHeightMap.address(), heightmap.address(), mHeightMap.size() );
  556. if ( updateCollision )
  557. _buildGridMap();
  558. }
  559. void TerrainFile::import( const GBitmap &heightMap,
  560. F32 heightScale,
  561. const Vector<U8> &layerMap,
  562. const Vector<String> &materials,
  563. bool flipYAxis )
  564. {
  565. AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainFile::import - Height map is not square!" );
  566. AssertFatal( isPow2( heightMap.getWidth() ), "TerrainFile::import - Height map is not power of two!" );
  567. const U32 newSize = heightMap.getWidth();
  568. if ( newSize != mSize )
  569. {
  570. mHeightMap.setSize( newSize * newSize );
  571. mHeightMap.compact();
  572. mSize = newSize;
  573. }
  574. // Convert the height map to heights.
  575. U16 *oBits = mHeightMap.address();
  576. if ( heightMap.getFormat() == GFXFormatL16)
  577. {
  578. const F32 toFixedPoint = ( 1.0f / (F32)U16_MAX ) * floatToFixed( heightScale );
  579. const U16 *iBits = (const U16*)heightMap.getBits();
  580. if ( flipYAxis )
  581. {
  582. for ( U32 i = 0; i < mSize * mSize; i++ )
  583. {
  584. U16 height = convertBEndianToHost( *iBits );
  585. *oBits = (U16)mCeil( (F32)height * toFixedPoint );
  586. ++oBits;
  587. ++iBits;
  588. }
  589. }
  590. else
  591. {
  592. for(S32 y = mSize - 1; y >= 0; y--) {
  593. for(U32 x = 0; x < mSize; x++) {
  594. U16 height = convertBEndianToHost( *iBits );
  595. mHeightMap[x + y * mSize] = (U16)mCeil( (F32)height * toFixedPoint );
  596. ++iBits;
  597. }
  598. }
  599. }
  600. }
  601. else
  602. {
  603. const F32 toFixedPoint = ( 1.0f / (F32)U8_MAX ) * floatToFixed( heightScale );
  604. const U8 *iBits = heightMap.getBits();
  605. if ( flipYAxis )
  606. {
  607. for ( U32 i = 0; i < mSize * mSize; i++ )
  608. {
  609. *oBits = (U16)mCeil( ((F32)*iBits) * toFixedPoint );
  610. ++oBits;
  611. iBits += heightMap.getBytesPerPixel();
  612. }
  613. }
  614. else
  615. {
  616. for(S32 y = mSize - 1; y >= 0; y--) {
  617. for(U32 x = 0; x < mSize; x++) {
  618. mHeightMap[x + y * mSize] = (U16)mCeil( ((F32)*iBits) * toFixedPoint );
  619. iBits += heightMap.getBytesPerPixel();
  620. }
  621. }
  622. }
  623. }
  624. // Copy over the layer map.
  625. AssertFatal( layerMap.size() == mHeightMap.size(), "TerrainFile::import - Layer map is the wrong size!" );
  626. mLayerMap = layerMap;
  627. mLayerMap.compact();
  628. // Resolve the materials.
  629. _resolveMaterials( materials );
  630. // Rebuild the collision grid map.
  631. _buildGridMap();
  632. }
  633. void TerrainFile::create( String *inOutFilename,
  634. U32 newSize,
  635. const Vector<String> &materials )
  636. {
  637. // Determine the path and basename
  638. Torque::Path basePath( *inOutFilename );
  639. if ( !basePath.getExtension().equal("ter") )
  640. {
  641. // Use the default path and filename
  642. String terrainDirectory( Con::getVariable( "$pref::Directories::Terrain" ) );
  643. if ( terrainDirectory.isEmpty() )
  644. {
  645. terrainDirectory = "data/terrains";
  646. }
  647. basePath.setPath( terrainDirectory );
  648. basePath.setFileName( "terrain" );
  649. }
  650. // Construct a default file name
  651. (*inOutFilename) = Torque::FS::MakeUniquePath( basePath.getRootAndPath(), basePath.getFileName(), "ter" );
  652. // Create the file
  653. TerrainFile *file = new TerrainFile;
  654. for ( U32 i=0; i < materials.size(); i++ )
  655. file->mMaterials.push_back( TerrainMaterial::findOrCreate( materials[i] ) );
  656. file->setSize( newSize, true );
  657. file->save( *inOutFilename );
  658. delete file;
  659. }
  660. inline void getMinMax( U16 &inMin, U16 &inMax, U16 height )
  661. {
  662. if ( height < inMin )
  663. inMin = height;
  664. if ( height > inMax )
  665. inMax = height;
  666. }
  667. inline void checkSquare( TerrainSquare *parent, const TerrainSquare *child )
  668. {
  669. if(parent->minHeight > child->minHeight)
  670. parent->minHeight = child->minHeight;
  671. if(parent->maxHeight < child->maxHeight)
  672. parent->maxHeight = child->maxHeight;
  673. if ( child->flags & (TerrainSquare::Empty | TerrainSquare::HasEmpty) )
  674. parent->flags |= TerrainSquare::HasEmpty;
  675. }
  676. void TerrainFile::updateGrid( const Point2I &minPt, const Point2I &maxPt )
  677. {
  678. // here's how it works:
  679. // for the current terrain renderer we only care about
  680. // the minHeight and maxHeight on the GridSquare
  681. // so we do one pass through, updating minHeight and maxHeight
  682. // on the level 0 squares, then we loop up the grid map from 1 to
  683. // the top, expanding the bounding boxes as necessary.
  684. // this should end up being way, way, way, way faster for the terrain
  685. // editor
  686. PROFILE_SCOPE( TerrainFile_UpdateGrid );
  687. for ( S32 y = minPt.y - 1; y < maxPt.y + 1; y++ )
  688. {
  689. for ( S32 x = minPt.x - 1; x < maxPt.x + 1; x++ )
  690. {
  691. S32 px = x;
  692. S32 py = y;
  693. if ( px < 0 )
  694. px += mSize;
  695. if ( py < 0 )
  696. py += mSize;
  697. TerrainSquare *sq = findSquare( 0, px, py );
  698. sq->minHeight = 0xFFFF;
  699. sq->maxHeight = 0;
  700. // Update the empty state.
  701. if ( isEmptyAt( x, y ) )
  702. sq->flags |= TerrainSquare::Empty;
  703. else
  704. sq->flags &= ~TerrainSquare::Empty;
  705. getMinMax( sq->minHeight, sq->maxHeight, getHeight( x, y ) );
  706. getMinMax( sq->minHeight, sq->maxHeight, getHeight( x+1, y ) );
  707. getMinMax( sq->minHeight, sq->maxHeight, getHeight( x, y+1 ) );
  708. getMinMax( sq->minHeight, sq->maxHeight, getHeight( x+1, y+1 ) );
  709. }
  710. }
  711. // ok, all the level 0 grid squares are updated:
  712. // now update all the parent grid squares that need to be updated:
  713. for( S32 level = 1; level <= mGridLevels; level++ )
  714. {
  715. S32 size = 1 << level;
  716. S32 halfSize = size >> 1;
  717. for( S32 y = (minPt.y - 1) >> level; y < (maxPt.y + size) >> level; y++ )
  718. {
  719. for ( S32 x = (minPt.x - 1) >> level; x < (maxPt.x + size) >> level; x++ )
  720. {
  721. S32 px = x << level;
  722. S32 py = y << level;
  723. TerrainSquare *sq = findSquare(level, px, py);
  724. sq->minHeight = 0xFFFF;
  725. sq->maxHeight = 0;
  726. sq->flags &= ~( TerrainSquare::Empty | TerrainSquare::HasEmpty );
  727. checkSquare( sq, findSquare( level - 1, px, py ) );
  728. checkSquare( sq, findSquare( level - 1, px + halfSize, py ) );
  729. checkSquare( sq, findSquare( level - 1, px, py + halfSize ) );
  730. checkSquare( sq, findSquare( level - 1, px + halfSize, py + halfSize ) );
  731. }
  732. }
  733. }
  734. }