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