Merge pull request #1388 from Azaezel/alpha41/erosionBrushes

additional terrain brushes
This commit is contained in:
Brian Roberts 2025-02-20 12:08:22 -06:00 committed by GitHub
commit 7bb8587db5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 349 additions and 62 deletions

View file

@ -26,6 +26,7 @@
#include "gui/core/guiCanvas.h"
TerrainScratchPad gTerrainScratchPad;
//------------------------------------------------------------------------------
bool TerrainAction::isValid(GridInfo tile)
{
@ -746,60 +747,212 @@ void PaintNoiseAction::process(Selection * sel, const Gui3DMouseEvent &, bool se
mTerrainEditor->scheduleGridUpdate();
}
}
/*
void ThermalErosionAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
void ThermalErosionAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
{
if( selChanged )
// 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)
{
TerrainBlock *tblock = mTerrainEditor->getActiveTerrain();
if ( !tblock )
return;
F32 height = 0;
F32 maxHeight = 0;
U32 shift = getBinLog2( TerrainBlock::BlockSize );
for ( U32 x = 0; x < TerrainBlock::BlockSize; x++ )
{
for ( U32 y = 0; y < TerrainBlock::BlockSize; y++ )
{
height = fixedToFloat( tblock->getHeight( x, y ) );
mTerrainHeights[ x + (y << 8)] = height;
if ( height > maxHeight )
maxHeight = height;
}
}
//mNoise.erodeThermal( &mTerrainHeights, &mNoiseData, 30.0f, 5.0f, 5, TerrainBlock::BlockSize, tblock->getSquareSize(), maxHeight );
mNoise.erodeHydraulic( &mTerrainHeights, &mNoiseData, 1, TerrainBlock::BlockSize );
F32 heightDiff = 0;
for( U32 i = 0; i < sel->size(); i++ )
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;
// Need to get the height difference
// between the current height and the
// erosion height to properly apply the
// softness and pressure settings of the brush
// for this selection.
heightDiff = (*sel)[i].mHeight - mNoiseData[ gridPos.x + (gridPos.y << shift)];
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;
(*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->gridUpdateComplete();
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 );

View file

@ -302,27 +302,94 @@ class PaintNoiseAction : public TerrainAction
F32 mScale;
};
/*
class ThermalErosionAction : public TerrainAction
{
public:
ThermalErosionAction(TerrainEditor * editor)
: TerrainAction(editor)
{
mNoise.setSeed( 1 );//Sim::getCurrentTime() );
mNoiseData.setSize( TerrainBlock::BlockSize * TerrainBlock::BlockSize );
mTerrainHeights.setSize( TerrainBlock::BlockSize * TerrainBlock::BlockSize );
}
}
StringTableEntry getName(){return("thermalErode");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
Noise2D mNoise;
Vector<F32> mNoiseData;
Vector<F32> mTerrainHeights;
};
*/
class HydraulicErosionAction : public TerrainAction
{
public:
HydraulicErosionAction(TerrainEditor* editor)
: TerrainAction(editor)
{
}
StringTableEntry getName() { return("hydraulicErode"); }
void process(Selection* sel, const Gui3DMouseEvent& event, bool selChanged, Type type);
};
class TerrainScratchPad
{
public:
F32 mBottom, mTop;
TerrainScratchPad(): mBottom(F32_MAX), mTop(F32_MIN_EX){};
~TerrainScratchPad() { mContents.clear(); };
void clear();
class gridStub
{
public:
gridStub(F32 height, U8 material) : mHeight(height), mMaterial(material) {};
F32 mHeight;
U8 mMaterial;
};
void addTile(F32 height, U8 material);
U32 size() { return(mContents.size()); };
gridStub* operator [](U32 index) { return mContents[index]; };
private:
Vector<gridStub*> mContents;
};
class copyAction : public TerrainAction
{
public:
copyAction(TerrainEditor* editor)
: TerrainAction(editor)
{
}
StringTableEntry getName() { return("copy"); }
void process(Selection* sel, const Gui3DMouseEvent& event, bool selChanged, Type type);
};
class pasteAction : public TerrainAction
{
public:
pasteAction(TerrainEditor* editor)
: TerrainAction(editor)
{
}
StringTableEntry getName() { return("paste"); }
void process(Selection* sel, const Gui3DMouseEvent& event, bool selChanged, Type type);
};
class pasteUpAction : public TerrainAction
{
public:
pasteUpAction(TerrainEditor* editor)
: TerrainAction(editor)
{
}
StringTableEntry getName() { return("pasteUp"); }
void process(Selection* sel, const Gui3DMouseEvent& event, bool selChanged, Type type);
};
class pasteDownAction : public TerrainAction
{
public:
pasteDownAction(TerrainEditor* editor)
: TerrainAction(editor)
{
}
StringTableEntry getName() { return("pasteDown"); }
void process(Selection* sel, const Gui3DMouseEvent& event, bool selChanged, Type type);
};
/// An undo action used to perform terrain wide smoothing.
class TerrainSmoothAction : public UndoAction

View file

@ -712,8 +712,13 @@ TerrainEditor::TerrainEditor() :
mActions.push_back(new SmoothHeightAction(this));
mActions.push_back(new SmoothSlopeAction(this));
mActions.push_back(new PaintNoiseAction(this));
//mActions.push_back(new ThermalErosionAction(this));
mActions.push_back(new ThermalErosionAction(this));
mActions.push_back(new HydraulicErosionAction(this));
mActions.push_back(new copyAction(this));
mActions.push_back(new pasteAction(this));
mActions.push_back(new pasteUpAction(this));
mActions.push_back(new pasteDownAction(this));
// set the default action
mCurrentAction = mActions[0];