From da3be3df6b70efc55d27c757629a5efe9cfaaaad Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 22 Mar 2010 13:33:55 +0000 Subject: [PATCH] cogl-material: Add support for point sprites This adds a new API call to enable point sprite coordinate generation for a material layer: void cogl_material_set_layer_point_sprite_coords_enabled (CoglHandle material, int layer_index, gboolean enable); There is also a corresponding get function. Enabling point sprite coords simply sets the GL_COORD_REPLACE of the GL_POINT_SPRITE glTexEnv when flusing the material. There is no separate application control for glEnable(GL_POINT_SPRITE). Instead it is left permanently enabled under the assumption that it has no affect unless GL_COORD_REPLACE is enabled for a texture unit. http://bugzilla.openedhand.com/show_bug.cgi?id=2047 --- cogl/cogl-context.c | 14 ++ cogl/cogl-material-private.h | 12 +- cogl/cogl-material.c | 166 +++++++++++++++++++++++- cogl/cogl-material.h | 44 +++++++ cogl/cogl-types.h | 5 +- cogl/driver/gl/cogl-feature-functions.h | 7 + cogl/driver/gles/cogl-gles2-wrapper.c | 65 +++++++--- cogl/driver/gles/cogl-gles2-wrapper.h | 19 ++- cogl/driver/gles/cogl.c | 3 + doc/reference/cogl/cogl-sections.txt | 2 + 10 files changed, 306 insertions(+), 31 deletions(-) diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index 5a636a9b4..114336a4e 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -41,6 +41,11 @@ #define glActiveTexture _context->drv.pf_glActiveTexture #endif +/* This isn't defined in the GLES headers */ +#ifndef GL_POINT_SPRITE +#define GL_POINT_SPRITE 0x8861 +#endif + extern void _cogl_create_context_driver (CoglContext *context); extern void @@ -211,6 +216,15 @@ cogl_create_context (void) _context->atlas = NULL; _context->atlas_texture = COGL_INVALID_HANDLE; + /* As far as I can tell, GL_POINT_SPRITE doesn't have any effect + unless GL_COORD_REPLACE is enabled for an individual + layer. Therefore it seems like it should be ok to just leave it + enabled all the time instead of having to have a set property on + each material to track whether any layers have point sprite + coords enabled */ + if (cogl_features_available (COGL_FEATURE_POINT_SPRITE)) + GE (glEnable (GL_POINT_SPRITE)); + return TRUE; } diff --git a/cogl/cogl-material-private.h b/cogl/cogl-material-private.h index 9873c934e..56990c88d 100644 --- a/cogl/cogl-material-private.h +++ b/cogl/cogl-material-private.h @@ -184,7 +184,9 @@ typedef enum COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT = 1L<<5, COGL_MATERIAL_LAYER_STATE_USER_MATRIX = 1L<<6, - /* COGL_MATERIAL_LAYER_STATE_TEXTURE_INTERN = 1L<<7, */ + COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS = 1L<<7, + + /* COGL_MATERIAL_LAYER_STATE_TEXTURE_INTERN = 1L<<8, */ COGL_MATERIAL_LAYER_STATE_ALL_SPARSE = COGL_MATERIAL_LAYER_STATE_UNIT | @@ -193,12 +195,14 @@ typedef enum COGL_MATERIAL_LAYER_STATE_WRAP_MODES | COGL_MATERIAL_LAYER_STATE_COMBINE | COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT | - COGL_MATERIAL_LAYER_STATE_USER_MATRIX, + COGL_MATERIAL_LAYER_STATE_USER_MATRIX | + COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS, COGL_MATERIAL_LAYER_STATE_NEEDS_BIG_STATE = COGL_MATERIAL_LAYER_STATE_COMBINE | COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT | - COGL_MATERIAL_LAYER_STATE_USER_MATRIX + COGL_MATERIAL_LAYER_STATE_USER_MATRIX | + COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS, } CoglMaterialLayerState; @@ -219,6 +223,8 @@ typedef struct /* The texture matrix dscribes how to transform texture coordinates */ CoglMatrix matrix; + gboolean point_sprite_coords; + } CoglMaterialLayerBigState; struct _CoglMaterialLayer diff --git a/cogl/cogl-material.c b/cogl/cogl-material.c index d2ae7b8e6..bc9807e30 100644 --- a/cogl/cogl-material.c +++ b/cogl/cogl-material.c @@ -72,7 +72,13 @@ #define glUseProgram ctx->drv.pf_glUseProgram #endif -/* This isn't defined in the GLES headers */ +/* These aren't defined in the GLES headers */ +#ifndef GL_POINT_SPRITE +#define GL_POINT_SPRITE 0x8861 +#endif +#ifndef GL_COORD_REPLACE +#define GL_COORD_REPLACE 0x8862 +#endif #ifndef GL_CLAMP_TO_BORDER #define GL_CLAMP_TO_BORDER 0x812d #endif @@ -1549,6 +1555,9 @@ _cogl_material_layer_initialize_state (CoglMaterialLayer *dest, if (differences & COGL_MATERIAL_LAYER_STATE_USER_MATRIX) dest->big_state->matrix = src->big_state->matrix; + + if (differences & COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS) + dest->big_state->point_sprite_coords = src->big_state->point_sprite_coords; } /* NB: This function will allocate a new derived layer if you are @@ -2466,6 +2475,123 @@ _cogl_material_layer_get_wrap_modes (CoglMaterialLayer *layer, *wrap_mode_r = authority->wrap_mode_r; } +gboolean +cogl_material_set_layer_point_sprite_coords_enabled (CoglMaterial *material, + int layer_index, + gboolean enable, + GError **error) +{ + CoglMaterialLayerState change = + COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS; + CoglMaterialLayer *layer; + CoglMaterialLayer *new; + CoglMaterialLayer *authority; + + g_return_val_if_fail (cogl_is_material (material), FALSE); + + /* Don't allow point sprite coordinates to be enabled if the driver + doesn't support it */ + if (enable && !cogl_features_available (COGL_FEATURE_POINT_SPRITE)) + { + if (error) + { + g_set_error (error, COGL_ERROR, COGL_ERROR_MISSING_FEATURE, + "Point sprite texture coordinates are enabled " + "for a layer but the GL driver does not support it."); + } + else + { + static gboolean warning_seen = FALSE; + if (!warning_seen) + g_warning ("Point sprite texture coordinates are enabled " + "for a layer but the GL driver does not support it."); + warning_seen = TRUE; + } + + return FALSE; + } + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * material. If the layer is created then it will be owned by + * material. */ + layer = _cogl_material_get_layer (material, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_material_layer_get_authority (layer, change); + + if (authority->big_state->point_sprite_coords == enable) + return TRUE; + + new = _cogl_material_layer_pre_change_notify (material, layer, change); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && authority->parent != NULL) + { + CoglMaterialLayer *old_authority = + _cogl_material_layer_get_authority (authority->parent, change); + + if (old_authority->big_state->point_sprite_coords == enable) + { + layer->differences &= ~change; + + g_assert (layer->owner == material); + if (layer->differences == 0) + _cogl_material_prune_empty_layer_difference (material, + layer); + return TRUE; + } + } + } + + layer->big_state->point_sprite_coords = enable; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_material_layer_prune_redundant_ancestry (layer); + } + + return TRUE; +} + +gboolean +cogl_material_get_layer_point_sprite_coords_enabled (CoglMaterial *material, + int layer_index) +{ + CoglMaterialLayerState change = + COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS; + CoglMaterialLayer *layer; + CoglMaterialLayer *authority; + + g_return_val_if_fail (cogl_is_material (material), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * material. If the layer is created then it will be owned by + * material. */ + layer = _cogl_material_get_layer (material, layer_index); + /* FIXME: we shouldn't ever construct a layer in a getter function */ + + authority = _cogl_material_layer_get_authority (layer, change); + + return authority->big_state->point_sprite_coords; +} + typedef struct { CoglMaterial *material; @@ -2793,6 +2919,16 @@ _cogl_material_layer_user_matrix_equal (CoglMaterialLayer *authority0, return TRUE; } +static gboolean +_cogl_material_layer_point_sprite_coords_equal (CoglMaterialLayer *authority0, + CoglMaterialLayer *authority1) +{ + CoglMaterialLayerBigState *big_state0 = authority0->big_state; + CoglMaterialLayerBigState *big_state1 = authority1->big_state; + + return big_state0->point_sprite_coords == big_state1->point_sprite_coords; +} + typedef gboolean (*CoglMaterialLayerStateComparitor) (CoglMaterialLayer *authority0, CoglMaterialLayer *authority1); @@ -2859,6 +2995,12 @@ _cogl_material_layer_equal (CoglMaterialLayer *layer0, _cogl_material_layer_user_matrix_equal)) return FALSE; + if (layers_difference & COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS && + !layer_state_equal (COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS, + layer0, layer1, + _cogl_material_layer_point_sprite_coords_equal)) + return FALSE; + return TRUE; } @@ -4357,6 +4499,8 @@ _cogl_material_init_default_layers (void) big_state->texture_combine_alpha_op[0] = GL_SRC_ALPHA; big_state->texture_combine_alpha_op[1] = GL_SRC_ALPHA; + big_state->point_sprite_coords = FALSE; + cogl_matrix_init_identity (&big_state->matrix); ctx->default_layer_0 = _cogl_material_layer_object_new (layer); @@ -5575,9 +5719,23 @@ flush_layers_common_gl_state_cb (CoglMaterialLayer *layer, void *user_data) _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE); } - cogl_object_ref (layer); - if (unit->layer != NULL) - cogl_object_unref (unit->layer); + if (layers_difference & COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS) + { + CoglMaterialState change = COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS; + CoglMaterialLayer *authority = + _cogl_material_layer_get_authority (layer, change); + CoglMaterialLayerBigState *big_state = authority->big_state; + + _cogl_set_active_texture_unit (unit_index); + + GE (glTexEnvi (GL_POINT_SPRITE, GL_COORD_REPLACE, + big_state->point_sprite_coords)); + } + + cogl_handle_ref (layer); + if (unit->layer != COGL_INVALID_HANDLE) + cogl_handle_unref (unit->layer); + unit->layer = layer; unit->layer_changes_since_flush = 0; diff --git a/cogl/cogl-material.h b/cogl/cogl-material.h index 9a59989b3..7c9de1aaa 100644 --- a/cogl/cogl-material.h +++ b/cogl/cogl-material.h @@ -921,6 +921,50 @@ cogl_material_set_layer_filters (CoglMaterial *material, CoglMaterialFilter min_filter, CoglMaterialFilter mag_filter); +/** + * cogl_material_set_layer_point_sprite_coords_enabled: + * @material: a #CoglHandle to a material. + * @layer_index: the layer number to change. + * @enable: whether to enable point sprite coord generation. + * @error: A return location for a GError, or NULL to ignore errors. + * + * When rendering points, if @enable is %TRUE then the texture + * coordinates for this layer will be replaced with coordinates that + * vary from 0.0 to 1.0 across the primitive. The top left of the + * point will have the coordinates 0.0,0.0 and the bottom right will + * have 1.0,1.0. If @enable is %FALSE then the coordinates will be + * fixed for the entire point. + * + * This function will only work if %COGL_FEATURE_POINT_SPRITE is + * available. If the feature is not available then the function will + * return %FALSE and set @error. + * + * Return value: %TRUE if the function succeeds, %FALSE otherwise. + * Since: 1.4 + */ +gboolean +cogl_material_set_layer_point_sprite_coords_enabled (CoglMaterial *material, + int layer_index, + gboolean enable, + GError **error); + +/** + * cogl_material_get_layer_point_sprite_coords_enabled: + * @material: a #CoglHandle to a material. + * @layer_index: the layer number to check. + * + * Gets whether point sprite coordinate generation is enabled for this + * texture layer. + * + * Return value: whether the texture coordinates will be replaced with + * point sprite coordinates. + * + * Since: 1.4 + */ +gboolean +cogl_material_get_layer_point_sprite_coords_enabled (CoglMaterial *material, + int layer_index); + /** * cogl_material_set_layer_wrap_mode_s: * @material: A #CoglMaterial object diff --git a/cogl/cogl-types.h b/cogl/cogl-types.h index de8f962a4..2790640e7 100644 --- a/cogl/cogl-types.h +++ b/cogl/cogl-types.h @@ -248,6 +248,8 @@ typedef enum { /*< prefix=COGL_PIXEL_FORMAT >*/ * @COGL_FEATURE_TEXTURE_NPOT_REPEAT: Repeat modes other than * %COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE are supported by the * hardware. + * @COGL_FEATURE_POINT_SPRITE: Whether + * cogl_material_set_layer_point_sprite_coords_enabled() is supported. * * Flags for the supported features. * @@ -271,7 +273,8 @@ typedef enum COGL_FEATURE_DEPTH_RANGE = (1 << 14), COGL_FEATURE_TEXTURE_NPOT_BASIC = (1 << 15), COGL_FEATURE_TEXTURE_NPOT_MIPMAP = (1 << 16), - COGL_FEATURE_TEXTURE_NPOT_REPEAT = (1 << 17) + COGL_FEATURE_TEXTURE_NPOT_REPEAT = (1 << 17), + COGL_FEATURE_POINT_SPRITE = (1 << 18) } CoglFeatureFlags; /** diff --git a/cogl/driver/gl/cogl-feature-functions.h b/cogl/driver/gl/cogl-feature-functions.h index ce1379594..d075d56eb 100644 --- a/cogl/driver/gl/cogl-feature-functions.h +++ b/cogl/driver/gl/cogl-feature-functions.h @@ -380,3 +380,10 @@ COGL_FEATURE_FUNCTION (void, glBlendEquationSeparate, (GLenum modeRGB, GLenum modeAlpha)) COGL_FEATURE_END () + +COGL_FEATURE_BEGIN (point_sprites, 2, 0, + "ARB\0", + "point_sprite\0", + COGL_FEATURE_POINT_SPRITE, + 0) +COGL_FEATURE_END () diff --git a/cogl/driver/gles/cogl-gles2-wrapper.c b/cogl/driver/gles/cogl-gles2-wrapper.c index b3eb0a958..2acbee2a7 100644 --- a/cogl/driver/gles/cogl-gles2-wrapper.c +++ b/cogl/driver/gles/cogl-gles2-wrapper.c @@ -155,6 +155,9 @@ initialize_texture_units (CoglGles2Wrapper *w) GL_SRC_COLOR) ); GE( _cogl_wrap_glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_COLOR) ); + + GE( _cogl_wrap_glTexEnvi (GL_POINT_SPRITE, GL_COORD_REPLACE, + GL_FALSE) ); } GE( _cogl_wrap_glMatrixMode ((GLenum) prev_mode)); @@ -256,6 +259,10 @@ cogl_gles2_settings_equal (const CoglGles2WrapperSettings *a, int arg, n_args; GLenum func; + if (tex_env_a->point_sprite_coords != + tex_env_b->point_sprite_coords) + return FALSE; + func = tex_env_a->texture_combine_rgb_func; if (func != tex_env_b->texture_combine_rgb_func) @@ -329,7 +336,8 @@ cogl_gles2_get_vertex_shader (const CoglGles2WrapperSettings *settings) g_string_append (shader_source, _cogl_fixed_vertex_shader_main_start); for (i = 0; i < COGL_GLES2_MAX_TEXTURE_UNITS; i++) - if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i)) + if (COGL_GLES2_TEXTURE_UNIT_IS_ENABLED (settings->texture_units, i) && + !settings->tex_env[i].point_sprite_coords) { g_string_append_printf (shader_source, "transformed_tex_coord = " @@ -386,6 +394,26 @@ cogl_gles2_get_vertex_shader (const CoglGles2WrapperSettings *settings) return shader; } +static void +cogl_gles2_add_texture_lookup (int unit, + const char *swizzle, + GString *shader_source) +{ + _COGL_GET_GLES2_WRAPPER (w, NO_RETVAL); + + g_string_append_printf (shader_source, "texture2D (texture_unit[%d], ", unit); + + /* If point sprite coord generation is being used then divert to the + built-in varying var for that instead of the texture + coordinates */ + if (w->settings.tex_env[unit].point_sprite_coords) + g_string_append (shader_source, "gl_PointCoord"); + else + g_string_append_printf (shader_source, "tex_coord[%d]", unit); + + g_string_append_printf (shader_source, ").%s", swizzle); +} + static void cogl_gles2_add_arg (int unit, GLenum src, @@ -413,9 +441,7 @@ cogl_gles2_add_arg (int unit, switch (src) { case GL_TEXTURE: - g_string_append_printf (shader_source, - "texture2D (texture_unit[%d], tex_coord[%d]).%s", - unit, unit, swizzle); + cogl_gles2_add_texture_lookup (unit, swizzle, shader_source); break; case GL_CONSTANT: @@ -437,12 +463,8 @@ cogl_gles2_add_arg (int unit, default: if (src >= GL_TEXTURE0 && src < GL_TEXTURE0 + COGL_GLES2_MAX_TEXTURE_UNITS) - g_string_append_printf (shader_source, - "texture2D (texture_unit[%d], " - "tex_coord[%d]).%s", - src - GL_TEXTURE0, - src - GL_TEXTURE0, - swizzle); + cogl_gles2_add_texture_lookup (src - GL_TEXTURE0, + swizzle, shader_source); break; } @@ -1350,14 +1372,14 @@ _cogl_wrap_glDrawElements (GLenum mode, GLsizei count, GLenum type, void _cogl_wrap_glTexEnvi (GLenum target, GLenum pname, GLint param) { + CoglGles2WrapperTexEnv *tex_env; + + _COGL_GET_GLES2_WRAPPER (w, NO_RETVAL); + + tex_env = w->settings.tex_env + w->active_texture_unit; + if (target == GL_TEXTURE_ENV) { - CoglGles2WrapperTexEnv *tex_env; - - _COGL_GET_GLES2_WRAPPER (w, NO_RETVAL); - - tex_env = w->settings.tex_env + w->active_texture_unit; - switch (pname) { case GL_COMBINE_RGB: @@ -1388,6 +1410,17 @@ _cogl_wrap_glTexEnvi (GLenum target, GLenum pname, GLint param) break; } + w->settings_dirty = TRUE; + } + else if (target == GL_POINT_SPRITE) + { + switch (pname) + { + case GL_COORD_REPLACE: + tex_env->point_sprite_coords = param; + break; + } + w->settings_dirty = TRUE; } } diff --git a/cogl/driver/gles/cogl-gles2-wrapper.h b/cogl/driver/gles/cogl-gles2-wrapper.h index 5086b9a4c..af2e894e6 100644 --- a/cogl/driver/gles/cogl-gles2-wrapper.h +++ b/cogl/driver/gles/cogl-gles2-wrapper.h @@ -117,15 +117,17 @@ struct _CoglGles2WrapperUniforms struct _CoglGles2WrapperTexEnv { - GLenum texture_combine_rgb_func; - GLenum texture_combine_rgb_src[3]; - GLenum texture_combine_rgb_op[3]; + GLenum texture_combine_rgb_func; + GLenum texture_combine_rgb_src[3]; + GLenum texture_combine_rgb_op[3]; - GLenum texture_combine_alpha_func; - GLenum texture_combine_alpha_src[3]; - GLenum texture_combine_alpha_op[3]; + GLenum texture_combine_alpha_func; + GLenum texture_combine_alpha_src[3]; + GLenum texture_combine_alpha_op[3]; - GLfloat texture_combine_constant[4]; + GLfloat texture_combine_constant[4]; + + GLboolean point_sprite_coords; }; /* NB: We get a copy of this for each fragment/vertex @@ -319,6 +321,9 @@ struct _CoglGles2WrapperShader #define GL_MAX_TEXTURE_UNITS 0x84e2 +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 + #endif /* GL_MODELVIEW */ void _cogl_gles2_wrapper_init (CoglGles2Wrapper *wrapper); diff --git a/cogl/driver/gles/cogl.c b/cogl/driver/gles/cogl.c index bc716c6dc..0a85d8504 100644 --- a/cogl/driver/gles/cogl.c +++ b/cogl/driver/gles/cogl.c @@ -107,6 +107,9 @@ _cogl_features_init (void) flags |= COGL_FEATURE_VBOS; + /* Both GLES 1.1 and GLES 2.0 support point sprites in core */ + flags |= COGL_FEATURE_POINT_SPRITE; + /* Cache features */ ctx->feature_flags = flags; ctx->features_cached = TRUE; diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 099830e9b..18282bec3 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -491,6 +491,8 @@ cogl_material_remove_layer cogl_material_set_layer_combine cogl_material_set_layer_combine_constant cogl_material_set_layer_matrix +cogl_material_set_layer_point_sprite_coords_enabled +cogl_material_get_layer_point_sprite_coords_enabled cogl_material_get_layers cogl_material_get_n_layers CoglMaterialFilter