e007bc5358
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
2242 lines
72 KiB
C
2242 lines
72 KiB
C
/*
|
|
* Cogl
|
|
*
|
|
* An object oriented GL/GLES Abstraction/Utility Layer
|
|
*
|
|
* Copyright (C) 2008,2009 Intel Corporation.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
*
|
|
* Authors:
|
|
* Robert Bragg <robert@linux.intel.com>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "cogl.h"
|
|
#include "cogl-internal.h"
|
|
#include "cogl-context.h"
|
|
#include "cogl-handle.h"
|
|
|
|
#include "cogl-material-private.h"
|
|
#include "cogl-texture-private.h"
|
|
#include "cogl-blend-string.h"
|
|
#include "cogl-journal-private.h"
|
|
|
|
#include <glib.h>
|
|
#include <string.h>
|
|
|
|
/*
|
|
* GL/GLES compatability defines for material thingies:
|
|
*/
|
|
|
|
#ifdef HAVE_COGL_GLES2
|
|
#include "../gles/cogl-gles2-wrapper.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_COGL_GL
|
|
#define glActiveTexture ctx->drv.pf_glActiveTexture
|
|
#define glClientActiveTexture ctx->drv.pf_glClientActiveTexture
|
|
#define glBlendFuncSeparate ctx->drv.pf_glBlendFuncSeparate
|
|
#define glBlendEquation ctx->drv.pf_glBlendEquation
|
|
#define glBlendColor ctx->drv.pf_glBlendColor
|
|
#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);
|
|
static void _cogl_material_layer_free (CoglMaterialLayer *layer);
|
|
|
|
COGL_HANDLE_DEFINE (Material, material);
|
|
COGL_HANDLE_DEFINE (MaterialLayer, material_layer);
|
|
|
|
/* #define DISABLE_MATERIAL_CACHE 1 */
|
|
|
|
GQuark
|
|
_cogl_material_error_quark (void)
|
|
{
|
|
return g_quark_from_static_string ("cogl-material-error-quark");
|
|
}
|
|
|
|
void
|
|
_cogl_material_init_default_material (void)
|
|
{
|
|
/* Create new - blank - material */
|
|
CoglMaterial *material = g_slice_new0 (CoglMaterial);
|
|
GLubyte *unlit = material->unlit;
|
|
GLfloat *ambient = material->ambient;
|
|
GLfloat *diffuse = material->diffuse;
|
|
GLfloat *specular = material->specular;
|
|
GLfloat *emission = material->emission;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
/* Use the same defaults as the GL spec... */
|
|
unlit[0] = 0xff; unlit[1] = 0xff; unlit[2] = 0xff; unlit[3] = 0xff;
|
|
material->flags |= COGL_MATERIAL_FLAG_DEFAULT_COLOR;
|
|
|
|
/* Use the same defaults as the GL spec... */
|
|
ambient[0] = 0.2; ambient[1] = 0.2; ambient[2] = 0.2; ambient[3] = 1.0;
|
|
diffuse[0] = 0.8; diffuse[1] = 0.8; diffuse[2] = 0.8; diffuse[3] = 1.0;
|
|
specular[0] = 0; specular[1] = 0; specular[2] = 0; specular[3] = 1.0;
|
|
emission[0] = 0; emission[1] = 0; emission[2] = 0; emission[3] = 1.0;
|
|
material->flags |= COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
|
|
|
|
/* Use the same defaults as the GL spec... */
|
|
material->alpha_func = COGL_MATERIAL_ALPHA_FUNC_ALWAYS;
|
|
material->alpha_func_reference = 0.0;
|
|
material->flags |= COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC;
|
|
|
|
/* Not the same as the GL default, but seems saner... */
|
|
#ifndef HAVE_COGL_GLES
|
|
material->blend_equation_rgb = GL_FUNC_ADD;
|
|
material->blend_equation_alpha = GL_FUNC_ADD;
|
|
material->blend_src_factor_alpha = GL_ONE;
|
|
material->blend_dst_factor_alpha = GL_ONE_MINUS_SRC_ALPHA;
|
|
material->blend_constant[0] = 0;
|
|
material->blend_constant[1] = 0;
|
|
material->blend_constant[2] = 0;
|
|
material->blend_constant[3] = 0;
|
|
#endif
|
|
material->blend_src_factor_rgb = GL_ONE;
|
|
material->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA;
|
|
material->flags |= COGL_MATERIAL_FLAG_DEFAULT_BLEND;
|
|
|
|
material->layers = NULL;
|
|
material->n_layers = 0;
|
|
|
|
ctx->default_material = _cogl_material_handle_new (material);
|
|
}
|
|
|
|
CoglHandle
|
|
cogl_material_copy (CoglHandle handle)
|
|
{
|
|
CoglMaterial *material = g_slice_new (CoglMaterial);
|
|
GList *l;
|
|
|
|
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
|
|
|
|
memcpy (material, handle, sizeof (CoglMaterial));
|
|
|
|
material->layers = g_list_copy (material->layers);
|
|
for (l = material->layers; l; l = l->next)
|
|
l->data = _cogl_material_layer_copy (l->data);
|
|
|
|
return _cogl_material_handle_new (material);
|
|
}
|
|
|
|
CoglHandle
|
|
cogl_material_new (void)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
|
|
|
|
return cogl_material_copy (ctx->default_material);
|
|
}
|
|
|
|
static void
|
|
_cogl_material_free (CoglMaterial *material)
|
|
{
|
|
/* Frees material resources but its handle is not
|
|
released! Do that separately before this! */
|
|
|
|
g_list_foreach (material->layers,
|
|
(GFunc)cogl_handle_unref, NULL);
|
|
g_list_free (material->layers);
|
|
g_slice_free (CoglMaterial, material);
|
|
}
|
|
|
|
static gboolean
|
|
_cogl_material_needs_blending_enabled (CoglMaterial *material,
|
|
GLubyte *override_color)
|
|
{
|
|
GList *tmp;
|
|
|
|
/* XXX: If we expose manual control over ENABLE_BLEND, we'll add
|
|
* a flag to know when it's user configured, so we don't trash it */
|
|
|
|
/* XXX: Uncomment this to disable all blending */
|
|
#if 0
|
|
return;
|
|
#endif
|
|
|
|
if ((override_color && override_color[3] != 0xff) ||
|
|
material->unlit[3] != 0xff ||
|
|
material->ambient[3] != 1.0f ||
|
|
material->diffuse[3] != 1.0f ||
|
|
material->specular[3] != 1.0f ||
|
|
material->emission[3] != 1.0f)
|
|
return TRUE;
|
|
|
|
for (tmp = material->layers; tmp != NULL; tmp = tmp->next)
|
|
{
|
|
CoglMaterialLayer *layer = tmp->data;
|
|
|
|
/* NB: A layer may have a combine mode set on it but not yet have an
|
|
* associated texture. */
|
|
if (!layer->texture)
|
|
continue;
|
|
|
|
if (cogl_texture_get_format (layer->texture) & COGL_A_BIT)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
handle_automatic_blend_enable (CoglMaterial *material)
|
|
{
|
|
material->flags &= ~COGL_MATERIAL_FLAG_ENABLE_BLEND;
|
|
|
|
if (_cogl_material_needs_blending_enabled (material, NULL))
|
|
material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND;
|
|
}
|
|
|
|
/* If primitives have been logged in the journal referencing the current
|
|
* state of this material we need to flush the journal before we can
|
|
* modify it... */
|
|
static void
|
|
_cogl_material_pre_change_notify (CoglMaterial *material,
|
|
gboolean only_color_change,
|
|
GLubyte *new_color)
|
|
{
|
|
/* XXX: We don't usually need to flush the journal just due to color changes
|
|
* since material colors are logged in the journals vertex buffer. The
|
|
* exception is when the change in color enables or disables the need for
|
|
* blending. */
|
|
if (only_color_change)
|
|
{
|
|
gboolean will_need_blending =
|
|
_cogl_material_needs_blending_enabled (material, new_color);
|
|
if (will_need_blending ==
|
|
(material->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) ? TRUE : FALSE)
|
|
return;
|
|
}
|
|
|
|
if (material->journal_ref_count)
|
|
_cogl_journal_flush ();
|
|
}
|
|
|
|
void
|
|
cogl_material_get_color (CoglHandle handle,
|
|
CoglColor *color)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
cogl_color_set_from_4ub (color,
|
|
material->unlit[0],
|
|
material->unlit[1],
|
|
material->unlit[2],
|
|
material->unlit[3]);
|
|
}
|
|
|
|
/* This is used heavily by the cogl journal when logging quads */
|
|
void
|
|
_cogl_material_get_colorubv (CoglHandle handle,
|
|
guint8 *color)
|
|
{
|
|
CoglMaterial *material = _cogl_material_pointer_from_handle (handle);
|
|
memcpy (color, material->unlit, 4);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_color (CoglHandle handle,
|
|
const CoglColor *unlit_color)
|
|
{
|
|
CoglMaterial *material;
|
|
GLubyte unlit[4];
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
unlit[0] = cogl_color_get_red_byte (unlit_color);
|
|
unlit[1] = cogl_color_get_green_byte (unlit_color);
|
|
unlit[2] = cogl_color_get_blue_byte (unlit_color);
|
|
unlit[3] = cogl_color_get_alpha_byte (unlit_color);
|
|
if (memcmp (unlit, material->unlit, sizeof (unlit)) == 0)
|
|
return;
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, TRUE, unlit);
|
|
|
|
memcpy (material->unlit, unlit, sizeof (unlit));
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_COLOR;
|
|
if (unlit[0] == 0xff &&
|
|
unlit[1] == 0xff &&
|
|
unlit[2] == 0xff &&
|
|
unlit[3] == 0xff)
|
|
material->flags |= COGL_MATERIAL_FLAG_DEFAULT_COLOR;
|
|
|
|
handle_automatic_blend_enable (material);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_color4ub (CoglHandle handle,
|
|
guint8 red,
|
|
guint8 green,
|
|
guint8 blue,
|
|
guint8 alpha)
|
|
{
|
|
CoglColor color;
|
|
cogl_color_set_from_4ub (&color, red, green, blue, alpha);
|
|
cogl_material_set_color (handle, &color);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_color4f (CoglHandle handle,
|
|
float red,
|
|
float green,
|
|
float blue,
|
|
float alpha)
|
|
{
|
|
CoglColor color;
|
|
cogl_color_set_from_4f (&color, red, green, blue, alpha);
|
|
cogl_material_set_color (handle, &color);
|
|
}
|
|
|
|
void
|
|
cogl_material_get_ambient (CoglHandle handle,
|
|
CoglColor *ambient)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
cogl_color_set_from_4f (ambient,
|
|
material->ambient[0],
|
|
material->ambient[1],
|
|
material->ambient[2],
|
|
material->ambient[3]);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_ambient (CoglHandle handle,
|
|
const CoglColor *ambient_color)
|
|
{
|
|
CoglMaterial *material;
|
|
GLfloat *ambient;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
ambient = material->ambient;
|
|
ambient[0] = cogl_color_get_red_float (ambient_color);
|
|
ambient[1] = cogl_color_get_green_float (ambient_color);
|
|
ambient[2] = cogl_color_get_blue_float (ambient_color);
|
|
ambient[3] = cogl_color_get_alpha_float (ambient_color);
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
|
|
|
|
handle_automatic_blend_enable (material);
|
|
}
|
|
|
|
void
|
|
cogl_material_get_diffuse (CoglHandle handle,
|
|
CoglColor *diffuse)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
cogl_color_set_from_4f (diffuse,
|
|
material->diffuse[0],
|
|
material->diffuse[1],
|
|
material->diffuse[2],
|
|
material->diffuse[3]);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_diffuse (CoglHandle handle,
|
|
const CoglColor *diffuse_color)
|
|
{
|
|
CoglMaterial *material;
|
|
GLfloat *diffuse;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
diffuse = material->diffuse;
|
|
diffuse[0] = cogl_color_get_red_float (diffuse_color);
|
|
diffuse[1] = cogl_color_get_green_float (diffuse_color);
|
|
diffuse[2] = cogl_color_get_blue_float (diffuse_color);
|
|
diffuse[3] = cogl_color_get_alpha_float (diffuse_color);
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
|
|
|
|
handle_automatic_blend_enable (material);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_ambient_and_diffuse (CoglHandle handle,
|
|
const CoglColor *color)
|
|
{
|
|
cogl_material_set_ambient (handle, color);
|
|
cogl_material_set_diffuse (handle, color);
|
|
}
|
|
|
|
void
|
|
cogl_material_get_specular (CoglHandle handle,
|
|
CoglColor *specular)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
cogl_color_set_from_4f (specular,
|
|
material->specular[0],
|
|
material->specular[1],
|
|
material->specular[2],
|
|
material->specular[3]);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_specular (CoglHandle handle,
|
|
const CoglColor *specular_color)
|
|
{
|
|
CoglMaterial *material;
|
|
GLfloat *specular;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
specular = material->specular;
|
|
specular[0] = cogl_color_get_red_float (specular_color);
|
|
specular[1] = cogl_color_get_green_float (specular_color);
|
|
specular[2] = cogl_color_get_blue_float (specular_color);
|
|
specular[3] = cogl_color_get_alpha_float (specular_color);
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
|
|
|
|
handle_automatic_blend_enable (material);
|
|
}
|
|
|
|
float
|
|
cogl_material_get_shininess (CoglHandle handle)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_val_if_fail (cogl_is_material (handle), 0);
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
return material->shininess;
|
|
}
|
|
|
|
void
|
|
cogl_material_set_shininess (CoglHandle handle,
|
|
float shininess)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
if (shininess < 0.0 || shininess > 1.0)
|
|
g_warning ("Out of range shininess %f supplied for material\n",
|
|
shininess);
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
material->shininess = (GLfloat)shininess * 128.0;
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
|
|
}
|
|
|
|
void
|
|
cogl_material_get_emission (CoglHandle handle,
|
|
CoglColor *emission)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
cogl_color_set_from_4f (emission,
|
|
material->emission[0],
|
|
material->emission[1],
|
|
material->emission[2],
|
|
material->emission[3]);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_emission (CoglHandle handle,
|
|
const CoglColor *emission_color)
|
|
{
|
|
CoglMaterial *material;
|
|
GLfloat *emission;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
emission = material->emission;
|
|
emission[0] = cogl_color_get_red_float (emission_color);
|
|
emission[1] = cogl_color_get_green_float (emission_color);
|
|
emission[2] = cogl_color_get_blue_float (emission_color);
|
|
emission[3] = cogl_color_get_alpha_float (emission_color);
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
|
|
|
|
handle_automatic_blend_enable (material);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_alpha_test_function (CoglHandle handle,
|
|
CoglMaterialAlphaFunc alpha_func,
|
|
float alpha_reference)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
material->alpha_func = alpha_func;
|
|
material->alpha_func_reference = (GLfloat)alpha_reference;
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC;
|
|
}
|
|
|
|
GLenum
|
|
arg_to_gl_blend_factor (CoglBlendStringArgument *arg)
|
|
{
|
|
if (arg->source.is_zero)
|
|
return GL_ZERO;
|
|
if (arg->factor.is_one)
|
|
return GL_ONE;
|
|
else if (arg->factor.is_src_alpha_saturate)
|
|
return GL_SRC_ALPHA_SATURATE;
|
|
else if (arg->factor.source.info->type ==
|
|
COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR)
|
|
{
|
|
if (arg->factor.source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB)
|
|
{
|
|
if (arg->factor.source.one_minus)
|
|
return GL_ONE_MINUS_SRC_COLOR;
|
|
else
|
|
return GL_SRC_COLOR;
|
|
}
|
|
else
|
|
{
|
|
if (arg->factor.source.one_minus)
|
|
return GL_ONE_MINUS_SRC_ALPHA;
|
|
else
|
|
return GL_SRC_ALPHA;
|
|
}
|
|
}
|
|
else if (arg->factor.source.info->type ==
|
|
COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR)
|
|
{
|
|
if (arg->factor.source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB)
|
|
{
|
|
if (arg->factor.source.one_minus)
|
|
return GL_ONE_MINUS_DST_COLOR;
|
|
else
|
|
return GL_DST_COLOR;
|
|
}
|
|
else
|
|
{
|
|
if (arg->factor.source.one_minus)
|
|
return GL_ONE_MINUS_DST_ALPHA;
|
|
else
|
|
return GL_DST_ALPHA;
|
|
}
|
|
}
|
|
#ifndef HAVE_COGL_GLES
|
|
else if (arg->factor.source.info->type ==
|
|
COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT)
|
|
{
|
|
if (arg->factor.source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB)
|
|
{
|
|
if (arg->factor.source.one_minus)
|
|
return GL_ONE_MINUS_CONSTANT_COLOR;
|
|
else
|
|
return GL_CONSTANT_COLOR;
|
|
}
|
|
else
|
|
{
|
|
if (arg->factor.source.one_minus)
|
|
return GL_ONE_MINUS_CONSTANT_ALPHA;
|
|
else
|
|
return GL_CONSTANT_ALPHA;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
g_warning ("Unable to determine valid blend factor from blend string\n");
|
|
return GL_ONE;
|
|
}
|
|
|
|
void
|
|
setup_blend_state (CoglBlendStringStatement *statement,
|
|
GLenum *blend_equation,
|
|
GLint *blend_src_factor,
|
|
GLint *blend_dst_factor)
|
|
{
|
|
#ifndef HAVE_COGL_GLES
|
|
switch (statement->function->type)
|
|
{
|
|
case COGL_BLEND_STRING_FUNCTION_ADD:
|
|
*blend_equation = GL_FUNC_ADD;
|
|
break;
|
|
/* TODO - add more */
|
|
default:
|
|
g_warning ("Unsupported blend function given");
|
|
*blend_equation = GL_FUNC_ADD;
|
|
}
|
|
#endif
|
|
|
|
*blend_src_factor = arg_to_gl_blend_factor (&statement->args[0]);
|
|
*blend_dst_factor = arg_to_gl_blend_factor (&statement->args[1]);
|
|
}
|
|
|
|
gboolean
|
|
cogl_material_set_blend (CoglHandle handle,
|
|
const char *blend_description,
|
|
GError **error)
|
|
{
|
|
CoglMaterial *material;
|
|
CoglBlendStringStatement statements[2];
|
|
CoglBlendStringStatement split[2];
|
|
CoglBlendStringStatement *rgb;
|
|
CoglBlendStringStatement *a;
|
|
GError *internal_error = NULL;
|
|
int count;
|
|
|
|
g_return_val_if_fail (cogl_is_material (handle), FALSE);
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
count =
|
|
_cogl_blend_string_compile (blend_description,
|
|
COGL_BLEND_STRING_CONTEXT_BLENDING,
|
|
statements,
|
|
&internal_error);
|
|
if (!count)
|
|
{
|
|
if (error)
|
|
g_propagate_error (error, internal_error);
|
|
else
|
|
{
|
|
g_warning ("Cannot compile blend description: %s\n",
|
|
internal_error->message);
|
|
g_error_free (internal_error);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA)
|
|
{
|
|
_cogl_blend_string_split_rgba_statement (statements,
|
|
&split[0], &split[1]);
|
|
rgb = &split[0];
|
|
a = &split[1];
|
|
}
|
|
else
|
|
{
|
|
rgb = &statements[0];
|
|
a = &statements[1];
|
|
}
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
#ifndef HAVE_COGL_GLES
|
|
setup_blend_state (rgb,
|
|
&material->blend_equation_rgb,
|
|
&material->blend_src_factor_rgb,
|
|
&material->blend_dst_factor_rgb);
|
|
setup_blend_state (a,
|
|
&material->blend_equation_alpha,
|
|
&material->blend_src_factor_alpha,
|
|
&material->blend_dst_factor_alpha);
|
|
#else
|
|
setup_blend_state (rgb,
|
|
NULL,
|
|
&material->blend_src_factor_rgb,
|
|
&material->blend_dst_factor_rgb);
|
|
#endif
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
cogl_material_set_blend_constant (CoglHandle handle,
|
|
CoglColor *constant_color)
|
|
{
|
|
#ifndef HAVE_COGL_GLES
|
|
CoglMaterial *material;
|
|
GLfloat *constant;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
constant = material->blend_constant;
|
|
constant[0] = cogl_color_get_red_float (constant_color);
|
|
constant[1] = cogl_color_get_green_float (constant_color);
|
|
constant[2] = cogl_color_get_blue_float (constant_color);
|
|
constant[3] = cogl_color_get_alpha_float (constant_color);
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND;
|
|
#endif
|
|
}
|
|
|
|
/* Asserts that a layer corresponding to the given index exists. If no
|
|
* match is found, then a new empty layer is added.
|
|
*/
|
|
static CoglMaterialLayer *
|
|
_cogl_material_get_layer (CoglMaterial *material,
|
|
int index_,
|
|
gboolean create_if_not_found)
|
|
{
|
|
CoglMaterialLayer *layer;
|
|
GList *tmp;
|
|
CoglHandle layer_handle;
|
|
|
|
for (tmp = material->layers; tmp != NULL; tmp = tmp->next)
|
|
{
|
|
layer =
|
|
_cogl_material_layer_pointer_from_handle ((CoglHandle)tmp->data);
|
|
if (layer->index == index_)
|
|
return layer;
|
|
|
|
/* The layers are always sorted, so at this point we know this layer
|
|
* doesn't exist */
|
|
if (layer->index > index_)
|
|
break;
|
|
}
|
|
/* NB: if we now insert a new layer before tmp, that will maintain order.
|
|
*/
|
|
|
|
if (!create_if_not_found)
|
|
return NULL;
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
layer = g_slice_new0 (CoglMaterialLayer);
|
|
|
|
layer_handle = _cogl_material_layer_handle_new (layer);
|
|
layer->index = index_;
|
|
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:
|
|
* MODULATE(PREVIOUS[RGBA],TEXTURE[RGBA]) */
|
|
layer->texture_combine_rgb_func = GL_MODULATE;
|
|
layer->texture_combine_rgb_src[0] = GL_PREVIOUS;
|
|
layer->texture_combine_rgb_src[1] = GL_TEXTURE;
|
|
layer->texture_combine_rgb_op[0] = GL_SRC_COLOR;
|
|
layer->texture_combine_rgb_op[1] = GL_SRC_COLOR;
|
|
layer->texture_combine_alpha_func = GL_MODULATE;
|
|
layer->texture_combine_alpha_src[0] = GL_PREVIOUS;
|
|
layer->texture_combine_alpha_src[1] = GL_TEXTURE;
|
|
layer->texture_combine_alpha_op[0] = GL_SRC_ALPHA;
|
|
layer->texture_combine_alpha_op[1] = GL_SRC_ALPHA;
|
|
|
|
cogl_matrix_init_identity (&layer->matrix);
|
|
|
|
/* Note: see comment after for() loop above */
|
|
material->layers =
|
|
g_list_insert_before (material->layers, tmp, layer_handle);
|
|
|
|
return layer;
|
|
}
|
|
|
|
void
|
|
cogl_material_set_layer (CoglHandle material_handle,
|
|
int layer_index,
|
|
CoglHandle texture_handle)
|
|
{
|
|
CoglMaterial *material;
|
|
CoglMaterialLayer *layer;
|
|
|
|
g_return_if_fail (cogl_is_material (material_handle));
|
|
g_return_if_fail (texture_handle == COGL_INVALID_HANDLE
|
|
|| cogl_is_texture (texture_handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (material_handle);
|
|
|
|
layer = _cogl_material_get_layer (material, layer_index, TRUE);
|
|
|
|
if (texture_handle == layer->texture)
|
|
return;
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
material->n_layers = g_list_length (material->layers);
|
|
if (material->n_layers >= _cogl_get_max_texture_image_units ())
|
|
{
|
|
if (!(material->flags & COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING))
|
|
{
|
|
g_warning ("Your hardware does not have enough texture samplers"
|
|
"to handle this many texture layers");
|
|
material->flags |= COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING;
|
|
}
|
|
/* Note: We always make a best effort attempt to display as many
|
|
* layers as possible, so this isn't an _error_ */
|
|
/* Note: in the future we may support enabling/disabling layers
|
|
* too, so it may become valid to add more than
|
|
* MAX_COMBINED_TEXTURE_IMAGE_UNITS layers. */
|
|
}
|
|
|
|
if (texture_handle)
|
|
cogl_handle_ref (texture_handle);
|
|
|
|
if (layer->texture)
|
|
cogl_handle_unref (layer->texture);
|
|
|
|
layer->texture = texture_handle;
|
|
|
|
handle_automatic_blend_enable (material);
|
|
layer->flags |= COGL_MATERIAL_LAYER_FLAG_DIRTY;
|
|
}
|
|
|
|
static void
|
|
setup_texture_combine_state (CoglBlendStringStatement *statement,
|
|
GLint *texture_combine_func,
|
|
GLint *texture_combine_src,
|
|
GLint *texture_combine_op)
|
|
{
|
|
int i;
|
|
|
|
switch (statement->function->type)
|
|
{
|
|
case COGL_BLEND_STRING_FUNCTION_AUTO_COMPOSITE:
|
|
*texture_combine_func = GL_MODULATE; /* FIXME */
|
|
break;
|
|
case COGL_BLEND_STRING_FUNCTION_REPLACE:
|
|
*texture_combine_func = GL_REPLACE;
|
|
break;
|
|
case COGL_BLEND_STRING_FUNCTION_MODULATE:
|
|
*texture_combine_func = GL_MODULATE;
|
|
break;
|
|
case COGL_BLEND_STRING_FUNCTION_ADD:
|
|
*texture_combine_func = GL_ADD;
|
|
break;
|
|
case COGL_BLEND_STRING_FUNCTION_ADD_SIGNED:
|
|
*texture_combine_func = GL_ADD_SIGNED;
|
|
break;
|
|
case COGL_BLEND_STRING_FUNCTION_INTERPOLATE:
|
|
*texture_combine_func = GL_INTERPOLATE;
|
|
break;
|
|
case COGL_BLEND_STRING_FUNCTION_SUBTRACT:
|
|
*texture_combine_func = GL_SUBTRACT;
|
|
break;
|
|
case COGL_BLEND_STRING_FUNCTION_DOT3_RGB:
|
|
*texture_combine_func = GL_DOT3_RGB;
|
|
break;
|
|
case COGL_BLEND_STRING_FUNCTION_DOT3_RGBA:
|
|
*texture_combine_func = GL_DOT3_RGBA;
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < statement->function->argc; i++)
|
|
{
|
|
CoglBlendStringArgument *arg = &statement->args[i];
|
|
|
|
switch (arg->source.info->type)
|
|
{
|
|
case COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT:
|
|
texture_combine_src[i] = GL_CONSTANT;
|
|
break;
|
|
case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE:
|
|
texture_combine_src[i] = GL_TEXTURE;
|
|
break;
|
|
case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N:
|
|
texture_combine_src[i] =
|
|
GL_TEXTURE0 + arg->source.texture;
|
|
break;
|
|
case COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY:
|
|
texture_combine_src[i] = GL_PRIMARY_COLOR;
|
|
break;
|
|
case COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS:
|
|
texture_combine_src[i] = GL_PREVIOUS;
|
|
break;
|
|
default:
|
|
g_warning ("Unexpected texture combine source");
|
|
texture_combine_src[i] = GL_TEXTURE;
|
|
}
|
|
|
|
if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB)
|
|
{
|
|
if (statement->args[i].source.one_minus)
|
|
texture_combine_op[i] = GL_ONE_MINUS_SRC_COLOR;
|
|
else
|
|
texture_combine_op[i] = GL_SRC_COLOR;
|
|
}
|
|
else
|
|
{
|
|
if (statement->args[i].source.one_minus)
|
|
texture_combine_op[i] = GL_ONE_MINUS_SRC_ALPHA;
|
|
else
|
|
texture_combine_op[i] = GL_SRC_ALPHA;
|
|
}
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
cogl_material_set_layer_combine (CoglHandle handle,
|
|
int layer_index,
|
|
const char *combine_description,
|
|
GError **error)
|
|
{
|
|
CoglMaterial *material;
|
|
CoglMaterialLayer *layer;
|
|
CoglBlendStringStatement statements[2];
|
|
CoglBlendStringStatement split[2];
|
|
CoglBlendStringStatement *rgb;
|
|
CoglBlendStringStatement *a;
|
|
GError *internal_error = NULL;
|
|
int count;
|
|
|
|
g_return_val_if_fail (cogl_is_material (handle), FALSE);
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
layer = _cogl_material_get_layer (material, layer_index, TRUE);
|
|
|
|
count =
|
|
_cogl_blend_string_compile (combine_description,
|
|
COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE,
|
|
statements,
|
|
&internal_error);
|
|
if (!count)
|
|
{
|
|
if (error)
|
|
g_propagate_error (error, internal_error);
|
|
else
|
|
{
|
|
g_warning ("Cannot compile combine description: %s\n",
|
|
internal_error->message);
|
|
g_error_free (internal_error);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA)
|
|
{
|
|
_cogl_blend_string_split_rgba_statement (statements,
|
|
&split[0], &split[1]);
|
|
rgb = &split[0];
|
|
a = &split[1];
|
|
}
|
|
else
|
|
{
|
|
rgb = &statements[0];
|
|
a = &statements[1];
|
|
}
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
setup_texture_combine_state (rgb,
|
|
&layer->texture_combine_rgb_func,
|
|
layer->texture_combine_rgb_src,
|
|
layer->texture_combine_rgb_op);
|
|
|
|
setup_texture_combine_state (a,
|
|
&layer->texture_combine_alpha_func,
|
|
layer->texture_combine_alpha_src,
|
|
layer->texture_combine_alpha_op);
|
|
|
|
|
|
layer->flags |= COGL_MATERIAL_LAYER_FLAG_DIRTY;
|
|
layer->flags &= ~COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE;
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
cogl_material_set_layer_combine_constant (CoglHandle handle,
|
|
int layer_index,
|
|
CoglColor *constant_color)
|
|
{
|
|
CoglMaterial *material;
|
|
CoglMaterialLayer *layer;
|
|
GLfloat *constant;
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
layer = _cogl_material_get_layer (material, layer_index, TRUE);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
constant = layer->texture_combine_constant;
|
|
constant[0] = cogl_color_get_red_float (constant_color);
|
|
constant[1] = cogl_color_get_green_float (constant_color);
|
|
constant[2] = cogl_color_get_blue_float (constant_color);
|
|
constant[3] = cogl_color_get_alpha_float (constant_color);
|
|
|
|
layer->flags |= COGL_MATERIAL_LAYER_FLAG_DIRTY;
|
|
layer->flags &= ~COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE;
|
|
}
|
|
|
|
void
|
|
cogl_material_set_layer_matrix (CoglHandle material_handle,
|
|
int layer_index,
|
|
CoglMatrix *matrix)
|
|
{
|
|
CoglMaterial *material;
|
|
CoglMaterialLayer *layer;
|
|
|
|
g_return_if_fail (cogl_is_material (material_handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (material_handle);
|
|
layer = _cogl_material_get_layer (material, layer_index, TRUE);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
layer->matrix = *matrix;
|
|
|
|
layer->flags |= COGL_MATERIAL_LAYER_FLAG_DIRTY;
|
|
layer->flags |= COGL_MATERIAL_LAYER_FLAG_HAS_USER_MATRIX;
|
|
layer->flags &= ~COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE;
|
|
}
|
|
|
|
static void
|
|
_cogl_material_layer_free (CoglMaterialLayer *layer)
|
|
{
|
|
if (layer->texture != COGL_INVALID_HANDLE)
|
|
cogl_handle_unref (layer->texture);
|
|
g_slice_free (CoglMaterialLayer, layer);
|
|
}
|
|
|
|
void
|
|
cogl_material_remove_layer (CoglHandle material_handle,
|
|
int layer_index)
|
|
{
|
|
CoglMaterial *material;
|
|
CoglMaterialLayer *layer;
|
|
GList *tmp;
|
|
gboolean notified_change = FALSE;
|
|
|
|
g_return_if_fail (cogl_is_material (material_handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (material_handle);
|
|
|
|
for (tmp = material->layers; tmp != NULL; tmp = tmp->next)
|
|
{
|
|
layer = tmp->data;
|
|
if (layer->index == layer_index)
|
|
{
|
|
CoglHandle handle = (CoglHandle) layer;
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
if (!notified_change)
|
|
{
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
notified_change = TRUE;
|
|
}
|
|
|
|
cogl_handle_unref (handle);
|
|
material->layers = g_list_remove (material->layers, layer);
|
|
material->n_layers--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
handle_automatic_blend_enable (material);
|
|
}
|
|
|
|
/* XXX: This API is hopfully just a stop-gap solution. Ideally _cogl_enable
|
|
* will be replaced. */
|
|
unsigned long
|
|
_cogl_material_get_cogl_enable_flags (CoglHandle material_handle)
|
|
{
|
|
CoglMaterial *material;
|
|
unsigned long enable_flags = 0;
|
|
|
|
_COGL_GET_CONTEXT (ctx, 0);
|
|
|
|
g_return_val_if_fail (cogl_is_material (material_handle), 0);
|
|
|
|
material = _cogl_material_pointer_from_handle (material_handle);
|
|
|
|
/* Enable blending if the geometry has an associated alpha color,
|
|
* or the material wants blending enabled. */
|
|
if (material->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND)
|
|
enable_flags |= COGL_ENABLE_BLEND;
|
|
|
|
return enable_flags;
|
|
}
|
|
|
|
/* It's a bit out of the ordinary to return a const GList *, but it's
|
|
* probably sensible to try and avoid list manipulation for every
|
|
* primitive emitted in a scene, every frame.
|
|
*
|
|
* Alternatively; we could either add a _foreach function, or maybe
|
|
* a function that gets a passed a buffer (that may be stack allocated)
|
|
* by the caller.
|
|
*/
|
|
const GList *
|
|
cogl_material_get_layers (CoglHandle material_handle)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_val_if_fail (cogl_is_material (material_handle), NULL);
|
|
|
|
material = _cogl_material_pointer_from_handle (material_handle);
|
|
|
|
return material->layers;
|
|
}
|
|
|
|
int
|
|
cogl_material_get_n_layers (CoglHandle material_handle)
|
|
{
|
|
CoglMaterial *material;
|
|
|
|
g_return_val_if_fail (cogl_is_material (material_handle), 0);
|
|
|
|
material = _cogl_material_pointer_from_handle (material_handle);
|
|
|
|
return material->n_layers;
|
|
}
|
|
|
|
CoglMaterialLayerType
|
|
cogl_material_layer_get_type (CoglHandle layer_handle)
|
|
{
|
|
return COGL_MATERIAL_LAYER_TYPE_TEXTURE;
|
|
}
|
|
|
|
CoglHandle
|
|
cogl_material_layer_get_texture (CoglHandle layer_handle)
|
|
{
|
|
CoglMaterialLayer *layer;
|
|
|
|
g_return_val_if_fail (cogl_is_material_layer (layer_handle),
|
|
COGL_INVALID_HANDLE);
|
|
|
|
layer = _cogl_material_layer_pointer_from_handle (layer_handle);
|
|
return layer->texture;
|
|
}
|
|
|
|
unsigned long
|
|
_cogl_material_layer_get_flags (CoglHandle layer_handle)
|
|
{
|
|
CoglMaterialLayer *layer;
|
|
|
|
g_return_val_if_fail (cogl_is_material_layer (layer_handle), 0);
|
|
|
|
layer = _cogl_material_layer_pointer_from_handle (layer_handle);
|
|
|
|
return layer->flags & COGL_MATERIAL_LAYER_FLAG_HAS_USER_MATRIX;
|
|
}
|
|
|
|
static CoglHandle
|
|
_cogl_material_layer_copy (CoglHandle layer_handle)
|
|
{
|
|
CoglMaterialLayer *layer =
|
|
_cogl_material_layer_pointer_from_handle (layer_handle);
|
|
CoglMaterialLayer *layer_copy = g_slice_new (CoglMaterialLayer);
|
|
|
|
memcpy (layer_copy, layer, sizeof (CoglMaterialLayer));
|
|
|
|
if (layer_copy->texture != COGL_INVALID_HANDLE)
|
|
cogl_handle_ref (layer_copy->texture);
|
|
|
|
return _cogl_material_layer_handle_new (layer_copy);
|
|
}
|
|
|
|
static unsigned int
|
|
get_n_args_for_combine_func (GLint func)
|
|
{
|
|
switch (func)
|
|
{
|
|
case GL_REPLACE:
|
|
return 1;
|
|
case GL_MODULATE:
|
|
case GL_ADD:
|
|
case GL_ADD_SIGNED:
|
|
case GL_SUBTRACT:
|
|
case GL_DOT3_RGB:
|
|
case GL_DOT3_RGBA:
|
|
return 2;
|
|
case GL_INTERPOLATE:
|
|
return 3;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
is_mipmap_filter (CoglMaterialFilter filter)
|
|
{
|
|
return (filter == COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST
|
|
|| filter == COGL_MATERIAL_FILTER_LINEAR_MIPMAP_NEAREST
|
|
|| filter == COGL_MATERIAL_FILTER_NEAREST_MIPMAP_LINEAR
|
|
|| filter == COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR);
|
|
}
|
|
|
|
/* FIXME: All direct manipulation of GL texture unit state should be dealt with
|
|
* by extending the CoglTextureUnit abstraction */
|
|
static void
|
|
_cogl_material_layer_flush_gl_sampler_state (CoglMaterialLayer *layer,
|
|
CoglTextureUnit *unit,
|
|
CoglLayerInfo *gl_layer_info)
|
|
{
|
|
int n_rgb_func_args;
|
|
int n_alpha_func_args;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
#ifndef DISABLE_MATERIAL_CACHE
|
|
if (!(gl_layer_info &&
|
|
gl_layer_info->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE &&
|
|
(layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE)))
|
|
#endif
|
|
{
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE));
|
|
|
|
/* Set the combiner functions... */
|
|
GE (glTexEnvi (GL_TEXTURE_ENV,
|
|
GL_COMBINE_RGB,
|
|
layer->texture_combine_rgb_func));
|
|
GE (glTexEnvi (GL_TEXTURE_ENV,
|
|
GL_COMBINE_ALPHA,
|
|
layer->texture_combine_alpha_func));
|
|
|
|
/*
|
|
* Setup the function arguments...
|
|
*/
|
|
|
|
/* For the RGB components... */
|
|
n_rgb_func_args =
|
|
get_n_args_for_combine_func (layer->texture_combine_rgb_func);
|
|
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB,
|
|
layer->texture_combine_rgb_src[0]));
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB,
|
|
layer->texture_combine_rgb_op[0]));
|
|
if (n_rgb_func_args > 1)
|
|
{
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB,
|
|
layer->texture_combine_rgb_src[1]));
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB,
|
|
layer->texture_combine_rgb_op[1]));
|
|
}
|
|
if (n_rgb_func_args > 2)
|
|
{
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_RGB,
|
|
layer->texture_combine_rgb_src[2]));
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_RGB,
|
|
layer->texture_combine_rgb_op[2]));
|
|
}
|
|
|
|
/* For the Alpha component */
|
|
n_alpha_func_args =
|
|
get_n_args_for_combine_func (layer->texture_combine_alpha_func);
|
|
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA,
|
|
layer->texture_combine_alpha_src[0]));
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA,
|
|
layer->texture_combine_alpha_op[0]));
|
|
if (n_alpha_func_args > 1)
|
|
{
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA,
|
|
layer->texture_combine_alpha_src[1]));
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA,
|
|
layer->texture_combine_alpha_op[1]));
|
|
}
|
|
if (n_alpha_func_args > 2)
|
|
{
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_ALPHA,
|
|
layer->texture_combine_alpha_src[2]));
|
|
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_ALPHA,
|
|
layer->texture_combine_alpha_op[2]));
|
|
}
|
|
|
|
GE (glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,
|
|
layer->texture_combine_constant));
|
|
}
|
|
|
|
if ((gl_layer_info &&
|
|
gl_layer_info->flags & COGL_MATERIAL_LAYER_FLAG_HAS_USER_MATRIX) ||
|
|
(layer->flags & COGL_MATERIAL_LAYER_FLAG_HAS_USER_MATRIX))
|
|
_cogl_matrix_stack_set (unit->matrix_stack, &layer->matrix);
|
|
else
|
|
_cogl_matrix_stack_load_identity (unit->matrix_stack);
|
|
|
|
/* TODO: Eventually we should just have something like
|
|
* _cogl_flush_texture_units() that we can call in
|
|
* _cogl_material_flush_layers_gl_state */
|
|
_cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE);
|
|
}
|
|
|
|
void
|
|
_cogl_material_layer_ensure_mipmaps (CoglHandle layer_handle)
|
|
{
|
|
CoglMaterialLayer *layer;
|
|
|
|
layer = _cogl_material_layer_pointer_from_handle (layer_handle);
|
|
|
|
if (layer->texture &&
|
|
(is_mipmap_filter (layer->min_filter) ||
|
|
is_mipmap_filter (layer->mag_filter)))
|
|
_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
|
|
* replaced with the default, fallback textures. The fallback textures are
|
|
* fully transparent textures so they hopefully wont contribute to the
|
|
* texture combining.
|
|
*
|
|
* The intention of fallbacks is to try and preserve
|
|
* the number of layers the user is expecting so that texture coordinates
|
|
* they gave will mostly still correspond to the textures they intended, and
|
|
* have a fighting chance of looking close to their originally intended
|
|
* result.
|
|
*
|
|
* @disable_mask: is a bitmask of the material layers that will simply have
|
|
* texturing disabled. It's only really intended for disabling all layers
|
|
* > X; i.e. we'd expect to see a contiguous run of 0 starting from the LSB
|
|
* and at some point the remaining bits flip to 1. It might work to disable
|
|
* arbitrary layers; though I'm not sure a.t.m how OpenGL would take to
|
|
* that.
|
|
*
|
|
* The intention of the disable_mask is for emitting geometry when the user
|
|
* hasn't supplied enough texture coordinates for all the layers and it's
|
|
* not possible to auto generate default texture coordinates for those
|
|
* layers.
|
|
*
|
|
* @layer0_override_texture: forcibly tells us to bind this GL texture name for
|
|
* layer 0 instead of plucking the gl_texture from the CoglTexture of layer
|
|
* 0.
|
|
*
|
|
* The intention of this is for any geometry that supports sliced textures.
|
|
* 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.
|
|
*
|
|
* Normaly texture coords in the range [0, 1] refer to the extents of the
|
|
* texture, but when your GL texture represents a slice of the real texture
|
|
* (from the users POV) then a texture matrix would be a neat way of
|
|
* transforming the mapping for each slice.
|
|
*
|
|
* Currently for textured rectangles we manually calculate the texture
|
|
* coords for each slice based on the users given coords, but this solution
|
|
* isn't ideal, and can't be used with CoglVertexBuffers.
|
|
*/
|
|
static void
|
|
_cogl_material_flush_layers_gl_state (CoglMaterial *material,
|
|
guint32 fallback_mask,
|
|
guint32 disable_mask,
|
|
GLuint layer0_override_texture,
|
|
const CoglMaterialWrapModeOverrides *
|
|
wrap_mode_overrides)
|
|
{
|
|
GList *tmp;
|
|
int i;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
for (tmp = material->layers, i = 0; tmp != NULL; tmp = tmp->next, i++)
|
|
{
|
|
CoglHandle layer_handle = (CoglHandle)tmp->data;
|
|
CoglMaterialLayer *layer =
|
|
_cogl_material_layer_pointer_from_handle (layer_handle);
|
|
CoglLayerInfo *gl_layer_info = NULL;
|
|
CoglLayerInfo new_gl_layer_info;
|
|
CoglHandle tex_handle;
|
|
GLuint gl_texture;
|
|
GLenum gl_target;
|
|
CoglTextureUnit *unit;
|
|
|
|
/* Switch units first so we don't disturb the previous unit if
|
|
* something needs to bind the texture temporarily */
|
|
GE (glActiveTexture (GL_TEXTURE0 + i));
|
|
|
|
unit = _cogl_get_texture_unit (i);
|
|
|
|
_cogl_material_layer_ensure_mipmaps (layer_handle);
|
|
|
|
new_gl_layer_info.layer0_overridden =
|
|
layer0_override_texture ? TRUE : FALSE;
|
|
new_gl_layer_info.fallback =
|
|
(fallback_mask & (1<<i)) ? TRUE : FALSE;
|
|
new_gl_layer_info.disabled =
|
|
(disable_mask & (1<<i)) ? TRUE : FALSE;
|
|
|
|
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
|
|
{
|
|
new_gl_layer_info.fallback = TRUE;
|
|
gl_target = GL_TEXTURE_2D;
|
|
}
|
|
|
|
if (new_gl_layer_info.layer0_overridden)
|
|
gl_texture = layer0_override_texture;
|
|
else if (new_gl_layer_info.fallback)
|
|
{
|
|
if (gl_target == GL_TEXTURE_2D)
|
|
tex_handle = ctx->default_gl_texture_2d_tex;
|
|
#ifdef HAVE_COGL_GL
|
|
else if (gl_target == GL_TEXTURE_RECTANGLE_ARB)
|
|
tex_handle = ctx->default_gl_texture_rect_tex;
|
|
#endif
|
|
else
|
|
{
|
|
g_warning ("We don't have a default texture we can use to fill "
|
|
"in for an invalid material layer, since it was "
|
|
"using an unsupported texture target ");
|
|
/* might get away with this... */
|
|
tex_handle = ctx->default_gl_texture_2d_tex;
|
|
}
|
|
cogl_texture_get_gl_texture (tex_handle, &gl_texture, NULL);
|
|
}
|
|
|
|
/* FIXME: We could be more clever here and only bind the texture
|
|
if it is different from gl_layer_info->gl_texture to avoid
|
|
redundant GL calls. However a few other places in Cogl and
|
|
Clutter call glBindTexture such as ClutterGLXTexturePixmap so
|
|
we'd need to ensure they affect the cache. Also deleting a
|
|
texture should clear it from the cache in case a new texture
|
|
is generated with the same number */
|
|
GE (glBindTexture (gl_target, gl_texture));
|
|
|
|
/* XXX: Once we add caching for glBindTexture state, these
|
|
* checks should be moved back up to the top of the loop!
|
|
*/
|
|
if (i < ctx->current_layers->len)
|
|
{
|
|
gl_layer_info =
|
|
&g_array_index (ctx->current_layers, CoglLayerInfo, i);
|
|
|
|
#ifndef DISABLE_MATERIAL_CACHE
|
|
if (gl_layer_info->handle == layer_handle &&
|
|
!(layer->flags & COGL_MATERIAL_LAYER_FLAG_DIRTY) &&
|
|
!(gl_layer_info->layer0_overridden ||
|
|
new_gl_layer_info.layer0_overridden) &&
|
|
(gl_layer_info->fallback
|
|
== new_gl_layer_info.fallback) &&
|
|
(gl_layer_info->disabled
|
|
== new_gl_layer_info.disabled))
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Disable the previous target if it was different */
|
|
#ifndef DISABLE_MATERIAL_CACHE
|
|
if (gl_layer_info &&
|
|
gl_layer_info->gl_target != gl_target &&
|
|
!gl_layer_info->disabled)
|
|
{
|
|
GE (glDisable (gl_layer_info->gl_target));
|
|
}
|
|
#else
|
|
if (gl_layer_info)
|
|
GE (glDisable (gl_layer_info->gl_target));
|
|
#endif
|
|
|
|
/* Enable/Disable the new target */
|
|
if (!new_gl_layer_info.disabled)
|
|
{
|
|
#ifndef DISABLE_MATERIAL_CACHE
|
|
if (!(gl_layer_info &&
|
|
gl_layer_info->gl_target == gl_target &&
|
|
!gl_layer_info->disabled))
|
|
#endif
|
|
{
|
|
/* XXX: Debug: Comment this out to disable all texturing: */
|
|
#if 1
|
|
GE (glEnable (gl_target));
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifndef DISABLE_MATERIAL_CACHE
|
|
if (!(gl_layer_info &&
|
|
gl_layer_info->gl_target == gl_target &&
|
|
gl_layer_info->disabled))
|
|
#endif
|
|
{
|
|
GE (glDisable (gl_target));
|
|
}
|
|
}
|
|
|
|
_cogl_material_layer_flush_gl_sampler_state (layer, unit, gl_layer_info);
|
|
|
|
new_gl_layer_info.handle = layer_handle;
|
|
new_gl_layer_info.flags = layer->flags;
|
|
new_gl_layer_info.gl_target = gl_target;
|
|
new_gl_layer_info.gl_texture = gl_texture;
|
|
|
|
if (i < ctx->current_layers->len)
|
|
*gl_layer_info = new_gl_layer_info;
|
|
else
|
|
g_array_append_val (ctx->current_layers, new_gl_layer_info);
|
|
|
|
layer->flags &= ~COGL_MATERIAL_LAYER_FLAG_DIRTY;
|
|
|
|
if ((i+1) >= _cogl_get_max_texture_image_units ())
|
|
break;
|
|
}
|
|
|
|
/* Disable additional texture units that may have previously been in use.. */
|
|
for (; i < ctx->current_layers->len; i++)
|
|
{
|
|
CoglLayerInfo *gl_layer_info =
|
|
&g_array_index (ctx->current_layers, CoglLayerInfo, i);
|
|
|
|
#ifndef DISABLE_MATERIAL_CACHE
|
|
if (!gl_layer_info->disabled)
|
|
#endif
|
|
{
|
|
GE (glActiveTexture (GL_TEXTURE0 + i));
|
|
GE (glDisable (gl_layer_info->gl_target));
|
|
gl_layer_info->disabled = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cogl_material_flush_base_gl_state (CoglMaterial *material,
|
|
gboolean skip_gl_color)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
/* XXX:
|
|
* Currently we only don't update state when the flags indicate that the
|
|
* current material uses the defaults, and the new material also uses the
|
|
* defaults, but we could do deeper comparisons of state. */
|
|
|
|
if (!skip_gl_color)
|
|
{
|
|
if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR
|
|
&& material->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR) ||
|
|
/* Assume if we were previously told to skip the color, then
|
|
* the current color needs updating... */
|
|
ctx->current_material_flush_options.flags &
|
|
COGL_MATERIAL_FLUSH_SKIP_GL_COLOR)
|
|
{
|
|
GE (glColor4ub (material->unlit[0],
|
|
material->unlit[1],
|
|
material->unlit[2],
|
|
material->unlit[3]));
|
|
}
|
|
}
|
|
|
|
if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL
|
|
&& material->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL))
|
|
{
|
|
/* FIXME - we only need to set these if lighting is enabled... */
|
|
GE (glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, material->ambient));
|
|
GE (glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, material->diffuse));
|
|
GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, material->specular));
|
|
GE (glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, material->emission));
|
|
GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, &material->shininess));
|
|
}
|
|
|
|
if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC
|
|
&& material->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC))
|
|
{
|
|
/* NB: Currently the Cogl defines are compatible with the GL ones: */
|
|
GE (glAlphaFunc (material->alpha_func, material->alpha_func_reference));
|
|
}
|
|
|
|
if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND
|
|
&& material->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND))
|
|
{
|
|
#if defined (HAVE_COGL_GLES2)
|
|
gboolean have_blend_equation_seperate = TRUE;
|
|
gboolean have_blend_func_separate = TRUE;
|
|
#elif defined (HAVE_COGL_GL)
|
|
gboolean have_blend_equation_seperate = FALSE;
|
|
gboolean have_blend_func_separate = FALSE;
|
|
if (ctx->drv.pf_glBlendEquationSeparate) /* Only GL 2.0 + */
|
|
have_blend_equation_seperate = TRUE;
|
|
if (ctx->drv.pf_glBlendFuncSeparate) /* Only GL 1.4 + */
|
|
have_blend_func_separate = TRUE;
|
|
#endif
|
|
|
|
#ifndef HAVE_COGL_GLES /* GLES 1 only has glBlendFunc */
|
|
if (have_blend_func_separate &&
|
|
(material->blend_src_factor_rgb != material->blend_src_factor_alpha ||
|
|
(material->blend_src_factor_rgb !=
|
|
material->blend_src_factor_alpha)))
|
|
{
|
|
if (have_blend_equation_seperate &&
|
|
material->blend_equation_rgb != material->blend_equation_alpha)
|
|
GE (glBlendEquationSeparate (material->blend_equation_rgb,
|
|
material->blend_equation_alpha));
|
|
else
|
|
GE (glBlendEquation (material->blend_equation_rgb));
|
|
|
|
GE (glBlendFuncSeparate (material->blend_src_factor_rgb,
|
|
material->blend_dst_factor_rgb,
|
|
material->blend_src_factor_alpha,
|
|
material->blend_dst_factor_alpha));
|
|
GE (glBlendColor (material->blend_constant[0],
|
|
material->blend_constant[1],
|
|
material->blend_constant[2],
|
|
material->blend_constant[3]));
|
|
}
|
|
else
|
|
#endif
|
|
GE (glBlendFunc (material->blend_src_factor_rgb,
|
|
material->blend_dst_factor_rgb));
|
|
}
|
|
}
|
|
|
|
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;
|
|
const CoglMaterialWrapModeOverrides *wrap_mode_overrides = NULL;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
if (options)
|
|
{
|
|
if (options->flags & COGL_MATERIAL_FLUSH_FALLBACK_MASK)
|
|
fallback_layers = options->fallback_layers;
|
|
if (options->flags & COGL_MATERIAL_FLUSH_DISABLE_MASK)
|
|
disable_layers = options->disable_layers;
|
|
if (options->flags & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE)
|
|
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,
|
|
skip_gl_color);
|
|
|
|
_cogl_material_flush_layers_gl_state (material,
|
|
fallback_layers,
|
|
disable_layers,
|
|
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
|
|
* material pointer with ctx->current_material
|
|
*/
|
|
cogl_handle_ref (handle);
|
|
if (ctx->current_material)
|
|
cogl_handle_unref (ctx->current_material);
|
|
|
|
ctx->current_material = handle;
|
|
ctx->current_material_flags = material->flags;
|
|
if (options)
|
|
ctx->current_material_flush_options = *options;
|
|
else
|
|
memset (&ctx->current_material_flush_options,
|
|
0, sizeof (CoglMaterialFlushOptions));
|
|
}
|
|
|
|
static gboolean
|
|
_cogl_material_texture_equal (CoglHandle texture0, CoglHandle texture1)
|
|
{
|
|
GLenum gl_handle0, gl_handle1, gl_target0, gl_target1;
|
|
|
|
/* If the texture handles are the same then the textures are
|
|
definitely equal */
|
|
if (texture0 == texture1)
|
|
return TRUE;
|
|
|
|
/* If neither texture is sliced then they could still be the same if
|
|
the are referring to the same GL texture */
|
|
if (cogl_texture_is_sliced (texture0) ||
|
|
cogl_texture_is_sliced (texture1))
|
|
return FALSE;
|
|
|
|
cogl_texture_get_gl_texture (texture0, &gl_handle0, &gl_target0);
|
|
cogl_texture_get_gl_texture (texture1, &gl_handle1, &gl_target1);
|
|
|
|
return gl_handle0 == gl_handle1 && gl_target0 == gl_target1;
|
|
}
|
|
|
|
static gboolean
|
|
_cogl_material_layer_equal (CoglMaterialLayer *material0_layer,
|
|
CoglHandle material0_layer_texture,
|
|
CoglMaterialLayer *material1_layer,
|
|
CoglHandle material1_layer_texture)
|
|
{
|
|
if (!_cogl_material_texture_equal (material0_layer_texture,
|
|
material1_layer_texture))
|
|
return FALSE;
|
|
|
|
if ((material0_layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE) !=
|
|
(material1_layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE))
|
|
return FALSE;
|
|
|
|
#if 0 /* TODO */
|
|
if (!_deep_are_layer_combines_equal ())
|
|
return FALSE;
|
|
#else
|
|
if (!(material0_layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE))
|
|
return FALSE;
|
|
#endif
|
|
|
|
if (material0_layer->mag_filter != material1_layer->mag_filter)
|
|
return FALSE;
|
|
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;
|
|
}
|
|
|
|
/* This is used by the Cogl journal to compare materials so that it
|
|
* can split up geometry that needs different OpenGL state.
|
|
*
|
|
* It is acceptable to have false negatives - although they will result
|
|
* in redundant OpenGL calls that try and update the state.
|
|
*
|
|
* False positives aren't allowed.
|
|
*/
|
|
gboolean
|
|
_cogl_material_equal (CoglHandle material0_handle,
|
|
CoglMaterialFlushOptions *material0_flush_options,
|
|
CoglHandle material1_handle,
|
|
CoglMaterialFlushOptions *material1_flush_options)
|
|
{
|
|
CoglMaterial *material0;
|
|
CoglMaterial *material1;
|
|
CoglMaterialFlushFlag flush_flags0 = material0_flush_options->flags;
|
|
CoglMaterialFlushFlag flush_flags1 = material1_flush_options->flags;
|
|
guint32 fallback_layers0;
|
|
guint32 fallback_layers1;
|
|
guint32 disable_layers0;
|
|
guint32 disable_layers1;
|
|
GList *l0, *l1;
|
|
int i;
|
|
|
|
/* Compare the flush options first; if they are equivalent then we
|
|
* can potentially return quickly if the material handles then match. */
|
|
|
|
|
|
/* The skip color option is used when the color of the material is being
|
|
* submitted in a vertex array so cogl_material_flush_gl_state doesn't
|
|
* need to call glColor.
|
|
* - A skip gl color material following a non skip color material doesn't
|
|
* need a state change since putting a color in a vertex array (as done
|
|
* for skip color materials) would simply take precedence over one
|
|
* previously specified by glColor (as done for non skip color materials)
|
|
* - A non skip color material following a skip color material also doesn't
|
|
* need a state change for the same reason.
|
|
* - The problem is that a non skip color, followed by a skip color, followed
|
|
* by a non skip color does require a state change. Since we don't have
|
|
* enough contextual information here we currently return FALSE whenever
|
|
* the skip color option changes. */
|
|
if ((flush_flags0 & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR) !=
|
|
(flush_flags1 & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR))
|
|
return FALSE;
|
|
|
|
fallback_layers0 = flush_flags0 & COGL_MATERIAL_FLUSH_FALLBACK_MASK ?
|
|
material0_flush_options->fallback_layers : 0;
|
|
fallback_layers1 = flush_flags1 & COGL_MATERIAL_FLUSH_FALLBACK_MASK ?
|
|
material1_flush_options->fallback_layers : 0;
|
|
if (fallback_layers0 != fallback_layers1)
|
|
return FALSE;
|
|
|
|
disable_layers0 = flush_flags0 & COGL_MATERIAL_FLUSH_DISABLE_MASK ?
|
|
material0_flush_options->disable_layers : 0;
|
|
disable_layers1 = flush_flags1 & COGL_MATERIAL_FLUSH_DISABLE_MASK ?
|
|
material1_flush_options->disable_layers : 0;
|
|
if (disable_layers0 != disable_layers1)
|
|
return FALSE;
|
|
|
|
/* NB: Some unlikely false negatives are possible here. */
|
|
if ((flush_flags0 & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE) !=
|
|
(flush_flags1 & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE))
|
|
return FALSE;
|
|
|
|
if ((flush_flags0 & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE) &&
|
|
material0_flush_options->layer0_override_texture !=
|
|
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)
|
|
return TRUE;
|
|
|
|
/* Now we need to look in more detail... */
|
|
|
|
material0 = _cogl_material_pointer_from_handle (material0_handle);
|
|
material1 = _cogl_material_pointer_from_handle (material1_handle);
|
|
|
|
if (!(material0_flush_options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR) &&
|
|
!memcmp (material0->unlit, material1->unlit, sizeof (material0->unlit)))
|
|
return FALSE;
|
|
|
|
/* First we simply try and find a difference according to default flags
|
|
* for each material component to avoid deeper comparison. */
|
|
|
|
if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL) !=
|
|
(material1->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL))
|
|
return FALSE;
|
|
|
|
if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC) !=
|
|
(material1->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC))
|
|
return FALSE;
|
|
|
|
/* Potentially blending could be "enabled" but the blend mode
|
|
* could be equivalent to being disabled, but we accept those false
|
|
* negatives for now. */
|
|
if ((material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) !=
|
|
(material1->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND))
|
|
return FALSE;
|
|
|
|
if ((material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) &&
|
|
(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND) !=
|
|
(material1->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND))
|
|
return FALSE;
|
|
|
|
/* If we still haven't found a difference then do a deeper comparison..
|
|
*
|
|
* Actually we don't currently do this; we simply assume anything
|
|
* non default is different and accept the false negatives for now.
|
|
*/
|
|
|
|
#if 0 /* TODO */
|
|
if (!_deep_are_gl_materials_equal ())
|
|
return FALSE;
|
|
#else
|
|
/* Just assume that all non default materials are different */
|
|
if (!(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL))
|
|
return FALSE;
|
|
#endif
|
|
|
|
#if 0 /* TODO */
|
|
if (!_deep_are_alpha_funcs_equal ())
|
|
return FALSE;
|
|
#else
|
|
/* Just assume that all non default alpha funcs are different */
|
|
if (!(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC))
|
|
return FALSE;
|
|
#endif
|
|
|
|
if (material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND)
|
|
{
|
|
#if 0 /* TODO */
|
|
if (!_deep_is_blend_equal ())
|
|
return FALSE;
|
|
#else
|
|
if (!(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND))
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Finally compare each of the material layers ... */
|
|
|
|
l0 = material0->layers;
|
|
l1 = material1->layers;
|
|
i = 0;
|
|
|
|
/* NB: At this point we know if COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE is being
|
|
* used then both materials are overriding with the same texture. */
|
|
if (flush_flags0 & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE &&
|
|
l0 && l1)
|
|
{
|
|
/* We still need to check if the combine modes etc are equal, but we
|
|
* simply pass COGL_INVALID_HANDLE for both texture handles so they will
|
|
* be considered equal */
|
|
if (!_cogl_material_layer_equal (l0->data, COGL_INVALID_HANDLE,
|
|
l1->data, COGL_INVALID_HANDLE))
|
|
return FALSE;
|
|
|
|
l0 = l0->next;
|
|
l1 = l1->next;
|
|
i++;
|
|
}
|
|
|
|
while (l0 && l1)
|
|
{
|
|
CoglMaterialLayer *m0_layer;
|
|
CoglMaterialLayer *m1_layer;
|
|
|
|
if ((l0 == NULL && l1 != NULL) ||
|
|
(l1 == NULL && l0 != NULL))
|
|
return FALSE;
|
|
|
|
/* NB: At this point we know that the fallback and disable masks for
|
|
* both materials are equal. */
|
|
if (disable_layers0 & (1<<i))
|
|
goto next_layer;
|
|
|
|
m0_layer = l0->data;
|
|
m1_layer = l1->data;
|
|
|
|
/* NB: The use of a fallback texture doesn't imply that the combine
|
|
* modes etc are the same.
|
|
*/
|
|
if ((disable_layers0 & (1<<i)) || (fallback_layers0 & (1<<i)))
|
|
{
|
|
/* As with layer0 overrides, we simply pass COGL_INVALID_HANDLEs for
|
|
* both texture handles here so they will be considered equal. */
|
|
if (!_cogl_material_layer_equal (m0_layer, COGL_INVALID_HANDLE,
|
|
m1_layer, COGL_INVALID_HANDLE))
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (!_cogl_material_layer_equal (m0_layer, m0_layer->texture,
|
|
m1_layer, m1_layer->texture))
|
|
return FALSE;
|
|
}
|
|
|
|
next_layer:
|
|
l0 = l0->next;
|
|
l1 = l1->next;
|
|
i++;
|
|
}
|
|
|
|
if ((l0 == NULL && l1 != NULL) ||
|
|
(l1 == NULL && l0 != NULL))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* While a material is referenced by the Cogl journal we can not allow
|
|
* modifications, so this gives us a mechanism to track journal
|
|
* references separately */
|
|
CoglHandle
|
|
_cogl_material_journal_ref (CoglHandle material_handle)
|
|
{
|
|
CoglMaterial *material =
|
|
material = _cogl_material_pointer_from_handle (material_handle);
|
|
material->journal_ref_count++;
|
|
cogl_handle_ref (material_handle);
|
|
return material_handle;
|
|
}
|
|
|
|
void
|
|
_cogl_material_journal_unref (CoglHandle material_handle)
|
|
{
|
|
CoglMaterial *material =
|
|
material = _cogl_material_pointer_from_handle (material_handle);
|
|
material->journal_ref_count--;
|
|
cogl_handle_unref (material_handle);
|
|
}
|
|
|
|
CoglMaterialFilter
|
|
cogl_material_layer_get_min_filter (CoglHandle layer_handle)
|
|
{
|
|
CoglMaterialLayer *layer;
|
|
|
|
g_return_val_if_fail (cogl_is_material_layer (layer_handle), 0);
|
|
|
|
layer = _cogl_material_layer_pointer_from_handle (layer_handle);
|
|
|
|
return layer->min_filter;
|
|
}
|
|
|
|
CoglMaterialFilter
|
|
cogl_material_layer_get_mag_filter (CoglHandle layer_handle)
|
|
{
|
|
CoglMaterialLayer *layer;
|
|
|
|
g_return_val_if_fail (cogl_is_material_layer (layer_handle), 0);
|
|
|
|
layer = _cogl_material_layer_pointer_from_handle (layer_handle);
|
|
|
|
return layer->mag_filter;
|
|
}
|
|
|
|
void
|
|
cogl_material_set_layer_filters (CoglHandle handle,
|
|
int layer_index,
|
|
CoglMaterialFilter min_filter,
|
|
CoglMaterialFilter mag_filter)
|
|
{
|
|
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);
|
|
|
|
/* possibly flush primitives referencing the current state... */
|
|
_cogl_material_pre_change_notify (material, FALSE, NULL);
|
|
|
|
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;
|
|
}
|