So I have some openGL code (such code for example)
/* FUNCTION: YCamera :: CalculateWorldCoordinates
ARGUMENTS: x mouse x coordinate
y mouse y coordinate
vec where to store coordinates
RETURN: n/a
DESCRIPTION: Convert mouse coordinates into world coordinates
*/
void YCamera :: CalculateWorldCoordinates(float x, float y, YVector3 *vec) { // START GLint viewport[4]; GLdouble mvmatrix[16], projmatrix[16];
GLint real_y;
GLdouble mx, my, mz;
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
real_y = viewport[3] - (GLint) y - 1; // viewport[3] is height of window in pixels
gluUnProject((GLdouble) x, (GLdouble) real_y, 1.0, mvmatrix, projmatrix, viewport, &mx, &my, &mz);
/* 'mouse' is the point where mouse projection reaches FAR_PLANE.
World coordinates is intersection of line(camera->mouse) with plane(z=0) (see LaMothe 306)
Equation of line in 3D:
(x-x0)/a = (y-y0)/b = (z-z0)/c
Intersection of line with plane:
z = 0
x-x0 = a(z-z0)/c <=> x = x0+a(0-z0)/c <=> x = x0 -a*z0/c
y = y0 - b*z0/c
*/
double lx = fPosition.x - mx;
double ly = fPosition.y - my;
double lz = fPosition.z - mz;
double sum = lx*lx + ly*ly + lz*lz;
double normal = sqrt(sum);
double z0_c = fPosition.z / (lz/normal);
vec->x = (float) (fPosition.x - (lx/normal)*z0_c);
vec->y = (float) (fPosition.y - (ly/normal)*z0_c);
vec->z = 0.0f;
}
I want to run It but with out precompiling. Is there any way to do such thing
This is possible with LWJGL (OpenGL binding) and the REPL in Scala (runs on the JVM). I imagine that other languages like Clojure/Jython could also handle this request -- either through LWJGL or Jogl. There are also OpenGL bindings for a whole host of languages that don't require (explicit) compiling or come with their own REPL and/or 'integrated IDE'.
C normally always requires a compilation, but I did find this:
http://neugierig.org/software/c-repl/ and I'm sure there are other projects similar in nature.
Happy coding.
Related
In my SharpGL project (C#) I have used the Unproject function in order to get the world coordinates from mouse coordinates.
This procedure, quite trivial, fails when the drawing is scaled. I found many articles about this issue, but no one suited me.
When I say scaled means that in draw main proc i apply this code:
_gl.Scale(_params.ScaleFactor, _params.ScaleFactor, _params.ScaleFactor);
Then, when I intercept the mouse move I want to visualize the world coords. These coordinates are precise when the scale factor is 1, but when I change it these are wrong.
for example:
a world point (10, 10)
scaled 1 is detected (10, 10)
scaled 1,25 is detected (8, 8)
scaled 1,25 is detected (6.65, 6.65)
This is my simple code, consider that scale_factor is just passed for debugging.
public static XglVertex GetWorldCoords(this OpenGL gl, int x, int y, float scale_factor)
{
double worldX = 0;
double worldY = 0;
double worldZ = 0;
int[] viewport = new int[4];
double[] modelview = new double[16];
double[] projection = new double[16];
gl.GetDouble(OpenGL.GL_MODELVIEW_MATRIX, modelview); //get the modelview info
gl.GetDouble(OpenGL.GL_PROJECTION_MATRIX, projection); //get the projection matrix info
gl.GetInteger(OpenGL.GL_VIEWPORT, viewport); //get the viewport info
float winX = (float)x;
float winY = (float)viewport[3] - (float)y;
float winZ = 0;
//get the world coordinates from the screen coordinates
gl.UnProject(winX, winY, winZ, modelview, projection, viewport, ref worldX, ref worldY, ref worldZ);
XglVertex vres = new XglVertex((float)worldX, (float)worldY, (float)worldZ);
Debug.Print(string.Format("World Coordinate: x = {0}, y = {1}, z = {2}, sf = {3}", vres.X, vres.Y, vres.Z, scale_factor));
return vres;
}
I found a solution!
there were in the main draw procedure a portion of code that alterate results of UnProject function.
gl.PushMatrix();
.Translate(_mouse.CursorPosition.X * _params.ScaleFactor, _mouse.CursorPosition.Y * _params.ScaleFactor, _mouse.CursorPosition.Z * _params.ScaleFactor);
foreach (var el in _cursor.Elements) //objects to be drawn
{
gl.LineWidth(1f);
gl.Begin(el.Mode);
foreach (var v in el.Vertex)
{
gl.Color(v.Color.R, v.Color.G, v.Color.B, v.Color.A);
gl.Vertex(v.X, v.Y, v.Z);
}
gl.End();
}
gl.PopMatrix();
I'm trying to implement a picking ray via instructions from this website.
Right now I basically only want to be able to click on the ground to order my little figure to walk towards this point.
Since my ground plane is flat , non-rotated and non-translated I'd have to find the x and z coordinate of my picking ray when y hits 0.
So far so good, this is what I've come up with:
//some constants
float HEIGHT = 768.f;
float LENGTH = 1024.f;
float fovy = 45.f;
float nearClip = 0.1f;
//mouse position on screen
float x = MouseX;
float y = HEIGHT - MouseY;
//GetView() returns the viewing direction, not the lookAt point.
glm::vec3 view = cam->GetView();
glm::normalize(view);
glm::vec3 h = glm::cross(view, glm::vec3(0,1,0) ); //cameraUp
glm::normalize(h);
glm::vec3 v = glm::cross(h, view);
glm::normalize(v);
// convert fovy to radians
float rad = fovy * 3.14 / 180.f;
float vLength = tan(rad/2) * nearClip; //nearClippingPlaneDistance
float hLength = vLength * (LENGTH/HEIGHT);
v *= vLength;
h *= hLength;
// translate mouse coordinates so that the origin lies in the center
// of the view port
x -= LENGTH / 2.f;
y -= HEIGHT / 2.f;
// scale mouse coordinates so that half the view port width and height
// becomes 1
x /= (LENGTH/2.f);
y /= (HEIGHT/2.f);
glm::vec3 cameraPos = cam->GetPosition();
// linear combination to compute intersection of picking ray with
// view port plane
glm::vec3 pos = cameraPos + (view*nearClip) + (h*x) + (v*y);
// compute direction of picking ray by subtracting intersection point
// with camera position
glm::vec3 dir = pos - cameraPos;
//Get intersection between ray and the ground plane
pos -= (dir * (pos.y/dir.y));
At this point I'd expect pos to be the point where my picking ray hits my ground plane.
When I try it, however, I get something like this:
(The mouse cursor wasn't recorded)
It's hard to see since the ground has no texture, but the camera is tilted, like in most RTS games.
My pitiful attempt to model a remotely human looking being in Blender marks the point where the intersection happened according to my calculation.
So it seems that the transformation between view and dir somewhere messed up and my ray ended up pointing in the wrong direction.
The gap between the calculated position and the actual position increases the farther I mouse my move away from the center of the screen.
I've found out that:
HEIGHT and LENGTH aren't acurate. Since Windows cuts away a few pixels for borders it'd be more accurate to use 1006,728 as window resolution. I guess that could make for small discrepancies.
If I increase fovy from 45 to about 78 I get a fairly accurate ray. So maybe there's something wrong with what I use as fovy. I'm explicitely calling glm::perspective(45.f, 1.38f, 0.1f, 500.f) (fovy, aspect ratio, fNear, fFar respectively).
So here's where I am lost. What do I have to do in order to get an accurate ray?
PS: I know that there are functions and libraries that have this implemented, but I try to stay away from these things for learning purposes.
Here's working code that does cursor to 3D conversion using depth buffer info:
glGetIntegerv(GL_VIEWPORT, #fViewport);
glGetDoublev(GL_PROJECTION_MATRIX, #fProjection);
glGetDoublev(GL_MODELVIEW_MATRIX, #fModelview);
//fViewport already contains viewport offsets
PosX := X;
PosY := ScreenY - Y; //In OpenGL Y axis is inverted and starts from bottom
glReadPixels(PosX, PosY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, #vz);
gluUnProject(PosX, PosY, vz, fModelview, fProjection, fViewport, #wx, #wy, #wz);
XYZ.X := wx;
XYZ.Y := wy;
XYZ.Z := wz;
If you do test only ray/plane intersection this is the second part without DepthBuffer:
gluUnProject(PosX, PosY, 0, fModelview, fProjection, fViewport, #x1, #y1, #z1); //Near
gluUnProject(PosX, PosY, 1, fModelview, fProjection, fViewport, #x2, #y2, #z2); //Far
//No intersection
Result := False;
XYZ.X := 0;
XYZ.Y := 0;
XYZ.Z := aZ;
if z2 < z1 then
SwapFloat(z1, z2);
if (z1 <> z2) and InRange(aZ, z1, z2) then
begin
D := 1 - (aZ - z1) / (z2 - z1);
XYZ.X := Lerp(x1, x2, D);
XYZ.Y := Lerp(y1, y2, D);
Result := True;
end;
I find it rather different from what you are doing, but maybe that will make more sense.
I am trying to get the coordinates that the user clicks on on the plane y=0
I'm doing this by unprojecting the mouse coordinates to get the world coordinates on the near and far planes then using linear interpolation to find the coordinates on the plane but it's not giving me the correct coordinates.
My unprojection code:
int viewport[4];
double modelview[16];
double projection[16];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
double x, y, z;
//x_ and y_ are the x and y coordinates of the mouse
gluUnProject(x_, viewport[3] - y_, 0.0, modelview, projection, viewport, &x, &y, &z);
near.x = x;
near.y = y;
near.z = z;
gluUnProject(x_, viewport[3] - y_, 100.0, modelview, projection, viewport, &x, &y, &z);
far.x = x;
far.y = y;
far.z = z;
float t = -near.y / (far.y - near.y);
target_.y = 0.0f;
target_.x = near.x - t * (far.x - near.x);
target_.z = near.z - t * (far.z - near.z);
std::cout << target_ << std::endl;
but this always outputs:
x: a value between +-1 which seems to have a correlation to the click position just normalized even though I'm not normalizing anywhere
y: 0
z: -2
which I can't make sense of
Edit
Sorry, the error was me doing the unprojections before my transformations which you can't tell from the above code. I have solved it now.
Sorry, the error was me doing the unprojections before my transformations which you can't tell from the above code.
I have a quad on the y = -50 plane. At the moment, all I want to do is obtain the coordinates of a mouse click on the quad. I've managed to do this to a limited extent. The problem is that the transformations I applied when drawing the quad aren't accounted for. I can add in some constants and make it work, but I let the user rotate the scene about the x and y axes with glRotatef(), so the coordinates get messed up as soon as a rotation happens.
Here's what I'm doing now:
I call gluUnProject() twice, once with z = 0, and once with z = 1.
gluUnProject( mouseX, WINDOW_HEIGHT - mouseY, 0, modelView, projection, viewport, &x1, &y1, &z1);
gluUnProject( mouseX, WINDOW_HEIGHT - mouseY, 1, modelView, projection, viewport, &x2, &y2, &z2);
Normalized ray vector:
x = x2 - x1;
y = y2 - y1;
z = z2 - z1;
mag = sqrt(x*x + y*y + z*z);
x /= mag;
y /= mag;
z /= mag;
Parametric equation:
float t = -(camY) / y;
planeX = camX + t*x;
planeY = camY + t*y;
planeZ = camZ + t*z;
where (camX, camY, camZ) is the camera position passed to gluLookAt().
I want planeX, planeY, and planeZ to be the coordinates of the click on the quad, in the same coordinate system I used to draw the quad. How can I achieve this?
You are not supposed to pass in an explicit z-depth of your choosing. In order to find the world coordinate, you need to pass in the depth buffer value at that particular mouse coordinate.
GLfloat depth;
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
Passing that into your gluUnProject should yield the values you are looking for. Plus, as genpfault said in his comment, make sure you are grabbing the model view matrix data at the right moment. Otherwise, you have the wrong matrix.
I have this view set:
glMatrixMode(GL_MODELVIEW); //Switch to the drawing perspective
glLoadIdentity(); //Reset the drawing perspective
and I get a screen position (sx, sy) from a mouse click.
Given a value of z, how can I calculate x and y in 3d-space from sx and sy?
You should use gluUnProject:
First, compute the "unprojection" to the near plane:
GLdouble modelMatrix[16];
GLdouble projMatrix[16];
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
GLdouble x, y, z;
gluUnProject(sx, viewport[1] + viewport[3] - sy, 0, modelMatrix, projMatrix, viewport, &x, &y, &z);
and then to the far plane:
// replace the above gluUnProject call with
gluUnProject(sx, viewport[1] + viewport[3] - sy, 1, modelMatrix, projMatrix, viewport, &x, &y, &z);
Now you've got a line in world coordinates that traces out all possible points you could have been clicking on. So now you just need to interpolate: suppose you're given the z-coordinate:
GLfloat nearv[3], farv[3]; // already computed as above
if(nearv[2] == farv[2]) // this means we have no solutions
return;
GLfloat t = (nearv[2] - z) / (nearv[2] - farv[2]);
// so here are the desired (x, y) coordinates
GLfloat x = nearv[0] + (farv[0] - nearv[0]) * t,
y = nearv[1] + (farv[1] - nearv[1]) * t;