Terrain smoothing

Falagard

19-06-2006 04:03:49

I'd like to get terrain smoothing working in addition to raise and lower deformation by using something like this:


//get the existing height from the scene
//this actually modifies our mBrushArray
mSceneMgr->setOption("fillBrushArray", &mBrushPos);

//now we're going to smooth the data in mBrushArray by blurring it or doing something like find the average height and
//move all the heights towards the average height by a percentage
//of their distance from the average height
for(uint i = 0; i < mBrushArrayWidth * mBrushArrayWidth; ++i)
{
//todo - actual code for smoothing here
}

//set height center takes brush array and modifies the height
//to the provided value
//I believe this is currently not working, so until it is fixed, smoothing
//can't be done
mSceneMgr->setOption("setHeightCenter", &mBrushPos);


Assuming that the brush array had already been set by me, am I using it correctly?

I think that fillBrushArray fills brush array with the height of the terrain where the impact point is, and setHeightCenter sets the height to the exact values stored in brush array, so using this I could get the values, modify them, and set them back right?

However, when I just call setOption("fillbrushArray", &mBrushPos) and then immediately after call setOption("setHeightCenter", &mBrushPos) I would expect the terrain to be not modified at all (sets the exact same values back into the terrain), however it is modified - by a huge height value, so something's broken in there, or I'm not using it correctly.

Clay

tuan kuranes

19-06-2006 08:52:54

Just found a big in terrain deforming making each input each scaled twice by brushcale.
Perhaps you can have a quick try with brushcale == 1.0f ?

Falagard

19-06-2006 17:38:29

Why does brush scale affect setOption("setHeightCenter") at all?

tuan kuranes

20-06-2006 15:02:10

did that fix it ?
Why does brush scale affect setOption("setHeightCenter") at all?
not sure to understand the question, what do you mean ?
Everything that uses brush array is scaled by brushcale ?
Perhaps brushscale handling should be done by user, in its own brush array, and removed from PLSM2?

Falagard

21-06-2006 16:26:15

No, it's just that I thought setHeightCenter's purpose is to set the exact height of a certain area of terrain. If I'm setting the exact height I don't want to scale it.

Anyhow, I haven't tested with brushscale = 1 yet. I'll let you know.

tuan kuranes

21-06-2006 16:40:09

"setHeightCenter" does call SetHeight using "brushArray" at this Center...

You mean I should split it in "SetHeightCenter" and "setHeightCenterApplyingBrushArray" or I should change the name ?

Falagard

22-06-2006 03:06:56

I don't know what I need.

How about you tell me how I'd get terrain smoothing working? Perhaps it should just be implemented directly in the paging landscape manager instead of me trying to do it externally?

tuan kuranes

22-06-2006 07:58:47

How about you tell me how I'd get terrain smoothing working
I'll check that.
Perhaps it should just be implemented directly in the paging landscape manager instead of me trying to do it externally?
smoothing is perhaps a particular case of user terrain modification needs. (wind erosion, water erosion, "plane before putting buiding", etc... )

Falagard

22-06-2006 18:30:23

Smoothing isn't a particular case, I think it's absolutely necessary.

Here are the main features you need for terrain deformation:

Raise, Lower, Smooth, Flatten.

Erosion, etc. are usually done over a broad area or to the entire terrain, not to specific areas on a point the user clicks on (right?).

draktheas

22-06-2006 21:55:47

You got it Falagard, Raise, Lower, Smooth, and Flatten are absolutely required. I have also seen Stamp and Extrude (which is basically an intersection or union of a mesh and the terrain). So if the mesh used to Stamp or Extrude is in the shape of a box and you use it on some terrain, you end up with a box shaped indentation or raised area in the terrain.

Drak

Jerky

23-06-2006 05:40:43

Erosion, etc. are usually done over a broad area or to the entire terrain, not to specific areas on a point the user clicks on (right?).
Bryce 5.5 allows you to pinpoint your erosion with a brush, over specific areas. I don't know about other programs though.

Falagard

23-06-2006 06:27:11

Shhhh Jerky. Shhhh.

Just kidding, yes I can see erosion being localized to a specific point. However, I do think that erosion is a nice to have, whereas smoothing is a must have.

tuan kuranes

23-06-2006 10:44:33

ok, I see the point.
Can you define more exactly smooth and flatten ?
(need exact spec to be sure to be on scope.)

Jerky

23-06-2006 20:16:59

IMO, the differences between smoothing and flattening would be as follows:

Smoothing would take the average angle of the terain within the brush and gradually flatten to that average angle, where as actually flattening would gradually make it 0 degrees (flat). In this way, you *could* use smooth to flatten, but you would have to increase the brush size to grab enough terrain to make the average 0 degrees. Otherwise, it actually smooths the terrain, no matter what the slope, while flattening always flattens to 0. Does that make sense? I could draw what I mean if you need.

