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.
This commit is contained in:
Robert Bragg 2010-09-08 20:39:15 +01:00
parent 72eeb8e809
commit 3040b140bc
5 changed files with 882 additions and 833 deletions

View File

@ -238,6 +238,7 @@ source_c = \
$(srcdir)/clutter-timeout-pool.c \ $(srcdir)/clutter-timeout-pool.c \
$(srcdir)/clutter-units.c \ $(srcdir)/clutter-units.c \
$(srcdir)/clutter-util.c \ $(srcdir)/clutter-util.c \
$(srcdir)/clutter-paint-volume.c \
$(NULL) $(NULL)
source_c_priv = \ source_c_priv = \

View File

@ -652,7 +652,6 @@ static void _clutter_actor_get_relative_modelview (ClutterActor *self,
CoglMatrix *matrix); CoglMatrix *matrix);
static ClutterPaintVolume *_clutter_actor_get_paint_volume_mutable (ClutterActor *self); 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 /* Helper macro which translates by the anchor coord, applies the
given transformation and then translates back */ 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); 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 static gboolean
_clutter_actor_fully_transform_vertices (ClutterActor *self, _clutter_actor_fully_transform_vertices (ClutterActor *self,
const ClutterVertex *vertices_in, const ClutterVertex *vertices_in,
@ -2001,12 +1956,12 @@ _clutter_actor_fully_transform_vertices (ClutterActor *self,
&viewport[2], &viewport[2],
&viewport[3]); &viewport[3]);
_fully_transform_vertices (&modelview, _clutter_util_fully_transform_vertices (&modelview,
&projection, &projection,
viewport, viewport,
vertices_in, vertices_in,
vertices_out, vertices_out,
n_vertices); n_vertices);
return TRUE; return TRUE;
} }
@ -11831,787 +11786,6 @@ clutter_actor_has_key_focus (ClutterActor *self)
return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == 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;
}
/*<private>
* _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;
}
/*<private>
* _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.
*
* <note>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()</note>
*
* 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 /* The public clutter_actor_get_paint_volume API returns a const
* pointer since we return a pointer directly to the cached * pointer since we return a pointer directly to the cached
* PaintVolume associated with the actor and don't want the user to * PaintVolume associated with the actor and don't want the user to

View File

@ -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
* <http://www.gnu.org/licenses/>.
*
* Authors:
* Robert Bragg <robert@linux.intel.com>
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib-object.h>
#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;
}
/*<private>
* _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;
}
/*<private>
* _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.
*
* <note>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()</note>
*
* 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;
}

View File

@ -510,13 +510,25 @@ void _clutter_paint_volume_init_static (ClutterActor *actor,
ClutterPaintVolume *_clutter_paint_volume_new (ClutterActor *actor); ClutterPaintVolume *_clutter_paint_volume_new (ClutterActor *actor);
void _clutter_paint_volume_copy_static (const ClutterPaintVolume *src_pv, void _clutter_paint_volume_copy_static (const ClutterPaintVolume *src_pv,
ClutterPaintVolume *dst_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, void _clutter_paint_volume_project (ClutterPaintVolume *pv,
const CoglMatrix *modelview, const CoglMatrix *modelview,
const CoglMatrix *projection, const CoglMatrix *projection,
const int *viewport); const int *viewport);
void _clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv, void _clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv,
ClutterActorBox *box); 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 G_END_DECLS

View File

@ -73,3 +73,48 @@ _clutter_gettext (const gchar *str)
{ {
return g_dgettext (GETTEXT_PACKAGE, 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]);
}
}