/// LSU EE X70X GPU Prog / Microarch -*- c++ -*- // /// Quick and dirty code for ball / rectangle physics. // $Id:$ pVect vec_pos(pVect v) { return pVect(max(v.x,0.0f),max(v.y,0.0f),max(v.z,0.0f)); } pVect vec_neg(pVect v) { return pVect(min(v.x,0.0f),min(v.y,0.0f),min(v.z,0.0f)); } struct Bounding_Box { Bounding_Box(){ initialized = false; } Bounding_Box operator = (Bounding_Box b) { initialized = true; ll = b.ll; ur = b.ur; return *this; } Bounding_Box operator += (Bounding_Box b) { if ( !initialized ) return operator = (b); set_min(ll.x,b.ll.x); set_min(ll.y,b.ll.y); set_min(ll.z,b.ll.z); set_max(ur.x,b.ur.x); set_max(ur.y,b.ur.y); set_max(ur.z,b.ur.z); return *this; } bool initialized; pCoor ll, ur; }; class Tile : public Phys { public: Tile(bool &cuda_stale, pCoor ll, pVect up, pVect rt) :Phys(PT_Tile),cuda_stale(cuda_stale),marker(NULL) { read_only = true; set(ll,up,rt); } ~Tile(){ ASSERTS( false ); } void set(pCoor ll, pVect up, pVect rt) { pt_ll = ll; pt_ul = ll + up; pt_lr = ll + rt; vec_up = up; vec_rt = rt; normal.set(cross(rt,up)); norm_rt.set(rt); width = norm_rt.magnitude; norm_up.set(up); height = norm_up.magnitude; cuda_stale = true; bb.ll = pt_ll + vec_neg(vec_up) + vec_neg(vec_rt); bb.ur = pt_ll + vec_pos(vec_up) + vec_pos(vec_rt); } float max_z_get(double delta_t){ return bb.ur.z; } float min_z_get(double delta_t){ return bb.ll.z; } Bounding_Box bounding_box_get(){return bb;} bool& cuda_stale; void *marker; pCoor pt_ll; // A corner called lower-left but it doesn't have to be. pCoor pt_ul, pt_lr; pVect vec_up; pVect vec_rt; pNorm normal, norm_rt, norm_up; pColor color; float width, height; Bounding_Box bb; Ball *ball_tested; }; class Tile_Manager { public: Tile_Manager(){ phys_list = NULL; cuda_stale = true; }; void init(Phys_List *pl){ phys_list = pl; } void render(bool simple = false); void render_simple(); void render_shadow_volume(pCoor light_pos); Tile* new_tile(pCoor ll, pVect up, pVect rt, pColor color); Tile* new_tile(pCoor ll, pVect up, pVect rt); Tile* iterate(); int occ() { return tiles.occ(); } private: World* w; PStack<Tile*> tiles; Phys_List *phys_list; bool cuda_stale; }; Tile* Tile_Manager::new_tile(pCoor ll, pVect up, pVect rt, pColor color) { Tile* const rv = new_tile(ll,up,rt); rv->color = color; return rv; } Tile* Tile_Manager::new_tile(pCoor ll, pVect up, pVect rt) { Tile* const rv = new Tile(cuda_stale,ll,up,rt); tiles += rv; phys_list->push(rv); return rv; } Tile* Tile_Manager::iterate() { Tile** const tp = tiles.iterate(); return tp ? *tp : NULL; } void Tile_Manager::render(bool simple) { glBegin(GL_TRIANGLES); for ( PStackIterator<Tile*> tile(tiles); tile; tile++ ) { if ( !simple ) { glColor3fv(tile->color); glNormal3fv(tile->normal); } glVertex3fv(tile->pt_ul); glVertex3fv(tile->pt_ll); glVertex3fv(tile->pt_lr); glVertex3fv(tile->pt_lr); glVertex3fv(tile->pt_ll+tile->vec_rt+tile->vec_up); glVertex3fv(tile->pt_ul); } glEnd(); } void Tile_Manager::render_simple(){ render(true); } void Tile_Manager::render_shadow_volume(pCoor light_pos) { const float height = 1000; for ( PStackIterator<Tile*> tile(tiles); tile; tile++ ) { pCoor pt_ur = tile->pt_ll+tile->vec_rt+tile->vec_up; pNorm l_to_ul(light_pos,tile->pt_ul); pCoor ul_2 = light_pos + height * l_to_ul; pCoor ll_2 = light_pos + height * pNorm(light_pos,tile->pt_ll); pCoor lr_2 = light_pos + height * pNorm(light_pos,tile->pt_lr); pCoor ur_2 = light_pos + height * pNorm(light_pos,pt_ur); const bool facing_light = dot(tile->normal,l_to_ul) < 0; if ( facing_light ) glFrontFace(GL_CW); else glFrontFace(GL_CCW); glBegin(GL_QUAD_STRIP); glVertex3fv(tile->pt_ll); glVertex3fv(ll_2); glVertex3fv(tile->pt_lr); glVertex3fv(lr_2); glVertex3fv(pt_ur); glVertex3fv(ur_2); glVertex3fv(tile->pt_ul); glVertex3fv(ul_2); glVertex3fv(tile->pt_ll); glVertex3fv(ll_2); glEnd(); } glFrontFace(GL_CCW); } bool tile_sphere_intersect (Tile *tile, pCoor position, float radius, pCoor& tact_pos, pNorm& tact_dir, bool dont_compute_tact = false) { pVect tile_to_ball(tile->pt_ll,position); // Distance from tile's plane to the ball. const float dist = dot(tile_to_ball,tile->normal); if ( fabs(dist) > radius ) return false; // The closest point on tile plane to the ball. pCoor pt_closest = position - dist * tile->normal; // How far up the tile in the y direction the center of the ball sits const float dist_ht = dot(tile->norm_up,tile_to_ball); if ( dist_ht < -radius ) return false; if ( dist_ht > tile->height + radius ) return false; // How far up the tile in the x direction the center of the ball sits const float dist_wd = dot(tile->norm_rt,tile_to_ball); if ( dist_wd < -radius ) return false; if ( dist_wd > tile->width + radius ) return false; if ( dont_compute_tact ) return true; // If ball touching tile surface (not including an edge or corner) // then set up the pseudo ball for collision handling if ( dist_ht >= 0 && dist_ht <= tile->height && dist_wd >= 0 && dist_wd <= tile->width ) { tact_pos = pt_closest; tact_dir = dist > 0 ? -tile->normal : tile->normal; return true; } // Test whether the ball is touching a corner if ( ( dist_ht < 0 || dist_ht > tile->height ) && ( dist_wd < 0 || dist_wd > tile->width) ) { pCoor ref_pt; // We need to place the pseudo ball based upon the vector from // ball position to the corner. First step is to figure out which // corner. if ( dist_ht < 0 && dist_wd < 0 ) { ref_pt = tile->pt_ll; } else if ( dist_ht < 0 && dist_wd > tile->width ) { ref_pt = tile->pt_lr; } else if ( dist_ht > tile->height && dist_wd < 0 ) { ref_pt = tile->pt_ul; } else { ref_pt = tile->pt_ll+tile->vec_rt+tile->vec_up; } tact_pos = ref_pt; tact_dir = pVect(position,ref_pt); return true; } // Else the ball is touching an edge const bool tact_horiz = dist_ht < 0 || dist_ht > tile->height; const pVect corner_to_tact = tact_horiz ? dist_wd * tile->norm_rt : dist_ht * tile->norm_up; const pCoor ref_pt = tact_horiz ? ( dist_ht < 0 ? tile->pt_ll : tile->pt_ul ) : ( dist_wd < 0 ? tile->pt_ll : tile->pt_lr ); // Find the closest edge point of the tile to the ball tact_pos = ref_pt + corner_to_tact; tact_dir = pVect(position,tact_pos); return true; } bool tile_sphere_intersect (Tile *tile, pCoor position, float radius) { pCoor dummyc; pNorm dummyn; return tile_sphere_intersect(tile,position,radius,dummyc,dummyn,true); } bool xtile_ball_collide (Tile *tile, Ball *ball, pCoor& tact_pos, pNorm& tact_dir) { pVect tile_to_ball(tile->pt_ll,ball->position); // Distance from tile's plane to the ball. const float dist = dot(tile_to_ball,tile->normal); const float radius = ball->radius; if ( fabs(dist) > radius ) return false; // The closest point on tile plane to the ball. pCoor pt_closest = ball->position - dist * tile->normal; // How far up the tile in the y direction the center of the ball sits const float dist_ht = dot(tile->norm_up,tile_to_ball); if ( dist_ht < -radius ) return false; if ( dist_ht > tile->height + radius ) return false; // How far up the tile in the x direction the center of the ball sits const float dist_wd = dot(tile->norm_rt,tile_to_ball); if ( dist_wd < -radius ) return false; if ( dist_wd > tile->width + radius ) return false; // If ball touching tile surface (not including an edge or corner) // then set up the pseudo ball for collision handling if ( dist_ht >= 0 && dist_ht <= tile->height && dist_wd >= 0 && dist_wd <= tile->width ) { tile->ball_tested = ball; tact_pos = pt_closest; tact_dir = dist > 0 ? -tile->normal : tile->normal; return true; } // Test whether the ball is touching a corner if ( ( dist_ht < 0 || dist_ht > tile->height ) && ( dist_wd < 0 || dist_wd > tile->width) ) { pCoor ref_pt; // We need to place the pseudo ball based upon the vector from // ball position to the corner. First step is to figure out which // corner. if ( dist_ht < 0 && dist_wd < 0 ) { ref_pt = tile->pt_ll; } else if ( dist_ht < 0 && dist_wd > tile->width ) { ref_pt = tile->pt_lr; } else if ( dist_ht > tile->height && dist_wd < 0 ) { ref_pt = tile->pt_ul; } else { ref_pt = tile->pt_ll+tile->vec_rt+tile->vec_up; } tile->ball_tested = ball; tact_pos = ref_pt; tact_dir = pVect(ball->position,ref_pt); return true; } // Else the ball is touching an edge const bool tact_horiz = dist_ht < 0 || dist_ht > tile->height; const pVect corner_to_tact = tact_horiz ? dist_wd * tile->norm_rt : dist_ht * tile->norm_up; const pCoor ref_pt = tact_horiz ? ( dist_ht < 0 ? tile->pt_ll : tile->pt_ul ) : ( dist_wd < 0 ? tile->pt_ll : tile->pt_lr ); tile->ball_tested = ball; // Find the closest edge point of the tile to the ball tact_pos = ref_pt + corner_to_tact; tact_dir = pVect(ball->position,tact_pos); return true; }