terrainActions.cpp 24 KB

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