Falagard

24-06-2006 04:24:46

Jerky's definitions are interesting.

I think smoothing definition is easy - perform a guassian blur on a selected set of data.

However, it'd be ideal to use the brusharray as the strength modifier on how much to blur and multiply times the brush strength. So for example, something like: get the data under the click point - blur it to get a result, then interpolate between blurred and the unblurred data by brusharray * brushstrength for each pixel.

Flattening is interesting, not sure how to actually implement actually. I personally would rather use a strong smooth value to get the same effect, so I think flattening is less valuable. Perhaps a simple way to set a fixed height and flatten everything to that fixed height would be good enough to flatten say... a field or value, or even a road?

asmo

02-03-2007 13:33:45

I'm sorry if I bring up an old topic, but I saw your comment in the GOOFed source, Falagard, about smoothing.
I'v got smoothing to work by doing the following:

Line 923 - OgrePagingLandscapeSceneManager.cpp:
Removed mul with Hscale, this way, you will get the actuall size in mHeightData at the correct position.

mBrushArray[curr_indx] = mData2DManager->getHeight(mImpactInfo->mPageX,
mImpactInfo->mPageZ, x, z); // * Hscale



Line 740 - OgrePagingLandscapeSceneManager.cpp:
Scale again? - Here we want to set the exact height.

const Ogre::Real h = mBrushArray[curr_indx];


Line numbers may be a bit of (2-5).

vanzu

05-03-2007 21:49:01

Thanks asmo that was useful to me at least :)

I started to get into PLSM2 just a few days ago so i really don't know if the getAreaHeight change might badly affect the way things work in people's code but if it is safe enough to make its way into a patch that would be great Tuan.

Btw there are many ways to blur a 2 dimensional set of data but i think the easiest would be box smoothing, where you give a point the average value of its 8 neighbors.



asmo

06-03-2007 11:42:27

