/* * 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 for litl * Robert Bragg */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "cogl.h" #include "cogl-context.h" #include "cogl-internal.h" #include "cogl-current-matrix.h" #include "cogl-matrix-stack.h" #ifdef HAVE_COGL_GLES2 #include "cogl-gles2-wrapper.h" #define glFrustum(L,R,B,T,N,F) \ cogl_wrap_glFrustumf((GLfloat)L, (GLfloat)R, (GLfloat)B, \ (GLfloat)T, (GLfloat)N, (GLfloat)F) #elif defined (HAVE_COGL_GLES) #define glFrustum(L,R,B,T,N,F) \ glFrustumf((GLfloat)L, (GLfloat)R, (GLfloat)B, \ (GLfloat)T, (GLfloat)N, (GLfloat)F) #define glOrtho glOrthof #endif #include #include static void _cogl_get_client_stack (CoglContext *ctx, CoglMatrixMode mode, CoglMatrixStack **current_stack_p) { if (ctx->modelview_stack && mode == COGL_MATRIX_MODELVIEW) *current_stack_p = ctx->modelview_stack; else if (ctx->projection_stack && mode == COGL_MATRIX_PROJECTION) *current_stack_p = ctx->projection_stack; else *current_stack_p = NULL; } #define _COGL_GET_CONTEXT_AND_STACK(contextvar, stackvar, rval) \ CoglMatrixStack *stackvar; \ _COGL_GET_CONTEXT (contextvar, rval); \ _cogl_get_client_stack (contextvar, ctx->matrix_mode, &stackvar) void _cogl_set_current_matrix (CoglMatrixMode mode) { GLenum gl_mode; CoglMatrixStack *current_stack; \ _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (mode == ctx->matrix_mode) return; ctx->matrix_mode = mode; /* If we have a client side stack then then the GL matrix mode only needs * changing when we come to flush it to OpenGL */ _cogl_get_client_stack (ctx, mode, ¤t_stack); if (current_stack) return; gl_mode = 0; /* silence compiler warning */ 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)); } void _cogl_current_matrix_push (void) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_push (current_stack); else GE (glPushMatrix ()); } void _cogl_current_matrix_pop (void) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_pop (current_stack); else GE (glPopMatrix ()); } void _cogl_current_matrix_identity (void) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_load_identity (current_stack); else GE (glLoadIdentity ()); } void _cogl_current_matrix_load (const CoglMatrix *matrix) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_set (current_stack, matrix); else GE (glLoadMatrixf (cogl_matrix_get_array (matrix))); } void _cogl_current_matrix_multiply (const CoglMatrix *matrix) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_multiply (current_stack, matrix); else GE (glMultMatrixf (cogl_matrix_get_array (matrix))); } void _cogl_current_matrix_rotate (float angle, float x, float y, float z) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_rotate (current_stack, angle, x, y, z); else GE (glRotatef (angle, x, y, z)); } void _cogl_current_matrix_scale (float x, float y, float z) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_scale (current_stack, x, y, z); else GE (glScalef (x, y, z)); } void _cogl_current_matrix_translate (float x, float y, float z) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_translate (current_stack, x, y, z); else GE (glTranslatef (x, y, z)); } void _cogl_current_matrix_frustum (float left, float right, float bottom, float top, float near_val, float far_val) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_frustum (current_stack, left, right, bottom, top, near_val, far_val); else GE (glFrustum (left, right, bottom, top, near_val, far_val)); } void _cogl_current_matrix_perspective (float fov_y, float aspect, float z_near, float z_far) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_perspective (current_stack, fov_y, aspect, z_near, z_far); else { /* NB: There is no glPerspective() (only gluPerspective()) so we use * cogl_matrix_perspective: */ CoglMatrix matrix; _cogl_get_matrix (ctx->matrix_mode, &matrix); cogl_matrix_perspective (&matrix, fov_y, aspect, z_near, z_far); _cogl_current_matrix_load (&matrix); } } void _cogl_current_matrix_ortho (float left, float right, float bottom, float top, float near_val, float far_val) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack != NULL) _cogl_matrix_stack_ortho (current_stack, left, right, bottom, top, near_val, far_val); else { #ifdef HAVE_COGL_GLES2 /* NB: GLES 2 has no glOrtho(): */ CoglMatrix matrix; _cogl_get_matrix (ctx->matrix_mode, &matrix); cogl_matrix_ortho (&matrix, left, right, bottom, top, near_val, far_val); _cogl_current_matrix_load (&matrix); #else GE (glOrtho (left, right, bottom, top, near_val, far_val)); #endif } } void _cogl_get_matrix (CoglMatrixMode mode, CoglMatrix *matrix) { CoglMatrixStack *current_stack; _COGL_GET_CONTEXT (ctx, NO_RETVAL); _cogl_get_client_stack (ctx, mode, ¤t_stack); if (current_stack) _cogl_matrix_stack_get (current_stack, matrix); else { GLenum gl_mode; GLfloat gl_matrix[16]; gl_mode = 0; /* silence compiler warning */ switch (mode) { case COGL_MATRIX_MODELVIEW: gl_mode = GL_MODELVIEW_MATRIX; break; case COGL_MATRIX_PROJECTION: gl_mode = GL_PROJECTION_MATRIX; break; case COGL_MATRIX_TEXTURE: gl_mode = GL_TEXTURE_MATRIX; break; } /* Note: we have a redundant copy happening here. If that turns out to be * a problem then, since this is internal to Cogl, we could pass the * CoglMatrix pointer directly to glGetFloatv; the only problem with that * is that if we later add internal flags to CoglMatrix they will need to * be initialized seperatly. */ GE (glGetFloatv (gl_mode, gl_matrix)); cogl_matrix_init_from_array (matrix, gl_matrix); } } void _cogl_set_matrix (const CoglMatrix *matrix) { _cogl_current_matrix_load (matrix); } void _cogl_current_matrix_state_init (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); ctx->matrix_mode = COGL_MATRIX_MODELVIEW; ctx->modelview_stack = NULL; ctx->projection_stack = NULL; #if 0 if (ctx->indirect || cogl_debug_flags & COGL_DEBUG_FORCE_CLIENT_SIDE_MATRICES) #endif { ctx->modelview_stack = _cogl_matrix_stack_new (); ctx->projection_stack = _cogl_matrix_stack_new (); } } void _cogl_current_matrix_state_destroy (void) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack) _cogl_matrix_stack_destroy (current_stack); } void _cogl_current_matrix_state_flush (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (ctx->matrix_mode != COGL_MATRIX_MODELVIEW && ctx->matrix_mode != COGL_MATRIX_PROJECTION) { g_warning ("matrix state must be flushed in " "MODELVIEW or PROJECTION mode"); return; } if (ctx->modelview_stack && ctx->matrix_mode == COGL_MATRIX_MODELVIEW) { _cogl_matrix_stack_flush_to_gl (ctx->modelview_stack, GL_MODELVIEW); } else if (ctx->projection_stack && ctx->matrix_mode == COGL_MATRIX_PROJECTION) { _cogl_matrix_stack_flush_to_gl (ctx->projection_stack, GL_PROJECTION); } } void _cogl_current_matrix_state_dirty (void) { _COGL_GET_CONTEXT_AND_STACK (ctx, current_stack, NO_RETVAL); if (current_stack) _cogl_matrix_stack_dirty (current_stack); } void cogl_push_matrix (void) { _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_push (); } void cogl_pop_matrix (void) { _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_pop (); } void cogl_scale (float x, float y, float z) { _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_scale (x, y, z); } void cogl_translate (float x, float y, float z) { _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_translate (x, y, z); } void cogl_rotate (float angle, float x, float y, float z) { _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_rotate (angle, x, y, z); } void cogl_perspective (float fov_y, float aspect, float z_near, float z_far) { float ymax = z_near * tanf (fov_y * G_PI / 360.0); cogl_frustum (-ymax * aspect, /* left */ ymax * aspect, /* right */ -ymax, /* bottom */ ymax, /* top */ z_near, z_far); } void cogl_frustum (float left, float right, float bottom, float top, float z_near, float z_far) { float c, d; _COGL_GET_CONTEXT (ctx, NO_RETVAL); _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); _cogl_current_matrix_identity (); _cogl_current_matrix_frustum (left, right, bottom, top, z_near, z_far); /* Calculate and store the inverse of the matrix */ memset (ctx->inverse_projection, 0, sizeof (float) * 16); c = - (z_far + z_near) / (z_far - z_near); d = - (2 * (z_far * z_near)) / (z_far - z_near); #define M(row,col) ctx->inverse_projection[col*4+row] M(0,0) = (right - left) / (2 * z_near); M(0,3) = (right + left) / (2 * z_near); M(1,1) = (top - bottom) / (2 * z_near); M(1,3) = (top + bottom) / (2 * z_near); M(2,3) = -1.0; M(3,2) = 1.0 / d; M(3,3) = c / d; #undef M _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); } void cogl_ortho (float left, float right, float bottom, float top, float z_near, float z_far) { CoglMatrix ortho; _COGL_GET_CONTEXT (ctx, NO_RETVAL); cogl_matrix_init_identity (&ortho); cogl_matrix_ortho (&ortho, left, right, bottom, top, z_near, z_far); _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); _cogl_current_matrix_load (&ortho); /* Calculate and store the inverse of the matrix */ memset (ctx->inverse_projection, 0, sizeof (float) * 16); #define M(row,col) ctx->inverse_projection[col*4+row] M(0,0) = 1.0 / ortho.xx; M(0,3) = -ortho.xw; M(1,1) = 1.0 / ortho.yy; M(1,3) = -ortho.yw; M(2,2) = 1.0 / ortho.zz; M(2,3) = -ortho.zw; M(3,3) = 1.0; #undef M } void cogl_get_modelview_matrix (CoglMatrix *matrix) { _cogl_get_matrix (COGL_MATRIX_MODELVIEW, matrix); } void cogl_set_modelview_matrix (CoglMatrix *matrix) { _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_load (matrix); } void cogl_get_projection_matrix (CoglMatrix *matrix) { _cogl_get_matrix (COGL_MATRIX_PROJECTION, matrix); } void cogl_set_projection_matrix (CoglMatrix *matrix) { _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); _cogl_current_matrix_load (matrix); } void _cogl_flush_matrix_stacks (void) { _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); _cogl_current_matrix_state_flush (); _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_state_flush (); }