cogl-material: Add support for setting the wrap mode for a layer

Previously, Cogl's texture coordinate system was effectively always
GL_REPEAT so that if an application specifies coordinates outside the
range 0→1 it would get repeated copies of the texture. It would
however change the mode to GL_CLAMP_TO_EDGE if all of the coordinates
are in the range 0→1 so that in the common case that the whole texture
is being drawn with linear filtering it will not blend in edge pixels
from the opposite sides.

This patch adds the option for applications to change the wrap mode
per layer. There are now three wrap modes: 'repeat', 'clamp-to-edge'
and 'automatic'. The automatic map mode is the default and it
implements the previous behaviour. The wrap mode can be changed for
the s and t coordinates independently. I've tried to make the
internals support setting the r coordinate but as we don't support 3D
textures yet I haven't exposed any public API for it.

The texture backends still have a set_wrap_mode virtual but this value
is intended to be transitory and it will be changed whenever the
material is flushed (although the backends are expected to cache it so
that it won't use too many GL calls). In my understanding this value
was always meant to be transitory and all primitives were meant to set
the value before drawing. However there were comments suggesting that
this is not the expected behaviour. In particular the vertex buffer
drawing code never set a wrap mode so it would end up with whatever
the texture was previously used for. These issues are now fixed
because the material will always set the wrap modes.

There is code to manually implement clamp-to-edge for textures that
can't be hardware repeated. However this doesn't fully work because it
relies on being able to draw the stretched parts using quads with the
same values for tx1 and tx2. The texture iteration code doesn't
support this so it breaks. This is a separate bug and it isn't
trivially solved.

When flushing a material there are now extra options to set wrap mode
overrides. The overrides are an array of values for each layer that
specifies an override for the s, t or r coordinates. The primitives
use this to implement the automatic wrap mode. cogl_polygon also uses
it to set GL_CLAMP_TO_BORDER mode for its trick to render sliced
textures. Although this code has been added it looks like the sliced
trick has been broken for a while and I haven't attempted to fix it
here.

I've added a constant to represent the maximum number of layers that a
material supports so that I can size the overrides array. I've set it
to 32 because as far as I can tell we have that limit imposed anyway
because the other flush options use a guint32 to store a flag about
each layer. The overrides array ends up adding 32 bytes to each flush
options struct which may be a concern.

http://bugzilla.openedhand.com/show_bug.cgi?id=2063
This commit is contained in:
Neil Roberts 2010-04-01 11:31:33 +01:00
parent fb7f1a7fd6
commit e007bc5358
9 changed files with 523 additions and 52 deletions

View File

@ -46,6 +46,8 @@ _cogl_journal_log_quad (const float *position,
int n_layers, int n_layers,
guint32 fallback_layers, guint32 fallback_layers,
GLuint layer0_override_texture, GLuint layer0_override_texture,
const CoglMaterialWrapModeOverrides *
wrap_mode_overrides,
const float *tex_coords, const float *tex_coords,
unsigned int tex_coords_len); unsigned int tex_coords_len);

View File

