PDA

View Full Version : Screen to World Coordinates



atcBrambo
30-11-2010, 02:16 PM
Hi.
I have found code like this also under many C# C++ (and C-- ahh ahh)
---------------------------------------

function GetOpenGLPosFromMouse(X, Y: Integer): TGLVectord3;
var
viewport: TGLVectori4;
modelview: TGLMatrixd4;
projection: TGLMatrixd4;
winZ,winY: Single;
begin
glGetDoublev(GL_MODELVIEW_MATRIX, @modelview ); // Modelview
glGetDoublev(GL_PROJECTION_MATRIX, @projection ); // Projection
glGetIntegerv(GL_VIEWPORT, @viewport ); // Viewport

winY := viewport[3] - y; //Change from Win32 to OpenGL coordinate system

glReadPixels(X, Round(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, @winZ );
gluUnProject(X, winY, winZ,
modelview, projection, viewport,
@Result[0], @Result[1], @Result[2]);
end;---------------------------------------
But it doesn't work; in fact I tested with this
---------------------------------------------

OpenGLMousePosition:=GetOpenGLPosFromMouse(DelphiM ousePosition.X,DelphiMousePosition.Y);

glDisable(GL_DEPTH_TEST);

Main_Form.Caption:= 'Mouse Position is '+
'x..:'+IntToStr(DelphiMousePosition.X)+' '+
'y..:'+IntToStr(DelphiMousePosition.y)+' '+
' OpenGl Mouse '+
'x..:'+FormatFloat('#,##0.0;-#,##0.0;0.0',OpenGLMousePosition[0])+' '+
'y..:'+FormatFloat('#,##0.0;-#,##0.0;0.0',OpenGLMousePosition[1])+' '+
'z..:'+FormatFloat('#,##0.0;-#,##0.0;0.0',OpenGLMousePosition[2])+' ';

glColor3d(1.0,1.0,0.0);
glLineWidth(3);

glBegin(GL_LINES);
glVertex3d( 0, 0, 0);
glVertex3d( OpenGLMousePosition[0],OpenGLMousePosition[1],OpenGLMousePosition[2]);
glEnd();
------------------------------------------------------------------------------------
while moving the mouse the line goes somewhere else. Occasionally near (maybe about 40 pixels away) the mouse.

Did someone already faced the headache ?

JPG sample cannot be upload .... too large.
I will be happy to send it.


Giovanni.
Italy.

Ñuño Martínez
30-11-2010, 02:20 PM
IIRC, the Red Book (http://www.opengl.org/documentation/red_book/) has an example about this. I'm sure it will be more useful as it explains "why".

[edit] Didn't (re)read it but I think its in "Chapter 12 - Selection and Feedback" (http://fly.cc.fer.hr/~unreal/theredbook/chapter12.html)

atcBrambo
30-11-2010, 03:20 PM
But, as far as I have read, this doesn't cope with my request.
Else, please, be more precise.
Thanks
Giovanni

WILL
30-11-2010, 04:37 PM
What is it that you are trying to do in plain language?

For an interface, it's usually best to keep your mouse movement based on your screen's resolution while you World co-ordinates would be displayed using your own values multiplied by your 'zoom level' which would give you your world verses screen display calculation. You can then use this to calculate where your mouse is pointing in your world. This is of course easiest for a 2D world. If you want to know more about 3D worlds, then you'd have to consult someone else here who know more. :)

User137
30-11-2010, 06:14 PM
You didn't mention what you are trying to do, but when it comes to getting screen to world coordinates on various 3D objects or just 2D plane, this is what i do:
1) Calculate mouse-ray that goes from mouse towards infinity. (You only need to do this once per frame tick)
2) Calculate its collision point with a plane. Or calculate with each face of a 3D model.
Good thing is that in the process you get information such as face normal in collision point and which face it hits.

Then some code from Next3D:

