• Advertisement

Archived

This topic is now archived and is closed to further replies.

Mouse woes

This topic is 5857 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Im trying to make an rts, which is using 3d terrain which can be viewed from any angle. My problem is figureing out what the mouse was clicked on. Right now Im casting a ray into the screen from the mouse, but the problem is that it is not straight back it is being affected by perspective. My question is how I can get it to be cast strait back. I can send the code to those who ask. Thanks allot

Share this post


Link to post
Share on other sites
Advertisement
the real question is... what do you want to do with your mouse ?
to you want to know on where it hits the terrain, or where it hits any object in the 3D view ?
to you want to retrieve coordinates of the terrain, assuming that the terrain is a DEM (eg, defined in x/z with altitudes in y) ?

You can use the selection buffer, but the way you have to use it depends on what you look for.

Share this post


Link to post
Share on other sites
I was trying to avoid the selection buffer, its slow (on my system anyway). I want to be able find where on the terrain it is clicked. The way it works now, is I convert the x&y coords from get cursor pos and the from that point i cast the ray back.

I want to know what is under that point

Thnx

Share this post


Link to post
Share on other sites
I have one solution, but may not be the best :
under the mouse position, get the depth value (glReadPixels on the Depth Buffer) to get the 3rd coordinate.

You have X and Y coordinates (thanks to mouse coordinates) and now Z.
You *just* have to use some mathematical formula to use the projection and modelview matrix to retrive your coordinate in the 3D world from the "screen 3D coordinate".

Hope this helps.

Share this post


Link to post
Share on other sites
actually reading from the depth buffer to get the z coordinate will not work to well. you have to realize that the x,y you get from the GetCursorPos() function should be considered trasnformed points. this means that the x/y will not match the z in the depth buffer due to perspetive rendering. (the z buffer method may work in certain cases, but i have a strong feeling that it dont work in all cases like the ray cast method you are using).

condsider the problem like this: you have an x/y pair of transformed vertices. you require a vector going from the transformed pair along the viewing vector. this should give you the desired result. since the camera position is always at the center (0,0,0) after all the transformations are done, you can use that fact to help get the pre transformed line (since it will be a line along the z plane. i will warn you right now that i have not tested this theory, but it is pretty sound. here is the algo, if you care to try it:
1. get the transformed x/y
2. back transform your x/y mouse coordinates (there should be some UnProject function in gl since there is one in d3d, if not just apply you transformations in reverse order AND inversed).
3. now you have an x/y pair (and a psudeo z). now cast yoru ray along that line (ie the z value should only be changing). unfortunatly i am not sure exactly how similar that is to your system (i used this technique for draw particles where the mouse was located). you may need to cast the ray from the back transformed x,y,z to the x,y,z of the camera position in world space. (make sure on the back transformation you keep your back transformed mouse coordinates in world space). the vector that is created between those should be what you are looking for. then its just a matter of finding the first object it collides with closest to the camera and in front of it.


EDIT: heh, now that i think about it, the zbuffer are transformed points as well. the zbuffer method should work just as well if not better.

Edited by - a person on February 4, 2002 5:27:04 PM

Share this post


Link to post
Share on other sites
Ok... well this way seems to be too complicated... so idecided id just f--- it and go with selection. I just cant seem to get it to work, because I draw a number of triangle strips.
the code is as follows:
  
int count=0;
for(int y = 2;y<YW-3;y++) //Smoothing wont fix the edges... so chop em off!

{ //This gets me an amazing 150 fps with 128*128 terrain!


glBegin(GL_TRIANGLE_STRIP);
for(int x = 2;x<XW-2;x++)
{
if(!selection && PointInFrustum(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z,11))
{
glTexCoord2d(1,1);glNormal3f(normals[x][y+1].x,normals[x][y+1].y,normals[x][y+1].z); glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glTexCoord2d(1,0);glNormal3f(normals[x][y].x,normals[x][y].y,normals[x][y].z);glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);
glTexCoord2d(0,1);glNormal3f(normals[x][y+1].x,normals[x][y+1].y,normals[x][y+1].z);glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glTexCoord2d(0,0);glNormal3f(normals[x][y].x,normals[x][y].y,normals[x][y].z);glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);
polies++;
}
if(selection)
{
glPushName(count);
glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);
}
count++;
}
glEnd();
}

I cant seem to get it to work, the only hit i can ever get is the one I push onto the stack before i begin the selection.
Any suggestions?

Share this post


Link to post
Share on other sites
Ok... well this way seems to be too complicated... so idecided id just f--- it and go with selection. I just cant seem to get it to work, because I draw a number of triangle strips.
the code is as follows:
  
int count=0;
for(int y = 2;y<YW-3;y++) //Smoothing wont fix the edges... so chop em off!

