PDA

View Full Version : How to select vertex3f on mouse click



woolfik
09-03-2015, 05:58 AM
Hello there

I have array of points that I draw in OpenGL (code below):


procedure Draw();
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glClearColor(255,255,255,0);
glLoadIdentity; //biezaca macierz = I
gluLookAt(0,0,KameraZ, 0,0,0,0,1,0);
glRotatef(Theta, 1.0, 0.0, 0.0);
glRotatef(Phi, 0.0, 1.0, 0.0);
glTranslated(MoveX,MoveY,0);

glPointSize(2);
glBegin(GL_POINTS);
for I := 0 to High(Stars) - 1 do //stars is my array of array of points
for j := 0 to High(stars[i]) - 1 do
begin
glColor3ub(stars[i,j].R,stars[i,j].g, stars[i,j].b);
glVertex3i(stars[i,j].x,stars[i,j].y, stars[i,j].distance);
end;
glEnd;

SwapBuffers(wglGetCurrentDC);
end;

In my application onMouseMove event set KameraZ (zoom), Theta & Phi (rotate angle) and MoveX, MoveY (move left/right & up/down).

Now ... when user hold shift and click on scene I like to find point under clicked coords (mouse coords converted to opengl coords). After find first one (in this coords can be lot of points but I need only one neares camera look) i like to compare and find this point in my array by X, Y and Distans. I have to do that because point in array (stars) has more informations (OpenGL only show location of this point). I try to do that with such code:



procedure MouseClick(const X, Y: Integer);
type
T3D_Point = array [1..3] of Double;
var
i, j: Integer;
pt: T3D_Point;
function GetOGLPos(X, Y: Integer): T3D_Point;
var
viewport: array [1..4] of Integer;
modelview: array [1..16] of Double;
projection: array [1..16] of Double;
winZ: Single;
begin
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
if( Y = 0 )then Y := 1;
glReadPixels(X, -Y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, winZ );
gluUnProject(X, viewport[4]-Y, winZ,
modelview, projection, viewport,
Result[1], Result[2], Result[3]);
end;
begin
pt := GetOGLPos(X,Y);
for I := 0 to High(stars) do
for j := 0 to High(stars[i]) do
if (CompareValue(stars[i,j].x, pt[1], 2) = EqualsValue) and
(CompareValue(stars[i,j].y, pt[2], 2) = EqualsValue) then
begin
sbar.Panels[0].Text := 'Line:'+IntToStr(i)+',Point:'+IntToStr(j)+
',R:'+FloatToStr(Round(stars[i,j].R))+
',G:'+FloatToStr(Round(stars[i,j].G))+
',B:'+FloatToStr(Round(stars[i,j].B));
Break;
end;
end;

procedure pPaintMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if (ssShift in Shift) and (Button = mbLeft) then
MouseClick(x,y);
end;


but even if KameraZ = 100 and Theta, Phi, MoveX and MoveY is set to 0 sbar.Panels[0].Text is empty. Why?

GClements
09-03-2015, 02:22 PM
if( Y = 0 )then Y := 1;
glReadPixels(X, -Y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, winZ );


The above should probably be


glReadPixels(X, viewport[4]-1-Y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, winZ );

Except, viewport[4] is only correct if the viewport covers the entire window. Ideally you should use the actual height of the window.





