4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2010 Intel Corporation.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see
20 * <http://www.gnu.org/licenses/>.
23 * Robert Bragg <robert@linux.intel.com>
24 * Emmanuele Bassi <ebassi@linux.intel.com>
33 #include <glib-object.h>
36 #include "clutter-actor-private.h"
37 #include "clutter-paint-volume-private.h"
38 #include "clutter-private.h"
39 #include "clutter-stage-private.h"
41 G_DEFINE_BOXED_TYPE (ClutterPaintVolume, clutter_paint_volume,
42 clutter_paint_volume_copy,
43 clutter_paint_volume_free);
46 * _clutter_paint_volume_new:
47 * @actor: a #ClutterActor
49 * Creates a new #ClutterPaintVolume for the given @actor.
51 * Return value: the newly allocated #ClutterPaintVolume. Use
52 * clutter_paint_volume_free() to free the resources it uses
57 _clutter_paint_volume_new (ClutterActor *actor)
59 ClutterPaintVolume *pv;
61 g_return_val_if_fail (actor != NULL, NULL);
63 pv = g_slice_new (ClutterPaintVolume);
67 memset (pv->vertices, 0, 8 * sizeof (ClutterVertex));
69 pv->is_static = FALSE;
71 pv->is_axis_aligned = TRUE;
72 pv->is_complete = TRUE;
78 /* Since paint volumes are used so heavily in a typical paint
79 * traversal of a Clutter scene graph and since paint volumes often
80 * have a very short life cycle that maps well to stack allocation we
81 * allow initializing a static ClutterPaintVolume variable to avoid
82 * hammering the slice allocator.
84 * We were seeing slice allocation take about 1% cumulative CPU time
85 * for some very simple clutter tests which although it isn't a *lot*
86 * this is an easy way to basically drop that to 0%.
88 * The PaintVolume will be internally marked as static and
89 * clutter_paint_volume_free should still be used to "free" static
90 * volumes. This allows us to potentially store dynamically allocated
91 * data inside paint volumes in the future since we would be able to
92 * free it during _paint_volume_free().
95 _clutter_paint_volume_init_static (ClutterPaintVolume *pv,
100 memset (pv->vertices, 0, 8 * sizeof (ClutterVertex));
102 pv->is_static = TRUE;
104 pv->is_axis_aligned = TRUE;
105 pv->is_complete = TRUE;
110 _clutter_paint_volume_copy_static (const ClutterPaintVolume *src_pv,
111 ClutterPaintVolume *dst_pv)
114 g_return_if_fail (src_pv != NULL && dst_pv != NULL);
116 memcpy (dst_pv, src_pv, sizeof (ClutterPaintVolume));
117 dst_pv->is_static = TRUE;
121 * clutter_paint_volume_copy:
122 * @pv: a #ClutterPaintVolume
124 * Copies @pv into a new #ClutterPaintVolume
126 * Return value: a newly allocated copy of a #ClutterPaintVolume
131 clutter_paint_volume_copy (const ClutterPaintVolume *pv)
133 ClutterPaintVolume *copy;
135 g_return_val_if_fail (pv != NULL, NULL);
137 copy = g_slice_dup (ClutterPaintVolume, pv);
138 copy->is_static = FALSE;
144 _clutter_paint_volume_set_from_volume (ClutterPaintVolume *pv,
145 const ClutterPaintVolume *src)
147 gboolean is_static = pv->is_static;
148 memcpy (pv, src, sizeof (ClutterPaintVolume));
149 pv->is_static = is_static;
153 * clutter_paint_volume_free:
154 * @pv: a #ClutterPaintVolume
156 * Frees the resources allocated by @pv
161 clutter_paint_volume_free (ClutterPaintVolume *pv)
163 g_return_if_fail (pv != NULL);
165 if (G_LIKELY (pv->is_static))
168 g_slice_free (ClutterPaintVolume, pv);
172 * clutter_paint_volume_set_origin:
173 * @pv: a #ClutterPaintVolume
174 * @origin: a #ClutterVertex
176 * Sets the origin of the paint volume.
178 * The origin is defined as the X, Y and Z coordinates of the top-left
179 * corner of an actor's paint volume, in actor coordinates.
181 * The default is origin is assumed at: (0, 0, 0)
186 clutter_paint_volume_set_origin (ClutterPaintVolume *pv,
187 const ClutterVertex *origin)
189 static const int key_vertices[4] = { 0, 1, 3, 4 };
193 g_return_if_fail (pv != NULL);
195 dx = origin->x - pv->vertices[0].x;
196 dy = origin->y - pv->vertices[0].y;
197 dz = origin->z - pv->vertices[0].z;
199 /* If we change the origin then all the key vertices of the paint
200 * volume need to be shifted too... */
201 for (i = 0; i < 4; i++)
203 pv->vertices[key_vertices[i]].x += dx;
204 pv->vertices[key_vertices[i]].y += dy;
205 pv->vertices[key_vertices[i]].z += dz;
208 pv->is_complete = FALSE;
212 * clutter_paint_volume_get_origin:
213 * @pv: a #ClutterPaintVolume
214 * @vertex: (out): the return location for a #ClutterVertex
216 * Retrieves the origin of the #ClutterPaintVolume.
221 clutter_paint_volume_get_origin (const ClutterPaintVolume *pv,
222 ClutterVertex *vertex)
224 g_return_if_fail (pv != NULL);
225 g_return_if_fail (vertex != NULL);
227 *vertex = pv->vertices[0];
231 _clutter_paint_volume_update_is_empty (ClutterPaintVolume *pv)
233 if (pv->vertices[0].x == pv->vertices[1].x &&
234 pv->vertices[0].y == pv->vertices[3].y &&
235 pv->vertices[0].z == pv->vertices[4].z)
238 pv->is_empty = FALSE;
242 * clutter_paint_volume_set_width:
243 * @pv: a #ClutterPaintVolume
244 * @width: the width of the paint volume, in pixels
246 * Sets the width of the paint volume. The width is measured along
247 * the x axis in the actor coordinates that @pv is associated with.
252 clutter_paint_volume_set_width (ClutterPaintVolume *pv,
257 g_return_if_fail (pv != NULL);
258 g_return_if_fail (width >= 0.0f);
260 /* If the volume is currently empty then only the origin is
263 pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0];
265 if (!pv->is_axis_aligned)
266 _clutter_paint_volume_axis_align (pv);
268 right_xpos = pv->vertices[0].x + width;
270 /* Move the right vertices of the paint box relative to the
272 pv->vertices[1].x = right_xpos;
273 /* pv->vertices[2].x = right_xpos; NB: updated lazily */
274 /* pv->vertices[5].x = right_xpos; NB: updated lazily */
275 /* pv->vertices[6].x = right_xpos; NB: updated lazily */
277 pv->is_complete = FALSE;
279 _clutter_paint_volume_update_is_empty (pv);
283 * clutter_paint_volume_get_width:
284 * @pv: a #ClutterPaintVolume
286 * Retrieves the width of the volume's, axis aligned, bounding box.
288 * In other words; this takes into account what actor's coordinate
289 * space @pv belongs too and conceptually fits an axis aligned box
290 * around the volume. It returns the size of that bounding box as
291 * measured along the x-axis.
293 * <note><para>If, for example, clutter_actor_get_transformed_paint_volume()
294 * is used to transform a 2D child actor that is 100px wide, 100px
295 * high and 0px deep into container coordinates then the width might
296 * not simply be 100px if the child actor has a 3D rotation applied to
298 * <para>Remember; after clutter_actor_get_transformed_paint_volume() is
299 * used then a transformed child volume will be defined relative to the
300 * ancestor container actor and so a 2D child actor
301 * can have a 3D bounding volume.</para></note>
303 * <note>There are no accuracy guarantees for the reported width,
304 * except that it must always be >= to the true width. This is
305 * because actors may report simple, loose fitting paint-volumes
306 * for efficiency</note>
308 * Return value: the width, in units of @pv's local coordinate system.
313 clutter_paint_volume_get_width (const ClutterPaintVolume *pv)
315 g_return_val_if_fail (pv != NULL, 0.0);
319 else if (!pv->is_axis_aligned)
321 ClutterPaintVolume tmp;
323 _clutter_paint_volume_copy_static (pv, &tmp);
324 _clutter_paint_volume_axis_align (&tmp);
325 width = tmp.vertices[1].x - tmp.vertices[0].x;
326 clutter_paint_volume_free (&tmp);
330 return pv->vertices[1].x - pv->vertices[0].x;
334 * clutter_paint_volume_set_height:
335 * @pv: a #ClutterPaintVolume
336 * @height: the height of the paint volume, in pixels
338 * Sets the height of the paint volume. The height is measured along
339 * the y axis in the actor coordinates that @pv is associated with.
344 clutter_paint_volume_set_height (ClutterPaintVolume *pv,
349 g_return_if_fail (pv != NULL);
350 g_return_if_fail (height >= 0.0f);
352 /* If the volume is currently empty then only the origin is
355 pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0];
357 if (!pv->is_axis_aligned)
358 _clutter_paint_volume_axis_align (pv);
360 height_ypos = pv->vertices[0].y + height;
362 /* Move the bottom vertices of the paint box relative to the
364 /* pv->vertices[2].y = height_ypos; NB: updated lazily */
365 pv->vertices[3].y = height_ypos;
366 /* pv->vertices[6].y = height_ypos; NB: updated lazily */
367 /* pv->vertices[7].y = height_ypos; NB: updated lazily */
368 pv->is_complete = FALSE;
370 _clutter_paint_volume_update_is_empty (pv);
374 * clutter_paint_volume_get_height:
375 * @pv: a #ClutterPaintVolume
377 * Retrieves the height of the volume's, axis aligned, bounding box.
379 * In other words; this takes into account what actor's coordinate
380 * space @pv belongs too and conceptually fits an axis aligned box
381 * around the volume. It returns the size of that bounding box as
382 * measured along the y-axis.
384 * <note><para>If, for example, clutter_actor_get_transformed_paint_volume()
385 * is used to transform a 2D child actor that is 100px wide, 100px
386 * high and 0px deep into container coordinates then the height might
387 * not simply be 100px if the child actor has a 3D rotation applied to
389 * <para>Remember; after clutter_actor_get_transformed_paint_volume() is
390 * used then a transformed child volume will be defined relative to the
391 * ancestor container actor and so a 2D child actor
392 * can have a 3D bounding volume.</para></note>
394 * <note>There are no accuracy guarantees for the reported height,
395 * except that it must always be >= to the true height. This is
396 * because actors may report simple, loose fitting paint-volumes
397 * for efficiency</note>
399 * Return value: the height, in units of @pv's local coordinate system.
404 clutter_paint_volume_get_height (const ClutterPaintVolume *pv)
406 g_return_val_if_fail (pv != NULL, 0.0);
410 else if (!pv->is_axis_aligned)
412 ClutterPaintVolume tmp;
414 _clutter_paint_volume_copy_static (pv, &tmp);
415 _clutter_paint_volume_axis_align (&tmp);
416 height = tmp.vertices[3].y - tmp.vertices[0].y;
417 clutter_paint_volume_free (&tmp);
421 return pv->vertices[3].y - pv->vertices[0].y;
425 * clutter_paint_volume_set_depth:
426 * @pv: a #ClutterPaintVolume
427 * @depth: the depth of the paint volume, in pixels
429 * Sets the depth of the paint volume. The depth is measured along
430 * the z axis in the actor coordinates that @pv is associated with.
435 clutter_paint_volume_set_depth (ClutterPaintVolume *pv,
440 g_return_if_fail (pv != NULL);
441 g_return_if_fail (depth >= 0.0f);
443 /* If the volume is currently empty then only the origin is
446 pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0];
448 if (!pv->is_axis_aligned)
449 _clutter_paint_volume_axis_align (pv);
451 depth_zpos = pv->vertices[0].z + depth;
453 /* Move the back vertices of the paint box relative to the
455 pv->vertices[4].z = depth_zpos;
456 /* pv->vertices[5].z = depth_zpos; NB: updated lazily */
457 /* pv->vertices[6].z = depth_zpos; NB: updated lazily */
458 /* pv->vertices[7].z = depth_zpos; NB: updated lazily */
460 pv->is_complete = FALSE;
461 pv->is_2d = depth ? FALSE : TRUE;
462 _clutter_paint_volume_update_is_empty (pv);
466 * clutter_paint_volume_get_depth:
467 * @pv: a #ClutterPaintVolume
469 * Retrieves the depth of the volume's, axis aligned, bounding box.
471 * In other words; this takes into account what actor's coordinate
472 * space @pv belongs too and conceptually fits an axis aligned box
473 * around the volume. It returns the size of that bounding box as
474 * measured along the z-axis.
476 * <note><para>If, for example, clutter_actor_get_transformed_paint_volume()
477 * is used to transform a 2D child actor that is 100px wide, 100px
478 * high and 0px deep into container coordinates then the depth might
479 * not simply be 0px if the child actor has a 3D rotation applied to
481 * <para>Remember; after clutter_actor_get_transformed_paint_volume() is
482 * used then the transformed volume will be defined relative to the
483 * container actor and in container coordinates a 2D child actor
484 * can have a 3D bounding volume.</para></note>
486 * <note>There are no accuracy guarantees for the reported depth,
487 * except that it must always be >= to the true depth. This is
488 * because actors may report simple, loose fitting paint-volumes
489 * for efficiency.</note>
491 * Return value: the depth, in units of @pv's local coordinate system.
496 clutter_paint_volume_get_depth (const ClutterPaintVolume *pv)
498 g_return_val_if_fail (pv != NULL, 0.0);
502 else if (!pv->is_axis_aligned)
504 ClutterPaintVolume tmp;
506 _clutter_paint_volume_copy_static (pv, &tmp);
507 _clutter_paint_volume_axis_align (&tmp);
508 depth = tmp.vertices[4].z - tmp.vertices[0].z;
509 clutter_paint_volume_free (&tmp);
513 return pv->vertices[4].z - pv->vertices[0].z;
517 * clutter_paint_volume_union:
518 * @pv: The first #ClutterPaintVolume and destination for resulting
520 * @another_pv: A second #ClutterPaintVolume to union with @pv
522 * Updates the geometry of @pv to encompass @pv and @another_pv.
524 * <note>There are no guarantees about how precisely the two volumes
525 * will be encompassed.</note>
530 clutter_paint_volume_union (ClutterPaintVolume *pv,
531 const ClutterPaintVolume *another_pv)
533 ClutterPaintVolume aligned_pv;
535 g_return_if_fail (pv != NULL);
536 g_return_if_fail (another_pv != NULL);
538 /* Both volumes have to belong to the same local coordinate space */
539 g_return_if_fail (pv->actor == another_pv->actor);
541 /* NB: we only have to update vertices 0, 1, 3 and 4
542 * (See the ClutterPaintVolume typedef for more details) */
544 /* We special case empty volumes because otherwise we'd end up
545 * calculating a bounding box that would enclose the origin of
546 * the empty volume which isn't desired.
548 if (another_pv->is_empty)
553 _clutter_paint_volume_set_from_volume (pv, another_pv);
557 if (!pv->is_axis_aligned)
558 _clutter_paint_volume_axis_align (pv);
560 if (!another_pv->is_axis_aligned)
562 _clutter_paint_volume_copy_static (another_pv, &aligned_pv);
563 _clutter_paint_volume_axis_align (&aligned_pv);
564 another_pv = &aligned_pv;
568 /* left vertices 0, 3, 4, 7 */
569 if (another_pv->vertices[0].x < pv->vertices[0].x)
571 int min_x = another_pv->vertices[0].x;
572 pv->vertices[0].x = min_x;
573 pv->vertices[3].x = min_x;
574 pv->vertices[4].x = min_x;
575 /* pv->vertices[7].x = min_x; */
579 /* right vertices 1, 2, 5, 6 */
580 if (another_pv->vertices[1].x > pv->vertices[1].x)
582 int max_x = another_pv->vertices[1].x;
583 pv->vertices[1].x = max_x;
584 /* pv->vertices[2].x = max_x; */
585 /* pv->vertices[5].x = max_x; */
586 /* pv->vertices[6].x = max_x; */
590 /* top vertices 0, 1, 4, 5 */
591 if (another_pv->vertices[0].y < pv->vertices[0].y)
593 int min_y = another_pv->vertices[0].y;
594 pv->vertices[0].y = min_y;
595 pv->vertices[1].y = min_y;
596 pv->vertices[4].y = min_y;
597 /* pv->vertices[5].y = min_y; */
601 /* bottom vertices 2, 3, 6, 7 */
602 if (another_pv->vertices[3].y > pv->vertices[3].y)
604 int may_y = another_pv->vertices[3].y;
605 /* pv->vertices[2].y = may_y; */
606 pv->vertices[3].y = may_y;
607 /* pv->vertices[6].y = may_y; */
608 /* pv->vertices[7].y = may_y; */
612 /* front vertices 0, 1, 2, 3 */
613 if (another_pv->vertices[0].z < pv->vertices[0].z)
615 int min_z = another_pv->vertices[0].z;
616 pv->vertices[0].z = min_z;
617 pv->vertices[1].z = min_z;
618 /* pv->vertices[2].z = min_z; */
619 pv->vertices[3].z = min_z;
623 /* back vertices 4, 5, 6, 7 */
624 if (another_pv->vertices[4].z > pv->vertices[4].z)
626 int maz_z = another_pv->vertices[4].z;
627 pv->vertices[4].z = maz_z;
628 /* pv->vertices[5].z = maz_z; */
629 /* pv->vertices[6].z = maz_z; */
630 /* pv->vertices[7].z = maz_z; */
633 if (pv->vertices[4].z == pv->vertices[0].z)
639 pv->is_empty = FALSE;
640 pv->is_complete = FALSE;
644 * clutter_paint_volume_union_box:
645 * @pv: a #ClutterPaintVolume
646 * @box: a #ClutterActorBox to union to @pv
648 * Unions the 2D region represented by @box to a #ClutterPaintVolume.
650 * This function is similar to clutter_paint_volume_union(), but it is
651 * specific for 2D regions.
656 clutter_paint_volume_union_box (ClutterPaintVolume *pv,
657 const ClutterActorBox *box)
659 ClutterPaintVolume volume;
660 ClutterVertex origin;
662 g_return_if_fail (pv != NULL);
663 g_return_if_fail (box != NULL);
665 _clutter_paint_volume_init_static (&volume, pv->actor);
670 clutter_paint_volume_set_origin (&volume, &origin);
671 clutter_paint_volume_set_width (&volume, box->x2 - box->x1);
672 clutter_paint_volume_set_height (&volume, box->y2 - box->y1);
674 clutter_paint_volume_union (pv, &volume);
676 clutter_paint_volume_free (&volume);
679 /* The paint_volume setters only update vertices 0, 1, 3 and
680 * 4 since the others can be drived from them.
682 * This will set pv->completed = TRUE;
685 _clutter_paint_volume_complete (ClutterPaintVolume *pv)
687 float dx_l2r, dy_l2r, dz_l2r;
688 float dx_t2b, dy_t2b, dz_t2b;
696 /* Find the vector that takes us from any vertex on the left face to
697 * the corresponding vertex on the right face. */
698 dx_l2r = pv->vertices[1].x - pv->vertices[0].x;
699 dy_l2r = pv->vertices[1].y - pv->vertices[0].y;
700 dz_l2r = pv->vertices[1].z - pv->vertices[0].z;
702 /* Find the vector that takes us from any vertex on the top face to
703 * the corresponding vertex on the bottom face. */
704 dx_t2b = pv->vertices[3].x - pv->vertices[0].x;
705 dy_t2b = pv->vertices[3].y - pv->vertices[0].y;
706 dz_t2b = pv->vertices[3].z - pv->vertices[0].z;
708 /* front-bottom-right */
709 pv->vertices[2].x = pv->vertices[3].x + dx_l2r;
710 pv->vertices[2].y = pv->vertices[3].y + dy_l2r;
711 pv->vertices[2].z = pv->vertices[3].z + dz_l2r;
713 if (G_UNLIKELY (!pv->is_2d))
716 pv->vertices[5].x = pv->vertices[4].x + dx_l2r;
717 pv->vertices[5].y = pv->vertices[4].y + dy_l2r;
718 pv->vertices[5].z = pv->vertices[4].z + dz_l2r;
720 /* back-bottom-right */
721 pv->vertices[6].x = pv->vertices[5].x + dx_t2b;
722 pv->vertices[6].y = pv->vertices[5].y + dy_t2b;
723 pv->vertices[6].z = pv->vertices[5].z + dz_t2b;
725 /* back-bottom-left */
726 pv->vertices[7].x = pv->vertices[4].x + dx_t2b;
727 pv->vertices[7].y = pv->vertices[4].y + dy_t2b;
728 pv->vertices[7].z = pv->vertices[4].z + dz_t2b;
731 pv->is_complete = TRUE;
735 * _clutter_paint_volume_get_box:
736 * @pv: a #ClutterPaintVolume
737 * @box: a pixel aligned #ClutterGeometry
739 * Transforms a 3D paint volume into a 2D bounding box in the
740 * same coordinate space as the 3D paint volume.
742 * To get an actors "paint box" you should first project
743 * the paint volume into window coordinates before getting
744 * the 2D bounding box.
746 * <note>The coordinates of the returned box are not clamped to
747 * integer pixel values, if you need them to be clamped you can use
748 * clutter_actor_box_clamp_to_pixel()</note>
753 _clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv,
754 ClutterActorBox *box)
756 gfloat x_min, y_min, x_max, y_max;
757 ClutterVertex *vertices;
761 g_return_if_fail (pv != NULL);
762 g_return_if_fail (box != NULL);
766 box->x1 = box->x2 = pv->vertices[0].x;
767 box->y1 = box->y2 = pv->vertices[0].y;
771 /* Updates the vertices we calculate lazily
772 * (See ClutterPaintVolume typedef for more details) */
773 _clutter_paint_volume_complete (pv);
775 vertices = pv->vertices;
777 x_min = x_max = vertices[0].x;
778 y_min = y_max = vertices[0].y;
780 /* Most actors are 2D so we only have to look at the front 4
781 * vertices of the paint volume... */
782 if (G_LIKELY (pv->is_2d))
787 for (i = 1; i < count; i++)
789 if (vertices[i].x < x_min)
790 x_min = vertices[i].x;
791 else if (vertices[i].x > x_max)
792 x_max = vertices[i].x;
794 if (vertices[i].y < y_min)
795 y_min = vertices[i].y;
796 else if (vertices[i].y > y_max)
797 y_max = vertices[i].y;
807 _clutter_paint_volume_project (ClutterPaintVolume *pv,
808 const CoglMatrix *modelview,
809 const CoglMatrix *projection,
810 const float *viewport)
816 /* Just transform the origin... */
817 _clutter_util_fully_transform_vertices (modelview,
826 /* All the vertices must be up to date, since after the projection
827 * it wont be trivial to derive the other vertices. */
828 _clutter_paint_volume_complete (pv);
830 /* Most actors are 2D so we only have to transform the front 4
831 * vertices of the paint volume... */
832 if (G_LIKELY (pv->is_2d))
837 _clutter_util_fully_transform_vertices (modelview,
844 pv->is_axis_aligned = FALSE;
848 _clutter_paint_volume_transform (ClutterPaintVolume *pv,
849 const CoglMatrix *matrix)
856 /* Just transform the origin */
857 cogl_matrix_transform_point (matrix,
865 /* All the vertices must be up to date, since after the transform
866 * it wont be trivial to derive the other vertices. */
867 _clutter_paint_volume_complete (pv);
869 /* Most actors are 2D so we only have to transform the front 4
870 * vertices of the paint volume... */
871 if (G_LIKELY (pv->is_2d))
876 cogl_matrix_transform_points (matrix,
878 sizeof (ClutterVertex),
880 sizeof (ClutterVertex),
884 pv->is_axis_aligned = FALSE;
888 /* Given a paint volume that has been transformed by an arbitrary
889 * modelview and is no longer axis aligned, this derives a replacement
890 * that is axis aligned. */
892 _clutter_paint_volume_axis_align (ClutterPaintVolume *pv)
896 ClutterVertex origin;
901 g_return_if_fail (pv != NULL);
906 if (G_LIKELY (pv->is_axis_aligned))
909 if (G_LIKELY (pv->vertices[0].x == pv->vertices[1].x &&
910 pv->vertices[0].y == pv->vertices[3].y &&
911 pv->vertices[0].z == pv->vertices[4].z))
913 pv->is_axis_aligned = TRUE;
917 if (!pv->is_complete)
918 _clutter_paint_volume_complete (pv);
920 origin = pv->vertices[0];
921 max_x = pv->vertices[0].x;
922 max_y = pv->vertices[0].y;
923 max_z = pv->vertices[0].z;
925 count = pv->is_2d ? 4 : 8;
926 for (i = 1; i < count; i++)
928 if (pv->vertices[i].x < origin.x)
929 origin.x = pv->vertices[i].x;
930 else if (pv->vertices[i].x > max_x)
931 max_x = pv->vertices[i].x;
933 if (pv->vertices[i].y < origin.y)
934 origin.y = pv->vertices[i].y;
935 else if (pv->vertices[i].y > max_y)
936 max_y = pv->vertices[i].y;
938 if (pv->vertices[i].z < origin.z)
939 origin.z = pv->vertices[i].z;
940 else if (pv->vertices[i].z > max_z)
941 max_z = pv->vertices[i].z;
944 pv->vertices[0] = origin;
946 pv->vertices[1].x = max_x;
947 pv->vertices[1].y = origin.y;
948 pv->vertices[1].z = origin.z;
950 pv->vertices[3].x = origin.x;
951 pv->vertices[3].y = max_y;
952 pv->vertices[3].z = origin.z;
954 pv->vertices[4].x = origin.x;
955 pv->vertices[4].y = origin.y;
956 pv->vertices[4].z = max_z;
958 pv->is_complete = FALSE;
959 pv->is_axis_aligned = TRUE;
961 if (pv->vertices[4].z == pv->vertices[0].z)
968 * _clutter_actor_set_default_paint_volume:
969 * @self: a #ClutterActor
970 * @check_gtype: if not %G_TYPE_INVALID, match the type of @self against
972 * @volume: the #ClutterPaintVolume to set
974 * Sets the default paint volume for @self.
976 * This function should be called by #ClutterActor sub-classes that follow
977 * the default assumption that their paint volume is defined by their
980 * If @check_gtype is not %G_TYPE_INVALID, this function will check the
981 * type of @self and only compute the paint volume if the type matches;
982 * this can be used to avoid computing the paint volume for sub-classes
985 * Return value: %TRUE if the paint volume was set, and %FALSE otherwise
988 _clutter_actor_set_default_paint_volume (ClutterActor *self,
990 ClutterPaintVolume *volume)
994 if (check_gtype != G_TYPE_INVALID)
996 if (G_OBJECT_TYPE (self) != check_gtype)
1000 /* calling clutter_actor_get_allocation_* can potentially be very
1001 * expensive, as it can result in a synchronous full stage relayout
1004 if (!clutter_actor_has_allocation (self))
1007 clutter_actor_get_allocation_box (self, &box);
1009 /* we only set the width and height, as the paint volume is defined
1010 * to be relative to the actor's modelview, which means that the
1011 * allocation's origin has already been applied
1013 clutter_paint_volume_set_width (volume, box.x2 - box.x1);
1014 clutter_paint_volume_set_height (volume, box.y2 - box.y1);
1020 * clutter_paint_volume_set_from_allocation:
1021 * @pv: a #ClutterPaintVolume
1022 * @actor: a #ClutterActor
1024 * Sets the #ClutterPaintVolume from the allocation of @actor.
1026 * This function should be used when overriding the
1027 * #ClutterActorClass.get_paint_volume() by #ClutterActor sub-classes
1028 * that do not paint outside their allocation.
1030 * A typical example is:
1034 * my_actor_get_paint_volume (ClutterActor *self,
1035 * ClutterPaintVolume *volume)
1037 * return clutter_paint_volume_set_from_allocation (volume, self);
1041 * Return value: %TRUE if the paint volume was successfully set, and %FALSE
1047 clutter_paint_volume_set_from_allocation (ClutterPaintVolume *pv,
1048 ClutterActor *actor)
1050 g_return_val_if_fail (pv != NULL, FALSE);
1051 g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
1053 return _clutter_actor_set_default_paint_volume (actor, G_TYPE_INVALID, pv);
1056 /* Currently paint volumes are defined relative to a given actor, but
1057 * in some cases it is desireable to be able to change the actor that
1058 * a volume relates too (For instance for ClutterClone actors where we
1059 * need to masquarade the source actors volume as the volume for the
1062 _clutter_paint_volume_set_reference_actor (ClutterPaintVolume *pv,
1063 ClutterActor *actor)
1065 g_return_if_fail (pv != NULL);
1071 _clutter_paint_volume_cull (ClutterPaintVolume *pv,
1072 const ClutterPlane *planes)
1075 ClutterVertex *vertices = pv->vertices;
1076 gboolean partial = FALSE;
1081 return CLUTTER_CULL_RESULT_OUT;
1083 /* We expect the volume to already be transformed into eye coordinates
1085 g_return_val_if_fail (pv->is_complete == TRUE, CLUTTER_CULL_RESULT_IN);
1086 g_return_val_if_fail (pv->actor == NULL, CLUTTER_CULL_RESULT_IN);
1088 /* Most actors are 2D so we only have to transform the front 4
1089 * vertices of the paint volume... */
1090 if (G_LIKELY (pv->is_2d))
1095 for (i = 0; i < 4; i++)
1098 for (j = 0; j < vertex_count; j++)
1103 /* XXX: for perspective projections this can be optimized
1104 * out because all the planes should pass through the origin
1105 * so (0,0,0) is a valid v0. */
1106 p.x = vertices[j].x - planes[i].v0[0];
1107 p.y = vertices[j].y - planes[i].v0[1];
1108 p.z = vertices[j].z - planes[i].v0[2];
1110 distance = (planes[i].n[0] * p.x +
1111 planes[i].n[1] * p.y +
1112 planes[i].n[2] * p.z);
1118 if (out == vertex_count)
1119 return CLUTTER_CULL_RESULT_OUT;
1125 return CLUTTER_CULL_RESULT_PARTIAL;
1127 return CLUTTER_CULL_RESULT_IN;
1131 _clutter_paint_volume_get_stage_paint_box (ClutterPaintVolume *pv,
1132 ClutterStage *stage,
1133 ClutterActorBox *box)
1135 ClutterPaintVolume projected_pv;
1136 CoglMatrix modelview;
1137 CoglMatrix projection;
1142 _clutter_paint_volume_copy_static (pv, &projected_pv);
1144 cogl_matrix_init_identity (&modelview);
1146 /* If the paint volume isn't already in eye coordinates... */
1148 _clutter_actor_apply_relative_transformation_matrix (pv->actor, NULL,
1151 _clutter_stage_get_projection_matrix (stage, &projection);
1152 _clutter_stage_get_viewport (stage,
1158 _clutter_paint_volume_project (&projected_pv,
1163 _clutter_paint_volume_get_bounding_box (&projected_pv, box);
1165 /* The aim here is that for a given rectangle defined with floating point
1166 * coordinates we want to determine a stable quantized size in pixels
1167 * that doesn't vary due to the original box's sub-pixel position.
1169 * The reason this is important is because effects will use this
1170 * API to determine the size of offscreen framebuffers and so for
1171 * a fixed-size object that may be animated accross the screen we
1172 * want to make sure that the stage paint-box has an equally stable
1173 * size so that effects aren't made to continuously re-allocate
1174 * a corresponding fbo.
1176 * The other thing we consider is that the calculation of this box is
1177 * subject to floating point precision issues that might be slightly
1178 * different to the precision issues involved with actually painting the
1179 * actor, which might result in painting slightly leaking outside the
1180 * user's calculated paint-volume. For this we simply aim to pad out the
1181 * paint-volume by at least half a pixel all the way around.
1183 width = box->x2 - box->x1;
1184 height = box->y2 - box->y1;
1185 width = CLUTTER_NEARBYINT (width);
1186 height = CLUTTER_NEARBYINT (height);
1187 /* XXX: NB the width/height may now be up to 0.5px too small so we
1188 * must also pad by 0.25px all around to account for this. In total we
1189 * must padd by at least 0.75px around all sides. */
1191 /* XXX: The furthest that we can overshoot the bottom right corner by
1192 * here is 1.75px in total if you consider that the 0.75 padding could
1193 * just cross an integer boundary and so ceil will effectively add 1.
1195 box->x2 = ceilf (box->x2 + 0.75);
1196 box->y2 = ceilf (box->y2 + 0.75);
1198 /* Now we redefine the top-left relative to the bottom right based on the
1199 * rounded width/height determined above + a constant so that the overall
1200 * size of the box will be stable and not dependant on the box's
1203 * Adding 3px to the width/height will ensure we cover the maximum of
1204 * 1.75px padding on the bottom/right and still ensure we have > 0.75px
1205 * padding on the top/left.
1207 box->x1 = box->x2 - width - 3;
1208 box->y1 = box->y2 - height - 3;
1210 clutter_paint_volume_free (&projected_pv);
1214 _clutter_paint_volume_transform_relative (ClutterPaintVolume *pv,
1215 ClutterActor *relative_to_ancestor)
1218 ClutterActor *actor;
1222 g_return_if_fail (actor != NULL);
1224 _clutter_paint_volume_set_reference_actor (pv, relative_to_ancestor);
1226 cogl_matrix_init_identity (&matrix);
1227 _clutter_actor_apply_relative_transformation_matrix (actor,
1228 relative_to_ancestor,
1231 _clutter_paint_volume_transform (pv, &matrix);