terrainActions.cpp 29 KB

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