void gun_shell::check_collision_voxel(ship& s, const vector3f& oldrelpos, const vector3f& newrelpos) { // positions are relative to bbox of s. matrix4f obj2voxel = s.get_model().get_base_mesh_transformation().inverse(); vector3f oldvoxpos = obj2voxel * oldrelpos, newvoxpos = obj2voxel * newrelpos; vector3f diffvoxpos = newvoxpos - oldvoxpos; // now iterate in 8 steps between oldvoxpos to newvoxpos, // transform both to voxel coordinates (0...N) // and determine voxel number by pos. // if coordinate is invalid, no hit, otherwise check voxel state (volume > 0.25 or similar) // if the voxel is filled. vector3f voxel_size_rcp = s.get_model().get_voxel_size().rcp(); const vector3i& vres = s.get_model().get_voxel_resolution(); vector3i vidxmax = vres - vector3i(1, 1, 1); vector3f voxel_pos_trans = vector3f(vres) * 0.5f; int lastvn = -1; log_debug("check collision voxel"); for (unsigned k = 0; k <= 10; ++k) { float kf = k/10.0f; vector3f voxpos = oldvoxpos + diffvoxpos * kf; vector3i v = vector3i(voxpos.coeff_mul(voxel_size_rcp) + voxel_pos_trans); v = v.max(vector3i(0,0,0)).min(vidxmax); int vn = (v.z * vres.y + v.y)*vres.x + v.x; if (vn != lastvn) { lastvn = vn; log_debug("voxel hit k="<<k<<" voxpos="<<voxpos<<" v="<<v<<" vn="<<vn); const model::voxel* vox = s.get_model().get_voxel_by_pos(v); if (vox) { // we hit a part of the object! log_debug("..... Object hit! ....."); // first compute exact real word position of impact vector3 impactpos = s.get_pos() + s.get_orientation().rotate(s.get_model(). get_base_mesh_transformation() * voxpos); // move gun shell pos to hit position to // let the explosion be at right position position = impactpos; log_debug("Hit object at real world pos " << impactpos); log_debug("that is relative: " << s.get_pos()-impactpos); // now damage the ship if (s.damage(impactpos, int(damage_amount))) { // fixme, crude gm.ship_sunk(&s); } else { s.ignite(); } #if 0 //spawn some location marker object for testing //at exact impact position gm.spawn_particle(new marker_particle(impactpos)); #endif gm.add_event(new event_shell_explosion(get_pos())); kill(); // grenade is used and dead return; // no more checks } } } }
void gun_shell::check_collision_precise(ship& s, const vector3& oldrelpos, const vector3& newrelpos) { // transform positions to s' local bbox space quaternion qco = s.get_orientation().conj(); vector3f oldrelbbox = vector3f(qco.rotate(oldrelpos)); vector3f newrelbbox = vector3f(qco.rotate(newrelpos)); // now the model::get_min/get_max values can be used to compute the axis aligned bbox float tmin = 0.0f, tmax = 1.0f; // clip the line oldrelbbox->newrelbbox with the bbox vector3f d = newrelbbox - oldrelbbox; const vector3f& b = oldrelbbox; vector3f bmin = s.get_model().get_min(); vector3f bmax = s.get_model().get_max(); if (fabs(d.x) > 1e-5) { float t0 = (bmin.x - b.x) / d.x; float t1 = (bmax.x - b.x) / d.x; float ta = std::min(t0, t1); float tb = std::max(t0, t1); tmax = std::min(tmax, tb); tmin = std::max(tmin, ta); } if (fabs(d.y) > 1e-5) { float t0 = (bmin.y - b.y) / d.y; float t1 = (bmax.y - b.y) / d.y; float ta = std::min(t0, t1); float tb = std::max(t0, t1); tmax = std::min(tmax, tb); tmin = std::max(tmin, ta); } if (fabs(d.z) > 1e-5) { float t0 = (bmin.z - b.z) / d.z; float t1 = (bmax.z - b.z) / d.z; float ta = std::min(t0, t1); float tb = std::max(t0, t1); tmax = std::min(tmax, tb); tmin = std::max(tmin, ta); } if (tmin <= tmax) { //log_debug("shell hit object?!"); vector3f d = newrelbbox - oldrelbbox; check_collision_voxel(s, oldrelbbox + d * tmin, oldrelbbox + d * tmax); if (alive_stat == dead) return; // no more checks after hit } }