Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to calculate Collision ?

Status
Not open for further replies.

Kirilla

Programmer
Jul 12, 2000
101
HU
Hi.

I've made a simple labyrinth. One labyrinth unit consist of 6 triangles ( 3 square - floor and 2 walls ). Now I can walk along on it, but I can walk through the walls.
I've made an collosion detection algorithm, but I think it is not so good. (it doesn't like me) I've stored the floors coords (squares) and I check, if the EyePoint inside these square or not.
Do anybody know a better way to calculate collosion?
(I've read a lot of articles, they calculate something with normals, but it was not so clear to me)

best regards,
Kirilla
 
Try checking out the D3DXPlaneIntersectLine function. Maybe that could work for ya.
 
// --------------------
//
// collision.cpp
//
// Copyright (C) 1995-2001 Volition, Inc.
// All rights reserved
//
// This code is meant for illustrative purposes only. Volition takes
// no responsibility for any Bad Things(TM) that happen as a result
// of using it. Use at your own risk.
//
// --------------------

#include <math.h>
#include &quot;vector.h&quot;


// --------------------
//
// Defines
//
// --------------------

#define CLIP_RIGHT (1<<0) // cohen-sutherland clipping outcodes
#define CLIP_LEFT (1<<1)
#define CLIP_TOP (1<<2)
#define CLIP_BOTTOM (1<<3)
#define CLIP_FRONT (1<<4)
#define CLIP_BACK (1<<5)


// --------------------
//
// Helper Macros
//
// --------------------


// --------------------
//
// Enumerated Types
//
// --------------------


// --------------------
//
// Structures
//
// --------------------


// --------------------
//
// Classes
//
// --------------------


// --------------------
//
// Global Variables
//
// --------------------


// --------------------
//
// Local Variables
//
// --------------------


// --------------------
//
// Internal Functions
//
// --------------------

// calculates the cohen-sutherland outcode for a point and a bounding box.
//
// bbox_min: min vector of the bounding box
// bbox_max: max vector of the bounding box
// pnt: the point to check
//
// returns: the outcode
//
static ulong calc_outcode( vector &bbox_min, vector &bbox_max, vector &pnt )
{
ulong outcode = 0;

if( pnt.x > bbox_max.x ) {
outcode |= CLIP_RIGHT;
} else if( pnt.x < bbox_min.x ) {
outcode |= CLIP_LEFT;
}
if( pnt.y > bbox_max.y ) {
outcode |= CLIP_TOP;
} else if( pnt.y < bbox_min.y ) {
outcode |= CLIP_BOTTOM;
}
if( pnt.z > bbox_max.z ) {
outcode |= CLIP_BACK;
} else if( pnt.z < bbox_min.z ) {
outcode |= CLIP_FRONT;
}

return outcode;
}


// --------------------
//
// External Functions
//
// --------------------

// checks if a line crosses from the front of a plane to the back
//
// start: start point of the line
// dir: direction of the line, does not need to be normalized
// p: the plane
// fraction: (OUT) how far along the line it crossed the plane
//
// returns: true if the line crossed from front to back
//
bool collide_line_plane( vector &start, vector &dir, plane &p, float &fraction )
{
float dist, len;

dist = start * p.normal + p.offset;
if( dist < 0 ) {
// behind plane
return false;
}

len = -(dir * p.normal);
if( len < dist ) {
// moving away from plane or point too far away
return false;
}

//
fraction = dist / len;
return true;
}


// checks if a sphere crosses from the front of a plane to the back
//
// start: start point of the line
// dir: direction of the line, does not need to be normalized
// radius: the radius of the sphere
// p: the plane
// fraction: (OUT) how far along the line it crossed the plane
//
// returns: true if the sphere crossed from front to back
//
bool collide_sphere_plane( vector &start, vector &dir, float radius, plane &p, float &fraction, vector &hit_point )
{
// find the closest point on the sphere to the plane
vector sphere_bottom = start - (p.normal * radius);

// collide the point like a regular ray
if( !collide_line_plane(sphere_bottom, dir, p, fraction) ) {
// no dice
return false;
}

// we have a winner
hit_point = sphere_bottom + dir * fraction;
return true;
}


// see if a point in inside a face by projecting into 2d.
//
// hit_point: the point to test
// verts: polygon vertices
// norm1: polygon normal
//
// returns: true if hit point is within polygon
//
bool point_in_face( vector &hit_point, vector *verts[], vector &norm1 )
{
// given largest component of normal, return i & j
static int ij_table[3][2] = { {2,1}, // norm x biggest
{0,2}, // norm y biggest
{1,0} };// norm z biggest int i0, i1,i2;

float u0, u1, u2, v0, v1, v2;
int i0, i1, i2;
float alpha, beta;

// find largest component of normal
float abs_x = (float)fabs(norm1.x);
float abs_y = (float)fabs(norm1.y);
float abs_z = (float)fabs(norm1.z);
if( abs_x > abs_y ) {
if( abs_x > abs_z ) {
i0 = 0;
} else {
i0 = 2;
}
} else {
if( abs_y > abs_z ) {
i0 = 1;
} else {
i0 = 2;
}
}

// pretend that norm1 is an array of three floats.
float *norm1_xyz = &norm1.x;
// figure out the two dimensions we are going to use
if( norm1_xyz[i0] > 0.0f ) {
i1 = ij_table[i0][0];
i2 = ij_table[i0][1];
} else {
i1 = ij_table[i0][1];
i2 = ij_table[i0][0];
}

// pretend that hit_point and *(verts[N]) are arrays of three floats.
float *hit_point_xyz = &hit_point.x;
float *verts0_xyz = &(verts[0]->x);
float *verts1_xyz = &(verts[1]->x);
float *verts2_xyz = &(verts[2]->x);

// get deltas for hitpoint
u0 = hit_point_xyz[i1] - verts0_xyz[i1];
v0 = hit_point_xyz[i2] - verts0_xyz[i2];
// ditto edge 1
u1 = verts1_xyz[i1] - verts0_xyz[i1];
v1 = verts1_xyz[i2] - verts0_xyz[i2];
// ditto edge 2
u2 = verts2_xyz[i1] - verts0_xyz[i1];
v2 = verts2_xyz[i2] - verts0_xyz[i2];

// calculate alpha and beta
if( (u1 > -0.0001f) && (u1 < 0.0001f) ) {
// special case to guard against divide by zero
beta = u0 / u2;
if( (beta >= 0.0f) && (beta <= 1.0f) ) {
alpha = (v0 - beta*v2) / v1;
return ((alpha >= 0.0f) && (alpha+beta <= 1.0f));
}
} else {
beta = (v0*u1 - u0*v1) / (v2*u1 - u2*v1);
if( (beta >= 0.0f) && (beta <= 1.0f) ) {
alpha = (u0 - beta*u2) / u1;
return ((alpha >= 0.0f) && (alpha+beta <= 1.0f));
}
}

// not inside polygon
return false;
}


// given an edge of a polygon and a moving sphere, find the first contact the sphere
// makes with the edge, if any. note that hit_time must be primed with a value of 1
// before calling this function the first time. it will then maintain the closest
// collision in subsequent calls.
//
// xs0: start point (center) of sphere
// vs: path of sphere during frame
// rad: radius of sphere
// v0: vertex #1 of the edge
// v1: vertex #2 of the edge
// hit_time: (OUT) time at which sphere collides with polygon edge
// hit_point: (OUT) point on edge that is hit
//
// returns - whether the edge (or it's vertex) was hit
bool collide_edge_sphereline( const vector &xs0, const vector &vs, float rad, const vector &v0, const vector &v1, float &hit_time, vector &hit_point )
{
static vector temp_sphere_hit;
bool try_vertex = false; // Assume we don't have to try the vertices.

vector ve = v1 - v0;
vector delta = xs0 - v0;
float delta_dot_ve = delta * ve;
float delta_dot_vs = delta * vs;
float delta_sqr = delta.mag_squared();
float ve_dot_vs = ve * vs;
float ve_sqr = ve.mag_squared();
float vs_sqr = vs.mag_squared();

float temp;

// position of the collision along the edge is given by: xe = v0 + ve*s, where s is
// in the range [0,1]. position of sphere along its path is given by:
// xs = xs + vs*t, where t is in the range [0,1]. t is time, but s is arbitrary.
//
// solve simultaneous equations
// (1) distance between edge and sphere center must be sphere radius
// (2) line between sphere center and edge must be perpendicular to edge
//
// (1) (xe - xs)*(xe - xs) = rad*rad
// (2) (xe - xs) * ve = 0
//
// then apply mathematica

float A, B, C, root, discriminant;
float root1 = 0.0f;
float root2 = 0.0f;
A = ve_dot_vs * ve_dot_vs - ve_sqr * vs_sqr;
B = 2 * (delta_dot_ve * ve_dot_vs - delta_dot_vs * ve_sqr);
C = delta_dot_ve * delta_dot_ve + rad * rad * ve_sqr - delta_sqr * ve_sqr;

if( A > -0.0001f && A < 0.0001f ) {
// degenerate case, sphere is traveling parallel to edge
try_vertex = true;
} else {
discriminant = B*B - 4*A*C;
if( discriminant > 0 ) {
root = (float)sqrt(discriminant);
root1 = (-B + root) / (2 * A);
root2 = (-B - root) / (2 * A);

// sort root1 and root2, use the earliest intersection. the larger root
// corresponds to the final contact of the sphere with the edge on its
// way out.
if( root2 < root1 ) {
temp = root1;
root1 = root2;
root2 = temp;
}

// root1 is a time, check that it's in our currently valid range
if( (root1 < 0) || (root1 >= hit_time) ) {
return false;
}

// find sphere and edge positions
temp_sphere_hit = xs0 + vs * root1;

// check if hit is between v0 and v1
float s_edge = ((temp_sphere_hit - v0) * ve) / ve_sqr;
if( (s_edge >= 0) && (s_edge <= 1) ) {
// bingo
hit_time = root1;
hit_point = v0 + ve * s_edge;
return true;
}
} else {
// discriminant negative, sphere passed edge too far away
return false;
}
}

// sphere missed the edge, check for a collision with the first vertex. note
// that we only need to check one vertex per call to check all vertices.
A = vs_sqr;
B = 2 * delta_dot_vs;
C = delta_sqr - rad * rad;

discriminant = B*B - 4*A*C;
if( discriminant > 0 ) {
root = (float)sqrt(discriminant);
root1 = (-B + root) / (2 * A);
root2 = (-B - root) / (2 * A);

// sort the solutions
if( root1 > root2 ) {
temp = root1;
root1 = root2;
root2 = temp;
}

// check hit vertex is valid and earlier than what we already have
if( (root1 < 0) || (root1 >= hit_time) ) {
return false;
}
} else {
// discriminant negative, sphere misses vertex too
return false;
}

// bullseye
hit_time = root1;
hit_point = v0;
return true;
}


// tests two axis-aligned bounding boxes for intersection
//
// min1: - min vector of bounding box 1
// max1: - max vector of bounding box 1
// min2: - min vector of bounding box 2
// max2: - max vector of bounding box 2
//
// returns true if boxes intersect
//
bool ix_box_box_aligned( vector &min1, vector &max1, vector &min2, vector &max2 )
{
if( min1.x > max2.x || min1.y > max2.y || min1.z > max2.z || max1.x < min2.x || max1.y < min2.y || max1.z < min2.z ) {
return false;
}

return true;
}


// determines if a linesegment intersects a bounding box. this is based on
// the cohen-sutherland line-clipping algorithm.
//
// bbox_min: bounding box min vector
// bbox_max: bounding box max vector
// p1: end point of line segment
// p2: other end point
// intercept: (out) the point in/on the bounding box where the intersection
// occured. note that this point may not be on the surface of the box.
//
// returns: true if the segment and box intersect.
//
bool collide_linesegment_boundingbox( vector &bbox_min, vector &bbox_max, vector &p1, vector &p2, vector &intercept )
{
ulong outcode1, outcode2;

outcode1 = calc_outcode( bbox_min, bbox_max, p1 );
if( outcode1 == 0 ) {
// point inside bounding box
intercept = p1;
return true;
}

outcode2 = calc_outcode( bbox_min, bbox_max, p2 );
if( outcode2 == 0 ) {
// point inside bounding box
intercept = p2;
return true;
}

if( (outcode1 & outcode2) > 0 ) {
// both points on same side of box
return false;
}

// check intersections
if( outcode1 & (CLIP_RIGHT | CLIP_LEFT) ) {
if( outcode1 & CLIP_RIGHT ) {
intercept.x = bbox_max.x;
} else {
intercept.x = bbox_min.x;
}
float x1 = p2.x - p1.x;
float x2 = intercept.x - p1.x;
intercept.y = p1.y + x2 * (p2.y - p1.y) / x1;
intercept.z = p1.z + x2 * (p2.z - p1.z) / x1;

if( intercept.y <= bbox_max.y && intercept.y >= bbox_min.y && intercept.z <= bbox_max.z && intercept.z >= bbox_min.z ) {
return true;
}
}

if( outcode1 & (CLIP_TOP | CLIP_BOTTOM) ) {
if( outcode1 & CLIP_TOP ) {
intercept.y = bbox_max.y;
} else {
intercept.y = bbox_min.y;
}
float y1 = p2.y - p1.y;
float y2 = intercept.y - p1.y;
intercept.x = p1.x + y2 * (p2.x - p1.x) / y1;
intercept.z = p1.z + y2 * (p2.z - p1.z) / y1;

if( intercept.x <= bbox_max.x && intercept.x >= bbox_min.x && intercept.z <= bbox_max.z && intercept.z >= bbox_min.z ) {
return true;
}
}

if( outcode1 & (CLIP_FRONT | CLIP_BACK) ) {
if( outcode1 & CLIP_BACK ) {
intercept.z = bbox_max.z;
} else {
intercept.z = bbox_min.z;
}
float z1 = p2.z - p1.z;
float z2 = intercept.z - p1.z;
intercept.x = p1.x + z2 * (p2.x - p1.x) / z1;
intercept.y = p1.y + z2 * (p2.y - p1.y) / z1;

if( intercept.x <= bbox_max.x && intercept.x >= bbox_min.x && intercept.y <= bbox_max.y && intercept.y >= bbox_min.y ) {
return true;
}
}

// nothing found
return false;
}


// determines a collision between a ray and a sphere
//
// ray_start: the start pos of the ray
// ray_dir: the normalized direction of the ray
// length: length of ray to check
// sphere_pos: sphere position
// sphere_rad: sphere redius
// hit_time: (OUT) if a collision, contains the distance from ray.pos
// hit_pos: (OUT) if a collision, contains the world point of the collision
//
// returns: true if a collision occurred
//
bool collide_ray_sphere( vector &ray_start, vector &ray_dir, float length, vector &sphere_pos, float sphere_rad, float &hit_time, vector &hit_pos )
{
// get the offset vector
vector offset = sphere_pos - ray_start;

// get the distance along the ray to the center point of the sphere
float ray_dist = ray_dir * offset;
if( ray_dist <= 0 || (ray_dist - length) > sphere_rad) {
// moving away from object or too far away
return false;
}

// get the squared distances
float off2 = offset * offset;
float rad2 = sphere_rad * sphere_rad;
if( off2 <= rad2 ) {
// we're in the sphere
hit_pos = ray_start;
hit_time = 0;
return true;
}

// find hit distance squared
float d = rad2 - (off2 - ray_dist * ray_dist);
if( d < 0 ) {
// ray passes by sphere without hitting
return false;
}

// get the distance along the ray
hit_time = (float)(ray_dist - sqrt( d ));
if( hit_time > length ) {
// hit point beyond length
return false;
}

// sort out the details
hit_pos = ray_start + ray_dir * hit_time;
hit_time /= length;
return true;
} ---------------------------------------
wmail.jpg


someone knowledge ends where
someone else knowledge starts
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top