procedure TNXGL.GetMouseRay(const mx, my: single; const p, normal: PVector;
rayMove: single);
var viewport: TGLVectori4;
modelM,projM: TGLMatrixd4;
x1,y1,z1,x2,y2,z2: double;
n: TVector;
begin
glGetIntegerv(GL_VIEWPORT,@viewPort);
glGetDoublev(GL_PROJECTION_MATRIX,@projM);
glGetDoublev(GL_MODELVIEW_MATRIX,@modelM);
{$HINTS OFF} // hints about x1..z2 not initialized
gluUnProject(mx,my,modelM[2,3],modelM,projM,viewport,x1,y1,z1);
gluUnProject(mx,my,modelM[2,3]-1,modelM,projM,viewport,x2,y2,z2);
{$HINTS ON}
n.x:=x1-x2; n.y:=y1-y2; n.z:=z1-z2; n:=Norm(n);
if p<>nil then begin
p^.x:=x1+n.x*rayMove;
p^.y:=y1+n.y*rayMove;
p^.z:=z1+n.z*rayMove;
end;
if normal<>nil then normal^:=n;
end;

// Result >= 0 if intersection happens
function RayPlaneIntersect(const rayOrigin, rayDirection,
planeOrigin, planeNormal: TVector; intersection: PVector): single;
var d,numer,denom: single;
begin
d:=planeNormal.x*planeOrigin.x+planeNormal.y*plane Origin.y+planeNormal.z*planeOrigin.z;
numer:=-planeNormal.x*rayOrigin.x-planeNormal.y*rayOrigin.y-planeNormal.z*rayOrigin.z+d;
denom:=planeNormal.x*rayDirection.x+planeNormal.y* rayDirection.y+planeNormal.z*rayDirection.z;
if denom=0 then begin
result:=-1; exit;
end;
result:=numer/denom;
if intersection<>nil then begin
intersection^.x:=rayOrigin.x+result*rayDirection.x ;
intersection^.y:=rayOrigin.y+result*rayDirection.y ;
intersection^.z:=rayOrigin.z+result*rayDirection.z ;
end;
end;

function TNXGL.MouseRayAtPlane(const mx,my: single; const planePos,planeNormal: TVector): TVector;
var rayOrigin, rayDirection: TVector;
begin
GetMouseRay(mx,my,@rayOrigin,@rayDirection);
rayPlaneIntersect(rayOrigin,rayDirection,planePos, planeNormal,@result);
end;

atcBrambo
02-12-2010, 08:32 AM
1st. Thank you very for sources. They are very hi tech. Your way to solve the problem is sure out of my (little) mind.

2nd. I have a 3ds model of an airport. I have to send coords to planes in order to move themselves using mouse.
Airport Y value is not request, because zero or very close.
So, is there no a easyer way to have x,z (NO Y need) coordinates ?

3rd. Please, if posibile, mail to info@brambo.it for a question.

Thanks however.

Giovanni

User137
02-12-2010, 03:05 PM
You don't need to go private messaging because of this, what comes told may be useful to someone else later. So i'll answer here too...

Firstly full source of my 3D engine and demos which include mouse-ray demo with 3D model are here: http://next3d.webs.com/

The code itself is really copy-paste stuff. You don't need to deeply understand how it works, just what parameter are needed and result they give. This might explain a little more:

type
TVector = record x,y,z: single; end;
PVector = ^TVector;

function vector(const x, y, z: single): TVector;
begin
result.x:=x; result.y:=y; result.z:=z;
end;

function TNXGL.MouseRayAtXZPlane(const mx, my: single): TVector;
begin
result:=MouseRayAtPlane(mx,my,Vector(0,0,0),Vector (0,1,0));
end;
So in order to get point (x,y,z) at airfield you need to call only
pointAtAirfield:=MouseRayAtXZPlane(mouseX,mouseY);
...where pointAtAirfield is TVector type.

Also notice that with OpenGL you may have to flip mouseY coordinate around because 0,0 is bottom left. DisplayHeight-mouseY.