terrainActions.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  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 "console/engineAPI.h"
  23. #include "platform/platform.h"
  24. #include "gui/worldEditor/terrainActions.h"
  25. #include "gui/core/guiCanvas.h"
  26. //------------------------------------------------------------------------------
  27. bool TerrainAction::isValid(GridInfo tile)
  28. {
  29. const bool slopeLimit = mTerrainEditor->mSlopeMinAngle > 0.0f || mTerrainEditor->mSlopeMaxAngle < 90.0f;
  30. const F32 minSlope = mSin(mDegToRad(90.0f - mTerrainEditor->mSlopeMinAngle));
  31. const F32 maxSlope = mSin(mDegToRad(90.0f - mTerrainEditor->mSlopeMaxAngle));
  32. const TerrainBlock* terrain = mTerrainEditor->getActiveTerrain();
  33. const F32 squareSize = terrain->getSquareSize();
  34. Point2F p;
  35. Point3F norm;
  36. if (slopeLimit)
  37. {
  38. p.x = tile.mGridPoint.gridPos.x * squareSize;
  39. p.y = tile.mGridPoint.gridPos.y * squareSize;
  40. if (!terrain->getNormal(p, &norm, true))
  41. return false;
  42. if (norm.z > minSlope ||
  43. norm.z < maxSlope)
  44. return false;
  45. }
  46. if (tile.mHeight < mTerrainEditor->mTileMinHeight || tile.mHeight > mTerrainEditor->mTileMaxHeight)
  47. return false;
  48. return true;
  49. }
  50. void SelectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type)
  51. {
  52. if(sel == mTerrainEditor->getCurrentSel())
  53. return;
  54. if(type == Process)
  55. return;
  56. if(selChanged)
  57. {
  58. if(event.modifier & SI_MULTISELECT)
  59. {
  60. for(U32 i = 0; i < sel->size(); i++)
  61. mTerrainEditor->getCurrentSel()->remove((*sel)[i]);
  62. }
  63. else
  64. {
  65. for(U32 i = 0; i < sel->size(); i++)
  66. {
  67. GridInfo gInfo;
  68. if(mTerrainEditor->getCurrentSel()->getInfo((*sel)[i].mGridPoint.gridPos, gInfo))
  69. {
  70. if(!gInfo.mPrimarySelect)
  71. gInfo.mPrimarySelect = (*sel)[i].mPrimarySelect;
  72. if(gInfo.mWeight < (*sel)[i].mWeight)
  73. gInfo.mWeight = (*sel)[i].mWeight;
  74. mTerrainEditor->getCurrentSel()->setInfo(gInfo);
  75. }
  76. else
  77. mTerrainEditor->getCurrentSel()->add((*sel)[i]);
  78. }
  79. }
  80. }
  81. }
  82. void DeselectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type)
  83. {
  84. if(sel == mTerrainEditor->getCurrentSel())
  85. return;
  86. if(type == Process)
  87. return;
  88. if(selChanged)
  89. {
  90. for(U32 i = 0; i < sel->size(); i++)
  91. mTerrainEditor->getCurrentSel()->remove((*sel)[i]);
  92. }
  93. }
  94. //------------------------------------------------------------------------------
  95. void SoftSelectAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
  96. {
  97. TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain();
  98. if ( !terrBlock )
  99. return;
  100. // allow process of current selection
  101. Selection tmpSel;
  102. if(sel == mTerrainEditor->getCurrentSel())
  103. {
  104. tmpSel = *sel;
  105. sel = &tmpSel;
  106. }
  107. if(type == Begin || type == Process)
  108. mFilter.set(1, &mTerrainEditor->mSoftSelectFilter);
  109. //
  110. if(selChanged)
  111. {
  112. F32 radius = mTerrainEditor->mSoftSelectRadius;
  113. if(radius == 0.f)
  114. return;
  115. S32 squareSize = terrBlock->getSquareSize();
  116. U32 offset = U32(radius / F32(squareSize)) + 1;
  117. for(U32 i = 0; i < sel->size(); i++)
  118. {
  119. GridInfo & info = (*sel)[i];
  120. info.mPrimarySelect = true;
  121. info.mWeight = mFilter.getValue(0);
  122. if(!mTerrainEditor->getCurrentSel()->add(info))
  123. mTerrainEditor->getCurrentSel()->setInfo(info);
  124. Point2F infoPos((F32)info.mGridPoint.gridPos.x, (F32)info.mGridPoint.gridPos.y);
  125. //
  126. for(S32 x = info.mGridPoint.gridPos.x - offset; x < info.mGridPoint.gridPos.x + (offset << 1); x++)
  127. for(S32 y = info.mGridPoint.gridPos.y - offset; y < info.mGridPoint.gridPos.y + (offset << 1); y++)
  128. {
  129. //
  130. Point2F pos((F32)x, (F32)y);
  131. F32 dist = Point2F(pos - infoPos).len() * F32(squareSize);
  132. if(dist > radius)
  133. continue;
  134. F32 weight = mFilter.getValue(dist / radius);
  135. //
  136. GridInfo gInfo;
  137. GridPoint gridPoint = info.mGridPoint;
  138. gridPoint.gridPos.set(x, y);
  139. if(mTerrainEditor->getCurrentSel()->getInfo(Point2I(x, y), gInfo))
  140. {
  141. if(gInfo.mPrimarySelect)
  142. continue;
  143. if(gInfo.mWeight < weight)
  144. {
  145. gInfo.mWeight = weight;
  146. mTerrainEditor->getCurrentSel()->setInfo(gInfo);
  147. }
  148. }
  149. else
  150. {
  151. Vector<GridInfo> gInfos;
  152. mTerrainEditor->getGridInfos(gridPoint, gInfos);
  153. for (U32 z = 0; z < gInfos.size(); z++)
  154. {
  155. gInfos[z].mWeight = weight;
  156. gInfos[z].mPrimarySelect = false;
  157. mTerrainEditor->getCurrentSel()->add(gInfos[z]);
  158. }
  159. }
  160. }
  161. }
  162. }
  163. }
  164. //------------------------------------------------------------------------------
  165. void OutlineSelectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type)
  166. {
  167. TORQUE_UNUSED(sel); TORQUE_UNUSED(event); TORQUE_UNUSED(type);
  168. switch(type)
  169. {
  170. case Begin:
  171. if(event.modifier & SI_SHIFT)
  172. break;
  173. mTerrainEditor->getCurrentSel()->reset();
  174. break;
  175. case End:
  176. case Update:
  177. default:
  178. return;
  179. }
  180. mLastEvent = event;
  181. }
  182. //------------------------------------------------------------------------------
  183. void PaintMaterialAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  184. {
  185. S32 mat = mTerrainEditor->getPaintMaterialIndex();
  186. if ( !selChanged || mat < 0 )
  187. return;
  188. for( U32 i = 0; i < sel->size(); i++ )
  189. {
  190. GridInfo &inf = (*sel)[i];
  191. if (!isValid(inf))
  192. continue;
  193. // If grid is already set to our material, or it is an
  194. // empty grid spot, then skip painting.
  195. if ( inf.mMaterial == mat || inf.mMaterial == U8_MAX )
  196. continue;
  197. if ( mRandF() > mTerrainEditor->getBrushPressure() )
  198. continue;
  199. inf.mMaterialChanged = true;
  200. mTerrainEditor->getUndoSel()->add(inf);
  201. // Painting is really simple now... set the one mat index.
  202. inf.mMaterial = mat;
  203. mTerrainEditor->setGridInfo(inf, true);
  204. }
  205. mTerrainEditor->scheduleMaterialUpdate();
  206. }
  207. //------------------------------------------------------------------------------
  208. void ClearMaterialsAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  209. {
  210. if(selChanged)
  211. {
  212. for(U32 i = 0; i < sel->size(); i++)
  213. {
  214. GridInfo &inf = (*sel)[i];
  215. mTerrainEditor->getUndoSel()->add(inf);
  216. inf.mMaterialChanged = true;
  217. // Reset to the first texture layer.
  218. inf.mMaterial = 0;
  219. mTerrainEditor->setGridInfo(inf);
  220. }
  221. mTerrainEditor->scheduleMaterialUpdate();
  222. }
  223. }
  224. //------------------------------------------------------------------------------
  225. void RaiseHeightAction::process( Selection *sel, const Gui3DMouseEvent &evt, bool selChanged, Type type )
  226. {
  227. // ok the raise height action is our "dirt pour" action
  228. // only works on brushes...
  229. Brush *brush = dynamic_cast<Brush*>(sel);
  230. if ( !brush )
  231. return;
  232. if ( type == End )
  233. return;
  234. Point2I brushPos = brush->getPosition();
  235. GridPoint brushGridPoint = brush->getGridPoint();
  236. Vector<GridInfo> cur; // the height at the brush position
  237. mTerrainEditor->getGridInfos(brushGridPoint, cur);
  238. if ( cur.size() == 0 )
  239. return;
  240. // we get 30 process actions per second (at least)
  241. F32 heightAdjust = mTerrainEditor->mAdjustHeightVal / 30;
  242. // nothing can get higher than the current brush pos adjusted height
  243. F32 maxHeight = cur[0].mHeight + heightAdjust;
  244. for ( U32 i = 0; i < sel->size(); i++ )
  245. {
  246. if (!isValid((*sel)[i]))
  247. continue;
  248. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  249. if ( (*sel)[i].mHeight < maxHeight )
  250. {
  251. (*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight;
  252. if ( (*sel)[i].mHeight > maxHeight )
  253. (*sel)[i].mHeight = maxHeight;
  254. }
  255. mTerrainEditor->setGridInfo((*sel)[i]);
  256. }
  257. mTerrainEditor->scheduleGridUpdate();
  258. }
  259. //------------------------------------------------------------------------------
  260. void LowerHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
  261. {
  262. // ok the lower height action is our "dirt dig" action
  263. // only works on brushes...
  264. Brush *brush = dynamic_cast<Brush *>(sel);
  265. if(!brush)
  266. return;
  267. if ( type == End )
  268. return;
  269. Point2I brushPos = brush->getPosition();
  270. GridPoint brushGridPoint = brush->getGridPoint();
  271. Vector<GridInfo> cur; // the height at the brush position
  272. mTerrainEditor->getGridInfos(brushGridPoint, cur);
  273. if (cur.size() == 0)
  274. return;
  275. // we get 30 process actions per second (at least)
  276. F32 heightAdjust = -mTerrainEditor->mAdjustHeightVal / 30;
  277. // nothing can get higher than the current brush pos adjusted height
  278. F32 maxHeight = cur[0].mHeight + heightAdjust;
  279. if(maxHeight < 0)
  280. maxHeight = 0;
  281. for(U32 i = 0; i < sel->size(); i++)
  282. {
  283. if (!isValid((*sel)[i]))
  284. continue;
  285. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  286. if((*sel)[i].mHeight > maxHeight)
  287. {
  288. (*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight;
  289. if((*sel)[i].mHeight < maxHeight)
  290. (*sel)[i].mHeight = maxHeight;
  291. }
  292. mTerrainEditor->setGridInfo((*sel)[i]);
  293. }
  294. mTerrainEditor->scheduleGridUpdate();
  295. }
  296. //------------------------------------------------------------------------------
  297. void SetHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  298. {
  299. if(selChanged)
  300. {
  301. for(U32 i = 0; i < sel->size(); i++)
  302. {
  303. if (!isValid((*sel)[i]))
  304. continue;
  305. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  306. (*sel)[i].mHeight = mTerrainEditor->mSetHeightVal;
  307. mTerrainEditor->setGridInfo((*sel)[i]);
  308. }
  309. mTerrainEditor->scheduleGridUpdate();
  310. }
  311. }
  312. //------------------------------------------------------------------------------
  313. void SetEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  314. {
  315. if ( !selChanged )
  316. return;
  317. mTerrainEditor->setMissionDirty();
  318. for ( U32 i = 0; i < sel->size(); i++ )
  319. {
  320. GridInfo &inf = (*sel)[i];
  321. // Skip already empty blocks.
  322. if ( inf.mMaterial == U8_MAX )
  323. continue;
  324. if (!isValid(inf))
  325. continue;
  326. // The change flag needs to be set on the undo
  327. // so that it knows to restore materials.
  328. inf.mMaterialChanged = true;
  329. mTerrainEditor->getUndoSel()->add( inf );
  330. // Set the material to empty.
  331. inf.mMaterial = -1;
  332. mTerrainEditor->setGridInfo( inf );
  333. }
  334. mTerrainEditor->scheduleGridUpdate();
  335. }
  336. //------------------------------------------------------------------------------
  337. void ClearEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  338. {
  339. if ( !selChanged )
  340. return;
  341. mTerrainEditor->setMissionDirty();
  342. for ( U32 i = 0; i < sel->size(); i++ )
  343. {
  344. GridInfo &inf = (*sel)[i];
  345. // Skip if not empty.
  346. if ( inf.mMaterial != U8_MAX )
  347. continue;
  348. // The change flag needs to be set on the undo
  349. // so that it knows to restore materials.
  350. inf.mMaterialChanged = true;
  351. mTerrainEditor->getUndoSel()->add( inf );
  352. // Set the material
  353. inf.mMaterial = 0;
  354. mTerrainEditor->setGridInfo( inf );
  355. }
  356. mTerrainEditor->scheduleGridUpdate();
  357. }
  358. //------------------------------------------------------------------------------
  359. void ScaleHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  360. {
  361. if(selChanged)
  362. {
  363. for(U32 i = 0; i < sel->size(); i++)
  364. {
  365. if (!isValid((*sel)[i]))
  366. continue;
  367. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  368. (*sel)[i].mHeight *= mTerrainEditor->mScaleVal;
  369. mTerrainEditor->setGridInfo((*sel)[i]);
  370. }
  371. mTerrainEditor->scheduleGridUpdate();
  372. }
  373. }
  374. void BrushAdjustHeightAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type)
  375. {
  376. if(type == Process)
  377. return;
  378. TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain();
  379. if ( !terrBlock )
  380. return;
  381. if(type == Begin)
  382. {
  383. mTerrainEditor->lockSelection(true);
  384. mTerrainEditor->getRoot()->mouseLock(mTerrainEditor);
  385. // the way this works is:
  386. // construct a plane that goes through the collision point
  387. // with one axis up the terrain Z, and horizontally parallel to the
  388. // plane of projection
  389. // the cross of the camera ffdv and the terrain up vector produces
  390. // the cross plane vector.
  391. // all subsequent mouse actions are collided against the plane and the deltaZ
  392. // from the previous position is used to delta the selection up and down.
  393. Point3F cameraDir;
  394. EditTSCtrl::smCamMatrix.getColumn(1, &cameraDir);
  395. terrBlock->getTransform().getColumn(2, &mTerrainUpVector);
  396. // ok, get the cross vector for the plane:
  397. Point3F planeCross;
  398. mCross(cameraDir, mTerrainUpVector, &planeCross);
  399. planeCross.normalize();
  400. Point3F planeNormal;
  401. Point3F intersectPoint;
  402. mTerrainEditor->collide(event, intersectPoint);
  403. mCross(mTerrainUpVector, planeCross, &planeNormal);
  404. mIntersectionPlane.set(intersectPoint, planeNormal);
  405. // ok, we have the intersection point...
  406. // project the collision point onto the up vector of the terrain
  407. mPreviousZ = mDot(mTerrainUpVector, intersectPoint);
  408. // add to undo
  409. // and record the starting heights
  410. for(U32 i = 0; i < sel->size(); i++)
  411. {
  412. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  413. (*sel)[i].mStartHeight = (*sel)[i].mHeight;
  414. }
  415. }
  416. else if(type == Update)
  417. {
  418. // ok, collide the ray from the event with the intersection plane:
  419. Point3F intersectPoint;
  420. Point3F start = event.pos;
  421. Point3F end = start + event.vec * 1000;
  422. F32 t = mIntersectionPlane.intersect(start, end);
  423. m_point3F_interpolate( start, end, t, intersectPoint);
  424. F32 currentZ = mDot(mTerrainUpVector, intersectPoint);
  425. F32 diff = currentZ - mPreviousZ;
  426. for(U32 i = 0; i < sel->size(); i++)
  427. {
  428. (*sel)[i].mHeight = (*sel)[i].mStartHeight + diff * (*sel)[i].mWeight;
  429. // clamp it
  430. if((*sel)[i].mHeight < 0.f)
  431. (*sel)[i].mHeight = 0.f;
  432. if((*sel)[i].mHeight > 2047.f)
  433. (*sel)[i].mHeight = 2047.f;
  434. mTerrainEditor->setGridInfoHeight((*sel)[i]);
  435. }
  436. mTerrainEditor->scheduleGridUpdate();
  437. }
  438. else if(type == End)
  439. {
  440. mTerrainEditor->getRoot()->mouseUnlock(mTerrainEditor);
  441. }
  442. }
  443. //------------------------------------------------------------------------------
  444. AdjustHeightAction::AdjustHeightAction(TerrainEditor * editor) :
  445. BrushAdjustHeightAction(editor)
  446. {
  447. mCursor = 0;
  448. }
  449. void AdjustHeightAction::process(Selection *sel, const Gui3DMouseEvent & event, bool b, Type type)
  450. {
  451. Selection * curSel = mTerrainEditor->getCurrentSel();
  452. BrushAdjustHeightAction::process(curSel, event, b, type);
  453. }
  454. //------------------------------------------------------------------------------
  455. // flatten the primary selection then blend in the rest...
  456. void FlattenHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  457. {
  458. if(!sel->size())
  459. return;
  460. if(selChanged)
  461. {
  462. F32 average = 0.f;
  463. // get the average height
  464. U32 cPrimary = 0;
  465. for(U32 k = 0; k < sel->size(); k++)
  466. if((*sel)[k].mPrimarySelect)
  467. {
  468. cPrimary++;
  469. average += (*sel)[k].mHeight;
  470. }
  471. average /= cPrimary;
  472. // set it
  473. for(U32 i = 0; i < sel->size(); i++)
  474. {
  475. if (!isValid((*sel)[i]))
  476. continue;
  477. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  478. //
  479. if((*sel)[i].mPrimarySelect)
  480. (*sel)[i].mHeight = average;
  481. else
  482. {
  483. F32 h = average - (*sel)[i].mHeight;
  484. (*sel)[i].mHeight += (h * (*sel)[i].mWeight);
  485. }
  486. mTerrainEditor->setGridInfo((*sel)[i]);
  487. }
  488. mTerrainEditor->scheduleGridUpdate();
  489. }
  490. }
  491. //------------------------------------------------------------------------------
  492. void SmoothHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  493. {
  494. if(!sel->size())
  495. return;
  496. if(selChanged)
  497. {
  498. F32 avgHeight = 0.f;
  499. for(U32 k = 0; k < sel->size(); k++)
  500. {
  501. mTerrainEditor->getUndoSel()->add((*sel)[k]);
  502. avgHeight += (*sel)[k].mHeight;
  503. }
  504. avgHeight /= sel->size();
  505. // clamp the terrain smooth factor...
  506. if(mTerrainEditor->mSmoothFactor < 0.f)
  507. mTerrainEditor->mSmoothFactor = 0.f;
  508. if(mTerrainEditor->mSmoothFactor > 1.f)
  509. mTerrainEditor->mSmoothFactor = 1.f;
  510. // linear
  511. for(U32 i = 0; i < sel->size(); i++)
  512. {
  513. (*sel)[i].mHeight += (avgHeight - (*sel)[i].mHeight) * mTerrainEditor->mSmoothFactor * (*sel)[i].mWeight;
  514. mTerrainEditor->setGridInfo((*sel)[i]);
  515. }
  516. mTerrainEditor->scheduleGridUpdate();
  517. }
  518. }
  519. void SmoothSlopeAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  520. {
  521. if(!sel->size())
  522. return;
  523. if(selChanged)
  524. {
  525. // Perform simple 2d linear regression on x&z and y&z:
  526. // b = (Avg(xz) - Avg(x)Avg(z))/(Avg(x^2) - Avg(x)^2)
  527. Point2F prod(0.f, 0.f); // mean of product for covar
  528. Point2F avgSqr(0.f, 0.f); // mean sqr of x, y for var
  529. Point2F avgPos(0.f, 0.f);
  530. F32 avgHeight = 0.f;
  531. F32 z;
  532. Point2F pos;
  533. for(U32 k = 0; k < sel->size(); k++)
  534. {
  535. mTerrainEditor->getUndoSel()->add((*sel)[k]);
  536. pos = Point2F((*sel)[k].mGridPoint.gridPos.x, (*sel)[k].mGridPoint.gridPos.y);
  537. z = (*sel)[k].mHeight;
  538. prod += pos * z;
  539. avgSqr += pos * pos;
  540. avgPos += pos;
  541. avgHeight += z;
  542. }
  543. prod /= sel->size();
  544. avgSqr /= sel->size();
  545. avgPos /= sel->size();
  546. avgHeight /= sel->size();
  547. Point2F avgSlope = (prod - avgPos*avgHeight)/(avgSqr - avgPos*avgPos);
  548. F32 goalHeight;
  549. for(U32 i = 0; i < sel->size(); i++)
  550. {
  551. goalHeight = avgHeight + ((*sel)[i].mGridPoint.gridPos.x - avgPos.x)*avgSlope.x +
  552. ((*sel)[i].mGridPoint.gridPos.y - avgPos.y)*avgSlope.y;
  553. (*sel)[i].mHeight += (goalHeight - (*sel)[i].mHeight) * (*sel)[i].mWeight;
  554. mTerrainEditor->setGridInfo((*sel)[i]);
  555. }
  556. mTerrainEditor->scheduleGridUpdate();
  557. }
  558. }
  559. void PaintNoiseAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
  560. {
  561. // If this is the ending
  562. // mouse down event, then
  563. // update the noise values.
  564. if ( type == Begin )
  565. {
  566. mNoise.setSeed( Sim::getCurrentTime() );
  567. mNoise.fBm( &mNoiseData, mNoiseSize, 12, 1.0f, 5.0f );
  568. mNoise.getMinMax( &mNoiseData, &mMinMaxNoise.x, &mMinMaxNoise.y, mNoiseSize );
  569. mScale = 1.5f / ( mMinMaxNoise.x - mMinMaxNoise.y);
  570. }
  571. if( selChanged )
  572. {
  573. for( U32 i = 0; i < sel->size(); i++ )
  574. {
  575. if (!isValid((*sel)[i]))
  576. continue;
  577. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  578. const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos;
  579. const F32 noiseVal = mNoiseData[ ( gridPos.x % mNoiseSize ) +
  580. ( ( gridPos.y % mNoiseSize ) * mNoiseSize ) ];
  581. (*sel)[i].mHeight += (noiseVal - mMinMaxNoise.y * mScale) * (*sel)[i].mWeight * mTerrainEditor->mNoiseFactor;
  582. if ((*sel)[i].mHeight > mTerrainEditor->mTileMaxHeight)
  583. (*sel)[i].mHeight = mTerrainEditor->mTileMaxHeight;
  584. if ((*sel)[i].mHeight < mTerrainEditor->mTileMinHeight)
  585. (*sel)[i].mHeight = mTerrainEditor->mTileMinHeight;
  586. mTerrainEditor->setGridInfo((*sel)[i]);
  587. }
  588. mTerrainEditor->scheduleGridUpdate();
  589. }
  590. }
  591. void ThermalErosionAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
  592. {
  593. // If this is the ending
  594. // mouse down event, then
  595. // update the noise values.
  596. U32 shift = getBinLog2(mNoiseSize);
  597. TerrainBlock* tblock = mTerrainEditor->getActiveTerrain();
  598. if (!tblock)
  599. return;
  600. mSelectionOrigin = Point2I(S32_MAX, S32_MAX);
  601. F32 height = 0;
  602. F32 maxHeight = 0;
  603. U32 i = 0;
  604. for (i=0; i < sel->size(); i++)
  605. {
  606. mSelectionOrigin.x = S32(mMin(F32((*sel)[i].mGridPoint.gridPos.x), F32(mSelectionOrigin.x)));
  607. mSelectionOrigin.y = S32(mMin(F32((*sel)[i].mGridPoint.gridPos.y), F32(mSelectionOrigin.y)));
  608. height = fixedToFloat(tblock->getHeight((*sel)[i].mGridPoint.gridPos));
  609. if (height > maxHeight)
  610. maxHeight = height;
  611. }
  612. for (i = 0; i < mTerrainHeights.size(); i++)
  613. {
  614. mTerrainHeights[i] = 0;
  615. }
  616. for (i = 0; i < sel->size(); i++)
  617. {
  618. mTerrainHeights[((*sel)[i].mGridPoint.gridPos.x - mSelectionOrigin.x) + (((*sel)[i].mGridPoint.gridPos.y - mSelectionOrigin.y) << shift)] = (*sel)[i].mHeight;
  619. }
  620. mNoise.setSeed(Sim::getCurrentTime());
  621. //mNoise.fBm(&mNoiseData, mNoiseSize, 12, 1.0f, 5.0f);
  622. //generate noise based on terrain hieghts
  623. mNoise.rigidMultiFractal( &mNoiseData, &mTerrainHeights, mNoiseSize, 12, 1.0f, 5.0f );
  624. //erode the noise
  625. mNoise.erodeThermal(&mNoiseData, &mTerrainHeights, 45.0f, 0.5f, 12, mNoiseSize, 1, maxHeight);
  626. mNoise.getMinMax(&mTerrainHeights, &mMinMaxNoise.x, &mMinMaxNoise.y, mNoiseSize);
  627. mScale = (mMinMaxNoise.x - mMinMaxNoise.y) / maxHeight;
  628. if (selChanged)
  629. {
  630. F32 heightDiff = 0;
  631. for (i = 0; i < sel->size(); i++)
  632. {
  633. if (!isValid((*sel)[i]))
  634. continue;
  635. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  636. const Point2I& gridPos = (*sel)[i].mGridPoint.gridPos;
  637. // Need to get the height difference
  638. // between the current height and the
  639. // erosion height to properly apply the
  640. // softness and pressure settings of the brush
  641. // for this selection.
  642. heightDiff = ((*sel)[i].mHeight - mTerrainHeights[(gridPos.x - mSelectionOrigin.x) + ((gridPos.y-mSelectionOrigin.y) << shift)]) * mScale;
  643. (*sel)[i].mHeight += heightDiff * (*sel)[i].mWeight * mTerrainEditor->getBrushPressure();
  644. if ((*sel)[i].mHeight > mTerrainEditor->mTileMaxHeight)
  645. (*sel)[i].mHeight = mTerrainEditor->mTileMaxHeight;
  646. if ((*sel)[i].mHeight < mTerrainEditor->mTileMinHeight)
  647. (*sel)[i].mHeight = mTerrainEditor->mTileMinHeight;
  648. mTerrainEditor->setGridInfo((*sel)[i]);
  649. }
  650. mTerrainEditor->scheduleGridUpdate();
  651. }
  652. }
  653. void HydraulicErosionAction::process(Selection* sel, const Gui3DMouseEvent&, bool selChanged, Type type)
  654. {
  655. if (selChanged)
  656. {
  657. TerrainBlock* tblock = mTerrainEditor->getActiveTerrain();
  658. if (!tblock)
  659. return;
  660. U32 size = tblock->getBlockSize();
  661. F32 height = 0;
  662. F32 maxHeight = 0;
  663. U32 shift = getBinLog2(size);
  664. mNoiseData.setSize(size * size);
  665. mTerrainHeights.setSize(size * size);
  666. mNoise.fBm(&mNoiseData, size, 12, 1.0f, 5.0f);
  667. for (U32 x = 0; x < size; x++)
  668. {
  669. for (U32 y = 0; y < size; y++)
  670. {
  671. height = fixedToFloat(tblock->getHeight(Point2I(x, y)));
  672. mTerrainHeights[x + (y << shift)] = height * mNoiseData[x + (y << shift)];
  673. if (height > maxHeight)
  674. maxHeight = height;
  675. }
  676. }
  677. mNoise.erodeHydraulic(&mTerrainHeights, &mNoiseData, 1, size);
  678. F32 heightDiff = 0;
  679. for (U32 i = 0; i < sel->size(); i++)
  680. {
  681. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  682. const Point2I& gridPos = (*sel)[i].mGridPoint.gridPos;
  683. // Need to get the height difference
  684. // between the current height and the
  685. // erosion height to properly apply the
  686. // softness and pressure settings of the brush
  687. // for this selection.
  688. heightDiff = (*sel)[i].mHeight - mNoiseData[gridPos.x + (gridPos.y << shift)];
  689. (*sel)[i].mHeight -= (heightDiff * (*sel)[i].mWeight) / maxHeight;
  690. mTerrainEditor->setGridInfo((*sel)[i]);
  691. }
  692. mTerrainEditor->gridUpdateComplete();
  693. }
  694. }
  695. IMPLEMENT_CONOBJECT( TerrainSmoothAction );
  696. ConsoleDocClass( TerrainSmoothAction,
  697. "@brief Terrain action used for leveling varying terrain heights smoothly.\n\n"
  698. "Editor use only.\n\n"
  699. "@internal"
  700. );
  701. TerrainSmoothAction::TerrainSmoothAction()
  702. : UndoAction("Terrain Smoothing"), mFactor(1.0), mSteps(1), mTerrainId(0)
  703. {
  704. }
  705. void TerrainSmoothAction::initPersistFields()
  706. {
  707. docsURL;
  708. Parent::initPersistFields();
  709. }
  710. void TerrainSmoothAction::smooth( TerrainBlock *terrain, F32 factor, U32 steps )
  711. {
  712. AssertFatal( terrain, "TerrainSmoothAction::smooth() - Got null object!" );
  713. // Store our input parameters.
  714. mTerrainId = terrain->getId();
  715. mSteps = steps;
  716. mFactor = factor;
  717. // The redo can do the rest.
  718. redo();
  719. }
  720. DefineEngineMethod( TerrainSmoothAction, smooth, void, ( TerrainBlock *terrain, F32 factor, U32 steps ), , "( TerrainBlock obj, F32 factor, U32 steps )")
  721. {
  722. if (terrain)
  723. object->smooth( terrain, factor, mClamp( steps, 1, 13 ) );
  724. }
  725. void TerrainSmoothAction::undo()
  726. {
  727. // First find the terrain from the id.
  728. TerrainBlock *terrain;
  729. if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
  730. return;
  731. // Get the terrain file.
  732. TerrainFile *terrFile = terrain->getFile();
  733. // Copy our stored heightmap to the file.
  734. terrFile->setHeightMap( mUnsmoothedHeights, false );
  735. // Tell the terrain to update itself.
  736. terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
  737. }
  738. void TerrainSmoothAction::redo()
  739. {
  740. // First find the terrain from the id.
  741. TerrainBlock *terrain;
  742. if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
  743. return;
  744. // Get the terrain file.
  745. TerrainFile *terrFile = terrain->getFile();
  746. // First copy the heightmap state.
  747. mUnsmoothedHeights = terrFile->getHeightMap();
  748. // Do the smooth.
  749. terrFile->smooth( mFactor, mSteps, false );
  750. // Tell the terrain to update itself.
  751. terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
  752. }