diff --git a/cogl/cogl-journal-private.h b/cogl/cogl-journal-private.h index 8c74d093a..afe7ea10f 100644 --- a/cogl/cogl-journal-private.h +++ b/cogl/cogl-journal-private.h @@ -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); diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c index 87db61885..3b3a1cce8 100644 --- a/cogl/cogl-journal.c +++ b/cogl/cogl-journal.c @@ -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; diff --git a/cogl/cogl-material-private.h b/cogl/cogl-material-private.h index 7f444036d..df4e0816a 100644 --- a/cogl/cogl-material-private.h +++ b/cogl/cogl-material-private.h @@ -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,30 +234,53 @@ _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_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_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: * */ typedef struct _CoglMaterialFlushOptions { - CoglMaterialFlushFlag flags; + CoglMaterialFlushFlag flags; - guint32 fallback_layers; - guint32 disable_layers; - GLuint layer0_override_texture; + 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 */ diff --git a/cogl/cogl-material.c b/cogl/cogl-material.c index 231ac3f95..794809dda 100644 --- a/cogl/cogl-material.c +++ b/cogl/cogl-material.c @@ -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 @@ -1653,11 +1736,12 @@ void _cogl_material_flush_gl_state (CoglHandle handle, CoglMaterialFlushOptions *options) { - CoglMaterial *material; - guint32 fallback_layers = 0; - guint32 disable_layers = 0; - GLuint layer0_override_texture = 0; - gboolean skip_gl_color = FALSE; + CoglMaterial *material; + guint32 fallback_layers = 0; + 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; +} diff --git a/cogl/cogl-material.h b/cogl/cogl-material.h index 88c4b35fe..425c7f77d 100644 --- a/cogl/cogl-material.h +++ b/cogl/cogl-material.h @@ -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__ */ diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index 16753ff54..7f98f35d9 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -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; } diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index f1b6aa153..9769174db 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -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); diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c index 0c3a0f63d..044f75d33 100644 --- a/cogl/cogl-texture-2d.c +++ b/cogl/cogl-texture-2d.c @@ -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) ); diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 92c812ca6..770d4f68d 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -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 cogl_blend_string_error_get_type cogl_blend_string_error_quark