/* * Copyright (c) 2008-2009 Bill Whitacre http://rampancy.g0dsoft.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "collision.h" rat_coll_query_fn rat_coll_query_fn_arr[rat_num_hull_types*rat_num_hull_types*2]; int rat_is_coll_query_fn_arr_init=0; int rat_always_collide(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) {return 1;} int rat_never_collide(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) {return 0;} static int rat_circle_query(rat_block_allocator *bal,rat_contact_array **cons,vector2 center_a,rat_real radius_a,vector2 center_b,rat_real radius_b) { int numcons; rat_real pen,dist,radii; dist=vector2_distance(center_a,center_b); dist=dist==0?0.001:dist; radii=radius_a+radius_b; pen=dist-radii; if (pen<=0.0) { if (cons) { rat_contact *con; *cons=rat_alloc_contact_array(bal,1); con=rat_indexed_contact(*cons,0); con->idvala=0; con->idvalb=0; con->point=vector2_add(center_a,vector2_multf(vector2_sub(center_b,center_a),0.5+(radius_a-0.5*radii)/dist)); con->normal=vector2_sub(center_a,center_b); if (con->normal.x==0&&con->normal.y==0) { con->normal.x=1; con->normal.y=0; } else vector2_normalize(&(con->normal)); con->penetration=pen; } return 1; } return 0; } int rat_coll_particle_circle(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) { rat_hull_particle *hull_a=(rat_hull_particle *)a; rat_hull_circle *hull_b=(rat_hull_circle *)b; int rtnval=rat_circle_query(bal,cons,hull_a->point,0.0,hull_b->center,hull_b->radius); if (cons&&rtnval) { rat_indexed_contact(*cons,0)->hull_a=a; rat_indexed_contact(*cons,0)->hull_b=b; } return rtnval; } int rat_coll_particle_poly(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) { int returnvalue=rat_coll_poly_particle(bal,b,a,cons); if (returnvalue&&cons) rat_invert_contacts(*cons); return returnvalue; } int rat_coll_circle_circle(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) { rat_hull_circle *hull_a=(rat_hull_circle *)a; rat_hull_circle *hull_b=(rat_hull_circle *)b; int rtnval=rat_circle_query(bal,cons,hull_a->center,hull_a->radius,hull_b->center,hull_b->radius); if (rtnval&&cons) { rat_indexed_contact(*cons,0)->hull_a=a; rat_indexed_contact(*cons,0)->hull_b=b; } return rtnval; } int rat_coll_circle_poly(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) { int returnvalue=rat_coll_poly_circle(bal,b,a,cons); if (returnvalue&&cons) rat_invert_contacts(*cons); return returnvalue; } int rat_coll_circle_particle(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) { rat_hull_circle *hull_a=(rat_hull_circle *)a; rat_hull_particle *hull_b=(rat_hull_particle *)b; int rtnval=rat_circle_query(bal,cons,hull_a->center,hull_a->radius,hull_b->point,0.0); if (rtnval&&cons) { rat_indexed_contact(*cons,0)->hull_a=a; rat_indexed_contact(*cons,0)->hull_b=b; } return rtnval; } int rat_coll_poly_circle(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) { register unsigned int i; unsigned int min_interval_i=0; rat_real min_interval; rat_hull_polygon *polygon=(rat_hull_polygon *)a; rat_hull_circle *circle=(rat_hull_circle *)b; rat_real dist_a,dist_b,dist_n; // get the minimum interval / normal min_interval=vector2_dot(polygon->world_axes[0].normal,circle->center)-polygon->world_axes[0].dist-circle->radius; for (i=0; inumverts; i++) { rat_real dist=vector2_dot(polygon->world_axes[i].normal,circle->center)-polygon->world_axes[i].dist-circle->radius; if (dist>0.0) return 0; else if (dist>min_interval) { min_interval=dist; min_interval_i=i; } } // get the contact dist_a=vector2_cross(polygon->world_axes[min_interval_i].normal,polygon->world_verts[min_interval_i]); dist_b=vector2_cross(polygon->world_axes[min_interval_i].normal,polygon->world_verts[(min_interval_i+1)%polygon->numverts]); dist_n=vector2_cross(polygon->world_axes[min_interval_i].normal,circle->center); if (dist_nworld_verts[(min_interval_i+1)%polygon->numverts],0.0,circle->center,circle->radius); if (rtn&&cons) { rat_contact *con=rat_indexed_contact(*cons,0); con->hull_a=a; con->hull_b=b; con->idvala=0; con->idvalb=1; } return rtn; } else if (dist_npoint=vector2_sub(circle->center,vector2_multf(polygon->world_axes[min_interval_i].normal,circle->radius+min_interval/2.0)); con->normal=vector2_negative(polygon->world_axes[min_interval_i].normal); con->penetration=min_interval; con->hull_a=a; con->hull_b=b; con->idvala=1; con->idvalb=2; } return 1; } return 0; } else { int rtn=rat_circle_query(bal,cons,polygon->world_verts[min_interval_i],0.0,circle->center,circle->radius); if (rtn&&cons) { rat_contact *con=rat_indexed_contact(*cons,0); con->hull_a=a; con->hull_b=b; con->idvala=2; con->idvalb=3; } return rtn; } } static int minimum_separating_axis(rat_hull_polygon *poly,rat_polygon_axis *axes,unsigned int numaxes,rat_real *minsepaxis) { register unsigned int i; register int min_index=0; register rat_real min=rat_polygon_axis_value(poly->world_verts,poly->numverts,axes[0]); if (min>0.0) return -1; for (i=1; iworld_verts,poly->numverts,axes[i]); if (dist>0.0) return -1; else if (dist>min) { min=dist; min_index=i; } } *minsepaxis=min; return min_index; } static void contact_verts(rat_contact_array **cons,rat_hull_polygon *poly_a, rat_hull_polygon *poly_b,vector2 normal,rat_real normal_coef, rat_real distance) { register unsigned int i,j; vector2_timesf(&normal,normal_coef); for (i=0; inumverts; i++) { vector2 point=poly_a->world_verts[i]; if (rat_hull_polygon_point_query((rat_hull *)poly_b,point)) { rat_contact *con=rat_add_array_contact(*cons); con->hull_a=(rat_hull *)poly_a; con->hull_b=(rat_hull *)poly_b; con->idvala=i; con->idvalb=1; con->point=point; con->normal=normal; con->penetration=distance; } } for (i=0; inumverts; i++) { vector2 point=poly_b->world_verts[i]; if (rat_hull_polygon_point_query((rat_hull *)poly_a,point)) { rat_contact *con=rat_add_array_contact(*cons); con->hull_a=(rat_hull *)poly_a; con->hull_b=(rat_hull *)poly_b; con->idvala=i; con->idvalb=2; con->point=point; con->normal=normal; con->penetration=distance; } } } static void contact_crossings(rat_contact_array **cons,rat_hull_polygon *poly_a, rat_hull_polygon *poly_b,vector2 normal,rat_real normal_coef, rat_real distance) { register unsigned int i,j; vector2 *crossings; vector2 crossings_centroid; register unsigned int numcrosses=0; crossings=(vector2 *)rat_alloc(poly_a->numverts*poly_b->numverts*2*sizeof(vector2)); vector2_timesf(&normal,normal_coef); for (i=0; inumverts; i++) { line2 edge_a; edge_a.a=poly_a->world_verts[i]; edge_a.b=poly_a->world_verts[(i+1)%poly_a->numverts]; for (j=0; jnumverts; j++) { vector2 intersection; line2 edge_b; edge_b.a=poly_b->world_verts[j]; edge_b.b=poly_b->world_verts[(j+1)%poly_b->numverts]; if (line2_intersection(edge_a,edge_b,&intersection)) { crossings[numcrosses]=intersection; numcrosses++; } } } if (numcrosses) { rat_contact *con=rat_add_array_contact(*cons); crossings_centroid=vector2_average(crossings,numcrosses); con->hull_a=(rat_hull *)poly_a; con->hull_b=(rat_hull *)poly_b; con->idvala=(unsigned int)poly_a; con->idvalb=(unsigned int)poly_a; con->point=crossings_centroid; con->normal=normal; con->penetration=distance; } rat_dealloc((void *)crossings); } /*static void contact_smallest_manifold(rat_contact_array **cons,rat_hull_polygon *poly_a, rat_hull_polygon *poly_b,vector2 normal,rat_real normal_coef, rat_real distance) { register unsigned int i; rat_real mindist; int scm_a_i=0,lcm_a_i=0,scm_b_i=0,lcm_b_i=0; rat_real scm_a,lcm_a=0,scm_b,lcm_b=0; rat_contact *con; vector2_timesf(&normal,normal_coef); rat_polygon_point_manifold_depth(poly_b->world_verts,poly_b->world_axes,poly_b->numverts,poly_a->world_verts[0],&scm_a); for (i=1; inumverts; i++) { vector2 point=poly_a->world_verts[i]; if (rat_polygon_point_manifold_depth(poly_b->world_verts,poly_b->world_axes,poly_b->numverts,point,&mindist)!=-1) { if (mindist<0.0) { if (mindist>scm_a) { scm_a=mindist; scm_a_i=i; } if (mindistworld_verts,poly_a->world_axes,poly_a->numverts,poly_b->world_verts[0],&scm_b); for (i=1; inumverts; i++) { vector2 point=poly_b->world_verts[i]; if (rat_polygon_point_manifold_depth(poly_a->world_verts,poly_a->world_axes,poly_a->numverts,point,&mindist)!=-1) { if (mindist<0.0) { if (mindist>scm_b) { scm_b=mindist; scm_b_i=i; } if (mindisthull_a=(rat_hull *)poly_a; con->hull_b=(rat_hull *)poly_b; con->idvala=(unsigned int)poly_a; con->idvalb=(unsigned int)poly_a; con->point=vector2_multf(vector2_add(poly_a->world_verts[scm_a_i],poly_a->world_verts[lcm_a_i]),0.5); con->normal=normal; con->penetration=distance; } } else { if (scm_b<=0.0) { con=rat_add_array_contact(*cons); con->hull_a=(rat_hull *)poly_a; con->hull_b=(rat_hull *)poly_b; con->idvala=(unsigned int)poly_a; con->idvalb=(unsigned int)poly_a; con->point=vector2_multf(vector2_add(poly_b->world_verts[scm_b_i],poly_b->world_verts[lcm_b_i]),0.5); con->normal=normal; con->penetration=distance; } } }*/ int rat_coll_poly_poly(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) { rat_hull_polygon *poly_a=(rat_hull_polygon *)a; rat_hull_polygon *poly_b=(rat_hull_polygon *)b; int msa_a_i,msa_b_i; rat_real msa_a,msa_b; msa_a_i=minimum_separating_axis(poly_b,poly_a->world_axes,poly_a->numverts,&msa_a); if (msa_a_i==-1) return 0; msa_b_i=minimum_separating_axis(poly_a,poly_b->world_axes,poly_b->numverts,&msa_b); if (msa_b_i==-1) return 0; if (cons) { *cons=rat_alloc_contact_array(bal,0); if (msa_a>msa_b) { contact_verts(cons,poly_a,poly_b,poly_a->world_axes[msa_a_i].normal,-1.0,msa_a); contact_crossings(cons,poly_a,poly_b,poly_a->world_axes[msa_a_i].normal,-1.0,msa_a); //contact_smallest_manifold(cons,poly_a,poly_b,poly_a->world_axes[msa_a_i].normal,-1.0,msa_a); } else { contact_verts(cons,poly_a,poly_b,poly_b->world_axes[msa_b_i].normal,1.0,msa_b); contact_crossings(cons,poly_a,poly_b,poly_b->world_axes[msa_b_i].normal,1.0,msa_b); //contact_smallest_manifold(cons,poly_a,poly_b,poly_b->world_axes[msa_b_i].normal,1.0,msa_b); } } return 1; } int rat_coll_poly_particle(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) { unsigned int i; unsigned int min_interval_i=0; rat_real min_interval; rat_hull_polygon *polygon=(rat_hull_polygon *)a; rat_hull_particle *particle=(rat_hull_particle *)b; // get the minimum interval / normal min_interval=vector2_dot(polygon->world_axes[0].normal,particle->point)-polygon->world_axes[0].dist; for (i=0; inumverts; i++) { rat_real dist=vector2_dot(polygon->world_axes[i].normal,particle->point)-polygon->world_axes[i].dist; if (dist>0.0) return 0; else if (dist>min_interval) { min_interval=dist; min_interval_i=i; } } if (min_interval<0.0) { if (cons) { rat_contact *con; *cons=rat_alloc_contact_array(bal,1); con=rat_indexed_contact(*cons,0); con->point=vector2_sub(particle->point,vector2_multf(polygon->world_axes[min_interval_i].normal,min_interval/2.0)); con->normal=vector2_negative(polygon->world_axes[min_interval_i].normal); con->penetration=min_interval; con->hull_a=a; con->hull_b=b; con->idvala=1; con->idvalb=2; } return 1; } return 0; } #define set_coll_query_fn(a,b,f) rat_coll_query_fn_arr[(a)+(b)*rat_num_hull_types]=(f) void rat_init_collision_array() { if (!rat_is_coll_query_fn_arr_init) { set_coll_query_fn(rat_hull_type_particle,rat_hull_type_circle,rat_coll_particle_circle); set_coll_query_fn(rat_hull_type_particle,rat_hull_type_polygon,rat_coll_particle_poly); set_coll_query_fn(rat_hull_type_particle,rat_hull_type_particle,rat_never_collide); set_coll_query_fn(rat_hull_type_circle,rat_hull_type_circle,rat_coll_circle_circle); set_coll_query_fn(rat_hull_type_circle,rat_hull_type_polygon,rat_coll_circle_poly); set_coll_query_fn(rat_hull_type_circle,rat_hull_type_particle,rat_coll_circle_particle); set_coll_query_fn(rat_hull_type_polygon,rat_hull_type_circle,rat_coll_poly_circle); set_coll_query_fn(rat_hull_type_polygon,rat_hull_type_polygon,rat_coll_poly_poly); set_coll_query_fn(rat_hull_type_polygon,rat_hull_type_particle,rat_coll_poly_particle); rat_is_coll_query_fn_arr_init=1; } } int rat_query_collision(rat_block_allocator *bal,rat_hull *a,rat_hull *b,rat_contact_array **cons) { return rat_coll_query_fn_arr[a->type+b->type*rat_num_hull_types](bal,a,b,cons); }