71b62e75d2
Although the underlying materials should allow layers with INVALID_HANDLES it shouldn't be necissary to expose that via cogl_set_source_texture() and it's easier to resolve a warning/crash here than odd artefacts/crashes later in the pipeline.
1564 lines
48 KiB
C
1564 lines
48 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, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*
|
|
* 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 <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->pf_glActiveTexture
|
|
#define glClientActiveTexture ctx->pf_glClientActiveTexture
|
|
#define glBlendFuncSeparate ctx->pf_glBlendFuncSeparate
|
|
#define glBlendEquation ctx->pf_glBlendEquation
|
|
#define glBlendColor ctx->pf_glBlendColor
|
|
#define glBlendEquationSeparate ctx->pf_glBlendEquationSeparate
|
|
#endif
|
|
|
|
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");
|
|
}
|
|
|
|
CoglHandle
|
|
cogl_material_new (void)
|
|
{
|
|
/* Create new - blank - material */
|
|
CoglMaterial *material = g_new0 (CoglMaterial, 1);
|
|
GLfloat *unlit = material->unlit;
|
|
GLfloat *ambient = material->ambient;
|
|
GLfloat *diffuse = material->diffuse;
|
|
GLfloat *specular = material->specular;
|
|
GLfloat *emission = material->emission;
|
|
|
|
/* Use the same defaults as the GL spec... */
|
|
unlit[0] = 1.0; unlit[1] = 1.0; unlit[2] = 1.0; unlit[3] = 1.0;
|
|
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_SRC_ALPHA;
|
|
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_FUNC;
|
|
|
|
material->layers = NULL;
|
|
|
|
return _cogl_material_handle_new (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_free (material);
|
|
}
|
|
|
|
static void
|
|
handle_automatic_blend_enable (CoglMaterial *material)
|
|
{
|
|
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 */
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_ENABLE_BLEND;
|
|
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)
|
|
material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND;
|
|
}
|
|
|
|
if (material->unlit[3] != 1.0)
|
|
material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND;
|
|
}
|
|
|
|
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_4f (color,
|
|
material->unlit[0],
|
|
material->unlit[1],
|
|
material->unlit[2],
|
|
material->unlit[3]);
|
|
}
|
|
|
|
void
|
|
cogl_material_set_color (CoglHandle handle,
|
|
const CoglColor *unlit_color)
|
|
{
|
|
CoglMaterial *material;
|
|
GLfloat unlit[4];
|
|
|
|
g_return_if_fail (cogl_is_material (handle));
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
unlit[0] = cogl_color_get_red_float (unlit_color);
|
|
unlit[1] = cogl_color_get_green_float (unlit_color);
|
|
unlit[2] = cogl_color_get_blue_float (unlit_color);
|
|
unlit[3] = cogl_color_get_alpha_float (unlit_color);
|
|
if (memcmp (unlit, material->unlit, sizeof (unlit)) == 0)
|
|
return;
|
|
|
|
memcpy (material->unlit, unlit, sizeof (unlit));
|
|
|
|
material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_COLOR;
|
|
if (unlit[0] == 1.0 &&
|
|
unlit[1] == 1.0 &&
|
|
unlit[2] == 1.0 &&
|
|
unlit[3] == 1.0)
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
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];
|
|
}
|
|
|
|
#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_FUNC;
|
|
|
|
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);
|
|
|
|
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_FUNC;
|
|
#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,
|
|
gint 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;
|
|
|
|
layer = g_new0 (CoglMaterialLayer, 1);
|
|
|
|
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->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,
|
|
gint layer_index,
|
|
CoglHandle texture_handle)
|
|
{
|
|
CoglMaterial *material;
|
|
CoglMaterialLayer *layer;
|
|
int n_layers;
|
|
|
|
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_handle, layer_index, TRUE);
|
|
|
|
if (texture_handle == layer->texture)
|
|
return;
|
|
|
|
n_layers = g_list_length (material->layers);
|
|
if (n_layers >= CGL_MAX_COMBINED_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,
|
|
gint 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];
|
|
}
|
|
|
|
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,
|
|
gint 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);
|
|
|
|
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,
|
|
gint 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);
|
|
|
|
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_free (layer);
|
|
}
|
|
|
|
void
|
|
cogl_material_remove_layer (CoglHandle material_handle,
|
|
gint layer_index)
|
|
{
|
|
CoglMaterial *material;
|
|
CoglMaterialLayer *layer;
|
|
GList *tmp;
|
|
|
|
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;
|
|
cogl_handle_unref (handle);
|
|
material->layers = g_list_remove (material->layers, layer);
|
|
break;
|
|
}
|
|
}
|
|
|
|
handle_automatic_blend_enable (material);
|
|
}
|
|
|
|
/* XXX: This API is hopfully just a stop-gap solution. Ideally cogl_enable
|
|
* will be replaced. */
|
|
gulong
|
|
_cogl_material_get_cogl_enable_flags (CoglHandle material_handle)
|
|
{
|
|
CoglMaterial *material;
|
|
gulong 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
gulong
|
|
_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 guint
|
|
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);
|
|
}
|
|
|
|
static void
|
|
_cogl_material_layer_flush_gl_sampler_state (CoglMaterialLayer *layer,
|
|
CoglLayerInfo *gl_layer_info)
|
|
{
|
|
int n_rgb_func_args;
|
|
int n_alpha_func_args;
|
|
|
|
#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));
|
|
}
|
|
|
|
#ifndef DISABLE_MATERIAL_CACHE
|
|
if (gl_layer_info &&
|
|
(gl_layer_info->flags & COGL_MATERIAL_LAYER_FLAG_HAS_USER_MATRIX ||
|
|
layer->flags & COGL_MATERIAL_LAYER_FLAG_HAS_USER_MATRIX))
|
|
#endif
|
|
{
|
|
_cogl_set_current_matrix (COGL_MATRIX_TEXTURE);
|
|
_cogl_current_matrix_load (&layer->matrix);
|
|
_cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* _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.
|
|
*
|
|
* 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)
|
|
{
|
|
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;
|
|
#ifdef HAVE_COGL_GLES2
|
|
GLenum gl_internal_format;
|
|
#endif
|
|
|
|
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;
|
|
cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target);
|
|
|
|
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);
|
|
}
|
|
|
|
#ifdef HAVE_COGL_GLES2
|
|
{
|
|
CoglTexture *tex =
|
|
_cogl_texture_pointer_from_handle (tex_handle);
|
|
gl_internal_format = tex->gl_intformat;
|
|
}
|
|
#endif
|
|
|
|
GE (glActiveTexture (GL_TEXTURE0 + i));
|
|
|
|
_cogl_texture_set_filters (layer->texture,
|
|
layer->min_filter,
|
|
layer->mag_filter);
|
|
if (is_mipmap_filter (layer->min_filter)
|
|
|| is_mipmap_filter (layer->mag_filter))
|
|
_cogl_texture_ensure_mipmaps (layer->texture);
|
|
|
|
/* 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 */
|
|
#ifdef HAVE_COGL_GLES2
|
|
cogl_gles2_wrapper_bind_texture (gl_target,
|
|
gl_texture,
|
|
gl_internal_format);
|
|
#else
|
|
GE (glBindTexture (gl_target, gl_texture));
|
|
#endif
|
|
|
|
/* 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
|
|
{
|
|
GE (glEnable (gl_target));
|
|
}
|
|
}
|
|
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, 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) >= CGL_MAX_COMBINED_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)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR
|
|
&& material->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR))
|
|
{
|
|
/* GLES doesn't have glColor4fv... */
|
|
GE (glColor4f (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_FUNC
|
|
&& material->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC))
|
|
{
|
|
#if defined (HAVE_COGL_GLES2)
|
|
gboolean have_blend_equation_seperate = TRUE;
|
|
#elif defined (HAVE_COGL_GL)
|
|
gboolean have_blend_equation_seperate = FALSE;
|
|
if (ctx->pf_glBlendEquationSeparate) /* Only GL 2.0 + */
|
|
have_blend_equation_seperate = TRUE;
|
|
#endif
|
|
|
|
#ifndef HAVE_COGL_GLES /* GLES 1 only has glBlendFunc */
|
|
if (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, ...)
|
|
{
|
|
CoglMaterial *material;
|
|
va_list ap;
|
|
CoglMaterialFlushOption option;
|
|
guint32 fallback_layers = 0;
|
|
guint32 disable_layers = 0;
|
|
GLuint layer0_override_texture = 0;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
material = _cogl_material_pointer_from_handle (handle);
|
|
|
|
_cogl_material_flush_base_gl_state (material);
|
|
|
|
va_start (ap, handle);
|
|
while ((option = va_arg (ap, CoglMaterialFlushOption)))
|
|
{
|
|
if (option == COGL_MATERIAL_FLUSH_FALLBACK_MASK)
|
|
fallback_layers = va_arg (ap, guint32);
|
|
else if (option == COGL_MATERIAL_FLUSH_DISABLE_MASK)
|
|
disable_layers = va_arg (ap, guint32);
|
|
else if (option == COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE)
|
|
layer0_override_texture = va_arg (ap, GLuint);
|
|
}
|
|
va_end (ap);
|
|
|
|
_cogl_material_flush_layers_gl_state (material,
|
|
fallback_layers,
|
|
disable_layers,
|
|
layer0_override_texture);
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* TODO: Should go in cogl.c, but that implies duplication which is also
|
|
* not ideal. */
|
|
void
|
|
cogl_set_source (CoglHandle material_handle)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
g_return_if_fail (cogl_is_material (material_handle));
|
|
|
|
if (ctx->source_material == material_handle)
|
|
return;
|
|
|
|
cogl_handle_ref (material_handle);
|
|
|
|
if (ctx->source_material)
|
|
cogl_handle_unref (ctx->source_material);
|
|
|
|
ctx->source_material = material_handle;
|
|
}
|
|
/* TODO: add cogl_set_front_source (), and cogl_set_back_source () */
|
|
|
|
void
|
|
cogl_set_source_texture (CoglHandle texture_handle)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
CoglColor white;
|
|
|
|
g_return_if_fail (texture_handle != COGL_INVALID_HANDLE);
|
|
|
|
cogl_material_set_layer (ctx->default_material, 0, texture_handle);
|
|
cogl_color_set_from_4ub (&white, 0xff, 0xff, 0xff, 0xff);
|
|
cogl_material_set_color (ctx->default_material, &white);
|
|
cogl_set_source (ctx->default_material);
|
|
}
|
|
|
|
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,
|
|
gint 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);
|
|
|
|
layer->min_filter = min_filter;
|
|
layer->mag_filter = mag_filter;
|
|
}
|