mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 19:42:05 +00:00
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:
parent
72eeb8e809
commit
3040b140bc
@ -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 = \
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*<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
|
||||
* pointer since we return a pointer directly to the cached
|
||||
* PaintVolume associated with the actor and don't want the user to
|
||||
|
817
clutter/clutter-paint-volume.c
Normal file
817
clutter/clutter-paint-volume.c
Normal 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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user