[multi-texturing] This adds a new cogl_multi_texture API for GL,GLES1 + GLES2

Multitexturing allows blending multiple layers of texture data when texturing
some geometry. A common use is for pre-baked light maps which can give nice
lighting effects relativly cheaply. Another is for dot-3 bump mapping, and
another is applying alpha channel masks.

The dot-3 bump mapping would be really nice one day, but currently cogl doesn't
support lighting so that's not dealt with in this patch.

notable limitations:
- It can only texture rectangles a.t.m - and like cogl_texture_rectangle there
is no support for rotated texturing.
- Sliced textures are not supported. I think I've figured out how to handle
layers with different slice sizes at least for rectangular geometry, but I'm
not sure how complex it becomes once rotations are possible and texturing
arbitrary cogl_polygons.
- Except for this new API, cogl still doesn't know about more than one texture
unit, and so has no way of caching any enables related to other units. So that
things don't break it's currently necessary to disable anything to do with
additional units as soon as we are done with them which isn't ideal.
- No clutter API yet.
This commit is contained in:
Robert Bragg 2008-12-11 15:33:38 +00:00 committed by Robert Bragg
parent b4438143e5
commit 509928cc76
16 changed files with 1260 additions and 179 deletions

View File

@ -385,6 +385,68 @@ void cogl_texture_polygon (CoglHandle handle,
CoglTextureVertex *vertices,
gboolean use_color);
/**
* cogl_muti_texture_new:
*
* Creates a multi layered texture object. When first created it has
* zero layers. You must use cogl_multi_texture_layer_set_texture to
* define new layers.
*/
CoglHandle cogl_multi_texture_new (void);
/**
* cogl_multi_texture_layer_set_texture:
* @multi_texture_handle: a @CoglHandle
* @layer: The index of the layer you want a handle for.
*
* A layer is implicitly created once you set a texture for a certain
* layer_index. The texture layers are blended together starting with
* the lowest index so the order is significant. It doesn't matter what
* order you create the layers in and you may use sparsely generated index
* values, it is only the relative index order that matters.
*/
void cogl_multi_texture_layer_set_texture (CoglHandle multi_texture_handle,
guint layer_index,
CoglHandle texture_handle);
/**
* cogl_multi_texture_layer_remove:
* @multi_texture_handle: a @CoglHandle
* @index: The index of the layer you want to remove.
*
* Removes a single texture layer.
*/
void cogl_multi_texture_layer_remove (CoglHandle multi_texture_handle,
guint layer_index);
/**
* cogl_multi_texture_rectangle:
* @handle: a @CoglHandle
* @x1: x coordinate upper left on screen.
* @y1: y coordinate upper left on screen.
* @x2: x coordinate lower right on screen.
* @y2: y coordinate lower right on screen.
* @texcoords: A multidimensional array containing sets of 4 texture
* coordinates - one set for each texture layer that has been created.
*
* Draw a rectangle combining multiple texture layers together
* where each layer can use different texture data and different texture
* coordinates.
*
* The texture coordinates are supplied as a contiguous array of
* CoglFixed items containing groups of [tx1, ty1, tx2, ty2] values
* that are interpreted in the same way as the corresponding arguments
* to cogl_texture_rectangle. The first group of coordinates are for the
* first layer (with the smallest layer_index) you _must_ supply as many
* groups of texture coordinates as you have layers.
*/
void cogl_multi_texture_rectangle (CoglHandle handle,
CoglFixed x1,
CoglFixed y1,
CoglFixed x2,
CoglFixed y2,
CoglFixed *tex_coords);
G_END_DECLS
#endif /* __COGL_TEXTURE_H__ */

View File

