Bad intersect result with smaller terrain scale

SongOfTheWeave

11-04-2008 11:52:58

Ever since I scaled down my world (I had been using 100 units between terrain verticies and I changed it to 1) my brush position has been behaving oddly. It has been jerking around rather than moving smoothly. I just did some experimentation and figured out exactly when it happens. I'll describe a couple of cases.


Case 1 (control): Camera is looking straight down at flat terrain.
Brush behaviour: Brush moves smoothly across the terrain in all directions.

Case 2 (buggy): Camera is low looking out over flat terrain at a shallow angle.
Brush behaviour: Brush moves smoothly perpendicular to the camera direction (mouse moves left to right) but jerks as it moves forward and back, parallel to the camera direction (mouse moves up and down.)


In the second case the brush appears to be jumping from one vertex position to the other, unable to pass smoothly between them.

I realize that a brush cannot be -applied- to the mesh at a finer resolution than terrain verticies, but before my cursor could move smoothly across the terrain regardless of camera position.

Is there an issue with TerrainInfo::rayIntersects that results in less accurate results with smaller terrain scales?

Ogre::Ray mouseRay = m_camMain->getCameraToViewportRay(evt.state.X.abs / (float) evt.state.width, evt.state.Y.abs / (float) evt.state.height);
std::pair<bool, Ogre::Vector3> intersectResult = info.rayIntersects(mouseRay);

if (intersectResult.first)
m_cursor->setPosition(intersectResult.second); // update pointer's position

SongOfTheWeave

11-04-2008 12:09:10

Okay, I found the problem

// have we arived at or under the terrain height?
// note that this approach means that ray queries from below won't work
// correctly, but then again, that shouldn't be a usual case...
float height = getHeightAt(point.x, point.z);
if (point.y <= height)
{
point.y = height;
return make_pair(true, point);
}

// move further...
point += dir;


It was accurate at a scale of 100 per vertex because, assuming Ray::getDirection returns a normalised vector, it could only ever return a value 1/100th of a vertex scale off, where as with a scale of 1, it can be a whole vertex scale beneath the terrain by the time it realizes it's intersected.

Could we make this method a binary search?

Find the start of the ray (or where it enters the AABB) and where it leaves the AABB and then check the midpoint of that segment to see if it's above or below the terrain, then halfway between the midpoint and either the beginning or the end, depending on whether the midpoint was above or below the terrain.

Considering that the terrain is most often near the middle of the AABB, this might be a good approach. Of course, it will be most expensive when the ray doesn't intersect the terrain at all and there would have to be some way to decide when to stop looking so it doesn't loop infinitely.

-----

Either that or add a precision parameter to TerrainInfo::rayIntersects and then have it travel along the ray in increments of dir * precision

That might be the better solution, by virtue of being simpler.

CABAListic

11-04-2008 12:42:04

No, this problem is not solvable by binary search (and neither by ternary search or any such). Consider a terrain with two sharp, high mountains and a ray which almost passes horizontally through the terrain. Binary search will fail miserably.

In any case, this issue is already corrected in ETL v3 where I'm using a different strategy for ray checks. In v3 the terrain is checked quad-wise for all quads over which the ray passes. This way it's independent of scaling and in most circumstances will also be faster than the old method due to less height checks being made. It's also accurate to the triangle level which the old method is not.
(The old method was taken over from Ogre's TSM, btw)

SongOfTheWeave

11-04-2008 13:08:38

In any case, this issue is already corrected in ETL v3 where I'm using a different strategy for ray checks. In v3 the terrain is checked quad-wise for all quads over which the ray passes. This way it's independent of scaling and in most circumstances will also be faster than the old method due to less height checks being made. It's also accurate to the triangle level which the old method is not.
(The old method was taken over from Ogre's TSM, btw)


Now that you mention it, I remember this discussion heh.

I'll just make it traverse the ray in smaller steps for now, until v3.

luis

12-05-2008 09:37:50

I'm having the same problem...


Either that or add a precision parameter to TerrainInfo::rayIntersects and then have it travel along the ray in increments of dir * precision
That might be the better solution, by virtue of being simpler.

please let me know if this work-around solves the problem ;)

SongOfTheWeave

13-05-2008 09:08:30

I'm having the same problem...


Either that or add a precision parameter to TerrainInfo::rayIntersects and then have it travel along the ray in increments of dir * precision
That might be the better solution, by virtue of being simpler.

please let me know if this work-around solves the problem ;)


Oh, yep, works fine.

Been using it since I first posted about it.

luis

13-05-2008 11:45:42

thanks.
I'm using a variant to avoid too many iterations in the loop:

...
float pres = 1.0f;
while (true)
{
float const height = getHeightAt(point.x, point.z);
float const diff = point.y - height;
if( diff <= 0.0f )
{
point.y = height;
return make_pair(true, point);
}
if( diff < 0.5f )
pres = 0.01f;
// move further...
point += dir * pres;
...


I need a lot of presition specially since my editor inserts small objects in very small terrains (140x140 units).