From 5a4d8ffad3b172eae67844deda6b65273b7c9757 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 10 Apr 2011 15:16:27 +0300 Subject: implemented rats in new system to verify that it works --- src/collision.cpp | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/collision.cpp (limited to 'src/collision.cpp') diff --git a/src/collision.cpp b/src/collision.cpp new file mode 100644 index 000000000..83cefe4d1 --- /dev/null +++ b/src/collision.cpp @@ -0,0 +1,184 @@ +/* +Minetest-c55 +Copyright (C) 2010 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "collision.h" +#include "mapblock.h" +#include "map.h" + +collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d, + const core::aabbox3d &box_0, + f32 dtime, v3f &pos_f, v3f &speed_f) +{ + collisionMoveResult result; + + v3f oldpos_f = pos_f; + v3s16 oldpos_i = floatToInt(oldpos_f, BS); + + /* + Calculate new position + */ + pos_f += speed_f * dtime; + + /* + Collision detection + */ + + // position in nodes + v3s16 pos_i = floatToInt(pos_f, BS); + + /* + Collision uncertainty radius + Make it a bit larger than the maximum distance of movement + */ + f32 d = pos_max_d * 1.1; + // A fairly large value in here makes moving smoother + //f32 d = 0.15*BS; + + // This should always apply, otherwise there are glitches + assert(d > pos_max_d); + + /* + Calculate collision box + */ + core::aabbox3d box = box_0; + box.MaxEdge += pos_f; + box.MinEdge += pos_f; + core::aabbox3d oldbox = box_0; + oldbox.MaxEdge += oldpos_f; + oldbox.MinEdge += oldpos_f; + + /* + If the object lies on a walkable node, this is set to true. + */ + result.touching_ground = false; + + /* + Go through every node around the object + */ + for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++) + for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++) + for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++) + { + try{ + // Object collides into walkable nodes + if(content_walkable(map->getNode(v3s16(x,y,z)).d) == false) + continue; + } + catch(InvalidPositionException &e) + { + // Doing nothing here will block the object from + // walking over map borders + } + + core::aabbox3d nodebox = getNodeBox(v3s16(x,y,z), BS); + + /* + See if the object is touching ground. + + Object touches ground if object's minimum Y is near node's + maximum Y and object's X-Z-area overlaps with the node's + X-Z-area. + + Use 0.15*BS so that it is easier to get on a node. + */ + if( + //fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < d + fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS + && nodebox.MaxEdge.X-d > box.MinEdge.X + && nodebox.MinEdge.X+d < box.MaxEdge.X + && nodebox.MaxEdge.Z-d > box.MinEdge.Z + && nodebox.MinEdge.Z+d < box.MaxEdge.Z + ){ + result.touching_ground = true; + } + + // If object doesn't intersect with node, ignore node. + if(box.intersectsWithBox(nodebox) == false) + continue; + + /* + Go through every axis + */ + v3f dirs[3] = { + v3f(0,0,1), // back-front + v3f(0,1,0), // top-bottom + v3f(1,0,0), // right-left + }; + for(u16 i=0; i<3; i++) + { + /* + Calculate values along the axis + */ + f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]); + f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]); + f32 objectmax = box.MaxEdge.dotProduct(dirs[i]); + f32 objectmin = box.MinEdge.dotProduct(dirs[i]); + f32 objectmax_old = oldbox.MaxEdge.dotProduct(dirs[i]); + f32 objectmin_old = oldbox.MinEdge.dotProduct(dirs[i]); + + /* + Check collision for the axis. + Collision happens when object is going through a surface. + */ + bool negative_axis_collides = + (nodemax > objectmin && nodemax <= objectmin_old + d + && speed_f.dotProduct(dirs[i]) < 0); + bool positive_axis_collides = + (nodemin < objectmax && nodemin >= objectmax_old - d + && speed_f.dotProduct(dirs[i]) > 0); + bool main_axis_collides = + negative_axis_collides || positive_axis_collides; + + /* + Check overlap of object and node in other axes + */ + bool other_axes_overlap = true; + for(u16 j=0; j<3; j++) + { + if(j == i) + continue; + f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]); + f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]); + f32 objectmax = box.MaxEdge.dotProduct(dirs[j]); + f32 objectmin = box.MinEdge.dotProduct(dirs[j]); + if(!(nodemax - d > objectmin && nodemin + d < objectmax)) + { + other_axes_overlap = false; + break; + } + } + + /* + If this is a collision, revert the pos_f in the main + direction. + */ + if(other_axes_overlap && main_axis_collides) + { + speed_f -= speed_f.dotProduct(dirs[i]) * dirs[i]; + pos_f -= pos_f.dotProduct(dirs[i]) * dirs[i]; + pos_f += oldpos_f.dotProduct(dirs[i]) * dirs[i]; + } + + } + } // xyz + + return result; +} + + -- cgit v1.2.3