Change of coordinates with glFrustum - c++

I am trying to use glFrustum for a perspective projection and as I understand glFrustum like glOrtho can be used to modify the mapping of our desired coordinate system to real screen coordinates (as read in Blue book). So, if I do glFrustum(-1,1,-1,1,1,1000);, it changes the coordinates to
left = -1, right = 1, bottom = -1, top = 1 in the form of cartesian coordinates.
I tried to draw a simple room (with 2 side walls) in this coordinate system by calling the following function in my draw method and it comes out drawing appropriately on the screen.
void drawRoomWalls(){
//Left wall
glBegin(GL_QUADS);
glVertex3f(-1.0, 1.0, 0.0);
glVertex3f(-1.0, 1.0, -0.4);
glVertex3f(-1.0, -1.0, -0.4);
glVertex3f(-1.0, -1.0, 0.0);
glEnd();
//Right wall
glBegin(GL_QUADS);
glVertex3f(1.0, 1.0, 0.0);
glVertex3f(1.0, 1.0, -0.4);
glVertex3f(1.0, -1.0, -0.4);
glVertex3f(1.0, -1.0, 0.0);
glEnd();
}
The function was called as follows:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(1.0, -1.0, -1.0, 1.0, 1.0, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -1.0);
drawRoomWalls();
Subsequently, I tried to do an off-axis projection (by taking mouse as input instead of user's head). The code is as follows:
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
double fov, near, far;
double headX, headY, headZ;
float aspectRatio;
near = 0.5f; far = 1000.0f; aspectRatio = ofGetWidth()/ofGetHeight();
fov = tan(DEG_TO_RAD * 30/2); //tan accepts angle in radians. tan of the half of the fov angle
fov = 0.5; //taken constant for now
double msX = (double)ofGetMouseX();
double msY = (double)ofGetMouseY();
double scrWidth = (double)ofGetWidth();
double scrHeight = (double)ofGetHeight();
headX = (msX / scrWidth) - 0.5;
headY = ((scrHeight - msY) / scrHeight) - 0.5;
headZ = -2.0;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum( near * (-fov * aspectRatio + headX),
near * (fov * aspectRatio + headX),
near * (-fov + headY),
near * (fov + headY),
near,
far);
leftValue = near * (-fov * aspectRatio + headX); //for printing out on screen
rightValue = near * (fov * aspectRatio + headX); //for printing out on screen
bottomValue = near * (-fov + headY); //for printing out on screen
topValue = near * (fov + headY); //for printing out on screen
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(headX * headZ, headY * headZ, 0, headX * headZ, headY * headZ, -1, 0, 1, 0);
glTranslatef(0.0, 0.0, headZ);
drawRoomWalls();
I printed the values of leftValue, rightValue, bottomValue, topValue and when the mouse is in the center of the screen (the call to glFrustum looks like glFrustum(-0.25,0.25,-0.25,0.25,1.0,1000.0)
As per the above call, I expected the coordinate system's left=-0.25, right=0.25, bottom=-0.25, top=0.25 and I was expecting the walls to disappear (since they are draw at (1.0,1.0,0.0) for an example). However, the walls keep on appearing on the sides of the screen (with the scene being skewed and at the center being essentially the same as with off-axis projection). Why is it that the walls are still at the place on the sides (even though the coordinates changed to -0.25,0.25) or there's something in glFrustum call that I am missing here about the coordinate system?

I just realized that you also changed headZ to -2. That explains the behaviour.
If the camera is situated at z = 2 and you have a vertical half fov of 0.5, then at z = 0 you already see 1 unit in positive and negative y-direction. That's why you see the walls.

Related

glutWarpPointer for FP camera issue

I am working on a project for mouse control to look around. I have been successful with rotating the camera using the mouse however I am trying to get the mouse to do 360 turns without the mouse leaving the screen before it can fully rotate. I read around and came across glutWarpPointer(), where most posts said to use it after getting the difference between the centre and the new mouse position in the window.
The issue this causes with my program is that when I move the mouse it will rotate very slightly for that frame the restart the location to the original point. Basically it i have a blue ball in front of me and green behind when i try to rotate towards the green ball the camera will move slightly but stay locked on the blue ball.
Is there a way to around or a fix?
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//rotate and translating the camera
glRotatef(-camVert, 1.0, 0.0, 0.0);
glRotatef(-camHor, 0.0, 1.0, 0.0);
glTranslatef(-posx,-posy,-posz);
//blue and green ball
glPushMatrix();
glColor3f(0.0, 1.0, 0.0);
glTranslatef(1, 0, 25);
glutSolidSphere(5, 20, 20);
glPopMatrix();
glPushMatrix();
glColor3f(0.0,0.0,1.0);
glTranslatef(0, 0, -25);
glutSolidSphere(5, 20, 20);
glPopMatrix();
};
void setAngle(float angy, float angx)
{
camVert = angy*1.5;
if (camVert > 90)
camVert = 90;
else if (camVert < -90)
camVert = -90;
camHor = angx*0.5;
}
void MoveCam(float distance, float direction)
{
float rads = (camHor+direction)*PI / 180;
posx -= sin(rads)*distance;
posz -= cos(rads)*distance;
};
void mouseMove(int x, int y)
{
float diffx = midx - x;
float diffy = midy - y;
setAngle(diffy,diffx);
//glutWarpPointer(glutGet(GLUT_WINDOW_WIDTH) / 2, glutGet(GLUT_WINDOW_HEIGHT) / 2);
}

opengl: rotating an ellipse

I am having trouble rotating an ellipse in OpenGL. So, I have some code to draw an ellipse as follows:
glPushAttrib(GL_CURRENT_BIT);
glColor3f(1.0f, 0.0f, 0.0f);
glLineWidth(2.0);
glPushMatrix();
glTranslatef(0, 0, 0); // ellipse centre
glBegin(GL_LINE_LOOP);
float inc = (float) M_PI / 500.0;
for (GLfloat i = 0; i < M_PI * 2; i+=inc)
{
float x = cos(i) * 0.4;
float y = sin(i) * 0.4;
glVertex2f(x, y);
}
glEnd();
glPopMatrix();
glPopAttrib();
This produces a picture as so:
Now what I want to do is rotate this ellipse clockwise. So I added a glrotate in between but the result was not what I had expected.
So, I did something like:
glPushAttrib(GL_CURRENT_BIT);
glColor3f(1.0f, 0.0f, 0.0f);
glLineWidth(2.0);
glPushMatrix();
glTranslatef(0, 0, 0);
glRotatef(-90, 1, 1, 0);
glBegin(GL_LINE_LOOP);
float inc = (float) M_PI / 500.0;
for (GLfloat i = 0; i < M_PI * 2; i+=inc)
{
float x = cos(i) * 0.4;
float y = sin(i) * 0.4;
glVertex2f(x, y);
}
glEnd();
glPopMatrix();
glPopAttrib();
This produced an image which was simply collapsed. What I wanted to do was rotate the ellipse along its center by the specified degrees. Also, I tried playing around with the various parameters of glRotatef but could not get it do as I expected. The resulting image looks like:
You're working in the XY plane, so you can't really rotate around a vector in XY. Instead, you want to rotate along the unit Z axis (glRotate (angle, 0, 0, 1);). Imagine your screen being the XY coordinate system and the Z axis pointing inwards. What you want is to rotate around the Z axis, so your XY plane remains in the XY plane.

Rotate object around constantly rotating object

I'm trying to simulate the solar system and need to get the moon to orbit a planet orbiting the sun
i am currently using the following code to rotate the planets
glPushMatrix();
glRotated((GLdouble)(spin*earth.speed), 0.0, 0.0, 1.0);
glTranslated(earth.xPos, earth.yPos, earth.zPos);
earth.draw();
glPopMatrix();
i'm trying to use the code below to make my moon orbit the earth however at the moment all i can do is rotate around a specific point.
glPushMatrix();
//define one time only start location
bool start = true;
if (start)
{
glTranslated(earthMoon.xPos, earthMoon.yPos, earthMoon.zPos);
start = false;
}
//orbit earths start point
//perfectly fits around earth
glTranslatef(-0.1, -0.1, 0);
glRotatef(spin*10, 0, 0, 1);
glTranslatef(0.1, 0.1, 0);
// need translation vector to follow earth
//glTranslated(earthMoon.xPos, earthMoon.yPos, earthMoon.zPos);
earthMoon.draw();
glPopMatrix();
i think what i need to do is find some way of knowing earths position from the rotatef function.
I have a class for the planets with the following attributes and methods:
float radius;
float xPos;
float yPos;
float zPos;
float speed;
planet(float r, float x, float y, float z, float speed);
~planet();
void draw(void)
{
glPushMatrix();
glColor3f(0.0, 1.0, 1.0);
glutSolidSphere(radius, 20, 10);
glPopMatrix();
}
the class' coordinates do not get updated when the planet rotates
Does anyone know how to get this to work?
Don't pop your matrix once you drew earth,
then your new referential will be the earth position,
you just have to call the moon drawing code and it will rotate around your earth.
Found a fix that works as intended in case anyone else is struggling with this concept
//earth
glPushMatrix();
//earth orbit
glRotated((GLdouble)(spin*earth.speed), 0.0, 0.0, 1.0);
glTranslated(earth.xPos, earth.yPos, earth.zPos);
//earth mooon
glPushMatrix();
//orbit around earth
glRotatef(spin * 5, 0, 0, 1);
glTranslatef(0.1, 0.1, 0.0);
//rotate around self
glRotated((GLdouble)spin, 0.0, 1.0, 0.0);
//draw moon
earthMoon.draw();
glPopMatrix();
//rotate around self
glRotated((GLdouble)spin, 0.0, 1.0, 0.0);
//draw earth
earth.draw();
glPopMatrix();
//
Hope this helps anyone else

Keep world within viewport bounds

I am currently trying to create a 2D side scroller and i currently have my "world" drawing (a large white box for the time being), but i cannot figure out any relationship between the edge of the world map and the edge of the viewport to ensure that the viewport is always fully covered by the map.
My basic world drawing code is:
void drawTiles(void)
{
for (int i = 0; i < 50; i++)
{
for (int j = 0; j < 500; j++)
{
glPushMatrix();
glTranslatef(j, -i, 0);
glBegin (GL_QUADS);
glTexCoord2d(0.0, 0.0);
glVertex3f(0.0, 0.0, 0.0);
glTexCoord2d(1.0, 0.0);
glVertex3f(1.0, 0.0, 0.0);
glTexCoord2d(1.0, 1.0);
glVertex3f(1.0, 1.0, 0.0);
glTexCoord2d(0.0, 1.0);
glVertex3f(0.0, 1.0, 0.0);
glEnd();
glPopMatrix();
}
}
}
void display(void)
{
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glPushMatrix();
glTranslatef(camX, camY, -20); //translate back a bit to view the map correctly (our camera)
drawTiles(); //draw our tiles
glPopMatrix();
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
}
void keyboard(unsigned char key, int x, int y)
{
switch(key)
{
case 'w':
camY -= 0.25;
break;
case 's':
camY += 0.25;
break;
case 'a':
camX += 0.25;
break;
case 'd':
camX -= 0.25;
break;
}
}
How would i go about ensuring that when I use WASD and on viewport resize, that i do not translate beyond the bounds of the map (currently 500x50 tiles)?
If you have a planar scene (2D only), it should be sufficient to use an orthographic projection transformation. The projection transformation determines the camera's parameters. In the current state (with the perspective projection) you have a usual pin hole camera with a vertical opening angle of 60°.
An orthographic projection is defined by its edges. Let's say you want your camera to "see" two units to the left, 3 units to the right, 1 unit up and 4 units down. This would be possible, although it might not be reasonable in your case.
The current perspective camera "sees" about 11.5 units up and down. The according width can be calculated from the window dimensions (we do not want to stretch the image). So instead of gluPerspective, use the following:
float halfHeight = 11.5f;
float halfWidth = halfHeight * (GLfloat)w / (GLfloat)h; //based on window aspect ratio
glOrtho(-halfWidth, halfWidth, halfHeight, -halfHeight, -1, 1);
If you want to change the visible area, you just need to adjust the halfHeight. The -1 and 1 are the znear and zfar plane. Everything that is between these planes is visible. Everything else will be cut off. But since you have only 2D content, this should not be relevant.
In your call to glTranslatef(camX, camY, -20);, set the z-coordinate to 0. This is not needed any more, because we have an orthographic view.
Now if you want to check if the map is still visible, do the following. I'll just show the exmple of checking the left/right boundary. The vertical case is similar:
//The camera can see from camX-halfWidth to camX+halfWidth
//You might want to make halfWidth and halfHeight class variables
float leftMapBoundary = 0;
float rightMapBoundary = 500;
//the camera must be
// * at least halfWidth right of the left boundary and
// * at least halfWidth left of the right one:
if(camX < leftMapBoundary + halfWidth)
camX = leftMapBoundary + halfWidth;
if(camX > rightMapBoundary - halfWidth)
camX = rightMapBoundary - halfWidth;
Add the code after the switch in your keyboard function or whenever you move the camera.

The object flips after 180 degree of rotation

I m new to OpenGL. I have a viewer for a point cloud.
When I rotate the point cloud around x-axis or z-axis , it gets flipped or inverted after 180 degrees and flips back again at 0 degrees.
From 0-180 degrees it works fine and from 180-360 degrees it flips.
The following is a code snippet for the paint event
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_center[0] = (m_minX+m_maxX)/2.0f;
m_center[1] = (m_minY+m_maxY)/2.0f;
m_center[2] = (m_minZ+m_maxZ)/2.0f;
m_radius = static_cast<float>(std::sqrt((m_maxX-m_center[0])*(m_maxX-m_center[0])
+(m_maxY-m_center[1])*(m_maxY-m_center[1])+(m_maxZ-m_center[2])*(m_maxZ-m_center[2])));
m_fDistance = m_radius/0.57735f; //where 0.57735f is tan(30 degrees)
m_dNear = m_fDistance - 3*m_radius;
m_dFar = m_fDistance + 3*m_radius;
glLoadIdentity();
glPushMatrix();
// glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-m_zoomFactor*2*m_radius, +m_zoomFactor*2*m_radius, -m_zoomFactor*2*m_radius, +m_zoomFactor*2*m_radius, m_dNear, m_dFar);
glMatrixMode(GL_MODELVIEW);
glRotatef(m_modelRotation.x/16.0, 1.0, 0.0, 0.0);
glRotatef(m_modelRotation.y/16.0, 0.0, 1.0, 0.0);
glRotatef(m_modelRotation.z/16.0, 0.0, 0.0, 1.0);
glScalef(m_modelScale.x,m_modelScale.y,m_modelScale.z);
switch(m_shapeMode)
{
case eShapePointCloud:
drawPoints();
break;
case eShapeSolid:
drawQuads();
break;
default:
qDebug()<<"No shape is specified, so use the points shape.";
drawPoints();
break;
}
glPopMatrix();
Here is the code for the mouse move event
void OglWidget::mouseMoveEvent(QMouseEvent *event)
{
GLfloat dx = GLfloat(event->x() - m_lastPos.x()) ;/// width();
GLfloat dy = GLfloat(event->y() - m_lastPos.y()) ;/// height();
if (event->buttons() & Qt::LeftButton)
{
setXRotation(m_modelRotation.x + 8*dy);
setZRotation(m_modelRotation.z + 8*dx);
this->setPropertyValue(1,m_modelRotation);
}
m_lastPos = event->pos();
}