I'm currently calling Trace (method below) from a game loop. Right now all I'm trying to do is get the world coordinates from the screen mouse so I can move objects around in the world space. The values I'm getting from gluUnProject are however; puzzling me.
I was using glReadPixel(...) to get the Z value but that produced little to no movement in the object I was drawing and the resulting vector ended up being the same as my cameras location (except for the tiny decimal changes due to mouse movement), so I decided to get rid of the call and replace the Z value with 1.
My question is: Does the following code look right to you? Every example I've seen thusfar is either identical or -very- similar but I can't seem to produce correct results, even if I lock down the Y axis. If the code is correct, then I'm guessing that I'm just not using the resulting vector properly. Should I not be able to draw an object or point directly with the resulting vector or do I have to do something else with it, like normalize?
The current render mode is GL_RENDER and I am using glFrustum with a NearZ value of 1 and FarZ value of 2048, to create a perspective. There is also a series of viewports created along with scissors, with a size and width of 512x768 and positioned in each corner of a 1024x768 window. Trace(...) is called in between rendering of the upper left viewport and is the only perspective projection, while the other viewports are orthographic. FOV is set to 45.
void VideoWindow::Trace(int cursorX, int cursorY)
{
double objX, objY, objZ;//holder for world coordinates
GLint view[4];//viewport dimensions+pos
GLdouble p[16];//projection matrix
GLdouble m[16];//modelview matrix
GLdouble z;//Z-Buffer Value?
glGetDoublev (GL_MODELVIEW_MATRIX, m);
glGetDoublev (GL_PROJECTION_MATRIX,p);
glGetIntegerv( GL_VIEWPORT, view );
//view[3]-cursorY = conversion from upper left (0,0) to lower left (0,0)
//Unproject 2D Screen coordinates into wonderful world coordinates
gluUnProject(cursorX, view[3]-cursorY, 1, m, p, view, &objX, &objY, &objZ);
//Do something useful here???
}
Any ideas?
Edit: I've changed the winZ value to 0.5 instead of 1 which gives a vector thats more reasonable but drawing a point still wasn't matching the mouse. I found out that the value of view[3] was 384 which is correct for the viewport I'm using but I replaced it with 768 (the actual window size) and the point followed the mouse 100%. Further experimentation reveals that I can't use the coordinates to move around a 3D object in the perspective world space using this these coordinates however moving around 3D object in Orthographic space works fine.
The winz argument to gluUnproject specifies the depth from the camera at which you're "picking" your points. As you've stated this coordinate should be in the [0, 1] range.
Some tutorials like NeHes read out the z coordinate from the depth buffer so that you "pick" at the right depth, of course for this to work you'll have to do the gluUnproject after you've rendered everything else.
Regardless, if you set winz to 0.5 or something (not 0 or 1 or the point will end up on the near or far clip plane, and maybe culled) and do the following:
gluUnProject(cursorX, view[3]-cursorY, 0.5, m, p, view, &objX, &objY, &objZ);
//Do something useful here???
glPointSize(10);
glBegin(GL_POINTS);
glColor3f(1, 0, 0);
glVertex3f(objX, objY, objZ);
glEnd();
You should end up with a red blob at the mouse pointer (provided nothing else overdraws it afterwards and you don't have any funny render states which renders the point invisible).
just a thought, but if the third argument to gluUnProject is the z distance to the camera, wouldn't any point you draw at that location be on the near clipping plane of your frustum?
Better make that z value a bit higher.
Related
I am trying to implement a logic where, on mouse click, a shot is fired at an object.To do so, I did the following,
I first considered the .obj file of my model and found the region (list of coordinates) that the shot works on (a particular weak point of the body).
I then considered the least and largest x,y and z values present in the file for that particular region (xmin,ymin,zmin and xmax,ymax,zmax).
To figure out whether the shot has landed on the weak point, I considered the assumption that a shot would land on the weak point, if the coordinates of the shot lie between (xmin,ymin,zmin) and (xmax,ymax,zmax).
I assumed the coordinates from the .obj file to be the actual coordinates of the model, since the assimp code I have directly loads in the coordinates of the model. Considering (xmin,ymin,zmin) and (xmax,ymax,zmax), I converted the coordinates to window coordinates via gluProject().
I then considered the current cursor position and checked if the cursor position lies between (xmin,ymin,zmin) and (xmax,ymax,zmax).
The problems I now face are:
The object coordinates provided in the .obj file range between -4 to 4, which then lie around 1.0 after gluProject(), whereas the cursor position lies between (0,0) and (1280,720).
After gluProject(), (xmin,ymin) and (xmax,ymax) are either (0,1) or (1,0) whereas the zmin and zmax values seem fine.
How can I get my logic working ?
Here is the code:
// Call shader to draw and acquire necessary information for gluProject()
modelShader.use();
modelShader.setMat4("projection", projection);
modelShader.setMat4("view", view);
glm::mat4 model_dragon;
double time=glfwGetTime();
model_dragon=glm::translate(model_dragon, glm::vec3(cos((360.0-time)/2.0)*60.0,cos(((360.0-time)/2.0))*(-2.5),sin((360-time)/1.0)*60.0));
model_dragon=glm::rotate(model_dragon,(float)(glm::radians(30.0)),glm::vec3(0.0,0.0,1.0));
model_dragon=glm::scale(model_dragon,glm::vec3(1.4,1.4,1.4));
modelShader.setMat4("model", model_dragon);
collision_model=model_dragon;collision_view=view;collision_proj=projection; //so that I can provide the view,model and projection required for gluProject()
ourModel.Draw(modelShader);
Mouse button callback
// Note: dragon_min and dragon_max variables hold the constant position of the min and max coordinates.
void mouse_button_callback(GLFWwindow* window,int button,int action,int mods){
if(button==GLFW_MOUSE_BUTTON_LEFT && action==GLFW_PRESS){
Mix_PlayChannel( -1, shot, 0 ); //Play sound
GLdouble x,y,xmin,ymin,zmin,xmax,ymax,zmax,dmodel[16],dproj[16];
GLint dview[16];
float *model = (float*)glm::value_ptr(collision_model);
float *proj = (float*)glm::value_ptr(collision_proj);
float *view = (float*)glm::value_ptr(collision_view);
for (int i = 0; i < 16; ++i){dmodel[i]=model[i];dproj[i]=proj[i];dview[i]=(int)view[i];} // Convert mat4 to double array
glfwGetCursorPos(window,&x,&y);
gluProject(dragon_min_x,dragon_min_y,dragon_min_z,dmodel,dproj,dview,&xmin,&ymin,&zmin);
gluProject(dragon_max_x,dragon_max_y,dragon_max_z,dmodel,dproj,dview,&xmax,&ymax,&zmax);
if((x>=xmin && x<=xmax) && (y>=ymin && y<=ymax)){printf("Hit\n");defense--;}
The .obj coordinates have eg. values as shown:
0.032046 1.533727 4.398055
You are confusing the parameters of gluProject, especially the view parameter. This parameter should contain 4 integers which describe the viewport (x,y,width,height) and not the view matrix.
gluProject (and a lot of other glu functions) are tailored towards the fixed function pipeline and their matrix stacks. Due to this, you have to pass the following information:
model: The modelview matrix, as returned by glGetDoublev( GL_MODELVIEW_MATRIX, ...)).
proj: The projection matrix, as returned by glGetDoublev( GL_PROJECTION_MATRIX, ...).
view: The current viewport, as returned by glGetIntegerv( GL_VIEWPORT, ...)
As you see, the view matrix is packed together with the model matrix and view contains the viewport.
I'd strongly advice not to use glu functions at all when working with modern OpenGL. Especially when the matrices are already stored in glm, it would be better to use glm::project.
Note1: Converting a floating point matrix to an integer matrix by casting each element almost never results in anything meaningful.
Note2: When projecting a bounding rectangle to screenspace, the result will in general not be a rectangle anymore. During projection, angles are not preserved, thus the result is a general four cornered polygon and not a rectangle anymore. Same goes for bounding boxes: You can't even guarantee that the projected box is contained in the screen-space rectangle defined by projecting [x_min, y_min, z_min] and [x_max, y_max, z_max].
I have an object that I want to to move around using the following mechanic: the left and right arrows change its rotation and the up arrow increments its position.
My problem is that I either can't rotate the object around itself, or I can't move it in the direction being looked at.
The draw function is as follows:
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glScalef(SCALE, SCALE, SCALE);
glTranslatef(x, 0, 0);
glRotatef(rotationZ, 0, 0, 1);
glTranslatef(-x, 0, 0);
// Draw the object...
glPopMatrix();
Key press detection code:
case GLUT_KEY_UP:
teclas.up = GL_TRUE;
glutPostRedisplay();
break;
case GLUT_KEY_LEFT:
teclas.left = GL_TRUE;
glutPostRedisplay();
break;
case GLUT_KEY_RIGHT:
teclas.right = GL_TRUE;
glutPostRedisplay();
break;
Timer function:
if (teclas.up) {
x++;
}
if (teclas.left) {
rotationZ++;
}
if (teclas.right) {
rotationZ--;
}
glutPostRedisplay();
I've seen multiple threads about this, and I've tried changing the signal of the x variable but nothing seems to work.
Edit(solved):
I just changed the part of the Timer function that is responsible for the forward movement to this:
if (estado.teclas.up) {
homer.x+= (float)cos(homer.rotationZ * M_PI / 180);
homer.y+= (float)sin(homer.rotationZ * M_PI / 180);
}
And also, my Draw function:
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glScalef(SCALE, SCALE, SCALE);
glTranslatef(x, 0, 0);
glRotatef(rotationZ, 0, 0, 1);
// Draw the object...
glPopMatrix();
This way, the object always moves towards what it's facing
This is a case of problem with Moving Reference Frame, those are the keywords. Unless you simulate physics of process as well, for OpenGL rendering all we have to worry about are the coordinates. Here we have the stationary reference frame, sometimes called a world frame (especially if observer is moving relative to it as well), and a moving reference frame (MRF )connected to object. MRF can have arbitrary rotation and translation relative to world frame, there are traditional ways how it is defined.
For example for Earth globe MRF defined as origin in center of Earth, positive X axis intersecting equator and 0 meridian, positive Z - north pole and Y is complementary to them. For static point on surface of earth (local geographic coordinates) it usually Y directed to zenith and positive Z - toward North in plane of horizon and positive X - toward east. In case of moving vehicle's the positive Y- or pitch axis always points to its left, and the positive Z- or yaw axis always points up, X - the roll axis is pointed straight forward. This one seem to match your case.
Regardless of axis specification, the rotation of vehicle is equivalent of changing matrix corresponding to it. Lets call it transformation matrix. In local coordinates vehicle speed v = {vx,0,0} is a vector collinear to positive X axis. But in world coordinates it is equal to
v' = M*v
where M is a transformation matrix of MRF. As v is change of coordinates per unit of time, then any translations should follow this formula too. There are two ways to solve this , if you're using legacy OpenGL, you have two options:
First: you would start with identity matrix and recreate all transforms in proper order.
Set identity matrix.
Translate by value required (in local cords)
Apply rotations of vehicle
Translate by values of last known position of vehicle.
Either calculate new position of vehicle, knowing transforms, or read that value , by getting matrix from OpenGL (by glGetFloatv(GL_MODELVIEW_MATRIX, ptr)) and extracting offset from it.
Downside of this method is that you have to use functions of OpenGL,where each call of glTranslate or glRotate is creating another matrix that is getting multiplied with other (in opposite order). That's excess math operations and precision of them isn't brilliant either. It can get quite interesting in Chinese manner if you have several frames of reference, especially nested.
Second method is to do all matrix math yourself, for example using some math library like GLM (glm.h) and store matrix for each frame of reference, modifying or regenerating them when needed. You can supply matrix directly to OpenGL even in legacy mode by glLoadMatrix. If you worry about performance, you should know that all modern implementations are done that math on CPU anyway, GPUs do not work with matrix stack anymore, for long time. It can be found quickly by inspecting open-source implementations.
In case of modern, flexible pipeline you don't have glScale, glTranslate, glRotate available at all. Entire matrix stack is deprecated in OpenGL 3. You can do it only in second way, but in this case you would supply matrices to shader program through uniforms.
I'm having an issue with drawing a model and rotating it using the mouse,
I'm pretty sure there's a problem with the mathematics but not sure .
The object just rotates in a weird way.
I want the object to start rotating each click from its current spot and not reset because of the
vectors are now changed and the calculation starts all over again.
void DrawHandler::drawModel(Model * model){
unsigned int l_index;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW); // Modeling transformation
glLoadIdentity();
Point tempCross;
crossProduct(tempCross,model->getBeginRotate(),model->getCurrRotate());
float tempInner= innerProduct(model->getBeginRotate(),model->getCurrRotate());
float tempNormA =normProduct(model->getBeginRotate());
float tempNormB=normProduct(model->getCurrRotate());
glTranslatef(0.0,0.0,-250.0);
glRotatef(acos (tempInner/(tempNormA*tempNormB)) * 180.0 / M_PI,tempCross.getX(),tempCross.getY(),tempCross.getZ());
glColor3d(1,1,1);
glBegin(GL_TRIANGLES);
for (l_index=0;l_index < model->getTrianglesDequeSize() ;l_index++)
{
Triangle t = model->getTriangleByPosition(l_index);
Vertex a1 = model->getVertexByPosition(t.getA());
Vertex a2 = model->getVertexByPosition(t.getB());
Vertex a3 = model->getVertexByPosition(t.getC());
glVertex3f( a1.getX(),a1.getY(),a1.getZ());
glVertex3f( a2.getX(),a2.getY(),a2.getZ());
glVertex3f( a3.getX(),a3.getY(),a3.getZ());
}
glEnd();
}
This is the mouse function which saves the beginning vector of the rotating formula
void Controller::mouse(int btn, int state, int x, int y)
{
x=x-WINSIZEX/2;
y=y-WINSIZEY/2;
if (btn==GLUT_LEFT_BUTTON){
switch(state){
case(GLUT_DOWN):
if(!_rotating){
_model->setBeginRotate(Point(float(x),float(y),
(-float(x)*x - y*y + SPHERERADIUS*SPHERERADIUS < 0)? 0:float(sqrt(-float(x)*x - y*y + SPHERERADIUS*SPHERERADIUS))));
_rotating=true;
}
break;
case(GLUT_UP):
_rotating=false;
break;
}
}
}
and finally the following function which holds the current vector.
(the beginning vector is where the mouse was clicked at
and the curr vector is where the mouse position at the moment )
void Controller::getMousePosition(int x,int y){
x=x-WINSIZEX/2;
y=y-WINSIZEY/2;
if(_rotating){
_model->setCurrRotate(Point(float(x),float(y),
(-float(x)*x - y*y + SPHERERADIUS*SPHERERADIUS < 0)? 0:float(sqrt(-float(x)*x - y*y + SPHERERADIUS*SPHERERADIUS))));
}
}
where sphereradius is the sphere radius O_O of 70 degress
is any calculation wrong ? cant seem to find the problem...
thanks
Why so complicated? Either you change the view matrix or you change the model matrix of your focused object. If you choose to change the model matrix and your object is centered in (0,0,0) of the world coordinate system, computing the rotation around a sphere illusion is trivial - you just rotate into the opposite direction. If you want to change the view matrix (which is actually done when you change the position of the camera) you have to approximate the surface points on the chosen sphere. Therefore, you could introduce two parameters specifying two angles. Everytime you click move your mouse, you update the params and compute the new locations on the sphere. There are some useful equations in [http://en.wikipedia.org/wiki/Sphere].
Without knowing what library (or libraries) you're using your code is rather difficult to read. It seems you're setting up your camera at (0, 0, -250), looking towards the origin, then rotating around the origin by the angle between two vectors, model->getCurrRotate() and model->getBeginRotate().
The problem seems to be that in "mouse down" events you explicitly set BeginRotate to the point on the sphere under the mouse, then in "mouse move" events you set CurrRotate to the point under the mouse, so every time you click somewhere else, you lose the previous state of rotation because BeginRotate and CurrRotate are simply overwritten.
Combining multiple rotations around arbitrary different axes is not a trivially simple task. The proper way to do it is to use quaternions. You may find this primer on quaternions and other 3D math concepts useful.
You might also want a more robust algorithm for converting screen coordinates to model coordinates on the sphere. The one you are using is assuming the sphere appears 70 pixels in radius on the screen and that the projection matrix is orthographic.
I have managed to rotate a rectangle in OpenGL (C++) just fine. I am making a program that tests two rectangles for collision using the "separated axis theorem". To use the theorem, I need the x and y coordinates of each corner of the rectangle, but my problem is that, although I call glRotatef(...), the coordinates of the corners of the rectangle are not changed to the values that they are rotated too, but the rectangle rotates as it should. How can I update the coordinates of my rectangle after glRotatef is called?
Code:
// float r1.x[4] and r1.y[4] hold the x and y position of each of the 4 corners, starting with the upper left (r1.x[0], r1.y[0])
glLoadIdentity();
glTranslatef((r1.x[0] + r1.x[2]) / 2, (r1.y[1] + r1.y[3]) / 2, 0); // Translates matrix to center of rectangle
glRotatef(r1.angle, 0, 0, 1);
glTranslatef(-((r1.x[0] + r1.x[2]) / 2), -((r1.y[1] + r1.y[3]) / 2), 0); // Translates back
r1.angle++;
glBegin(GL_QUADS);
glVertex2f(r1.x[0], r1.y[0]);
glVertex2f(r1.x[1], r1.y[1]);
glVertex2f(r1.x[2], r1.y[2]);
glVertex2f(r1.x[3], r1.y[3]);
glEnd();
Transformation calls in OpenGL (like glTranslatef and glRotatef) update an internal transformation matrix that get multiplied by the points you provide before getting drawn on the screen. OpenGL does not at all touch your data.
In general, that is what you want. You have a model that is constant in time, but it gets transformed around.
If however, you do need to update your data, you need to create your own transformation matrix, manually multiply it and then draw with a clean transformation matrix (with glLoadIdentity)
You could get a little help from OpenGL though, as you can get the transformation matrix from OpenGL, but I don't recommend this. The math is not that hard and you'd appreciate learning how to do it.
How can i flip, ot mirror up/left of the image OBJECT, not the screen in the onDraw method?
I tried scale(1, -1, 1) that not worked
my code:
opengl.selectVertex(vertexname)
.translate(x-1, y, -9);
if (opengl.getPathModifier(vertexname).getAngle()>-180 &&
opengl.getPathModifier(vertexname).getAngle()<0 ) {
opengl.selectVertex(vertexname).scale(-scale,scale,1);
} else {
opengl.selectVertex(vertexname).scale(scale,scale,1);
}
opengl.rotate(opengl.getPathModifier(vertexname).getAngle()+90, 0, 0, 1);
Not 100% sure what you're asking here, but I think what you want to do is transform the projection matrix. If you want to flip so that things on the left appear on the right then you need to Scale the projection matrix by (-1, 1, 1). If you want things at the top to appear at the bottom you need to scale by (1,-1, 1) and if you want both you can scale (-1, -1, 1).
Edit based on extra information: If all you want to do is display the object exactly the same, but with texture flipped, you need to change the texture coordinates of the vertices in the objects - flip the texture coordinates by replacing the old u texture coordinate with 1-u.
If you are using glFrustum(l,r,b,t,n,f) then change it yo glFrustum(l,r,t,b,n,f), in case you want a vertical flip. This apply also to glOrtho function.