@ -57,6 +57,12 @@
#define COGL_HANDLE_DEFINE(TypeName, type_name, handle_array) \
\
static CoglHandle * \
_cogl_##type_name##_handle_from_pointer (Cogl##TypeName *obj) \
{ \
return (CoglHandle)obj; \
} \
\
static gint \
_cogl_##type_name##_handle_find (CoglHandle handle) \
{ \

View File

@ -58,6 +58,9 @@ cogl_create_context ()
_context->texture_handles = NULL;
_context->texture_vertices_size = 0;
_context->texture_vertices = NULL;
_context->multi_texture_handles = NULL;
_context->multi_texture_layer_handles = NULL;
_context->fbo_handles = NULL;
_context->draw_buffer = COGL_WINDOW_BUFFER;

View File

@ -66,6 +66,12 @@ typedef struct
CoglTextureGLVertex *texture_vertices;
gulong texture_vertices_size;
/* Multi Textures */
GArray *multi_texture_handles;
/* Multi Texture Layers */
GArray *multi_texture_layer_handles;
/* Framebuffer objects */
GArray *fbo_handles;
CoglBufferTarget draw_buffer;

View File

@ -309,6 +309,7 @@ typedef GLuint COGLuint;
#define CGL_MAX_TEXTURE_STACK_DEPTH GL_MAX_TEXTURE_STACK_DEPTH
#define CGL_MAX_VIEWPORT_DIMS GL_MAX_VIEWPORT_DIMS
#define CGL_MAX_CLIENT_ATTRIB_STACK_DEPTH GL_MAX_CLIENT_ATTRIB_STACK_DEPTH
#define CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS GL_MAX_TEXTURE_UNITS
#define CGL_ATTRIB_STACK_DEPTH GL_ATTRIB_STACK_DEPTH
#define CGL_CLIENT_ATTRIB_STACK_DEPTH GL_CLIENT_ATTRIB_STACK_DEPTH
#define CGL_COLOR_CLEAR_VALUE GL_COLOR_CLEAR_VALUE

View File

@ -28,9 +28,11 @@
#include "cogl-bitmap.h"
typedef struct _CoglTexture CoglTexture;
typedef struct _CoglTexSliceSpan CoglTexSliceSpan;
typedef struct _CoglSpanIter CoglSpanIter;
typedef struct _CoglTexture CoglTexture;
typedef struct _CoglTexSliceSpan CoglTexSliceSpan;
typedef struct _CoglSpanIter CoglSpanIter;
typedef struct _CoglMultiTexture CoglMultiTexture;
typedef struct _CoglMultiTextureLayer CoglMultiTextureLayer;
struct _CoglTexSliceSpan
{
@ -59,6 +61,25 @@ struct _CoglTexture
gboolean auto_mipmap;
};
struct _CoglMultiTextureLayer
{
guint ref_count;
guint index; /*!< lowest index is blended first then others
on top */
CoglTexture *tex; /*!< The texture for this layer, or NULL
for an empty layer */
/* TODO: Add more control over the texture environment for each texture
* unit. For example we should support dot3 normal mapping. */
};
struct _CoglMultiTexture
{
guint ref_count;
GList *layers;
};
CoglTexture*
_cogl_texture_pointer_from_handle (CoglHandle handle);

View File

@ -49,6 +49,16 @@
printf("err: 0x%x\n", err); \
} */
static void _cogl_texture_free (CoglTexture *tex);
static void _cogl_multi_texture_free (CoglMultiTexture *multi_texture);
static void _cogl_multi_texture_layer_free (CoglMultiTextureLayer *layer);
COGL_HANDLE_DEFINE (Texture, texture, texture_handles);
COGL_HANDLE_DEFINE (MultiTexture, multi_texture, multi_texture_handles);
COGL_HANDLE_DEFINE (MultiTextureLayer,
multi_texture_layer,
multi_texture_layer_handles);
struct _CoglSpanIter
{
gint index;
@ -66,11 +76,6 @@ struct _CoglSpanIter
gboolean intersects;
};
static void _cogl_texture_free (CoglTexture *tex);
COGL_HANDLE_DEFINE (Texture, texture, texture_handles);
static void
_cogl_texture_bitmap_free (CoglTexture *tex)
{
@ -2405,3 +2410,263 @@ cogl_texture_polygon (CoglHandle handle,
}
}
}
CoglHandle
cogl_multi_texture_new (void)
{
CoglMultiTexture *multi_tex = g_new0 (CoglMultiTexture, 1);
return _cogl_multi_texture_handle_new (multi_tex);
}
static void
_cogl_multi_texture_free (CoglMultiTexture *multi_tex)
{
g_list_foreach (multi_tex->layers,
(GFunc)cogl_multi_texture_layer_unref, NULL);
g_free (multi_tex);
}
static CoglMultiTextureLayer *
_cogl_multi_texture_get_layer (CoglMultiTexture *multi_tex, guint index)
{
CoglMultiTextureLayer *layer;
GList *tmp;
for (tmp = multi_tex->layers; tmp != NULL; tmp = tmp->next)
{
layer = tmp->data;
if (layer->index == index)
return layer;
/* The layers are always sorted, so we know this layer doesn't exists */
if (layer->index > index)
break;
}
/* NB: if we now insert a new layer before tmp, that will maintain order.
*/
layer = g_new (CoglMultiTextureLayer, 1);
layer->ref_count = 1;
layer->index = index;
/* Note: comment after for() loop above */
multi_tex->layers = g_list_insert_before (multi_tex->layers, tmp, layer);
return layer;
}
void
cogl_multi_texture_layer_set_texture (CoglHandle multi_texture_handle,
guint layer_index,
CoglHandle tex_handle)
{
CoglMultiTexture *multi_tex;
CoglMultiTextureLayer *layer;
CoglTexture *tex;
if (!cogl_is_multi_texture (multi_texture_handle)
|| !cogl_is_texture (tex_handle))
return;
multi_tex = _cogl_multi_texture_pointer_from_handle (multi_texture_handle);
layer = _cogl_multi_texture_get_layer (multi_tex, layer_index);
tex = _cogl_texture_pointer_from_handle (tex_handle);
cogl_texture_ref (tex_handle);
layer->tex = tex;
}
static void
_cogl_multi_texture_layer_free (CoglMultiTextureLayer *layer)
{
cogl_texture_unref (layer->tex);
g_free (layer);
}
void
cogl_multi_texture_layer_remove (CoglHandle multi_texture_handle,
guint layer_index)
{
CoglMultiTexture *multi_tex;
CoglMultiTextureLayer *layer;
GList *tmp;
/* Check if valid multi texture */
if (!cogl_is_multi_texture (multi_texture_handle))
return;
multi_tex = _cogl_multi_texture_pointer_from_handle (multi_texture_handle);
for (tmp = multi_tex->layers; tmp != NULL; tmp = tmp->next)
{
layer = tmp->data;
if (layer->index == layer_index)
{
CoglHandle handle =
_cogl_multi_texture_layer_handle_from_pointer (layer);
cogl_multi_texture_layer_unref (handle);
multi_tex->layers = g_list_remove (multi_tex->layers, layer);
return;
}
}
}
void
cogl_multi_texture_rectangle (CoglHandle multi_texture_handle,
CoglFixed x1,
CoglFixed y1,
CoglFixed x2,
CoglFixed y2,
CoglFixed *user_tex_coords)
{
CoglMultiTexture *multi_tex;
GLfloat quad_coords[8];
GList *tmp;
GList *valid_layers = NULL;
int count;
GLfloat *tex_coords_buff;
gulong enable_flags = 0;
/* FIXME - currently cogl deals with enabling texturing
* via enable flags, but that can't scale to n texture
* units. Currently we have to be carefull how we leave the
* environment so we don't break things. See the cleanup
* notes at the end of this function */
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
/* Check if valid multi texture */
if (!cogl_is_multi_texture (multi_texture_handle))
return;
multi_tex = _cogl_multi_texture_pointer_from_handle (multi_texture_handle);
#define CFX_F COGL_FIXED_TO_FLOAT
quad_coords[0] = CFX_F (x1);
quad_coords[1] = CFX_F (y1);
quad_coords[2] = CFX_F (x2);
quad_coords[3] = CFX_F (y1);
quad_coords[4] = CFX_F (x1);
quad_coords[5] = CFX_F (y2);
quad_coords[6] = CFX_F (x2);
quad_coords[7] = CFX_F (y2);
#undef CFX_F
enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
GE( glVertexPointer (2, GL_FLOAT, 0, quad_coords));
for (count = 0, tmp = multi_tex->layers;
tmp != NULL;
count++, tmp = tmp->next)
{
CoglMultiTextureLayer *layer = tmp->data;
/* Skip empty layers */
if (!layer->tex)
{
count--;
continue;
}
/* FIXME - currently we don't support sliced textures */
if (layer->tex->slice_gl_handles == NULL
|| layer->tex->slice_gl_handles->len < 1)
continue;
if (count >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
{
static gboolean shown_warning = FALSE;
if (!shown_warning)
{
g_warning ("Your driver does not support enough texture layers"
"to correctly handle this multi texturing");
shown_warning = TRUE;
}
/* NB: We make a best effort attempt to display as many layers as
* possible. */
break;
}
if (layer->tex->bitmap.format & COGL_A_BIT)
enable_flags |= COGL_ENABLE_BLEND;
valid_layers = g_list_prepend (valid_layers, layer);
}
valid_layers = g_list_reverse (valid_layers);
/* Enable blending if the geometry has an associated alpha color,
* or - see above - we also check each layer texture and if any has
* an alpha channel also enable blending. */
if (ctx->color_alpha < 255)
enable_flags |= COGL_ENABLE_BLEND;
cogl_enable (enable_flags);
/* NB: It could be that no valid texture layers were found, but
* we will still submit a non-textured rectangle in that case. */
if (count)
tex_coords_buff = alloca (sizeof(GLfloat) * 8 * count);
/* NB: valid_layers is in order, sorted by index */
for (count = 0, tmp = valid_layers;
tmp != NULL;
count++, tmp = tmp->next)
{
CoglMultiTextureLayer *layer = tmp->data;
CoglFixed *in_tex_coords = &user_tex_coords[count * 4];
GLfloat *out_tex_coords = &tex_coords_buff[count * 8];
GLenum gl_tex_handle;
#define CFX_F COGL_FIXED_TO_FLOAT
/* IN LAYOUT: [ tx1:0, ty1:1, tx2:2, ty2:3 ] */
out_tex_coords[0] = CFX_F (in_tex_coords[0]); /* tx1 */
out_tex_coords[1] = CFX_F (in_tex_coords[1]); /* ty1 */
out_tex_coords[2] = CFX_F (in_tex_coords[2]); /* tx2 */
out_tex_coords[3] = CFX_F (in_tex_coords[1]); /* ty1 */
out_tex_coords[4] = CFX_F (in_tex_coords[0]); /* tx1 */
out_tex_coords[5] = CFX_F (in_tex_coords[3]); /* ty2 */
out_tex_coords[6] = CFX_F (in_tex_coords[2]); /* tx2 */
out_tex_coords[7] = CFX_F (in_tex_coords[3]); /* ty2 */
#undef CFX_F
/* TODO - support sliced textures */
gl_tex_handle = g_array_index (layer->tex->slice_gl_handles, GLuint, 0);
GE (glActiveTexture (GL_TEXTURE0 + count));
GE (glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE));
GE (glBindTexture (GL_TEXTURE_2D, gl_tex_handle));
/* GE (glEnable (GL_TEXTURE_2D)); */
GE (glClientActiveTexture (GL_TEXTURE0 + count));
GE (glTexCoordPointer (2, GL_FLOAT, 0, out_tex_coords));
/* GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY)); */
/* FIXME - cogl only knows about one texture unit a.t.m
* (Also see cleanup note below) */
if (count == 0)
{
enable_flags |= COGL_ENABLE_TEXTURE_2D | COGL_ENABLE_TEXCOORD_ARRAY;
cogl_enable (enable_flags);
}
else
{
GE (glEnable (GL_TEXTURE_2D));
GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY));
}
}
glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
/* FIXME - cogl doesn't currently have a way of caching the
* enable states for more than one texture unit so for now,
* we just disable anything relating to additional units once
* we are done with them. */
while (--count > 0)
{
GE (glActiveTexture (GL_TEXTURE0 + count));
GE (glClientActiveTexture (GL_TEXTURE0 + count));
GE (glDisable (GL_TEXTURE_2D));
GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY));
}
}

View File

@ -61,7 +61,9 @@ cogl_create_context ()
_context->texture_handles = NULL;
_context->texture_vertices_size = 0;
_context->texture_vertices = NULL;
_context->multi_texture_handles = NULL;
_context->multi_texture_layer_handles = NULL;
_context->fbo_handles = NULL;
_context->program_handles = NULL;
_context->shader_handles = NULL;

View File

@ -68,6 +68,12 @@ typedef struct
CoglTextureGLVertex *texture_vertices;
gulong texture_vertices_size;
/* Multi Textures */
GArray *multi_texture_handles;
/* Multi Texture Layers */
GArray *multi_texture_layer_handles;
/* Framebuffer objects */
GArray *fbo_handles;
CoglBufferTarget draw_buffer;

View File

@ -162,7 +162,11 @@ G_BEGIN_DECLS
#define CGL_STENCIL_PASS_DEPTH_PASS GL_STENCIL_PASS_DEPTH_PASS
#define CGL_STENCIL_REF GL_STENCIL_REF
#define CGL_STENCIL_WRITEMASK GL_STENCIL_WRITEMASK
#ifdef HAVE_COGL_GLES2
#define CGL_MATRIX_MODE 0x0BA0 /* bad style but works for now */
#else
#define CGL_MATRIX_MODE GL_MATRIX_MODE
#endif
#define CGL_VIEWPORT GL_VIEWPORT
#define CGL_MODELVIEW_STACK_DEPTH GL_MODELVIEW_STACK_DEPTH
#define CGL_PROJECTION_STACK_DEPTH GL_PROJECTION_STACK_DEPTH
@ -190,7 +194,11 @@ G_BEGIN_DECLS
#define CGL_MAX_VIEWPORT_DIMS GL_MAX_VIEWPORT_DIMS
#define CGL_MAX_ELEMENTS_VERTICES GL_MAX_ELEMENTS_VERTICES
#define CGL_MAX_ELEMENTS_INDICES GL_MAX_ELEMENTS_INDICES
#define CGL_MAX_TEXTURE_UNITS GL_MAX_TEXTURE_UNITS
#ifdef HAVE_COGL_GLES2
#define CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
#else
#define CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS GL_MAX_TEXTURE_UNITS
#endif
#define CGL_SUBPIXEL_BITS GL_SUBPIXEL_BITS
#define CGL_RED_BITS GL_RED_BITS
#define CGL_GREEN_BITS GL_GREEN_BITS
@ -315,7 +323,7 @@ G_BEGIN_DECLS
/* PixelType */
/* GL_UNSIGNED_BYTE */
#define CGL_UNSIGNED_SHORT_4_4_4_4 GL_UNSIGNED_SHORT_4_4_4_4
#define CGL_UNSIGNED_SHORT_4_4_4_4 GL_UNSIGNED_SHORT_4_4_4_4
#define CGL_UNSIGNED_SHORT_5_5_5_1 GL_UNSIGNED_SHORT_5_5_5_1
#define CGL_UNSIGNED_SHORT_5_6_5 CGL_UNSIGNED_SHORT_5_6_5

