1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "console/engineAPI.h"
- #include "platform/platform.h"
- #include "gui/worldEditor/terrainActions.h"
- #include "gui/core/guiCanvas.h"
- TerrainScratchPad gTerrainScratchPad;
- //------------------------------------------------------------------------------
- bool TerrainAction::isValid(GridInfo tile)
- {
- const bool slopeLimit = mTerrainEditor->mSlopeMinAngle > 0.0f || mTerrainEditor->mSlopeMaxAngle < 90.0f;
- const F32 minSlope = mSin(mDegToRad(90.0f - mTerrainEditor->mSlopeMinAngle));
- const F32 maxSlope = mSin(mDegToRad(90.0f - mTerrainEditor->mSlopeMaxAngle));
- const TerrainBlock* terrain = mTerrainEditor->getActiveTerrain();
- const F32 squareSize = terrain->getSquareSize();
- Point2F p;
- Point3F norm;
- if (slopeLimit)
- {
- p.x = tile.mGridPoint.gridPos.x * squareSize;
- p.y = tile.mGridPoint.gridPos.y * squareSize;
- if (!terrain->getNormal(p, &norm, true))
- return false;
- if (norm.z > minSlope ||
- norm.z < maxSlope)
- return false;
- }
- if (tile.mHeight < mTerrainEditor->mTileMinHeight || tile.mHeight > mTerrainEditor->mTileMaxHeight)
- return false;
- return true;
- }
- void SelectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type)
- {
- if(sel == mTerrainEditor->getCurrentSel())
- return;
- if(type == Process)
- return;
- if(selChanged)
- {
- if(event.modifier & SI_MULTISELECT)
- {
- for(U32 i = 0; i < sel->size(); i++)
- mTerrainEditor->getCurrentSel()->remove((*sel)[i]);
- }
- else
- {
- for(U32 i = 0; i < sel->size(); i++)
- {
- GridInfo gInfo;
- if(mTerrainEditor->getCurrentSel()->getInfo((*sel)[i].mGridPoint.gridPos, gInfo))
- {
- if(!gInfo.mPrimarySelect)
- gInfo.mPrimarySelect = (*sel)[i].mPrimarySelect;
- if(gInfo.mWeight < (*sel)[i].mWeight)
- gInfo.mWeight = (*sel)[i].mWeight;
- mTerrainEditor->getCurrentSel()->setInfo(gInfo);
- }
- else
- mTerrainEditor->getCurrentSel()->add((*sel)[i]);
- }
- }
- }
- }
- void DeselectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type)
- {
- if(sel == mTerrainEditor->getCurrentSel())
- return;
- if(type == Process)
- return;
- if(selChanged)
- {
- for(U32 i = 0; i < sel->size(); i++)
- mTerrainEditor->getCurrentSel()->remove((*sel)[i]);
- }
- }
- //------------------------------------------------------------------------------
- void SoftSelectAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
- {
- TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain();
- if ( !terrBlock )
- return;
-
- // allow process of current selection
- Selection tmpSel;
- if(sel == mTerrainEditor->getCurrentSel())
- {
- tmpSel = *sel;
- sel = &tmpSel;
- }
- if(type == Begin || type == Process)
- mFilter.set(1, &mTerrainEditor->mSoftSelectFilter);
- //
- if(selChanged)
- {
- F32 radius = mTerrainEditor->mSoftSelectRadius;
- if(radius == 0.f)
- return;
- S32 squareSize = terrBlock->getSquareSize();
- U32 offset = U32(radius / F32(squareSize)) + 1;
- for(U32 i = 0; i < sel->size(); i++)
- {
- GridInfo & info = (*sel)[i];
- info.mPrimarySelect = true;
- info.mWeight = mFilter.getValue(0);
- if(!mTerrainEditor->getCurrentSel()->add(info))
- mTerrainEditor->getCurrentSel()->setInfo(info);
- Point2F infoPos((F32)info.mGridPoint.gridPos.x, (F32)info.mGridPoint.gridPos.y);
- //
- for(S32 x = info.mGridPoint.gridPos.x - offset; x < info.mGridPoint.gridPos.x + (offset << 1); x++)
- for(S32 y = info.mGridPoint.gridPos.y - offset; y < info.mGridPoint.gridPos.y + (offset << 1); y++)
- {
- //
- Point2F pos((F32)x, (F32)y);
- F32 dist = Point2F(pos - infoPos).len() * F32(squareSize);
- if(dist > radius)
- continue;
- F32 weight = mFilter.getValue(dist / radius);
- //
- GridInfo gInfo;
- GridPoint gridPoint = info.mGridPoint;
- gridPoint.gridPos.set(x, y);
- if(mTerrainEditor->getCurrentSel()->getInfo(Point2I(x, y), gInfo))
- {
- if(gInfo.mPrimarySelect)
- continue;
- if(gInfo.mWeight < weight)
- {
- gInfo.mWeight = weight;
- mTerrainEditor->getCurrentSel()->setInfo(gInfo);
- }
- }
- else
- {
- Vector<GridInfo> gInfos;
- mTerrainEditor->getGridInfos(gridPoint, gInfos);
- for (U32 z = 0; z < gInfos.size(); z++)
- {
- gInfos[z].mWeight = weight;
- gInfos[z].mPrimarySelect = false;
- mTerrainEditor->getCurrentSel()->add(gInfos[z]);
- }
- }
- }
- }
- }
- }
- //------------------------------------------------------------------------------
- void OutlineSelectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type)
- {
- TORQUE_UNUSED(sel); TORQUE_UNUSED(event); TORQUE_UNUSED(type);
- switch(type)
- {
- case Begin:
- if(event.modifier & SI_SHIFT)
- break;
- mTerrainEditor->getCurrentSel()->reset();
- break;
- case End:
- case Update:
- default:
- return;
- }
- mLastEvent = event;
- }
- //------------------------------------------------------------------------------
- void PaintMaterialAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
- {
- S32 mat = mTerrainEditor->getPaintMaterialIndex();
- if ( !selChanged || mat < 0 )
- return;
- for( U32 i = 0; i < sel->size(); i++ )
- {
- GridInfo &inf = (*sel)[i];
- if (!isValid(inf))
- continue;
- // If grid is already set to our material, or it is an
- // empty grid spot, then skip painting.
- if ( inf.mMaterial == mat || inf.mMaterial == U8_MAX )
- continue;
- if ( mRandF() > mTerrainEditor->getBrushPressure() )
- continue;
- inf.mMaterialChanged = true;
- mTerrainEditor->getUndoSel()->add(inf);
- // Painting is really simple now... set the one mat index.
- inf.mMaterial = mat;
- mTerrainEditor->setGridInfo(inf, true);
- }
- mTerrainEditor->scheduleMaterialUpdate();
- }
- //------------------------------------------------------------------------------
- void ClearMaterialsAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
- {
- if(selChanged)
- {
- for(U32 i = 0; i < sel->size(); i++)
- {
- GridInfo &inf = (*sel)[i];
- mTerrainEditor->getUndoSel()->add(inf);
- inf.mMaterialChanged = true;
- // Reset to the first texture layer.
- inf.mMaterial = 0;
- mTerrainEditor->setGridInfo(inf);
- }
- mTerrainEditor->scheduleMaterialUpdate();
- }
- }
- //------------------------------------------------------------------------------
- void RaiseHeightAction::process( Selection *sel, const Gui3DMouseEvent &evt, bool selChanged, Type type )
- {
- // ok the raise height action is our "dirt pour" action
- // only works on brushes...
- Brush *brush = dynamic_cast<Brush*>(sel);
- if ( !brush )
- return;
- if ( type == End )
- return;
- Point2I brushPos = brush->getPosition();
- GridPoint brushGridPoint = brush->getGridPoint();
- Vector<GridInfo> cur; // the height at the brush position
- mTerrainEditor->getGridInfos(brushGridPoint, cur);
- if ( cur.size() == 0 )
- return;
- // we get 30 process actions per second (at least)
- F32 heightAdjust = mTerrainEditor->mAdjustHeightVal / 30;
- // nothing can get higher than the current brush pos adjusted height
- F32 maxHeight = cur[0].mHeight + heightAdjust;
- for ( U32 i = 0; i < sel->size(); i++ )
- {
- if (!isValid((*sel)[i]))
- continue;
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- if ( (*sel)[i].mHeight < maxHeight )
- {
- (*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight;
- if ( (*sel)[i].mHeight > maxHeight )
- (*sel)[i].mHeight = maxHeight;
- }
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- //------------------------------------------------------------------------------
- void LowerHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
- {
- // ok the lower height action is our "dirt dig" action
- // only works on brushes...
- Brush *brush = dynamic_cast<Brush *>(sel);
- if(!brush)
- return;
- if ( type == End )
- return;
- Point2I brushPos = brush->getPosition();
- GridPoint brushGridPoint = brush->getGridPoint();
- Vector<GridInfo> cur; // the height at the brush position
- mTerrainEditor->getGridInfos(brushGridPoint, cur);
- if (cur.size() == 0)
- return;
- // we get 30 process actions per second (at least)
- F32 heightAdjust = -mTerrainEditor->mAdjustHeightVal / 30;
- // nothing can get higher than the current brush pos adjusted height
- F32 maxHeight = cur[0].mHeight + heightAdjust;
- if(maxHeight < 0)
- maxHeight = 0;
- for(U32 i = 0; i < sel->size(); i++)
- {
- if (!isValid((*sel)[i]))
- continue;
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- if((*sel)[i].mHeight > maxHeight)
- {
- (*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight;
- if((*sel)[i].mHeight < maxHeight)
- (*sel)[i].mHeight = maxHeight;
- }
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- //------------------------------------------------------------------------------
- void SetHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
- {
- if(selChanged)
- {
- for(U32 i = 0; i < sel->size(); i++)
- {
- if (!isValid((*sel)[i]))
- continue;
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- (*sel)[i].mHeight = mTerrainEditor->mSetHeightVal;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- }
- //------------------------------------------------------------------------------
- void SetEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
- {
- if ( !selChanged )
- return;
- mTerrainEditor->setMissionDirty();
- for ( U32 i = 0; i < sel->size(); i++ )
- {
- GridInfo &inf = (*sel)[i];
- // Skip already empty blocks.
- if ( inf.mMaterial == U8_MAX )
- continue;
- if (!isValid(inf))
- continue;
- // The change flag needs to be set on the undo
- // so that it knows to restore materials.
- inf.mMaterialChanged = true;
- mTerrainEditor->getUndoSel()->add( inf );
- // Set the material to empty.
- inf.mMaterial = -1;
- mTerrainEditor->setGridInfo( inf );
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- //------------------------------------------------------------------------------
- void ClearEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
- {
- if ( !selChanged )
- return;
- mTerrainEditor->setMissionDirty();
- for ( U32 i = 0; i < sel->size(); i++ )
- {
- GridInfo &inf = (*sel)[i];
- // Skip if not empty.
- if ( inf.mMaterial != U8_MAX )
- continue;
- // The change flag needs to be set on the undo
- // so that it knows to restore materials.
- inf.mMaterialChanged = true;
- mTerrainEditor->getUndoSel()->add( inf );
- // Set the material
- inf.mMaterial = 0;
- mTerrainEditor->setGridInfo( inf );
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- //------------------------------------------------------------------------------
- void ScaleHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
- {
- if(selChanged)
- {
- for(U32 i = 0; i < sel->size(); i++)
- {
- if (!isValid((*sel)[i]))
- continue;
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- (*sel)[i].mHeight *= mTerrainEditor->mScaleVal;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- }
- void BrushAdjustHeightAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type)
- {
- if(type == Process)
- return;
- TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain();
- if ( !terrBlock )
- return;
- if(type == Begin)
- {
- mTerrainEditor->lockSelection(true);
- mTerrainEditor->getRoot()->mouseLock(mTerrainEditor);
- // the way this works is:
- // construct a plane that goes through the collision point
- // with one axis up the terrain Z, and horizontally parallel to the
- // plane of projection
- // the cross of the camera ffdv and the terrain up vector produces
- // the cross plane vector.
- // all subsequent mouse actions are collided against the plane and the deltaZ
- // from the previous position is used to delta the selection up and down.
- Point3F cameraDir;
- EditTSCtrl::smCamMatrix.getColumn(1, &cameraDir);
- terrBlock->getTransform().getColumn(2, &mTerrainUpVector);
- // ok, get the cross vector for the plane:
- Point3F planeCross;
- mCross(cameraDir, mTerrainUpVector, &planeCross);
- planeCross.normalize();
- Point3F planeNormal;
- Point3F intersectPoint;
- mTerrainEditor->collide(event, intersectPoint);
- mCross(mTerrainUpVector, planeCross, &planeNormal);
- mIntersectionPlane.set(intersectPoint, planeNormal);
- // ok, we have the intersection point...
- // project the collision point onto the up vector of the terrain
- mPreviousZ = mDot(mTerrainUpVector, intersectPoint);
- // add to undo
- // and record the starting heights
- for(U32 i = 0; i < sel->size(); i++)
- {
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- (*sel)[i].mStartHeight = (*sel)[i].mHeight;
- }
- }
- else if(type == Update)
- {
- // ok, collide the ray from the event with the intersection plane:
- Point3F intersectPoint;
- Point3F start = event.pos;
- Point3F end = start + event.vec * 1000;
- F32 t = mIntersectionPlane.intersect(start, end);
- m_point3F_interpolate( start, end, t, intersectPoint);
- F32 currentZ = mDot(mTerrainUpVector, intersectPoint);
- F32 diff = currentZ - mPreviousZ;
- for(U32 i = 0; i < sel->size(); i++)
- {
- (*sel)[i].mHeight = (*sel)[i].mStartHeight + diff * (*sel)[i].mWeight;
- // clamp it
- if((*sel)[i].mHeight < 0.f)
- (*sel)[i].mHeight = 0.f;
- if((*sel)[i].mHeight > 2047.f)
- (*sel)[i].mHeight = 2047.f;
- mTerrainEditor->setGridInfoHeight((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- else if(type == End)
- {
- mTerrainEditor->getRoot()->mouseUnlock(mTerrainEditor);
- }
- }
- //------------------------------------------------------------------------------
- AdjustHeightAction::AdjustHeightAction(TerrainEditor * editor) :
- BrushAdjustHeightAction(editor)
- {
- mCursor = 0;
- }
- void AdjustHeightAction::process(Selection *sel, const Gui3DMouseEvent & event, bool b, Type type)
- {
- Selection * curSel = mTerrainEditor->getCurrentSel();
- BrushAdjustHeightAction::process(curSel, event, b, type);
- }
- //------------------------------------------------------------------------------
- // flatten the primary selection then blend in the rest...
- void FlattenHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
- {
- if(!sel->size())
- return;
- if(selChanged)
- {
- F32 average = 0.f;
- // get the average height
- U32 cPrimary = 0;
- for(U32 k = 0; k < sel->size(); k++)
- if((*sel)[k].mPrimarySelect)
- {
- cPrimary++;
- average += (*sel)[k].mHeight;
- }
- average /= cPrimary;
- // set it
- for(U32 i = 0; i < sel->size(); i++)
- {
- if (!isValid((*sel)[i]))
- continue;
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- //
- if((*sel)[i].mPrimarySelect)
- (*sel)[i].mHeight = average;
- else
- {
- F32 h = average - (*sel)[i].mHeight;
- (*sel)[i].mHeight += (h * (*sel)[i].mWeight);
- }
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- }
- //------------------------------------------------------------------------------
- void SmoothHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
- {
- if(!sel->size())
- return;
- if(selChanged)
- {
- F32 avgHeight = 0.f;
- for(U32 k = 0; k < sel->size(); k++)
- {
- mTerrainEditor->getUndoSel()->add((*sel)[k]);
- avgHeight += (*sel)[k].mHeight;
- }
- avgHeight /= sel->size();
- // clamp the terrain smooth factor...
- if(mTerrainEditor->mSmoothFactor < 0.f)
- mTerrainEditor->mSmoothFactor = 0.f;
- if(mTerrainEditor->mSmoothFactor > 1.f)
- mTerrainEditor->mSmoothFactor = 1.f;
- // linear
- for(U32 i = 0; i < sel->size(); i++)
- {
- (*sel)[i].mHeight += (avgHeight - (*sel)[i].mHeight) * mTerrainEditor->mSmoothFactor * (*sel)[i].mWeight;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- }
- void SmoothSlopeAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
- {
- if(!sel->size())
- return;
-
- if(selChanged)
- {
- // Perform simple 2d linear regression on x&z and y&z:
- // b = (Avg(xz) - Avg(x)Avg(z))/(Avg(x^2) - Avg(x)^2)
- Point2F prod(0.f, 0.f); // mean of product for covar
- Point2F avgSqr(0.f, 0.f); // mean sqr of x, y for var
- Point2F avgPos(0.f, 0.f);
- F32 avgHeight = 0.f;
- F32 z;
- Point2F pos;
- for(U32 k = 0; k < sel->size(); k++)
- {
- mTerrainEditor->getUndoSel()->add((*sel)[k]);
- pos = Point2F((*sel)[k].mGridPoint.gridPos.x, (*sel)[k].mGridPoint.gridPos.y);
- z = (*sel)[k].mHeight;
-
- prod += pos * z;
- avgSqr += pos * pos;
- avgPos += pos;
- avgHeight += z;
- }
-
- prod /= sel->size();
- avgSqr /= sel->size();
- avgPos /= sel->size();
- avgHeight /= sel->size();
-
- Point2F avgSlope = (prod - avgPos*avgHeight)/(avgSqr - avgPos*avgPos);
-
- F32 goalHeight;
- for(U32 i = 0; i < sel->size(); i++)
- {
- goalHeight = avgHeight + ((*sel)[i].mGridPoint.gridPos.x - avgPos.x)*avgSlope.x +
- ((*sel)[i].mGridPoint.gridPos.y - avgPos.y)*avgSlope.y;
- (*sel)[i].mHeight += (goalHeight - (*sel)[i].mHeight) * (*sel)[i].mWeight;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- }
- void PaintNoiseAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
- {
- // If this is the ending
- // mouse down event, then
- // update the noise values.
- if ( type == Begin )
- {
- mNoise.setSeed( Sim::getCurrentTime() );
- mNoise.fBm( &mNoiseData, mNoiseSize, 12, 1.0f, 5.0f );
- mNoise.getMinMax( &mNoiseData, &mMinMaxNoise.x, &mMinMaxNoise.y, mNoiseSize );
-
- mScale = 1.5f / ( mMinMaxNoise.x - mMinMaxNoise.y);
- }
- if( selChanged )
- {
- for( U32 i = 0; i < sel->size(); i++ )
- {
- if (!isValid((*sel)[i]))
- continue;
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos;
- const F32 noiseVal = mNoiseData[ ( gridPos.x % mNoiseSize ) +
- ( ( gridPos.y % mNoiseSize ) * mNoiseSize ) ];
- (*sel)[i].mHeight += (noiseVal - mMinMaxNoise.y * mScale) * (*sel)[i].mWeight * mTerrainEditor->mNoiseFactor;
- if ((*sel)[i].mHeight > mTerrainEditor->mTileMaxHeight)
- (*sel)[i].mHeight = mTerrainEditor->mTileMaxHeight;
- if ((*sel)[i].mHeight < mTerrainEditor->mTileMinHeight)
- (*sel)[i].mHeight = mTerrainEditor->mTileMinHeight;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- }
- void ThermalErosionAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
- {
- // If this is the ending
- // mouse down event, then
- // update the noise values.
- TerrainBlock* tblock = mTerrainEditor->getActiveTerrain();
- if (!tblock)
- return;
- F32 selRange = sel->getMaxHeight()-sel->getMinHeight();
- F32 avg = sel->getAvgHeight();
- if (selChanged)
- {
- F32 heightDiff = 0;
- for (U32 i = 0; i < sel->size(); i++)
- {
- if (!isValid((*sel)[i]))
- continue;
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- F32 bias = ((*sel)[i].mHeight - avg) / selRange;
- F32 nudge = mRandF(-mTerrainEditor->getBrushPressure(), mTerrainEditor->getBrushPressure());
- F32 heightTarg = mRoundF((*sel)[i].mHeight - bias * nudge, mTerrainEditor->getBrushPressure() * 2.0f) ;
- heightDiff = heightTarg - (*sel)[i].mHeight;
- (*sel)[i].mHeight += heightDiff * (*sel)[i].mWeight;
- if ((*sel)[i].mHeight > mTerrainEditor->mTileMaxHeight)
- (*sel)[i].mHeight = mTerrainEditor->mTileMaxHeight;
- if ((*sel)[i].mHeight < mTerrainEditor->mTileMinHeight)
- (*sel)[i].mHeight = mTerrainEditor->mTileMinHeight;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- }
- void HydraulicErosionAction::process(Selection* sel, const Gui3DMouseEvent&, bool selChanged, Type type)
- {
- // If this is the ending
- // mouse down event, then
- // update the noise values.
- TerrainBlock* tblock = mTerrainEditor->getActiveTerrain();
- if (!tblock)
- return;
- F32 selRange = sel->getMaxHeight() - sel->getMinHeight();
- F32 avg = sel->getAvgHeight();
- if (selChanged)
- {
- F32 heightDiff = 0;
- const F32 squareSize = tblock->getSquareSize();
- for (U32 i = 0; i < sel->size(); i++)
- {
- if (!isValid((*sel)[i]))
- continue;
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- Point2F p;
- Point3F norm;
- p.x = (*sel)[i].mGridPoint.gridPos.x * squareSize;
- p.y = (*sel)[i].mGridPoint.gridPos.y * squareSize;
- tblock->getNormal(p, &norm, true);
- F32 bias = mPow(norm.z,3.0f) * ((*sel)[i].mHeight - avg) / selRange;
- F32 nudge = mRandF(-mTerrainEditor->getBrushPressure(), mTerrainEditor->getBrushPressure());
- heightDiff = bias * (-(*sel)[i].mHeight + bias * nudge) / tblock->getObjBox().len_z() * 2.0;
- (*sel)[i].mHeight += heightDiff * (*sel)[i].mWeight;
- if ((*sel)[i].mHeight > mTerrainEditor->mTileMaxHeight)
- (*sel)[i].mHeight = mTerrainEditor->mTileMaxHeight;
- if ((*sel)[i].mHeight < mTerrainEditor->mTileMinHeight)
- (*sel)[i].mHeight = mTerrainEditor->mTileMinHeight;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- mTerrainEditor->scheduleGridUpdate();
- }
- }
- void TerrainScratchPad::addTile(F32 height, U8 material)
- {
- mContents.push_back(new gridStub(height, material));
- mBottom = mMin(height, mBottom);
- mTop = mMax(height, mTop);
- };
- void TerrainScratchPad::clear()
- {
- for (U32 i = 0; i < mContents.size(); i++)
- delete(mContents[i]);
- mContents.clear();
- mBottom = F32_MAX;
- mTop = F32_MIN_EX;
- }
- void copyAction::process(Selection* sel, const Gui3DMouseEvent&, bool selChanged, Type type)
- {
- gTerrainScratchPad.clear();
- for (U32 i=0;i<sel->size();i++)
- {
- if (isValid((*sel)[i]))
- gTerrainScratchPad.addTile((*sel)[i].mHeight, (*sel)[i].mMaterial);
- else
- gTerrainScratchPad.addTile(0, 0);
- }
- }
- void pasteAction::process(Selection* sel, const Gui3DMouseEvent&, bool selChanged, Type type)
- {
- if (gTerrainScratchPad.size() == 0)
- return;
- if (gTerrainScratchPad.size() != sel->size())
- return;
- if (type != Begin)
- return;
- for (U32 i = 0; i < sel->size(); i++)
- {
- if (isValid((*sel)[i]))
- {
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- (*sel)[i].mHeight = gTerrainScratchPad[i]->mHeight;
- (*sel)[i].mMaterial = gTerrainScratchPad[i]->mMaterial;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- }
- mTerrainEditor->scheduleGridUpdate();
- mTerrainEditor->scheduleMaterialUpdate();
- }
- void pasteUpAction::process(Selection* sel, const Gui3DMouseEvent&, bool selChanged, Type type)
- {
- if (gTerrainScratchPad.size() == 0)
- return;
- if (gTerrainScratchPad.size() != sel->size())
- return;
- if (type != Begin)
- return;
- F32 floor = F32_MAX;
- for (U32 i = 0; i < sel->size(); i++)
- {
- floor = mMin((*sel)[i].mHeight, floor);
- }
- for (U32 i = 0; i < sel->size(); i++)
- {
- if (isValid((*sel)[i]))
- {
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- (*sel)[i].mHeight = gTerrainScratchPad[i]->mHeight - gTerrainScratchPad.mBottom + floor;
- (*sel)[i].mMaterial = gTerrainScratchPad[i]->mMaterial;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- }
- mTerrainEditor->scheduleGridUpdate();
- mTerrainEditor->scheduleMaterialUpdate();
- }
- void pasteDownAction::process(Selection* sel, const Gui3DMouseEvent&, bool selChanged, Type type)
- {
- if (gTerrainScratchPad.size() == 0)
- return;
- if (gTerrainScratchPad.size() != sel->size())
- return;
- if (type != Begin)
- return;
- F32 ceiling = F32_MIN_EX;
- for (U32 i = 0; i < sel->size(); i++)
- {
- ceiling = mMax((*sel)[i].mHeight, ceiling);
- }
- for (U32 i = 0; i < sel->size(); i++)
- {
- if (isValid((*sel)[i]))
- {
- mTerrainEditor->getUndoSel()->add((*sel)[i]);
- (*sel)[i].mHeight = gTerrainScratchPad[i]->mHeight - gTerrainScratchPad.mTop + ceiling;
- (*sel)[i].mMaterial = gTerrainScratchPad[i]->mMaterial;
- mTerrainEditor->setGridInfo((*sel)[i]);
- }
- }
- mTerrainEditor->scheduleGridUpdate();
- mTerrainEditor->scheduleMaterialUpdate();
- }
- IMPLEMENT_CONOBJECT( TerrainSmoothAction );
- ConsoleDocClass( TerrainSmoothAction,
- "@brief Terrain action used for leveling varying terrain heights smoothly.\n\n"
- "Editor use only.\n\n"
- "@internal"
- );
- TerrainSmoothAction::TerrainSmoothAction()
- : UndoAction("Terrain Smoothing"), mFactor(1.0), mSteps(1), mTerrainId(0)
- {
- }
- void TerrainSmoothAction::initPersistFields()
- {
- docsURL;
- Parent::initPersistFields();
- }
- void TerrainSmoothAction::smooth( TerrainBlock *terrain, F32 factor, U32 steps )
- {
- AssertFatal( terrain, "TerrainSmoothAction::smooth() - Got null object!" );
- // Store our input parameters.
- mTerrainId = terrain->getId();
- mSteps = steps;
- mFactor = factor;
- // The redo can do the rest.
- redo();
- }
- DefineEngineMethod( TerrainSmoothAction, smooth, void, ( TerrainBlock *terrain, F32 factor, U32 steps ), , "( TerrainBlock obj, F32 factor, U32 steps )")
- {
- if (terrain)
- object->smooth( terrain, factor, mClamp( steps, 1, 13 ) );
- }
- void TerrainSmoothAction::undo()
- {
- // First find the terrain from the id.
- TerrainBlock *terrain;
- if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
- return;
- // Get the terrain file.
- TerrainFile *terrFile = terrain->getFile();
- // Copy our stored heightmap to the file.
- terrFile->setHeightMap( mUnsmoothedHeights, false );
- // Tell the terrain to update itself.
- terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
- }
- void TerrainSmoothAction::redo()
- {
- // First find the terrain from the id.
- TerrainBlock *terrain;
- if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
- return;
- // Get the terrain file.
- TerrainFile *terrFile = terrain->getFile();
- // First copy the heightmap state.
- mUnsmoothedHeights = terrFile->getHeightMap();
- // Do the smooth.
- terrFile->smooth( mFactor, mSteps, false );
- // Tell the terrain to update itself.
- terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
- }
|