diff options
Diffstat (limited to 'Spear/Math/Octree.hs')
-rw-r--r-- | Spear/Math/Octree.hs | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/Spear/Math/Octree.hs b/Spear/Math/Octree.hs new file mode 100644 index 0000000..74689a0 --- /dev/null +++ b/Spear/Math/Octree.hs | |||
@@ -0,0 +1,282 @@ | |||
1 | module Spear.Math.Octree | ||
2 | ( | ||
3 | Octree | ||
4 | , makeOctree | ||
5 | , clone | ||
6 | , Spear.Math.Octree.insert | ||
7 | , insertl | ||
8 | , Spear.Math.Octree.map | ||
9 | , gmap | ||
10 | , population | ||
11 | ) | ||
12 | where | ||
13 | |||
14 | import Spear.Collision.AABB as AABB | ||
15 | import Spear.Collision.Types | ||
16 | import Spear.Math.Vector3 as Vector | ||
17 | |||
18 | import Control.Applicative ((<*>)) | ||
19 | import Data.List | ||
20 | import Data.Functor | ||
21 | import Data.Monoid | ||
22 | import qualified Data.Foldable as F | ||
23 | |||
24 | |||
25 | -- | Represents an Octree. | ||
26 | data Octree e | ||
27 | = Octree | ||
28 | { | ||
29 | root :: AABB, | ||
30 | ents :: [e], | ||
31 | c1 :: Octree e, | ||
32 | c2 :: Octree e, | ||
33 | c3 :: Octree e, | ||
34 | c4 :: Octree e, | ||
35 | c5 :: Octree e, | ||
36 | c6 :: Octree e, | ||
37 | c7 :: Octree e, | ||
38 | c8 :: Octree e | ||
39 | } | ||
40 | | | ||
41 | Leaf | ||
42 | { | ||
43 | root :: AABB, | ||
44 | ents :: [e] | ||
45 | } | ||
46 | |||
47 | |||
48 | -- | Builds an Octree using the specified AABB as the root and having the specified depth. | ||
49 | makeOctree :: Int -> AABB -> Octree e | ||
50 | makeOctree d root@(AABB min max) | ||
51 | | d == 0 = Leaf root [] | ||
52 | | otherwise = Octree root [] c1 c2 c3 c4 c5 c6 c7 c8 | ||
53 | where | ||
54 | boxes = subdivide root | ||
55 | c1 = makeOctree (d-1) $ boxes !! 0 | ||
56 | c2 = makeOctree (d-1) $ boxes !! 1 | ||
57 | c3 = makeOctree (d-1) $ boxes !! 2 | ||
58 | c4 = makeOctree (d-1) $ boxes !! 3 | ||
59 | c5 = makeOctree (d-1) $ boxes !! 4 | ||
60 | c6 = makeOctree (d-1) $ boxes !! 5 | ||
61 | c7 = makeOctree (d-1) $ boxes !! 6 | ||
62 | c8 = makeOctree (d-1) $ boxes !! 7 | ||
63 | |||
64 | |||
65 | subdivide :: AABB -> [AABB] | ||
66 | subdivide (AABB min max) = [a1, a2, a3, a4, a5, a6, a7, a8] | ||
67 | where | ||
68 | v = (max-min) / 2 | ||
69 | c = vec3 (x min + x v) (y min + y v) (z min + z v) | ||
70 | a1 = AABB min c | ||
71 | a2 = AABB ( vec3 (x min) (y min) (z c) ) ( vec3 (x c) (y c) (z max) ) | ||
72 | a3 = AABB ( vec3 (x min) (y c) (z min) ) ( vec3 (x c) (y max) (z c) ) | ||
73 | a4 = AABB ( vec3 (x min) (y c) (z c) ) ( vec3 (x c) (y max) (z max) ) | ||
74 | a5 = AABB ( vec3 (x c) (y min) (z min) ) ( vec3 (x max) (y c) (z c) ) | ||
75 | a6 = AABB ( vec3 (x c) (y min) (z c) ) ( vec3 (x max) (y c) (z max) ) | ||
76 | a7 = AABB ( vec3 (x c) (y c) (z min) ) ( vec3 (x max) (y max) (z c) ) | ||
77 | a8 = AABB c max | ||
78 | |||
79 | |||
80 | -- | Clones the structure of an octree. The new octree has no entities. | ||
81 | clone :: Octree e -> Octree e | ||
82 | clone (Leaf root ents) = Leaf root [] | ||
83 | clone (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = Octree root [] c1' c2' c3' c4' c5' c6' c7' c8' | ||
84 | where | ||
85 | c1' = clone c1 | ||
86 | c2' = clone c2 | ||
87 | c3' = clone c3 | ||
88 | c4' = clone c4 | ||
89 | c5' = clone c5 | ||
90 | c6' = clone c6 | ||
91 | c7' = clone c7 | ||
92 | c8' = clone c8 | ||
93 | |||
94 | |||
95 | keep :: (e -> AABB -> CollisionType) -> AABB -> e -> Bool | ||
96 | keep testAABB aabb e = test == FullyContainedBy || test == Equal | ||
97 | where test = e `testAABB` aabb | ||
98 | |||
99 | |||
100 | -- | Inserts an entity into the given octree. | ||
101 | insert :: (e -> AABB -> CollisionType) -> Octree e -> e -> Octree e | ||
102 | insert testAABB octree e = octree' where (octree', _) = insert' testAABB e octree | ||
103 | |||
104 | |||
105 | insert' :: (e -> AABB -> CollisionType) -> e -> Octree e -> (Octree e, Bool) | ||
106 | |||
107 | |||
108 | insert' testAABB e l@(Leaf root ents) | ||
109 | | test == True = (Leaf root (e:ents), True) | ||
110 | | otherwise = (l, False) | ||
111 | where | ||
112 | test = keep testAABB root e | ||
113 | |||
114 | |||
115 | insert' testAABB e o@(Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) | ||
116 | | test == False = (o, False) | ||
117 | | otherwise = | ||
118 | if isContainedInChild then (Octree root ents c1' c2' c3' c4' c5' c6' c7' c8', True) | ||
119 | else (Octree root (e:ents) c1 c2 c3 c4 c5 c6 c7 c8, True) | ||
120 | where | ||
121 | children = [c1,c2,c3,c4,c5,c6,c7,c8] | ||
122 | test = keep testAABB root e | ||
123 | descend = fmap (Spear.Math.Octree.insert' testAABB e) children | ||
124 | (children', results) = unzip descend | ||
125 | isContainedInChild = or results | ||
126 | c1' = children' !! 0 | ||
127 | c2' = children' !! 1 | ||
128 | c3' = children' !! 2 | ||
129 | c4' = children' !! 3 | ||
130 | c5' = children' !! 4 | ||
131 | c6' = children' !! 5 | ||
132 | c7' = children' !! 6 | ||
133 | c8' = children' !! 7 | ||
134 | |||
135 | |||
136 | -- | Inserts a list of entities into the given octree. | ||
137 | insertl :: (e -> AABB -> CollisionType) -> Octree e -> [e] -> Octree e | ||
138 | insertl testAABB octree es = octree' where (octree', _) = insertl' testAABB es octree | ||
139 | |||
140 | |||
141 | insertl' :: (e -> AABB -> CollisionType) -> [e] -> Octree e -> (Octree e, [e]) | ||
142 | |||
143 | insertl' testAABB es (Leaf root ents) = (Leaf root ents', outliers) | ||
144 | where | ||
145 | ents' = ents ++ ents_kept | ||
146 | ents_kept = filter (keep testAABB root) es | ||
147 | outliers = filter (not . keep testAABB root) es | ||
148 | |||
149 | insertl' testAABB es (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
150 | (Octree root ents' c1' c2' c3' c4' c5' c6' c7' c8', outliers) | ||
151 | where | ||
152 | ents' = ents ++ ents_kept | ||
153 | new_ents = es ++ ents1 ++ ents2 ++ ents3 ++ ents4 ++ ents5 ++ ents6 ++ ents7 ++ ents8 | ||
154 | ents_kept = filter (keep testAABB root) new_ents | ||
155 | outliers = filter (not . keep testAABB root) new_ents | ||
156 | (c1', ents1) = insertl' testAABB es c1 | ||
157 | (c2', ents2) = insertl' testAABB es c2 | ||
158 | (c3', ents3) = insertl' testAABB es c3 | ||
159 | (c4', ents4) = insertl' testAABB es c4 | ||
160 | (c5', ents5) = insertl' testAABB es c5 | ||
161 | (c6', ents6) = insertl' testAABB es c6 | ||
162 | (c7', ents7) = insertl' testAABB es c7 | ||
163 | (c8', ents8) = insertl' testAABB es c8 | ||
164 | |||
165 | |||
166 | -- | Extracts all entities from an octree. The resulting octree has no entities. | ||
167 | extract :: Octree e -> (Octree e, [e]) | ||
168 | extract (Leaf root ents) = (Leaf root [], ents) | ||
169 | extract (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = (Octree root [] c1' c2' c3' c4' c5' c6' c7' c8', ents') | ||
170 | where | ||
171 | (c1', ents1) = extract c1 | ||
172 | (c2', ents2) = extract c2 | ||
173 | (c3', ents3) = extract c3 | ||
174 | (c4', ents4) = extract c4 | ||
175 | (c5', ents5) = extract c5 | ||
176 | (c6', ents6) = extract c6 | ||
177 | (c7', ents7) = extract c7 | ||
178 | (c8', ents8) = extract c8 | ||
179 | ents' = ents ++ ents1 ++ ents2 ++ ents3 ++ ents4 ++ ents5 ++ ents6 ++ ents7 ++ ents8 | ||
180 | |||
181 | |||
182 | -- | Applies the given function to the entities in the octree. | ||
183 | -- Entities that break out of their cell are reallocated appropiately. | ||
184 | map :: (e -> AABB -> CollisionType) -> (e -> e) -> Octree e -> Octree e | ||
185 | map testAABB f o = let (o', outliers) = map' testAABB f o in insertl testAABB o' outliers | ||
186 | |||
187 | |||
188 | map' :: (e -> AABB -> CollisionType) -> (e -> e) -> Octree e -> (Octree e, [e]) | ||
189 | |||
190 | |||
191 | map' testAABB f (Leaf root ents) = (Leaf root ents_kept, outliers) | ||
192 | where | ||
193 | ents' = fmap f ents | ||
194 | ents_kept = filter (keep testAABB root) ents' | ||
195 | outliers = filter (not . keep testAABB root) ents' | ||
196 | |||
197 | |||
198 | map' testAABB f (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
199 | (Octree root ents_kept c1' c2' c3' c4' c5' c6' c7' c8', outliers) | ||
200 | where | ||
201 | ents' = (fmap f ents) ++ out1 ++ out2 ++ out3 ++ out4 ++ out5 ++ out6 ++ out7 ++ out8 | ||
202 | ents_kept = filter (keep testAABB root) ents' | ||
203 | outliers = filter (not . keep testAABB root) ents' | ||
204 | (c1', out1) = map' testAABB f c1 | ||
205 | (c2', out2) = map' testAABB f c2 | ||
206 | (c3', out3) = map' testAABB f c3 | ||
207 | (c4', out4) = map' testAABB f c4 | ||
208 | (c5', out5) = map' testAABB f c5 | ||
209 | (c6', out6) = map' testAABB f c6 | ||
210 | (c7', out7) = map' testAABB f c7 | ||
211 | (c8', out8) = map' testAABB f c8 | ||
212 | |||
213 | |||
214 | -- | Applies a function to the entity groups in the octree. | ||
215 | -- Entities that break out of their cell are reallocated appropiately. | ||
216 | gmap :: (e -> AABB -> CollisionType) -> (e -> e -> e) -> Octree e -> Octree e | ||
217 | gmap testAABB f o = let (o', outliers) = gmap' testAABB f o in insertl testAABB o' outliers | ||
218 | |||
219 | |||
220 | gmap' :: (e -> AABB -> CollisionType) -> (e -> e -> e) -> Octree e -> (Octree e, [e]) | ||
221 | |||
222 | gmap' testAABB f (Leaf root ents) = (Leaf root ents_kept, outliers) | ||
223 | where | ||
224 | ents' = f <$> ents <*> ents | ||
225 | ents_kept = filter (keep testAABB root) ents' | ||
226 | outliers = filter (not . keep testAABB root) ents' | ||
227 | |||
228 | gmap' testAABB f (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
229 | (Octree root ents_kept c1' c2' c3' c4' c5' c6' c7' c8', outliers) | ||
230 | where | ||
231 | ents' = (f <$> ents <*> ents) ++ out1 ++ out2 ++ out3 ++ out4 ++ out5 ++ out6 ++ out7 ++ out8 | ||
232 | ents_kept = filter (keep testAABB root) ents' | ||
233 | outliers = filter (not . keep testAABB root) ents' | ||
234 | (c1', out1) = gmap' testAABB f c1 | ||
235 | (c2', out2) = gmap' testAABB f c2 | ||
236 | (c3', out3) = gmap' testAABB f c3 | ||
237 | (c4', out4) = gmap' testAABB f c4 | ||
238 | (c5', out5) = gmap' testAABB f c5 | ||
239 | (c6', out6) = gmap' testAABB f c6 | ||
240 | (c7', out7) = gmap' testAABB f c7 | ||
241 | (c8', out8) = gmap' testAABB f c8 | ||
242 | |||
243 | |||
244 | population :: Octree e -> Int | ||
245 | population = F.foldr (\_ acc -> acc+1) 0 | ||
246 | |||
247 | |||
248 | |||
249 | |||
250 | instance Functor Octree where | ||
251 | fmap f (Leaf root ents) = Leaf root $ fmap f ents | ||
252 | |||
253 | fmap f (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
254 | Octree root (fmap f ents) c1' c2' c3' c4' c5' c6' c7' c8' | ||
255 | where | ||
256 | c1' = fmap f c1 | ||
257 | c2' = fmap f c2 | ||
258 | c3' = fmap f c3 | ||
259 | c4' = fmap f c4 | ||
260 | c5' = fmap f c5 | ||
261 | c6' = fmap f c6 | ||
262 | c7' = fmap f c7 | ||
263 | c8' = fmap f c8 | ||
264 | |||
265 | |||
266 | |||
267 | instance F.Foldable Octree where | ||
268 | foldMap f (Leaf root ents) = mconcat . fmap f $ ents | ||
269 | |||
270 | foldMap f (Octree root ents c1 c2 c3 c4 c5 c6 c7 c8) = | ||
271 | mconcat (fmap f ents) `mappend` | ||
272 | c1' `mappend` c2' `mappend` c3' `mappend` c4' `mappend` | ||
273 | c5' `mappend` c6' `mappend` c7' `mappend` c8' | ||
274 | where | ||
275 | c1' = F.foldMap f c1 | ||
276 | c2' = F.foldMap f c2 | ||
277 | c3' = F.foldMap f c3 | ||
278 | c4' = F.foldMap f c4 | ||
279 | c5' = F.foldMap f c5 | ||
280 | c6' = F.foldMap f c6 | ||
281 | c7' = F.foldMap f c7 | ||
282 | c8' = F.foldMap f c8 | ||