Terrain editing question (with screenshots)

Started by
7 comments, last by zedz 16 years, 11 months ago
Using the opengl selection buffer, i'm currently able to determine which terrain vertex has been clicked, and paint some trees in its contiguous right-top terrain patch (see the screenshot). I'd like to have the freedom to click on any part of the terrain and being able to put an object there (therefore retrieving its coordinates) without relying on opengl selection, that needs a vertex to work. Any idea ? Thanks
Advertisement
Sure. Cast a ray with the mouse pointer projected into the view frustum and do collision detection with your terrain mesh.

You can extract the frustum from the world matrices. The line cast by the mouse is just a ray starting on the near clip plane and ending on the far clip plane with the same relative coordinates on each as the relative coords of the mouse in screenspace.

-me
Thanks for the answer, Palidine.

Can you give me some more details please ?
First, the ray is starting from 0,0,0 ? The ray end-point coordinates can be retrieved using screen-to-world coordinates conversion (i suppose) ?

Once i have the start and end coordinates of the line, how do i test collision with the terrain ? There is a specific method for it ?
Your start and end points are given in screen space as s=(mouse.x, mouse.y, 0) and e=(mouse.x, mouse.y, 1). Then unproject both (screen-to-world) convert them to world coodinates.

Determining the intersection point is not that trivial. You could either check each triangle or build a quadtree of bounding boxes and traverse the hierarchy until you've found the triangle (or region where testing each triangle is feasible). Then it's a ray-triangle intersection.

Another approach just comes to my mind. If your terrain is a heightmap an always aligned to the x,z-plane you could traverse the ray and for each 'grid cell' you check the corresponding quad (imagine drawing your ray into your heightmap and checking each affected pixel).
If I was helpful, feel free to rate me up ;)If I wasn't and you feel to rate me down, please let me know why!
What I do is render the entire scene, then when I click in the viewport, call glReadPixels to get the current depth value at that point. I then call gluUnproject using the x, y window coordinates and this z value, to return actual world x y z coordinates. This (x,y,z) point is now the 3D position to place the object.


          Vector3 ConvertScreenToWorld(int x, int y) {				static GLint viewport[4];		static GLdouble modelview[16];		static GLdouble projection[16];		static GLfloat winX, winY, winZ;		glGetDoublev( GL_MODELVIEW_MATRIX, modelview );		glGetDoublev( GL_PROJECTION_MATRIX, projection );		glGetIntegerv( GL_VIEWPORT, viewport );		winX = (float)x;		winY = (float)viewport[3] - (float)y;		glReadPixels(int(winX), int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );		double worldX, worldY, worldZ;		gluUnProject( winX, winY, winZ, modelview, projection, viewport, &worldX, &worldY, &worldZ);		return Vector3((float)worldX, (float)worldY, (float)worldZ);	}


Now you have to render your scene in SOLID POLYGON mode for this to work, NOT wireframe, or else all the z values will not be correctly written to the screen. What you can do is when the user clicks, re-render the scene in solid mode but only allow writing to the depth buffer by calling glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE).
Author Freeworld3Dhttp://www.freeworld3d.org
Don't involve reading values from the depthbuffer. You may run into precisional problems, you'll force a transfer from video memory to system memory, and you'll be restricted to solid rendering.
Building a simple hierarchy and using a regular ray/triangle check is a much better option.


One tutorial on picking should be here.
Well guys, thanks for all these suggestions, i will try both methods.
About the ray-triangle intersection test, i found the following code built on Moller-Trumbore abstract, and, if anyone has used it, i have some questions:

#define EPSILON 0.000001#define CROSS(dest,v1,v2)           dest[0]=v1[1]*v2[2]-v1[2]*v2[1];           dest[1]=v1[2]*v2[0]-v1[0]*v2[2];           dest[2]=v1[0]*v2[1]-v1[1]*v2[0];#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])#define SUB(dest,v1,v2)           dest[0]=v1[0]-v2[0];           dest[1]=v1[1]-v2[1];           dest[2]=v1[2]-v2[2]; /* the original jgt code */int intersect_triangle(double orig[3], double dir[3],		       double vert0[3], double vert1[3], double vert2[3],		       double *t, double *u, double *v){   double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3];   double det,inv_det;   /* find vectors for two edges sharing vert0 */   SUB(edge1, vert1, vert0);   SUB(edge2, vert2, vert0);   /* begin calculating determinant - also used to calculate U parameter */   CROSS(pvec, dir, edge2);   /* if determinant is near zero, ray lies in plane of triangle */   det = DOT(edge1, pvec);   if (det > -EPSILON && det < EPSILON)     return 0;   inv_det = 1.0 / det;   /* calculate distance from vert0 to ray origin */   SUB(tvec, orig, vert0);   /* calculate U parameter and test bounds */   *u = DOT(tvec, pvec) * inv_det;   if (*u < 0.0 || *u > 1.0)     return 0;   /* prepare to test V parameter */   CROSS(qvec, tvec, edge1);   /* calculate V parameter and test bounds */   *v = DOT(dir, qvec) * inv_det;   if (*v < 0.0 || *u + *v > 1.0)     return 0;   /* calculate t, ray intersects triangle */   *t = DOT(edge2, qvec) * inv_det;		return(true);   return 1;}


1) orig[3] is the ray origin point: i guess i have to get it with glUnProject and (mouse.x, mouse.y, 0) ?
2) the direction vector dir[3] is formed using glUnproject and (mouse.x, mouse.y, 1) ?
3) double *t, double *u, double *v are passed empty (are used to store calculations inside the function)
4) *t is the point where the ray intersects the triangle: how do i calculate x,y,z of the intersection point ?


I haven't used it myself but let me try and answer your questions:

1.) Orig seems to be the start of your ray, you get it by unprojecting (mouse.x, mouse.y, 0).
2.) Dir is the direction vector. First you need the end point which you get by unprojecting (mouse.x, mouse.y, 1). Then dir = end - start. I don't know if you need to normalize it but I'd suggest doing it, so dir = (end - start) / length(end - start).
3. and 4.) t, u and v seem to be the barycentric coordinates of the intersection point, so if the function returns true (everything except 0) you have to convert those coordinates to a vector.

Basically a ray-triangle test can be done as follows:

1. get the triangle's plane
2. do a ray-plane intersection test and get the intersection point
3. check whether the interesction point lies within the triangle (or on its edges)
If I was helpful, feel free to rate me up ;)If I wasn't and you feel to rate me down, please let me know why!
*t is the point where the ray intersects the triangle: how do i calculate x,y,z of the intersection point ?

i assume its (origin + dir * t) // i assume dir is not normallized

This topic is closed to new replies.

Advertisement