1 | /* graphene-frustum.c: A 3D field of view |
2 | * |
3 | * SPDX-License-Identifier: MIT |
4 | * |
5 | * Copyright 2014 Emmanuele Bassi |
6 | * |
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | * of this software and associated documentation files (the "Software"), to deal |
9 | * in the Software without restriction, including without limitation the rights |
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | * copies of the Software, and to permit persons to whom the Software is |
12 | * furnished to do so, subject to the following conditions: |
13 | * |
14 | * The above copyright notice and this permission notice shall be included in |
15 | * all copies or substantial portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
23 | * THE SOFTWARE. |
24 | */ |
25 | |
26 | /** |
27 | * SECTION:graphene-frustum |
28 | * @Title: Frustum |
29 | * @Short_Description: A 3D field of view |
30 | * |
31 | * A #graphene_frustum_t represents a volume of space delimited by planes. It |
32 | * is usually employed to represent the field of view of a camera, and can be |
33 | * used to determine whether an object falls within that view, to efficiently |
34 | * remove invisible objects from the render process. |
35 | */ |
36 | |
37 | #include "graphene-private.h" |
38 | |
39 | #include "graphene-frustum.h" |
40 | |
41 | #include "graphene-alloc-private.h" |
42 | #include "graphene-box.h" |
43 | #include "graphene-matrix.h" |
44 | #include "graphene-sphere.h" |
45 | #include "graphene-point3d.h" |
46 | #include "graphene-vec4.h" |
47 | |
48 | #define N_CLIP_PLANES 6 |
49 | |
50 | /** |
51 | * graphene_frustum_alloc: (constructor) |
52 | * |
53 | * Allocates a new #graphene_frustum_t structure. |
54 | * |
55 | * The contents of the returned structure are undefined. |
56 | * |
57 | * Returns: (transfer full): the newly allocated #graphene_frustum_t |
58 | * structure. Use graphene_frustum_free() to free the resources |
59 | * allocated by this function. |
60 | * |
61 | * Since: 1.2 |
62 | */ |
63 | graphene_frustum_t * |
64 | graphene_frustum_alloc (void) |
65 | { |
66 | return graphene_aligned_alloc0 (size: sizeof (graphene_frustum_t), number: 1, alignment: 16); |
67 | } |
68 | |
69 | /** |
70 | * graphene_frustum_free: |
71 | * @f: a #graphene_frustum_t |
72 | * |
73 | * Frees the resources allocated by graphene_frustum_alloc(). |
74 | * |
75 | * Since: 1.2 |
76 | */ |
77 | void |
78 | graphene_frustum_free (graphene_frustum_t *f) |
79 | { |
80 | graphene_aligned_free (mem: f); |
81 | } |
82 | |
83 | /** |
84 | * graphene_frustum_init: |
85 | * @f: the #graphene_frustum_t to initialize |
86 | * @p0: a clipping plane |
87 | * @p1: a clipping plane |
88 | * @p2: a clipping plane |
89 | * @p3: a clipping plane |
90 | * @p4: a clipping plane |
91 | * @p5: a clipping plane |
92 | * |
93 | * Initializes the given #graphene_frustum_t using the provided |
94 | * clipping planes. |
95 | * |
96 | * Returns: (transfer none): the initialized frustum |
97 | * |
98 | * Since: 1.2 |
99 | */ |
100 | graphene_frustum_t * |
101 | graphene_frustum_init (graphene_frustum_t *f, |
102 | const graphene_plane_t *p0, |
103 | const graphene_plane_t *p1, |
104 | const graphene_plane_t *p2, |
105 | const graphene_plane_t *p3, |
106 | const graphene_plane_t *p4, |
107 | const graphene_plane_t *p5) |
108 | { |
109 | graphene_plane_init_from_plane (p: &f->planes[0], src: p0); |
110 | graphene_plane_init_from_plane (p: &f->planes[1], src: p1); |
111 | graphene_plane_init_from_plane (p: &f->planes[2], src: p2); |
112 | graphene_plane_init_from_plane (p: &f->planes[3], src: p3); |
113 | graphene_plane_init_from_plane (p: &f->planes[4], src: p4); |
114 | graphene_plane_init_from_plane (p: &f->planes[5], src: p5); |
115 | |
116 | return f; |
117 | } |
118 | |
119 | /** |
120 | * graphene_frustum_init_from_frustum: |
121 | * @f: the #graphene_frustum_t to initialize |
122 | * @src: a #graphene_frustum_t |
123 | * |
124 | * Initializes the given #graphene_frustum_t using the clipping |
125 | * planes of another #graphene_frustum_t. |
126 | * |
127 | * Returns: (transfer none): the initialized frustum |
128 | * |
129 | * Since: 1.2 |
130 | */ |
131 | graphene_frustum_t * |
132 | graphene_frustum_init_from_frustum (graphene_frustum_t *f, |
133 | const graphene_frustum_t *src) |
134 | { |
135 | for (int i = 0; i < N_CLIP_PLANES; i++) |
136 | graphene_plane_init_from_plane (p: &f->planes[i], src: &src->planes[i]); |
137 | |
138 | return f; |
139 | } |
140 | |
141 | /** |
142 | * graphene_frustum_init_from_matrix: |
143 | * @f: a #graphene_frustum_t |
144 | * @matrix: a #graphene_matrix_t |
145 | * |
146 | * Initializes a #graphene_frustum_t using the given @matrix. |
147 | * |
148 | * Returns: (transfer none): the initialized frustum |
149 | * |
150 | * Since: 1.2 |
151 | */ |
152 | graphene_frustum_t * |
153 | graphene_frustum_init_from_matrix (graphene_frustum_t *f, |
154 | const graphene_matrix_t *matrix) |
155 | { |
156 | graphene_vec4_t r1, r2, r3, r4, t; |
157 | graphene_plane_t p; |
158 | graphene_matrix_t m; |
159 | |
160 | graphene_matrix_transpose (m: matrix, res: &m); |
161 | |
162 | graphene_matrix_get_row (m: &m, index_: 0, res: &r1); |
163 | graphene_matrix_get_row (m: &m, index_: 1, res: &r2); |
164 | graphene_matrix_get_row (m: &m, index_: 2, res: &r3); |
165 | graphene_matrix_get_row (m: &m, index_: 3, res: &r4); |
166 | |
167 | graphene_vec4_subtract (a: &r4, b: &r1, res: &t); |
168 | graphene_plane_init_from_vec4 (p: &p, src: &t); |
169 | graphene_plane_normalize (p: &p, res: &f->planes[0]); |
170 | |
171 | graphene_vec4_add (a: &r4, b: &r1, res: &t); |
172 | graphene_plane_init_from_vec4 (p: &p, src: &t); |
173 | graphene_plane_normalize (p: &p, res: &f->planes[1]); |
174 | |
175 | graphene_vec4_subtract (a: &r4, b: &r2, res: &t); |
176 | graphene_plane_init_from_vec4 (p: &p, src: &t); |
177 | graphene_plane_normalize (p: &p, res: &f->planes[2]); |
178 | |
179 | graphene_vec4_add (a: &r4, b: &r2, res: &t); |
180 | graphene_plane_init_from_vec4 (p: &p, src: &t); |
181 | graphene_plane_normalize (p: &p, res: &f->planes[3]); |
182 | |
183 | graphene_vec4_subtract (a: &r4, b: &r3, res: &t); |
184 | graphene_plane_init_from_vec4 (p: &p, src: &t); |
185 | graphene_plane_normalize (p: &p, res: &f->planes[4]); |
186 | |
187 | graphene_vec4_add (a: &r4, b: &r3, res: &t); |
188 | graphene_plane_init_from_vec4 (p: &p, src: &t); |
189 | graphene_plane_normalize (p: &p, res: &f->planes[5]); |
190 | |
191 | return f; |
192 | } |
193 | |
194 | /** |
195 | * graphene_frustum_get_planes: |
196 | * @f: a #graphene_frustum_t |
197 | * @planes: (out) (array fixed-size=6): return location for an array |
198 | * of 6 #graphene_plane_t |
199 | * |
200 | * Retrieves the planes that define the given #graphene_frustum_t. |
201 | * |
202 | * Since: 1.2 |
203 | */ |
204 | void |
205 | graphene_frustum_get_planes (const graphene_frustum_t *f, |
206 | graphene_plane_t planes[]) |
207 | { |
208 | for (int i = 0; i < N_CLIP_PLANES; i++) |
209 | graphene_plane_init_from_plane (p: &planes[i], src: &f->planes[i]); |
210 | } |
211 | |
212 | /** |
213 | * graphene_frustum_contains_point: |
214 | * @f: a #graphene_frustum_t |
215 | * @point: a #graphene_point3d_t |
216 | * |
217 | * Checks whether a point is inside the volume defined by the given |
218 | * #graphene_frustum_t. |
219 | * |
220 | * Returns: `true` if the point is inside the frustum |
221 | * |
222 | * Since: 1.2 |
223 | */ |
224 | bool |
225 | graphene_frustum_contains_point (const graphene_frustum_t *f, |
226 | const graphene_point3d_t *point) |
227 | { |
228 | if (point == NULL) |
229 | return false; |
230 | |
231 | for (int i = 0; i < N_CLIP_PLANES; i++) |
232 | { |
233 | const graphene_plane_t *p = &f->planes[i]; |
234 | |
235 | if (graphene_plane_distance (p, point) < 0) |
236 | return false; |
237 | } |
238 | |
239 | return true; |
240 | } |
241 | |
242 | /** |
243 | * graphene_frustum_intersects_sphere: |
244 | * @f: a #graphene_frustum_t |
245 | * @sphere: a #graphene_sphere_t |
246 | * |
247 | * Checks whether the given @sphere intersects a plane of |
248 | * a #graphene_frustum_t. |
249 | * |
250 | * Returns: `true` if the sphere intersects the frustum |
251 | * |
252 | * Since: 1.2 |
253 | */ |
254 | bool |
255 | graphene_frustum_intersects_sphere (const graphene_frustum_t *f, |
256 | const graphene_sphere_t *sphere) |
257 | { |
258 | graphene_point3d_t center; |
259 | |
260 | graphene_point3d_init_from_vec3 (p: ¢er, v: &sphere->center); |
261 | |
262 | for (int i = 0; i < N_CLIP_PLANES; i++) |
263 | { |
264 | float distance = graphene_plane_distance (p: &f->planes[i], point: ¢er); |
265 | |
266 | if (distance < -sphere->radius) |
267 | return false; |
268 | } |
269 | |
270 | return true; |
271 | } |
272 | |
273 | /** |
274 | * graphene_frustum_intersects_box: |
275 | * @f: a #graphene_frustum_t |
276 | * @box: a #graphene_box_t |
277 | * |
278 | * Checks whether the given @box intersects a plane of |
279 | * a #graphene_frustum_t. |
280 | * |
281 | * Returns: `true` if the box intersects the frustum |
282 | * |
283 | * Since: 1.2 |
284 | */ |
285 | bool |
286 | graphene_frustum_intersects_box (const graphene_frustum_t *f, |
287 | const graphene_box_t *box) |
288 | { |
289 | graphene_point3d_t min, max, normal; |
290 | graphene_point3d_t p0, p1; |
291 | const graphene_plane_t *plane; |
292 | float d0, d1; |
293 | |
294 | graphene_box_get_min (box, min: &min); |
295 | graphene_box_get_max (box, max: &max); |
296 | |
297 | for (int i = 0; i < N_CLIP_PLANES; i++) |
298 | { |
299 | plane = &f->planes[i]; |
300 | |
301 | graphene_point3d_init_from_vec3 (p: &normal, v: &(plane->normal)); |
302 | |
303 | p0.x = normal.x > 0 ? min.x : max.x; |
304 | p1.x = normal.x > 0 ? max.x : min.x; |
305 | |
306 | p0.y = normal.y > 0 ? min.y : max.y; |
307 | p1.y = normal.y > 0 ? max.y : min.y; |
308 | |
309 | p0.z = normal.z > 0 ? min.z : max.z; |
310 | p1.z = normal.z > 0 ? max.z : min.z; |
311 | |
312 | d0 = graphene_plane_distance (p: plane, point: &p0); |
313 | d1 = graphene_plane_distance (p: plane, point: &p1); |
314 | |
315 | if (d0 < 0 && d1 < 0) |
316 | return false; |
317 | } |
318 | |
319 | return true; |
320 | } |
321 | |
322 | static bool |
323 | frustum_equal (const void *p1, |
324 | const void *p2) |
325 | { |
326 | const graphene_frustum_t *a = p1; |
327 | const graphene_frustum_t *b = p2; |
328 | |
329 | return graphene_plane_equal (a: &a->planes[0], b: &b->planes[0]) && |
330 | graphene_plane_equal (a: &a->planes[1], b: &b->planes[1]) && |
331 | graphene_plane_equal (a: &a->planes[2], b: &b->planes[2]) && |
332 | graphene_plane_equal (a: &a->planes[3], b: &b->planes[3]) && |
333 | graphene_plane_equal (a: &a->planes[4], b: &b->planes[4]) && |
334 | graphene_plane_equal (a: &a->planes[5], b: &b->planes[5]); |
335 | } |
336 | |
337 | /** |
338 | * graphene_frustum_equal: |
339 | * @a: a #graphene_frustum_t |
340 | * @b: a #graphene_frustum_t |
341 | * |
342 | * Checks whether the two given #graphene_frustum_t are equal. |
343 | * |
344 | * Returns: `true` if the given frustums are equal |
345 | * |
346 | * Since: 1.6 |
347 | */ |
348 | bool |
349 | graphene_frustum_equal (const graphene_frustum_t *a, |
350 | const graphene_frustum_t *b) |
351 | { |
352 | return graphene_pointer_equal (p1: a, p2: b, func: frustum_equal); |
353 | } |
354 | |