@ -663,6 +663,8 @@ _cogl_journal_log_quad (const float *position,
int n_layers, int n_layers,
guint32 fallback_layers, guint32 fallback_layers,
GLuint layer0_override_texture, GLuint layer0_override_texture,
const CoglMaterialWrapModeOverrides *
wrap_mode_overrides,
const float *tex_coords, const float *tex_coords,
unsigned int tex_coords_len) unsigned int tex_coords_len)
{ {
@ -803,6 +805,11 @@ _cogl_journal_log_quad (const float *position,
COGL_MATERIAL_FLUSH_SKIP_GL_COLOR; COGL_MATERIAL_FLUSH_SKIP_GL_COLOR;
entry->flush_options.fallback_layers = fallback_layers; entry->flush_options.fallback_layers = fallback_layers;
entry->flush_options.disable_layers = disable_layers; entry->flush_options.disable_layers = disable_layers;
if (wrap_mode_overrides)
{
entry->flush_options.flags |= COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES;
entry->flush_options.wrap_mode_overrides = *wrap_mode_overrides;
}
if (layer0_override_texture) if (layer0_override_texture)
{ {
entry->flush_options.flags |= COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE; entry->flush_options.flags |= COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE;

View File

@ -81,6 +81,10 @@ struct _CoglMaterialLayer
CoglMaterialFilter mag_filter; CoglMaterialFilter mag_filter;
CoglMaterialFilter min_filter; CoglMaterialFilter min_filter;
CoglMaterialWrapMode wrap_mode_s;
CoglMaterialWrapMode wrap_mode_t;
CoglMaterialWrapMode wrap_mode_r;
/* Determines how the color of individual texture fragments /* Determines how the color of individual texture fragments
* are calculated. */ * are calculated. */
GLint texture_combine_rgb_func; GLint texture_combine_rgb_func;
@ -230,30 +234,53 @@ _cogl_material_layer_ensure_mipmaps (CoglHandle layer_handler);
* not passing the option at all. * not passing the option at all.
* @COGL_MATERIAL_FLUSH_SKIP_GL_COLOR: When flushing the GL state for the * @COGL_MATERIAL_FLUSH_SKIP_GL_COLOR: When flushing the GL state for the
* material don't call glColor. * material don't call glColor.
* @COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES: Specifies that a bitmask
* of overrides for the wrap modes for some or all layers is
* given.
*/ */
typedef enum _CoglMaterialFlushFlag typedef enum _CoglMaterialFlushFlag
{ {
COGL_MATERIAL_FLUSH_FALLBACK_MASK = 1L<<0, COGL_MATERIAL_FLUSH_FALLBACK_MASK = 1L<<0,
COGL_MATERIAL_FLUSH_DISABLE_MASK = 1L<<1, COGL_MATERIAL_FLUSH_DISABLE_MASK = 1L<<1,
COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE = 1L<<2, COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE = 1L<<2,
COGL_MATERIAL_FLUSH_SKIP_GL_COLOR = 1L<<3 COGL_MATERIAL_FLUSH_SKIP_GL_COLOR = 1L<<3,
COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES = 1L<<4
} CoglMaterialFlushFlag; } CoglMaterialFlushFlag;
/* These constants are used to fill in wrap_mode_overrides */
#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_NONE 0 /* no override */
#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT 1
#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE 2
#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_BORDER 3
/* There can't be more than 32 layers because we need to fit a bitmask
of the layers into a guint32 */
#define COGL_MATERIAL_MAX_LAYERS 32
typedef struct _CoglMaterialWrapModeOverrides
{
struct
{
unsigned long s : 2;
unsigned long t : 2;
unsigned long r : 2;
} values[COGL_MATERIAL_MAX_LAYERS];
} CoglMaterialWrapModeOverrides;
/* /*
* CoglMaterialFlushOptions: * CoglMaterialFlushOptions:
* *
*/ */
typedef struct _CoglMaterialFlushOptions typedef struct _CoglMaterialFlushOptions
{ {
CoglMaterialFlushFlag flags; CoglMaterialFlushFlag flags;
guint32 fallback_layers; guint32 fallback_layers;
guint32 disable_layers; guint32 disable_layers;
GLuint layer0_override_texture; GLuint layer0_override_texture;
CoglMaterialWrapModeOverrides wrap_mode_overrides;
} CoglMaterialFlushOptions; } CoglMaterialFlushOptions;
void void
_cogl_material_get_colorubv (CoglHandle handle, _cogl_material_get_colorubv (CoglHandle handle,
guint8 *color); guint8 *color);
@ -274,6 +301,15 @@ _cogl_material_journal_ref (CoglHandle material_handle);
void void
_cogl_material_journal_unref (CoglHandle material_handle); _cogl_material_journal_unref (CoglHandle material_handle);
/* TODO: These should be made public once we add support for 3D
textures in Cogl */
void
_cogl_material_set_layer_wrap_mode_r (CoglHandle material,
int layer_index,
CoglMaterialWrapMode mode);
CoglMaterialWrapMode
_cogl_material_layer_get_wrap_mode_r (CoglHandle layer);
#endif /* __COGL_MATERIAL_PRIVATE_H */ #endif /* __COGL_MATERIAL_PRIVATE_H */

View File

@ -58,6 +58,11 @@
#define glBlendEquationSeparate ctx->drv.pf_glBlendEquationSeparate #define glBlendEquationSeparate ctx->drv.pf_glBlendEquationSeparate
#endif #endif
/* This isn't defined in the GLES headers */
#ifndef GL_CLAMP_TO_BORDER
#define GL_CLAMP_TO_BORDER 0x812d
#endif
static CoglHandle _cogl_material_layer_copy (CoglHandle layer_handle); static CoglHandle _cogl_material_layer_copy (CoglHandle layer_handle);
static void _cogl_material_free (CoglMaterial *tex); static void _cogl_material_free (CoglMaterial *tex);
@ -776,6 +781,9 @@ _cogl_material_get_layer (CoglMaterial *material,
layer->flags = COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE; layer->flags = COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE;
layer->mag_filter = COGL_MATERIAL_FILTER_LINEAR; layer->mag_filter = COGL_MATERIAL_FILTER_LINEAR;
layer->min_filter = COGL_MATERIAL_FILTER_LINEAR; layer->min_filter = COGL_MATERIAL_FILTER_LINEAR;
layer->wrap_mode_s = COGL_MATERIAL_WRAP_MODE_AUTOMATIC;
layer->wrap_mode_t = COGL_MATERIAL_WRAP_MODE_AUTOMATIC;
layer->wrap_mode_r = COGL_MATERIAL_WRAP_MODE_AUTOMATIC;
layer->texture = COGL_INVALID_HANDLE; layer->texture = COGL_INVALID_HANDLE;
/* Choose the same default combine mode as OpenGL: /* Choose the same default combine mode as OpenGL:
@ -1334,6 +1342,72 @@ _cogl_material_layer_ensure_mipmaps (CoglHandle layer_handle)
_cogl_texture_ensure_mipmaps (layer->texture); _cogl_texture_ensure_mipmaps (layer->texture);
} }
static void
_cogl_material_set_wrap_modes_for_layer (CoglMaterialLayer *layer,
int layer_num,
CoglHandle tex_handle,
const CoglMaterialWrapModeOverrides *
wrap_mode_overrides)
{
GLenum wrap_mode_s, wrap_mode_t, wrap_mode_r;
/* Update the wrap mode on the texture object. The texture backend
should cache the value so that it will be a no-op if the object
already has the same wrap mode set. The backend is best placed to
do this because it knows how many of the coordinates will
actually be used (ie, a 1D texture only cares about the 's'
coordinate but a 3D texture would use all three). GL uses the
wrap mode as part of the texture object state but we are
pretending it's part of the per-layer environment state. This
will break if the application tries to use different modes in
different layers using the same texture. */
if (wrap_mode_overrides && wrap_mode_overrides->values[layer_num].s)
wrap_mode_s = (wrap_mode_overrides->values[layer_num].s ==
COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT ?
GL_REPEAT :
wrap_mode_overrides->values[layer_num].s ==
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE ?
GL_CLAMP_TO_EDGE :
GL_CLAMP_TO_BORDER);
else if (layer->wrap_mode_s == COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
wrap_mode_s = GL_REPEAT;
else
wrap_mode_s = layer->wrap_mode_s;
if (wrap_mode_overrides && wrap_mode_overrides->values[layer_num].t)
wrap_mode_t = (wrap_mode_overrides->values[layer_num].t ==
COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT ?
GL_REPEAT :
wrap_mode_overrides->values[layer_num].t ==
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE ?
GL_CLAMP_TO_EDGE :
GL_CLAMP_TO_BORDER);
else if (layer->wrap_mode_t == COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
wrap_mode_t = GL_REPEAT;
else
wrap_mode_t = layer->wrap_mode_t;
if (wrap_mode_overrides && wrap_mode_overrides->values[layer_num].r)
wrap_mode_r = (wrap_mode_overrides->values[layer_num].r ==
COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT ?
GL_REPEAT :
wrap_mode_overrides->values[layer_num].r ==
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE ?
GL_CLAMP_TO_EDGE :
GL_CLAMP_TO_BORDER);
else if (layer->wrap_mode_r == COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
wrap_mode_r = GL_REPEAT;
else
wrap_mode_r = layer->wrap_mode_r;
_cogl_texture_set_wrap_mode_parameters (tex_handle,
wrap_mode_s,
wrap_mode_t,
wrap_mode_r);
}
/* /*
* _cogl_material_flush_layers_gl_state: * _cogl_material_flush_layers_gl_state:
* @fallback_mask: is a bitmask of the material layers that need to be * @fallback_mask: is a bitmask of the material layers that need to be
@ -1367,6 +1441,9 @@ _cogl_material_layer_ensure_mipmaps (CoglHandle layer_handle)
* The code will can iterate each of the slices and re-flush the material * The code will can iterate each of the slices and re-flush the material
* forcing the GL texture of each slice in turn. * forcing the GL texture of each slice in turn.
* *
* @wrap_mode_overrides: overrides the wrap modes set on each
* layer. This is used to implement the automatic wrap mode.
*
* XXX: It might also help if we could specify a texture matrix for code * XXX: It might also help if we could specify a texture matrix for code
* dealing with slicing that would be multiplied with the users own matrix. * dealing with slicing that would be multiplied with the users own matrix.
* *
@ -1383,7 +1460,9 @@ static void
_cogl_material_flush_layers_gl_state (CoglMaterial *material, _cogl_material_flush_layers_gl_state (CoglMaterial *material,
guint32 fallback_mask, guint32 fallback_mask,
guint32 disable_mask, guint32 disable_mask,
GLuint layer0_override_texture) GLuint layer0_override_texture,
const CoglMaterialWrapModeOverrides *
wrap_mode_overrides)
{ {
GList *tmp; GList *tmp;
int i; int i;
@ -1420,10 +1499,14 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
tex_handle = layer->texture; tex_handle = layer->texture;
if (tex_handle != COGL_INVALID_HANDLE) if (tex_handle != COGL_INVALID_HANDLE)
{ {
_cogl_texture_set_filters (tex_handle, _cogl_texture_set_filters (tex_handle,
layer->min_filter, layer->min_filter,
layer->mag_filter); layer->mag_filter);
_cogl_material_set_wrap_modes_for_layer (layer, i, tex_handle,
wrap_mode_overrides);
cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target); cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target);
} }
else else
@ -1653,11 +1736,12 @@ void
_cogl_material_flush_gl_state (CoglHandle handle, _cogl_material_flush_gl_state (CoglHandle handle,
CoglMaterialFlushOptions *options) CoglMaterialFlushOptions *options)
{ {
CoglMaterial *material; CoglMaterial *material;
guint32 fallback_layers = 0; guint32 fallback_layers = 0;
guint32 disable_layers = 0; guint32 disable_layers = 0;
GLuint layer0_override_texture = 0; GLuint layer0_override_texture = 0;
gboolean skip_gl_color = FALSE; gboolean skip_gl_color = FALSE;
const CoglMaterialWrapModeOverrides *wrap_mode_overrides = NULL;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -1673,6 +1757,8 @@ _cogl_material_flush_gl_state (CoglHandle handle,
layer0_override_texture = options->layer0_override_texture; layer0_override_texture = options->layer0_override_texture;
if (options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR) if (options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR)
skip_gl_color = TRUE; skip_gl_color = TRUE;
if (options->flags & COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES)
wrap_mode_overrides = &options->wrap_mode_overrides;
} }
_cogl_material_flush_base_gl_state (material, _cogl_material_flush_base_gl_state (material,
@ -1681,7 +1767,8 @@ _cogl_material_flush_gl_state (CoglHandle handle,
_cogl_material_flush_layers_gl_state (material, _cogl_material_flush_layers_gl_state (material,
fallback_layers, fallback_layers,
disable_layers, disable_layers,
layer0_override_texture); layer0_override_texture,
wrap_mode_overrides);
/* NB: we have to take a reference so that next time /* NB: we have to take a reference so that next time
* cogl_material_flush_gl_state is called, we can compare the incomming * cogl_material_flush_gl_state is called, we can compare the incomming
@ -1749,6 +1836,11 @@ _cogl_material_layer_equal (CoglMaterialLayer *material0_layer,
if (material0_layer->min_filter != material1_layer->min_filter) if (material0_layer->min_filter != material1_layer->min_filter)
return FALSE; return FALSE;
if (material0_layer->wrap_mode_s != material1_layer->wrap_mode_s ||
material0_layer->wrap_mode_t != material1_layer->wrap_mode_t ||
material0_layer->wrap_mode_r != material1_layer->wrap_mode_r)
return FALSE;
return TRUE; return TRUE;
} }
@ -1822,6 +1914,17 @@ _cogl_material_equal (CoglHandle material0_handle,
material1_flush_options->layer0_override_texture) material1_flush_options->layer0_override_texture)
return FALSE; return FALSE;
/* If one has wrap mode overrides and the other doesn't then the
materials are different */
if (((flush_flags0 ^ flush_flags1) & COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES))
return FALSE;
/* If they both have overrides then we need to compare them */
if ((flush_flags0 & COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES) &&
memcmp (&material0_flush_options->wrap_mode_overrides,
&material1_flush_options->wrap_mode_overrides,
sizeof (CoglMaterialWrapModeOverrides)))
return FALSE;
/* Since we know the flush options match at this point, if the material /* Since we know the flush options match at this point, if the material
* handles match then we know they are equivalent. */ * handles match then we know they are equivalent. */
if (material0_handle == material1_handle) if (material0_handle == material1_handle)
@ -2032,3 +2135,107 @@ cogl_material_set_layer_filters (CoglHandle handle,
layer->min_filter = min_filter; layer->min_filter = min_filter;
layer->mag_filter = mag_filter; layer->mag_filter = mag_filter;
} }
void
cogl_material_set_layer_wrap_mode_s (CoglHandle handle,
int layer_index,
CoglMaterialWrapMode mode)
{
CoglMaterial *material;
CoglMaterialLayer *layer;
g_return_if_fail (cogl_is_material (handle));
material = _cogl_material_pointer_from_handle (handle);
layer = _cogl_material_get_layer (material, layer_index, TRUE);
if (layer->wrap_mode_s != mode)
{
/* possibly flush primitives referencing the current state... */
_cogl_material_pre_change_notify (material, FALSE, NULL);
layer->wrap_mode_s = mode;
}
}
void
cogl_material_set_layer_wrap_mode_t (CoglHandle handle,
int layer_index,
CoglMaterialWrapMode mode)
{
CoglMaterial *material;
CoglMaterialLayer *layer;
g_return_if_fail (cogl_is_material (handle));
material = _cogl_material_pointer_from_handle (handle);
layer = _cogl_material_get_layer (material, layer_index, TRUE);
if (layer->wrap_mode_t != mode)
{
/* possibly flush primitives referencing the current state... */
_cogl_material_pre_change_notify (material, FALSE, NULL);
layer->wrap_mode_t = mode;
}
}
/* TODO: this should be made public once we add support for 3D
textures in Cogl */
void
_cogl_material_set_layer_wrap_mode_r (CoglHandle handle,
int layer_index,
CoglMaterialWrapMode mode)
{
CoglMaterial *material;
CoglMaterialLayer *layer;
g_return_if_fail (cogl_is_material (handle));
material = _cogl_material_pointer_from_handle (handle);
layer = _cogl_material_get_layer (material, layer_index, TRUE);
if (layer->wrap_mode_r != mode)
{
/* possibly flush primitives referencing the current state... */
_cogl_material_pre_change_notify (material, FALSE, NULL);
layer->wrap_mode_r = mode;
}
}
void
cogl_material_set_layer_wrap_mode (CoglHandle material,
int layer_index,
CoglMaterialWrapMode mode)
{
cogl_material_set_layer_wrap_mode_s (material, layer_index, mode);
cogl_material_set_layer_wrap_mode_t (material, layer_index, mode);
_cogl_material_set_layer_wrap_mode_r (material, layer_index, mode);
}
CoglMaterialWrapMode
cogl_material_layer_get_wrap_mode_s (CoglHandle handle)
{
g_return_val_if_fail (cogl_is_material_layer (handle), FALSE);
return _cogl_material_layer_pointer_from_handle (handle)->wrap_mode_s;
}
CoglMaterialWrapMode
cogl_material_layer_get_wrap_mode_t (CoglHandle handle)
{
g_return_val_if_fail (cogl_is_material_layer (handle), FALSE);
return _cogl_material_layer_pointer_from_handle (handle)->wrap_mode_t;
}
/* TODO: this should be made public once we add support for 3D
textures in Cogl */
CoglMaterialWrapMode
_cogl_material_layer_get_wrap_mode_r (CoglHandle handle)
{
g_return_val_if_fail (cogl_is_material_layer (handle), FALSE);
return _cogl_material_layer_pointer_from_handle (handle)->wrap_mode_r;
}

View File

@ -79,6 +79,40 @@ typedef enum {
COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR
} CoglMaterialFilter; } CoglMaterialFilter;
/**
* CoglMaterialWrapMode:
* @COGL_MATERIAL_WRAP_MODE_REPEAT: The texture will be repeated. This
* is useful for example to draw a tiled background.
* @COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE: The coordinates outside the
* range 01 will sample copies of the edge pixels of the
* texture. This is useful to avoid artifacts if only one copy of
* the texture is being rendered.
* @COGL_MATERIAL_WRAP_MODE_AUTOMATIC: Cogl will try to automatically
* decide which of the above two to use. For cogl_rectangle(), it
* will use repeat mode if any of the texture coordinates are
* outside the range 01, otherwise it will use clamp to edge. For
* cogl_polygon() and cogl_vertex_buffer_draw() it will always use
* repeat mode. This is the default value.
*
* The wrap mode specifies what happens when texture coordinates
* outside the range 01 are used. Note that if the filter mode is
* anything but %COGL_MATERIAL_FILTER_NEAREST then texels outside the
* range 01 might be used even when the coordinate is exactly 0 or 1
* because OpenGL will try to sample neighbouring pixels. For example
* if you are trying to render the full texture then you may get
* artifacts around the edges when the pixels from the other side are
* merged in if the wrap mode is set to repeat.
*
* Since: 1.4
*/
/* GL_ALWAYS is just used here as a value that is known not to clash
with any valid GL wrap modes */
typedef enum {
COGL_MATERIAL_WRAP_MODE_REPEAT = GL_REPEAT,
COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,
COGL_MATERIAL_WRAP_MODE_AUTOMATIC = GL_ALWAYS
} CoglMaterialWrapMode;
/** /**
* cogl_material_new: * cogl_material_new:
* *
@ -836,6 +870,76 @@ cogl_material_set_layer_filters (CoglHandle material,
CoglMaterialFilter min_filter, CoglMaterialFilter min_filter,
CoglMaterialFilter mag_filter); CoglMaterialFilter mag_filter);
/**
* cogl_material_set_layer_wrap_mode_s:
* @material: a #CoglHandle to a material.
* @layer_index: the layer number to change.
* @mode: the new wrap mode
*
* Sets the wrap mode for the 's' coordinate of texture lookups on this layer.
*
* Since: 1.4
*/
void
cogl_material_set_layer_wrap_mode_s (CoglHandle material,
int layer_index,
CoglMaterialWrapMode mode);
/**
* cogl_material_set_layer_wrap_mode_t:
* @material: a #CoglHandle to a material.
* @layer_index: the layer number to change.
* @mode: the new wrap mode
*
* Sets the wrap mode for the 't' coordinate of texture lookups on this layer.
*
* Since: 1.4
*/
void
cogl_material_set_layer_wrap_mode_t (CoglHandle material,
int layer_index,
CoglMaterialWrapMode mode);
/**
* cogl_material_set_layer_wrap_mode:
* @material: a #CoglHandle to a material.
* @layer_index: the layer number to change.
* @mode: the new wrap mode
*
* Sets the wrap mode for both coordinates of texture lookups on this
* layer. This is equivalent to calling
* cogl_material_set_layer_wrap_mode_s() and
* cogl_material_set_layer_wrap_mode_t() separately.
*
* Since: 1.4
*/
void
cogl_material_set_layer_wrap_mode (CoglHandle material,
int layer_index,
CoglMaterialWrapMode mode);
/**
* cogl_material_layer_get_wrap_mode_s:
* @layer: a #CoglHandle to a material mayer.
*
* Gets the wrap mode for the 's' coordinate of texture lookups on this layer.
*
* Since: 1.4
*/
CoglMaterialWrapMode
cogl_material_layer_get_wrap_mode_s (CoglHandle layer);
/**
* cogl_material_layer_get_wrap_mode_t:
* @layer: a #CoglHandle to a material mayer.
*
* Gets the wrap mode for the 't' coordinate of texture lookups on this layer.
*
* Since: 1.4
*/
CoglMaterialWrapMode
cogl_material_layer_get_wrap_mode_t (CoglHandle layer);
G_END_DECLS G_END_DECLS
#endif /* __COGL_MATERIAL_H__ */ #endif /* __COGL_MATERIAL_H__ */

View File

@ -57,6 +57,7 @@ typedef struct _TextureSlicedQuadState
float quad_len_y; float quad_len_y;
gboolean flipped_x; gboolean flipped_x;
gboolean flipped_y; gboolean flipped_y;
CoglMaterialWrapModeOverrides wrap_mode_overrides;
} TextureSlicedQuadState; } TextureSlicedQuadState;
typedef struct _TextureSlicedPolygonState typedef struct _TextureSlicedPolygonState
@ -66,7 +67,6 @@ typedef struct _TextureSlicedPolygonState
int stride; int stride;
} TextureSlicedPolygonState; } TextureSlicedPolygonState;
static void static void
log_quad_sub_textures_cb (CoglHandle texture_handle, log_quad_sub_textures_cb (CoglHandle texture_handle,
GLuint gl_handle, GLuint gl_handle,
@ -117,6 +117,7 @@ log_quad_sub_textures_cb (CoglHandle texture_handle,
1, /* one layer */ 1, /* one layer */
0, /* don't need to use fallbacks */ 0, /* don't need to use fallbacks */
gl_handle, /* replace the layer0 texture */ gl_handle, /* replace the layer0 texture */
&state->wrap_mode_overrides, /* use CLAMP_TO_EDGE */
subtexture_coords, subtexture_coords,
4); 4);
} }
@ -138,6 +139,8 @@ log_quad_sub_textures_cb (CoglHandle texture_handle,
static void static void
_cogl_texture_quad_multiple_primitives (CoglHandle tex_handle, _cogl_texture_quad_multiple_primitives (CoglHandle tex_handle,
CoglHandle material, CoglHandle material,
gboolean clamp_s,
gboolean clamp_t,
const float *position, const float *position,
float tx_1, float tx_1,
float ty_1, float ty_1,
@ -152,15 +155,112 @@ _cogl_texture_quad_multiple_primitives (CoglHandle tex_handle,
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
COGL_NOTE (DRAW, "Drawing Tex Quad (Multi-Prim Mode)"); /* If the wrap mode is clamp to edge then we'll recursively draw the
stretched part and replace the coordinates */
if (clamp_s && tx_1 != tx_2)
{
float *replacement_position = g_newa (float, 4);
float old_tx_1 = tx_1, old_tx_2 = tx_2;
memcpy (replacement_position, position, sizeof (float) * 4);
tx_1 = CLAMP (tx_1, 0.0f, 1.0f);
tx_2 = CLAMP (tx_2, 0.0f, 1.0f);
if (old_tx_1 != tx_1)
{
/* Draw the left part of the quad as a stretched copy of tx_1 */
float tmp_position[] =
{ position[0], position[1],
(position[0] +
(position[2] - position[0]) *
(tx_1 - old_tx_1) / (old_tx_2 - old_tx_1)),
position[3] };
_cogl_texture_quad_multiple_primitives (tex_handle, material,
FALSE, clamp_t,
tmp_position,
tx_1, ty_1, tx_1, ty_2);
replacement_position[0] = tmp_position[2];
}
if (old_tx_2 != tx_2)
{
/* Draw the right part of the quad as a stretched copy of tx_2 */
float tmp_position[] =
{ (position[0] +
(position[2] - position[0]) *
(tx_2 - old_tx_1) / (old_tx_2 - old_tx_1)),
position[1], position[2], position[3] };
_cogl_texture_quad_multiple_primitives (tex_handle, material,
FALSE, clamp_t,
tmp_position,
tx_2, ty_1, tx_2, ty_2);
replacement_position[2] = tmp_position[0];
}
/* If there's no main part left then we don't need to continue */
if (tx_1 == tx_2)
return;
position = replacement_position;
}
if (clamp_t && ty_1 != ty_2)
{
float *replacement_position = g_newa (float, 4);
float old_ty_1 = ty_1, old_ty_2 = ty_2;
memcpy (replacement_position, position, sizeof (float) * 4);
ty_1 = CLAMP (ty_1, 0.0f, 1.0f);
ty_2 = CLAMP (ty_2, 0.0f, 1.0f);
if (old_ty_1 != ty_1)
{
/* Draw the top part of the quad as a stretched copy of ty_1 */
float tmp_position[] =
{ position[0], position[1], position[2],
(position[1] +
(position[3] - position[1]) *
(ty_1 - old_ty_1) / (old_ty_2 - old_ty_1)) };
_cogl_texture_quad_multiple_primitives (tex_handle, material,
clamp_s, FALSE,
tmp_position,
tx_1, ty_1, tx_2, ty_1);
replacement_position[1] = tmp_position[3];
}
if (old_ty_2 != ty_2)
{
/* Draw the bottom part of the quad as a stretched copy of ty_2 */
float tmp_position[] =
{ position[0],
(position[1] +
(position[3] - position[1]) *
(ty_2 - old_ty_1) / (old_ty_2 - old_ty_1)),
position[2], position[3] };
_cogl_texture_quad_multiple_primitives (tex_handle, material,
clamp_s, FALSE,
tmp_position,
tx_1, ty_2, tx_2, ty_2);
replacement_position[3] = tmp_position[1];
}
/* If there's no main part left then we don't need to continue */
if (ty_1 == ty_2)
return;
position = replacement_position;
}
memset (&state.wrap_mode_overrides, 0, sizeof (state.wrap_mode_overrides));
/* We can't use hardware repeat so we need to set clamp to edge /* We can't use hardware repeat so we need to set clamp to edge
otherwise it might pull in edge pixels from the other side */ otherwise it might pull in edge pixels from the other side */
/* FIXME: wrap modes should be part of the material! */ state.wrap_mode_overrides.values[0].s =
_cogl_texture_set_wrap_mode_parameters (tex_handle, COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE;
GL_CLAMP_TO_EDGE, state.wrap_mode_overrides.values[0].t =
GL_CLAMP_TO_EDGE, COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE;
GL_CLAMP_TO_EDGE);
state.material = material; state.material = material;
@ -239,9 +339,12 @@ _cogl_multitexture_quad_single_primitive (const float *position,
const GList *layers; const GList *layers;
GList *tmp; GList *tmp;
int i; int i;
CoglMaterialWrapModeOverrides wrap_mode_overrides;
_COGL_GET_CONTEXT (ctx, FALSE); _COGL_GET_CONTEXT (ctx, FALSE);
memset (&wrap_mode_overrides, 0, sizeof (wrap_mode_overrides));
/* /*
* Validate the texture coordinates for this rectangle. * Validate the texture coordinates for this rectangle.
*/ */
@ -254,7 +357,7 @@ _cogl_multitexture_quad_single_primitive (const float *position,
float *out_tex_coords; float *out_tex_coords;
float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0};
CoglTransformResult transform_result; CoglTransformResult transform_result;
GLenum wrap_mode; unsigned long auto_wrap_mode;
tex_handle = cogl_material_layer_get_texture (layer); tex_handle = cogl_material_layer_get_texture (layer);
@ -328,14 +431,16 @@ _cogl_multitexture_quad_single_primitive (const float *position,
to the edge otherwise it can pull in edge pixels from the to the edge otherwise it can pull in edge pixels from the
wrong side when scaled */ wrong side when scaled */
if (transform_result == COGL_TRANSFORM_HARDWARE_REPEAT) if (transform_result == COGL_TRANSFORM_HARDWARE_REPEAT)
wrap_mode = GL_REPEAT; auto_wrap_mode = COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT;
else else
wrap_mode = GL_CLAMP_TO_EDGE; auto_wrap_mode = COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE;
_cogl_texture_set_wrap_mode_parameters (tex_handle, if (cogl_material_layer_get_wrap_mode_s (layer) ==
wrap_mode, COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
wrap_mode, wrap_mode_overrides.values[i].s = auto_wrap_mode;
wrap_mode); if (cogl_material_layer_get_wrap_mode_t (layer) ==
COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
wrap_mode_overrides.values[i].t = auto_wrap_mode;
} }
_cogl_journal_log_quad (position, _cogl_journal_log_quad (position,
@ -343,6 +448,7 @@ _cogl_multitexture_quad_single_primitive (const float *position,
n_layers, n_layers,
fallback_layers, fallback_layers,
0, /* don't replace the layer0 texture */ 0, /* don't replace the layer0 texture */
&wrap_mode_overrides,
final_tex_coords, final_tex_coords,
n_layers * 4); n_layers * 4);
@ -480,6 +586,7 @@ _cogl_rectangles_with_multitexture_coords (
CoglHandle first_layer, tex_handle; CoglHandle first_layer, tex_handle;
const float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; const float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0};
const float *tex_coords; const float *tex_coords;
gboolean clamp_s, clamp_t;
if (!all_use_sliced_quad_fallback) if (!all_use_sliced_quad_fallback)
{ {
@ -509,8 +616,16 @@ _cogl_rectangles_with_multitexture_coords (
else else
tex_coords = default_tex_coords; tex_coords = default_tex_coords;
clamp_s = (cogl_material_layer_get_wrap_mode_s (first_layer) ==
COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE);
clamp_t = (cogl_material_layer_get_wrap_mode_t (first_layer) ==
COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE);
COGL_NOTE (DRAW, "Drawing Tex Quad (Multi-Prim Mode)");
_cogl_texture_quad_multiple_primitives (tex_handle, _cogl_texture_quad_multiple_primitives (tex_handle,
material, material,
clamp_s, clamp_t,
rects[i].position, rects[i].position,
tex_coords[0], tex_coords[0],
tex_coords[1], tex_coords[1],
@ -691,11 +806,26 @@ draw_polygon_sub_texture_cb (CoglHandle tex_handle,
options.flags = options.flags =
COGL_MATERIAL_FLUSH_DISABLE_MASK | COGL_MATERIAL_FLUSH_DISABLE_MASK |
COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE; COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE |
COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES;
/* disable all except the first layer */ /* disable all except the first layer */
options.disable_layers = (guint32)~1; options.disable_layers = (guint32)~1;
options.layer0_override_texture = gl_handle; options.layer0_override_texture = gl_handle;
/* Override the wrapping mode on all of the slices to use a
transparent border so that we can draw the full polygon for
each slice. Coordinates outside the texture will be transparent
so only the part of the polygon that intersects the slice will
be visible. This is a fairly hacky fallback and it relies on
the blending function working correctly */
memset (&options.wrap_mode_overrides, 0,
sizeof (options.wrap_mode_overrides));
options.wrap_mode_overrides.values[0].s =
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_BORDER;
options.wrap_mode_overrides.values[0].t =
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_BORDER;
_cogl_material_flush_gl_state (ctx->source_material, &options); _cogl_material_flush_gl_state (ctx->source_material, &options);
GE (glDrawArrays (GL_TRIANGLE_FAN, 0, state->n_vertices)); GE (glDrawArrays (GL_TRIANGLE_FAN, 0, state->n_vertices));
@ -920,18 +1050,6 @@ cogl_polygon (const CoglTextureVertex *vertices,
return; return;
} }
#ifdef HAVE_COGL_GL
{
/* Temporarily change the wrapping mode on all of the slices to use
* a transparent border
* XXX: it's doesn't look like we save/restore this, like
* the comment implies? */
_cogl_texture_set_wrap_mode_parameters (tex_handle,
GL_CLAMP_TO_BORDER,
GL_CLAMP_TO_BORDER,
GL_CLAMP_TO_BORDER);
}
#endif
break; break;
} }

View File

@ -676,10 +676,6 @@ _cogl_texture_2d_sliced_set_wrap_mode_parameters (CoglTexture *tex,
{ {
int i; int i;
/* Any queued texture rectangles may be depending on the previous
* wrap mode... */
_cogl_journal_flush ();
for (i = 0; i < tex_2ds->slice_gl_handles->len; i++) for (i = 0; i < tex_2ds->slice_gl_handles->len; i++)
{ {
GLuint texnum = g_array_index (tex_2ds->slice_gl_handles, GLuint, i); GLuint texnum = g_array_index (tex_2ds->slice_gl_handles, GLuint, i);

View File

@ -137,10 +137,6 @@ _cogl_texture_2d_set_wrap_mode_parameters (CoglTexture *tex,
if (tex_2d->wrap_mode_s != wrap_mode_s || if (tex_2d->wrap_mode_s != wrap_mode_s ||
tex_2d->wrap_mode_t != wrap_mode_t) tex_2d->wrap_mode_t != wrap_mode_t)
{ {
/* Any queued texture rectangles may be depending on the
* previous wrap mode... */
_cogl_journal_flush ();
GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) );
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode_s) ); GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode_s) );
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode_t) ); GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode_t) );

View File

@ -467,6 +467,11 @@ cogl_material_layer_get_type
cogl_material_layer_get_texture cogl_material_layer_get_texture
cogl_material_layer_get_min_filter cogl_material_layer_get_min_filter
cogl_material_layer_get_mag_filter cogl_material_layer_get_mag_filter
cogl_material_set_layer_wrap_mode
cogl_material_set_layer_wrap_mode_s
cogl_material_set_layer_wrap_mode_t
cogl_material_layer_get_wrap_mode_s
cogl_material_layer_get_wrap_mode_t
<SUBSECTION Private> <SUBSECTION Private>
cogl_blend_string_error_get_type cogl_blend_string_error_get_type
cogl_blend_string_error_quark cogl_blend_string_error_quark