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:
parent
fb7f1a7fd6
commit
e007bc5358
@ -46,6 +46,8 @@ _cogl_journal_log_quad (const float *position,
|
||||
int n_layers,
|
||||
guint32 fallback_layers,
|
||||
GLuint layer0_override_texture,
|
||||
const CoglMaterialWrapModeOverrides *
|
||||
wrap_mode_overrides,
|
||||
const float *tex_coords,
|
||||
unsigned int tex_coords_len);
|
||||
|
||||
|
@ -663,6 +663,8 @@ _cogl_journal_log_quad (const float *position,
|
||||
int n_layers,
|
||||
guint32 fallback_layers,
|
||||
GLuint layer0_override_texture,
|
||||
const CoglMaterialWrapModeOverrides *
|
||||
wrap_mode_overrides,
|
||||
const float *tex_coords,
|
||||
unsigned int tex_coords_len)
|
||||
{
|
||||
@ -803,6 +805,11 @@ _cogl_journal_log_quad (const float *position,
|
||||
COGL_MATERIAL_FLUSH_SKIP_GL_COLOR;
|
||||
entry->flush_options.fallback_layers = fallback_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)
|
||||
{
|
||||
entry->flush_options.flags |= COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE;
|
||||
|
@ -81,6 +81,10 @@ struct _CoglMaterialLayer
|
||||
CoglMaterialFilter mag_filter;
|
||||
CoglMaterialFilter min_filter;
|
||||
|
||||
CoglMaterialWrapMode wrap_mode_s;
|
||||
CoglMaterialWrapMode wrap_mode_t;
|
||||
CoglMaterialWrapMode wrap_mode_r;
|
||||
|
||||
/* Determines how the color of individual texture fragments
|
||||
* are calculated. */
|
||||
GLint texture_combine_rgb_func;
|
||||
@ -230,15 +234,39 @@ _cogl_material_layer_ensure_mipmaps (CoglHandle layer_handler);
|
||||
* not passing the option at all.
|
||||
* @COGL_MATERIAL_FLUSH_SKIP_GL_COLOR: When flushing the GL state for the
|
||||
* 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
|
||||
{
|
||||
COGL_MATERIAL_FLUSH_FALLBACK_MASK = 1L<<0,
|
||||
COGL_MATERIAL_FLUSH_DISABLE_MASK = 1L<<1,
|
||||
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;
|
||||
|
||||
/* 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:
|
||||
*
|
||||
@ -250,10 +278,9 @@ typedef struct _CoglMaterialFlushOptions
|
||||
guint32 fallback_layers;
|
||||
guint32 disable_layers;
|
||||
GLuint layer0_override_texture;
|
||||
CoglMaterialWrapModeOverrides wrap_mode_overrides;
|
||||
} CoglMaterialFlushOptions;
|
||||
|
||||
|
||||
|
||||
void
|
||||
_cogl_material_get_colorubv (CoglHandle handle,
|
||||
guint8 *color);
|
||||
@ -274,6 +301,15 @@ _cogl_material_journal_ref (CoglHandle material_handle);
|
||||
void
|
||||
_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 */
|
||||
|
||||
|
@ -58,6 +58,11 @@
|
||||
#define glBlendEquationSeparate ctx->drv.pf_glBlendEquationSeparate
|
||||
#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 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->mag_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;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
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:
|
||||
* @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
|
||||
* 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
|
||||
* 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,
|
||||
guint32 fallback_mask,
|
||||
guint32 disable_mask,
|
||||
GLuint layer0_override_texture)
|
||||
GLuint layer0_override_texture,
|
||||
const CoglMaterialWrapModeOverrides *
|
||||
wrap_mode_overrides)
|
||||
{
|
||||
GList *tmp;
|
||||
int i;
|
||||
@ -1420,10 +1499,14 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
|
||||
tex_handle = layer->texture;
|
||||
if (tex_handle != COGL_INVALID_HANDLE)
|
||||
{
|
||||
|
||||
_cogl_texture_set_filters (tex_handle,
|
||||
layer->min_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);
|
||||
}
|
||||
else
|
||||
@ -1658,6 +1741,7 @@ _cogl_material_flush_gl_state (CoglHandle handle,
|
||||
guint32 disable_layers = 0;
|
||||
GLuint layer0_override_texture = 0;
|
||||
gboolean skip_gl_color = FALSE;
|
||||
const CoglMaterialWrapModeOverrides *wrap_mode_overrides = NULL;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
@ -1673,6 +1757,8 @@ _cogl_material_flush_gl_state (CoglHandle handle,
|
||||
layer0_override_texture = options->layer0_override_texture;
|
||||
if (options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR)
|
||||
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,
|
||||
@ -1681,7 +1767,8 @@ _cogl_material_flush_gl_state (CoglHandle handle,
|
||||
_cogl_material_flush_layers_gl_state (material,
|
||||
fallback_layers,
|
||||
disable_layers,
|
||||
layer0_override_texture);
|
||||
layer0_override_texture,
|
||||
wrap_mode_overrides);
|
||||
|
||||
/* NB: we have to take a reference so that next time
|
||||
* 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)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1822,6 +1914,17 @@ _cogl_material_equal (CoglHandle material0_handle,
|
||||
material1_flush_options->layer0_override_texture)
|
||||
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
|
||||
* handles match then we know they are equivalent. */
|
||||
if (material0_handle == material1_handle)
|
||||
@ -2032,3 +2135,107 @@ cogl_material_set_layer_filters (CoglHandle handle,
|
||||
layer->min_filter = min_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;
|
||||
}
|
||||
|
@ -79,6 +79,40 @@ typedef enum {
|
||||
COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR
|
||||
} 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 0→1 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 0→1, 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 0→1 are used. Note that if the filter mode is
|
||||
* anything but %COGL_MATERIAL_FILTER_NEAREST then texels outside the
|
||||
* range 0→1 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:
|
||||
*
|
||||
@ -836,6 +870,76 @@ cogl_material_set_layer_filters (CoglHandle material,
|
||||
CoglMaterialFilter min_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
|
||||
|
||||
#endif /* __COGL_MATERIAL_H__ */
|
||||
|
@ -57,6 +57,7 @@ typedef struct _TextureSlicedQuadState
|
||||
float quad_len_y;
|
||||
gboolean flipped_x;
|
||||
gboolean flipped_y;
|
||||
CoglMaterialWrapModeOverrides wrap_mode_overrides;
|
||||
} TextureSlicedQuadState;
|
||||
|
||||
typedef struct _TextureSlicedPolygonState
|
||||
@ -66,7 +67,6 @@ typedef struct _TextureSlicedPolygonState
|
||||
int stride;
|
||||
} TextureSlicedPolygonState;
|
||||
|
||||
|
||||
static void
|
||||
log_quad_sub_textures_cb (CoglHandle texture_handle,
|
||||
GLuint gl_handle,
|
||||
@ -117,6 +117,7 @@ log_quad_sub_textures_cb (CoglHandle texture_handle,
|
||||
1, /* one layer */
|
||||
0, /* don't need to use fallbacks */
|
||||
gl_handle, /* replace the layer0 texture */
|
||||
&state->wrap_mode_overrides, /* use CLAMP_TO_EDGE */
|
||||
subtexture_coords,
|
||||
4);
|
||||
}
|
||||
@ -138,6 +139,8 @@ log_quad_sub_textures_cb (CoglHandle texture_handle,
|
||||
static void
|
||||
_cogl_texture_quad_multiple_primitives (CoglHandle tex_handle,
|
||||
CoglHandle material,
|
||||
gboolean clamp_s,
|
||||
gboolean clamp_t,
|
||||
const float *position,
|
||||
float tx_1,
|
||||
float ty_1,
|
||||
@ -152,15 +155,112 @@ _cogl_texture_quad_multiple_primitives (CoglHandle tex_handle,
|
||||
|
||||
_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
|
||||
otherwise it might pull in edge pixels from the other side */
|
||||
/* FIXME: wrap modes should be part of the material! */
|
||||
_cogl_texture_set_wrap_mode_parameters (tex_handle,
|
||||
GL_CLAMP_TO_EDGE,
|
||||
GL_CLAMP_TO_EDGE,
|
||||
GL_CLAMP_TO_EDGE);
|
||||
state.wrap_mode_overrides.values[0].s =
|
||||
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE;
|
||||
state.wrap_mode_overrides.values[0].t =
|
||||
COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE;
|
||||
|
||||
state.material = material;
|
||||
|
||||
@ -239,9 +339,12 @@ _cogl_multitexture_quad_single_primitive (const float *position,
|
||||
const GList *layers;
|
||||
GList *tmp;
|
||||
int i;
|
||||
CoglMaterialWrapModeOverrides wrap_mode_overrides;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, FALSE);
|
||||
|
||||
memset (&wrap_mode_overrides, 0, sizeof (wrap_mode_overrides));
|
||||
|
||||
/*
|
||||
* Validate the texture coordinates for this rectangle.
|
||||
*/
|
||||
@ -254,7 +357,7 @@ _cogl_multitexture_quad_single_primitive (const float *position,
|
||||
float *out_tex_coords;
|
||||
float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0};
|
||||
CoglTransformResult transform_result;
|
||||
GLenum wrap_mode;
|
||||
unsigned long auto_wrap_mode;
|
||||
|
||||
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
|
||||
wrong side when scaled */
|
||||
if (transform_result == COGL_TRANSFORM_HARDWARE_REPEAT)
|
||||
wrap_mode = GL_REPEAT;
|
||||
auto_wrap_mode = COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT;
|
||||
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,
|
||||
wrap_mode,
|
||||
wrap_mode,
|
||||
wrap_mode);
|
||||
if (cogl_material_layer_get_wrap_mode_s (layer) ==
|
||||
COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
|
||||
wrap_mode_overrides.values[i].s = auto_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,
|
||||
@ -343,6 +448,7 @@ _cogl_multitexture_quad_single_primitive (const float *position,
|
||||
n_layers,
|
||||
fallback_layers,
|
||||
0, /* don't replace the layer0 texture */
|
||||
&wrap_mode_overrides,
|
||||
final_tex_coords,
|
||||
n_layers * 4);
|
||||
|
||||
@ -480,6 +586,7 @@ _cogl_rectangles_with_multitexture_coords (
|
||||
CoglHandle first_layer, tex_handle;
|
||||
const float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0};
|
||||
const float *tex_coords;
|
||||
gboolean clamp_s, clamp_t;
|
||||
|
||||
if (!all_use_sliced_quad_fallback)
|
||||
{
|
||||
@ -509,8 +616,16 @@ _cogl_rectangles_with_multitexture_coords (
|
||||
else
|
||||
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,
|
||||
material,
|
||||
clamp_s, clamp_t,
|
||||
rects[i].position,
|
||||
tex_coords[0],
|
||||
tex_coords[1],
|
||||
@ -691,11 +806,26 @@ draw_polygon_sub_texture_cb (CoglHandle tex_handle,
|
||||
|
||||
options.flags =
|
||||
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 */
|
||||
options.disable_layers = (guint32)~1;
|
||||
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);
|
||||
|
||||
GE (glDrawArrays (GL_TRIANGLE_FAN, 0, state->n_vertices));
|
||||
@ -920,18 +1050,6 @@ cogl_polygon (const CoglTextureVertex *vertices,
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -676,10 +676,6 @@ _cogl_texture_2d_sliced_set_wrap_mode_parameters (CoglTexture *tex,
|
||||
{
|
||||
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++)
|
||||
{
|
||||
GLuint texnum = g_array_index (tex_2ds->slice_gl_handles, GLuint, i);
|
||||
|
@ -137,10 +137,6 @@ _cogl_texture_2d_set_wrap_mode_parameters (CoglTexture *tex,
|
||||
if (tex_2d->wrap_mode_s != wrap_mode_s ||
|
||||
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( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode_s) );
|
||||
GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode_t) );
|
||||
|
@ -467,6 +467,11 @@ cogl_material_layer_get_type
|
||||
cogl_material_layer_get_texture
|
||||
cogl_material_layer_get_min_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>
|
||||
cogl_blend_string_error_get_type
|
||||
cogl_blend_string_error_quark
|
||||
|
Loading…
Reference in New Issue
Block a user