terrainActions.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  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. if (!isValid((*sel)[i]))
  413. continue;
  414. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  415. (*sel)[i].mStartHeight = (*sel)[i].mHeight;
  416. }
  417. }
  418. else if(type == Update)
  419. {
  420. // ok, collide the ray from the event with the intersection plane:
  421. Point3F intersectPoint;
  422. Point3F start = event.pos;
  423. Point3F end = start + event.vec * 1000;
  424. F32 t = mIntersectionPlane.intersect(start, end);
  425. m_point3F_interpolate( start, end, t, intersectPoint);
  426. F32 currentZ = mDot(mTerrainUpVector, intersectPoint);
  427. F32 diff = currentZ - mPreviousZ;
  428. for(U32 i = 0; i < sel->size(); i++)
  429. {
  430. (*sel)[i].mHeight = (*sel)[i].mStartHeight + diff * (*sel)[i].mWeight;
  431. // clamp it
  432. if((*sel)[i].mHeight < 0.f)
  433. (*sel)[i].mHeight = 0.f;
  434. if((*sel)[i].mHeight > 2047.f)
  435. (*sel)[i].mHeight = 2047.f;
  436. mTerrainEditor->setGridInfoHeight((*sel)[i]);
  437. }
  438. mTerrainEditor->scheduleGridUpdate();
  439. }
  440. else if(type == End)
  441. {
  442. mTerrainEditor->getRoot()->mouseUnlock(mTerrainEditor);
  443. }
  444. }
  445. //------------------------------------------------------------------------------
  446. AdjustHeightAction::AdjustHeightAction(TerrainEditor * editor) :
  447. BrushAdjustHeightAction(editor)
  448. {
  449. mCursor = 0;
  450. }
  451. void AdjustHeightAction::process(Selection *sel, const Gui3DMouseEvent & event, bool b, Type type)
  452. {
  453. Selection * curSel = mTerrainEditor->getCurrentSel();
  454. BrushAdjustHeightAction::process(curSel, event, b, type);
  455. }
  456. //------------------------------------------------------------------------------
  457. // flatten the primary selection then blend in the rest...
  458. void FlattenHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  459. {
  460. if(!sel->size())
  461. return;
  462. if(selChanged)
  463. {
  464. F32 average = 0.f;
  465. // get the average height
  466. U32 cPrimary = 0;
  467. for(U32 k = 0; k < sel->size(); k++)
  468. if((*sel)[k].mPrimarySelect)
  469. {
  470. cPrimary++;
  471. average += (*sel)[k].mHeight;
  472. }
  473. average /= cPrimary;
  474. // set it
  475. for(U32 i = 0; i < sel->size(); i++)
  476. {
  477. if (!isValid((*sel)[i]))
  478. continue;
  479. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  480. //
  481. if((*sel)[i].mPrimarySelect)
  482. (*sel)[i].mHeight = average;
  483. else
  484. {
  485. F32 h = average - (*sel)[i].mHeight;
  486. (*sel)[i].mHeight += (h * (*sel)[i].mWeight);
  487. }
  488. mTerrainEditor->setGridInfo((*sel)[i]);
  489. }
  490. mTerrainEditor->scheduleGridUpdate();
  491. }
  492. }
  493. //------------------------------------------------------------------------------
  494. void SmoothHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  495. {
  496. if(!sel->size())
  497. return;
  498. if(selChanged)
  499. {
  500. F32 avgHeight = 0.f;
  501. for(U32 k = 0; k < sel->size(); k++)
  502. {
  503. mTerrainEditor->getUndoSel()->add((*sel)[k]);
  504. avgHeight += (*sel)[k].mHeight;
  505. }
  506. avgHeight /= sel->size();
  507. // clamp the terrain smooth factor...
  508. if(mTerrainEditor->mSmoothFactor < 0.f)
  509. mTerrainEditor->mSmoothFactor = 0.f;
  510. if(mTerrainEditor->mSmoothFactor > 1.f)
  511. mTerrainEditor->mSmoothFactor = 1.f;
  512. // linear
  513. for(U32 i = 0; i < sel->size(); i++)
  514. {
  515. if (!isValid((*sel)[i]))
  516. continue;
  517. (*sel)[i].mHeight += (avgHeight - (*sel)[i].mHeight) * mTerrainEditor->mSmoothFactor * (*sel)[i].mWeight;
  518. mTerrainEditor->setGridInfo((*sel)[i]);
  519. }
  520. mTerrainEditor->scheduleGridUpdate();
  521. }
  522. }
  523. void SmoothSlopeAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  524. {
  525. if(!sel->size())
  526. return;
  527. if(selChanged)
  528. {
  529. // Perform simple 2d linear regression on x&z and y&z:
  530. // b = (Avg(xz) - Avg(x)Avg(z))/(Avg(x^2) - Avg(x)^2)
  531. Point2F prod(0.f, 0.f); // mean of product for covar
  532. Point2F avgSqr(0.f, 0.f); // mean sqr of x, y for var
  533. Point2F avgPos(0.f, 0.f);
  534. F32 avgHeight = 0.f;
  535. F32 z;
  536. Point2F pos;
  537. for(U32 k = 0; k < sel->size(); k++)
  538. {
  539. mTerrainEditor->getUndoSel()->add((*sel)[k]);
  540. pos = Point2F((*sel)[k].mGridPoint.gridPos.x, (*sel)[k].mGridPoint.gridPos.y);
  541. z = (*sel)[k].mHeight;
  542. prod += pos * z;
  543. avgSqr += pos * pos;
  544. avgPos += pos;
  545. avgHeight += z;
  546. }
  547. prod /= sel->size();
  548. avgSqr /= sel->size();
  549. avgPos /= sel->size();
  550. avgHeight /= sel->size();
  551. Point2F avgSlope = (prod - avgPos*avgHeight)/(avgSqr - avgPos*avgPos);
  552. F32 goalHeight;
  553. for(U32 i = 0; i < sel->size(); i++)
  554. {
  555. if (!isValid((*sel)[i]))
  556. continue;
  557. goalHeight = avgHeight + ((*sel)[i].mGridPoint.gridPos.x - avgPos.x)*avgSlope.x +
  558. ((*sel)[i].mGridPoint.gridPos.y - avgPos.y)*avgSlope.y;
  559. (*sel)[i].mHeight += (goalHeight - (*sel)[i].mHeight) * (*sel)[i].mWeight;
  560. mTerrainEditor->setGridInfo((*sel)[i]);
  561. }
  562. mTerrainEditor->scheduleGridUpdate();
  563. }
  564. }
  565. void PaintNoiseAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
  566. {
  567. // If this is the ending
  568. // mouse down event, then
  569. // update the noise values.
  570. if ( type == Begin )
  571. {
  572. mNoise.setSeed( Sim::getCurrentTime() );
  573. mNoise.fBm( &mNoiseData, mNoiseSize, 12, 1.0f, 5.0f );
  574. mNoise.getMinMax( &mNoiseData, &mMinMaxNoise.x, &mMinMaxNoise.y, mNoiseSize );
  575. mScale = 1.5f / ( mMinMaxNoise.x - mMinMaxNoise.y);
  576. }
  577. if( selChanged )
  578. {
  579. for( U32 i = 0; i < sel->size(); i++ )
  580. {
  581. if (!isValid((*sel)[i]))
  582. continue;
  583. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  584. const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos;
  585. const F32 noiseVal = mNoiseData[ ( gridPos.x % mNoiseSize ) +
  586. ( ( gridPos.y % mNoiseSize ) * mNoiseSize ) ];
  587. (*sel)[i].mHeight += (noiseVal - mMinMaxNoise.y * mScale) * (*sel)[i].mWeight * mTerrainEditor->mNoiseFactor;
  588. if ((*sel)[i].mHeight > mTerrainEditor->mTileMaxHeight)
  589. (*sel)[i].mHeight = mTerrainEditor->mTileMaxHeight;
  590. if ((*sel)[i].mHeight < mTerrainEditor->mTileMinHeight)
  591. (*sel)[i].mHeight = mTerrainEditor->mTileMinHeight;
  592. mTerrainEditor->setGridInfo((*sel)[i]);
  593. }
  594. mTerrainEditor->scheduleGridUpdate();
  595. }
  596. }
  597. /*
  598. void ThermalErosionAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  599. {
  600. if( selChanged )
  601. {
  602. TerrainBlock *tblock = mTerrainEditor->getActiveTerrain();
  603. if ( !tblock )
  604. return;
  605. F32 height = 0;
  606. F32 maxHeight = 0;
  607. U32 shift = getBinLog2( TerrainBlock::BlockSize );
  608. for ( U32 x = 0; x < TerrainBlock::BlockSize; x++ )
  609. {
  610. for ( U32 y = 0; y < TerrainBlock::BlockSize; y++ )
  611. {
  612. height = fixedToFloat( tblock->getHeight( x, y ) );
  613. mTerrainHeights[ x + (y << 8)] = height;
  614. if ( height > maxHeight )
  615. maxHeight = height;
  616. }
  617. }
  618. //mNoise.erodeThermal( &mTerrainHeights, &mNoiseData, 30.0f, 5.0f, 5, TerrainBlock::BlockSize, tblock->getSquareSize(), maxHeight );
  619. mNoise.erodeHydraulic( &mTerrainHeights, &mNoiseData, 1, TerrainBlock::BlockSize );
  620. F32 heightDiff = 0;
  621. for( U32 i = 0; i < sel->size(); i++ )
  622. {
  623. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  624. const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos;
  625. // Need to get the height difference
  626. // between the current height and the
  627. // erosion height to properly apply the
  628. // softness and pressure settings of the brush
  629. // for this selection.
  630. heightDiff = (*sel)[i].mHeight - mNoiseData[ gridPos.x + (gridPos.y << shift)];
  631. (*sel)[i].mHeight -= (heightDiff * (*sel)[i].mWeight);
  632. mTerrainEditor->setGridInfo((*sel)[i]);
  633. }
  634. mTerrainEditor->gridUpdateComplete();
  635. }
  636. }
  637. */
  638. IMPLEMENT_CONOBJECT( TerrainSmoothAction );
  639. ConsoleDocClass( TerrainSmoothAction,
  640. "@brief Terrain action used for leveling varying terrain heights smoothly.\n\n"
  641. "Editor use only.\n\n"
  642. "@internal"
  643. );
  644. TerrainSmoothAction::TerrainSmoothAction()
  645. : UndoAction("Terrain Smoothing"), mFactor(1.0), mSteps(1), mTerrainId(0)
  646. {
  647. }
  648. void TerrainSmoothAction::initPersistFields()
  649. {
  650. docsURL;
  651. Parent::initPersistFields();
  652. }
  653. void TerrainSmoothAction::smooth( TerrainBlock *terrain, F32 factor, U32 steps )
  654. {
  655. AssertFatal( terrain, "TerrainSmoothAction::smooth() - Got null object!" );
  656. // Store our input parameters.
  657. mTerrainId = terrain->getId();
  658. mSteps = steps;
  659. mFactor = factor;
  660. // The redo can do the rest.
  661. redo();
  662. }
  663. DefineEngineMethod( TerrainSmoothAction, smooth, void, ( TerrainBlock *terrain, F32 factor, U32 steps ), , "( TerrainBlock obj, F32 factor, U32 steps )")
  664. {
  665. if (terrain)
  666. object->smooth( terrain, factor, mClamp( steps, 1, 13 ) );
  667. }
  668. void TerrainSmoothAction::undo()
  669. {
  670. // First find the terrain from the id.
  671. TerrainBlock *terrain;
  672. if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
  673. return;
  674. // Get the terrain file.
  675. TerrainFile *terrFile = terrain->getFile();
  676. // Copy our stored heightmap to the file.
  677. terrFile->setHeightMap( mUnsmoothedHeights, false );
  678. // Tell the terrain to update itself.
  679. terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
  680. }
  681. void TerrainSmoothAction::redo()
  682. {
  683. // First find the terrain from the id.
  684. TerrainBlock *terrain;
  685. if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
  686. return;
  687. // Get the terrain file.
  688. TerrainFile *terrFile = terrain->getFile();
  689. // First copy the heightmap state.
  690. mUnsmoothedHeights = terrFile->getHeightMap();
  691. // Do the smooth.
  692. terrFile->smooth( mFactor, mSteps, false );
  693. // Tell the terrain to update itself.
  694. terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
  695. }