View File

@ -1,16 +1,20 @@
/*** cogl_fixed_fragment_shader_start ***/
/*** cogl_fixed_fragment_shader_variables_start ***/
/* There is no default precision for floats in fragment shaders in
GLES 2 so we need to define one */
precision mediump float;
precision highp float;
/*** cogl_fixed_fragment_shader_inputs ***/
/* Inputs from the vertex shader */
varying vec4 frag_color;
varying vec2 tex_coord;
varying float fog_amount;
/*** cogl_fixed_fragment_shader_texturing_options ***/
/* Texturing options */
uniform sampler2D texture_unit;
/*** cogl_fixed_fragment_shader_fogging_options ***/
/* Fogging options */
uniform vec4 fog_color;
@ -18,28 +22,13 @@ uniform vec4 fog_color;
/* Alpha test options */
uniform float alpha_test_ref;
/*** cogl_fixed_fragment_shader_main_declare ***/
void
main (void)
{
/*** cogl_fixed_fragment_shader_texture_alpha_only ***/
/*** cogl_fixed_fragment_shader_main_start ***/
/* If the texture only has an alpha channel (eg, with the textures
from the pango renderer) then the RGB components will be
black. We want to use the RGB from the current color in that
case */
gl_FragColor = frag_color;
gl_FragColor.a *= texture2D (texture_unit, tex_coord).a;
/*** cogl_fixed_fragment_shader_texture ***/
/* This pointless extra variable is needed to work around an
apparent bug in the PowerVR drivers. Without it the alpha
blending seems to stop working */
vec4 frag_color_copy = frag_color;
gl_FragColor = frag_color_copy * texture2D (texture_unit, tex_coord);
/*** cogl_fixed_fragment_shader_solid_color ***/
gl_FragColor = frag_color;
/*** cogl_fixed_fragment_shader_fog ***/

View File

@ -1,34 +1,41 @@
/*** cogl_fixed_vertex_shader_start ***/
/*** cogl_fixed_vertex_shader_per_vertex_attribs ***/
/* Per vertex attributes */
attribute vec4 vertex_attrib;
attribute vec4 tex_coord_attrib;
attribute vec4 color_attrib;
/*** cogl_fixed_vertex_shader_transform_matrices ***/
/* Transformation matrices */
uniform mat4 modelview_matrix;
uniform mat4 mvp_matrix; /* combined modelview and projection matrix */
uniform mat4 texture_matrix;
/*** cogl_fixed_vertex_shader_output_variables ***/
/* Outputs to the fragment shader */
varying vec4 frag_color;
varying vec2 tex_coord;
varying float fog_amount;
/*** cogl_fixed_vertex_shader_fogging_options ***/
/* Fogging options */
uniform float fog_density;
uniform float fog_start;
uniform float fog_end;
/*** cogl_fixed_vertex_shader_main_start ***/
void
main (void)
{
vec4 transformed_tex_coord;
/* Calculate the transformed position */
gl_Position = mvp_matrix * vertex_attrib;
/* Calculate the transformed texture coordinate */
vec4 transformed_tex_coord = texture_matrix * tex_coord_attrib;
tex_coord = transformed_tex_coord.st / transformed_tex_coord.q;
/*** cogl_fixed_vertex_shader_frag_color_start ***/
/* Pass the interpolated vertex color on to the fragment shader */
frag_color = color_attrib;

View File

@ -38,6 +38,7 @@
#include "cogl-context.h"
#include "cogl-shader-private.h"
#include "cogl-program.h"
#include "cogl-internal.h"
#define _COGL_GET_GLES2_WRAPPER(wvar, retval) \
CoglGles2Wrapper *wvar; \
@ -66,9 +67,9 @@
while (0)
#define COGL_GLES2_WRAPPER_VERTEX_ATTRIB 0
#define COGL_GLES2_WRAPPER_TEX_COORD_ATTRIB 1
#define COGL_GLES2_WRAPPER_COLOR_ATTRIB 2
#define COGL_GLES2_WRAPPER_NORMAL_ATTRIB 3
#define COGL_GLES2_WRAPPER_COLOR_ATTRIB 1
#define COGL_GLES2_WRAPPER_NORMAL_ATTRIB 2
static GLuint
cogl_gles2_wrapper_create_shader (GLenum type, const char *source)
@ -109,13 +110,19 @@ cogl_gles2_wrapper_init (CoglGles2Wrapper *wrapper)
memset (wrapper, 0, sizeof (CoglGles2Wrapper));
/* Initialize the stacks */
cogl_wrap_glMatrixMode (GL_TEXTURE);
cogl_wrap_glLoadIdentity ();
cogl_wrap_glMatrixMode (GL_PROJECTION);
cogl_wrap_glLoadIdentity ();
cogl_wrap_glMatrixMode (GL_MODELVIEW);
cogl_wrap_glLoadIdentity ();
wrapper->texture_units =
g_array_new (FALSE, FALSE, sizeof (CoglGles2WrapperTextureUnit *));
/* The gl*ActiveTexture wrappers will initialise the texture
* stack for the texture unit when it's first activated */
cogl_wrap_glActiveTexture (GL_TEXTURE0);
cogl_wrap_glClientActiveTexture (GL_TEXTURE0);
/* Initialize the fogging options */
cogl_wrap_glDisable (GL_FOG);
cogl_wrap_glFogx (GL_FOG_MODE, GL_LINEAR);
@ -137,11 +144,14 @@ cogl_gles2_settings_equal (const CoglGles2WrapperSettings *a,
{
if (fragment_tests)
{
if (a->texture_2d_enabled != b->texture_2d_enabled)
return FALSE;
if (a->texture_2d_enabled && a->alpha_only != b->alpha_only)
return FALSE;
int i;
for (i = 0; i < a->n_texture_units; i++)
{
if (a->texture_units[i].enabled != b->texture_units[i].enabled)
return FALSE;
if (a->texture_units[i].alpha_only != b->texture_units[i].alpha_only)
return FALSE;
}
if (a->alpha_test_enabled != b->alpha_test_enabled)
return FALSE;
@ -165,6 +175,7 @@ cogl_gles2_get_vertex_shader (const CoglGles2WrapperSettings *settings)
GLuint shader_obj;
CoglGles2WrapperShader *shader;
GSList *node;
int i;
_COGL_GET_GLES2_WRAPPER (w, NULL);
@ -177,7 +188,47 @@ cogl_gles2_get_vertex_shader (const CoglGles2WrapperSettings *settings)
return (CoglGles2WrapperShader *) node->data;
/* Otherwise create a new shader */
shader_source = g_string_new (cogl_fixed_vertex_shader_start);
shader_source = g_string_new (cogl_fixed_vertex_shader_per_vertex_attribs);
for (i = 0; i < settings->n_texture_units; i++)
{
if (!settings->texture_units[i].enabled)
continue;
g_string_append_printf (shader_source,
"attribute vec4 multi_tex_coord_attrib%d;\n",
i);
}
g_string_append (shader_source, cogl_fixed_vertex_shader_transform_matrices);
g_string_append_printf (shader_source,
"uniform mat4 texture_matrix[%d];\n",
settings->n_texture_units);
g_string_append (shader_source, cogl_fixed_vertex_shader_output_variables);
g_string_append_printf (shader_source,
"varying vec2 tex_coord[%d];",
settings->n_texture_units);
g_string_append (shader_source, cogl_fixed_vertex_shader_fogging_options);
g_string_append (shader_source, cogl_fixed_vertex_shader_main_start);
for (i = 0; i < settings->n_texture_units; i++)
{
if (!settings->texture_units[i].enabled)
continue;
g_string_append_printf (shader_source,
"transformed_tex_coord = "
"texture_matrix[%d] "
" * multi_tex_coord_attrib%d;\n",
i, i);
g_string_append_printf (shader_source,
"tex_coord[%d] = transformed_tex_coord.st "
" / transformed_tex_coord.q;\n",
i);
}
g_string_append (shader_source, cogl_fixed_vertex_shader_frag_color_start);
if (settings->fog_enabled)
{
@ -207,10 +258,10 @@ cogl_gles2_get_vertex_shader (const CoglGles2WrapperSettings *settings)
shader_source->str);
g_string_free (shader_source, TRUE);
if (shader_obj == 0)
return NULL;
shader = g_slice_new (CoglGles2WrapperShader);
shader->shader = shader_obj;
shader->settings = *settings;
@ -228,6 +279,7 @@ cogl_gles2_get_fragment_shader (const CoglGles2WrapperSettings *settings)
GLuint shader_obj;
CoglGles2WrapperShader *shader;
GSList *node;
int i;
_COGL_GET_GLES2_WRAPPER (w, NULL);
@ -240,18 +292,61 @@ cogl_gles2_get_fragment_shader (const CoglGles2WrapperSettings *settings)
return (CoglGles2WrapperShader *) node->data;
/* Otherwise create a new shader */
shader_source = g_string_new (cogl_fixed_fragment_shader_start);
if (settings->texture_2d_enabled)
shader_source = g_string_new (cogl_fixed_fragment_shader_variables_start);
g_string_append (shader_source, cogl_fixed_fragment_shader_inputs);
g_string_append_printf (shader_source,
"varying vec2 tex_coord[%d];\n",
settings->n_texture_units);
g_string_append (shader_source, cogl_fixed_fragment_shader_texturing_options);
g_string_append_printf (shader_source,
"uniform sampler2D texture_unit[%d];\n",
settings->n_texture_units);
g_string_append (shader_source, cogl_fixed_fragment_shader_fogging_options);
g_string_append (shader_source, cogl_fixed_fragment_shader_main_declare);
g_string_append (shader_source, cogl_fixed_fragment_shader_main_start);
/* This pointless extra variable is needed to work around an
apparent bug in the PowerVR drivers. Without it the alpha
blending seems to stop working */
/* g_string_append (shader_source, "gl_FragColor = frag_color;\n");
*/
g_string_append (shader_source,
"vec4 frag_color_copy = frag_color;\n");
g_string_append (shader_source, "gl_FragColor = frag_color;\n");
for (i = 0; i < settings->n_texture_units; i++)
{
if (settings->alpha_only)
g_string_append (shader_source,
cogl_fixed_fragment_shader_texture_alpha_only);
if (settings->texture_units[i].alpha_only)
{
/* If the texture only has an alpha channel (eg, with the textures
from the pango renderer) then the RGB components will be
black. We want to use the RGB from the current color in that
case */
g_string_append_printf (
shader_source,
"gl_FragColor.a *= "
"texture2D (texture_unit[%d], tex_coord[%d]).a;\n",
i, i);
}
else
g_string_append (shader_source,
cogl_fixed_fragment_shader_texture);
{
g_string_append_printf (
shader_source,
"gl_FragColor *= "
"texture2D (texture_unit[%d], tex_coord[%d]);\n",
i, i);
}
}
else
g_string_append (shader_source, cogl_fixed_fragment_shader_solid_color);
/* FIXME */
g_string_append (shader_source, "gl_FragColor.r = 1.0;\n");
g_string_append (shader_source, "gl_FragColor.g = 1.0;\n");
if (i == 0)
g_string_append (shader_source, "gl_FragColor = frag_color;\n");
if (settings->fog_enabled)
g_string_append (shader_source, cogl_fixed_fragment_shader_fog);
@ -294,10 +389,10 @@ cogl_gles2_get_fragment_shader (const CoglGles2WrapperSettings *settings)
shader_source->str);
g_string_free (shader_source, TRUE);
if (shader_obj == 0)
return NULL;
shader = g_slice_new (CoglGles2WrapperShader);
shader->shader = shader_obj;
shader->settings = *settings;
@ -308,6 +403,69 @@ cogl_gles2_get_fragment_shader (const CoglGles2WrapperSettings *settings)
return shader;
}
static void
cogl_gles2_wrapper_get_locations (GLuint program,
CoglGles2WrapperSettings *settings,
CoglGles2WrapperUniforms *uniforms,
CoglGles2WrapperAttributes *attribs)
{
int i;
uniforms->mvp_matrix_uniform
= glGetUniformLocation (program, "mvp_matrix");
uniforms->modelview_matrix_uniform
= glGetUniformLocation (program, "modelview_matrix");
uniforms->texture_matrix_uniforms =
g_array_new (FALSE, FALSE, sizeof (GLint));
uniforms->texture_sampler_uniforms =
g_array_new (FALSE, FALSE, sizeof (GLint));
attribs->multi_texture_coords =
g_array_new (FALSE, FALSE, sizeof (GLint));
for (i = 0; i < settings->n_texture_units; i++)
{
char *matrix_var_name = g_strdup_printf ("texture_matrix[%d]", i);
char *sampler_var_name = g_strdup_printf ("texture_unit[%d]", i);
char *tex_coord_var_name =
g_strdup_printf ("multi_tex_coord_attrib%d", i);
GLint location;
location = glGetUniformLocation (program, matrix_var_name);
g_array_append_val (uniforms->texture_matrix_uniforms, location);
location = glGetUniformLocation (program, sampler_var_name);
g_array_append_val (uniforms->texture_sampler_uniforms, location);
location = glGetAttribLocation (program, tex_coord_var_name);
g_array_append_val (attribs->multi_texture_coords, location);
g_free (tex_coord_var_name);
g_free (sampler_var_name);
g_free (matrix_var_name);
}
uniforms->fog_density_uniform
= glGetUniformLocation (program, "fog_density");
uniforms->fog_start_uniform
= glGetUniformLocation (program, "fog_start");
uniforms->fog_end_uniform
= glGetUniformLocation (program, "fog_end");
uniforms->fog_color_uniform
= glGetUniformLocation (program, "fog_color");
uniforms->alpha_test_ref_uniform
= glGetUniformLocation (program, "alpha_test_ref");
}
static void
cogl_gles2_wrapper_bind_attributes (GLuint program)
{
glBindAttribLocation (program, COGL_GLES2_WRAPPER_VERTEX_ATTRIB,
"vertex_attrib");
glBindAttribLocation (program, COGL_GLES2_WRAPPER_COLOR_ATTRIB,
"color_attrib");
glBindAttribLocation (program, COGL_GLES2_WRAPPER_NORMAL_ATTRIB,
"normal_attrib");
}
static CoglGles2WrapperProgram *
cogl_gles2_wrapper_get_program (const CoglGles2WrapperSettings *settings)
{
@ -403,61 +561,22 @@ cogl_gles2_wrapper_get_program (const CoglGles2WrapperSettings *settings)
}
program->settings = *settings;
cogl_gles2_wrapper_get_uniforms (program->program, &program->uniforms);
cogl_gles2_wrapper_get_locations (program->program,
&program->settings,
&program->uniforms,
&program->attributes);
/* We haven't tried to get a location for any of the custom uniforms
yet */
for (i = 0; i < COGL_GLES2_NUM_CUSTOM_UNIFORMS; i++)
program->custom_uniforms[i] = COGL_GLES2_UNBOUND_CUSTOM_UNIFORM;
w->compiled_programs = g_slist_append (w->compiled_programs, program);
return program;
}
void
cogl_gles2_wrapper_bind_attributes (GLuint program)
{
glBindAttribLocation (program, COGL_GLES2_WRAPPER_VERTEX_ATTRIB,
"vertex_attrib");
glBindAttribLocation (program, COGL_GLES2_WRAPPER_TEX_COORD_ATTRIB,
"tex_coord_attrib");
glBindAttribLocation (program, COGL_GLES2_WRAPPER_COLOR_ATTRIB,
"color_attrib");
glBindAttribLocation (program, COGL_GLES2_WRAPPER_NORMAL_ATTRIB,
"normal_attrib");
}
void
cogl_gles2_wrapper_get_uniforms (GLuint program,
CoglGles2WrapperUniforms *uniforms)
{
uniforms->mvp_matrix_uniform
= glGetUniformLocation (program, "mvp_matrix");
uniforms->modelview_matrix_uniform
= glGetUniformLocation (program, "modelview_matrix");
uniforms->texture_matrix_uniform
= glGetUniformLocation (program, "texture_matrix");
uniforms->bound_texture_uniform
= glGetUniformLocation (program, "texture_unit");
uniforms->fog_density_uniform
= glGetUniformLocation (program, "fog_density");
uniforms->fog_start_uniform
= glGetUniformLocation (program, "fog_start");
uniforms->fog_end_uniform
= glGetUniformLocation (program, "fog_end");
uniforms->fog_color_uniform
= glGetUniformLocation (program, "fog_color");
uniforms->alpha_test_ref_uniform
= glGetUniformLocation (program, "alpha_test_ref");
uniforms->texture_unit_uniform
= glGetUniformLocation (program, "texture_unit");
}
void
cogl_gles2_wrapper_deinit (CoglGles2Wrapper *wrapper)
{
@ -493,9 +612,11 @@ cogl_gles2_wrapper_deinit (CoglGles2Wrapper *wrapper)
g_free (wrapper->custom_uniforms[i].v.array);
}
void
static void
cogl_gles2_wrapper_update_matrix (CoglGles2Wrapper *wrapper, GLenum matrix_num)
{
CoglGles2WrapperTextureUnit *texture_unit;
switch (matrix_num)
{
default:
@ -509,7 +630,11 @@ cogl_gles2_wrapper_update_matrix (CoglGles2Wrapper *wrapper, GLenum matrix_num)
break;
case GL_TEXTURE:
wrapper->dirty_uniforms |= COGL_GLES2_DIRTY_TEXTURE_MATRIX;
wrapper->dirty_uniforms |= COGL_GLES2_DIRTY_TEXTURE_MATRICES;
texture_unit = g_array_index (wrapper->texture_units,
CoglGles2WrapperTextureUnit *,
wrapper->active_texture_unit);
texture_unit->dirty_matrix = 1;
break;
}
}
@ -551,11 +676,19 @@ cogl_wrap_glPushMatrix ()
break;
case GL_TEXTURE:
src = w->texture_stack + w->texture_stack_pos * 16;
w->texture_stack_pos = (w->texture_stack_pos + 1)
& (COGL_GLES2_TEXTURE_STACK_SIZE - 1);
dst = w->texture_stack + w->texture_stack_pos * 16;
break;
{
CoglGles2WrapperTextureUnit *texture_unit;
texture_unit = g_array_index (w->texture_units,
CoglGles2WrapperTextureUnit *,
w->active_texture_unit);
src = texture_unit->texture_stack
+ texture_unit->texture_stack_pos * 16;
texture_unit->texture_stack_pos = (texture_unit->texture_stack_pos + 1)
& (COGL_GLES2_TEXTURE_STACK_SIZE - 1);
dst = texture_unit->texture_stack
+ texture_unit->texture_stack_pos * 16;
break;
}
}
/* Copy the old matrix to the new position */
@ -565,6 +698,7 @@ cogl_wrap_glPushMatrix ()
void
cogl_wrap_glPopMatrix ()
{
CoglGles2WrapperTextureUnit *texture_unit;
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
/* Decrement the stack pointer */
@ -582,7 +716,10 @@ cogl_wrap_glPopMatrix ()
break;
case GL_TEXTURE:
w->texture_stack_pos = (w->texture_stack_pos - 1)
texture_unit = g_array_index (w->texture_units,
CoglGles2WrapperTextureUnit *,
w->active_texture_unit);
texture_unit->texture_stack_pos = (texture_unit->texture_stack_pos - 1)
& (COGL_GLES2_TEXTURE_STACK_SIZE - 1);
break;
}
@ -602,6 +739,8 @@ cogl_wrap_glMatrixMode (GLenum mode)
static float *
cogl_gles2_get_matrix_stack_top (CoglGles2Wrapper *wrapper)
{
CoglGles2WrapperTextureUnit *texture_unit;
switch (wrapper->matrix_mode)
{
default:
@ -612,7 +751,12 @@ cogl_gles2_get_matrix_stack_top (CoglGles2Wrapper *wrapper)
return wrapper->projection_stack + wrapper->projection_stack_pos * 16;
case GL_TEXTURE:
return wrapper->texture_stack + wrapper->texture_stack_pos * 16;
texture_unit = g_array_index (wrapper->texture_units,
CoglGles2WrapperTextureUnit *,
wrapper->active_texture_unit);
return texture_unit->texture_stack
+ texture_unit->texture_stack_pos * 16;
}
}
@ -743,7 +887,7 @@ cogl_wrap_glRotatex (GLfixed angle, GLfixed x, GLfixed y, GLfixed z)
float anglef = COGL_FIXED_TO_FLOAT (angle) * G_PI / 180.0f;
float c = cosf (anglef);
float s = sinf (anglef);
matrix[0] = xf * xf * (1.0f - c) + c;
matrix[1] = yf * xf * (1.0f - c) + zf * s;
matrix[2] = xf * zf * (1.0f - c) - yf * s;
@ -800,8 +944,22 @@ void
cogl_wrap_glTexCoordPointer (GLint size, GLenum type, GLsizei stride,
const GLvoid *pointer)
{
glVertexAttribPointer (COGL_GLES2_WRAPPER_TEX_COORD_ATTRIB, size, type,
GL_FALSE, stride, pointer);
int active_unit;
CoglGles2WrapperTextureUnit *texture_unit;
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
active_unit = w->active_client_texture_unit;
texture_unit = g_array_index (w->texture_units,
CoglGles2WrapperTextureUnit *,
active_unit);
texture_unit->texture_coords_size = size;
texture_unit->texture_coords_type = type;
texture_unit->texture_coords_stride = stride;
texture_unit->texture_coords_pointer = pointer;
w->dirty_attribute_pointers
|= COGL_GLES2_DIRTY_TEX_COORD_VERTEX_ATTRIB;
}
void
@ -935,7 +1093,7 @@ cogl_wrap_glDrawArrays (GLenum mode, GLint first, GLsizei count)
cogl_gles2_wrapper_mult_matrix (mvp_matrix,
w->projection_stack
+ w->projection_stack_pos * 16,
modelview_matrix);
modelview_matrix);
if (program->uniforms.mvp_matrix_uniform != -1)
glUniformMatrix4fv (program->uniforms.mvp_matrix_uniform, 1,
@ -944,11 +1102,28 @@ cogl_wrap_glDrawArrays (GLenum mode, GLint first, GLsizei count)
glUniformMatrix4fv (program->uniforms.modelview_matrix_uniform, 1,
GL_FALSE, modelview_matrix);
}
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_TEXTURE_MATRIX)
&& program->uniforms.texture_matrix_uniform != -1)
glUniformMatrix4fv (program->uniforms.texture_matrix_uniform, 1,
GL_FALSE,
w->texture_stack + w->texture_stack_pos * 16);
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_TEXTURE_MATRICES))
{
int i;
/* TODO - we should probably have a per unit dirty flag too */
for (i = 0; i < program->uniforms.texture_matrix_uniforms->len; i++)
{
CoglGles2WrapperTextureUnit *texture_unit;
GLint uniform =
g_array_index (program->uniforms.texture_matrix_uniforms,
GLint, i);
texture_unit = g_array_index (w->texture_units,
CoglGles2WrapperTextureUnit *,
i);
if (uniform != -1)
glUniformMatrix4fv (uniform, 1, GL_FALSE,
texture_unit->texture_stack
+ texture_unit->texture_stack_pos * 16);
}
}
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_FOG_DENSITY)
&& program->uniforms.fog_density_uniform != -1)
@ -965,9 +1140,22 @@ cogl_wrap_glDrawArrays (GLenum mode, GLint first, GLsizei count)
glUniform1f (program->uniforms.alpha_test_ref_uniform,
w->alpha_test_ref);
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_TEXTURE_UNIT)
&& program->uniforms.texture_unit_uniform != -1)
glUniform1i (program->uniforms.texture_unit_uniform, 0);
if ((w->dirty_uniforms & COGL_GLES2_DIRTY_TEXTURE_UNITS))
{
int i;
/* TODO - we should probably have a per unit dirty flag too */
for (i = 0; i < program->uniforms.texture_sampler_uniforms->len; i++)
{
GLint uniform =
g_array_index (program->uniforms.texture_sampler_uniforms,
GLint, i);
if (uniform != -1)
glUniform1i (uniform, i);
}
}
w->dirty_uniforms = 0;
}
@ -995,10 +1183,70 @@ cogl_wrap_glDrawArrays (GLenum mode, GLint first, GLsizei count)
&w->custom_uniforms[i]);
}
}
w->dirty_custom_uniforms = 0;
}
if (w->dirty_attribute_pointers
& COGL_GLES2_DIRTY_TEX_COORD_VERTEX_ATTRIB)
{
int i;
/* TODO - coverage test */
for (i = 0; i < w->settings.n_texture_units; i++)
{
GLint tex_coord_var_index;
CoglGles2WrapperTextureUnit *texture_unit;
if (!w->settings.texture_units[i].enabled)
continue;
texture_unit = g_array_index (w->texture_units,
CoglGles2WrapperTextureUnit *,
w->active_texture_unit);
if (!texture_unit->texture_coords_enabled)
continue;
/* TODO - we should probably have a per unit dirty flag too */
/* TODO - coverage test */
tex_coord_var_index =
g_array_index (program->attributes.multi_texture_coords,
GLint, i);
glVertexAttribPointer (tex_coord_var_index,
texture_unit->texture_coords_size,
texture_unit->texture_coords_type,
GL_FALSE,
texture_unit->texture_coords_stride,
texture_unit->texture_coords_pointer);
}
}
if (w->dirty_vertex_attrib_enables)
{
int i;
/* TODO - coverage test */
/* TODO - we should probably have a per unit dirty flag too */
for (i = 0; i < w->texture_units->len; i++)
{
CoglGles2WrapperTextureUnit *texture_unit =
g_array_index (w->texture_units,
CoglGles2WrapperTextureUnit *,
w->active_texture_unit);
if (texture_unit->texture_coords_enabled)
glEnableVertexAttribArray (
g_array_index (program->attributes.multi_texture_coords,
GLint, i));
else
glDisableVertexAttribArray (
g_array_index (program->attributes.multi_texture_coords,
GLint, i));
w->dirty_vertex_attrib_enables = 0;
}
}
glDrawArrays (mode, first, count);
}
@ -1013,7 +1261,10 @@ cogl_gles2_wrapper_bind_texture (GLenum target, GLuint texture,
/* We need to keep track of whether the texture is alpha-only
because the emulation of GL_MODULATE needs to work differently in
that case */
_COGL_GLES2_CHANGE_SETTING (w, alpha_only, internal_format == GL_ALPHA);
_COGL_GLES2_CHANGE_SETTING (
w, texture_units[w->active_texture_unit].alpha_only,
internal_format == GL_ALPHA);
}
void
@ -1024,6 +1275,79 @@ cogl_wrap_glTexEnvx (GLenum target, GLenum pname, GLfixed param)
nothing needs to be done here. */
}
static void
realize_texture_units (CoglGles2Wrapper *w, int texture_unit_index)
{
/* We save the active texture unit since we may need to temporarily
* change this to initialise each new texture unit and we want to
* restore the active unit afterwards */
int initial_active_unit = w->active_texture_unit;
if (texture_unit_index >= w->settings.n_texture_units)
{
int n_new_texture_units =
texture_unit_index + 1 - w->settings.n_texture_units;
GLint prev_mode;
int i;
w->settings.texture_units =
g_realloc (w->settings.texture_units,
texture_unit_index + 1
* sizeof (CoglGles2WrapperTextureUnitSettings));
/* We will need to set the matrix mode to GL_TEXTURE to
* initialise any new texture units, so we save the current
* mode for restoring afterwards */
GE( cogl_wrap_glGetIntegerv (CGL_MATRIX_MODE, &prev_mode));
for (i = 0; i < n_new_texture_units; i++)
{
CoglGles2WrapperTextureUnit *new_unit;
CoglGles2WrapperTextureUnitSettings *new_unit_settings;
new_unit = g_new0 (CoglGles2WrapperTextureUnit, 1);
g_array_append_val (w->texture_units, new_unit);
w->active_texture_unit = i;
GE( cogl_wrap_glMatrixMode (GL_TEXTURE));
GE( cogl_wrap_glLoadIdentity ());
new_unit_settings =
&w->settings.texture_units[w->settings.n_texture_units + i];
new_unit_settings->enabled = FALSE;
new_unit_settings->alpha_only = FALSE;
}
GE( cogl_wrap_glMatrixMode ((GLenum)prev_mode));
w->settings.n_texture_units = w->texture_units->len;
}
w->active_texture_unit = initial_active_unit;
}
void
cogl_wrap_glClientActiveTexture (GLenum texture)
{
int texture_unit_index = texture - GL_TEXTURE0;
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
w->active_client_texture_unit = texture_unit_index;
realize_texture_units (w, texture_unit_index);
}
void
cogl_wrap_glActiveTexture (GLenum texture)
{
int texture_unit_index = texture - GL_TEXTURE0;
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
w->active_texture_unit = texture_unit_index;
realize_texture_units (w, texture_unit_index);
}
void
cogl_wrap_glEnable (GLenum cap)
{
@ -1032,7 +1356,8 @@ cogl_wrap_glEnable (GLenum cap)
switch (cap)
{
case GL_TEXTURE_2D:
_COGL_GLES2_CHANGE_SETTING (w, texture_2d_enabled, TRUE);
_COGL_GLES2_CHANGE_SETTING (
w, texture_units[w->active_texture_unit].enabled, TRUE);
break;
case GL_FOG:
@ -1056,7 +1381,8 @@ cogl_wrap_glDisable (GLenum cap)
switch (cap)
{
case GL_TEXTURE_2D:
_COGL_GLES2_CHANGE_SETTING (w, texture_2d_enabled, FALSE);
_COGL_GLES2_CHANGE_SETTING (
w, texture_units[w->active_texture_unit].enabled, FALSE);
break;
case GL_FOG:
@ -1075,13 +1401,26 @@ cogl_wrap_glDisable (GLenum cap)
void
cogl_wrap_glEnableClientState (GLenum array)
{
CoglGles2WrapperTextureUnit *texture_unit;
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
switch (array)
{
case GL_VERTEX_ARRAY:
glEnableVertexAttribArray (COGL_GLES2_WRAPPER_VERTEX_ATTRIB);
break;
case GL_TEXTURE_COORD_ARRAY:
glEnableVertexAttribArray (COGL_GLES2_WRAPPER_TEX_COORD_ATTRIB);
/* TODO - review if this should be in w->settings? */
texture_unit = g_array_index (w->texture_units,
CoglGles2WrapperTextureUnit *,
w->active_texture_unit);
if (texture_unit->texture_coords_enabled != 1)
{
texture_unit->texture_coords_enabled = 1;
w->dirty_vertex_attrib_enables
|= COGL_GLES2_DIRTY_TEX_COORD_ATTRIB_ENABLES;
}
break;
case GL_COLOR_ARRAY:
glEnableVertexAttribArray (COGL_GLES2_WRAPPER_COLOR_ATTRIB);
@ -1095,13 +1434,26 @@ cogl_wrap_glEnableClientState (GLenum array)
void
cogl_wrap_glDisableClientState (GLenum array)
{
CoglGles2WrapperTextureUnit *texture_unit;
_COGL_GET_GLES2_WRAPPER (w, NO_RETVAL);
switch (array)
{
case GL_VERTEX_ARRAY:
glDisableVertexAttribArray (COGL_GLES2_WRAPPER_VERTEX_ATTRIB);
break;
case GL_TEXTURE_COORD_ARRAY:
glDisableVertexAttribArray (COGL_GLES2_WRAPPER_TEX_COORD_ATTRIB);
texture_unit = g_array_index (w->texture_units,
CoglGles2WrapperTextureUnit *,
w->active_texture_unit);
/* TODO - review if this should be in w->settings? */
if (texture_unit->texture_coords_enabled != 0)
{
texture_unit->texture_coords_enabled = 0;
w->dirty_vertex_attrib_enables
|= COGL_GLES2_DIRTY_TEX_COORD_ATTRIB_ENABLES;
}
break;
case GL_COLOR_ARRAY:
glDisableVertexAttribArray (COGL_GLES2_WRAPPER_COLOR_ATTRIB);
@ -1162,6 +1514,10 @@ cogl_wrap_glGetIntegerv (GLenum pname, GLint *params)
*params = 0;
break;
case CGL_MATRIX_MODE:
*params = w->matrix_mode;
break;
default:
glGetIntegerv (pname, params);
break;
@ -1208,7 +1564,7 @@ cogl_wrap_glFogx (GLenum pname, GLfixed param)
case GL_FOG_MODE:
_COGL_GLES2_CHANGE_SETTING (w, fog_mode, param);
break;
case GL_FOG_DENSITY:
_COGL_GLES2_CHANGE_UNIFORM (w, FOG_DENSITY, fog_density,
COGL_FIXED_TO_FLOAT (param));

View File

@ -32,11 +32,17 @@ G_BEGIN_DECLS
#ifdef HAVE_COGL_GLES2
typedef struct _CoglGles2Wrapper CoglGles2Wrapper;
typedef struct _CoglGles2WrapperUniforms CoglGles2WrapperUniforms;
typedef struct _CoglGles2WrapperSettings CoglGles2WrapperSettings;
typedef struct _CoglGles2WrapperProgram CoglGles2WrapperProgram;
typedef struct _CoglGles2WrapperShader CoglGles2WrapperShader;
typedef struct _CoglGles2Wrapper CoglGles2Wrapper;
typedef struct _CoglGles2WrapperTextureUnit
CoglGles2WrapperTextureUnit;
typedef struct _CoglGles2WrapperAttributes CoglGles2WrapperAttributes;
typedef struct _CoglGles2WrapperUniforms CoglGles2WrapperUniforms;
typedef struct _CoglGles2WrapperTextureUnitSettings
CoglGles2WrapperTextureUnitSettings;
typedef struct _CoglGles2WrapperSettings CoglGles2WrapperSettings;
typedef struct _CoglGles2WrapperProgram CoglGles2WrapperProgram;
typedef struct _CoglGles2WrapperShader CoglGles2WrapperShader;
#define COGL_GLES2_NUM_CUSTOM_UNIFORMS 16
#define COGL_GLES2_UNBOUND_CUSTOM_UNIFORM -2
@ -46,78 +52,129 @@ typedef struct _CoglGles2WrapperShader CoglGles2WrapperShader;
#define COGL_GLES2_PROJECTION_STACK_SIZE 2
#define COGL_GLES2_TEXTURE_STACK_SIZE 2
/* Dirty flags for shader uniforms */
enum
{
COGL_GLES2_DIRTY_MVP_MATRIX = 1 << 0,
COGL_GLES2_DIRTY_MODELVIEW_MATRIX = 1 << 1,
COGL_GLES2_DIRTY_TEXTURE_MATRIX = 1 << 2,
COGL_GLES2_DIRTY_TEXTURE_MATRICES = 1 << 2,
COGL_GLES2_DIRTY_FOG_DENSITY = 1 << 3,
COGL_GLES2_DIRTY_FOG_START = 1 << 4,
COGL_GLES2_DIRTY_FOG_END = 1 << 5,
COGL_GLES2_DIRTY_FOG_COLOR = 1 << 6,
COGL_GLES2_DIRTY_ALPHA_TEST_REF = 1 << 7,
COGL_GLES2_DIRTY_TEXTURE_UNIT = 1 << 8,
COGL_GLES2_DIRTY_TEXTURE_UNITS = 1 << 8,
COGL_GLES2_DIRTY_ALL = (1 << 9) - 1
};
/* Dirty flags for shader vertex attribute pointers */
enum
{
COGL_GLES2_DIRTY_TEX_COORD_VERTEX_ATTRIB = 1 << 0
};
/* Dirty flags for shader vertex attributes enabled status */
enum
{
COGL_GLES2_DIRTY_TEX_COORD_ATTRIB_ENABLES = 1 << 0
};
struct _CoglGles2WrapperAttributes
{
GArray *multi_texture_coords;
};
struct _CoglGles2WrapperUniforms
{
GLint mvp_matrix_uniform;
GLint modelview_matrix_uniform;
GLint texture_matrix_uniform;
GLint bound_texture_uniform;
GLint mvp_matrix_uniform;
GLint modelview_matrix_uniform;
GArray *texture_matrix_uniforms;
GLint fog_density_uniform;
GLint fog_start_uniform;
GLint fog_end_uniform;
GLint fog_color_uniform;
GArray *texture_sampler_uniforms;
GLint alpha_test_ref_uniform;
GLint fog_density_uniform;
GLint fog_start_uniform;
GLint fog_end_uniform;
GLint fog_color_uniform;
GLint alpha_test_ref_uniform;
GLint texture_unit_uniform;
};
struct _CoglGles2WrapperTextureUnitSettings
{
guint enabled:1;
guint alpha_only:1;
/* TODO: blending state */
};
/* NB: We get a copy of this for each fragment/vertex
* program varient we generate so we try to keep it
* fairly lean */
struct _CoglGles2WrapperSettings
{
gboolean texture_2d_enabled;
gboolean alpha_only;
CoglGles2WrapperTextureUnitSettings *texture_units;
guint n_texture_units;
gboolean alpha_test_enabled;
GLint alpha_test_func;
gboolean fog_enabled;
GLint fog_mode;
/* The current in-use user program */
CoglHandle user_program;
guint alpha_test_enabled:1;
guint fog_enabled:1;
};
struct _CoglGles2WrapperTextureUnit
{
GLfloat texture_stack[COGL_GLES2_TEXTURE_STACK_SIZE * 16];
GLuint texture_stack_pos;
GLenum texture_coords_type;
GLint texture_coords_size;
GLsizei texture_coords_stride;
const void *texture_coords_pointer;
guint texture_coords_enabled:1;
guint dirty_matrix:1; /*!< shader uniform needs updating */
};
struct _CoglGles2Wrapper
{
GLuint matrix_mode;
GLfloat modelview_stack[COGL_GLES2_MODELVIEW_STACK_SIZE * 16];
GLuint modelview_stack_pos;
GLfloat projection_stack[COGL_GLES2_PROJECTION_STACK_SIZE * 16];
GLuint projection_stack_pos;
GLfloat texture_stack[COGL_GLES2_TEXTURE_STACK_SIZE * 16];
GLuint texture_stack_pos;
GLuint matrix_mode;
GLfloat modelview_stack[COGL_GLES2_MODELVIEW_STACK_SIZE * 16];
GLuint modelview_stack_pos;
GLfloat projection_stack[COGL_GLES2_PROJECTION_STACK_SIZE * 16];
GLuint projection_stack_pos;
GArray *texture_units;
guint active_texture_unit;
guint active_client_texture_unit;
/* The combined modelview and projection matrix is only updated at
the last minute in glDrawArrays to avoid recalculating it for
every change to the modelview matrix */
GLboolean mvp_uptodate;
GLboolean mvp_uptodate;
/* The currently bound program */
CoglGles2WrapperProgram *current_program;
/* The current settings */
/* The current settings. Effectively these represent anything that
* will require a modified fixed function shader */
CoglGles2WrapperSettings settings;
/* Whether the settings have changed since the last draw */
gboolean settings_dirty;
/* Uniforms that have changed since the last draw */
int dirty_uniforms, dirty_custom_uniforms;
/* Attribute pointers that have changed since the last draw */
int dirty_attribute_pointers;
/* Vertex attribute pointer enables that have changed since the last draw */
int dirty_vertex_attrib_enables;
/* List of all compiled program combinations */
GSList *compiled_programs;
@ -143,6 +200,10 @@ struct _CoglGles2WrapperProgram
/* The settings that were used to generate this combination */
CoglGles2WrapperSettings settings;
/* The attributes for this program that are not bound up-front
* with constant indices */
CoglGles2WrapperAttributes attributes;
/* The uniforms for this program */
CoglGles2WrapperUniforms uniforms;
GLint custom_uniforms[COGL_GLES2_NUM_CUSTOM_UNIFORMS];
@ -234,6 +295,9 @@ void cogl_wrap_glNormalPointer (GLenum type, GLsizei stride,
void cogl_wrap_glTexEnvx (GLenum target, GLenum pname, GLfixed param);
void cogl_wrap_glClientActiveTexture (GLenum texture);
void cogl_wrap_glActiveTexture (GLenum texture);
void cogl_wrap_glEnableClientState (GLenum array);
void cogl_wrap_glDisableClientState (GLenum array);
@ -259,12 +323,6 @@ void cogl_gles2_wrapper_bind_texture (GLenum target, GLuint texture,
/* This function is only available on GLES 2 */
#define cogl_wrap_glGenerateMipmap glGenerateMipmap
void cogl_gles2_wrapper_bind_attributes (GLuint program);
void cogl_gles2_wrapper_get_uniforms (GLuint program,
CoglGles2WrapperUniforms *uniforms);
void cogl_gles2_wrapper_update_matrix (CoglGles2Wrapper *wrapper,
GLenum matrix_num);
void _cogl_gles2_clear_cache_for_program (CoglHandle program);
#else /* HAVE_COGL_GLES2 */
@ -291,6 +349,7 @@ void _cogl_gles2_clear_cache_for_program (CoglHandle program);
#define cogl_wrap_glColorPointer glColorPointer
#define cogl_wrap_glNormalPointer glNormalPointer
#define cogl_wrap_glTexEnvx glTexEnvx
#define cogl_wrap_glActiveTexture glActiveTexture
#define cogl_wrap_glEnableClientState glEnableClientState
#define cogl_wrap_glDisableClientState glDisableClientState
#define cogl_wrap_glAlphaFunc glAlphaFunc

View File

@ -31,6 +31,8 @@
typedef struct _CoglTexture CoglTexture;
typedef struct _CoglTexSliceSpan CoglTexSliceSpan;
typedef struct _CoglSpanIter CoglSpanIter;
typedef struct _CoglMultiTexture CoglMultiTexture;
typedef struct _CoglMultiTextureLayer CoglMultiTextureLayer;
struct _CoglTexSliceSpan
{
@ -59,7 +61,26 @@ struct _CoglTexture
gboolean auto_mipmap;
};
struct _CoglMultiTextureLayer
{
guint ref_count;
guint index; /*!< lowest index is blended first then others
on top */
CoglTexture *tex; /*!< The texture for this layer, or NULL
for an empty layer */
/* TODO: Add more control over the texture environment for each texture
* unit. For example we should support dot3 normal mapping. */
};
struct _CoglMultiTexture
{
guint ref_count;
GList *layers;
};
CoglTexture*
_cogl_texture_pointer_from_handle (CoglHandle handle);
#endif /* __COGL_TEXTURE_H */

View File

@ -40,11 +40,20 @@
#include <string.h>
#include <stdlib.h>
#if HAVE_COGL_GLES2
#define glVertexPointer cogl_wrap_glVertexPointer
#define glTexCoordPointer cogl_wrap_glTexCoordPointer
#define glColorPointer cogl_wrap_glColorPointer
#define glDrawArrays cogl_wrap_glDrawArrays
#define glTexParameteri cogl_wrap_glTexParameteri
#define glClientActiveTexture cogl_wrap_glClientActiveTexture
#define glActiveTexture cogl_wrap_glActiveTexture
#define glEnable cogl_wrap_glEnable
#define glEnableClientState cogl_wrap_glEnableClientState
#define glDisable cogl_wrap_glDisable
#define glDisableClientState cogl_wrap_glDisableClientState
#define glTexEnvx cogl_wrap_glTexEnvx
#endif
/*
#define COGL_DEBUG 1
@ -75,14 +84,14 @@ struct _CoglSpanIter
};
static void _cogl_texture_free (CoglTexture *tex);
static void _cogl_multi_texture_free (CoglMultiTexture *multi_texture);
static void _cogl_multi_texture_layer_free (CoglMultiTextureLayer *layer);
COGL_HANDLE_DEFINE (Texture, texture, texture_handles);
CoglHandle
_cogl_texture_handle_from_pointer (CoglTexture *tex)
{
return (CoglHandle) tex;
}
COGL_HANDLE_DEFINE (MultiTexture, multi_texture, multi_texture_handles);
COGL_HANDLE_DEFINE (MultiTextureLayer,
multi_texture_layer,
multi_texture_layer_handles);
static void
_cogl_texture_bitmap_free (CoglTexture *tex)
@ -2493,3 +2502,263 @@ cogl_texture_polygon (CoglHandle handle,
if (use_color && n_vertices > 0)
cogl_set_source_color (&vertices[n_vertices - 1].color);
}
CoglHandle
cogl_multi_texture_new (void)
{
CoglMultiTexture *multi_tex = g_new0 (CoglMultiTexture, 1);
return _cogl_multi_texture_handle_new (multi_tex);
}
static void
_cogl_multi_texture_free (CoglMultiTexture *multi_tex)
{
g_list_foreach (multi_tex->layers,
(GFunc)cogl_multi_texture_layer_unref, NULL);
g_free (multi_tex);
}
static CoglMultiTextureLayer *
_cogl_multi_texture_get_layer (CoglMultiTexture *multi_tex, guint index)
{
CoglMultiTextureLayer *layer;
GList *tmp;
for (tmp = multi_tex->layers; tmp != NULL; tmp = tmp->next)
{
layer = tmp->data;
if (layer->index == index)
return layer;
/* The layers are always sorted, so we know this layer doesn't exists */
if (layer->index > index)
break;
}
/* NB: if we now insert a new layer before tmp, that will maintain order.
*/
layer = g_new (CoglMultiTextureLayer, 1);
layer->ref_count = 1;
layer->index = index;
/* Note: comment after for() loop above */
multi_tex->layers = g_list_insert_before (multi_tex->layers, tmp, layer);
return layer;
}
void
cogl_multi_texture_layer_set_texture (CoglHandle multi_texture_handle,
guint layer_index,
CoglHandle tex_handle)
{
CoglMultiTexture *multi_tex;
CoglMultiTextureLayer *layer;
CoglTexture *tex;
if (!cogl_is_multi_texture (multi_texture_handle)
|| !cogl_is_texture (tex_handle))
return;
multi_tex = _cogl_multi_texture_pointer_from_handle (multi_texture_handle);
layer = _cogl_multi_texture_get_layer (multi_tex, layer_index);
tex = _cogl_texture_pointer_from_handle (tex_handle);
cogl_texture_ref (tex_handle);
layer->tex = tex;
}
static void
_cogl_multi_texture_layer_free (CoglMultiTextureLayer *layer)
{
cogl_texture_unref (layer->tex);
g_free (layer);
}
void
cogl_multi_texture_layer_remove (CoglHandle multi_texture_handle,
guint layer_index)
{
CoglMultiTexture *multi_tex;
CoglMultiTextureLayer *layer;
GList *tmp;
/* Check if valid multi texture */
if (!cogl_is_multi_texture (multi_texture_handle))
return;
multi_tex = _cogl_multi_texture_pointer_from_handle (multi_texture_handle);
for (tmp = multi_tex->layers; tmp != NULL; tmp = tmp->next)
{
layer = tmp->data;
if (layer->index == layer_index)
{
CoglHandle handle =
_cogl_multi_texture_layer_handle_from_pointer (layer);
cogl_multi_texture_layer_unref (handle);
multi_tex->layers = g_list_remove (multi_tex->layers, layer);
return;
}
}
}
void
cogl_multi_texture_rectangle (CoglHandle multi_texture_handle,
CoglFixed x1,
CoglFixed y1,
CoglFixed x2,
CoglFixed y2,
CoglFixed *user_tex_coords)
{
CoglMultiTexture *multi_tex;
GLfixed quad_coords[8];
GList *tmp;
GList *valid_layers = NULL;
int count;
GLfloat *tex_coords_buff;
gulong enable_flags = 0;
/* FIXME - currently cogl deals with enabling texturing
* via enable flags, but that can't scale to n texture
* units. Currently we have to be carefull how we leave the
* environment so we don't break things. See the cleanup
* notes at the end of this function */
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
/* Check if valid multi texture */
if (!cogl_is_multi_texture (multi_texture_handle))
return;
multi_tex = _cogl_multi_texture_pointer_from_handle (multi_texture_handle);
#define CFX_F COGL_FIXED_TO_FLOAT
quad_coords[0] = CFX_F (x1);
quad_coords[1] = CFX_F (y1);
quad_coords[2] = CFX_F (x2);
quad_coords[3] = CFX_F (y1);
quad_coords[4] = CFX_F (x1);
quad_coords[5] = CFX_F (y2);
quad_coords[6] = CFX_F (x2);
quad_coords[7] = CFX_F (y2);
#undef CFX_F
enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
GE( glVertexPointer (2, GL_FIXED, 0, quad_coords) );
for (count = 0, tmp = multi_tex->layers;
tmp != NULL;
count++, tmp = tmp->next)
{
CoglMultiTextureLayer *layer = tmp->data;
/* Skip empty layers */
if (!layer->tex)
{
count--;
continue;
}
/* FIXME - currently we don't support sliced textures */
if (layer->tex->slice_gl_handles == NULL
|| layer->tex->slice_gl_handles->len < 1)
continue;
if (count >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
{
static gboolean shown_warning = FALSE;
if (!shown_warning)
{
g_warning ("Your driver does not support enough texture layers"
"to correctly handle this multi texturing");
shown_warning = TRUE;
}
/* NB: We make a best effort attempt to display as many layers as
* possible. */
break;
}
if (layer->tex->bitmap.format & COGL_A_BIT)
enable_flags |= COGL_ENABLE_BLEND;
valid_layers = g_list_prepend (valid_layers, layer);
}
valid_layers = g_list_reverse (valid_layers);
/* Enable blending if the geometry has an associated alpha color,
* or - see above - we also check each layer texture and if any has
* an alpha channel also enable blending. */
if (ctx->color_alpha < 255)
enable_flags |= COGL_ENABLE_BLEND;
cogl_enable (enable_flags);
/* NB: It could be that no valid texture layers were found, but
* we will still submit a non-textured rectangle in that case. */
if (count)
tex_coords_buff = alloca (sizeof(GLfloat) * 8 * count);
/* NB: valid_layers is in order, sorted by index */
for (count = 0, tmp = valid_layers;
tmp != NULL;
count++, tmp = tmp->next)
{
CoglMultiTextureLayer *layer = tmp->data;
CoglFixed *in_tex_coords = &user_tex_coords[count * 4];
GLfloat *out_tex_coords = &tex_coords_buff[count * 8];
GLenum gl_tex_handle;
#define CFX_F COGL_FIXED_TO_FLOAT
/* IN LAYOUT: [ tx1:0, ty1:1, tx2:2, ty2:3 ] */
out_tex_coords[0] = CFX_F (in_tex_coords[0]); /* tx1 */
out_tex_coords[1] = CFX_F (in_tex_coords[1]); /* ty1 */
out_tex_coords[2] = CFX_F (in_tex_coords[2]); /* tx2 */
out_tex_coords[3] = CFX_F (in_tex_coords[1]); /* ty1 */
out_tex_coords[4] = CFX_F (in_tex_coords[0]); /* tx1 */
out_tex_coords[5] = CFX_F (in_tex_coords[3]); /* ty2 */
out_tex_coords[6] = CFX_F (in_tex_coords[2]); /* tx2 */
out_tex_coords[7] = CFX_F (in_tex_coords[3]); /* ty2 */
#undef CFX_F
/* TODO - support sliced textures */
gl_tex_handle = g_array_index (layer->tex->slice_gl_handles, GLuint, 0);
GE (glActiveTexture (GL_TEXTURE0 + count));
GE (glTexEnvx (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE));
GE( cogl_gles2_wrapper_bind_texture (layer->tex->gl_target,
gl_tex_handle,
layer->tex->gl_intformat));
GE (glClientActiveTexture (GL_TEXTURE0 + count));
GE (glTexCoordPointer (2, GL_FLOAT, 0, out_tex_coords));
/* FIXME - cogl only knows about one texture unit a.t.m
* (Also see cleanup note below) */
if (count == 0)
{
enable_flags |= COGL_ENABLE_TEXTURE_2D | COGL_ENABLE_TEXCOORD_ARRAY;
cogl_enable (enable_flags);
}
else
{
GE (glEnable (GL_TEXTURE_2D));
GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY));
}
}
GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) );
/* FIXME - cogl doesn't currently have a way of caching the
* enable states for more than one texture unit so for now,
* we just disable anything relating to additional units once
* we are done with them. */
while (--count > 0)
{
GE (glActiveTexture (GL_TEXTURE0 + count));
GE (glClientActiveTexture (GL_TEXTURE0 + count));
GE (cogl_wrap_glDisable (GL_TEXTURE_2D));
GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY));
}
}