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