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 ...
I have a problem regarding the gluOrtho2D function. I want to resize the window size and calibrate its coordinates, but i have problems with the function. I think that the coordinates calculated
The problem is that if i click somewhere on the window, the point isn't on the cursor, but somewhere near it ( ~20 pixels). If i resize the window, i want the window points to be converted to the new window size, that's why the gluOrtho2D function had those parameters. Maybe i didn't figure it out the good way.
GLdouble mouseX = ( double )(2* widthValue * x / windowWidthSize) - widthSize ;
GLdouble mouseY = -( double )(2 * heightValue * y / windowHeightSize) + heightValue;
are good as i want them to take values from -30 to +30 (both x and y), but the
gluOrtho2D( -width / widthValue , width / widthValue , -height / heightValue , height / heightValue);
function doesn't convert them as i want. Any ideas to fix it?
#include<windows.h>
#include<GL/Glut.h> //includes the opengl, glu, and glut header files
#include<stdlib.h> //includes the standard library header file
#include<iostream>
#include<fstream>
#include<vector>
using namespace std;
vector<pair<GLdouble,GLdouble>>v;
GLdouble heightValue = 30.0; // the points X and Y coordinates would be [-30,30]
GLdouble widthValue = 30.0; // the points X and Y coordinates would be [-30,30]
void initGL()
{
glClearColor(1,1,1,0);
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(5.0);
glBegin(GL_POINTS);
glColor3f(1.0f, 1.0f, 0.0f);
// prints the points
for(auto i : v)
glVertex2d(i.first, i.second);
glEnd();
glFlush();
}
void reshape(GLsizei width, GLsizei height) {
// Set the viewport to cover the new window
glViewport(0, 0, width, height);
// Set the aspect ratio of the clipping area to match the viewport
glMatrixMode(GL_PROJECTION); // To operate on the Projection matrix
glLoadIdentity(); // Reset the projection matrix
// here is the problem with the coordinates. Any help??
gluOrtho2D(-width/ widthValue, width/ widthValue,-height / heightValue ,height / heightValue);
}
void mouseClick(int button, int state, int x, int y)
{
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) // left button is pressed
{
double windowHeightSize = glutGet(GLUT_WINDOW_HEIGHT); // gets window's size
double windowWidthSize = glutGet(GLUT_WINDOW_WIDTH); // gets window's size
// converts the click coordinate on the screen to the coordinate on the window
GLdouble mouseX = (double)(60.0 * x / windowWidthSize) - 30.0;
GLdouble mouseY = -(double)(60.0 * y / windowHeightSize) + 30.0;
v.push_back(make_pair(mouseX,mouseY) ); // insert the point's coordinates in the vector
display();
}
}
int main(int argc,char** argv)
{
glutInit(&argc,argv);
glutInitWindowSize(800, 600);
glutInitWindowPosition(50, 50);//sets the position of the window in pixels from top left corner
glutCreateWindow("Program Find Convex Hull");
glutDisplayFunc(display);
glutReshapeFunc(reshape); // functia este apelata cand redimensionam fereastra
glutMouseFunc(mouseClick);
// initGL();
glutMainLoop();//loops the current event
return 0;
}
If the Orthographic projection is set by:
gluOrtho2D(-ortho_x, ortho_x, -ortho_y, ortho_y);
then the transformation of the moue position (window coordinates) to view coordinates is:
GLdouble mouseX = (double)(x/windowWidthSize * 2*ortho_x) - ortho_x;
GLdouble mouseY = ortho_y - (double)(y/windowHeightSize * 2*ortho_y);
Apply that to your example:
GLdouble ortho_x = 30.0;
GLdouble ortho_y = -30.0;
void reshape(GLsizei width, GLsizei height) {
// Set the viewport to cover the new window
glViewport(0, 0, width, height);
// Set the aspect ratio of the clipping area to match the viewport
glMatrixMode(GL_PROJECTION); // To operate on the Projection matrix
glLoadIdentity(); // Reset the projection matrix
ortho_x = width / widthValue;
ortho_y = height / heightValue;
gluOrtho2D(-ortho_x, ortho_x, -ortho_y, ortho_y);
}
void mouseClick(int button, int state, int x, int y)
{
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) // left button is pressed
{
double windowHeightSize = glutGet(GLUT_WINDOW_HEIGHT); // gets window's size
double windowWidthSize = glutGet(GLUT_WINDOW_WIDTH); // gets window's size
// converts the click coordinate on the screen to the coordinate on the window
GLdouble mouseX = (double)(x/windowWidthSize * 2*ortho_x) - ortho_x;
GLdouble mouseY = ortho_y - (double)(y/windowHeightSize * 2*ortho_y);
v.push_back(make_pair(mouseX,mouseY) ); // insert the point's coordinates in the vector
glutPostRedisplay();
}
}
Note, it is not necessary to change (ortho_x, ortho_y) in reshape. Since you mentioned in your question "i want them to take values from -30 to +30", it is even possible to keep (30.0, 30.0).
I created Lines and when I'm rotate the line. Line will be stretch. How can I stop stretch at rotation time. When I change height in Ortho it will be not displaying properly. When Line is going left or right it will be start strtching but when it will be reach in main point it will come in real position.
#include<fstream>
#include<iostream>
#include<stdlib.h>
#include<glut.h>
using namespace std;
float yr = 0;
void introscreen();
void screen();
void screen1();
void PitchLadder();
int width = 1268;
int height = 720;
float translate = 0.0f;
GLfloat angle = 0.0f;
void display(void) {
glClearColor(0, 0, 0, 0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-300, 300, -10, 25, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
static int center_x = 0;
static int center_y = 0;
}
void specialKey(int key, int x, int y) {
switch (key) {
case GLUT_KEY_UP:
translate += 1.0f;
break;
case GLUT_KEY_DOWN:
translate -= 1.0f;
break;
case GLUT_KEY_LEFT:
angle += 1.0f;
break;
case GLUT_KEY_RIGHT:
angle -= 1.0f;
break;
}
glutPostRedisplay();
}
void Rolling(void) {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0, 1, 0);
glPushMatrix();
glRotatef(-angle, 0, 0, 1);
glTranslatef(-10, translate,0);
PitchLadder();
glPopMatrix();
glFlush();
}
void PitchLadder() {
GLfloat y;
GLfloat y2;
GLfloat fSize[5];
GLfloat fCurrSize;
fCurrSize = fSize[2];
for (y2 = -90.0f ; y2 <= 90.0f ; y2 += 10.0f) {
glLineWidth(fCurrSize);
glBegin(GL_LINES);
glVertex3f(-50.0f , y2 , 0);
glVertex3f(50.0f , y2 , 0);
glEnd();
fCurrSize += 1.0f;
screen();
screen1();
}
}
void renderbitmap1(float x3, float y3, void *font1, char *string1) {
char *c1;
glRasterPos2f(x3, y3);
for (c1=string1; *c1 != '\0'; c1++) {
glutBitmapCharacter(font1, *c1);
}
}
void screen(void) {
glColor3f(0, 1, 0);
char buf1[20] = { '\0' };
for (int row1 = -90.0f; row1 <= 90 + yr; row1 +=10.0f) {
sprintf_s(buf1,"%i", row1);
renderbitmap1(70 , (yr+row1), GLUT_BITMAP_TIMES_ROMAN_24, buf1);
}
}
void renderbitmap2(float x4, float y4, void *font2, char *string2) {
char *c1;
glRasterPos2f(x4, y4);
for (c1=string2; *c1 != '\0'; c1++) {
glutBitmapCharacter(font2, *c1);
}
}
void screen1(void) {
glColor3f(0, 1, 0);
char buf1[20] = { '\0' };
for (int row1 = -90.0f; row1 <= 90 + yr; row1 +=10.0f) {
sprintf_s(buf1,"%i", row1);
renderbitmap2(-70 , (yr+row1), GLUT_BITMAP_TIMES_ROMAN_24, buf1);
}
}
int main(int arg, char** argv) {
glutInit(&arg, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(width, height);
glutInitWindowPosition(50, 100);
glutCreateWindow("HUD Lines");
display();
glutDisplayFunc(Rolling);
glutSpecialFunc(specialKey);
glutMainLoop();
return 0;
}
At Orthographic Projection, the view space coordinates are linearly mapped to the clip space coordinates respectively normalized device coordinates. The normlaized device space is a cube with a minimum of (-1, -1, -1) and a maximum of (1, 1, 1).
Finally the coordinates in normalized device space are mapped to the rectangular viewport.
If the viewport is rectangular then the aspect ratio has to be considered, when the view space coordinates are transformed to clip space.
The mapping of the normalized device coordinates to the viewport distorted the geometry by the reciprocal aspect ration of the viewport. This distortion has to be compensated by the orthographic projection.
When the orthographic projection is set by glOrtho(left, right, bottom, top, near, far), then the cuboid volume is defined, which maps (left, bottom, near) to (-1, -1, -1) and (right, top, far) to (1, 1, 1).
It is not necessary that the x and y range of the orthographic projection is equal the view port rectangle, bit the ration (left-right)/(top-bottom)hast to be equal the ration of the viewport rectangle else the geometry will be distored.
double size = 200.0f;
double aspect = (double)width / (double)height;
glOrtho(-aspect*size/2.0, aspect*size/2.0, -size/2.0, size/2.0, -1.0, 1.0);
Your window size and orthographic "view" do not have the same aspect ratio:
// This creates a window that's 1268 x 720 (a wide rectangle)
int width = 1268;
int height = 720;
glutInitWindowSize(width, height);
// This creates a "view" that's 300 x 300 (a square)
glOrtho(-300, 300, -10, 25, 0, 1);
The "view" will be stretched to fill the viewport (window). You are seeing a 300 x 300 image being stretched to 1268x720, which definitely makes horizontal lines appear longer than vertical lines even though they're the same length in the code.
You should call glOrtho using the width and height variables of your window:
glOrtho(0, width, 0, height, 0, 1);
Notice that I have changed the arguments to (left = 0, right = width, bottom = 0, top = height, ...). This allows you to work with a screen coordinate space that is similar to 2D rendering but the bottom-left corner is (0,0) and the top-right is (width,height).
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