@vanzu
Greate that it could be usefull for someone (:
I do believe the changes will not effect anything else, but of course, I'm not sure. There are many ways to smooth the terrain, none which worked without the changes above.

rt15

21-03-2007 10:35:08

Hi (And sorry for my bad english)

I'm trying to write some code to able users to deform my landscape.

I made three tools, one make planes, an other make hills and holes, and the last looks like a smoother.

All 3 ar ok, but I'm taking blue screens, after 2, 10 or 40 minutes of editing... :shock:

Here is my code. Maybe it'll help you to using setheighcenter and others functions.

Main tips are a minus (-), and the 5000.0f is the y scale of our land.

#include "ElevationEditorState.h"
#include "GUIManager.h"

#include "OgreLogManager.h"
#include "TestTerrain.h"
#include <math.h>

namespace DSO
{

void ElevationEditorState::enter()
{
// Ogre variable intialization
initOgreVars();
mCursor = new Cursor();

// Select first mode
mMode = EEM_Smooth;

resume();
}

void ElevationEditorState::exit()
{
pause();
delete mCursor;
}

void ElevationEditorState::pause()
{
unregisterEvents();
mCursor->disable();
mCamMgr->setDirectionLocked(false);
}

void ElevationEditorState::resume()
{
// Init GUI
GUIManager::getInstance()->setCurrentGUI("TerrainElevEdt.xml");
GUIManager::getInstance()->showCursor(true);
mCamMgr->setDirectionLocked(true);

// Cursor management
mMask.load("heightMapCircle.bmp", "General");
mCursor->setImage(mMask);
mCursor->enable();

// Put callbacks
registerEvents();
}

void ElevationEditorState::registerEvents()
{
addInputListeners();
}

void ElevationEditorState::unregisterEvents()
{
removeInputListeners();
}

void ElevationEditorState::Update(Timer::TimeElapsed timeSinceLastFrame)
{

}

bool ElevationEditorState::onKeyReleased(const OIS::KeyEvent& e)
{
if (e.key == OIS::KC_ESCAPE)
GameManager::getInstance()->popState();
else if (e.key == OIS::KC_SPACE)
mMode = (EES_Mode)(mMode == EEM_Flat ? 0 : mMode + 1);
return true;
}

bool ElevationEditorState::isMouseInGUI(const OIS::MouseEvent& e)
{
return false;
}

bool ElevationEditorState::onMouseMoved(const OIS::MouseEvent& e)
{
if (e.state.buttonDown(OIS::MB_Left) && (mWorkingPos != mWorkingPos.ZERO))
{
Ogre::uint size = mCursor->getSize() / 4;
Ogre::Real * deltaArray = new Ogre::Real[size * size];

switch (mMode)
{
case EEM_FullCos:

TestTerrain::getInstance()->getHeight(mWorkingPos, 0);
if ((mWorkingPos.y > 4750.0f) || (mWorkingPos.y < 250.0f))
{
delete[] deltaArray;
return true;
}

for (Ogre::uint i = 0 ; i < size ; i++)
for (Ogre::uint j = 0 ; j < size ; j++)
{
// Compute length between this point an the center
Ogre::Real r = sqrtf((i - size / 2) * (i - size / 2) + (j - size / 2) * (j - size / 2));

// Are we inthe circle ?
if (r / size < 0.5f)
deltaArray[i * size + j] = (-(Ogre::Real)e.state.relY + cosf(r / size * 6.28f) * -(Ogre::Real)e.state.relY) / 5000.0f;
else
deltaArray[i * size + j] = 0.0f;
}
editHeight(mWorkingPos, deltaArray, size, true);

break;
case EEM_Smooth:

if (mCursor->getCenter(mWorkingPos))
{
getHeight(mWorkingPos, deltaArray, size);


for (Ogre::uint i = 0 ; i < size ; i++)
for (Ogre::uint j = 0 ; j < size ; j++)
{
// Compute length between this point an the center
Ogre::Real r = sqrtf((i - size / 2) * (i - size / 2) + (j - size / 2) * (j - size / 2));

if ((r / size < 0.50f) && (i > 1) && (i < size - 2) && (j > 1) && (j < size - 2))
{
Ogre::Real moy;

// Average of altitudes of near points
moy = -deltaArray[(i-1) * size + j] +
deltaArray[(i+1) * size + j] -
deltaArray[i * size + j - 1] +
deltaArray[i * size + j + 1];

moy = moy / 2.0f;


deltaArray[i * size + j] = -(moy + deltaArray[i * size + j]) / 3.0f;
}
else
deltaArray[i * size + j] = -deltaArray[i * size + j];


// A test for checking values
if ((deltaArray[i * size + j] <= -0.99f) || (deltaArray[i * size + j] >= -0.01f))
{
delete[] deltaArray;
return true;
}

}

editHeight(mWorkingPos, deltaArray, size, false);

}

break;
case EEM_Flat:

mWorkingPos.y = mWorkingPos.y - (Ogre::Real)e.state.relY;
if (mWorkingPos.y > 4750.0f) mWorkingPos.y = 4750.0f;
if (mWorkingPos.y < 250.0f) mWorkingPos.y = 250.0f;

for (Ogre::uint i = 0 ; i < size ; i++)
for (Ogre::uint j = 0 ; j < size ; j++)
{
// Compute length between this point an the center
Ogre::Real r = sqrtf((i - size / 2) * (i - size / 2) + (j - size / 2) * (j - size / 2));

if (r / size < 0.5f)
deltaArray[i * size + j] = - mWorkingPos.y / 5000;
else
deltaArray[i * size + j] = 0.0f;
}
editHeight(mWorkingPos, deltaArray, size, false);

break;
default:
break;
}
delete[] deltaArray;
}
return true;
}

void ElevationEditorState::editHeight(Ogre::Vector3& center, Ogre::Real * heightMap, Ogre::uint size, bool relative)
{
// Put scale
Ogre::Real brushScale = 1.0f;
mSceneMgr->setOption("BrushScale", &brushScale);

// Put size
mSceneMgr->setOption("BrushArrayWidth", &size);
mSceneMgr->setOption("BrushArrayHeight", &size);

// Put array
mSceneMgr->setOption("BrushArray", heightMap);

// Apply array
if (relative)
mSceneMgr->setOption("DeformationCenter", &center);
else
mSceneMgr->setOption("setHeightCenter", &center);
}

void ElevationEditorState::getHeight(Ogre::Vector3& center, Ogre::Real * heightMap, Ogre::uint size)
{
// Put scale
Ogre::Real brushScale = 1.0f;
mSceneMgr->setOption("BrushScale", &brushScale);

// Put size
mSceneMgr->setOption("BrushArrayWidth", &size);
mSceneMgr->setOption("BrushArrayHeight", &size);

// Put array
mSceneMgr->setOption("BrushArray", heightMap);

// Fill array
mSceneMgr->setOption("fillBrushArray", &center);
}

bool ElevationEditorState::onMousePressed(const OIS::MouseEvent& e, OIS::MouseButtonID id)
{
if (e.state.buttonDown(OIS::MB_Left))
{
// To remind modified area
if (!mCursor->getCenter(mWorkingPos)) mWorkingPos = mWorkingPos.ZERO;

// Executing treatments which occurs after a click
if ((mMode == EEM_Flat) || (mMode == EEM_Smooth))
onMouseMoved(e);
}
return true;
}

} // namespace DSO


[edit]
Comments translated.
Add some precision in the text