aboutsummaryrefslogtreecommitdiff
path: root/Spear/Math/Collision.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Spear/Math/Collision.hs')
-rw-r--r--Spear/Math/Collision.hs482
1 files changed, 241 insertions, 241 deletions
diff --git a/Spear/Math/Collision.hs b/Spear/Math/Collision.hs
index 47cc5fd..a69ea7a 100644
--- a/Spear/Math/Collision.hs
+++ b/Spear/Math/Collision.hs
@@ -1,242 +1,242 @@
1module Spear.Math.Collision 1module Spear.Math.Collision
2( 2(
3 CollisionType(..) 3 CollisionType(..)
4 -- * 2D Collision 4 -- * 2D Collision
5, Collisionable2(..) 5, Collisionable2(..)
6, Collisioner2(..) 6, Collisioner2(..)
7 -- ** Construction 7 -- ** Construction
8, aabb2Collisioner 8, aabb2Collisioner
9, circleCollisioner 9, circleCollisioner
10, mkCols 10, mkCols
11 -- ** Collision test 11 -- ** Collision test
12, collide 12, collide
13 -- ** Manipulation 13 -- ** Manipulation
14, move 14, move
15 -- ** Helpers 15 -- ** Helpers
16, buildAABB2 16, buildAABB2
17, aabb2FromCircle 17, aabb2FromCircle
18, circleFromAABB2 18, circleFromAABB2
19 -- * 3D Collision 19 -- * 3D Collision
20, Collisionable3(..) 20, Collisionable3(..)
21 -- ** Helpers 21 -- ** Helpers
22, aabb3FromSphere 22, aabb3FromSphere
23) 23)
24where 24where
25 25
26import Spear.Assets.Model 26import Spear.Assets.Model
27import Spear.Math.AABB 27import Spear.Math.AABB
28import Spear.Math.Circle 28import Spear.Math.Circle
29import qualified Spear.Math.Matrix4 as M4 29import qualified Spear.Math.Matrix4 as M4
30import Spear.Math.Plane 30import Spear.Math.Plane
31import Spear.Math.Sphere 31import Spear.Math.Sphere
32import Spear.Math.Vector 32import Spear.Math.Vector
33 33
34import Data.List (foldl') 34import Data.List (foldl')
35 35
36data CollisionType = NoCollision | Collision | FullyContains | FullyContainedBy 36data CollisionType = NoCollision | Collision | FullyContains | FullyContainedBy
37 deriving (Eq, Show) 37 deriving (Eq, Show)
38 38
39-- 2D collision 39-- 2D collision
40 40
41class Collisionable2 a where 41class Collisionable2 a where
42 42
43 -- | Collide the object with an AABB. 43 -- | Collide the object with an AABB.
44 collideAABB2 :: AABB2 -> a -> CollisionType 44 collideAABB2 :: AABB2 -> a -> CollisionType
45 45
46 -- | Collide the object with a circle. 46 -- | Collide the object with a circle.
47 collideCircle :: Circle -> a -> CollisionType 47 collideCircle :: Circle -> a -> CollisionType
48 48
49instance Collisionable2 AABB2 where 49instance Collisionable2 AABB2 where
50 50
51 collideAABB2 box1@(AABB2 min1 max1) box2@(AABB2 min2 max2) 51 collideAABB2 box1@(AABB2 min1 max1) box2@(AABB2 min2 max2)
52 | (x max1) < (x min2) = NoCollision 52 | (x max1) < (x min2) = NoCollision
53 | (x min1) > (x max2) = NoCollision 53 | (x min1) > (x max2) = NoCollision
54 | (y max1) < (y min2) = NoCollision 54 | (y max1) < (y min2) = NoCollision
55 | (y min1) > (y max2) = NoCollision 55 | (y min1) > (y max2) = NoCollision
56 | box1 `aabb2pt` min2 && box1 `aabb2pt` max2 = FullyContains 56 | box1 `aabb2pt` min2 && box1 `aabb2pt` max2 = FullyContains
57 | box2 `aabb2pt` min1 && box2 `aabb2pt` max1 = FullyContainedBy 57 | box2 `aabb2pt` min1 && box2 `aabb2pt` max1 = FullyContainedBy
58 | otherwise = Collision 58 | otherwise = Collision
59 59
60 collideCircle circle@(Circle c r) aabb@(AABB2 min max) 60 collideCircle circle@(Circle c r) aabb@(AABB2 min max)
61 | test == FullyContains || test == FullyContainedBy = test 61 | test == FullyContains || test == FullyContainedBy = test
62 | normSq (c - boxC) > (l + r)^2 = NoCollision 62 | normSq (c - boxC) > (l + r)^2 = NoCollision
63 | otherwise = Collision 63 | otherwise = Collision
64 where 64 where
65 test = collideAABB2 aabb $ aabb2FromCircle circle 65 test = collideAABB2 aabb $ aabb2FromCircle circle
66 boxC = min + (max-min)/2 66 boxC = min + (max-min)/2
67 l = norm $ min + (vec2 (x boxC) (y min)) - min 67 l = norm $ min + (vec2 (x boxC) (y min)) - min
68 68
69instance Collisionable2 Circle where 69instance Collisionable2 Circle where
70 70
71 collideAABB2 box circle = case collideCircle circle box of 71 collideAABB2 box circle = case collideCircle circle box of
72 FullyContains -> FullyContainedBy 72 FullyContains -> FullyContainedBy
73 FullyContainedBy -> FullyContains 73 FullyContainedBy -> FullyContains
74 x -> x 74 x -> x
75 75
76 collideCircle s1@(Circle c1 r1) s2@(Circle c2 r2) 76 collideCircle s1@(Circle c1 r1) s2@(Circle c2 r2)
77 | distance_centers <= sub_radii = if (r1 > r2) then FullyContains else FullyContainedBy 77 | distance_centers <= sub_radii = if (r1 > r2) then FullyContains else FullyContainedBy
78 | distance_centers <= sum_radii = Collision 78 | distance_centers <= sum_radii = Collision
79 | otherwise = NoCollision 79 | otherwise = NoCollision
80 where 80 where
81 distance_centers = normSq $ c1 - c2 81 distance_centers = normSq $ c1 - c2
82 sum_radii = (r1 + r2)^2 82 sum_radii = (r1 + r2)^2
83 sub_radii = (r1 - r2)^2 83 sub_radii = (r1 - r2)^2
84 84
85instance Collisionable2 Collisioner2 where 85instance Collisionable2 Collisioner2 where
86 86
87 collideAABB2 box (AABB2Col self) = collideAABB2 box self 87 collideAABB2 box (AABB2Col self) = collideAABB2 box self
88 collideAABB2 box (CircleCol self) = collideAABB2 box self 88 collideAABB2 box (CircleCol self) = collideAABB2 box self
89 89
90 collideCircle circle (AABB2Col self) = collideCircle circle self 90 collideCircle circle (AABB2Col self) = collideCircle circle self
91 collideCircle circle (CircleCol self) = collideCircle circle self 91 collideCircle circle (CircleCol self) = collideCircle circle self
92 92
93aabbPoints :: AABB2 -> [Vector2] 93aabbPoints :: AABB2 -> [Vector2]
94aabbPoints (AABB2 min max) = [p1,p2,p3,p4,p5,p6,p7,p8] 94aabbPoints (AABB2 min max) = [p1,p2,p3,p4,p5,p6,p7,p8]
95 where 95 where
96 p1 = vec2 (x min) (y min) 96 p1 = vec2 (x min) (y min)
97 p2 = vec2 (x min) (y min) 97 p2 = vec2 (x min) (y min)
98 p3 = vec2 (x min) (y max) 98 p3 = vec2 (x min) (y max)
99 p4 = vec2 (x min) (y max) 99 p4 = vec2 (x min) (y max)
100 p5 = vec2 (x max) (y min) 100 p5 = vec2 (x max) (y min)
101 p6 = vec2 (x max) (y min) 101 p6 = vec2 (x max) (y min)
102 p7 = vec2 (x max) (y max) 102 p7 = vec2 (x max) (y max)
103 p8 = vec2 (x max) (y max) 103 p8 = vec2 (x max) (y max)
104 104
105 105
106-- | A collisioner component. 106-- | A collisioner component.
107data Collisioner2 107data Collisioner2
108 -- | An axis-aligned bounding box. 108 -- | An axis-aligned bounding box.
109 = AABB2Col {-# UNPACK #-} !AABB2 109 = AABB2Col {-# UNPACK #-} !AABB2
110 -- | A bounding circle. 110 -- | A bounding circle.
111 | CircleCol {-# UNPACK #-} !Circle 111 | CircleCol {-# UNPACK #-} !Circle
112 112
113 113
114-- | Create a collisioner from the specified box. 114-- | Create a collisioner from the specified box.
115aabb2Collisioner :: AABB2 -> Collisioner2 115aabb2Collisioner :: AABB2 -> Collisioner2
116aabb2Collisioner = AABB2Col 116aabb2Collisioner = AABB2Col
117 117
118-- | Create a collisioner from the specified circle. 118-- | Create a collisioner from the specified circle.
119circleCollisioner :: Circle -> Collisioner2 119circleCollisioner :: Circle -> Collisioner2
120circleCollisioner = CircleCol 120circleCollisioner = CircleCol
121 121
122-- | Compute AABB collisioners in view space from the given AABB. 122-- | Compute AABB collisioners in view space from the given AABB.
123mkCols :: M4.Matrix4 -- ^ Modelview matrix 123mkCols :: M4.Matrix4 -- ^ Modelview matrix
124 -> Box 124 -> Box
125 -> [Collisioner2] 125 -> [Collisioner2]
126mkCols modelview (Box (Vec3 xmin ymin zmin) (Vec3 xmax ymax zmax)) = 126mkCols modelview (Box (Vec3 xmin ymin zmin) (Vec3 xmax ymax zmax)) =
127 let 127 let
128 toVec2 v = vec2 (x v) (y v) 128 toVec2 v = vec2 (x v) (y v)
129 p1 = toVec2 $ modelview `M4.mulp` vec3 xmin ymin zmax 129 p1 = toVec2 $ modelview `M4.mulp` vec3 xmin ymin zmax
130 p2 = toVec2 $ modelview `M4.mulp` vec3 xmax ymin zmin 130 p2 = toVec2 $ modelview `M4.mulp` vec3 xmax ymin zmin
131 p3 = toVec2 $ modelview `M4.mulp` vec3 xmax ymax zmin 131 p3 = toVec2 $ modelview `M4.mulp` vec3 xmax ymax zmin
132 col1 = AABB2Col $ AABB2 p1 p2 132 col1 = AABB2Col $ AABB2 p1 p2
133 col2 = AABB2Col $ AABB2 p1 p3 133 col2 = AABB2Col $ AABB2 p1 p3
134 in 134 in
135 [col1, col2] 135 [col1, col2]
136 136
137-- | Create the minimal AABB fully containing the specified collisioners. 137-- | Create the minimal AABB fully containing the specified collisioners.
138buildAABB2 :: [Collisioner2] -> AABB2 138buildAABB2 :: [Collisioner2] -> AABB2
139buildAABB2 cols = aabb2 $ generatePoints cols 139buildAABB2 cols = aabb2 $ generatePoints cols
140 140
141-- | Create the minimal box fully containing the specified circle. 141-- | Create the minimal box fully containing the specified circle.
142aabb2FromCircle :: Circle -> AABB2 142aabb2FromCircle :: Circle -> AABB2
143aabb2FromCircle (Circle c r) = AABB2 bot top 143aabb2FromCircle (Circle c r) = AABB2 bot top
144 where 144 where
145 bot = c - (vec2 r r) 145 bot = c - (vec2 r r)
146 top = c + (vec2 r r) 146 top = c + (vec2 r r)
147 147
148-- | Create the minimal circle fully containing the specified box. 148-- | Create the minimal circle fully containing the specified box.
149circleFromAABB2 :: AABB2 -> Circle 149circleFromAABB2 :: AABB2 -> Circle
150circleFromAABB2 (AABB2 min max) = Circle c r 150circleFromAABB2 (AABB2 min max) = Circle c r
151 where 151 where
152 c = scale 0.5 (min + max) 152 c = scale 0.5 (min + max)
153 r = norm . scale 0.5 $ max - min 153 r = norm . scale 0.5 $ max - min
154 154
155generatePoints :: [Collisioner2] -> [Vector2] 155generatePoints :: [Collisioner2] -> [Vector2]
156generatePoints = foldl' generate [] 156generatePoints = foldl' generate []
157 where 157 where
158 generate acc (AABB2Col (AABB2 pmin pmax)) = p1:p2:p3:p4:p5:p6:p7:p8:acc 158 generate acc (AABB2Col (AABB2 pmin pmax)) = p1:p2:p3:p4:p5:p6:p7:p8:acc
159 where 159 where
160 p1 = vec2 (x pmin) (y pmin) 160 p1 = vec2 (x pmin) (y pmin)
161 p2 = vec2 (x pmin) (y pmin) 161 p2 = vec2 (x pmin) (y pmin)
162 p3 = vec2 (x pmin) (y pmax) 162 p3 = vec2 (x pmin) (y pmax)
163 p4 = vec2 (x pmin) (y pmax) 163 p4 = vec2 (x pmin) (y pmax)
164 p5 = vec2 (x pmax) (y pmin) 164 p5 = vec2 (x pmax) (y pmin)
165 p6 = vec2 (x pmax) (y pmin) 165 p6 = vec2 (x pmax) (y pmin)
166 p7 = vec2 (x pmax) (y pmax) 166 p7 = vec2 (x pmax) (y pmax)
167 p8 = vec2 (x pmax) (y pmax) 167 p8 = vec2 (x pmax) (y pmax)
168 168
169 generate acc (CircleCol (Circle c r)) = p1:p2:p3:p4:acc 169 generate acc (CircleCol (Circle c r)) = p1:p2:p3:p4:acc
170 where 170 where
171 p1 = c + unitx2 * (vec2 r r) 171 p1 = c + unitx2 * (vec2 r r)
172 p2 = c - unitx2 * (vec2 r r) 172 p2 = c - unitx2 * (vec2 r r)
173 p3 = c + unity2 * (vec2 r r) 173 p3 = c + unity2 * (vec2 r r)
174 p4 = c - unity2 * (vec2 r r) 174 p4 = c - unity2 * (vec2 r r)
175 175
176-- | Collide the given collisioners. 176-- | Collide the given collisioners.
177collide :: Collisioner2 -> Collisioner2 -> CollisionType 177collide :: Collisioner2 -> Collisioner2 -> CollisionType
178collide (AABB2Col box1) (AABB2Col box2) = collideAABB2 box1 box2 178collide (AABB2Col box1) (AABB2Col box2) = collideAABB2 box1 box2
179collide (AABB2Col box) (CircleCol circle) = collideAABB2 box circle 179collide (AABB2Col box) (CircleCol circle) = collideAABB2 box circle
180collide (CircleCol s1) (CircleCol s2) = collideCircle s1 s2 180collide (CircleCol s1) (CircleCol s2) = collideCircle s1 s2
181collide (CircleCol circle) (AABB2Col box) = collideCircle circle box 181collide (CircleCol circle) (AABB2Col box) = collideCircle circle box
182 182
183-- | Move the collisioner. 183-- | Move the collisioner.
184move :: Vector2 -> Collisioner2 -> Collisioner2 184move :: Vector2 -> Collisioner2 -> Collisioner2
185move v (AABB2Col (AABB2 min max)) = AABB2Col (AABB2 (min+v) (max+v)) 185move v (AABB2Col (AABB2 min max)) = AABB2Col (AABB2 (min+v) (max+v))
186move v (CircleCol (Circle c r)) = CircleCol (Circle (c+v) r) 186move v (CircleCol (Circle c r)) = CircleCol (Circle (c+v) r)
187 187
188 188
189-- 3D collision 189-- 3D collision
190 190
191class Collisionable3 a where 191class Collisionable3 a where
192 192
193 -- | Collide the object with an AABB. 193 -- | Collide the object with an AABB.
194 collideAABB3 :: AABB3 -> a -> CollisionType 194 collideAABB3 :: AABB3 -> a -> CollisionType
195 195
196 -- | Collide the object with a sphere. 196 -- | Collide the object with a sphere.
197 collideSphere :: Sphere -> a -> CollisionType 197 collideSphere :: Sphere -> a -> CollisionType
198 198
199instance Collisionable3 AABB3 where 199instance Collisionable3 AABB3 where
200 200
201 collideAABB3 box1@(AABB3 min1 max1) box2@(AABB3 min2 max2) 201 collideAABB3 box1@(AABB3 min1 max1) box2@(AABB3 min2 max2)
202 | (x max1) < (x min2) = NoCollision 202 | (x max1) < (x min2) = NoCollision
203 | (x min1) > (x max2) = NoCollision 203 | (x min1) > (x max2) = NoCollision
204 | (y max1) < (y min2) = NoCollision 204 | (y max1) < (y min2) = NoCollision
205 | (y min1) > (y max2) = NoCollision 205 | (y min1) > (y max2) = NoCollision
206 | box1 `aabb3pt` min2 && box1 `aabb3pt` max2 = FullyContains 206 | box1 `aabb3pt` min2 && box1 `aabb3pt` max2 = FullyContains
207 | box2 `aabb3pt` min1 && box2 `aabb3pt` max1 = FullyContainedBy 207 | box2 `aabb3pt` min1 && box2 `aabb3pt` max1 = FullyContainedBy
208 | otherwise = Collision 208 | otherwise = Collision
209 209
210 collideSphere sphere@(Sphere c r) aabb@(AABB3 min max) 210 collideSphere sphere@(Sphere c r) aabb@(AABB3 min max)
211 | test == FullyContains || test == FullyContainedBy = test 211 | test == FullyContains || test == FullyContainedBy = test
212 | normSq (c - boxC) > (l + r)^2 = NoCollision 212 | normSq (c - boxC) > (l + r)^2 = NoCollision
213 | otherwise = Collision 213 | otherwise = Collision
214 where 214 where
215 test = collideAABB3 aabb $ aabb3FromSphere sphere 215 test = collideAABB3 aabb $ aabb3FromSphere sphere
216 boxC = min + v 216 boxC = min + v
217 l = norm v 217 l = norm v
218 v = (max-min)/2 218 v = (max-min)/2
219 219
220instance Collisionable3 Sphere where 220instance Collisionable3 Sphere where
221 221
222 collideAABB3 box sphere = case collideSphere sphere box of 222 collideAABB3 box sphere = case collideSphere sphere box of
223 FullyContains -> FullyContainedBy 223 FullyContains -> FullyContainedBy
224 FullyContainedBy -> FullyContains 224 FullyContainedBy -> FullyContains
225 x -> x 225 x -> x
226 226
227 collideSphere s1@(Sphere c1 r1) s2@(Sphere c2 r2) 227 collideSphere s1@(Sphere c1 r1) s2@(Sphere c2 r2)
228 | distance_centers <= sub_radii = 228 | distance_centers <= sub_radii =
229 if (r1 > r2) then FullyContains else FullyContainedBy 229 if (r1 > r2) then FullyContains else FullyContainedBy
230 | distance_centers <= sum_radii = Collision 230 | distance_centers <= sum_radii = Collision
231 | otherwise = NoCollision 231 | otherwise = NoCollision
232 where 232 where
233 distance_centers = normSq $ c1 - c2 233 distance_centers = normSq $ c1 - c2
234 sum_radii = (r1 + r2)^2 234 sum_radii = (r1 + r2)^2
235 sub_radii = (r1 - r2)^2 235 sub_radii = (r1 - r2)^2
236 236
237-- | Create the minimal box fully containing the specified sphere. 237-- | Create the minimal box fully containing the specified sphere.
238aabb3FromSphere :: Sphere -> AABB3 238aabb3FromSphere :: Sphere -> AABB3
239aabb3FromSphere (Sphere c r) = AABB3 bot top 239aabb3FromSphere (Sphere c r) = AABB3 bot top
240 where 240 where
241 bot = c - (vec3 r r r) 241 bot = c - (vec3 r r r)
242 top = c + (vec3 r r r) \ No newline at end of file 242 top = c + (vec3 r r r) \ No newline at end of file