PDA

View Full Version : two objects overlap each other



sergey
11-05-2002, 12:57 AM
Dear fellow programmer,
i started OpenGL a couple of month ago. And I got really stuck on detecting if one object overlaps another. I've looked through some books and couldn't find anything of help. Could someone please help me with this one.
Thankful beforehand.

I include the code of my programme. The idea is the Ihave to detect when the blue square overlaps with the white box -> if it does then the programme should exit.
(i'm using VC++ 6 on Windows and Mesa 3.0 on Unix)
Here goes the programme:

/*
*
*/


#include <GL/glut.h>

#define CLOSED_X_CENTRE 20.0 /* centre point of CLOSED BOX */
#define CLOSED_Y_CENTRE 30.0
#define OP_LENGTH 10.0 /* lengths of sides of CLOSED BOX */

#define OPEN_X_CENTRE 80.0 /* centre point of OPEN BOX */
#define OPEN_Y_CENTRE 30.0
#define CL_LENGTH 5.0 /* lengths of sides of OPEN BOX */

GLfloat angle = 45.0; /* angle of rotation (in degrees) */
GLfloat move_ver = 0.0; /* units to be translated vertically*/
GLfloat move_hor = 0.0; /* units to be translated horizontally*/
GLfloat scale_x = 1.0;
GLfloat scale_y = 1.0;


/* reshape callback function
executed when window is moved or resized */
void reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
/* use orthographic (parallel) projection
use xmin = -1, xmax = 1
ymin = -1, ymax = 1
znear = -1, zfar = 1 - not relevant here (2D) */
glOrtho(0.0, 100.0, 0.0, 100.0, -1.0, 1.0);
glMatrixMode( GL_MODELVIEW );
}


/* display callback function
called whenever contents of window need to be re-displayed */
void display(void)
{
glClear (GL_COLOR_BUFFER_BIT); /* clear window */
glColor3f(0.0, 0.0, 1.0); /* blue objects */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();


glTranslatef(CLOSED_X_CENTRE + move_hor, CLOSED_Y_CENTRE + move_ver, 0.0);
glScalef( scale_x, scale_y, 1.0);
glTranslatef(-CLOSED_X_CENTRE - move_hor, -CLOSED_Y_CENTRE - move_ver, 0.0);


glTranslatef(CLOSED_X_CENTRE + move_hor, CLOSED_Y_CENTRE + move_ver, 0.0);
glRotatef( angle, 0.0, 0.0, 1.0 ); /* modelling transformation */
glTranslatef(-CLOSED_X_CENTRE, -CLOSED_Y_CENTRE, 0.0);

glBegin(GL_POLYGON);
glVertex2f( CLOSED_X_CENTRE - OP_LENGTH, CLOSED_Y_CENTRE - OP_LENGTH );
glVertex2f( CLOSED_X_CENTRE - OP_LENGTH, CLOSED_Y_CENTRE + OP_LENGTH );
glVertex2f( CLOSED_X_CENTRE + OP_LENGTH, CLOSED_Y_CENTRE + OP_LENGTH );
glVertex2f( CLOSED_X_CENTRE + OP_LENGTH, CLOSED_Y_CENTRE - OP_LENGTH );
glEnd();


glLoadIdentity();
glColor3f(1.0, 1.0, 1.0); /* white object*/
glBegin(GL_LINE_STRIP);
glVertex2f( OPEN_X_CENTRE - CL_LENGTH, OPEN_Y_CENTRE + CL_LENGTH );
glVertex2f( OPEN_X_CENTRE - CL_LENGTH, OPEN_Y_CENTRE - CL_LENGTH );
glVertex2f( OPEN_X_CENTRE + CL_LENGTH, OPEN_Y_CENTRE - CL_LENGTH );
glVertex2f( OPEN_X_CENTRE + CL_LENGTH, OPEN_Y_CENTRE + CL_LENGTH );
glEnd();


glFlush(); /* execute drawing commands in buffer */
}


/* graphics initialisation */
void init(void)
{
glClearColor (0.0, 0.0, 0.0, 0.0); /* window will be cleared to black */
}


/* mouse callback function
called when mouse button clicked */
void mouse(int button, int state, int x, int y)
{
switch ( button )
{
case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN )
{
angle = 45.0;
move_ver = 0.0;
move_hor = 0.0;
scale_x = 1.0;
scale_y = 1.0;
glutPostRedisplay();
}
break;
case GLUT_RIGHT_BUTTON: if (state == GLUT_DOWN )
{
exit(0);
}
break;
default: break;
}
}

