So basically i have created an application that renders a curve in a 2D-Dimension (X and Y). This application also uses a haptic device, which is represented as a cursor inside of the frame where the curve is also rendered.
The only thing i want to achieve is to map the y-axis of my projection to my z-axis of the haptic workspace of my haptic device. So basically i want to mantain the logic of the curve and cursor rendering and only change the used axis for my haptic workspace. I used this question: Swap axis y and z
as a guidance. Practically i just have to use glRotatef(90, 1, 0, 0) to achieve what i want. I tried rotating only the projection matrix of my haptic workspace, but this sadly doesnt work for me.
This is how my normal curve looks:
Those are the results, while trying different options:
This is my code
reshapeCallback:
void reshapeCallback(int width, int height) {
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(minX, maxX, minY, maxY, -1, 11);
// glRotatef(90, 1, 0, 0) [4th Try]
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// glRotatef(90, 1, 0, 0) [5th Try]
updateWorkspace();
}
updateWorkspace():
void updateWorkspace() {
GLdouble modelview[16];
GLdouble projection[16];
GLint viewport[4];
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
//glRotatef(90, 1, 0, 0) [2nd try]
glGetDoublev(GL_PROJECTION_MATRIX, projection);
//glRotatef(90, 1, 0, 0) [1st try]
glGetIntegerv(GL_VIEWPORT, viewport);
hlMatrixMode(HL_TOUCHWORKSPACE);
hlLoadIdentity();
//glRotatef(90, 1, 0, 0) [3rd try]
// Fit haptic workspace to view volume.
hluFitWorkspace(projection);
// Compute cursor scale.
gCursorScale = hluScreenToModelScale(modelview, projection, viewport);
gCursorScale *= CURSOR_SIZE_PIXELS;
}
drawLine():
void drawLine() {
static GLuint displayList = 0;
if (displayList)
{
glCallList(displayList);
}
else
{
displayList = glGenLists(1);
glNewList(displayList, GL_COMPILE_AND_EXECUTE);
glPushAttrib(GL_ENABLE_BIT | GL_LIGHTING_BIT);
glDisable(GL_LIGHTING);
glLineWidth(2.0);
glBegin(GL_LINE_STRIP);
for (int i = 0; i < vertices.size(); i += 2) {
glVertex2f(vertices[i], vertices[i + 1]);
}
glEnd();
glPopAttrib();
glEndList();
}
}
I numerated the tries of glRotatef() for different positions. I hope someone can give me a hint where my thought process went wrong.
Point the first: For the love of God USE SHADERS AND NOT OLD OPENGL.
With shaders, it is a trivial task of swapping 2 arguments, changing
gl_Position = vec4(x, y, z, 1);
to
gl_Position = vec4(x, z, y, 1);
I would recommend looking at this tutorial for shaders.
Other that that, in my opinion the code looks like it should work, but if it doesn't I know how finicky glRotate can be, so I really have no advice other that to try things until it works.
Sorry I couldn't give a full, direct answer.
Related
I am trying to render a rectangle onto the screen. When the program is run, only the clear color shows up, and no rectangle.
Here's the code:
glClearColor(0.0, 0.0, 0.0, 0.0);
glViewport(0, 0, 1280, 720);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1280, 720, 0, -10, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT || GL_DEPTH_BUFFER_BIT); //Clear the screen and depth buffer
int x = 100;
int y = 100;
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
glBegin(GL_QUADS);
glVertex2f(x, y);
glVertex2f(x + 10, y);
glVertex2f(x + 10, y + 10);
glVertex2f(x, y + 10);
glEnd();
gsm->update();
gsm->render();
glfwSwapBuffers(window);
}
It got culled. You had inverted Y axis with your projection, by supplying bottom =720 larger than top 0. Your quad is counterclockwise in your local coordinates, but in normalized coordinates it is clockwise. Remember, projection matrix is a part of global transform matrix! Now, if that's default state, then out of those two winding directions
the GL_CCW is the actual one, it is considered "Front". By default OpenGL culls triangles with mode glCullFace(GL_BACK), and quad internally is considered as pair of triangles).
Either change order of vertices
glBegin(GL_QUADS);
glVertex2f(x, y);
glVertex2f(x, y + 10);
glVertex2f(x + 10, y + 10);
glVertex2f(x + 10, y);
glEnd();
or change culling mode to match left-handedness of your coordinate system or disable culling.
See also:
1. https://www.khronos.org/opengl/wiki/Viewing_and_Transformations
2. The answer to Is OpenGL coordinate system left-handed or right-handed?
Okay, so my program opens a file, reads in xyz-points, then draws a line strip out of it. I originally had this program written in SharpGL (implemnted as WPF window) and it worked, but not well due to using immediate mode, so I have moved onto OpenGL in C++. I have (somewhat) figured out VBO's and I now I am trying to add mouse functionality now. My problem is I can't move the picture with my mouse, I want to be able to click and 'drag' the picture. My mouseClickFunc and mouseMotion work (my cout statements execute), however it seems like my translate call is never being executed (i.e. the picture starts partially 'clipped' in the scene and I would like the ability to drag it and center it). I know this is a shot in the dark but I am really not sure what to do.
MotionFunc:
void mouseMotion(int x, int y)
{
if (moveable)
{
xMove += xTransform(x) - xTransform(xDown);
yMove += yTransform(y) - yTransform(yDown);
xDown = x;
yDown = y;
cout << yMove << "---" << xMove << endl;
glutSwapBuffers();
glutPostRedisplay();
}
}
Display Function:
void RenderFunction(void)
{
++FrameCount;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glOrtho(xMin - 1, xMax + 1, yMin - 1, yMax + 1, -diameter * zScale, diameter * zScale);
// Reset the modelview matrix.
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
glTranslatef(xMove, -yMove, 0);
//glViewport((GLint)xMove*100, (GLint)-yMove*100, CurrentWidth, CurrentHeight);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_LINE_STRIP, 0, 29000);
glPopMatrix();
glutSwapBuffers();
glutPostRedisplay();
}
I am sure there is more code that I need to show, this is just where I think the problem is. Any help would be greatly appreciated.
Here is a picture of a console output and the screen (OpenGL context) as I see it.
UPDATE: Updated my code. It looks like my coordinates are moving, but the picture is not if that makes sense. If you look at my output, if I keep 'dragging' the picture, you can see in the console that the variable xMove and yMove can get as large or small as they want, again translate is just never moving it.
You pop your matrix before drawing things, which resets the matrix to the state of last push matrix. Move glPopMatrix(); below draw call
You're popping the matrix before you call glDrawArrays(), so this naturally negates the effect of the translation. It also negates the glOrtho() call, but that should be issued on the projection matrix and not on the modelview matrix in the first place.
And, of course, the problem is in your code, and not in OpenGL.
In this code excerpt :
glLoadIdentity();
glPushMatrix();
glTranslatef(xMove, -yMove, 0);
//glViewport((GLint)xMove*100, (GLint)-yMove*100, CurrentWidth, CurrentHeight);
glOrtho(xMin - 1, xMax + 1, yMin - 1, yMax + 1, -diameter * zScale, diameter * zScale);
glPopMatrix();
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_LINE_STRIP, 0, 29000);
you are :
setting the identity as the view matrix
push it into the queue
modify it by glTranslate
pop it of the stack
render the image
Therefore, your translation is ignored.
This is correct operation :
glLoadIdentity();
glPushMatrix();
glTranslatef(xMove, -yMove, 0);
//glViewport((GLint)xMove*100, (GLint)-yMove*100, CurrentWidth, CurrentHeight);
glOrtho(xMin - 1, xMax + 1, yMin - 1, yMax + 1, -diameter * zScale, diameter * zScale);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_LINE_STRIP, 0, 29000);
glPopMatrix();
You are multiplying projection before translation, remember to always read matrix transformation from bottome to top in OpenGL 1.1 which you should upgrade IMO. Another issue is that you are poping the matrix before drawing.
Correct code:
//glViewport((GLint)xMove*100, (GLint)-yMove*100, CurrentWidth, CurrentHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(xMin - 1, xMax + 1, yMin - 1, yMax + 1, -diameter * zScale, diameter * zScale);
// Reset the modelview matrix.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
glTranslatef(xMove, -yMove, 0);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_LINE_STRIP, 0, 29000);
glPopMatrix();
I'm working on a 2D image viewer, I want to retrieve openGL mouse position on texture but I can't get it to work if glTranslatef() or glScalef() calls are made on the modelview matrix.
I'm using a QGLWidget , of the famous Qt library.
Here are the important calls :
Resize function :
void ViewerGL::resizeGL(int width, int height){
glViewport (0, 0, width, height);
Display function :
void ViewerGL::paintGL()
{ int w = width();
int h = height();
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
//transX,transY are for panning around the image in the viewer
float left = (0.f+transX) ;
float right = (w+transX) ;
float bottom = (h-transY);
float top = (0.f-transY) ;
glOrtho(left, right, top, bottom, -1, 1);
... later in paintGL:
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
//padx,pady are used to translate the image from the bottom left corner
// to the center of the viewer
float padx,pady;
padx= ((float)width() - _dw.w()*zoomFactor)/2.f; // _dw.w is the width of the texture
pady =((float)height() - _dw.h()*zoomFactor)/2.f ;// _dw.h is the height of the texture
glTranslatef( padx , pady, 0);
//zoomX,zoomY are the position at which the user required a zoom
glTranslatef(-zoomX,-zoomY, 0.f);
glScalef(zoomFactor, zoomFactor,0.f);
glTranslatef(zoomX ,zoomY, 0.f);
Now here is my function to retrieve the openGL coordinates :
QPoint ViewerGL::openGLpos(int x,int y){
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX=0, winY=0, winZ=0;
GLdouble posX=0, posY=0, posZ=0;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
winX = (float)x;
winY = height()- y;
if(winY == 0) winY =1.f;
glReadPixels( x, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
return QPoint(posX,posY);
}
So far , here is what I noticed:
The code like this returns always (0,0) and GLU_FALSE is returned from gluUnproject. I read somewhere on a forum that it could be because of the modelview matrix, so I put the identity matrix instead, but,if I do it, I get exactly the coordinates of the mouse in the window...
Before , I dealt with the zoom using the orthographic projection, but I couldn't make it work perfectly, so to make it simpler I decided to retrieve openGL position, and use glTranslatef/glScalef instead .
If I remove all the translating / scaling stuff in the paintGL function, everything is working...but the zoom doesn't work :x)
I'm requesting your help to make this damned zoom to point working, using the gluUnProject solution;)
Aigth , nevermind, I found the solution : I was zeroing out the z in glScalef(x,y,z)
so it made the matrix non-invertible...
how to handle clik on specific 3d or 2d object in opengl, for example i have the following code
void Widget::drawCircle(float radius) {
glBegin(GL_TRIANGLE_FAN);
for (int i = 0; i < 360; i++) {
float degInRad = i*DEG2RAD;
glVertex2f(cos(degInRad) * radius, sin(degInRad) * radius);
}
glEnd();
}
So i need to handle click on this circle, is there any solutions for this problem?
When I need to detect clicks, I usually do my ordinary draw loop, but instead of drawing the objects with texturing, lighting and other effects enabled, I draw each of them with flat/no shading, each in a different color. I then check the color on the pixel the mouse is on, and map backwards from the color returned from the framebuffer to the object that I drew with that color.
Perhaps this technique is useful for you, too.
Take a look into this nehe tutorial item. It is very complex, but it shows how opengl picking works. In my opinion, if you need it, you are better with some game engine then with opengl.
Here is another (similar) way of selecting items in opengl.
opengl mouse raytracing will provide you with all details how to select items in opengl. This thread even provides the code how it is done :
Vector3 World::projectedMouse(float mx, float my){
GLdouble model_view[16];
GLint viewport[4];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble dx, dy, dz;
glGetDoublev(GL_MODELVIEW_MATRIX, model_view);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
winX = (float)mx;
winY = (float)viewport[3] - (float)my;
glReadPixels ((int)mx, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, 0, model_view, projection, viewport, &bx, &by, &bz);
Vector3 pr2 = Vector3(bx, by, bz);
glColor3f(1,0,0);
glBegin (GL_LINE_LOOP);
glVertex3f(player->getPosition().x, player->getPosition().y + 100, player->getPosition().z); // 0
glVertex3f(pr.x,pr.y,pr.z); // 1
glVertex3f(player->getPosition().x, player->getPosition().y, player->getPosition().z); // 0
glEnd();
return pr;
}
I am using SDL with OpenGL and up until now I have done everything in 2d (Using glOrtho())
Now I want to try drawing things in 3D but of course since I have now thrown a 3rd dimension into the mixture, things are getting more complicated.
What I want to do is, take the screen co-ordinates of the cursor, translate them to (?)world co-ordinates (Co-ordinates I can use in OpenGL since I now have perspective etc.), and draw a quad at that position (The mouse cursor being at the center of the quad)
I have read a few tutorials on gluUnProject() and I sort of understand what I am supposed to do, but since I am learning by myself it is very easy to get confused and not properly understand what I am doing.
As such, I have mainly copied from examples I have found.
Below is my code (I took out error checking etc in an attempt to cut it down a bit)
As you can see, I have mouseX, mouseY and mouseZ variables, of which mouseX and mouseY get their values from SDL.
I tried to use glReadPixel() to get mouseZ but I am not sure if I am doing it right.
The problem is that when I click, the quad is not drawn in the correct position (I guess partly due to the fact that I don't know how to get mouseZ properly, so I just replaced it in gluUnProject() with 1.0?)
I am using the new co-ordinates (wx, wy, wz) in the glTranslatef() function but once again because I don't know how to get mouseZ properly I am guessing this is the reason it doesn't work properly.
If I replace wz with -499 it seems to work ok, but I need to know how to get accurate results without manually finding the correct (Or almost correct) numbers
If anybody can tell me what I am doing wrong, or give me any advice it would be greatly appreciated.
#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
SDL_Surface* screen = 0;
SDL_Event event;
bool isRunning = true;
int mouseX, mouseY, mouseZ = 0;
GLint viewport[4];
GLdouble mvmatrix[16], projmatrix[16];
GLint realY; /* OpenGL y coordinate position */
GLdouble wx, wy, wz; /* returned world x, y, z coords */
int main(int argc, char **argv) {
SDL_Init(SDL_INIT_EVERYTHING);
screen = SDL_SetVideoMode(800, 600, 32, SDL_OPENGL);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glViewport(0, 0, 800, 600);
glClearDepth(1.f);
glClearColor(0, 0, 0, 0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(90.f, 1.f, 1.f, 500.f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
while(isRunning) {
while(SDL_PollEvent(&event)) {
if(event.type == SDL_QUIT) {
isRunning = false;
}
if(event.type == SDL_MOUSEBUTTONDOWN) {
if(event.button.button == SDL_BUTTON_LEFT) {
SDL_GetMouseState(&mouseX, &mouseY);
glGetIntegerv(GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
realY = viewport[3] - (GLint) mouseY - 1;
glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &mouseZ);
gluUnProject((GLdouble)mouseX, (GLdouble)realY, 1.0, mvmatrix, projmatrix, viewport, &wx, &wy, &wz);
}
}
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(wx, wy, wz);
glBegin(GL_QUADS);
glColor4f(1.f, 0.f, 0.f, 1.f);
glVertex3f(-20.f, -20.f, 0.f);
glColor4f(0.f, 1.f, 0.f, 1.f);
glVertex3f(-20.f, 20.f, 0.f);
glColor4f(0.f, 0.f, 1.f, 1.f);
glVertex3f(20.f, 20.f, 0.f);
glColor4f(1.f, 0.f, 1.f, 1.f);
glVertex3f(20.f, -20.f, 0.f);
glEnd();
glPopMatrix();
SDL_GL_SwapBuffers();
}
SDL_FreeSurface(screen);
return 0;
}
I found that if I added the following:
GLfloat depth[2];
then changed the glReadPixels() from:
glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &mouseZ);
to:
glReadPixels(mouseX, realY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, depth);
then I just changed gluUnProject() from:
gluUnProject((GLdouble)mouseX, (GLdouble)realY, mouseZ, mvmatrix, projmatrix, viewport, &wx, &wy, &wz);
to:
gluUnProject((GLdouble) mouseX, (GLdouble) realY, depth[0], mvmatrix, projmatrix, viewport, &wx, &wy, &wz);
then I could get the Z value I wanted.
Then I just added a small value to wz, for example: wz += 1; to make sure the quad appeared above the place I clicked and it seems to work as I intended now.
To guess a Z you need to have a meaning for this z :
Usually the meaning is that you want to intersect the line corresponding to (camera origin,pixel) with a 3d object with is displayed. You can use the Z buffer for this.
If not ( you want to stick a polygon to the cursor with no 3D relation ), just use the 2D functions, you can just alternate between 3D and 2D projections while drawing in the same frame (with either the projection matrix or with 2D specific functions), thus you don't need a z.
Setting a default value for Z is a bad idea if it has no rationale in your design.
There is only one value of Z which will give you correct pixel coordinates.
The link between 2D and 3D coordinates in openGL is that OpenGl uses a projection plane at Z = 1.
And then scales the pixel coordinates from [-1,1] to pixels coordinates.