1 /************************************************************************
3 * voxelands - 3d voxel world sandbox game
4 * Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>
18 ************************************************************************/
21 #include "collision.h"
29 static void collision_initinfo(collisioninfo_t
*i
)
31 i
->type
= COLLISION_NODE
;
45 /* checks if movingbox collides with staticbox
46 * -1 no collision, 0 x collision, 1 y collision, 2 z collision
47 * time of collision stored in dtime */
48 static int collision_aa(aabox_t
*staticbox
, aabox_t
*movingbox
, v3_t
*speed
, float d
, float *dtime
)
57 xsize
= (staticbox
->max
.x
- staticbox
->min
.x
);
58 ysize
= (staticbox
->max
.y
- staticbox
->min
.y
);
59 zsize
= (staticbox
->max
.z
- staticbox
->min
.z
);
61 relbox
.min
.x
= (movingbox
->min
.x
- staticbox
->min
.x
);
62 relbox
.min
.y
= (movingbox
->min
.y
- staticbox
->min
.y
);
63 relbox
.min
.z
= (movingbox
->min
.z
- staticbox
->min
.z
);
64 relbox
.max
.x
= (movingbox
->max
.x
- staticbox
->min
.x
);
65 relbox
.max
.y
= (movingbox
->max
.y
- staticbox
->min
.y
);
66 relbox
.max
.z
= (movingbox
->max
.z
- staticbox
->min
.z
);
68 /* check against x- */
70 if (relbox
.max
.x
<= d
) {
71 dt
= -relbox
.max
.x
/speed
->x
;
73 dspeed
.x
= speed
->x
*dt
;
74 dspeed
.y
= speed
->y
*dt
;
75 dspeed
.z
= speed
->z
*dt
;
77 ((relbox
.min
.y
+dspeed
.y
) < ysize
)
78 && ((relbox
.max
.y
+dspeed
.y
) > 0.0)
79 && ((relbox
.min
.z
+dspeed
.z
) < zsize
)
80 && ((relbox
.max
.z
+dspeed
.z
) > 0.0)
83 }else if (relbox
.min
.x
> xsize
) {
86 /* check against x+ */
87 }else if (speed
->x
< 0.0) {
88 if (relbox
.min
.x
>= (xsize
-d
)) {
89 dt
= (xsize
-relbox
.min
.x
)/speed
->x
;
91 dspeed
.x
= speed
->x
*dt
;
92 dspeed
.y
= speed
->y
*dt
;
93 dspeed
.z
= speed
->z
*dt
;
95 ((relbox
.min
.y
+dspeed
.y
) < ysize
)
96 && ((relbox
.max
.y
+dspeed
.y
) > 0.0)
97 && ((relbox
.min
.z
+dspeed
.z
) < zsize
)
98 && ((relbox
.max
.z
+dspeed
.z
) > 0.0)
101 }else if (relbox
.max
.x
< 0.0) {
106 /* check against y- */
107 if (speed
->y
> 0.0) {
108 if (relbox
.max
.y
<= d
) {
109 dt
= -relbox
.max
.y
/speed
->y
;
111 dspeed
.x
= speed
->x
*dt
;
112 dspeed
.y
= speed
->y
*dt
;
113 dspeed
.z
= speed
->z
*dt
;
115 ((relbox
.min
.x
+dspeed
.x
) < xsize
)
116 && ((relbox
.max
.x
+dspeed
.x
) > 0.0)
117 && ((relbox
.min
.z
+dspeed
.z
) < zsize
)
118 && ((relbox
.max
.z
+dspeed
.z
) > 0.0)
121 }else if (relbox
.min
.y
> ysize
) {
124 /* check against y+ */
125 }else if (speed
->y
< 0.0) {
126 if (relbox
.min
.y
>= (ysize
-d
)) {
127 dt
= (ysize
-relbox
.min
.y
)/speed
->y
;
129 dspeed
.x
= speed
->x
*dt
;
130 dspeed
.y
= speed
->y
*dt
;
131 dspeed
.z
= speed
->z
*dt
;
133 ((relbox
.min
.x
+dspeed
.x
) < xsize
)
134 && ((relbox
.max
.x
+dspeed
.x
) > 0.0)
135 && ((relbox
.min
.z
+dspeed
.z
) < zsize
)
136 && ((relbox
.max
.z
+dspeed
.z
) > 0.0)
139 }else if (relbox
.max
.y
< 0) {
144 /* check against z- */
145 if (speed
->z
> 0.0) {
146 if(relbox
.max
.z
<= d
) {
147 dt
= -relbox
.max
.z
/speed
->z
;
149 dspeed
.x
= speed
->x
*dt
;
150 dspeed
.y
= speed
->y
*dt
;
151 dspeed
.z
= speed
->z
*dt
;
153 ((relbox
.min
.x
+dspeed
.x
) < xsize
)
154 && ((relbox
.max
.x
+dspeed
.x
) > 0.0)
155 && ((relbox
.min
.y
+dspeed
.y
) < ysize
)
156 && ((relbox
.max
.y
+dspeed
.y
) > 0)
160 /* check against z+ */
161 }else if (speed
->z
< 0.0) {
162 if (relbox
.min
.z
>= (zsize
-d
)) {
163 dt
= (zsize
-relbox
.min
.z
)/speed
->z
;
165 dspeed
.x
= speed
->x
*dt
;
166 dspeed
.y
= speed
->y
*dt
;
167 dspeed
.z
= speed
->z
*dt
;
169 ((relbox
.min
.x
+dspeed
.x
) < xsize
)
170 && ((relbox
.max
.x
+dspeed
.x
) > 0.0)
171 && ((relbox
.min
.y
+dspeed
.y
) < ysize
)
172 && ((relbox
.max
.y
+dspeed
.y
) > 0.0)
181 /* checks if movingbox+y_increase would hit a ceiling */
182 static int collision_ceiling(array_t
*staticboxes
, aabox_t
*movingbox
, float y_increase
, float d
)
190 for (i
=0; i
<staticboxes
->length
; i
++) {
191 s
= &((collisionblock_t
**)staticboxes
->data
)[i
]->box
;
195 ((movingbox
->max
.y
-d
) <= s
->min
.y
)
196 && ((movingbox
->max
.y
+y_increase
) > s
->min
.y
)
197 && (movingbox
->min
.x
< s
->max
.x
)
198 && (movingbox
->max
.x
> s
->min
.x
)
199 && (movingbox
->min
.z
< s
->max
.z
)
200 && (movingbox
->max
.z
> s
->min
.z
)
208 /* gets any block that could be collided with */
209 static int collision_get_nearby(array_t
*hits
, v3_t
*pos
, v3_t
*speed
, float dtime
)
211 /* TODO: this, once content*.c/h is done */
215 /* move pos by (speed+(accel*dtime))*dtime, checking for collisions */
216 /* TODO: this will need something changed for the client, so it doesn't use the server map */
217 collisionresult_t
*collision_move(float pos_max_d
, aabox_t
*box
, float stepheight
, float dtime
, v3_t
*pos
, v3_t
*speed
, v3_t accel
)
219 collisionresult_t
*result
;
220 collisionblock_t
*cb
;
221 collisioninfo_t
*info
= NULL
;
227 float d
= pos_max_d
* 1.1;
232 result
= malloc(sizeof(collisionresult_t
));
236 result
->touching_ground
= 0;
237 result
->in_liquid
= 0;
238 result
->touching_lethal
= 0;
239 result
->collides
= 0;
240 result
->collides_xz
= 0;
241 result
->standing_on_unloaded
= 0;
242 result
->collisions
= NULL
;
247 speed
->x
+= (accel
.x
*dtime
);
248 speed
->y
+= (accel
.y
*dtime
);
249 speed
->z
+= (accel
.z
*dtime
);
251 /* no movement == no collision */
252 if (speed
->x
== 0.0 && speed
->y
== 0.0 && speed
->z
== 0.0)
255 if (speed
->x
> 5000.0) {
257 }else if (speed
->x
< -5000.0) {
260 if (speed
->y
> 5000.0) {
262 }else if (speed
->y
< -5000.0) {
265 if (speed
->z
> 5000.0) {
267 }else if (speed
->z
< -5000.0) {
271 array_init(&hits
, ARRAY_TYPE_PTR
);
273 if (!collision_get_nearby(&hits
,pos
,speed
,dtime
)) {
278 for (i
=0; i
<100 && dtime
>1e-10; ++i
) {
279 int nearest_collided
= -1;
280 float nearest_dtime
= dtime
;
281 int nearest_boxindex
= -1;
283 mbox
.min
.x
= box
->min
.x
+pos
->x
;
284 mbox
.min
.y
= box
->min
.y
+pos
->y
;
285 mbox
.min
.z
= box
->min
.z
+pos
->z
;
286 mbox
.max
.x
= box
->max
.x
+pos
->x
;
287 mbox
.max
.y
= box
->max
.y
+pos
->y
;
288 mbox
.max
.z
= box
->max
.z
+pos
->z
;
290 for (k
=0; k
<hits
.length
; k
++) {
293 cb
= ((collisionblock_t
**)hits
.data
)[k
];
297 collided
= collision_aa(
305 if (collided
== -1 || dtime_tmp
>= nearest_dtime
)
308 nearest_dtime
= dtime_tmp
;
309 nearest_collided
= collided
;
310 nearest_boxindex
= k
;
314 if (nearest_collided
== -1) {
315 pos
->x
+= speed
->x
*dtime
;
316 pos
->y
+= speed
->y
*dtime
;
317 pos
->z
+= speed
->z
*dtime
;
320 /* collision occurred */
322 cb
= ((collisionblock_t
**)hits
.data
)[nearest_boxindex
];
324 /* steps are not counted as a collision */
326 (nearest_collided
!= 1)
327 && (mbox
.min
.y
< cb
->box
.max
.y
)
328 && ((mbox
.min
.y
+stepheight
) > cb
->box
.max
.y
)
329 && (!collision_ceiling(&hits
, &mbox
, cb
->box
.max
.y
-mbox
.min
.y
, d
))
333 }else if (nearest_dtime
>= 0.0) {
334 pos
->x
+= speed
->x
*nearest_dtime
;
335 pos
->y
+= speed
->y
*nearest_dtime
;
336 pos
->z
+= speed
->z
*nearest_dtime
;
337 dtime
-= nearest_dtime
;
339 if (nearest_collided
== 0)
340 pos
->x
+= speed
->x
*nearest_dtime
;
341 if (nearest_collided
== 1)
342 pos
->y
+= speed
->y
*nearest_dtime
;
343 if (nearest_collided
== 2)
344 pos
->z
+= speed
->z
*nearest_dtime
;
351 info
= malloc(sizeof(collisioninfo_t
));
356 collision_initinfo(info
);
359 info
->speed_o
= *speed
;
361 switch (nearest_collided
) {
364 result
->collides
= 1;
365 result
->collides_xz
= 1;
369 result
->collides
= 1;
373 result
->collides
= 1;
374 result
->collides_xz
= 1;
379 info
->speed_n
= *speed
;
380 if (math_distance(&info
->speed_n
,&info
->speed_o
) >= 0.1) {
381 result
->collisions
= list_push(&result
->collisions
,info
);
390 sbox
.min
.x
= box
->min
.x
+pos
->x
;
391 sbox
.min
.y
= box
->min
.y
+pos
->y
;
392 sbox
.min
.z
= box
->min
.z
+pos
->z
;
393 sbox
.max
.x
= box
->max
.x
+pos
->x
;
394 sbox
.max
.y
= box
->max
.y
+pos
->y
;
395 sbox
.max
.z
= box
->max
.z
+pos
->z
;
396 for (i
=0; i
<hits
.length
; i
++) {
397 cb
= ((collisionblock_t
**)hits
.data
)[i
];
400 See if the object is touching ground.
402 Object touches ground if object's minimum Y is near node's
403 maximum Y and object's X-Z-area overlaps with the node's
406 Use 0.15*BS so that it is easier to get on a node.
409 (cb
->box
.max
.x
-d
) > sbox
.min
.x
410 && (cb
->box
.min
.x
+d
) < sbox
.max
.x
411 && (cb
->box
.max
.z
-d
) > sbox
.min
.z
412 && (cb
->box
.min
.z
+d
) < sbox
.max
.z
415 float diff
= (cb
->box
.max
.y
-sbox
.min
.y
);
420 if (fabs(cb
->box
.max
.y
-sbox
.min
.y
) < 0.15) {
421 result
->touching_ground
= 1;
423 result
->standing_on_unloaded
= 1;
433 /* free a collisionresult_t */
434 void collision_free(collisionresult_t
*r
)
440 while ((i
= list_pop(&r
->collisions
))) {