Related
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 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 drawing a 10x10 grid of squares at a depth of 0 and trying to highlight the one the mouse is over. I've tried following the tutorial here: http://antongerdelan.net/opengl/raycasting.html
but I don't know if I did it right. I end up with a vector at the end, but I'm not sure what to do with it.
Here's a screenshot of the squares (not sure how it helps..)
http://postimg.org/image/dau330qwt/2
/* Enable attribute index 1 as being used */
glEnableVertexAttribArray(1);
float camera_z = 50;
float camera_x = 0;
float camera_y = 0;
GLuint MatrixID = glGetUniformLocation(program, "MVP");
GLuint ColorID = glGetUniformLocation(program, "input_color");
int mouse_x;
int mouse_y;
while (1) {
int window_width;
int window_height;
SDL_GetWindowSize(win, &window_width, &window_height);
glm::mat4 Projection = glm::perspective(45.0f, ((float)window_width) / window_height, 0.1f, 100.0f);
// printf("Camera at %f %f\n", camera_x, camera_y);
glm::mat4 View = glm::lookAt(glm::vec3(camera_x,camera_y,camera_z), // camera position
glm::vec3(camera_x,camera_y,0), // looking at
glm::vec3(0,1,0)); // up
int map_width = map.width();
int map_height = map.height();
/* Make our background black */
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// go through my 10x10 map and
for (int i = 0; i < map_width; i++) {
for ( int j = 0; j < map_height; j++) {
glm::mat4 Model = glm::translate(glm::mat4(1.0f), glm::vec3(i, j, 0.0f));
glm::mat4 MVP = Projection * View * Model;
glm::vec3 color = random_color();
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
glUniform3fv(ColorID, 1, &color[0]);
glDrawArrays(GL_LINE_LOOP, 0, 4);
}
}
/* Swap our buffers to make our changes visible */
SDL_GL_SwapWindow(win);
// printf("Window dimensions %d x %d\n", window_width, window_height);
float normalized_mouse_x = (2.0f * mouse_x) / window_width - 1.0f;
float normalized_mouse_y = 1.0f - (2.0f * mouse_y) / window_height;
printf("Normalized mouse position %f x %f\n", normalized_mouse_x, normalized_mouse_y);
glm::vec3 normalized_mouse_vector = glm::vec3(normalized_mouse_x, normalized_mouse_y, 1.0f);
glm::vec4 ray_clip = glm::vec4 (normalized_mouse_vector.x, normalized_mouse_vector.y, -1.0, 1.0);
glm::vec4 ray_eye = glm::inverse(Projection) * ray_clip;
ray_eye = glm::vec4(ray_eye.xy(), -1.0, 0.0);
glm::vec3 ray_world = (glm::inverse(View) * ray_eye).xyz();
ray_world = glm::normalize(ray_world);
// this prints out values like: World ray: 0.000266, 0.000382, 1.000000
printf("World ray: %f, %f, %f\n", ray_world.x, ray_world.y, ray_world.z);
// l = -(camera_z / ray_world.z)
float l = -(camera_z / ray_world.z);
float mouse_world_x = camera_x + l * ray_world.x;
float mouse_world_y = camera_y + l * ray_world.y;
printf("mouse world %f, %f\n", mouse_world_x, mouse_world_y);
}
Updated with code from BDL's comment. The output I get now is:
Normalized mouse position 0.087500 x 0.145833
World ray: 0.065083, 0.081353, 499.000000
World ray: 0.000130, 0.000163, 1.000000
mouse world -0.006521, -0.008152
I'm expecting the "mouse world" line to have numbers in the 1-10 range, not in the .00x range, though, based on the screenshot above showing a grid of squares with x and y ranging from 0-10.
Thanks for looking.
The intersection between a given ray r, starting at point C (in this case the camera position) with a x/y plane with z=0 can be calculated as follows:
C ... Camera position [cx,cy,cz]
r ... ray direction [rx,ry,rz]
We are searching for the point on the ray that has z=0
C + l*r = [x,y,0]
=>
cz + l*rz = 0
l * rz = -cz
l = -(cz / rz)
The xy-coordinates of the intersection are now:
x = cx + l * rx
y = cy + l * ry
What is left to do is to check in which rectangle this (x,y) coordinates are located.
So I have this piece of code, which pretty much draws various 2D textures on the screen, though there are multiple sprites that have to be 'dissected' from the texture (spritesheet). The problem is that rotation is not working properly; while it rotates, it does not rotate on the center of the texture, which is what I am trying to do. I have narrowed it down to the translation being incorrect:
glTranslatef(x + sr->x/2 - sr->w/2,
y + sr->y/2 - sr->h/2,0);
glRotatef(ang,0,0,1.f);
glTranslatef(-x + -sr->x/2 - -sr->w/2,
-y + -sr->y/2 - -sr->h/2,0);
X and Y is the position that it's being drawn to, the sheet rect struct contains the position X and Y of the sprite being drawn from the texture, along with w and h, which are the width and heights of the 'sprite' from the texture. I've tried various other formulas, such as:
glTranslatef(x, y, 0);
The below three switching the negative sign to positive (x - y to x + y)
glTranslatef(sr->x/2 - sr->w/2, sr->y/2 - sr->h/2 0 );
glTranslatef(sr->x - sr->w/2, sr->y - sr->h/2, 0 );
glTranslatef(sr->x - sr->w, sr->y - sr->w, 0 );
glTranslatef(.5,.5,0);
It might also be helpful to say that:
glOrtho(0,screen_width,screen_height,0,-2,10);
is in use.
I've tried reading various tutorials, going through various forums, asking various people, but there doesn't seem to be a solution that works, nor can I find any useful resources that explain to me how I find the center of the image in order to translate it to '(0,0)'. I'm pretty new to OpenGL so a lot of this stuff takes awhile for me to digest.
Here's the entire function:
void Apply_Surface( float x, float y, Sheet_Container* source, Sheet_Rect* sr , float ang = 0, bool flipx = 0, bool flipy = 0, int e_x = -1, int e_y = -1 ) {
float imgwi,imghi;
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,source->rt());
// rotation
imghi = source->rh();
imgwi = source->rw();
Sheet_Rect t_shtrct(0,0,imgwi,imghi);
if ( sr == NULL ) // in case a sheet rect is not provided, assume it's width
//and height of texture with 0/0 x/y
sr = &t_shtrct;
glPushMatrix();
//
int wid, hei;
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_WIDTH,&wid);
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_HEIGHT,&hei);
glTranslatef(-sr->x + -sr->w,
-sr->y + -sr->h,0);
glRotatef(ang,0,0,1.f);
glTranslatef(sr->x + sr->w,
sr->y + sr->h,0);
// Yeah, out-dated way of drawing to the screen but it works for now.
GLfloat tex[] = {
(sr->x+sr->w * flipx) /imgwi, 1 - (sr->y+sr->h *!flipy )/imghi,
(sr->x+sr->w * flipx) /imgwi, 1 - (sr->y+sr->h * flipy)/imghi,
(sr->x+sr->w * !flipx) /imgwi, 1 - (sr->y+sr->h * flipy)/imghi,
(sr->x+sr->w * !flipx) /imgwi, 1 - (sr->y+sr->h *!flipy)/imghi
};
GLfloat vertices[] = { // vertices to put on screen
x, (y + sr->h),
x, y,
(x +sr->w), y,
(x +sr->w),(y +sr->h)
};
// index array
GLubyte index[6] = { 0,1,2, 2,3,0 };
float fx = (x/(float)screen_width)-(float)sr->w/2/(float)imgwi;
float fy = (y/(float)screen_height)-(float)sr->h/2/(float)imghi;
// activate arrays
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
// pass verteices and texture information
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, tex);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, index);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
Sheet container class:
class Sheet_Container {
GLuint texture;
int width, height;
public:
Sheet_Container();
Sheet_Container(GLuint, int = -1,int = -1);
void Load(GLuint,int = -1,int = -1);
float rw();
float rh();
GLuint rt();
};
Sheet rect class:
struct Sheet_Rect {
float x, y, w, h;
Sheet_Rect();
Sheet_Rect(int xx,int yy,int ww,int hh);
};
Image loading function:
Sheet_Container Game_Info::Load_Image(const char* fil) {
ILuint t_id;
ilGenImages(1, &t_id);
ilBindImage(t_id);
ilLoadImage(const_cast<char*>(fil));
int width = ilGetInteger(IL_IMAGE_WIDTH), height = ilGetInteger(IL_IMAGE_HEIGHT);
return Sheet_Container(ilutGLLoadImage(const_cast<char*>(fil)),width,height);
}
Your quad (two triangles) is centered at:
( x + sr->w / 2, y + sr->h / 2 )
You need to move that point to the origin, rotate, and then move it back:
glTranslatef ( (x + sr->w / 2.0f), (y + sr->h / 2.0f), 0.0f); // 3rd
glRotatef (0,0,0,1.f); // 2nd
glTranslatef (-(x + sr->w / 2.0f), -(y + sr->h / 2.0f), 0.0f); // 1st
Here is where I think you are getting tripped up. People naturally assume that OpenGL applies transformations in the order they appear (top-to-bottom), that is not the case. OpenGL effectively swaps the operands everytime it multiplies two matrices:
M1 x M2 x M3
~~~~~~~
(1)
~~~~~~~~~~
(2)
(1) M2 * M1
(2) M3 * (M2 * M1) --> M3 * M2 * M1 (row-major / textbook math notation)
The technical term for this is post-multiplication, it all has to do with the way matrices are implemented in OpenGL (column-major). Suffice it to say, you should generally read glTranslatef, glRotatef, glScalef, etc. calls from bottom-to-top.
With that out of the way, your current rotation does not make any sense.
You are telling GL to rotate 0 degrees around an axis: <0,0,1> (the z-axis in other words). The axis is correct, but a 0 degree rotation is not going to do anything ;)
I'm currently rendering triangle strips within a display list and it shows weird little holes.
Beside that some triangles seem to disappear completely when rotating the environment.
Here is the Code:
public class Landscape {
int displayList;
public Landscape() throws IOException {
try {
Display.setDisplayMode(new DisplayMode(640, 480));
Display.create();
} catch (LWJGLException e) {
e.printStackTrace();
}
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLU.gluPerspective(60f, 640f/480f, 0.0001f, 1000f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glPolygonMode(GL_FRONT_AND_BACK, GL_FLAT);
glEnable(GL_CULL_FACE);
BufferedImage heightMap = ImageIO.read(new File("heightmap.png"));
BufferedImage heightMapColor = ImageIO.read(new File("hoehenprofil.png"));
displayList = glGenLists(1);
glNewList(displayList, GL_COMPILE);
for (int z = 0; z < heightMap.getHeight(); z++) {
glBegin(GL_TRIANGLE_STRIP);
for (int x = 0; x < heightMap.getWidth(); x++) {
int y = 0xFF - heightMap.getRGB(x, z) & 0xFF;
int color = heightMapColor.getRGB(y-1, 0);
glColor3ub((byte)((color >> 16) & 0xFF), (byte)((color >> 8) & 0xFF), (byte)(color & 0xFF));
glVertex3i(x, y*5, z);
if (z < heightMap.getHeight() - 1) {
y = 0xFF - heightMap.getRGB(x, z+1) & 0xFF;
color = heightMapColor.getRGB(y-1, 0);
glColor3ub((byte)((color >> 16) & 0xFF), (byte)((color >> 8) & 0xFF), (byte)(color & 0xFF));
glVertex3i(x, y*5, z+1);
}
}
glEnd();
}
glEndList();
float camX = 500, camY = 500, camZ = -500;
float rotX = 45, rotY = 0, rotZ = 0;
long lastTick = System.currentTimeMillis();
int fps = 0;
while (!Display.isCloseRequested()) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
if (Keyboard.isKeyDown(Keyboard.KEY_W)) camZ++;
if (Keyboard.isKeyDown(Keyboard.KEY_S)) camZ--;
if (Keyboard.isKeyDown(Keyboard.KEY_A)) camX--;
if (Keyboard.isKeyDown(Keyboard.KEY_D)) camX++;
if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)) camY++;
if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) camY--;
if (Keyboard.isKeyDown(Keyboard.KEY_Q)) rotY -= 0.5f;
if (Keyboard.isKeyDown(Keyboard.KEY_E)) rotY += 0.5f;
glRotatef(rotX, 1, 0, 0);
glRotatef(rotY, 0, 1, 0);
glRotatef(rotZ, 0, 0, 1);
glTranslatef(-camX, -camY, camZ);
glCallList(displayList);
Display.update();
fps++;
if (lastTick + 1000 <= System.currentTimeMillis()) {
lastTick += 1000;
System.out.println("FPS: " + fps);
fps = 0;
}
}
}
public static void main(String[] args) throws IOException {
new Landscape();
}
}
It seem to also appear with depth test disabled. And even with cull face enabled this doesn't appear to work.
EDIT:
Other than that I get some black flickering just when translating a certain amount around the x or y axis. Here is a screenshot of that.
It causes a huge amount of lag and I can't find any reason for that.
EDIT2:
I scaled the x and z axis and removed the y axis scale for a screenshot, just as requested. It only seem to cause the bug when stretching far around the y axis.
Your difference between near and far plane (last two arguments to gluPerspective) is extremely large. The larger the relative size (far divided by near) between these two, the less depth buffer precision you get. In your case, the far/near is 1,000,000, which is much more than you normally want. I normally like to keep them at a ratio of around 10, even though something like 100 would probably still be fine.
Tweak the values while making sure that none of the geometry you want to see gets clipped away. Try something like 1.0 and 10.0. If that clips away geometry at the far end, increase the far value to something like 100.0, and maybe see if you can increase the near value as well.