{ //This gets me an amazing 150 fps with 128*128 terrain!


glBegin(GL_TRIANGLE_STRIP);
for(int x = 2;x<XW-2;x++)
{
if(!selection && PointInFrustum(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z,11))
{
glTexCoord2d(1,1);glNormal3f(normals[x][y+1].x,normals[x][y+1].y,normals[x][y+1].z); glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glTexCoord2d(1,0);glNormal3f(normals[x][y].x,normals[x][y].y,normals[x][y].z);glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);
glTexCoord2d(0,1);glNormal3f(normals[x][y+1].x,normals[x][y+1].y,normals[x][y+1].z);glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glTexCoord2d(0,0);glNormal3f(normals[x][y].x,normals[x][y].y,normals[x][y].z);glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);
polies++;
}
if(selection)
{
glPushName(count);
glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);
}
count++;
}
glEnd();
}

I cant seem to get it to work, the only hit i can ever get is the one I push onto the stack before i begin the selection.
Any suggestions?

Share this post


Link to post
Share on other sites
I don't know if you already knew, but you gotta use "picking".

#define BUFSIZE 512
void mouse_click(int x, int y)
{
GLuint selectBuf[BUFSIZE];
GLint hits;
GLint viewport[4];

// Get window size.
glGetIntegerv (GL_VIEWPORT, viewport);

// Set select buffer
glSelectBuffer (BUFSIZE, selectBuf);

// Switch to rendering mode to selection. Nothing will be drawn on screen
(void)glRenderMode(GL_SELECT);

// Initialize name stack
glInitNames();
glPushName(0);

// Set the projection matrix to focus around mouse cooridnates
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
/* create 5x5 pixel picking region near cursor location */
gluPickMatrix((GLdouble)x, (GLdouble)(viewport[3] - y), 5.0, 5.0, viewport);
gluPrespective(...); // Copy the command that you use for your perspective or ortho.

// Render the scene
glMatrixMode(GL_MODELVIEW);
draw_my_scene(GL_SELECT);

// Restore projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();

// Switch to normal rendering mode, and get the number of hits in the selection buffer
glFlush();
hits = glRenderMode(GL_RENDER);

// Do what you want with the selection buffer
process_hits(hits, selectBuf);
}



and you scene may be draw with something like that :

void draw_my_scene(GLint rendering_mode = GL_RENDER)
{
bool selection = (rendering_mode==GL_SELECT);

if(selection)
for(int y = 2;y < YW-3;y++) //Smoothing wont fix the edges... so chop em off!
{ //This gets me an amazing 150 fps with 128*128 terrain!
glLoadName(y);
for(int x = 3;x < XW-2;x++)
{
glPushName(x);
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(verts[x-1][y+1].x,verts[x-1][y+1].y,verts[x-1][y+1].z);
glVertex3f(verts[x-1][y].x,verts[x-1][y].y,verts[x-1][y].z);

glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);
glEnd();
glPopName();
}
}
else // eg (!selection)
for(int y = 2;y < YW-3;y++) //Smoothing wont fix the edges... so chop em off!
{ //This gets me an amazing 150 fps with 128*128 terrain!
glBegin(GL_TRIANGLE_STRIP);
for(int x = 2;x < XW-2;x++)
{
if(PointInFrustum(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z,11))
{
glTexCoord2d(1,1);glNormal3f(normals[x][y+1].x,normals[x][y+1].y,normals[x][y+1].z);glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glTexCoord2d(1,0);glNormal3f(normals[x][y].x,normals[x][y].y,normals[x][y].z);glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);
glTexCoord2d(0,1);glNormal3f(normals[x][y+1].x,normals[x][y+1].y,normals[x][y+1].z);glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glTexCoord2d(0,0);glNormal3f(normals[x][y].x,normals[x][y].y,normals[x][y].z);glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);
polies++;
}
}
glEnd();
}
}


Your method had 2 problems :
- You always pushed names without popping, thus experiencing stack overflow,
- You called glPushName between glBegin and glEnd, which is forbidden.

Edited by - vincoof on February 5, 2002 5:07:44 AM

Share this post


Link to post
Share on other sites
ok... I see the problem, This puts me basically back to square one, because I cant draw my triangles like that, although I may be able to find another way. Thanks

Share this post


Link to post
Share on other sites
Another way ?
Well, just use the mathematical proposition I''ve done a few posts above

What''s wrong with this "math" method ?
It works very good, and it is not very complicated in fact.

Share this post


Link to post
Share on other sites
Well the problem with the math one is im in grade 11 and the math they teach in ontario is about 1+1*2

Anyway I might get it to draw triangles right... never know

Share this post


