Placing trees on Terrain with mouse?

I have created a height field editor, I now wish to select a tree button in the app then when I click the mouse button on the terrain it places a tree exactly on the x,y,z of that piece of terrain.(the exact point might be between terrain vertexes)
anyone?

What I would do is once you click on the “Tree” button,you set a flag status to your mouse which is tree.Then when you click on the height field bitmap (I’m assuming you’ve got the grayscale image drawn on the window) you get the Z value of the terrain at the clicked co-ordinates and then draw your tree at the XYZ coordinates you clicked on.THat’s how I would do it anyway.

Point is,if you click on a top view of you terrain at location X and Y,you can easily get the Z value and then just position your tree at those XYZ values.Simple.

The height field is already drawn, I am currently view the terrain at a 30 degree angle.

I have set up the code but the gluunproject always returns false!

here is the code.

void CHeightFieldView::OnLButtonDown(UINT nFlags, CPoint point)
{
CHeightFieldDoc* pDoc = GetDocument();

if(pDoc->m_bTreeChecked)
{
	GLint viewport[4];
	GLdouble mvmatrix[16], projmatrix[16];
	GLint realy;
	

	if(nFlags == MK_LBUTTON)
	{
		glGetIntegerv(GL_VIEWPORT, viewport);
		glGetDoublev(GL_MODELVIEW, mvmatrix);
		glGetDoublev(GL_PROJECTION, projmatrix);

		//realy = viewport[3] - (GLint) point.y - 1;

		if( !gluUnProject( (GLdouble) point.x, (GLdouble) realy, 1.0, mvmatrix,
			projmatrix, viewport, &wx, &wy, &wz) )
		{
			AfxMessageBox("unprojectdidnot work");
		}
	}
	
}

CView::OnLButtonDown(nFlags, point);

}

First thing I see is, that realy is undefined (commented out). Next I would
check the viewport, mvmatrix and projmatrix
you get returned from OpenGL. May there is
a wrong matrix? At last you can check if the point.y and point.z values are correct.

I found in my codes, that I store the viewport, view and projectionmatrix in my draw-function for later use. Maybe they are invalid outside glbegin and glend (cant remember, too long ago…).

Hi Ironduke.

In your glGet, you should use GL_PROJECTION_MATRIX and GL_MODELVIEW_MATRIX instead of GL_PROJECTION and GL_MODELVIEW.

The values are not the same (look at gl.h) and so your glGet do not retrieve the proper values ! You should find an error if you do glGetError just after your fist glGet.
This is also probably why the gluUnproject does not work.

Last thing, you pass 1.0 for the winz value to gluUnproject. You should obtain the proper winz by using :

float winz;
glReadPixels(point.x,realy,1,1,GL_DEPTH,GL_FLOAT,&winz);

As I suppose you are using a double buffer, do not forget to do glReadBuffer(GL_BACK) before the glReadPixels !

With all this, your code should run properly !

Regards.

Eric

GluUnproject does not return false anymore, but the wx,wy, wz output is way off!

Let me explain what I do : when I draw my terrain, I translate to the center of the whole terrain, so if the terrain is 10 x 10
and each point is vertex is space 1.0 apart.

then the topleft corner will be at -5, -5 and the bottom right corner will be at 5,5

heights are from 0.0 to 15.0.

so how can I be getting output of

wx 108.0
wy 150.0
wz 306.0

my camera looks like this just to give you a picture in your mind

gluLookAt(0 ,150.0, 150.0, 0.0f,0.0f,0,0,1,0);

The code I am now using is this

void CHeightFieldView::OnLButtonDown(UINT nFlags, CPoint point)
{
CHeightFieldDoc* pDoc = GetDocument();

if(pDoc->m_bTreeChecked)
{
	GLint viewport[4];
	GLdouble mvmatrix[16], projmatrix[16];
	GLint realy;
	

	if(nFlags == MK_LBUTTON)
	{
		glGetIntegerv(GL_VIEWPORT, viewport);
		glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
		glGetDoublev(GL_PROJECTION_MATRIX , projmatrix);

		realy = viewport[3] - (GLint) point.y - 1;

		glReadBuffer(GL_BACK);
		glReadPixels(point.x,realy,1,1,GL_DEPTH,GL_FLOAT,&wz);
		
		if( !gluUnProject( (GLdouble) point.x, (GLdouble) realy, (GLdouble) wz, mvmatrix,
			projmatrix, viewport, &wx, &wy, &wz) )
		{
			AfxMessageBox("unprojectdidnot work");
		}
	}
	
}

CView::OnLButtonDown(nFlags, point);

}

