forked from enlightenment/efl
375 lines
10 KiB
C
375 lines
10 KiB
C
#include "evas_gl_private.h"
|
|
#include "evas_gl_3d_private.h"
|
|
|
|
#define E3D_MAX_TEXTURE_COUNT 8
|
|
#define E3D_MAX_VERTEX_ATTRIB_COUNT 8
|
|
|
|
struct _E3D_Renderer
|
|
{
|
|
Eina_List *programs;
|
|
|
|
GLuint fbo;
|
|
GLuint program;
|
|
E3D_Texture *textures[E3D_MAX_TEXTURE_COUNT];
|
|
|
|
Eina_Bool vertex_attrib_enable[E3D_MAX_VERTEX_ATTRIB_COUNT];
|
|
Eina_Bool depth_test_enable;
|
|
GLuint texDepth;
|
|
GLint smap_sampler;
|
|
};
|
|
|
|
static inline GLenum
|
|
_gl_assembly_get(Evas_3D_Vertex_Assembly assembly)
|
|
{
|
|
switch (assembly)
|
|
{
|
|
case EVAS_3D_VERTEX_ASSEMBLY_POINTS:
|
|
return GL_POINTS;
|
|
case EVAS_3D_VERTEX_ASSEMBLY_LINES:
|
|
return GL_LINES;
|
|
case EVAS_3D_VERTEX_ASSEMBLY_LINE_STRIP:
|
|
return GL_LINE_STRIP;
|
|
case EVAS_3D_VERTEX_ASSEMBLY_LINE_LOOP:
|
|
return GL_LINE_LOOP;
|
|
case EVAS_3D_VERTEX_ASSEMBLY_TRIANGLES:
|
|
return GL_TRIANGLES;
|
|
case EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_STRIP:
|
|
return GL_TRIANGLE_STRIP;
|
|
case EVAS_3D_VERTEX_ASSEMBLY_TRIANGLE_FAN:
|
|
return GL_TRIANGLE_FAN;
|
|
default:
|
|
return GL_NONE;
|
|
}
|
|
}
|
|
|
|
static inline GLenum
|
|
_gl_blend_func_get(Evas_3D_Blend_Func blend_func)
|
|
{
|
|
switch (blend_func)
|
|
{
|
|
case EVAS_3D_BLEND_ZERO:
|
|
return GL_ZERO;
|
|
case EVAS_3D_BLEND_ONE:
|
|
return GL_ONE;
|
|
case EVAS_3D_BLEND_SRC_COLOR:
|
|
return GL_SRC_COLOR;
|
|
case EVAS_3D_BLEND_ONE_MINUS_SRC_COLOR:
|
|
return GL_ONE_MINUS_SRC_COLOR;
|
|
case EVAS_3D_BLEND_DST_COLOR:
|
|
return GL_DST_COLOR;
|
|
case EVAS_3D_BLEND_ONE_MINUS_DST_COLOR:
|
|
return GL_ONE_MINUS_DST_COLOR;
|
|
case EVAS_3D_BLEND_SRC_ALPHA:
|
|
return GL_SRC_ALPHA;
|
|
case EVAS_3D_BLEND_ONE_MINUS_SRC_ALPHA:
|
|
return GL_ONE_MINUS_SRC_ALPHA;
|
|
case EVAS_3D_BLEND_DST_ALPHA:
|
|
return GL_DST_ALPHA;
|
|
case EVAS_3D_BLEND_ONE_MINUS_DST_ALPHA:
|
|
return GL_ONE_MINUS_DST_ALPHA;
|
|
case EVAS_3D_BLEND_CONSTANT_COLOR:
|
|
return GL_CONSTANT_COLOR;
|
|
case EVAS_3D_BLEND_ONE_MINUS_CONSTANT_COLOR:
|
|
return GL_ONE_MINUS_CONSTANT_COLOR;
|
|
case EVAS_3D_BLEND_CONSTANT_ALPHA:
|
|
return GL_CONSTANT_ALPHA;
|
|
case EVAS_3D_BLEND_ONE_MINUS_CONSTANT_ALPHA:
|
|
return GL_ONE_MINUS_CONSTANT_ALPHA;
|
|
case EVAS_3D_BLEND_SRC_ALPHA_SATURATE:
|
|
return GL_SRC_ALPHA_SATURATE;
|
|
default:
|
|
return GL_ZERO;
|
|
}
|
|
}
|
|
|
|
static inline GLenum
|
|
_gl_comparison_func_get(Evas_3D_Comparison comparison_func)
|
|
{
|
|
switch (comparison_func)
|
|
{
|
|
case EVAS_3D_COMPARISON_NEVER:
|
|
return GL_NEVER;
|
|
case EVAS_3D_COMPARISON_LESS:
|
|
return GL_LESS;
|
|
case EVAS_3D_COMPARISON_EQUAL:
|
|
return GL_EQUAL;
|
|
case EVAS_3D_COMPARISON_LEQUAL:
|
|
return GL_LEQUAL;
|
|
case EVAS_3D_COMPARISON_GREATER:
|
|
return GL_GREATER;
|
|
case EVAS_3D_COMPARISON_NOTEQUAL:
|
|
return GL_NOTEQUAL;
|
|
case EVAS_3D_COMPARISON_GEQUAL:
|
|
return GL_GEQUAL;
|
|
case EVAS_3D_COMPARISON_ALWAYS:
|
|
return GL_ALWAYS;
|
|
default:
|
|
return GL_ALWAYS;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
_renderer_vertex_attrib_array_enable(E3D_Renderer *renderer, int index)
|
|
{
|
|
if (renderer->vertex_attrib_enable[index])
|
|
return;
|
|
|
|
glEnableVertexAttribArray(index);
|
|
renderer->vertex_attrib_enable[index] = EINA_TRUE;
|
|
}
|
|
|
|
static inline void
|
|
_renderer_vertex_attrib_array_disable(E3D_Renderer *renderer, int index)
|
|
{
|
|
if (!renderer->vertex_attrib_enable[index])
|
|
return;
|
|
|
|
glDisableVertexAttribArray(index);
|
|
renderer->vertex_attrib_enable[index] = EINA_FALSE;
|
|
}
|
|
|
|
static inline void
|
|
_renderer_vertex_attrib_pointer_set(E3D_Renderer *renderer EINA_UNUSED, int index,
|
|
const Evas_3D_Vertex_Buffer *buffer)
|
|
{
|
|
glVertexAttribPointer(index, buffer->element_count, GL_FLOAT,
|
|
GL_FALSE, buffer->stride, buffer->data);
|
|
}
|
|
|
|
static inline void
|
|
_renderer_elements_draw(E3D_Renderer *renderer EINA_UNUSED, Evas_3D_Vertex_Assembly assembly,
|
|
int count, Evas_3D_Index_Format format, const void *indices)
|
|
{
|
|
GLenum mode = _gl_assembly_get(assembly);
|
|
|
|
if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_BYTE)
|
|
glDrawElements(mode, count, GL_UNSIGNED_BYTE, indices);
|
|
else if (format == EVAS_3D_INDEX_FORMAT_UNSIGNED_SHORT)
|
|
glDrawElements(mode, count, GL_UNSIGNED_SHORT, indices);
|
|
}
|
|
|
|
static inline void
|
|
_renderer_array_draw(E3D_Renderer *renderer EINA_UNUSED,
|
|
Evas_3D_Vertex_Assembly assembly, int count)
|
|
{
|
|
GLenum mode = _gl_assembly_get(assembly);
|
|
|
|
glDrawArrays(mode, 0, count);
|
|
}
|
|
|
|
static inline void
|
|
_renderer_program_use(E3D_Renderer *renderer ,E3D_Program *program)
|
|
{
|
|
GLuint prog = e3d_program_id_get(program);
|
|
|
|
if (renderer->program != prog)
|
|
{
|
|
glUseProgram(prog);
|
|
renderer->program = prog;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
_renderer_texture_bind(E3D_Renderer *renderer, E3D_Draw_Data *data)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < EVAS_3D_MATERIAL_ATTRIB_COUNT; i++)
|
|
{
|
|
if (data->materials[i].tex0)
|
|
{
|
|
if (renderer->textures[data->materials[i].sampler0] != data->materials[i].tex0)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + data->materials[i].sampler0);
|
|
glBindTexture(GL_TEXTURE_2D, data->materials[i].tex0->tex);
|
|
e3d_texture_param_update(data->materials[i].tex0);
|
|
|
|
renderer->textures[data->materials[i].sampler0] = data->materials[i].tex0;
|
|
}
|
|
}
|
|
|
|
if (data->materials[i].tex1)
|
|
{
|
|
if (renderer->textures[data->materials[i].sampler1] != data->materials[i].tex1)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + data->materials[i].sampler1);
|
|
glBindTexture(GL_TEXTURE_2D, data->materials[i].tex1->tex);
|
|
e3d_texture_param_update(data->materials[i].tex1);
|
|
|
|
renderer->textures[data->materials[i].sampler1] = data->materials[i].tex1;
|
|
}
|
|
}
|
|
}
|
|
if ((data->flags & E3D_SHADER_FLAG_SHADOWED) && (renderer->smap_sampler != data->smap_sampler))
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + data->smap_sampler);
|
|
glBindTexture(GL_TEXTURE_2D, renderer->texDepth);
|
|
renderer->smap_sampler = data->smap_sampler;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
_renderer_depth_test_enable(E3D_Renderer *renderer, Eina_Bool enable)
|
|
{
|
|
if (renderer->depth_test_enable != enable)
|
|
{
|
|
if (enable)
|
|
{
|
|
glEnable(GL_DEPTH_TEST);
|
|
/* Use default depth func. */
|
|
}
|
|
else
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
renderer->depth_test_enable = enable;
|
|
}
|
|
}
|
|
|
|
E3D_Renderer *
|
|
e3d_renderer_new(void)
|
|
{
|
|
E3D_Renderer *renderer = NULL;
|
|
|
|
renderer = (E3D_Renderer *)calloc(1, sizeof(E3D_Renderer));
|
|
|
|
if (renderer == NULL)
|
|
{
|
|
ERR("Failed to allocate memory.");
|
|
return NULL;
|
|
}
|
|
|
|
return renderer;
|
|
}
|
|
|
|
void
|
|
e3d_renderer_free(E3D_Renderer *renderer)
|
|
{
|
|
Eina_List *l;
|
|
E3D_Program *p;
|
|
|
|
EINA_LIST_FOREACH(renderer->programs, l, p)
|
|
{
|
|
e3d_program_free(p);
|
|
}
|
|
|
|
eina_list_free(renderer->programs);
|
|
}
|
|
|
|
void
|
|
e3d_renderer_target_set(E3D_Renderer *renderer, E3D_Drawable *target)
|
|
{
|
|
if (renderer->fbo == target->fbo)
|
|
return;
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, target->fbo);
|
|
glViewport(0, 0, target->w, target->h);
|
|
renderer->fbo = target->fbo;
|
|
renderer->texDepth = target->texDepth;
|
|
}
|
|
|
|
void
|
|
e3d_renderer_clear(E3D_Renderer *renderer EINA_UNUSED, const Evas_Color *color)
|
|
{
|
|
glClearColor(color->r, color->g, color->b, color->a);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
void
|
|
e3d_renderer_draw(E3D_Renderer *renderer, E3D_Draw_Data *data)
|
|
{
|
|
Eina_List *l;
|
|
E3D_Program *program = NULL, *p;
|
|
int i, index = 0;
|
|
|
|
_renderer_depth_test_enable(renderer, EINA_TRUE);
|
|
|
|
EINA_LIST_FOREACH(renderer->programs, l, p)
|
|
{
|
|
if (e3d_program_shade_mode_get(p) == data->mode &&
|
|
e3d_program_shader_flags_get(p) == data->flags)
|
|
{
|
|
program = p;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (program == NULL)
|
|
{
|
|
program = e3d_program_new(data->mode, data->flags);
|
|
|
|
if (program == NULL)
|
|
{
|
|
ERR("Failed to create shader program.");
|
|
return;
|
|
}
|
|
|
|
renderer->programs = eina_list_append(renderer->programs, program);
|
|
}
|
|
|
|
_renderer_program_use(renderer, program);
|
|
e3d_program_uniform_upload(program, data);
|
|
_renderer_texture_bind(renderer, data);
|
|
|
|
/* Set up vertex attrib pointers. */
|
|
index = 0;
|
|
|
|
for (i = 0; i < EVAS_3D_VERTEX_ATTRIB_COUNT; i++)
|
|
{
|
|
const Evas_3D_Vertex_Buffer *buffer;
|
|
|
|
buffer = &data->vertices[i].vertex0;
|
|
|
|
if (buffer->data != NULL)
|
|
{
|
|
_renderer_vertex_attrib_array_enable(renderer, index);
|
|
_renderer_vertex_attrib_pointer_set(renderer, index, buffer);
|
|
index++;
|
|
}
|
|
|
|
buffer = &data->vertices[i].vertex1;
|
|
|
|
if (buffer->data != NULL)
|
|
{
|
|
_renderer_vertex_attrib_array_enable(renderer, index);
|
|
_renderer_vertex_attrib_pointer_set(renderer, index, buffer);
|
|
index++;
|
|
}
|
|
}
|
|
|
|
while (index < E3D_MAX_VERTEX_ATTRIB_COUNT)
|
|
_renderer_vertex_attrib_array_disable(renderer, index++);
|
|
|
|
if (data->blending)
|
|
{
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(_gl_blend_func_get(data->blend_sfactor), _gl_blend_func_get(data->blend_dfactor));
|
|
}
|
|
else glDisable(GL_BLEND);
|
|
|
|
if (data->alpha_test_enabled)
|
|
{
|
|
glEnable(GL_ALPHA_TEST);
|
|
glAlphaFunc(_gl_comparison_func_get(data->alpha_comparison),
|
|
(GLclampf)data->alpha_ref_value);
|
|
}
|
|
else glDisable(GL_ALPHA_TEST);
|
|
|
|
if (data->indices)
|
|
{
|
|
_renderer_elements_draw(renderer, data->assembly, data->index_count,
|
|
data->index_format, data->indices);
|
|
}
|
|
else
|
|
{
|
|
_renderer_array_draw(renderer, data->assembly, data->vertex_count);
|
|
}
|
|
}
|
|
|
|
void
|
|
e3d_renderer_flush(E3D_Renderer *renderer EINA_UNUSED)
|
|
{
|
|
glFlush();
|
|
}
|