Link to post
Share on other sites
So, heh, what do you decide ? picking ? math ? raycasting ? something else ?

Share this post


Link to post
Share on other sites
//This gets me an amazing 150 fps with 128*128 terrain!
With what kind of hardware?


There are more worlds than the one that you hold in your hand...

Share this post


Link to post
Share on other sites
Vincoof --> At the moment to run around in circles and scream A person at starcraft3d.net (another project similar to what I would like to build) has another possible solution.

Dark somthing --> p4 1.7 ghz w/ asus v7700 delux geforce 2 gts 32mb ... 128 mb rdram @400 Mhz (I think cause this new mobo sux) & winxp pro


Share this post


Link to post
Share on other sites
Use color selection, just disable lighting, dither, texture, etx.. Draw a new frame that only contains the selectable objects each with a different color value, then use glGetPixels with the mouse coords to see what object the mouse cursor is over when the button is pushed.

To be reliable, you wont want to use a value with more than 1 decimal place, but since you have 3 colors, that gives you around 999 (10red*10blue*10green-1clearcolor) selectable objects at a time, plus you could allways render more frames.

Edited by - Eber Kain on February 6, 2002 2:50:31 AM

Share this post


Link to post
Share on other sites
That''s a very intersting solution, but I think it''s slower than picking, though. Have you benchmarked that, and if so, could you confirm which is faster ?

Share this post


Link to post
Share on other sites
I use it on all my stuff, you never notice that anything is going on.

Remember that once you click the mouse you only need to draw one frame, and you only need to make one call to glGetPixels. Then just compare the returned colors to your color values. I dont think it could be slower than useing picking.

You could also do progressive testing, like instead of testing every object aginst all three colors, you could group them in a way so that once the first match was made it narrowed the number to test, then it would narrow again when the second match was made.

I wouldnt worry about that unless you were dealing with a huge number of selectable objects at once.

And when you do this you want to read from the back buffer, and not swap buffers, that way the user never sees it, You could put the state changes in a display list to speed things up, you could even build a 3d array of indices to the selectable objects, and use that to see what was picked, that way there is not matching going on.

Edited by - Eber Kain on February 6, 2002 3:43:36 AM

Share this post


Link to post
Share on other sites
IMO, if it''s slower than picking, this is only because picking does not render to the framebuffer. It saves a bit of fillrate, but as you may render a "mini-frame" to speed up this invisble rendering, then the fillrate is not really a problem.

Your technique about grouping objects is exactly what the selection buffer mean to do with the name stack.
In fact, your technique emulates something that OpenGL already does

Anyway, I find the "color-picking" thingy very elegant since you can (kind of) debug your picking by rendering the flat-colored scene on screen. It is very natural for the human eye to detect objects by their colors. I bet that sometimes you swap this flat-colored buffer just to see how awesome it looks !

Share this post


Link to post
Share on other sites
Hey

Cant you use gluUnproject that turns mouse coords to world coords. Is that what u want?



"To err is human, to really mess up requires a computer"

"Only two things are infinite, the universe and human stupidity, and I''m not sure about the former. "
- Albert Einstein

Share this post


Link to post
Share on other sites
wow miss a day and see all the cool stuff people come up with!

My problem still though, is that I draw y # of triangle strips x long. And i cant seem to get the damn thing to render in the loop with glbegin and glend each iteration. ie

  
for(int y = 2;y<YW-3;y++) {
for(int x = 2;x<XW-2;x++)
{
glPushName(count);
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(verts[x-1][y+1].x,verts[x-1][y+1].y,verts[x-1][y+1].z);
glVertex3f(verts[x-1][y].x,verts[x-1][y].y,verts[x-1][y].z);
glVertex3f(verts[x][y+1].x,verts[x][y+1].y,verts[x][y+1].z);
glVertex3f(verts[x][y].x,verts[x][y].y,verts[x][y].z);

glEnd();
glPopName();

count++;
}
}


That wont work. If i can get the verts setup right to draw like that Selection, or Eber Kain''s method will work.

Share this post


Link to post
Share on other sites
What do you mean by "can''t get to render" ?

It doesn''t appear on screen ? or it doesn''t fill the selection buffer ?

do you use picking ?

Share this post


Link to post
Share on other sites
Right now I am trying to get it working with selection. But when I render it like that the terrain does not render properly (to selection or the visible one...)

I can post a screen shot if that would help?

Share this post


Link to post
Share on other sites
If "it does not render properly" means that one border is bad, that may be because you start x at 2 (I recommend starting x at 3 in the ''for'' instruction).

I hope you decode the selection buffer correctly. Or else you''ll think it gives weird results but in fact they''re correct.

Anyway, you can post a screenshot. I think that would help a bit.

Share this post


Link to post
Share on other sites

  • Advertisement