glReadPixels(point.x,realy,1,1,GL_DEPTH_COMPONENT ,GL_FLOAT,&wz);
Must be GL_DEPTH_COMPONENT!!!

Have a look here for gluUnproject and windows z coordinates explanation here: http://www.opengl.org/About/FAQ/technical/glu.htm#0070

[This message has been edited by Relic (edited 07-19-2000).]

Hum yes, Relic is right…

Sorry IronDuke, I mislead you : change GL_DEPTH for GL_DEPTH_COMPONENT and it should be OK…

Apologies…

Eric

Well I changed the code to GL_DEPTH_COMPONENT, but no matter where I click the mouse I get the same numbers I stated above?

Not sure about this one, but have you tried a different destination variable for &wz?
gluUnProject( (GLdouble) point.x, (GLdouble) realy, b wz[/b], mvmatrix,
projmatrix, viewport, &wx, &wy, &wz) )

It’s actually defined as a float, isn’t it?

[This message has been edited by Relic (edited 07-19-2000).]

You might want to try something completely different. It’s primitive, but it’ll work:

You can just ‘fire’ the tree, in the direction of the place clicked on. Of course there’s no need to render the tree hurling towards the ground (although it can be quite cool , just simple loop that stops when the tree’s branch is lower then the elevation of the field at that location.

Calculating the direction-vector from the mouse click may involve a little trigonometry, but it’s nothing serious. You can also try line-polygon intersection for the whole field, but this is O(n) and may take some time on large datasets.

Just my two cents.

Ofer.

ofer_hu stop taking the piss!

Where’s Gork only he seems to be able to fix my Problems.

GORK!!!

This the code which works in my Source:

class pane3d
{
public:
bool pick()
void draw()
protected:
GLdouble modelMatrix[16];
GLdouble projMatrix[16];
GLint viewport[4];
}

void pane3d::draw()
{
.
.
.
glGetDoublev(GL_MODELVIEW_MATRIX,modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX,projMatrix);
glGetIntegerv(GL_VIEWPORT,viewport);

.
.
.
return;
}

bool pane3d: ick()
{
GLfloat pixels[3*3] = {0.0f};
GLdouble x,y;
GLdouble ox,oy,oz;

x=pIn.x();
y=drawSizeY-pIn.y();
glReadPixels(x-1,y-1,3,3,GL_DEPTH_COMPONENT,GL_FLOAT,pixels);

if (pixels[4]>0.001 && pixels[4]<0.999)
gluUnProject(x,y,pixels[4],modelMatrix,projMatrix,viewport,&ox,&oy,&oz);
else
return false;

return true;

}

It’s the same code than yours (The glReadPixels reads a 3x3 field to pick pixels near the mouse too), so I don’t think the problem lies here. I save my modelview and projectionmatrix directly after painting the object I want to pick. So if you did any translate, rotate or pushmatrix after drawing your landscape, the gluunproject will use this modelviewmatrix (as you read it out at the pick time).

Kilam.

[This message has been edited by Kilam Malik (edited 07-19-2000).]

[This message has been edited by Kilam Malik (edited 07-19-2000).]

I will give it a try but now I must sleep.

GORK!!!

Hey, relax.
Maybe I’ve not described clearly enough what I suppose to be weird:

From this line
glReadPixels(point.x,realy,1,1,GL_DEPTH,GL_FLOAT,&wz);
I assmume your variable wz is a GLfloat.

and is this line
if( !gluUnProject( (GLdouble) point.x, (GLdouble) realy, (GLdouble) wz, mvmatrix,
projmatrix, viewport, &wx, &wy, &wz) )
wz it is a GLfloat as input parameter, otherwise you wouldn’t have to cast it to GLdouble, but the output parameters have all to be a pointers to GLdouble, so this is clearly wrong.
Defining as GLdouble does not work for the glReadPixels though, so take two different variables.

I wonder why the compiler didn’t warn you about that. Try MSVC++'s warning level 4.

Interestingly your results are in a range of your gluLookAt distance. If the above is still not fixing the problem, I would like to see more code around the matrix initialization. Otherwise it’s difficult to get a grip to the un-projection.

[This message has been edited by Relic (edited 07-20-2000).]

Another thing I just realised… you are using wz two times. It’s your parameter for your input z AND it’s your parameter for your output z. As you give gluUnProject a pointer to wz, the function maybe changes the value of wz and later wants to read from “the other” wz:

if( !gluUnProject( (GLdouble) point.x, (GLdouble) realy, (GLdouble) wz, mvmatrix, projmatrix, viewport, &wx, &wy, &wz))

Just try:

if( !gluUnProject( (GLdouble) point.x, (GLdouble) realy, (GLdouble) wz, mvmatrix, projmatrix, viewport, &ox, &oy, &oz))

Kilam.

Right, see six posts above.
But I’m not sure if gluUnProject is that dumb.

Sorry, didn’t recognize…
I checked Mesa source now, it doesn’t bother there, if wz is used two times. The pointers are filled just before the function returns.
For windows I have to try it later when I can compile my source again :wink:

i changed the output of glreadpixels to GLDOUBLE, as far as gluunproject goes are you saying a pass the value of wz into the function, then read it out, that opengl my rewire the original value of wz again after it has changed it for output?

I dont think so. That would be silly? wouldnt it!

GORK!!!

Try this excerpt from a windows’ message event handler. It does all you wanted and you’ll see it works.

Watch out for the values you use in gluLookAt and zNear and zFar!!
The glReadPixels value reaches numbers very near to 1.0 if the zNear plane is too far away from the front of your drawing and might impact precision.

If this still doesn’t help, put a watch on all variables and analyze the results by hand, man!

GLdouble dmatModelview[16];
GLdouble dmatProjection[16];
GLint viewport[4];
static POINT point;
GLint y;
GLfloat fWindowZ;
GLdouble dWorldX;
GLdouble dWorldY;
GLdouble dWorldZ;

// …

case WM_SIZE:
  nWidth  = LOWORD(lParam); // width of client area 
  nHeight = HIWORD(lParam); // height of client area
  return 1;

case WM_LBUTTONUP:
  point.x = LOWORD(lParam);
  point.y = HIWORD(lParam);
  InvalidateRect(hwnd, NULL, FALSE);
  return 0;

case WM_PAINT:
  glViewport(0, 0, nWidth, nHeight);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glEnable(GL_DEPTH_TEST);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0, (double) nWidth / (double) nHeight, 10.0, 40.0);
  gluLookAt(0.0,   15.0, 15.0,
            0.0,   0.0,   0.0,
            0.0,   1.0,   0.0);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  glBegin(GL_POLYGON);
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3f(-5.0f, 0.0f, -5.0f);
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(-5.0f, 0.0f,  5.0f);
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f( 5.0f, 0.0f,  5.0f);
    glColor3f(1.0f, 1.0f, 0.0f);
    glVertex3f( 5.0f, 0.0f, -5.0f);
  glEnd();
  glFlush();

  glGetIntegerv(GL_VIEWPORT, viewport);
  glGetDoublev(GL_MODELVIEW_MATRIX, dmatModelview);
  glGetDoublev(GL_PROJECTION_MATRIX, dmatProjection);

  // There is only one Z buffer, no need to specify glReadBuffer
  y = viewport[3] - point.y;
  glReadPixels(point.x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &fWindowZ);
  gluUnProject((GLdouble) point.x, (GLdouble) y, (GLdouble) fWindowZ,
               dmatModelview, dmatProjection, viewport,
               &dWorldX, &dWorldY, &dWorldZ);
  
  glFinish(); // Single buffered.
  ValidateRect(hwnd, NULL);
  
  return 0;