gluUnProject(X, viewport[4]-Y, winZ,


Here, I'd suggest


gluUnProject(X+0.5, viewport[2]+viewport[4]-0.5-Y, winZ,

so that you're projecting the centre of the pixel rather than one corner.

Other than that, I suggest printing out the value of "pt" in case that provides any clues.

woolfik
09-04-2015, 12:36 AM
I have change my code as you show me but it still not work eg.

X = 503, Y = 339
After run GetOGLPos
pt = (-498,63926995, 29,131270601, -9906,5914513)

and points in stars array is between:
X: -262,26186891 and 289,87443628
Y: -0,90275827758 and 0,53627689465
Z: -15,801837661 and 25,229662989

GClements
09-04-2015, 01:56 AM
Well, the Z coordinate appears to be quite a way off, so I suggest checking the value of winZ. If you're reading the depth for a pixel which wasn't rendered, everything will be way off.

Another possibility: if you're using a perspective projection and the near distance is far too small, most of your scene will have very coarse depth resolution, which could be an issue.

woolfik
09-04-2015, 03:05 AM
I'm not sure if I understund You correctly so here is my all code for OpenGL:


function GL_UstalFormatPikseli(uchwytDC: HDC): Boolean;
var
opisFormatuPikseli :PIXELFORMATDESCRIPTOR;
formatPikseli :Integer;
begin
Result:=False;
with opisFormatuPikseli do
begin
dwFlags:=PFD_SUPPORT_OPENGL or PFD_DRAW_TO_WINDOW or PFD_DOUBLEBUFFER;
iPixelType:=PFD_TYPE_RGBA;
cColorBits:=32;
cDepthBits:=16;
iLayerType:=PFD_MAIN_PLANE;
end;
formatPikseli:=ChoosePixelFormat(uchwytDC, @opisFormatuPikseli);
if (formatPikseli=0) then
Exit;
if (SetPixelFormat(uchwytDC, formatPikseli, @opisFormatuPikseli) <> True) then
Exit;
Result:=True;
end;

procedure InitOpenGL;
begin
uchwytDC:=GetDC(pPaint.Handle);
GL_UstalFormatPikseli(uchwytDC);
uchwytRC:=wglCreateContext(uchwytDC);
wglMakeCurrent(uchwytDC,uchwytRC);
glMatrixMode(GL_PROJECTION);
glFrustum(-0.1, 0.1, -0.075, 0.075, 0.3, 10000.0);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
glClearColor(255,255,255,0);
KameraZ := 100;
end;

procedure Draw();
var
i,j: Integer;
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glClearColor(255,255,255,0);
glLoadIdentity;
glTranslated(MoveX,-MoveY,0);
gluLookAt(0,0,KameraZ, 0,0,0, 0,1,0);
glRotatef(Theta, 1.0, 0.0, 0.0);
glRotatef(Phi, 0.0, 1.0, 0.0);
glPointSize(2);
glBegin(GL_POINTS);
for I := 0 to High(Stars) do //stars is my array of array of points
for j := 0 to High(stars[i]) do
begin
glColor3ub(stars[i,j].R,stars[i,j].g, stars[i,j].b);
glVertex3i(stars[i,j].x,stars[i,j].y, stars[i,j].distance);
end;
glEnd;
SwapBuffers(wglGetCurrentDC);
end;


And on resize of TFrame is:

procedure FrameResize(Sender: TObject);
begin
glViewport(0,0,pPaint.Width,pPaint.Height);
end;

GClements
09-04-2015, 12:40 PM
cDepthBits:=16;



glMatrixMode(GL_PROJECTION);
glFrustum(-0.1, 0.1, -0.075, 0.075, 0.3, 10000.0);


Between these two, you're only using around 185 distinct depth values with the default viewpoint. The nearest star will be 75 units from the viewpoint, which is 250 times the near distance, meaning that the region between the nearest star and the far plane only uses around 1/250th of the depth range; with a 16-bit depth buffer, that's around 262 distinct values.

But that's not the issue here. The Z value of -9906.6 from your previous message corresponds to a point that's exactly on the far plane (to within the accuracy of single-precision floating point), i.e. you're reading the depth value for a pixel that wasn't rendered to.

Check that the mouse coordinates are what you expect, i.e. (0,0) is the top-left corner of the OpenGL viewport. If it's reporting screen coordinates or coordinates relative to the window frame, you'll be reading the wrong pixel.

Aleksandar
09-05-2015, 06:03 AM
Now ... when user hold shift and click on scene I like to find point under clicked coords (mouse coords converted to opengl coords). After find first one (in this coords can be lot of points but I need only one neares camera look) i like to compare and find this point in my array by X, Y and Distans. I have to do that because point in array (stars) has more informations (OpenGL only show location of this point).
I'm sorry for interfering, but why don't you use a selection mechanism available in the legacy OpenGL?
It would save you from all the trouble, like converting the screen coordinates to the normalized device coordinates (results are already in NDC), from searching other structures to find the star data (selection retrieves its ID or a pointer to the particular data), from picking the exact point (a buffer around the cursor can be defined), it retrieves all objects intersecting defined picking frustum, and does whatever you need about selection. So the question is really obvious: Why don't you use it?

woolfik
09-07-2015, 05:41 AM
Aleksandar because I don't know this selection mechanizm and I'm new in OpenGL. Can You show me some example based on my code? (it can be in C++ it does not mather wich language). I need to understund how this vertexf can be selection and how to operate on this selection in my OnMouse event.

GClements
09-07-2015, 06:46 AM
I believe that he's referring to glRenderMode(GL_SELECT) (https://www.opengl.org/sdk/docs/man2/xhtml/glRenderMode.xml).

This is covered in some detail in the red book (http://glprogramming.com/red/chapter13.html).