mutter/cogl/cogl-matrix-stack.c

435 lines
11 KiB
C
Raw Normal View History

/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2009 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Authors:
* Havoc Pennington <hp@pobox.com> for litl
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl.h"
#include "cogl-context.h"
#include "cogl-internal.h"
#include "cogl-matrix-stack.h"
typedef struct {
CoglMatrix matrix;
gboolean is_identity;
/* count of pushes with no changes; when a change is
* requested, we create a new state and decrement this
*/
int push_count;
} CoglMatrixState;
/**
* CoglMatrixStack:
*
* Stores a cogl-side matrix stack, which we use as a cache
* so we can get the matrix efficiently when using indirect
* rendering.
*/
struct _CoglMatrixStack
{
GSList *stack;
/* which state does GL have, NULL if unknown */
CoglMatrixState *flushed_state;
gboolean flushed_identity;
};
/* XXX: this doesn't initialize the matrix! */
static CoglMatrixState*
_cogl_matrix_state_new (void)
{
CoglMatrixState *state;
state = g_slice_new (CoglMatrixState);
state->push_count = 0;
state->is_identity = FALSE;
return state;
}
static void
_cogl_matrix_state_destroy (CoglMatrixState *state)
{
g_slice_free (CoglMatrixState, state);
}
static CoglMatrixState*
_cogl_matrix_stack_top (CoglMatrixStack *stack)
{
return stack->stack->data;
}
/* XXX:
* Operations like scale, translate, rotate etc need to have an
* initialized state->matrix to work with, so they will pass
* initialize = TRUE.
*
* _cogl_matrix_stack_load_identity and _cogl_matrix_stack_set on the
* other hand don't so they will pass initialize = FALSE
*
* NB: Identity matrices are represented by setting
* state->is_identity=TRUE in which case state->matrix will be
* uninitialized.
*/
static CoglMatrixState *
_cogl_matrix_stack_top_mutable (CoglMatrixStack *stack,
gboolean initialize)
{
CoglMatrixState *state;
CoglMatrixState *new_top;
state = _cogl_matrix_stack_top (stack);
if (state->push_count == 0)
{
if (state->is_identity && initialize)
cogl_matrix_init_identity (&state->matrix);
return state;
}
state->push_count -= 1;
new_top = _cogl_matrix_state_new ();
if (initialize)
{
if (state->is_identity)
cogl_matrix_init_identity (&new_top->matrix);
else
new_top->matrix = state->matrix;
if (stack->flushed_state == state)
stack->flushed_state = new_top;
}
stack->stack = g_slist_prepend (stack->stack, new_top);
return new_top;
}
CoglMatrixStack*
_cogl_matrix_stack_new (void)
{
CoglMatrixStack *stack;
CoglMatrixState *state;
stack = g_slice_new0 (CoglMatrixStack);
state = _cogl_matrix_state_new ();
state->is_identity = TRUE;
stack->stack = g_slist_prepend (stack->stack, state);
return stack;
}
void
_cogl_matrix_stack_destroy (CoglMatrixStack *stack)
{
while (stack->stack)
{
CoglMatrixState *state;
state = stack->stack->data;
_cogl_matrix_state_destroy (state);
stack->stack =
g_slist_delete_link (stack->stack,
stack->stack);
}
g_slice_free (CoglMatrixStack, stack);
}
void
_cogl_matrix_stack_push (CoglMatrixStack *stack)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top (stack);
/* we lazily create a new stack top if someone changes the matrix
* while push_count > 0
*/
state->push_count += 1;
}
void
_cogl_matrix_stack_pop (CoglMatrixStack *stack)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top (stack);
if (state->push_count > 0)
{
state->push_count -= 1;
}
else
{
if (stack->stack->next == NULL)
{
g_warning ("Too many matrix pops");
return;
}
if (stack->flushed_state == state)
{
stack->flushed_state = NULL;
}
stack->stack =
g_slist_delete_link (stack->stack,
stack->stack);
_cogl_matrix_state_destroy (state);
}
}
void
_cogl_matrix_stack_load_identity (CoglMatrixStack *stack)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top_mutable (stack, FALSE);
/* NB: Identity matrices are represented by setting
* state->is_identity = TRUE and leaving state->matrix
* uninitialized.
*
* This is done to optimize the heavy usage of
* _cogl_matrix_stack_load_identity by the Cogl Journal.
*/
if (!state->is_identity)
{
state->is_identity = TRUE;
/* mark dirty */
stack->flushed_state = NULL;
}
}
void
_cogl_matrix_stack_scale (CoglMatrixStack *stack,
float x,
float y,
float z)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top_mutable (stack, TRUE);
cogl_matrix_scale (&state->matrix, x, y, z);
/* mark dirty */
stack->flushed_state = NULL;
state->is_identity = FALSE;
}
void
_cogl_matrix_stack_translate (CoglMatrixStack *stack,
float x,
float y,
float z)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top_mutable (stack, TRUE);
cogl_matrix_translate (&state->matrix, x, y, z);
/* mark dirty */
stack->flushed_state = NULL;
state->is_identity = FALSE;
}
void
_cogl_matrix_stack_rotate (CoglMatrixStack *stack,
float angle,
float x,
float y,
float z)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top_mutable (stack, TRUE);
cogl_matrix_rotate (&state->matrix, angle, x, y, z);
/* mark dirty */
stack->flushed_state = NULL;
state->is_identity = FALSE;
}
void
_cogl_matrix_stack_multiply (CoglMatrixStack *stack,
const CoglMatrix *matrix)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top_mutable (stack, TRUE);
cogl_matrix_multiply (&state->matrix, &state->matrix, matrix);
/* mark dirty */
stack->flushed_state = NULL;
state->is_identity = FALSE;
}
void
_cogl_matrix_stack_frustum (CoglMatrixStack *stack,
float left,
float right,
float bottom,
float top,
float z_near,
float z_far)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top_mutable (stack, TRUE);
cogl_matrix_frustum (&state->matrix,
left, right, bottom, top,
z_near, z_far);
/* mark dirty */
stack->flushed_state = NULL;
state->is_identity = FALSE;
}
void
_cogl_matrix_stack_perspective (CoglMatrixStack *stack,
float fov_y,
float aspect,
float z_near,
float z_far)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top_mutable (stack, TRUE);
cogl_matrix_perspective (&state->matrix,
fov_y, aspect, z_near, z_far);
/* mark dirty */
stack->flushed_state = NULL;
state->is_identity = FALSE;
}
void
_cogl_matrix_stack_ortho (CoglMatrixStack *stack,
float left,
float right,
float bottom,
float top,
float z_near,
float z_far)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top_mutable (stack, TRUE);
cogl_matrix_ortho (&state->matrix,
left, right, bottom, top, z_near, z_far);
/* mark dirty */
stack->flushed_state = NULL;
state->is_identity = FALSE;
}
void
_cogl_matrix_stack_get (CoglMatrixStack *stack,
CoglMatrix *matrix)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top (stack);
/* NB: identity matrices are lazily initialized because we can often avoid
* initializing them at all if nothing is pushed on top of them since we
* load them using glLoadIdentity()
*
* The Cogl journal typically loads an identiy matrix because it performs
* software transformations, which is why we have optimized this case.
*/
if (state->is_identity)
cogl_matrix_init_identity (matrix);
else
*matrix = state->matrix;
}
void
_cogl_matrix_stack_set (CoglMatrixStack *stack,
const CoglMatrix *matrix)
{
CoglMatrixState *state;
state = _cogl_matrix_stack_top_mutable (stack, FALSE);
state->matrix = *matrix;
/* mark dirty */
stack->flushed_state = NULL;
state->is_identity = FALSE;
}
void
_cogl_matrix_stack_flush_to_gl (CoglMatrixStack *stack,
CoglMatrixMode mode)
{
CoglMatrixState *state;
GLenum gl_mode;
state = _cogl_matrix_stack_top (stack);
if (stack->flushed_state == state)
return;
switch (mode)
{
case COGL_MATRIX_MODELVIEW:
gl_mode = GL_MODELVIEW;
break;
case COGL_MATRIX_PROJECTION:
gl_mode = GL_PROJECTION;
break;
case COGL_MATRIX_TEXTURE:
gl_mode = GL_TEXTURE;
break;
}
GE (glMatrixMode (gl_mode));
/* In theory it might help the GL implementation if we used our
* local analysis of the matrix and called Translate/Scale rather
* than LoadMatrix to send a 2D matrix
*/
if (state->is_identity)
{
if (!stack->flushed_identity)
GE (glLoadIdentity ());
stack->flushed_identity = TRUE;
}
else
{
GE (glLoadMatrixf (cogl_matrix_get_array (&state->matrix)));
stack->flushed_identity = FALSE;
}
stack->flushed_state = state;
}
void
_cogl_matrix_stack_dirty (CoglMatrixStack *stack)
{
stack->flushed_state = NULL;
stack->flushed_identity = FALSE;
}