]> git.lizzy.rs Git - minetest.git/blob - src/collision.cpp
merged an old head into main branch
[minetest.git] / src / collision.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "collision.h"
21 #include "mapblock.h"
22 #include "map.h"
23
24 collisionMoveResult collisionMoveSimple(Map *map, f32 pos_max_d,
25                 const core::aabbox3d<f32> &box_0,
26                 f32 dtime, v3f &pos_f, v3f &speed_f)
27 {
28         collisionMoveResult result;
29
30         v3f oldpos_f = pos_f;
31         v3s16 oldpos_i = floatToInt(oldpos_f, BS);
32
33         /*
34                 Calculate new position
35         */
36         pos_f += speed_f * dtime;
37
38         /*
39                 Collision detection
40         */
41         
42         // position in nodes
43         v3s16 pos_i = floatToInt(pos_f, BS);
44         
45         /*
46                 Collision uncertainty radius
47                 Make it a bit larger than the maximum distance of movement
48         */
49         f32 d = pos_max_d * 1.1;
50         // A fairly large value in here makes moving smoother
51         //f32 d = 0.15*BS;
52
53         // This should always apply, otherwise there are glitches
54         assert(d > pos_max_d);
55         
56         /*
57                 Calculate collision box
58         */
59         core::aabbox3d<f32> box = box_0;
60         box.MaxEdge += pos_f;
61         box.MinEdge += pos_f;
62         core::aabbox3d<f32> oldbox = box_0;
63         oldbox.MaxEdge += oldpos_f;
64         oldbox.MinEdge += oldpos_f;
65
66         /*
67                 If the object lies on a walkable node, this is set to true.
68         */
69         result.touching_ground = false;
70         
71         /*
72                 Go through every node around the object
73                 TODO: Calculate the range of nodes that need to be checked
74         */
75         for(s16 y = oldpos_i.Y - 1; y <= oldpos_i.Y + 2; y++)
76         for(s16 z = oldpos_i.Z - 1; z <= oldpos_i.Z + 1; z++)
77         for(s16 x = oldpos_i.X - 1; x <= oldpos_i.X + 1; x++)
78         {
79                 try{
80                         // Object collides into walkable nodes
81                         if(content_walkable(map->getNode(v3s16(x,y,z)).d) == false)
82                                 continue;
83                 }
84                 catch(InvalidPositionException &e)
85                 {
86                         // Doing nothing here will block the object from
87                         // walking over map borders
88                 }
89
90                 core::aabbox3d<f32> nodebox = getNodeBox(v3s16(x,y,z), BS);
91                 
92                 /*
93                         See if the object is touching ground.
94
95                         Object touches ground if object's minimum Y is near node's
96                         maximum Y and object's X-Z-area overlaps with the node's
97                         X-Z-area.
98
99                         Use 0.15*BS so that it is easier to get on a node.
100                 */
101                 if(
102                                 //fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < d
103                                 fabs(nodebox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS
104                                 && nodebox.MaxEdge.X-d > box.MinEdge.X
105                                 && nodebox.MinEdge.X+d < box.MaxEdge.X
106                                 && nodebox.MaxEdge.Z-d > box.MinEdge.Z
107                                 && nodebox.MinEdge.Z+d < box.MaxEdge.Z
108                 ){
109                         result.touching_ground = true;
110                 }
111                 
112                 // If object doesn't intersect with node, ignore node.
113                 if(box.intersectsWithBox(nodebox) == false)
114                         continue;
115                 
116                 /*
117                         Go through every axis
118                 */
119                 v3f dirs[3] = {
120                         v3f(0,0,1), // back-front
121                         v3f(0,1,0), // top-bottom
122                         v3f(1,0,0), // right-left
123                 };
124                 for(u16 i=0; i<3; i++)
125                 {
126                         /*
127                                 Calculate values along the axis
128                         */
129                         f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[i]);
130                         f32 nodemin = nodebox.MinEdge.dotProduct(dirs[i]);
131                         f32 objectmax = box.MaxEdge.dotProduct(dirs[i]);
132                         f32 objectmin = box.MinEdge.dotProduct(dirs[i]);
133                         f32 objectmax_old = oldbox.MaxEdge.dotProduct(dirs[i]);
134                         f32 objectmin_old = oldbox.MinEdge.dotProduct(dirs[i]);
135                         
136                         /*
137                                 Check collision for the axis.
138                                 Collision happens when object is going through a surface.
139                         */
140                         bool negative_axis_collides =
141                                 (nodemax > objectmin && nodemax <= objectmin_old + d
142                                         && speed_f.dotProduct(dirs[i]) < 0);
143                         bool positive_axis_collides =
144                                 (nodemin < objectmax && nodemin >= objectmax_old - d
145                                         && speed_f.dotProduct(dirs[i]) > 0);
146                         bool main_axis_collides =
147                                         negative_axis_collides || positive_axis_collides;
148                         
149                         /*
150                                 Check overlap of object and node in other axes
151                         */
152                         bool other_axes_overlap = true;
153                         for(u16 j=0; j<3; j++)
154                         {
155                                 if(j == i)
156                                         continue;
157                                 f32 nodemax = nodebox.MaxEdge.dotProduct(dirs[j]);
158                                 f32 nodemin = nodebox.MinEdge.dotProduct(dirs[j]);
159                                 f32 objectmax = box.MaxEdge.dotProduct(dirs[j]);
160                                 f32 objectmin = box.MinEdge.dotProduct(dirs[j]);
161                                 if(!(nodemax - d > objectmin && nodemin + d < objectmax))
162                                 {
163                                         other_axes_overlap = false;
164                                         break;
165                                 }
166                         }
167                         
168                         /*
169                                 If this is a collision, revert the pos_f in the main
170                                 direction.
171                         */
172                         if(other_axes_overlap && main_axis_collides)
173                         {
174                                 speed_f -= speed_f.dotProduct(dirs[i]) * dirs[i];
175                                 pos_f -= pos_f.dotProduct(dirs[i]) * dirs[i];
176                                 pos_f += oldpos_f.dotProduct(dirs[i]) * dirs[i];
177                         }
178                 
179                 }
180         } // xyz
181         
182         return result;
183 }
184
185