void keyboard (unsigned char key, int x, int y)
{
switch ( key )
{
case 't':
case 'T':
angle += 5.0;
glutPostRedisplay();
break;
case 'u':
case 'U':
case '8':
case GLUT_KEY_UP:
move_ver += 2.0;
glutPostRedisplay();
break;
case 'd':
case 'D':
case '2':
move_ver -= 2.0;
glutPostRedisplay();
break;
case 'l':
case 'L':
case '4':
move_hor -= 2.0;
glutPostRedisplay();
break;
case 'r':
case 'R':
case '6':
move_hor += 2.0;
glutPostRedisplay();
break;
case 'b':
case 'B':
case '+':
scale_x += 0.1;
scale_y += 0.1;
glutPostRedisplay();
break;
case 's':
case 'S':
case '-':
scale_x -= 0.1;
scale_y -= 0.1;
glutPostRedisplay();
break;

}
}

int main(int argc, char** argv)
{
/* window management code ... */
/* initialises GLUT and processes any command line arguments */
glutInit(&argc, argv);
/* use single-buffered window and RGBA colour model */
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
/* window width = 400 pixels, height = 400 pixels */
glutInitWindowSize (400, 400);
/* window upper left corner at (100, 100) */
glutInitWindowPosition (100, 100);
/* creates an OpenGL window with command argument in its title bar */
glutCreateWindow (argv[0]);

init();

glutDisplayFunc(display);
glutReshapeFunc(reshape);

/* register mouse callback function */
glutKeyboardFunc(keyboard);
glutMouseFunc(mouse);
glutMainLoop();
return 0;
}


Thanks again!

nexusone
11-05-2002, 05:43 AM
You mean when two object hit each other during movement or just if they are touching when you draw them?

There is no real function for that, you use math to find the distance between the objects and then that will tell if they are overlaping or not.

x1 - x2 = 0 // Objects touching or same space
and
y1 - y2 = 0

But if you are just drawing an object, then you should know if they are. because you have made up the point in which they lay.


[This message has been edited by nexusone (edited 11-05-2002).]

mm_freak
11-05-2002, 11:18 AM
I can't interpret your question, but I guess, the problem is one of the following two:

1. Collision detection

This is the art of detecting a possible collision between two primitives. The primitives can be points, lines, polygons, circles and more. For points and circles it is easy. But other primitives can be more complicated to check for collision. And once you're in 3-space things get really difficult.

Examples:

For points you would compare the coordinates of the points. For circles you would check, if the sum of the radiuses of both circles are greater than the distance between the centers. For lines you would use straight equations and polygons are just multiple lines.

Collision detection is a huge topic. First you need to know, what type of collision detection you need. In your program, you would test, if one vertex of the first box is in the second box, or one of the second in the first. If one of those conditions are is true, a collision is detected and the program should exit. This is the simplest form of collision detection. More advanced techniques include detecting the intersection point of two lines, if any. For 3D collision detection, you'll want to check, if two polygons intersect or if a line crosses a plane in 3-space, and if it does, find the intersection point and check, if it's in a polygon. Collision detection is such a complex topic, that there are many books about it.

For your problem, let's say:

typedef struct {
float x1,y1;
float x2,y2;
} box;

bool BoxCollide(box &b1,box &b2) {
if (
b1.x1 >= b2.x1 &&
b1.x1 <= b2.x2 &&
b1.y1 >= b2.y1 &&
b1.y1 <= b2.y2
) return true;
if (
b1.x2 >= b2.x1 &&
b1.x2 <= b2.x2 &&
b1.y2 >= b2.y1 &&
b1.y2 <= b2.y2
) return true;
if (
b1.x1 >= b2.x1 &&
b1.x1 <= b2.x2 &&
b1.y2 >= b2.y1 &&
b1.y2 <= b2.y2
) return true;
if (
b1.x2 >= b2.x1 &&
b1.x2 <= b2.x2 &&
b1.y1 >= b2.y1 &&
b1.y1 <= b2.y2
) return true;
return false;
}

bool AreColliding(box &b1,box &b2) {
return BoxCollide(b1,b2) | BoxCollide(b2,b1);
}

Call AreColliding() and pass two box{} structures containing the lower left and upper right vertices of the box. The function will return true, if they collide.

