GLUT torus colliding with camera - c++
I want to implement collision of 6 torus which are randomly disturbed in the game area. It is a simple 3D space game using the perspective view and in first person. I saw some stack overflow answer suggesting to compute distance of whatever (player) to torus cell and if bigger than half or whole cell size you are colliding +/- your coordinate system and map topology tweaks. But if we take the distance that means we're only considering the z co-ordinates so if the camera moved to that distance (without considering x,y coordinates) it's always taking as a collision which is wrong right?
I'm hoping to do this using AABB algorithm. Is it ok to consider camera position and torus position as 2 boxes and check the collision (box to box collision) or camera as a point and torus as a box (point to box)? Or can somebody suggest best way to do that?
Below is the code that I've tried so far.
float im[16], m[16], znear = 0.1, zfar = 100.0, fovx = 45.0 * M_PI / 180.0;
glm::vec3 p0, p1, p2, p3, o, u, v;
//p0, p1, p2, p3 holds your znear camera screen corners in world coordinates
void ChangeSize(int w, int h)
{
GLfloat fAspect;
// Prevent a divide by zero
if(h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
// Calculate aspect ratio of the window
fAspect = (GLfloat)w*1.0/(GLfloat)h;
// Set the perspective coordinate system
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// field of view of 45 degrees, near and far planes 1.0 and 1000
//that znear and zfar should typically have a ratio of 1000:1 to make sorting out z depth easier for the GPU
gluPerspective(45.0f, fAspect, 0.1f, 300.0f); //may need to make larger depending on project
// Modelview matrix reset
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// get camera matrix (must be in right place in code before model transformations)
glGetFloatv(GL_MODELVIEW_MATRIX, im); // get camera inverse matrix
matrix_inv(m, im); // m = inverse(im)
u = glm::vec3(m[0], m[1], m[2]); // x axis
v = glm::vec3(m[4], m[5], m[6]); // y axis
o = glm::vec3(m[12], m[13], m[14]); // origin
o -= glm::vec3(m[8], m[9], m[10]) * znear; // z axis offset
// scale by FOV
u *= znear * tan(0.5 * fovx);
v *= znear * tan(0.5 * fovx / fAspect);
// get rectangle coorners
p0 = o - u - v;
p1 = o + u - v;
p2 = o + u + v;
p3 = o - u + v;
}
void matrix_inv(float* a, float* b) // a[16] = Inverse(b[16])
{
float x, y, z;
// transpose of rotation matrix
a[0] = b[0];
a[5] = b[5];
a[10] = b[10];
x = b[1]; a[1] = b[4]; a[4] = x;
x = b[2]; a[2] = b[8]; a[8] = x;
x = b[6]; a[6] = b[9]; a[9] = x;
// copy projection part
a[3] = b[3];
a[7] = b[7];
a[11] = b[11];
a[15] = b[15];
// convert origin: new_pos = - new_rotation_matrix * old_pos
x = (a[0] * b[12]) + (a[4] * b[13]) + (a[8] * b[14]);
y = (a[1] * b[12]) + (a[5] * b[13]) + (a[9] * b[14]);
z = (a[2] * b[12]) + (a[6] * b[13]) + (a[10] * b[14]);
a[12] = -x;
a[13] = -y;
a[14] = -z;
}
//Store torus coordinates
std::vector<std::vector<GLfloat>> translateTorus = { { 0.0, 1.0, -10.0, 1 }, { 0.0, 4.0, -6.0, 1 } , { -1.0, 0.0, -4.0, 1 },
{ 3.0, 1.0, -6.0, 1 }, { 1.0, -1.0, -9.0, 1 } , { 4.0, 1.0, -4.0, 1 } };
GLfloat xpos, ypos, zpos, flagToDisplayCrystal;
//Looping through 6 Torus
for (int i = 0; i < translateTorus.size(); i++) {
//Get the torus coordinates
xpos = translateTorus[i][0];
ypos = translateTorus[i][1];
zpos = translateTorus[i][2];
//This variable will work as a variable to display crystal after collision
flagToDisplayCrystal = translateTorus[i][3];
//p0 min, p2 max
//Creating a square using Torus index coordinates and radius
double halfside = 1.0 / 2;
//This (xpos+halfside), (xpos-halfside), (ypos+halfside), (ypos-halfside) are //created using Torus index and radius
float d1x = p0[0] - (xpos + halfside);
float d1y = p0[1] - (ypos + halfside);
float d2x = (xpos - halfside) - p2[0];
float d2y = (ypos - halfside) - p2[1];
//Collision is checking here
//For square's min z and max z is checking whether equal to camera's min //z and max z
if ((d1x > 0.0f || d1y > 0.0f || d2x > 0.0f || d2y > 0.0f) && p2[2] == zpos && p0[2] == zpos) {
//If there is collision update the variable as 0
translateTorus[i][3] = 0;
}
else {
if (flagToDisplayCrystal == 1) {
glPushMatrix();
glEnable(GL_TEXTURE_2D);
glTranslatef(xpos, ypos, zpos);
glRotatef(fPlanetRot, 0.0f, -1.0f, 0.0f);
glColor3f(0.0, 0.0, 0.0);
// Select the texture object
glBindTexture(GL_TEXTURE_2D, textures[3]);
glutSolidTorus(0.1, 1.0, 30, 30);
glDisable(GL_TEXTURE_2D);
glPopMatrix();
}
}
}
as I mentioned in the comments you got 2 options either use OpenGL rendering or compute entirely on CPU side without it. Let start with rendering first:
render your scene
but instead of color of torus and stuff use integer indexes (for example 0 empty space, 1 obstacle, 2 torus ...) you can even have separate indexes for each object in the world so you know exactly which one is hit etc ...
so: clear screen with empty color, render your scene (using indexes instead of color with glColor??(???)) without lighting or shading or whatever. But Do not swap buffers !!! as that would show the stuff on screen and cause flickering.
read rendered screen and depth buffers
you simply use glReadPixels to copy your screen and depth buffers into CPU side memory (1D arrays) lets call them scr[],zed[].
scan the scr[] for color matching torus indexes
simply loop through all pixels and if torus pixel found check its depth. If it is close enough to camera you found your collision.
render normally
now clear screen again and render your screen with colors and lighting... now you can swap buffers too.
Beware depth buffer will be non linear which requires linearization to obtain original depth in world units. For more about it and example of reading both scr,zed see:
depth buffer got by glReadPixels is always 1
OpenGL 3D-raypicking with high poly meshes
The other approach is is much faster in case you have not too many torus'es. You simply compute intersection between camera znear plane and torus. Which boils down to either AABB vs rectangle intersection or cylinder vs. rectangle intersection.
However if you not familiar with 3D vector math you might get lost quickly.
let assume the torus is described by AABB. Then intersection between that and rectangle boils down to checking intersection between line (each edge of AABB) and rectangle. So simply finding instersection between plane and line and checking if the point is inside rectangle.
if our rectangle is defined by its vertexes in CW or CCW order (p0,p1,p2,p3) and line by endpoints q0,q1 then:
n = normalize(cross(p1-p0,p2-p1)) // is rectangle normal
dq = normalize(q1-q0) // is line direction
q = q0 + dq*dot(dq,p1-p0) // is plane/line intersection
So now just check if q is inside rectangle. There are 2 ways either test if all crosses between q-edge_start and edge_end-edge_start have the same direction or all dots between all edge_normal and q-edge_point has the same sign or zero.
The problem is that both AABB and rectangle must be in the same coordinate system so either transform AABB into camera coordinates by using modelview matrix or transform the rectangle into world coordinates using inverse of modelview. The latter is better as you do it just once instead of transforming each torus'es AABB ...
For more info about math side see:
Cone to box collision
Understanding 4x4 homogenous transform matrices
The rectangle itself is just extracted from your camera matrix (part of modelviev) position, and x,y basis vectors gives you the "center" and axises of your rectangle... The size must be derived from the perspective matrix (or parameters you passed to it especially aspect ratio, FOV and znear)
Well first you need to obtain camera (view) matrix. The GL_MODELVIEW usually holds:
GL_MODELVIEW = Inverse(Camera)*Rendered_Object
so you need to find the place in your code where your GL_MODELVIEW holds just the Inverse(Camera) transformation and there place:
float aspect=float(xs)/float(ys); // aspect from OpenGL window resolution
float im[16],m[16],znear=0.1,zfar=100.0,fovx=60.0*M_PI/180.0;
vec3 p0,p1,p2,p3,o,u,v; // 3D vectors
// this is how my perspective is set
// glMatrixMode(GL_PROJECTION);
// glLoadIdentity();
// gluPerspective(fovx*180.0/(M_PI*aspect),aspect,znear,zfar);
// get camera matrix (must be in right place in code before model transformations)
glGetFloatv(GL_MODELVIEW_MATRIX,im); // get camera inverse matrix
matrix_inv(m,im); // m = inverse(im)
u =vec3(m[ 0],m[ 1],m[ 2]); // x axis
v =vec3(m[ 4],m[ 5],m[ 6]); // y axis
o =vec3(m[12],m[13],m[14]); // origin
o-=vec3(m[ 8],m[ 9],m[10])*znear; // z axis offset
// scale by FOV
u*=znear*tan(0.5*fovx);
v*=znear*tan(0.5*fovx/aspect);
// get rectangle coorners
p0=o-u-v;
p1=o+u-v;
p2=o+u+v;
p3=o-u+v;
// render it for debug
glColor3f(1.0,1.0,0.0);
glBegin(GL_QUADS);
glColor3f(1.0,0.0,0.0); glVertex3fv(p0.dat);
glColor3f(0.0,0.0,0.0); glVertex3fv(p1.dat);
glColor3f(0.0,0.0,1.0); glVertex3fv(p2.dat);
glColor3f(1.0,1.0,1.0); glVertex3fv(p3.dat);
glEnd();
Which basicaly loads the matrix into CPU side variables inverse it like this:
void matrix_inv(float *a,float *b) // a[16] = Inverse(b[16])
{
float x,y,z;
// transpose of rotation matrix
a[ 0]=b[ 0];
a[ 5]=b[ 5];
a[10]=b[10];
x=b[1]; a[1]=b[4]; a[4]=x;
x=b[2]; a[2]=b[8]; a[8]=x;
x=b[6]; a[6]=b[9]; a[9]=x;
// copy projection part
a[ 3]=b[ 3];
a[ 7]=b[ 7];
a[11]=b[11];
a[15]=b[15];
// convert origin: new_pos = - new_rotation_matrix * old_pos
x=(a[ 0]*b[12])+(a[ 4]*b[13])+(a[ 8]*b[14]);
y=(a[ 1]*b[12])+(a[ 5]*b[13])+(a[ 9]*b[14]);
z=(a[ 2]*b[12])+(a[ 6]*b[13])+(a[10]*b[14]);
a[12]=-x;
a[13]=-y;
a[14]=-z;
}
And compute the corners with perspective in mind as described above...
I used GLSL like vec3 but you can use any 3D math even own like float p0[3],.... You just need +,- and multiplying by constant.
Now the p0,p1,p2,p3 holds your znear camera screen corners in world coordinates.
[Edit1] example
I managed to put together simple example for this. Here support functiosn used first:
//---------------------------------------------------------------------------
void glutSolidTorus(float r,float R,int na,int nb) // render torus(r,R)
{
float *pnt=new float[(na+1)*(nb+1)*3*2]; if (pnt==NULL) return;
float *nor=pnt+((na+1)*(nb+1)*3);
float ca,sa,cb,sb,a,b,da,db,x,y,z,nx,ny,nz;
int ia,ib,i,j;
da=2.0*M_PI/float(na);
db=2.0*M_PI/float(nb);
glBegin(GL_LINES);
for (i=0,a=0.0,ia=0;ia<=na;ia++,a+=da){ ca=cos(a); sa=sin(a);
for ( b=0.0,ib=0;ib<=nb;ib++,b+=db){ cb=cos(b); sb=sin(b);
z=r*ca;
x=(R+z)*cb; nx=(x-(R*cb))/r;
y=(R+z)*sb; ny=(y-(R*sb))/r;
z=r*sa; nz=sa;
pnt[i]=x; nor[i]=nx; i++;
pnt[i]=y; nor[i]=ny; i++;
pnt[i]=z; nor[i]=nz; i++;
}}
glEnd();
for (ia=0;ia<na;ia++)
{
i=(ia+0)*(nb+1)*3;
j=(ia+1)*(nb+1)*3;
glBegin(GL_QUAD_STRIP);
for (ib=0;ib<=nb;ib++)
{
glNormal3fv(nor+i); glVertex3fv(pnt+i); i+=3;
glNormal3fv(nor+j); glVertex3fv(pnt+j); j+=3;
}
glEnd();
}
delete[] pnt;
}
//---------------------------------------------------------------------------
const int AABB_lin[]= // AABB lines
{
0,1,
1,2,
2,3,
3,0,
4,5,
5,6,
6,7,
7,4,
0,4,
1,5,
2,6,
3,7,
-1
};
const int AABB_fac[]= // AABB quads
{
3,2,1,0,
4,5,6,7,
0,1,5,4,
1,2,6,5,
2,3,7,6,
3,0,4,7,
-1
};
void AABBSolidTorus(vec3 *aabb,float r,float R) // aabb[8] = AABB of torus(r,R)
{
R+=r;
aabb[0]=vec3(-R,-R,-r);
aabb[1]=vec3(+R,-R,-r);
aabb[2]=vec3(+R,+R,-r);
aabb[3]=vec3(-R,+R,-r);
aabb[4]=vec3(-R,-R,+r);
aabb[5]=vec3(+R,-R,+r);
aabb[6]=vec3(+R,+R,+r);
aabb[7]=vec3(-R,+R,+r);
}
//---------------------------------------------------------------------------
void matrix_inv(float *a,float *b) // a[16] = Inverse(b[16])
{
float x,y,z;
// transpose of rotation matrix
a[ 0]=b[ 0];
a[ 5]=b[ 5];
a[10]=b[10];
x=b[1]; a[1]=b[4]; a[4]=x;
x=b[2]; a[2]=b[8]; a[8]=x;
x=b[6]; a[6]=b[9]; a[9]=x;
// copy projection part
a[ 3]=b[ 3];
a[ 7]=b[ 7];
a[11]=b[11];
a[15]=b[15];
// convert origin: new_pos = - new_rotation_matrix * old_pos
x=(a[ 0]*b[12])+(a[ 4]*b[13])+(a[ 8]*b[14]);
y=(a[ 1]*b[12])+(a[ 5]*b[13])+(a[ 9]*b[14]);
z=(a[ 2]*b[12])+(a[ 6]*b[13])+(a[10]*b[14]);
a[12]=-x;
a[13]=-y;
a[14]=-z;
}
//---------------------------------------------------------------------------
const int QUAD_lin[]= // quad lines
{
0,1,
1,2,
2,3,
3,0,
-1
};
const int QUAD_fac[]= // quad quads
{
0,1,2,3,
-1
};
void get_perspective_znear(vec3 *quad) // quad[4] = world coordinates of 4 corners of screen at znear distance from camera
{
vec3 o,u,v; // 3D vectors
float im[16],m[16],znear,zfar,aspect,fovx;
// get stuff from perspective
glGetFloatv(GL_PROJECTION_MATRIX,m); // get perspective projection matrix
zfar =0.5*m[14]*(1.0-((m[10]-1.0)/(m[10]+1.0)));// compute zfar from perspective matrix
znear=zfar*(m[10]+1.0)/(m[10]-1.0); // compute znear from perspective matrix
aspect=m[5]/m[0];
fovx=2.0*atan(1.0/m[5])*aspect;
// get stuff from camera matrix (must be in right place in code before model transformations)
glGetFloatv(GL_MODELVIEW_MATRIX,im); // get camera inverse matrix
matrix_inv(m,im); // m = inverse(im)
u =vec3(m[ 0],m[ 1],m[ 2]); // x axis
v =vec3(m[ 4],m[ 5],m[ 6]); // y axis
o =vec3(m[12],m[13],m[14]); // origin
o-=vec3(m[ 8],m[ 9],m[10])*znear; // z axis offset
// scale by FOV
u*=znear*tan(0.5*fovx);
v*=znear*tan(0.5*fovx/aspect);
// get rectangle coorners
quad[0]=o-u-v;
quad[1]=o+u-v;
quad[2]=o+u+v;
quad[3]=o-u+v;
}
//---------------------------------------------------------------------------
bool collideLineQuad(vec3 *lin,vec3 *quad) // return if lin[2] is colliding quad[4]
{
float t,l,u,v;
vec3 p,p0,p1,dp;
vec3 U,V,W;
// quad (rectangle) basis vectors
U=quad[1]-quad[0]; u=length(U); u*=u;
V=quad[3]-quad[0]; v=length(V); v*=v;
W=normalize(cross(U,V));
// convert line from world coordinates to quad local ones
p0=lin[0]-quad[0]; p0=vec3(dot(p0,U)/u,dot(p0,V)/v,dot(p0,W));
p1=lin[1]-quad[0]; p1=vec3(dot(p1,U)/u,dot(p1,V)/v,dot(p1,W));
dp=p1-p0;
// test if crossing the plane
if (fabs(dp.z)<1e-10) return false;
t=-p0.z/dp.z;
p=p0+(t*dp);
// test inside 2D quad (rectangle)
if ((p.x<0.0)||(p.x>1.0)) return false;
if ((p.y<0.0)||(p.y>1.0)) return false;
// inside line
if ((t<0.0)||(t>1.0)) return false;
return true;
}
//---------------------------------------------------------------------------
bool collideQuadQuad(vec3 *quad0,vec3 *quad1) // return if quad0[4] is colliding quad1[4]
{
int i;
vec3 l[2];
// lines vs. quads
for (i=0;QUAD_lin[i]>=0;)
{
l[0]=quad0[QUAD_lin[i]]; i++;
l[1]=quad0[QUAD_lin[i]]; i++;
if (collideLineQuad(l,quad1)) return true;
}
for (i=0;QUAD_lin[i]>=0;)
{
l[0]=quad1[QUAD_lin[i]]; i++;
l[1]=quad1[QUAD_lin[i]]; i++;
if (collideLineQuad(l,quad0)) return true;
}
// ToDo coplanar quads tests (not needed for AABB test)
return false;
}
//---------------------------------------------------------------------------
bool collideAABBQuad(vec3 *aabb,vec3 *quad) // return if aabb[8] is colliding quad[4]
{
int i;
vec3 q[4],n,p;
// test all AABB faces (rectangle) for intersection with quad (rectangle)
for (i=0;AABB_fac[i]>=0;)
{
q[0]=aabb[AABB_fac[i]]; i++;
q[1]=aabb[AABB_fac[i]]; i++;
q[2]=aabb[AABB_fac[i]]; i++;
q[3]=aabb[AABB_fac[i]]; i++;
if (collideQuadQuad(q,quad)) return true;
}
// test if one point of quad is fully inside AABB
for (i=0;AABB_fac[i]>=0;i+=4)
{
n=cross(aabb[AABB_fac[i+1]]-aabb[AABB_fac[i+0]],
aabb[AABB_fac[i+2]]-aabb[AABB_fac[i+1]]);
if (dot(n,quad[0]-aabb[AABB_fac[i+0]])>0.0) return false;
}
return true;
}
//---------------------------------------------------------------------------
And here the usage (during rendering):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int i;
float m[16];
mat4 m0,m1;
vec4 v4;
float aspect=float(xs)/float(ys);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0/aspect,aspect,0.1,20.0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
static float anim=180.0; anim+=0.1; if (anim>=360.0) anim-=360.0;
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
vec3 line[2],quad[4],aabb[8]; // 3D vectors
get_perspective_znear(quad);
// store view matrix for latter
glMatrixMode(GL_MODELVIEW);
glGetFloatv(GL_MODELVIEW_MATRIX,m);
m0=mat4(m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7],m[8],m[9],m[10],m[11],m[12],m[13],m[14],m[15]);
m0=inverse(m0);
// <<-- here should be for start that loop through your toruses
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// set/animate torus position
glTranslatef(0.3,0.3,3.5*(-1.0-cos(anim)));
glRotatef(+75.0,0.5,0.5,0.0);
// get actual matrix and convert it to the change
glGetFloatv(GL_MODELVIEW_MATRIX,m);
m1=m0*mat4(m[0],m[1],m[2],m[3],m[4],m[5],m[6],m[7],m[8],m[9],m[10],m[11],m[12],m[13],m[14],m[15]);
// render torus and compute its AABB
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glColor3f(1.0,1.0,1.0);
glutSolidTorus(0.1,0.5,36,36);
AABBSolidTorus(aabb,0.1,0.5);
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
// convert AABB to the same coordinates as quad
for (i=0;i<8;i++) aabb[i]=(m1*vec4(aabb[i],1.0)).xyz;
// restore original view matrix
glPopMatrix();
// render wireframe AABB
glColor3f(0.0,1.0,0.0);
glBegin(GL_LINES);
for (i=0;AABB_lin[i]>=0;i++)
glVertex3fv(aabb[AABB_lin[i]].dat);
glEnd();
/*
// render filled AABB for debug
glBegin(GL_QUADS);
for (i=0;AABB_fac[i]>=0;i++)
glVertex3fv(aabb[AABB_fac[i]].dat);
glEnd();
// render quad for debug
glBegin(GL_QUADS);
glColor3f(1.0,1.0,1.0);
for (i=0;QUAD_fac[i]>=0;i++)
glVertex3fv(quad[QUAD_fac[i]].dat);
glEnd();
*/
// render X on colision
if (collideAABBQuad(aabb,quad))
{
glColor3f(1.0,0.0,0.0);
glBegin(GL_LINES);
glVertex3fv(quad[0].dat);
glVertex3fv(quad[2].dat);
glVertex3fv(quad[1].dat);
glVertex3fv(quad[3].dat);
glEnd();
}
// <<-- here should be end of the for that loop through your toruses
glFlush();
SwapBuffers(hdc);
just ignore the GLUT solid torus function as you already got it ... Here preview:
The red cross indicates collision with screen ...
Related
Resize a circle in openGL keeping the aspect ratio
I wrote this code that prints a circle. The problem comes when I try to resize the window. The aspect ratio is not kept and the circle becomes an oval. #include<GL/glut.h> #include<GL/glu.h> #include<GL/gl.h> #include<string.h> #include<stdio.h> #include <math.h> #define PI 3.1415 const float DEG2RAD = 3.14159 / 180; // Keep track of windows changing width and height GLfloat windowWidth; GLfloat windowHeight; void drawCircle(float radius) { glBegin(GL_LINE_LOOP); for (int i = 0; i <= 300; i++) { double angle = 2 * PI * i / 300; double x = radius * cos(angle); double y = radius * sin(angle); glVertex2d(x, y); } glEnd(); } /////////////////////////////////////////////////////////// // Called to draw scene void RenderScene(void) { // Clear the window with current clearing color glClear(GL_COLOR_BUFFER_BIT ); // Save the matrix state and do the rotations glMatrixMode(GL_MODELVIEW); //glPushMatrix(); glColor3d(1, 0, 0); drawCircle(100); glutSwapBuffers(); } /////////////////////////////////////////////////////////// // This function does any needed initialization on the // rendering context. void SetupRC() { // Light values and coordinates //glEnable(GL_DEPTH_TEST); // Hidden surface removal glClearColor(0,0,0,0); } void ChangeSize(int w, int h) { GLfloat aspectRatio; GLfloat nRange = 200.0f; // Prevent a divide by zero if (h == 0) h = 1; // Set Viewport to window dimensions glViewport(0, 0, w, h); // Reset coordinate system glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Establish clipping volume (left, right, bottom, top, near, far) aspectRatio = (GLfloat)w / (GLfloat)h; if (w <= h) { glOrtho(-nRange, nRange, -nRange*aspectRatio, nRange*aspectRatio, -nRange*2, nRange * 2); } else { glOrtho(-nRange /aspectRatio, nRange /aspectRatio, -nRange, nRange, -nRange * 2, nRange * 2); } // Specify the orthographic (or perpendicular) projection, // i.e., define the viewing box. glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } /////////////////////////////////////////////////////////// // Entry point of the program int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glClear(GL_COLOR_BUFFER_BIT); glutInitWindowSize(800, 800); glutCreateWindow("Circle"); glutReshapeFunc(ChangeSize); glutDisplayFunc(RenderScene); SetupRC(); glutMainLoop(); return 0; } That's the code. I think that the problem is in the ChangeSize() function. Can someone help me? I tried dividing and multiplaying the range by the aspect ratio defined as width/height by the problem remains.
The projection matrix describes the mapping from 3D points of a scene, to 2D points of the viewport. The projection matrix transforms from view space to the clip space. The coordinates in the clip space are transformed to the normalized device coordinates (NDC) in the range (-1, -1, -1) to (1, 1, 1) by dividing with the w component of the clip coordinates. At Orthographic Projection the coordinates in the eye space are linearly mapped to normalized device coordinates and the clip sapce coordinates are equal the normalized device coordiantes, because the w component is 1 (for a carthesian coordinate). Orthographic Projection Matrix: r = right, l = left, b = bottom, t = top, n = near, f = far 2/(r-l) 0 0 0 0 2/(t-b) 0 0 0 0 -2/(f-n) 0 -(r+l)/(r-l) -(t+b)/(t-b) -(f+n)/(f-n) 1 Lets assume you have a full HD window: w = 1920.0; h = 1080.0; The window has an aspcet ratio of 1.77778 aspectRatio = w / h = 1.77778 If you set up an orthographic projection matrix like this: glOrtho(-nRange*aspectRatio, nRange*aspectRatio, -nRange, nRange, -nRange*2, nRange*2 ); this will result in the following orthographic projections matrix (1.0 / 1.77778 == 0.5625): 0.5625/nRange 0 0.0 0.0 0.0 1.0/nRange 0.0 0.0 0.0 0.0 0.5/nRange 0.0 0.0 0.0 0.0 1.0 When a geometry is drawn, then each point of the geometry is transformed by the projection matrix. If a circle is drawn in the XY-plane of the viewport, then the X-coordinate is scaled by 0.5625/nRange: X' = X * prjMat[0][0] = X * 0.5625/nRange while the Y-coordinate is scaled by 1.0/nRange Y' = Y * prjMat[1][1] = Y * 1.0/nRange This means, the orthographic projection matrix applies the reciprocal aspect ratio of the viewport to the geometry, when the geometry is transformed from view space to normalized device space. This causes that the perfect circle is distorted to an ellipse, in normalized device space and looks like this: If you stretch this ellipse back to the rectangular viewport, the you can see the perfect circle in the window or on the screen:
Rotate Direction of Camera Inverses Itself After Some Time
I'm trying to create mini application which rotates camera around a cube with movement of mouse. It works perfectly on Rotating around Y axis, but I have a problem with rotating around X axis. When I continuously move my mouse upwards, cube starts to rotate on positive X direction. After some time, it reverses it's rotation direction and starts rotating on negative X direction, then after some time, again rotates on positive X direction. I want to make it rotate around Positive X as far as I move my mouse upwards. What is the problem here? Cube is positioned on the center of coordinate system. Projection is perspective: Edit: Here is the video related to problem: https://youtu.be/997ZdUM8fcQ vec4 eye; vec4 at; vec4 up; GLfloat mouseX = -1; GLfloat mouseY = -1; // Bound to glutPassiveMotionFunc void mouse(int x, int y) { // This rotation works perfect, cube rotates on same direction continuously as far as I move the mouse left or right GLfloat acceleration_x = mouseX - x; eye = RotateY(acceleration_x / 10.f) * eye; mouseX = x; // This rotation doesn't work properly, after some time, cube's rotation direction inverses GLfloat acceleration_y = mouseY - y; eye = RotateX(acceleration_y / 10.f) * eye; mouseY = y; // This part teleports pointer to opposite side of window after reaching window bounds if (x >= 1364) { glutWarpPointer(1, y); mouseX = 1; } else if (x <= 0) { glutWarpPointer(1363, y); mouseX = 1363; } if (y >= 751) { glutWarpPointer(x, 3); mouseY = 3; } else if (y <= 2) { glutWarpPointer(x, 750); mouseY = 750; } } void display( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); vec4 up( 0.0, 1.0, 0.0, 0.0 ); mat4 model_view = LookAt( eye, at, up ); glUniformMatrix4fv( ModelView, 1, GL_TRUE, model_view ); glDrawArrays( GL_TRIANGLES, 0, NumVertices ); glutSwapBuffers(); } // Bound to glutTimerFunc void timer( int id ) { glutPostRedisplay(); glutTimerFunc(1000.f/60.f, timer, 0); } int main( int argc, char **argv ) { ...... ...... eye = vec4(0, 0, 2, 0); at = vec4(0, 0, 0, 0); up = vec4(0.0, 1.0, 0.0, 0.0); ..... ..... }
It is because the camera gets flipped. Use your hand like this: let the index finger be the dir, and your thumb is up (thumb=(0, 1, 0)). Direct your index finder forwards, and your thumb upwards. Now, start rotating your hand around X: your index finger starts to point downwards, while your thumb forwards (during this, thumb.y is positive). When you rotated your hand 90 degrees, index finger points downwards, and thumb points forwards (thumb.y is 0). When you continue rotating, your thumb starts to point forwards+downwards (thumb.y becomes negative). At this moment, the LookAt function will not calculate the matrix you wanted, because you want the up vector to point downwards (as we said, thumb.y is negative), but in the code, up vector is a constant (0, 1, 0), so LookAt will calculate a matrix which has a positive up.y: The camera gets flipped. A solution could be that you register the needed rotation angles, and rotate camera matrix (instead of dir) with these angles
OpenGL Matrix Camera controls, local rotation not functioning properly
So I'm trying to figure out how to mannually create a camera class that creates a local frame for camera transformations. I've created a player object based on OpenGL SuperBible's GLFrame class. I got keyboard keys mapped to the MoveUp, MoveRight and MoveForward functions and the horizontal and vertical mouse movements are mapped to the xRot variable and rotateLocalY function. This is done to create a FPS style camera. The problem however is in the RotateLocalY. Translation works fine and so does the vertical mouse movement but the horizontal movement scales all my objects down or up in a weird way. Besides the scaling, the rotation also seems to restrict itself to 180 degrees and rotates around the world origin (0.0) instead of my player's local position. I figured that the scaling had something to do with normalizing vectors but the GLframe class (which I used for reference) never normalized any vectors and that class works just fine. Normalizing most of my vectors only solved the scaling and all the other problems were still there so I'm figuring one piece of code is causing all these problems? I can't seem to figure out where the problem lies, I'll post all the appropriate code here and a screenshot to show the scaling. Player object Player::Player() { location[0] = 0.0f; location[1] = 0.0f; location[2] = 0.0f; up[0] = 0.0f; up[1] = 1.0f; up[2] = 0.0f; forward[0] = 0.0f; forward[1] = 0.0f; forward[2] = -1.0f; } // Does all the camera transformation. Should be called before scene rendering! void Player::ApplyTransform() { M3DMatrix44f cameraMatrix; this->getTransformationMatrix(cameraMatrix); glRotatef(xAngle, 1.0f, 0.0f, 0.0f); glMultMatrixf(cameraMatrix); } void Player::MoveForward(GLfloat delta) { location[0] += forward[0] * delta; location[1] += forward[1] * delta; location[2] += forward[2] * delta; } void Player::MoveUp(GLfloat delta) { location[0] += up[0] * delta; location[1] += up[1] * delta; location[2] += up[2] * delta; } void Player::MoveRight(GLfloat delta) { // Get X axis vector first via cross product M3DVector3f xAxis; m3dCrossProduct(xAxis, up, forward); location[0] += xAxis[0] * delta; location[1] += xAxis[1] * delta; location[2] += xAxis[2] * delta; } void Player::RotateLocalY(GLfloat angle) { // Calculate a rotation matrix first M3DMatrix44f rotationMatrix; // Rotate around the up vector m3dRotationMatrix44(rotationMatrix, angle, up[0], up[1], up[2]); // Use up vector to get correct rotations even with multiple rotations used. // Get new forward vector out of the rotation matrix M3DVector3f newForward; newForward[0] = rotationMatrix[0] * forward[0] + rotationMatrix[4] * forward[1] + rotationMatrix[8] * forward[2]; newForward[1] = rotationMatrix[1] * forward[1] + rotationMatrix[5] * forward[1] + rotationMatrix[9] * forward[2]; newForward[2] = rotationMatrix[2] * forward[2] + rotationMatrix[6] * forward[1] + rotationMatrix[10] * forward[2]; m3dCopyVector3(forward, newForward); } void Player::getTransformationMatrix(M3DMatrix44f matrix) { // Get Z axis (Z axis is reversed with camera transformations) M3DVector3f zAxis; zAxis[0] = -forward[0]; zAxis[1] = -forward[1]; zAxis[2] = -forward[2]; // Get X axis M3DVector3f xAxis; m3dCrossProduct(xAxis, up, zAxis); // Fill in X column in transformation matrix m3dSetMatrixColumn44(matrix, xAxis, 0); // first column matrix[3] = 0.0f; // Set 4th value to 0 // Fill in the Y column m3dSetMatrixColumn44(matrix, up, 1); // 2nd column matrix[7] = 0.0f; // Fill in the Z column m3dSetMatrixColumn44(matrix, zAxis, 2); // 3rd column matrix[11] = 0.0f; // Do the translation M3DVector3f negativeLocation; // Required for camera transform (right handed OpenGL system. Looking down negative Z axis) negativeLocation[0] = -location[0]; negativeLocation[1] = -location[1]; negativeLocation[2] = -location[2]; m3dSetMatrixColumn44(matrix, negativeLocation, 3); // 4th column matrix[15] = 1.0f; } Player object header class Player { public: ////////////////////////////////////// // Variables M3DVector3f location; M3DVector3f up; M3DVector3f forward; GLfloat xAngle; // Used for FPS divided X angle rotation (can't combine yaw and pitch since we'll also get a Roll which we don't want for FPS) ///////////////////////////////////// // Functions Player(); void ApplyTransform(); void MoveForward(GLfloat delta); void MoveUp(GLfloat delta); void MoveRight(GLfloat delta); void RotateLocalY(GLfloat angle); // Only need rotation on local axis for FPS camera style. Then a translation on world X axis. (done in apply transform) private: void getTransformationMatrix(M3DMatrix44f matrix); }; Applying transformations // Clear screen glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); // Apply camera transforms player.ApplyTransform(); // Set up lights ... // Use shaders ... // Render the scene RenderScene(); // Do post rendering operations glutSwapBuffers(); and mouse float mouseSensitivity = 500.0f; float horizontal = (width / 2) - mouseX; float vertical = (height / 2) - mouseY; horizontal /= mouseSensitivity; vertical /= (mouseSensitivity / 25); player.xAngle += -vertical; player.RotateLocalY(horizontal); glutWarpPointer((width / 2), (height / 2));
Honestly I think you are taking a way to complicated approach to your problem. There are many ways to create a camera. My favorite is using a R3-Vector and a Quaternion, but you could also work with a R3-Vector and two floats (pitch and yaw). The setup with two angles is simple: glLoadIdentity(); glTranslatef(-pos[0], -pos[1], -pos[2]); glRotatef(-yaw, 0.0f, 0.0f, 1.0f); glRotatef(-pitch, 0.0f, 1.0f, 0.0f); The tricky part now is moving the camera. You must do something along the lines of: flaot ds = speed * dt; position += tranform_y(pich, tranform_z(yaw, Vector3(ds, 0, 0))); How to do the transforms, I would have to look that up, but you could to it by using a rotation matrix Rotation is trivial, just add or subtract from the pitch and yaw values. I like using a quaternion for the orientation because it is general and thus you have a camera (any entity that is) that independent of any movement scheme. In this case you have a camera that looks like so: class Camera { public: // lots of stuff omitted void setup(); void move_local(Vector3f value); void rotate(float dy, float dz); private: mx::Vector3f position; mx::Quaternionf orientation; }; Then the setup code uses shamelessly gluLookAt; you could make a transformation matrix out of it, but I never got it to work right. void Camera::setup() { // projection related stuff mx::Vector3f eye = position; mx::Vector3f forward = mx::transform(orientation, mx::Vector3f(1, 0, 0)); mx::Vector3f center = eye + forward; mx::Vector3f up = mx::transform(orientation, mx::Vector3f(0, 0, 1)); gluLookAt(eye(0), eye(1), eye(2), center(0), center(1), center(2), up(0), up(1), up(2)); } Moving the camera in local frame is also simple: void Camera::move_local(Vector3f value) { position += mx::transform(orientation, value); } The rotation is also straight forward. void Camera::rotate(float dy, float dz) { mx::Quaternionf o = orientation; o = mx::axis_angle_to_quaternion(horizontal, mx::Vector3f(0, 0, 1)) * o; o = o * mx::axis_angle_to_quaternion(vertical, mx::Vector3f(0, 1, 0)); orientation = o; } (Shameless plug): If you are asking what math library I use, it is mathex. I wrote it...
Camera rotation in OpenGL not using glRotate glLookAt
I am trying to write a own rotation function for a camera in OpenGL, but I can't get it to run. My camera is mainly from flipcode, with some minor changes: Camera code: Camera::Camera(float x, float y, float z) { memset(Transform, 0, 16*sizeof(float)); Transform[0] = 1.0f; Transform[5] = 1.0f; Transform[10] = 1.0f; Transform[15] = 1.0f; Transform[12] = x; Transform[13] = y; Transform[14] = z; Left=&Transform[0]; Up=&Transform[4]; Forward=&Transform[8]; Position=&Transform[12]; old_x = 0; old_y = 0; } The view is set before every rendered frame: void Camera::setView() { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); float viewmatrix[16]={//Remove the three - for non-inverted z-axis Transform[0], Transform[4], -Transform[8], 0, Transform[1], Transform[5], -Transform[9], 0, Transform[2], Transform[6], -Transform[10], 0, -(Transform[0]*Transform[12] + Transform[1]*Transform[13] + Transform[2]*Transform[14]), -(Transform[4]*Transform[12] + Transform[5]*Transform[13] + Transform[6]*Transform[14]), //add a - like above for non-inverted z-axis (Transform[8]*Transform[12] + Transform[9]*Transform[13] + Transform[10]*Transform[14]), 1}; glLoadMatrixf(viewmatrix); } Now to my problem, the rotation. Consider for example rotation around the y-axis. This is the rotation matrix stack: // deg is the angle it is not working in degree or radiant void Camera::rotateLocal_y(float deg){ glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadMatrixf(Transform); rotateMatrixf_y(Transform, deg); glGetFloatv(GL_MODELVIEW_MATRIX, Transform); glPopMatrix(); } So next I am going to show the rotation function: //rotate a matrix around y axis void rotateMatrixf_y(float *aMatrix, float angle){ // x y z t float rotMatrix[] = {cos(angle),0,-1*sin(angle),0, 0, 1, 0, 0, sin(angle), 0, cos(angle), 0, 0, 0, 0, 1}; multMatrixMatrix(rotMatrix,aMatrix); } And finally the matrix multiplication function: void multMatrixMatrix(float* m_a, float* m_b){ float m_c[16] = {m_a[0]*m_b[0]+m_a[4]*m_b[1]+m_a[8]*m_b[2]+m_a[12]*m_b[3], m_a[0]*m_b[4]+m_a[4]*m_b[5]+m_a[8]*m_b[6]+m_a[12]*m_b[7], m_a[0]*m_b[8]+m_a[4]*m_b[9]+m_a[8]*m_b[10]+m_a[12]*m_b[11], m_a[0]*m_b[12]+m_a[4]*m_b[13]+m_a[8]*m_b[14]+m_a[12]*m_b[15], m_a[1]*m_b[0]+m_a[5]*m_b[1]+m_a[9]*m_b[2]+m_a[13]*m_b[3], m_a[1]*m_b[4]+m_a[5]*m_b[5]+m_a[9]*m_b[6]+m_a[13]*m_b[7], m_a[1]*m_b[8]+m_a[5]*m_b[9]+m_a[9]*m_b[10]+m_a[13]*m_b[11], m_a[1]*m_b[12]+m_a[5]*m_b[13]+m_a[9]*m_b[14]+m_a[13]*m_b[15], m_a[2]*m_b[0]+m_a[6]*m_b[1]+m_a[10]*m_b[2]+m_a[14]*m_b[3], m_a[2]*m_b[4]+m_a[6]*m_b[5]+m_a[10]*m_b[6]+m_a[14]*m_b[7], m_a[2]*m_b[8]+m_a[6]*m_b[9]+m_a[10]*m_b[10]+m_a[14]*m_b[11], m_a[2]*m_b[12]+m_a[6]*m_b[13]+m_a[10]*m_b[14]+m_a[14]*m_b[15], m_a[3]*m_b[0]+m_a[7]*m_b[1]+m_a[11]*m_b[2]+m_a[15]*m_b[3], m_a[3]*m_b[4]+m_a[7]*m_b[5]+m_a[11]*m_b[6]+m_a[15]*m_b[7], m_a[3]*m_b[8]+m_a[7]*m_b[9]+m_a[11]*m_b[10]+m_a[15]*m_b[11], m_a[3]*m_b[12]+m_a[7]*m_b[13]+m_a[11]*m_b[14]+m_a[15]*m_b[15] }; m_b = m_c; } I though this must be it, but it seems as if something is fundamentaly wrong. It is not moving at all. the camera is properly set. The method order is: cam.rotate then cam.setView. Flipcodes originial rotate function: void Camera::rotateLoc(float deg, float x, float y, float z) { glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadMatrixf(Transform); glRotatef(deg, x,y,z); glGetFloatv(GL_MODELVIEW_MATRIX, Transform); glPopMatrix(); }
Your code is pretty messy and incomplete. I think your problem is here : glPushMatrix(); glLoadMatrixf(Transform); // give the Transform matrix to GL (why?) rotateMatrixf_y(Transform, deg); // modify the Transform matrix glGetFloatv(GL_MODELVIEW_MATRIX, Transform); // (3) retrieve the original Tranform matrix glPopMatrix(); (3) just undoes whatever changes you've been doing in 'Transform' by calling 'rotateMatrixf_y'. The flipcode code you added is using OpenGL to update the Tranform matrix, by calling glRotatef' and reading back the result, which is fine. In your method code, you should just remove every reference to OpenGL and just keep the call to rotateMatrixf_y, which does all the work in its own. Do you really understand what's the use of the GL matrix stack ? You should perhaps go back to the basics by either using only GL functions or using your own, but get to know why it works in either way before mixing the uses.
how to draw a spiral using opengl
I want to know how to draw a spiral. I wrote this code: void RenderScene(void) { glClear(GL_COLOR_BUFFER_BIT); GLfloat x,y,z = -50,angle; glBegin(GL_POINTS); for(angle = 0; angle < 360; angle += 1) { x = 50 * cos(angle); y = 50 * sin(angle); glVertex3f(x,y,z); z+=1; } glEnd(); glutSwapBuffers(); } If I don't include the z terms I get a perfect circle but when I include z, then I get 3 dots that's it. What might have happened? I set the viewport using glviewport(0,0,w,h) To include z should i do anything to set viewport in z direction?
You see points because you are drawing points with glBegin(GL_POINTS). Try replacing it by glBegin(GL_LINE_STRIP). NOTE: when you saw the circle you also drew only points, but drawn close enough to appear as a connected circle. Also, you may have not setup the depth buffer to accept values in the range z = [-50, 310] that you use. These arguments should be provided as zNear and zFar clipping planes in your gluPerspective, glOrtho() or glFrustum() call. NOTE: this would explain why with z value you only see a few points: the other points are clipped because they are outside the z-buffer range. UPDATE AFTER YOU HAVE SHOWN YOUR CODE: glOrtho(-100*aspectratio,100*aspectratio,-100,100,1,-1); would only allow z-values in the [-1, 1] range, which is why only the three points with z = -1, z = 0 and z = 1 will be drawn (thus 3 points). Finally, you're probably viewing the spiral from the top, looking directly in the direction of the rotation axis. If you are not using a perspective projection (but an isometric one), the spiral will still show up as a circle. You might want to change your view with gluLookAt(). EXAMPLE OF SETTING UP PERSPECTIVE The following code is taken from the excellent OpenGL tutorials by NeHe: glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); // Select The Projection Matrix glLoadIdentity(); // Reset The Projection Matrix // Calculate The Aspect Ratio Of The Window gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f); glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix glLoadIdentity(); // Reset The Modelview Matrix Then, in your draw loop would look something like this: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer glLoadIdentity(); glTranslatef(-1.5f,0.0f,-6.0f); // Move Left 1.5 Units And Into The Screen 6.0 glBegin(GL_TRIANGLES); // Drawing Using Triangles glVertex3f( 0.0f, 1.0f, 0.0f); // Top glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right glEnd(); Of course, you should alter this example code your needs.
catchmeifyoutry provides a perfectly capable method, but will not draw a spatially accurate 3D spiral, as any render call using a GL_LINE primitive type will rasterize to fixed pixel width. This means that as you change your perspective / view, the lines will not change width. In order to accomplish this, use a geometry shader in combination with GL_LINE_STRIP_ADJACENCY to create 3D geometry that can be rasterized like any other 3D geometry. (This does require that you use the post fixed-function pipeline however) I recommended you to try catchmeifyoutry's method first as it will be much simpler. If you are not satisfied, try the method I described. You can use the following post as guidance: http://prideout.net/blog/?tag=opengl-tron
Here is my Spiral function in C. The points are saved into a list which can be easily drawn by OpenGL (e.g. connect adjacent points in list with GL_LINES). cx,cy ... spiral centre x and y coordinates r ... max spiral radius num_segments ... number of segments the spiral will have SOME_LIST* UniformSpiralPoints(float cx, float cy, float r, int num_segments) { SOME_LIST *sl = newSomeList(); int i; for(i = 0; i < num_segments; i++) { float theta = 2.0f * 3.1415926f * i / num_segments; //the current angle float x = (r/num_segments)*i * cosf(theta); //the x component float y = (r/num_segments)*i * sinf(theta); //the y component //add (x + cx, y + cy) to list sl } return sl; } An example image with r = 1, num_segments = 1024: P.S. There is difference in using cos(double) and cosf(float). You use a float variable for a double function cos.