1 /*
2   Copyright (C) 2009 Facundo Domínguez
3 
4   This file is part of Spacejunk.
5 
6   Spacejunk 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.
10 
11   Foobar is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "collisionengine.h"
21 #include "cworld.h"
22 #include "gamebody.h"
23 #include <set>
24 
25 using namespace std;
26 
box_swept_collision(GameBody * gb1,GameBody * gb2)27 bool box_swept_collision(GameBody * gb1,GameBody * gb2) {
28     // The time unit in these calculations is the time
29     // passed between the moment when the bodies where at the
30     // last test position and the moment when they reach their
31     // current one. So we search a collision in the time interval
32     // [0..1].
33 
34 
35     // Calculate velocity of gb2 w.r.t. gb1
36     // Thus, we can consider now that gb1 is not moving.
37     Vector2d vel=gb2->getPosition()-gb2->lastTestPosition()-(gb1->getPosition()-gb1->lastTestPosition());
38     // Calculate original position of gb2 w.r.t. gb1. i.e. put gb1 as
39     // coordinates origin.
40     // This is the left top corner of gb2, and the origin is on  left top
41     // corner of gb1. Note we are accounting for the hotspots.
42     Vector2d pos=(gb2->lastTestPosition()-gb2->hotspot)-(gb1->lastTestPosition()-gb1->hotspot);
43 
44 
45     // This is the time needed for gb2 to reach the right border of gb1.
46     real tix=vel.x!=0 ? (gb1->getWidth()-pos.x)/vel.x : 0;
47     // And this is the time needed for gb2 to reach the left border of gb1.
48     real tfx=vel.x!=0 ? -(pos.x+gb2->getWidth())/vel.x : 0;
49     // Sort the times
50     if (tix>tfx) {
51         real temp=tix;
52         tix=tfx;
53         tfx=temp;
54     }
55     // If the interval does not overlap with [0..1] quit now.
56     if (tfx<=0 || tix>=1) return false;
57 
58 
59     // This is the time needed for gb2 to reach the bottom border of gb1.
60     real tiy=vel.y!=0 ? (gb1->getHeight()-pos.y)/vel.y : 0;
61     // And this is the time needed for gb2 to reach the top border of gb1.
62     real tfy=vel.y!=0 ? -(pos.y+gb2->getHeight())/vel.y : 0;
63     // Sort the times
64     if (tiy>tfy) {
65         real temp=tiy;
66         tiy=tfy;
67         tfy=temp;
68     }
69     // Calculate overlap with interval [0..1]
70     // If it does not overlap, quit.
71     if (tfx<=0 || tix>=1) return false;
72     // else clip the interval to the [0..1]
73     if (tiy<0) tiy=0;
74     if (1<tfy) tfy=1;
75 
76     // Now if the time intervals overlap it means there was a colision
77     return (tiy<tfx && tix<tfy);
78 }
79 
80 
81 
circle_swept_collision(GameBody * gb1,GameBody * gb2,real * t,Vector2d * normal)82 bool circle_swept_collision(GameBody * gb1,GameBody * gb2,real * t,Vector2d * normal) {
83     // The time unit in these calculations is the time
84     // passed between the moment when the bodies where at the
85     // last test position and the moment when they reach their
86     // current one. So we search a collision in the time interval
87     // [0..1].
88 
89     // Calculate original position of gb2 w.r.t. gb1. i.e. put gb1 as
90     // coordinates origin.
91     // This is the hotspot of gb2, and the origin is the hotspot
92     // of gb1. We assuming the hotspots are the circle centers.
93     Vector2d pos=gb2->lastTestPosition()-gb1->lastTestPosition();
94 
95     // Calculate velocity of gb2 w.r.t. gb1
96     // Thus, we can consider now that gb1 is not moving.
97     Vector2d vel=gb2->getPosition()-gb1->getPosition()-pos;
98 
99     return circle_swept_collision(gb1->hotspot.x-1,gb2->hotspot.x-1,pos,vel,t,normal);
100 }
101 
102 
mask_collision(GameBody * gb1,GameBody * gb2,real * t,Vector2d * normal)103 int mask_collision(GameBody * gb1,GameBody * gb2,real * t,Vector2d * normal) {
104     if (&*gb1->mask && &*gb2->mask) {
105         Matrix2x3 m1(1,1,-gb1->getAngle(),-gb1->lastTestPosition().x,-gb1->lastTestPosition().y,gb1->hotspot.x,gb1->hotspot.y);
106         Matrix2x3 m2=m1*Matrix2x3(1,1,gb2->getAngle(),-gb2->hotspot.x,-gb2->hotspot.y,gb2->lastTestPosition().x,gb2->lastTestPosition().y);
107         Vector2d vel=m1*(gb2->getPosition()-gb2->lastTestPosition()-gb1->getPosition()+2*gb1->lastTestPosition());
108         vel-=gb1->hotspot;
109         return CMASK_intersect(&*gb1->mask,m2,M_PI*(gb2->getAngle()-gb1->getAngle())/180,&*gb2->mask,vel,t);
110     } else if (!&*gb1->mask && !&*gb2->mask)
111         return circle_swept_collision(gb1,gb2,t,normal);
112     else {
113         GameBody * g,*g2;
114         Circle c;
115         if (&*gb1->mask) {
116             g=gb1;
117             g2=gb2;
118             c.center=gb2->lastTestPosition();
119             c.r=gb2->hotspot.x-2;
120         } else {
121             g=gb2;
122             g2=gb1;
123             c.center=gb1->lastTestPosition();
124             c.r=gb1->hotspot.x-2;
125         }
126         Matrix2x3 m1(1,1,-g->getAngle(),-g->lastTestPosition().x,-g->lastTestPosition().y,g->hotspot.x,g->hotspot.y);
127         Vector2d vel=m1*(g2->getPosition()-g2->lastTestPosition()-g->getPosition()+2*g->lastTestPosition());
128         c.center=m1*c.center;
129         vel-=g->hotspot;
130         return CMASK_intersect(&*g->mask,c,vel,t);
131         //	else CHERROR<<pos.dist()/(i->second.r+j->second.r)<<ENDL;
132     };
133 }
134 
135 #define is(x,t) (std::string::npos!=static_cast<GameBody*>(x)->type.find(t))
136 
swept_collision(GameBody * gb1,GameBody * gb2,real * t,Vector2d * normal)137 bool swept_collision(GameBody * gb1,GameBody * gb2,real * t,Vector2d * normal) {
138     if (is(gb1,"asteroid") && is(gb2,"asteroid")) return false;
139     return mask_collision(gb1,gb2,t,normal);
140     //  return circle_swept_collision(gb1,gb2,t,normal);
141 }
142