2. Depth testing

If your objects get fully drawn, even if they're obscured, here's the solution. In your code, you're not using the depth buffer (why would you). The depth buffer is a comparison buffer to detect obscured regions. It saves the z value of each pixel on the screen. It is set up and used as follows:

In the glutInitDisplayMode() call add the GLUT_DEPTH flag. That will create a window with a depth buffer.

For depth testing you must enable it. Use GL_DEPTH_TEST with glEnable().

Finally for every frame you must clear the depth buffer. Add the GL_DEPTH_BUFFER_BIT flag to the glClear() call. You don't have to clear it of course. But the old values will then be used in the new frame, where nothing was drawn yet.

Example:

void gl_disp() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* ... (prepare for drawing here (set the camera etc.)) */
glEnable(GL_DEPTH_TEST);
/* ... (draw here) */
glDisable(GL_DEPTH_TEST);
/* ... (do finishing operations here, if any (drawing a HUD, for example)) */
glutSwapBuffers();
}

int main(int argc,char *argv[]) {
/* ... */
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
/* ... */
glutDisplayFunc(gl_disp);
glutMainLoop();
return 0;
}

If you wonder, why you should enable the depth testing only while drawing, the reason is performance. Enable specific OpenGL features only while needed.

-----

Oh yeah, and I've noticed your keyboard() function. Why do you call glutPostRedisplay() before every break? Call it once, after the switch(), that will do the job.

/* Example: */
void keyboard(unsigned char key,int,int) {
switch(key) {
case 'q':
exit(0); /* No need to jump to the end of the switch, since exit() will never return. If it does, get another stdlib. =) */
case 'w':
/* do something for the w key. */
break; /* jump to the end of the switch. */
case 'e':
/* do something for the e key. */
break; /* jump to the end of the switch. */
default:
/* do something for all other keys.
No need to jump to the end of the switch, since we're already there.
}
/* The end of the switch */
beep(); /* Signal the key was processed. Don't ask, why this is not in the 'default' case of the switch. Test that switch and you'll see. =) */
}

Now you may ask, why I've posted a switch example. The reason is simple. Many (especially young) coders want to make great games and stuff, before understanding the language well. Bad idea, but why would you care about the language, when you can create a game with a few simple commands? You can do many things much simpler, if you know your programming language (for example, I wouldn't need two cases for both the lower case t and the capitol T). If that doesn't apply to you, I'm sorry.

sergey
11-05-2002, 02:23 PM
Thanks a lot for your time answering my question -> really appreciate it. I always thought it very useful to listen to what experienced people say.
Thanks a lot.
PS: how would you code those "t and T" using one case statement?
PS: and anoter one, if you don't mind, for some reason my Mesa 3.0 compiler doesn't understand my ASCII codes for up, down, left, right arrows. why?
PS: excuse my english -> not mother tongue.

[This message has been edited by sergey (edited 11-05-2002).]

nexusone
11-05-2002, 03:08 PM
The standard keyboard does not key an ASCII code for the arrow keys, if you are using GLUT then use glutSpecialFunc() to get arrow key's and function keys.

In the switch statment:

case: 'V' // If 'V' is pressed it falls to 'v' because of no break statement.
case: 'v'
do_something();
break; //exits out of switch statement.




Originally posted by sergey:

Thanks a lot for your time answering my question -> really appreciate it. I always thought it very useful to listen to what experienced people say.
Thanks a lot.
PS: how would you code those "t and T" using one case statement?
PS: and anoter one, if you don't mind, for some reason my Mesa 3.0 compiler doesn't understand my ASCII codes for up, down, left, right arrows. why?
PS: excuse my english -> not mother tongue.

[This message has been edited by sergey (edited 11-05-2002).]

mm_freak
11-06-2002, 03:41 PM
Example:

switch(_tolower(c)) {
case 't':
/* ... */
break;
case 'a':
/* ... */
break;
default:
/* ... */
}

The (char _tolower(char)) function is found in ctype.h. It converts any upper case letters to lowercase. Neither do I know, if it is available in C++ only, nor how portable it is. I use it. If it's not available on other systems, writing it shouldn't be a matter. =)

[This message has been edited by mm_freak (edited 11-06-2002).]

11-07-2002, 08:24 AM
please can you help as i am having the same problem! I need to use the glProject function but not sure how to. please explain using sergey's code example. Thanks.