From 3040b140bce59ac2e70de325e0afa98ec8fa7ce7 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 8 Sep 2010 20:39:15 +0100 Subject: [PATCH] paint_volume: Splits out clutter_paint_volume code This splits out all the clutter_paint_volume code from clutter-actor.c into clutter-paint-volume.c. Since clutter-actor.c and clutter-paint-volume.c both needed the functionality of _fully_transform_vertices, this function has now been moved to clutter-utils.c as _clutter_util_fully_transform_vertices. --- clutter/Makefile.am | 1 + clutter/clutter-actor.c | 838 +-------------------------------- clutter/clutter-paint-volume.c | 817 ++++++++++++++++++++++++++++++++ clutter/clutter-private.h | 14 +- clutter/clutter-util.c | 45 ++ 5 files changed, 882 insertions(+), 833 deletions(-) create mode 100644 clutter/clutter-paint-volume.c diff --git a/clutter/Makefile.am b/clutter/Makefile.am index f8e35e375..f9f26c2ac 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -238,6 +238,7 @@ source_c = \ $(srcdir)/clutter-timeout-pool.c \ $(srcdir)/clutter-units.c \ $(srcdir)/clutter-util.c \ + $(srcdir)/clutter-paint-volume.c \ $(NULL) source_c_priv = \ diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 2b37b4bea..6c4d949ee 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -652,7 +652,6 @@ static void _clutter_actor_get_relative_modelview (ClutterActor *self, CoglMatrix *matrix); static ClutterPaintVolume *_clutter_actor_get_paint_volume_mutable (ClutterActor *self); -static void _clutter_paint_volume_complete (ClutterPaintVolume *pv); /* Helper macro which translates by the anchor coord, applies the given transformation and then translates back */ @@ -1920,50 +1919,6 @@ clutter_actor_apply_relative_transform_to_point (ClutterActor *self, cogl_matrix_transform_point (&matrix, &vertex->x, &vertex->y, &vertex->z, &w); } -/* Help macros to scale from OpenGL <-1,1> coordinates system to - * window coordinates ranging [0,window-size] - */ -#define MTX_GL_SCALE_X(x,w,v1,v2) ((((((x) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) -#define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - (((((y) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) -#define MTX_GL_SCALE_Z(z,w,v1,v2) (MTX_GL_SCALE_X ((z), (w), (v1), (v2))) - -static void -_fully_transform_vertices (const CoglMatrix *modelview, - const CoglMatrix *projection, - const int *viewport, - const ClutterVertex *vertices_in, - ClutterVertex *vertices_out, - int n_vertices) -{ - CoglMatrix modelview_projection; - int i; - - /* XXX: we should find a way to cache this per actor */ - cogl_matrix_multiply (&modelview_projection, - projection, - modelview); - - for (i = 0; i < n_vertices; i++) - { - const ClutterVertex *vertex_in = &vertices_in[i]; - ClutterVertex *vertex_out = &vertices_out[i]; - gfloat x, y, z, w; - - x = vertex_in->x; - y = vertex_in->y; - z = vertex_in->z; - w = 1.0; - - /* Transform the point using the modelview matrix */ - cogl_matrix_transform_point (&modelview_projection, &x, &y, &z, &w); - - /* Finally translate from OpenGL coords to window coords */ - vertex_out->x = MTX_GL_SCALE_X (x, w, viewport[2], viewport[0]); - vertex_out->y = MTX_GL_SCALE_Y (y, w, viewport[3], viewport[1]); - vertex_out->z = MTX_GL_SCALE_Z (z, w, viewport[2], viewport[0]); - } -} - static gboolean _clutter_actor_fully_transform_vertices (ClutterActor *self, const ClutterVertex *vertices_in, @@ -2001,12 +1956,12 @@ _clutter_actor_fully_transform_vertices (ClutterActor *self, &viewport[2], &viewport[3]); - _fully_transform_vertices (&modelview, - &projection, - viewport, - vertices_in, - vertices_out, - n_vertices); + _clutter_util_fully_transform_vertices (&modelview, + &projection, + viewport, + vertices_in, + vertices_out, + n_vertices); return TRUE; } @@ -11831,787 +11786,6 @@ clutter_actor_has_key_focus (ClutterActor *self) return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == self; } -GType -clutter_paint_volume_get_type (void) -{ - static GType our_type = 0; - - if (G_UNLIKELY (our_type == 0)) - our_type = - g_boxed_type_register_static (I_("ClutterPaintVolume"), - (GBoxedCopyFunc) clutter_paint_volume_copy, - (GBoxedFreeFunc) clutter_paint_volume_free); - - return our_type; -} - -/* - * _clutter_paint_volume_new: - * @actor: a #ClutterActor - * - * Creates a new #ClutterPaintVolume for the given @actor. - * - * Return value: the newly allocated #ClutterPaintVolume. Use - * clutter_paint_volume_free() to free the resources it uses - * - * Since: 1.4 - */ -ClutterPaintVolume * -_clutter_paint_volume_new (ClutterActor *actor) -{ - ClutterPaintVolume *pv; - - g_return_val_if_fail (actor != NULL, NULL); - - pv = g_slice_new (ClutterPaintVolume); - - pv->actor = g_object_ref (actor); - - memset (pv->vertices, 0, 8 * sizeof (ClutterVertex)); - - pv->is_static = FALSE; - pv->is_empty = TRUE; - pv->is_axis_aligned = TRUE; - pv->is_complete = TRUE; - pv->is_2d = TRUE; - - return pv; -} - -/* Since paint volumes are used so heavily in a typical paint - * traversal of a Clutter scene graph and since paint volumes often - * have a very short life cycle that maps well to stack allocation we - * allow initializing a static ClutterPaintVolume variable to avoid - * hammering the slice allocator. - * - * We were seeing slice allocation take about 1% cumulative CPU time - * for some very simple clutter tests which although it isn't a *lot* - * this is an easy way to basically drop that to 0%. - * - * The PaintVolume will be internally marked as static and - * clutter_paint_volume_free should still be used to "free" static - * volumes. This allows us to potentially store dynamically allocated - * data inside paint volumes in the future since we would be able to - * free it during _paint_volume_free(). - */ -void -_clutter_paint_volume_init_static (ClutterActor *actor, - ClutterPaintVolume *pv) -{ - g_return_if_fail (actor != NULL); - - pv->actor = g_object_ref (actor); - - memset (pv->vertices, 0, 8 * sizeof (ClutterVertex)); - - pv->is_static = TRUE; - pv->is_empty = TRUE; - pv->is_axis_aligned = TRUE; - pv->is_complete = TRUE; - pv->is_2d = TRUE; -} - -void -_clutter_paint_volume_copy_static (const ClutterPaintVolume *src_pv, - ClutterPaintVolume *dst_pv) -{ - - g_return_if_fail (src_pv != NULL && dst_pv != NULL); - - memcpy (dst_pv, src_pv, sizeof (ClutterPaintVolume)); - g_object_ref (dst_pv->actor); - dst_pv->is_static = TRUE; -} - -/** - * clutter_paint_volume_copy: - * @pv: a #ClutterPaintVolume - * - * Copies @pv into a new #ClutterPaintVolume - * - * Return value: a newly allocated copy of a #ClutterPaintVolume - * - * Since: 1.4 - */ -ClutterPaintVolume * -clutter_paint_volume_copy (const ClutterPaintVolume *pv) -{ - ClutterPaintVolume *copy; - - /* XXX: can we just g_return_val_if_fail instead‽ */ - if (G_UNLIKELY (pv == NULL)) - return NULL; - - copy = g_slice_dup (ClutterPaintVolume, pv); - if (copy->actor) - g_object_ref (copy->actor); - - copy->is_static = FALSE; - - return copy; -} - -/** - * clutter_paint_volume_free: - * @pv: a #ClutterPaintVolume - * - * Frees the resources allocated by @pv - * - * Since: 1.4 - */ -void -clutter_paint_volume_free (ClutterPaintVolume *pv) -{ - g_return_if_fail (pv != NULL); - - g_object_unref (pv->actor); - - if (G_LIKELY (pv->is_static)) - return; - - g_slice_free (ClutterPaintVolume, pv); -} - -/** - * clutter_paint_volume_set_origin: - * @pv: a #ClutterPaintVolume - * @origin: a #ClutterVertex - * - * Sets the origin of the paint volume. - * - * The origin is defined as the X, Y and Z coordinates of the top-left - * corner of an actor's paint volume, in actor coordinates. - * - * The default is origin is assumed at: (0, 0, 0) - * - * Since: 1.4 - */ -void -clutter_paint_volume_set_origin (ClutterPaintVolume *pv, - const ClutterVertex *origin) -{ - float dx = origin->x - pv->vertices[0].x; - float dy = origin->y - pv->vertices[0].y; - float dz = origin->z - pv->vertices[0].z; - int key_vertices[4] = {0, 1, 3, 4}; - int i; - - g_return_if_fail (pv != NULL); - g_return_if_fail (pv->is_axis_aligned); - - /* If we change the origin then all the key vertices of the paint - * volume need to be shifted too... */ - for (i = 0; i < 4; i++) - { - pv->vertices[key_vertices[i]].x += dx; - pv->vertices[key_vertices[i]].y += dy; - pv->vertices[key_vertices[i]].z += dz; - } - - pv->is_complete = FALSE; -} - -/** - * clutter_paint_volume_get_origin: - * @pv: a #ClutterPaintVolume - * @vertex: (out): the return location for a #ClutterVertex - * - * Retrieves the origin of the #ClutterPaintVolume. - * - * Since: 1.4 - */ -void -clutter_paint_volume_get_origin (const ClutterPaintVolume *pv, - ClutterVertex *vertex) -{ - g_return_if_fail (pv != NULL); - g_return_if_fail (vertex != NULL); - - *vertex = pv->vertices[0]; -} - -static void -_clutter_paint_volume_update_is_empty (ClutterPaintVolume *pv) -{ - if (pv->vertices[0].x == pv->vertices[1].x && - pv->vertices[0].y == pv->vertices[3].y && - pv->vertices[0].z == pv->vertices[4].z) - pv->is_empty = TRUE; - else - pv->is_empty = FALSE; -} - -/** - * clutter_paint_volume_set_width: - * @pv: a #ClutterPaintVolume - * @width: the width of the paint volume, in pixels - * - * Sets the width of the paint volume. - * - * Since: 1.4 - */ -void -clutter_paint_volume_set_width (ClutterPaintVolume *pv, - gfloat width) -{ - gfloat right_xpos; - - g_return_if_fail (pv != NULL); - g_return_if_fail (pv->is_axis_aligned); - g_return_if_fail (width >= 0.0f); - - /* If the volume is currently empty then only the origin is - * currently valid */ - if (pv->is_empty) - pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0]; - - right_xpos = pv->vertices[0].x + width; - - /* Move the right vertices of the paint box relative to the - * origin... */ - pv->vertices[1].x = right_xpos; - /* pv->vertices[2].x = right_xpos; NB: updated lazily */ - /* pv->vertices[5].x = right_xpos; NB: updated lazily */ - /* pv->vertices[6].x = right_xpos; NB: updated lazily */ - - pv->is_complete = FALSE; - - _clutter_paint_volume_update_is_empty (pv); -} - -/** - * clutter_paint_volume_get_width: - * @pv: a #ClutterPaintVolume - * - * Retrieves the width set using clutter_paint_volume_get_width() - * - * Return value: the width, in pixels - * - * Since: 1.4 - */ -gfloat -clutter_paint_volume_get_width (const ClutterPaintVolume *pv) -{ - g_return_val_if_fail (pv != NULL, 0.0); - g_return_val_if_fail (pv->is_axis_aligned, 0); - - if (pv->is_empty) - return 0; - else - return pv->vertices[1].x - pv->vertices[0].x; -} - -/** - * clutter_paint_volume_set_height: - * @pv: a #ClutterPaintVolume - * @height: the height of the paint volume, in pixels - * - * Sets the height of the paint volume. - * - * Since: 1.4 - */ -void -clutter_paint_volume_set_height (ClutterPaintVolume *pv, - gfloat height) -{ - gfloat height_ypos; - - g_return_if_fail (pv != NULL); - g_return_if_fail (pv->is_axis_aligned); - g_return_if_fail (height >= 0.0f); - - /* If the volume is currently empty then only the origin is - * currently valid */ - if (pv->is_empty) - pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0]; - - height_ypos = pv->vertices[0].y + height; - - /* Move the bottom vertices of the paint box relative to the - * origin... */ - /* pv->vertices[2].y = height_ypos; NB: updated lazily */ - pv->vertices[3].y = height_ypos; - /* pv->vertices[6].y = height_ypos; NB: updated lazily */ - /* pv->vertices[7].y = height_ypos; NB: updated lazily */ - pv->is_complete = FALSE; - - _clutter_paint_volume_update_is_empty (pv); -} - -/** - * clutter_paint_volume_get_height: - * @pv: a #ClutterPaintVolume - * - * Retrieves the height of the paint volume set using - * clutter_paint_volume_get_height() - * - * Return value: the height of the paint volume, in pixels - * - * Since: 1.4 - */ -gfloat -clutter_paint_volume_get_height (const ClutterPaintVolume *pv) -{ - g_return_val_if_fail (pv != NULL, 0.0); - g_return_val_if_fail (pv->is_axis_aligned, 0); - - if (pv->is_empty) - return 0; - else - return pv->vertices[3].y - pv->vertices[0].y; -} - -/** - * clutter_paint_volume_set_depth: - * @pv: a #ClutterPaintVolume - * @depth: the depth of the paint volume, in pixels - * - * Sets the depth of the paint volume. - * - * Since: 1.4 - */ -void -clutter_paint_volume_set_depth (ClutterPaintVolume *pv, - gfloat depth) -{ - gfloat depth_zpos; - - g_return_if_fail (pv != NULL); - g_return_if_fail (pv->is_axis_aligned); - g_return_if_fail (depth >= 0.0f); - - /* If the volume is currently empty then only the origin is - * currently valid */ - if (pv->is_empty) - pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0]; - - depth_zpos = pv->vertices[0].z + depth; - - /* Move the back vertices of the paint box relative to the - * origin... */ - pv->vertices[4].z = depth_zpos; - /* pv->vertices[5].z = depth_zpos; NB: updated lazily */ - /* pv->vertices[6].z = depth_zpos; NB: updated lazily */ - /* pv->vertices[7].z = depth_zpos; NB: updated lazily */ - - pv->is_complete = FALSE; - pv->is_2d = depth ? FALSE : TRUE; - _clutter_paint_volume_update_is_empty (pv); -} - -/** - * clutter_paint_volume_get_depth: - * @pv: a #ClutterPaintVolume - * - * Retrieves the depth of the paint volume set using - * clutter_paint_volume_get_depth() - * - * Return value: the depth - * - * Since: 1.4 - */ -gfloat -clutter_paint_volume_get_depth (const ClutterPaintVolume *pv) -{ - g_return_val_if_fail (pv != NULL, 0.0); - g_return_val_if_fail (pv->is_axis_aligned, 0); - - if (pv->is_empty) - return 0; - else - return pv->vertices[4].z - pv->vertices[0].z; -} - -/** - * clutter_paint_volume_union: - * @pv: The first #ClutterPaintVolume and destination for resulting - * union - * @another_pv: A second #ClutterPaintVolume to union with @pv - * - * Updates the geometry of @pv to be the union bounding box that - * encompases @pv and @another_pv. - * - * Since: 1.4 - */ -void -clutter_paint_volume_union (ClutterPaintVolume *pv, - const ClutterPaintVolume *another_pv) -{ - int key_vertices[4] = {0, 1, 3, 4}; - - g_return_if_fail (pv != NULL); - g_return_if_fail (pv->is_axis_aligned); - g_return_if_fail (another_pv != NULL); - g_return_if_fail (another_pv->is_axis_aligned); - - /* NB: we only have to update vertices 0, 1, 3 and 4 - * (See the ClutterPaintVolume typedef for more details) */ - - /* We special case empty volumes because otherwise we'd end up - * calculating a bounding box that would enclose the origin of - * the empty volume which isn't desired. - */ - if (another_pv->is_empty) - return; - - if (pv->is_empty) - { - int i; - for (i = 0; i < 4; i++) - pv->vertices[key_vertices[i]] = another_pv->vertices[key_vertices[i]]; - pv->is_2d = another_pv->is_2d; - goto done; - } - - /* grow left*/ - /* left vertices 0, 3, 4, 7 */ - if (another_pv->vertices[0].x < pv->vertices[0].x) - { - int min_x = another_pv->vertices[0].x; - pv->vertices[0].x = min_x; - pv->vertices[3].x = min_x; - pv->vertices[4].x = min_x; - /* pv->vertices[7].x = min_x; */ - } - - /* grow right */ - /* right vertices 1, 2, 5, 6 */ - if (another_pv->vertices[1].x > pv->vertices[1].x) - { - int max_x = another_pv->vertices[1].x; - pv->vertices[1].x = max_x; - /* pv->vertices[2].x = max_x; */ - /* pv->vertices[5].x = max_x; */ - /* pv->vertices[6].x = max_x; */ - } - - /* grow up */ - /* top vertices 0, 1, 4, 5 */ - if (another_pv->vertices[0].y < pv->vertices[0].y) - { - int min_y = another_pv->vertices[0].y; - pv->vertices[0].y = min_y; - pv->vertices[1].y = min_y; - pv->vertices[4].y = min_y; - /* pv->vertices[5].y = min_y; */ - } - - /* grow down */ - /* bottom vertices 2, 3, 6, 7 */ - if (another_pv->vertices[3].y > pv->vertices[3].y) - { - int may_y = another_pv->vertices[3].y; - /* pv->vertices[2].y = may_y; */ - pv->vertices[3].y = may_y; - /* pv->vertices[6].y = may_y; */ - /* pv->vertices[7].y = may_y; */ - } - - /* grow forward */ - /* front vertices 0, 1, 2, 3 */ - if (another_pv->vertices[0].z < pv->vertices[0].z) - { - int min_z = another_pv->vertices[0].z; - pv->vertices[0].z = min_z; - pv->vertices[1].z = min_z; - /* pv->vertices[2].z = min_z; */ - pv->vertices[3].z = min_z; - } - - /* grow backward */ - /* back vertices 4, 5, 6, 7 */ - if (another_pv->vertices[4].z > pv->vertices[4].z) - { - int maz_z = another_pv->vertices[4].z; - pv->vertices[4].z = maz_z; - /* pv->vertices[5].z = maz_z; */ - /* pv->vertices[6].z = maz_z; */ - /* pv->vertices[7].z = maz_z; */ - } - - if (pv->vertices[4].z == pv->vertices[0].z) - pv->is_2d = TRUE; - else - pv->is_2d = FALSE; - -done: - pv->is_empty = FALSE; - pv->is_complete = FALSE; -} - -/* The paint_volume setters only update vertices 0, 1, 3 and - * 4 since the others can be drived from them. - * - * This will set pv->completed = TRUE; - */ -static void -_clutter_paint_volume_complete (ClutterPaintVolume *pv) -{ - if (pv->is_complete || pv->is_empty) - return; - - g_return_if_fail (pv->is_axis_aligned); - - /* front-bottom-right */ - pv->vertices[2].x = pv->vertices[1].x; - pv->vertices[2].y = pv->vertices[3].y; - pv->vertices[2].z = pv->vertices[0].z; - - if (G_UNLIKELY (!pv->is_2d)) - { - /* back-top-right */ - pv->vertices[5].x = pv->vertices[1].x; - pv->vertices[5].y = pv->vertices[0].y; - pv->vertices[5].z = pv->vertices[4].z; - - /* back-bottom-right */ - pv->vertices[6].x = pv->vertices[1].x; - pv->vertices[6].y = pv->vertices[3].y; - pv->vertices[6].z = pv->vertices[4].z; - - /* back-bottom-left */ - pv->vertices[7].x = pv->vertices[0].x; - pv->vertices[7].y = pv->vertices[3].y; - pv->vertices[7].z = pv->vertices[4].z; - } - - pv->is_complete = TRUE; -} - -/* - * _clutter_paint_volume_get_box: - * @pv: a #ClutterPaintVolume - * @box: a pixel aligned #ClutterGeometry - * - * Transforms a 3D paint volume into a 2D bounding box in the - * same coordinate space as the 3D paint volume. - * - * To get an actors "paint box" you should first project - * the paint volume into window coordinates before getting - * the 2D bounding box. - * - * The coordinates of the returned box are not clamped to - * integer pixel values, if you need them to be clamped you can use - * clutter_actor_box_clamp_to_pixel() - * - * Since: 1.4 - */ -void -_clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv, - ClutterActorBox *box) -{ - gfloat x_min, y_min, x_max, y_max; - ClutterVertex *vertices; - int count; - gint i; - - g_return_if_fail (pv != NULL); - g_return_if_fail (box != NULL); - - if (pv->is_empty) - { - box->x1 = box->x2 = pv->vertices[0].x; - box->y1 = box->y2 = pv->vertices[0].y; - return; - } - - /* Updates the vertices we calculate lazily - * (See ClutterPaintVolume typedef for more details) */ - _clutter_paint_volume_complete (pv); - - vertices = pv->vertices; - - x_min = x_max = vertices[0].x; - y_min = y_max = vertices[0].y; - - /* Most actors are 2D so we only have to look at the front 4 - * vertices of the paint volume... */ - if (G_LIKELY (pv->is_2d)) - count = 4; - else - count = 8; - - for (i = 1; i < count; i++) - { - if (vertices[i].x < x_min) - x_min = vertices[i].x; - else if (vertices[i].x > x_max) - x_max = vertices[i].x; - - if (vertices[i].y < y_min) - y_min = vertices[i].y; - else if (vertices[i].y > y_max) - y_max = vertices[i].y; - } - - box->x1 = x_min; - box->y1 = y_min; - box->x2 = x_max; - box->y2 = y_max; -} - -void -_clutter_paint_volume_project (ClutterPaintVolume *pv, - const CoglMatrix *modelview, - const CoglMatrix *projection, - const int *viewport) -{ - int transform_count; - - if (pv->is_empty) - { - /* Just transform the origin... */ - _fully_transform_vertices (modelview, - projection, - viewport, - pv->vertices, - pv->vertices, - 1); - return; - } - - /* All the vertices must be up to date, since after the projection - * it wont be trivial to derive the other vertices. */ - _clutter_paint_volume_complete (pv); - - /* Most actors are 2D so we only have to transform the front 4 - * vertices of the paint volume... */ - if (G_LIKELY (pv->is_2d)) - transform_count = 4; - else - transform_count = 8; - - _fully_transform_vertices (modelview, - projection, - viewport, - pv->vertices, - pv->vertices, - transform_count); - - pv->is_axis_aligned = FALSE; -} - -static void -_clutter_paint_volume_transform (ClutterPaintVolume *pv, - const CoglMatrix *matrix) -{ - int transform_count; - int i; - - if (pv->is_empty) - { - gfloat w = 1; - /* Just transform the origin */ - cogl_matrix_transform_point (matrix, - &pv->vertices[0].x, - &pv->vertices[0].y, - &pv->vertices[0].z, - &w); - return; - } - - /* All the vertices must be up to date, since after the transform - * it wont be trivial to derive the other vertices. */ - _clutter_paint_volume_complete (pv); - - /* Most actors are 2D so we only have to transform the front 4 - * vertices of the paint volume... */ - if (G_LIKELY (pv->is_2d)) - transform_count = 4; - else - transform_count = 8; - - - for (i = 0; i < transform_count; i++) - { - gfloat w = 1; - cogl_matrix_transform_point (matrix, - &pv->vertices[i].x, - &pv->vertices[i].y, - &pv->vertices[i].z, - &w); - } - - pv->is_axis_aligned = FALSE; -} - - -/* Given a paint volume that has been transformed by an arbitrary - * modelview and is no longer axis aligned, this derives a replacement - * that is axis aligned. */ -static void -_clutter_paint_volume_axis_align (ClutterPaintVolume *pv) -{ - int count; - int i; - ClutterVertex origin; - float max_x; - float max_y; - float max_z; - - if (pv->is_empty) - return; - - g_return_if_fail (pv->is_complete); - - if (G_LIKELY (pv->is_axis_aligned)) - return; - - if (G_LIKELY (pv->vertices[0].x == pv->vertices[1].x && - pv->vertices[0].y == pv->vertices[3].y && - pv->vertices[0].z == pv->vertices[4].y)) - { - pv->is_axis_aligned = TRUE; - return; - } - - origin = pv->vertices[0]; - max_x = pv->vertices[0].x; - max_y = pv->vertices[0].y; - max_z = pv->vertices[0].z; - - count = pv->is_2d ? 4 : 8; - for (i = 1; i < count; i++) - { - if (pv->vertices[i].x < origin.x) - origin.x = pv->vertices[i].x; - else if (pv->vertices[i].x > max_x) - max_x = pv->vertices[i].x; - - if (pv->vertices[i].y < origin.y) - origin.y = pv->vertices[i].y; - else if (pv->vertices[i].y > max_y) - max_y = pv->vertices[i].y; - - if (pv->vertices[i].z < origin.z) - origin.z = pv->vertices[i].z; - else if (pv->vertices[i].z > max_z) - max_z = pv->vertices[i].z; - } - - pv->vertices[0] = origin; - - pv->vertices[1].x = max_x; - pv->vertices[1].y = origin.y; - pv->vertices[1].z = origin.z; - - pv->vertices[3].x = origin.x; - pv->vertices[3].y = max_y; - pv->vertices[3].z = origin.z; - - pv->vertices[4].x = origin.x; - pv->vertices[4].y = origin.y; - pv->vertices[4].z = max_z; - - pv->is_complete = FALSE; - pv->is_axis_aligned = TRUE; - - if (pv->vertices[4].z == pv->vertices[0].z) - pv->is_2d = TRUE; - else - pv->is_2d = FALSE; -} - /* The public clutter_actor_get_paint_volume API returns a const * pointer since we return a pointer directly to the cached * PaintVolume associated with the actor and don't want the user to diff --git a/clutter/clutter-paint-volume.c b/clutter/clutter-paint-volume.c new file mode 100644 index 000000000..a184da7b5 --- /dev/null +++ b/clutter/clutter-paint-volume.c @@ -0,0 +1,817 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Authors: + * Robert Bragg + * Emmanuele Bassi + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "clutter-private.h" + +GType +clutter_paint_volume_get_type (void) +{ + static GType our_type = 0; + + if (G_UNLIKELY (our_type == 0)) + our_type = + g_boxed_type_register_static (I_("ClutterPaintVolume"), + (GBoxedCopyFunc) clutter_paint_volume_copy, + (GBoxedFreeFunc) clutter_paint_volume_free); + + return our_type; +} + +/* + * _clutter_paint_volume_new: + * @actor: a #ClutterActor + * + * Creates a new #ClutterPaintVolume for the given @actor. + * + * Return value: the newly allocated #ClutterPaintVolume. Use + * clutter_paint_volume_free() to free the resources it uses + * + * Since: 1.4 + */ +ClutterPaintVolume * +_clutter_paint_volume_new (ClutterActor *actor) +{ + ClutterPaintVolume *pv; + + g_return_val_if_fail (actor != NULL, NULL); + + pv = g_slice_new (ClutterPaintVolume); + + pv->actor = g_object_ref (actor); + + memset (pv->vertices, 0, 8 * sizeof (ClutterVertex)); + + pv->is_static = FALSE; + pv->is_empty = TRUE; + pv->is_axis_aligned = TRUE; + pv->is_complete = TRUE; + pv->is_2d = TRUE; + + return pv; +} + +/* Since paint volumes are used so heavily in a typical paint + * traversal of a Clutter scene graph and since paint volumes often + * have a very short life cycle that maps well to stack allocation we + * allow initializing a static ClutterPaintVolume variable to avoid + * hammering the slice allocator. + * + * We were seeing slice allocation take about 1% cumulative CPU time + * for some very simple clutter tests which although it isn't a *lot* + * this is an easy way to basically drop that to 0%. + * + * The PaintVolume will be internally marked as static and + * clutter_paint_volume_free should still be used to "free" static + * volumes. This allows us to potentially store dynamically allocated + * data inside paint volumes in the future since we would be able to + * free it during _paint_volume_free(). + */ +void +_clutter_paint_volume_init_static (ClutterActor *actor, + ClutterPaintVolume *pv) +{ + g_return_if_fail (actor != NULL); + + pv->actor = g_object_ref (actor); + + memset (pv->vertices, 0, 8 * sizeof (ClutterVertex)); + + pv->is_static = TRUE; + pv->is_empty = TRUE; + pv->is_axis_aligned = TRUE; + pv->is_complete = TRUE; + pv->is_2d = TRUE; +} + +void +_clutter_paint_volume_copy_static (const ClutterPaintVolume *src_pv, + ClutterPaintVolume *dst_pv) +{ + + g_return_if_fail (src_pv != NULL && dst_pv != NULL); + + memcpy (dst_pv, src_pv, sizeof (ClutterPaintVolume)); + g_object_ref (dst_pv->actor); + dst_pv->is_static = TRUE; +} + +/** + * clutter_paint_volume_copy: + * @pv: a #ClutterPaintVolume + * + * Copies @pv into a new #ClutterPaintVolume + * + * Return value: a newly allocated copy of a #ClutterPaintVolume + * + * Since: 1.4 + */ +ClutterPaintVolume * +clutter_paint_volume_copy (const ClutterPaintVolume *pv) +{ + ClutterPaintVolume *copy; + + /* XXX: can we just g_return_val_if_fail instead‽ */ + if (G_UNLIKELY (pv == NULL)) + return NULL; + + copy = g_slice_dup (ClutterPaintVolume, pv); + if (copy->actor) + g_object_ref (copy->actor); + + copy->is_static = FALSE; + + return copy; +} + +/** + * clutter_paint_volume_free: + * @pv: a #ClutterPaintVolume + * + * Frees the resources allocated by @pv + * + * Since: 1.4 + */ +void +clutter_paint_volume_free (ClutterPaintVolume *pv) +{ + g_return_if_fail (pv != NULL); + + g_object_unref (pv->actor); + + if (G_LIKELY (pv->is_static)) + return; + + g_slice_free (ClutterPaintVolume, pv); +} + +/** + * clutter_paint_volume_set_origin: + * @pv: a #ClutterPaintVolume + * @origin: a #ClutterVertex + * + * Sets the origin of the paint volume. + * + * The origin is defined as the X, Y and Z coordinates of the top-left + * corner of an actor's paint volume, in actor coordinates. + * + * The default is origin is assumed at: (0, 0, 0) + * + * Since: 1.4 + */ +void +clutter_paint_volume_set_origin (ClutterPaintVolume *pv, + const ClutterVertex *origin) +{ + float dx = origin->x - pv->vertices[0].x; + float dy = origin->y - pv->vertices[0].y; + float dz = origin->z - pv->vertices[0].z; + int key_vertices[4] = {0, 1, 3, 4}; + int i; + + g_return_if_fail (pv != NULL); + g_return_if_fail (pv->is_axis_aligned); + + /* If we change the origin then all the key vertices of the paint + * volume need to be shifted too... */ + for (i = 0; i < 4; i++) + { + pv->vertices[key_vertices[i]].x += dx; + pv->vertices[key_vertices[i]].y += dy; + pv->vertices[key_vertices[i]].z += dz; + } + + pv->is_complete = FALSE; +} + +/** + * clutter_paint_volume_get_origin: + * @pv: a #ClutterPaintVolume + * @vertex: (out): the return location for a #ClutterVertex + * + * Retrieves the origin of the #ClutterPaintVolume. + * + * Since: 1.4 + */ +void +clutter_paint_volume_get_origin (const ClutterPaintVolume *pv, + ClutterVertex *vertex) +{ + g_return_if_fail (pv != NULL); + g_return_if_fail (vertex != NULL); + + *vertex = pv->vertices[0]; +} + +static void +_clutter_paint_volume_update_is_empty (ClutterPaintVolume *pv) +{ + if (pv->vertices[0].x == pv->vertices[1].x && + pv->vertices[0].y == pv->vertices[3].y && + pv->vertices[0].z == pv->vertices[4].z) + pv->is_empty = TRUE; + else + pv->is_empty = FALSE; +} + +/** + * clutter_paint_volume_set_width: + * @pv: a #ClutterPaintVolume + * @width: the width of the paint volume, in pixels + * + * Sets the width of the paint volume. + * + * Since: 1.4 + */ +void +clutter_paint_volume_set_width (ClutterPaintVolume *pv, + gfloat width) +{ + gfloat right_xpos; + + g_return_if_fail (pv != NULL); + g_return_if_fail (pv->is_axis_aligned); + g_return_if_fail (width >= 0.0f); + + /* If the volume is currently empty then only the origin is + * currently valid */ + if (pv->is_empty) + pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0]; + + right_xpos = pv->vertices[0].x + width; + + /* Move the right vertices of the paint box relative to the + * origin... */ + pv->vertices[1].x = right_xpos; + /* pv->vertices[2].x = right_xpos; NB: updated lazily */ + /* pv->vertices[5].x = right_xpos; NB: updated lazily */ + /* pv->vertices[6].x = right_xpos; NB: updated lazily */ + + pv->is_complete = FALSE; + + _clutter_paint_volume_update_is_empty (pv); +} + +/** + * clutter_paint_volume_get_width: + * @pv: a #ClutterPaintVolume + * + * Retrieves the width set using clutter_paint_volume_get_width() + * + * Return value: the width, in pixels + * + * Since: 1.4 + */ +gfloat +clutter_paint_volume_get_width (const ClutterPaintVolume *pv) +{ + g_return_val_if_fail (pv != NULL, 0.0); + g_return_val_if_fail (pv->is_axis_aligned, 0); + + if (pv->is_empty) + return 0; + else + return pv->vertices[1].x - pv->vertices[0].x; +} + +/** + * clutter_paint_volume_set_height: + * @pv: a #ClutterPaintVolume + * @height: the height of the paint volume, in pixels + * + * Sets the height of the paint volume. + * + * Since: 1.4 + */ +void +clutter_paint_volume_set_height (ClutterPaintVolume *pv, + gfloat height) +{ + gfloat height_ypos; + + g_return_if_fail (pv != NULL); + g_return_if_fail (pv->is_axis_aligned); + g_return_if_fail (height >= 0.0f); + + /* If the volume is currently empty then only the origin is + * currently valid */ + if (pv->is_empty) + pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0]; + + height_ypos = pv->vertices[0].y + height; + + /* Move the bottom vertices of the paint box relative to the + * origin... */ + /* pv->vertices[2].y = height_ypos; NB: updated lazily */ + pv->vertices[3].y = height_ypos; + /* pv->vertices[6].y = height_ypos; NB: updated lazily */ + /* pv->vertices[7].y = height_ypos; NB: updated lazily */ + pv->is_complete = FALSE; + + _clutter_paint_volume_update_is_empty (pv); +} + +/** + * clutter_paint_volume_get_height: + * @pv: a #ClutterPaintVolume + * + * Retrieves the height of the paint volume set using + * clutter_paint_volume_get_height() + * + * Return value: the height of the paint volume, in pixels + * + * Since: 1.4 + */ +gfloat +clutter_paint_volume_get_height (const ClutterPaintVolume *pv) +{ + g_return_val_if_fail (pv != NULL, 0.0); + g_return_val_if_fail (pv->is_axis_aligned, 0); + + if (pv->is_empty) + return 0; + else + return pv->vertices[3].y - pv->vertices[0].y; +} + +/** + * clutter_paint_volume_set_depth: + * @pv: a #ClutterPaintVolume + * @depth: the depth of the paint volume, in pixels + * + * Sets the depth of the paint volume. + * + * Since: 1.4 + */ +void +clutter_paint_volume_set_depth (ClutterPaintVolume *pv, + gfloat depth) +{ + gfloat depth_zpos; + + g_return_if_fail (pv != NULL); + g_return_if_fail (pv->is_axis_aligned); + g_return_if_fail (depth >= 0.0f); + + /* If the volume is currently empty then only the origin is + * currently valid */ + if (pv->is_empty) + pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0]; + + depth_zpos = pv->vertices[0].z + depth; + + /* Move the back vertices of the paint box relative to the + * origin... */ + pv->vertices[4].z = depth_zpos; + /* pv->vertices[5].z = depth_zpos; NB: updated lazily */ + /* pv->vertices[6].z = depth_zpos; NB: updated lazily */ + /* pv->vertices[7].z = depth_zpos; NB: updated lazily */ + + pv->is_complete = FALSE; + pv->is_2d = depth ? FALSE : TRUE; + _clutter_paint_volume_update_is_empty (pv); +} + +/** + * clutter_paint_volume_get_depth: + * @pv: a #ClutterPaintVolume + * + * Retrieves the depth of the paint volume set using + * clutter_paint_volume_get_depth() + * + * Return value: the depth + * + * Since: 1.4 + */ +gfloat +clutter_paint_volume_get_depth (const ClutterPaintVolume *pv) +{ + g_return_val_if_fail (pv != NULL, 0.0); + g_return_val_if_fail (pv->is_axis_aligned, 0); + + if (pv->is_empty) + return 0; + else + return pv->vertices[4].z - pv->vertices[0].z; +} + +/** + * clutter_paint_volume_union: + * @pv: The first #ClutterPaintVolume and destination for resulting + * union + * @another_pv: A second #ClutterPaintVolume to union with @pv + * + * Updates the geometry of @pv to be the union bounding box that + * encompases @pv and @another_pv. + * + * Since: 1.4 + */ +void +clutter_paint_volume_union (ClutterPaintVolume *pv, + const ClutterPaintVolume *another_pv) +{ + int key_vertices[4] = {0, 1, 3, 4}; + + g_return_if_fail (pv != NULL); + g_return_if_fail (pv->is_axis_aligned); + g_return_if_fail (another_pv != NULL); + g_return_if_fail (another_pv->is_axis_aligned); + + /* NB: we only have to update vertices 0, 1, 3 and 4 + * (See the ClutterPaintVolume typedef for more details) */ + + /* We special case empty volumes because otherwise we'd end up + * calculating a bounding box that would enclose the origin of + * the empty volume which isn't desired. + */ + if (another_pv->is_empty) + return; + + if (pv->is_empty) + { + int i; + for (i = 0; i < 4; i++) + pv->vertices[key_vertices[i]] = another_pv->vertices[key_vertices[i]]; + pv->is_2d = another_pv->is_2d; + goto done; + } + + /* grow left*/ + /* left vertices 0, 3, 4, 7 */ + if (another_pv->vertices[0].x < pv->vertices[0].x) + { + int min_x = another_pv->vertices[0].x; + pv->vertices[0].x = min_x; + pv->vertices[3].x = min_x; + pv->vertices[4].x = min_x; + /* pv->vertices[7].x = min_x; */ + } + + /* grow right */ + /* right vertices 1, 2, 5, 6 */ + if (another_pv->vertices[1].x > pv->vertices[1].x) + { + int max_x = another_pv->vertices[1].x; + pv->vertices[1].x = max_x; + /* pv->vertices[2].x = max_x; */ + /* pv->vertices[5].x = max_x; */ + /* pv->vertices[6].x = max_x; */ + } + + /* grow up */ + /* top vertices 0, 1, 4, 5 */ + if (another_pv->vertices[0].y < pv->vertices[0].y) + { + int min_y = another_pv->vertices[0].y; + pv->vertices[0].y = min_y; + pv->vertices[1].y = min_y; + pv->vertices[4].y = min_y; + /* pv->vertices[5].y = min_y; */ + } + + /* grow down */ + /* bottom vertices 2, 3, 6, 7 */ + if (another_pv->vertices[3].y > pv->vertices[3].y) + { + int may_y = another_pv->vertices[3].y; + /* pv->vertices[2].y = may_y; */ + pv->vertices[3].y = may_y; + /* pv->vertices[6].y = may_y; */ + /* pv->vertices[7].y = may_y; */ + } + + /* grow forward */ + /* front vertices 0, 1, 2, 3 */ + if (another_pv->vertices[0].z < pv->vertices[0].z) + { + int min_z = another_pv->vertices[0].z; + pv->vertices[0].z = min_z; + pv->vertices[1].z = min_z; + /* pv->vertices[2].z = min_z; */ + pv->vertices[3].z = min_z; + } + + /* grow backward */ + /* back vertices 4, 5, 6, 7 */ + if (another_pv->vertices[4].z > pv->vertices[4].z) + { + int maz_z = another_pv->vertices[4].z; + pv->vertices[4].z = maz_z; + /* pv->vertices[5].z = maz_z; */ + /* pv->vertices[6].z = maz_z; */ + /* pv->vertices[7].z = maz_z; */ + } + + if (pv->vertices[4].z == pv->vertices[0].z) + pv->is_2d = TRUE; + else + pv->is_2d = FALSE; + +done: + pv->is_empty = FALSE; + pv->is_complete = FALSE; +} + +/* The paint_volume setters only update vertices 0, 1, 3 and + * 4 since the others can be drived from them. + * + * This will set pv->completed = TRUE; + */ +void +_clutter_paint_volume_complete (ClutterPaintVolume *pv) +{ + if (pv->is_complete || pv->is_empty) + return; + + /* TODO: it is possible to complete non axis aligned volumes too. */ + g_return_if_fail (pv->is_axis_aligned); + + /* front-bottom-right */ + pv->vertices[2].x = pv->vertices[1].x; + pv->vertices[2].y = pv->vertices[3].y; + pv->vertices[2].z = pv->vertices[0].z; + + if (G_UNLIKELY (!pv->is_2d)) + { + /* back-top-right */ + pv->vertices[5].x = pv->vertices[1].x; + pv->vertices[5].y = pv->vertices[0].y; + pv->vertices[5].z = pv->vertices[4].z; + + /* back-bottom-right */ + pv->vertices[6].x = pv->vertices[1].x; + pv->vertices[6].y = pv->vertices[3].y; + pv->vertices[6].z = pv->vertices[4].z; + + /* back-bottom-left */ + pv->vertices[7].x = pv->vertices[0].x; + pv->vertices[7].y = pv->vertices[3].y; + pv->vertices[7].z = pv->vertices[4].z; + } + + pv->is_complete = TRUE; +} + +/* + * _clutter_paint_volume_get_box: + * @pv: a #ClutterPaintVolume + * @box: a pixel aligned #ClutterGeometry + * + * Transforms a 3D paint volume into a 2D bounding box in the + * same coordinate space as the 3D paint volume. + * + * To get an actors "paint box" you should first project + * the paint volume into window coordinates before getting + * the 2D bounding box. + * + * The coordinates of the returned box are not clamped to + * integer pixel values, if you need them to be clamped you can use + * clutter_actor_box_clamp_to_pixel() + * + * Since: 1.4 + */ +void +_clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv, + ClutterActorBox *box) +{ + gfloat x_min, y_min, x_max, y_max; + ClutterVertex *vertices; + int count; + gint i; + + g_return_if_fail (pv != NULL); + g_return_if_fail (box != NULL); + + if (pv->is_empty) + { + box->x1 = box->x2 = pv->vertices[0].x; + box->y1 = box->y2 = pv->vertices[0].y; + return; + } + + /* Updates the vertices we calculate lazily + * (See ClutterPaintVolume typedef for more details) */ + _clutter_paint_volume_complete (pv); + + vertices = pv->vertices; + + x_min = x_max = vertices[0].x; + y_min = y_max = vertices[0].y; + + /* Most actors are 2D so we only have to look at the front 4 + * vertices of the paint volume... */ + if (G_LIKELY (pv->is_2d)) + count = 4; + else + count = 8; + + for (i = 1; i < count; i++) + { + if (vertices[i].x < x_min) + x_min = vertices[i].x; + else if (vertices[i].x > x_max) + x_max = vertices[i].x; + + if (vertices[i].y < y_min) + y_min = vertices[i].y; + else if (vertices[i].y > y_max) + y_max = vertices[i].y; + } + + box->x1 = x_min; + box->y1 = y_min; + box->x2 = x_max; + box->y2 = y_max; +} + +void +_clutter_paint_volume_project (ClutterPaintVolume *pv, + const CoglMatrix *modelview, + const CoglMatrix *projection, + const int *viewport) +{ + int transform_count; + + if (pv->is_empty) + { + /* Just transform the origin... */ + _clutter_util_fully_transform_vertices (modelview, + projection, + viewport, + pv->vertices, + pv->vertices, + 1); + return; + } + + /* All the vertices must be up to date, since after the projection + * it wont be trivial to derive the other vertices. */ + _clutter_paint_volume_complete (pv); + + /* Most actors are 2D so we only have to transform the front 4 + * vertices of the paint volume... */ + if (G_LIKELY (pv->is_2d)) + transform_count = 4; + else + transform_count = 8; + + _clutter_util_fully_transform_vertices (modelview, + projection, + viewport, + pv->vertices, + pv->vertices, + transform_count); + + pv->is_axis_aligned = FALSE; +} + +void +_clutter_paint_volume_transform (ClutterPaintVolume *pv, + const CoglMatrix *matrix) +{ + int transform_count; + int i; + + if (pv->is_empty) + { + gfloat w = 1; + /* Just transform the origin */ + cogl_matrix_transform_point (matrix, + &pv->vertices[0].x, + &pv->vertices[0].y, + &pv->vertices[0].z, + &w); + return; + } + + /* All the vertices must be up to date, since after the transform + * it wont be trivial to derive the other vertices. */ + _clutter_paint_volume_complete (pv); + + /* Most actors are 2D so we only have to transform the front 4 + * vertices of the paint volume... */ + if (G_LIKELY (pv->is_2d)) + transform_count = 4; + else + transform_count = 8; + + + for (i = 0; i < transform_count; i++) + { + gfloat w = 1; + cogl_matrix_transform_point (matrix, + &pv->vertices[i].x, + &pv->vertices[i].y, + &pv->vertices[i].z, + &w); + } + + pv->is_axis_aligned = FALSE; +} + + +/* Given a paint volume that has been transformed by an arbitrary + * modelview and is no longer axis aligned, this derives a replacement + * that is axis aligned. */ +void +_clutter_paint_volume_axis_align (ClutterPaintVolume *pv) +{ + int count; + int i; + ClutterVertex origin; + float max_x; + float max_y; + float max_z; + + if (pv->is_empty) + return; + + g_return_if_fail (pv->is_complete); + + if (G_LIKELY (pv->is_axis_aligned)) + return; + + if (G_LIKELY (pv->vertices[0].x == pv->vertices[1].x && + pv->vertices[0].y == pv->vertices[3].y && + pv->vertices[0].z == pv->vertices[4].y)) + { + pv->is_axis_aligned = TRUE; + return; + } + + origin = pv->vertices[0]; + max_x = pv->vertices[0].x; + max_y = pv->vertices[0].y; + max_z = pv->vertices[0].z; + + count = pv->is_2d ? 4 : 8; + for (i = 1; i < count; i++) + { + if (pv->vertices[i].x < origin.x) + origin.x = pv->vertices[i].x; + else if (pv->vertices[i].x > max_x) + max_x = pv->vertices[i].x; + + if (pv->vertices[i].y < origin.y) + origin.y = pv->vertices[i].y; + else if (pv->vertices[i].y > max_y) + max_y = pv->vertices[i].y; + + if (pv->vertices[i].z < origin.z) + origin.z = pv->vertices[i].z; + else if (pv->vertices[i].z > max_z) + max_z = pv->vertices[i].z; + } + + pv->vertices[0] = origin; + + pv->vertices[1].x = max_x; + pv->vertices[1].y = origin.y; + pv->vertices[1].z = origin.z; + + pv->vertices[3].x = origin.x; + pv->vertices[3].y = max_y; + pv->vertices[3].z = origin.z; + + pv->vertices[4].x = origin.x; + pv->vertices[4].y = origin.y; + pv->vertices[4].z = max_z; + + pv->is_complete = FALSE; + pv->is_axis_aligned = TRUE; + + if (pv->vertices[4].z == pv->vertices[0].z) + pv->is_2d = TRUE; + else + pv->is_2d = FALSE; +} + + diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 340990b9f..5d53e089c 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -510,13 +510,25 @@ void _clutter_paint_volume_init_static (ClutterActor *actor, ClutterPaintVolume *_clutter_paint_volume_new (ClutterActor *actor); void _clutter_paint_volume_copy_static (const ClutterPaintVolume *src_pv, ClutterPaintVolume *dst_pv); - +void _clutter_paint_volume_complete (ClutterPaintVolume *pv); +void _clutter_paint_volume_transform (ClutterPaintVolume *pv, + const CoglMatrix *matrix); void _clutter_paint_volume_project (ClutterPaintVolume *pv, const CoglMatrix *modelview, const CoglMatrix *projection, const int *viewport); void _clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv, ClutterActorBox *box); +void _clutter_paint_volume_axis_align (ClutterPaintVolume *pv); + + + +void _clutter_util_fully_transform_vertices (const CoglMatrix *modelview, + const CoglMatrix *projection, + const int *viewport, + const ClutterVertex *vertices_in, + ClutterVertex *vertices_out, + int n_vertices); G_END_DECLS diff --git a/clutter/clutter-util.c b/clutter/clutter-util.c index 8d5698c3a..aa4056d1f 100644 --- a/clutter/clutter-util.c +++ b/clutter/clutter-util.c @@ -73,3 +73,48 @@ _clutter_gettext (const gchar *str) { return g_dgettext (GETTEXT_PACKAGE, str); } + +/* Help macros to scale from OpenGL <-1,1> coordinates system to + * window coordinates ranging [0,window-size] + */ +#define MTX_GL_SCALE_X(x,w,v1,v2) ((((((x) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) +#define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - (((((y) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) +#define MTX_GL_SCALE_Z(z,w,v1,v2) (MTX_GL_SCALE_X ((z), (w), (v1), (v2))) + +void +_clutter_util_fully_transform_vertices (const CoglMatrix *modelview, + const CoglMatrix *projection, + const int *viewport, + const ClutterVertex *vertices_in, + ClutterVertex *vertices_out, + int n_vertices) +{ + CoglMatrix modelview_projection; + int i; + + /* XXX: we should find a way to cache this per actor */ + cogl_matrix_multiply (&modelview_projection, + projection, + modelview); + + for (i = 0; i < n_vertices; i++) + { + const ClutterVertex *vertex_in = &vertices_in[i]; + ClutterVertex *vertex_out = &vertices_out[i]; + gfloat x, y, z, w; + + x = vertex_in->x; + y = vertex_in->y; + z = vertex_in->z; + w = 1.0; + + /* Transform the point using the modelview matrix */ + cogl_matrix_transform_point (&modelview_projection, &x, &y, &z, &w); + + /* Finally translate from OpenGL coords to window coords */ + vertex_out->x = MTX_GL_SCALE_X (x, w, viewport[2], viewport[0]); + vertex_out->y = MTX_GL_SCALE_Y (y, w, viewport[3], viewport[1]); + vertex_out->z = MTX_GL_SCALE_Z (z, w, viewport[2], viewport[0]); + } +} +