terrainActions.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  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. Point2I brushSize = brush->getSize();
  228. GridPoint brushGridPoint = brush->getGridPoint();
  229. Vector<GridInfo> cur; // the height at the brush position
  230. mTerrainEditor->getGridInfos(brushGridPoint, cur);
  231. if ( cur.size() == 0 )
  232. return;
  233. // we get 30 process actions per second (at least)
  234. F32 heightAdjust = mTerrainEditor->mAdjustHeightVal / 30;
  235. // nothing can get higher than the current brush pos adjusted height
  236. F32 maxHeight = cur[0].mHeight + heightAdjust;
  237. for ( U32 i = 0; i < sel->size(); i++ )
  238. {
  239. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  240. if ( (*sel)[i].mHeight < maxHeight )
  241. {
  242. (*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight;
  243. if ( (*sel)[i].mHeight > maxHeight )
  244. (*sel)[i].mHeight = maxHeight;
  245. }
  246. mTerrainEditor->setGridInfo((*sel)[i]);
  247. }
  248. mTerrainEditor->scheduleGridUpdate();
  249. }
  250. //------------------------------------------------------------------------------
  251. void LowerHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
  252. {
  253. // ok the lower height action is our "dirt dig" action
  254. // only works on brushes...
  255. Brush *brush = dynamic_cast<Brush *>(sel);
  256. if(!brush)
  257. return;
  258. if ( type == End )
  259. return;
  260. Point2I brushPos = brush->getPosition();
  261. Point2I brushSize = brush->getSize();
  262. GridPoint brushGridPoint = brush->getGridPoint();
  263. Vector<GridInfo> cur; // the height at the brush position
  264. mTerrainEditor->getGridInfos(brushGridPoint, cur);
  265. if (cur.size() == 0)
  266. return;
  267. // we get 30 process actions per second (at least)
  268. F32 heightAdjust = -mTerrainEditor->mAdjustHeightVal / 30;
  269. // nothing can get higher than the current brush pos adjusted height
  270. F32 maxHeight = cur[0].mHeight + heightAdjust;
  271. if(maxHeight < 0)
  272. maxHeight = 0;
  273. for(U32 i = 0; i < sel->size(); i++)
  274. {
  275. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  276. if((*sel)[i].mHeight > maxHeight)
  277. {
  278. (*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight;
  279. if((*sel)[i].mHeight < maxHeight)
  280. (*sel)[i].mHeight = maxHeight;
  281. }
  282. mTerrainEditor->setGridInfo((*sel)[i]);
  283. }
  284. mTerrainEditor->scheduleGridUpdate();
  285. }
  286. //------------------------------------------------------------------------------
  287. void SetHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  288. {
  289. if(selChanged)
  290. {
  291. for(U32 i = 0; i < sel->size(); i++)
  292. {
  293. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  294. (*sel)[i].mHeight = mTerrainEditor->mSetHeightVal;
  295. mTerrainEditor->setGridInfo((*sel)[i]);
  296. }
  297. mTerrainEditor->scheduleGridUpdate();
  298. }
  299. }
  300. //------------------------------------------------------------------------------
  301. void SetEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  302. {
  303. if ( !selChanged )
  304. return;
  305. mTerrainEditor->setMissionDirty();
  306. for ( U32 i = 0; i < sel->size(); i++ )
  307. {
  308. GridInfo &inf = (*sel)[i];
  309. // Skip already empty blocks.
  310. if ( inf.mMaterial == U8_MAX )
  311. continue;
  312. // The change flag needs to be set on the undo
  313. // so that it knows to restore materials.
  314. inf.mMaterialChanged = true;
  315. mTerrainEditor->getUndoSel()->add( inf );
  316. // Set the material to empty.
  317. inf.mMaterial = -1;
  318. mTerrainEditor->setGridInfo( inf );
  319. }
  320. mTerrainEditor->scheduleGridUpdate();
  321. }
  322. //------------------------------------------------------------------------------
  323. void ClearEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  324. {
  325. if ( !selChanged )
  326. return;
  327. mTerrainEditor->setMissionDirty();
  328. for ( U32 i = 0; i < sel->size(); i++ )
  329. {
  330. GridInfo &inf = (*sel)[i];
  331. // Skip if not empty.
  332. if ( inf.mMaterial != U8_MAX )
  333. continue;
  334. // The change flag needs to be set on the undo
  335. // so that it knows to restore materials.
  336. inf.mMaterialChanged = true;
  337. mTerrainEditor->getUndoSel()->add( inf );
  338. // Set the material
  339. inf.mMaterial = 0;
  340. mTerrainEditor->setGridInfo( inf );
  341. }
  342. mTerrainEditor->scheduleGridUpdate();
  343. }
  344. //------------------------------------------------------------------------------
  345. void ScaleHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  346. {
  347. if(selChanged)
  348. {
  349. for(U32 i = 0; i < sel->size(); i++)
  350. {
  351. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  352. (*sel)[i].mHeight *= mTerrainEditor->mScaleVal;
  353. mTerrainEditor->setGridInfo((*sel)[i]);
  354. }
  355. mTerrainEditor->scheduleGridUpdate();
  356. }
  357. }
  358. void BrushAdjustHeightAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type)
  359. {
  360. if(type == Process)
  361. return;
  362. TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain();
  363. if ( !terrBlock )
  364. return;
  365. if(type == Begin)
  366. {
  367. mTerrainEditor->lockSelection(true);
  368. mTerrainEditor->getRoot()->mouseLock(mTerrainEditor);
  369. // the way this works is:
  370. // construct a plane that goes through the collision point
  371. // with one axis up the terrain Z, and horizontally parallel to the
  372. // plane of projection
  373. // the cross of the camera ffdv and the terrain up vector produces
  374. // the cross plane vector.
  375. // all subsequent mouse actions are collided against the plane and the deltaZ
  376. // from the previous position is used to delta the selection up and down.
  377. Point3F cameraDir;
  378. EditTSCtrl::smCamMatrix.getColumn(1, &cameraDir);
  379. terrBlock->getTransform().getColumn(2, &mTerrainUpVector);
  380. // ok, get the cross vector for the plane:
  381. Point3F planeCross;
  382. mCross(cameraDir, mTerrainUpVector, &planeCross);
  383. planeCross.normalize();
  384. Point3F planeNormal;
  385. Point3F intersectPoint;
  386. mTerrainEditor->collide(event, intersectPoint);
  387. mCross(mTerrainUpVector, planeCross, &planeNormal);
  388. mIntersectionPlane.set(intersectPoint, planeNormal);
  389. // ok, we have the intersection point...
  390. // project the collision point onto the up vector of the terrain
  391. mPreviousZ = mDot(mTerrainUpVector, intersectPoint);
  392. // add to undo
  393. // and record the starting heights
  394. for(U32 i = 0; i < sel->size(); i++)
  395. {
  396. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  397. (*sel)[i].mStartHeight = (*sel)[i].mHeight;
  398. }
  399. }
  400. else if(type == Update)
  401. {
  402. // ok, collide the ray from the event with the intersection plane:
  403. Point3F intersectPoint;
  404. Point3F start = event.pos;
  405. Point3F end = start + event.vec * 1000;
  406. F32 t = mIntersectionPlane.intersect(start, end);
  407. m_point3F_interpolate( start, end, t, intersectPoint);
  408. F32 currentZ = mDot(mTerrainUpVector, intersectPoint);
  409. F32 diff = currentZ - mPreviousZ;
  410. for(U32 i = 0; i < sel->size(); i++)
  411. {
  412. (*sel)[i].mHeight = (*sel)[i].mStartHeight + diff * (*sel)[i].mWeight;
  413. // clamp it
  414. if((*sel)[i].mHeight < 0.f)
  415. (*sel)[i].mHeight = 0.f;
  416. if((*sel)[i].mHeight > 2047.f)
  417. (*sel)[i].mHeight = 2047.f;
  418. mTerrainEditor->setGridInfoHeight((*sel)[i]);
  419. }
  420. mTerrainEditor->scheduleGridUpdate();
  421. }
  422. else if(type == End)
  423. {
  424. mTerrainEditor->getRoot()->mouseUnlock(mTerrainEditor);
  425. }
  426. }
  427. //------------------------------------------------------------------------------
  428. AdjustHeightAction::AdjustHeightAction(TerrainEditor * editor) :
  429. BrushAdjustHeightAction(editor)
  430. {
  431. mCursor = 0;
  432. }
  433. void AdjustHeightAction::process(Selection *sel, const Gui3DMouseEvent & event, bool b, Type type)
  434. {
  435. Selection * curSel = mTerrainEditor->getCurrentSel();
  436. BrushAdjustHeightAction::process(curSel, event, b, type);
  437. }
  438. //------------------------------------------------------------------------------
  439. // flatten the primary selection then blend in the rest...
  440. void FlattenHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  441. {
  442. if(!sel->size())
  443. return;
  444. if(selChanged)
  445. {
  446. F32 average = 0.f;
  447. // get the average height
  448. U32 cPrimary = 0;
  449. for(U32 k = 0; k < sel->size(); k++)
  450. if((*sel)[k].mPrimarySelect)
  451. {
  452. cPrimary++;
  453. average += (*sel)[k].mHeight;
  454. }
  455. average /= cPrimary;
  456. // set it
  457. for(U32 i = 0; i < sel->size(); i++)
  458. {
  459. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  460. //
  461. if((*sel)[i].mPrimarySelect)
  462. (*sel)[i].mHeight = average;
  463. else
  464. {
  465. F32 h = average - (*sel)[i].mHeight;
  466. (*sel)[i].mHeight += (h * (*sel)[i].mWeight);
  467. }
  468. mTerrainEditor->setGridInfo((*sel)[i]);
  469. }
  470. mTerrainEditor->scheduleGridUpdate();
  471. }
  472. }
  473. //------------------------------------------------------------------------------
  474. void SmoothHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  475. {
  476. if(!sel->size())
  477. return;
  478. if(selChanged)
  479. {
  480. F32 avgHeight = 0.f;
  481. for(U32 k = 0; k < sel->size(); k++)
  482. {
  483. mTerrainEditor->getUndoSel()->add((*sel)[k]);
  484. avgHeight += (*sel)[k].mHeight;
  485. }
  486. avgHeight /= sel->size();
  487. // clamp the terrain smooth factor...
  488. if(mTerrainEditor->mSmoothFactor < 0.f)
  489. mTerrainEditor->mSmoothFactor = 0.f;
  490. if(mTerrainEditor->mSmoothFactor > 1.f)
  491. mTerrainEditor->mSmoothFactor = 1.f;
  492. // linear
  493. for(U32 i = 0; i < sel->size(); i++)
  494. {
  495. (*sel)[i].mHeight += (avgHeight - (*sel)[i].mHeight) * mTerrainEditor->mSmoothFactor * (*sel)[i].mWeight;
  496. mTerrainEditor->setGridInfo((*sel)[i]);
  497. }
  498. mTerrainEditor->scheduleGridUpdate();
  499. }
  500. }
  501. void SmoothSlopeAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  502. {
  503. if(!sel->size())
  504. return;
  505. if(selChanged)
  506. {
  507. // Perform simple 2d linear regression on x&z and y&z:
  508. // b = (Avg(xz) - Avg(x)Avg(z))/(Avg(x^2) - Avg(x)^2)
  509. Point2F prod(0.f, 0.f); // mean of product for covar
  510. Point2F avgSqr(0.f, 0.f); // mean sqr of x, y for var
  511. Point2F avgPos(0.f, 0.f);
  512. F32 avgHeight = 0.f;
  513. F32 z;
  514. Point2F pos;
  515. for(U32 k = 0; k < sel->size(); k++)
  516. {
  517. mTerrainEditor->getUndoSel()->add((*sel)[k]);
  518. pos = Point2F((*sel)[k].mGridPoint.gridPos.x, (*sel)[k].mGridPoint.gridPos.y);
  519. z = (*sel)[k].mHeight;
  520. prod += pos * z;
  521. avgSqr += pos * pos;
  522. avgPos += pos;
  523. avgHeight += z;
  524. }
  525. prod /= sel->size();
  526. avgSqr /= sel->size();
  527. avgPos /= sel->size();
  528. avgHeight /= sel->size();
  529. Point2F avgSlope = (prod - avgPos*avgHeight)/(avgSqr - avgPos*avgPos);
  530. F32 goalHeight;
  531. for(U32 i = 0; i < sel->size(); i++)
  532. {
  533. goalHeight = avgHeight + ((*sel)[i].mGridPoint.gridPos.x - avgPos.x)*avgSlope.x +
  534. ((*sel)[i].mGridPoint.gridPos.y - avgPos.y)*avgSlope.y;
  535. (*sel)[i].mHeight += (goalHeight - (*sel)[i].mHeight) * (*sel)[i].mWeight;
  536. mTerrainEditor->setGridInfo((*sel)[i]);
  537. }
  538. mTerrainEditor->scheduleGridUpdate();
  539. }
  540. }
  541. void PaintNoiseAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
  542. {
  543. // If this is the ending
  544. // mouse down event, then
  545. // update the noise values.
  546. if ( type == Begin )
  547. {
  548. mNoise.setSeed( Sim::getCurrentTime() );
  549. mNoise.fBm( &mNoiseData, mNoiseSize, 12, 1.0f, 5.0f );
  550. mNoise.getMinMax( &mNoiseData, &mMinMaxNoise.x, &mMinMaxNoise.y, mNoiseSize );
  551. mScale = 1.5f / ( mMinMaxNoise.x - mMinMaxNoise.y);
  552. }
  553. if( selChanged )
  554. {
  555. for( U32 i = 0; i < sel->size(); i++ )
  556. {
  557. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  558. const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos;
  559. const F32 noiseVal = mNoiseData[ ( gridPos.x % mNoiseSize ) +
  560. ( ( gridPos.y % mNoiseSize ) * mNoiseSize ) ];
  561. (*sel)[i].mHeight += (noiseVal - mMinMaxNoise.y * mScale) * (*sel)[i].mWeight * mTerrainEditor->mNoiseFactor;
  562. mTerrainEditor->setGridInfo((*sel)[i]);
  563. }
  564. mTerrainEditor->scheduleGridUpdate();
  565. }
  566. }
  567. /*
  568. void ThermalErosionAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
  569. {
  570. if( selChanged )
  571. {
  572. TerrainBlock *tblock = mTerrainEditor->getActiveTerrain();
  573. if ( !tblock )
  574. return;
  575. F32 height = 0;
  576. F32 maxHeight = 0;
  577. U32 shift = getBinLog2( TerrainBlock::BlockSize );
  578. for ( U32 x = 0; x < TerrainBlock::BlockSize; x++ )
  579. {
  580. for ( U32 y = 0; y < TerrainBlock::BlockSize; y++ )
  581. {
  582. height = fixedToFloat( tblock->getHeight( x, y ) );
  583. mTerrainHeights[ x + (y << 8)] = height;
  584. if ( height > maxHeight )
  585. maxHeight = height;
  586. }
  587. }
  588. //mNoise.erodeThermal( &mTerrainHeights, &mNoiseData, 30.0f, 5.0f, 5, TerrainBlock::BlockSize, tblock->getSquareSize(), maxHeight );
  589. mNoise.erodeHydraulic( &mTerrainHeights, &mNoiseData, 1, TerrainBlock::BlockSize );
  590. F32 heightDiff = 0;
  591. for( U32 i = 0; i < sel->size(); i++ )
  592. {
  593. mTerrainEditor->getUndoSel()->add((*sel)[i]);
  594. const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos;
  595. // Need to get the height difference
  596. // between the current height and the
  597. // erosion height to properly apply the
  598. // softness and pressure settings of the brush
  599. // for this selection.
  600. heightDiff = (*sel)[i].mHeight - mNoiseData[ gridPos.x + (gridPos.y << shift)];
  601. (*sel)[i].mHeight -= (heightDiff * (*sel)[i].mWeight);
  602. mTerrainEditor->setGridInfo((*sel)[i]);
  603. }
  604. mTerrainEditor->gridUpdateComplete();
  605. }
  606. }
  607. */
  608. IMPLEMENT_CONOBJECT( TerrainSmoothAction );
  609. ConsoleDocClass( TerrainSmoothAction,
  610. "@brief Terrain action used for leveling varying terrain heights smoothly.\n\n"
  611. "Editor use only.\n\n"
  612. "@internal"
  613. );
  614. TerrainSmoothAction::TerrainSmoothAction()
  615. : UndoAction( "Terrain Smoothing" )
  616. {
  617. }
  618. void TerrainSmoothAction::initPersistFields()
  619. {
  620. Parent::initPersistFields();
  621. }
  622. void TerrainSmoothAction::smooth( TerrainBlock *terrain, F32 factor, U32 steps )
  623. {
  624. AssertFatal( terrain, "TerrainSmoothAction::smooth() - Got null object!" );
  625. // Store our input parameters.
  626. mTerrainId = terrain->getId();
  627. mSteps = steps;
  628. mFactor = factor;
  629. // The redo can do the rest.
  630. redo();
  631. }
  632. DefineConsoleMethod( TerrainSmoothAction, smooth, void, ( TerrainBlock *terrain, F32 factor, U32 steps ), , "( TerrainBlock obj, F32 factor, U32 steps )")
  633. {
  634. if (terrain)
  635. object->smooth( terrain, factor, mClamp( steps, 1, 13 ) );
  636. }
  637. void TerrainSmoothAction::undo()
  638. {
  639. // First find the terrain from the id.
  640. TerrainBlock *terrain;
  641. if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
  642. return;
  643. // Get the terrain file.
  644. TerrainFile *terrFile = terrain->getFile();
  645. // Copy our stored heightmap to the file.
  646. terrFile->setHeightMap( mUnsmoothedHeights, false );
  647. // Tell the terrain to update itself.
  648. terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
  649. }
  650. void TerrainSmoothAction::redo()
  651. {
  652. // First find the terrain from the id.
  653. TerrainBlock *terrain;
  654. if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
  655. return;
  656. // Get the terrain file.
  657. TerrainFile *terrFile = terrain->getFile();
  658. // First copy the heightmap state.
  659. mUnsmoothedHeights = terrFile->getHeightMap();
  660. // Do the smooth.
  661. terrFile->smooth( mFactor, mSteps, false );
  662. // Tell the terrain to update itself.
  663. terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
  664. }