blob: 66ce6943912a580b19970242e703c9158395f9c3 [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2000-2026
* All rights reserved
*
* This file is part of GPAC / Scene Compositor sub-project
*
* GPAC 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, or (at your option)
* any later version.
*
* GPAC 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; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "visual_manager.h"
#include "texturing.h"
#if !defined(GPAC_DISABLE_3D) && !defined(GPAC_DISABLE_COMPOSITOR)
#include <gpac/nodes_mpeg4.h>
#include "gl_inc.h"
#if (defined(WIN32) || defined(_WIN32_WCE)) && !defined(__GNUC__)
# if defined(GPAC_USE_TINYGL)
# pragma comment(lib, "TinyGL")
# elif defined(GPAC_USE_GLES1X)
# if 0
# pragma message("Using OpenGL-ES Common Lite Profile")
# pragma comment(lib, "libGLES_CL")
# define GL_ES_CL_PROFILE
# else
# pragma message("Using OpenGL-ES Common Profile")
# pragma comment(lib, "libGLES_CM")
# endif
# elif defined(GPAC_USE_GLES2)
# pragma comment(lib, "libGLESv2")
# else
# pragma comment(lib, "opengl32")
# endif
#ifdef GPAC_HAS_GLU
# pragma comment(lib, "glu32")
#endif
#endif
/*!! HORRIBLE HACK, but on my test devices, it seems that glClipPlanex is missing on the device but not in the SDK lib !!*/
#if defined(GL_MAX_CLIP_PLANES) && defined(__SYMBIAN32__)
#undef GL_MAX_CLIP_PLANES
#endif
#define CHECK_GL_EXT(name) ((strstr(ext, name) != NULL) ? 1 : 0)
void gf_sc_load_opengl_extensions(GF_Compositor *compositor, Bool has_gl_context)
{
#ifdef GPAC_USE_TINYGL
/*let TGL handle texturing*/
compositor->gl_caps.rect_texture = 1;
compositor->gl_caps.npot_texture = 1;
#else
const char *ext = NULL;
if (has_gl_context)
ext = (const char *) glGetString(GL_EXTENSIONS);
if (!ext) ext = gf_opts_get_key("core", "glext");
/*store OGL extension to config for app usage*/
else if (gf_opts_get_key("core", "glext")==NULL)
gf_opts_set_key("core", "glext", ext ? ext : "None");
if (!ext) return;
memset(&compositor->gl_caps, 0, sizeof(GLCaps));
if (CHECK_GL_EXT("GL_ARB_multisample") || CHECK_GL_EXT("GLX_ARB_multisample") || CHECK_GL_EXT("WGL_ARB_multisample"))
compositor->gl_caps.multisample = 1;
if (CHECK_GL_EXT("GL_ARB_texture_non_power_of_two"))
compositor->gl_caps.npot_texture = 1;
if (CHECK_GL_EXT("GL_EXT_abgr"))
compositor->gl_caps.abgr_texture = 1;
if (CHECK_GL_EXT("GL_EXT_bgra"))
compositor->gl_caps.bgra_texture = 1;
if (CHECK_GL_EXT("GL_EXT_framebuffer_object") || CHECK_GL_EXT("GL_ARB_framebuffer_object"))
compositor->gl_caps.fbo = 1;
if (CHECK_GL_EXT("GL_ARB_texture_non_power_of_two"))
compositor->gl_caps.npot = 1;
if (CHECK_GL_EXT("GL_ARB_point_parameters")) {
compositor->gl_caps.point_sprite = 1;
if (CHECK_GL_EXT("GL_ARB_point_sprite") || CHECK_GL_EXT("GL_NV_point_sprite")) {
compositor->gl_caps.point_sprite = 2;
}
}
#ifdef GPAC_USE_GLES2
compositor->gl_caps.vbo = 1;
#else
if (CHECK_GL_EXT("GL_ARB_vertex_buffer_object")) {
compositor->gl_caps.vbo = 1;
}
#endif
#ifndef GPAC_USE_GLES1X
if (CHECK_GL_EXT("GL_EXT_texture_rectangle") || CHECK_GL_EXT("GL_NV_texture_rectangle")) {
compositor->gl_caps.rect_texture = 1;
}
#endif
if (CHECK_GL_EXT("EXT_unpack_subimage") ) {
compositor->gl_caps.gles2_unpack = 1;
}
if (!has_gl_context) return;
/*we have a GL context, init the rest (proc addresses & co)*/
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &compositor->gl_caps.max_texture_size);
if (CHECK_GL_EXT("GL_ARB_pixel_buffer_object")) {
compositor->gl_caps.pbo=1;
}
#ifdef LOAD_GL_2_0
if (glCreateProgram != NULL) {
compositor->gl_caps.has_shaders = 1;
#ifndef GPAC_CONFIG_ANDROID
if (glGetAttribLocation == NULL) {
compositor->shader_mode_disabled = GF_TRUE;
}
#endif
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_COMPOSE, ("[Compositor] OpenGL shaders not supported\n"));
}
#endif //LOAD_GL_2_0
#endif //GPAC_USE_TINYGL
#ifdef GPAC_USE_GLES2
compositor->gl_caps.has_shaders = GF_TRUE;
#elif defined (GL_VERSION_2_0)
compositor->gl_caps.has_shaders = GF_TRUE;
#endif
if (!compositor->gl_caps.has_shaders) {
#if !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
compositor->shader_mode_disabled = GF_TRUE;
#endif
if (compositor->visual->autostereo_type > GF_3D_STEREO_LAST_SINGLE_BUFFER) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] OpenGL shaders not supported - disabling auto-stereo output\n"));
compositor->visual->nb_views=1;
compositor->visual->autostereo_type = GF_3D_STEREO_NONE;
compositor->visual->camlay = GF_3D_CAMERA_STRAIGHT;
}
}
#if !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
if (!compositor->shader_mode_disabled && compositor->vertshader && compositor->fragshader) {
if (!gf_file_exists(compositor->vertshader)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] GLES Vertex shader %s not found, disabling shaders\n", compositor->vertshader));
compositor->shader_mode_disabled = GF_TRUE;
}
if (!gf_file_exists(compositor->fragshader)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] GLES Fragment shader %s not found, disabling shaders\n", compositor->fragshader));
compositor->shader_mode_disabled = GF_TRUE;
}
}
#endif
}
#if !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
#ifdef GPAC_USE_GLES2
#define GLES_VERSION_STRING "#version 100 \n"
#else
#define GLES_VERSION_STRING "#version 120 \n"
#endif
#define GLSL_PREFIX GLES_VERSION_STRING \
"#ifdef GL_ES\n"\
"#ifdef GL_FRAGMENT_PRECISION_HIGH\n"\
"precision highp float;\n"\
"#else\n"\
"precision mediump float;\n"\
"#endif\n" \
"#endif\n"
static char *glsl_autostereo_vertex = GLSL_PREFIX "\
attribute vec4 gfVertex;\
attribute vec2 gfTextureCoordinates;\
uniform mat4 gfProjectionMatrix;\
varying vec2 TexCoord;\
void main(void)\
{\
TexCoord = gfTextureCoordinates;\
gl_Position = gfProjectionMatrix * gfVertex;\
}";
static char *glsl_view_anaglyph = GLSL_PREFIX "\
uniform sampler2D gfView1;\
uniform sampler2D gfView2;\
varying vec2 TexCoord;\
void main(void) \
{\
vec4 col1 = texture2D(gfView1, TexCoord.st); \
vec4 col2 = texture2D(gfView2, TexCoord.st); \
gl_FragColor.r = col1.r;\
gl_FragColor.g = col2.g;\
gl_FragColor.b = col2.b;\
}";
#ifdef GPAC_UNUSED_FUNC
static char *glsl_view_anaglyph_optimize = GLSL_PREFIX "\
uniform sampler2D gfView1;\
uniform sampler2D gfView2;\
varying vec2 TexCoord;\
void main(void) \
{\
vec4 col1 = texture2D(gfView1, TexCoord.st); \
vec4 col2 = texture2D(gfView2, TexCoord.st); \
gl_FragColor.r = 0.7*col1.g + 0.3*col1.b;\
gl_FragColor.r = pow(gl_FragColor.r, 1.5);\
gl_FragColor.g = col2.g;\
gl_FragColor.b = col2.b;\
}";
#endif /*GPAC_UNUSED_FUNC*/
static char *glsl_view_columns = GLSL_PREFIX "\
uniform sampler2D gfView1;\
uniform sampler2D gfView2;\
varying vec2 TexCoord;\
void main(void) \
{\
if ( int( mod(gl_FragCoord.x, 2.0) ) == 0) \
gl_FragColor = texture2D(gfView1, TexCoord.st); \
else \
gl_FragColor = texture2D(gfView2, TexCoord.st); \
}";
static char *glsl_view_rows = GLSL_PREFIX "\
uniform sampler2D gfView1;\
uniform sampler2D gfView2;\
varying vec2 TexCoord;\
void main(void) \
{\
if ( int( mod(gl_FragCoord.y, 2.0) ) == 0) \
gl_FragColor = texture2D(gfView1, TexCoord.st); \
else \
gl_FragColor = texture2D(gfView2, TexCoord.st); \
}";
static char *glsl_view_5VSP19 = GLSL_PREFIX "\
uniform sampler2D gfView1;\
uniform sampler2D gfView2;\
uniform sampler2D gfView3;\
uniform sampler2D gfView4;\
uniform sampler2D gfView5;\
varying vec2 TexCoord;\
\
void getTextureSample(in int texID, out vec4 color) { \
if (texID == 0 ) color = texture2D(gfView1, TexCoord.st); \
else if (texID == 1 ) color = texture2D(gfView2, TexCoord.st); \
else if (texID == 2 ) color = texture2D(gfView3, TexCoord.st); \
else if (texID == 3 ) color = texture2D(gfView4, TexCoord.st); \
else if (texID == 4 ) color = texture2D(gfView5, TexCoord.st); \
} \
\
void main(void) {\
vec4 color;\
float pitch = 5.0 + 1.0 - mod(gl_FragCoord.y , 5.0);\
int col = int( mod(pitch + 3.0 * (gl_FragCoord.x), 5.0 ) );\
int Vr = int(col);\
int Vg = int(col) + 1;\
int Vb = int(col) + 2;\
if (Vg >= 5) Vg -= 5;\
if (Vb >= 5) Vb -= 5;\
getTextureSample(Vr, color); \
gl_FragColor.r = color.r;\
getTextureSample(Vg, color); \
gl_FragColor.g = color.g;\
getTextureSample(Vb, color); \
gl_FragColor.b = color.b;\
}";
static char *glsl_view_8VAlio = GLSL_PREFIX "\
uniform sampler2D gfView1; \
uniform sampler2D gfView2; \
uniform sampler2D gfView3; \
uniform sampler2D gfView4; \
uniform sampler2D gfView5; \
uniform sampler2D gfView6; \
uniform sampler2D gfView7; \
uniform sampler2D gfView8; \
varying vec2 TexCoord;\
\
void getTextureSample(in int texID, out vec4 color) { \
if (texID == 0 ) color = texture2D(gfView1, TexCoord.st); \
else if (texID == 1 ) color = texture2D(gfView2, TexCoord.st); \
else if (texID == 2 ) color = texture2D(gfView3, TexCoord.st); \
else if (texID == 3 ) color = texture2D(gfView4, TexCoord.st); \
else if (texID == 4 ) color = texture2D(gfView5, TexCoord.st); \
else if (texID == 5 ) color = texture2D(gfView6, TexCoord.st); \
else if (texID == 6 ) color = texture2D(gfView7, TexCoord.st); \
else if (texID == 7 ) color = texture2D(gfView8, TexCoord.st); \
} \
\
void main() \
{ \
int x = int(gl_FragCoord.x + 0.5); \
int y = int(gl_FragCoord.y + 0.5); \
int modulox = x/8; \
int moduloy = y/8; \
modulox = x - 8 * modulox; \
moduloy = y - 8 * moduloy; \
\
int viewLine = 7 - moduloy; \
int viewPix = viewLine + 3 * modulox; \
int viewR = viewPix - 8*(viewPix/8); \
int viewG = viewPix + 1 - 8*((viewPix +1)/8); \
int viewB = viewPix + 2 - 8*((viewPix +2)/8); \
\
vec4 color; \
getTextureSample(viewR, color); \
gl_FragColor.r = color.r; \
getTextureSample(viewG, color); \
gl_FragColor.g = color.g; \
getTextureSample(viewB, color); \
gl_FragColor.b = color.b; \
}";
/**
parses (glShaderSource) and compiles (glCompileShader) shader source
\return GF_TRUE if successful
*/
Bool visual_3d_compile_shader(GF_SHADERID shader_id, const char *name, const char *source)
{
GLint blen = 0;
GLsizei slen = 0;
s32 len;
GLint is_compiled=0;
if (!source || !shader_id) return 0;
len = (u32) strlen(source);
glShaderSource(shader_id, 1, &source, &len);
glCompileShader(shader_id);
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &is_compiled);
if (is_compiled == GL_TRUE) return GF_TRUE;
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH , &blen);
if (blen > 1) {
char* compiler_log = (char*) gf_malloc(blen);
#ifdef CONFIG_DARWIN_GL
glGetInfoLogARB((GLhandleARB) shader_id, blen, &slen, compiler_log);
#elif defined(GPAC_USE_GLES2)
glGetShaderInfoLog(shader_id, blen, &slen, compiler_log);
#else
glGetInfoLogARB(shader_id, blen, &slen, compiler_log);
#endif
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[GLSL] Failed to compile %s shader: %s\n", name, compiler_log));
GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[GLSL] ***** faulty shader code ****\n%s\n**********************\n", source));
gf_free (compiler_log);
return 0;
}
return 1;
}
static GF_SHADERID visual_3d_shader_from_source_file(const char *src_path, u32 shader_type)
{
GF_SHADERID shader = 0;
u32 size;
char *shader_src;
GF_Err e = gf_file_load_data(src_path, (u8 **) &shader_src, &size);
if (e) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to open shader file %s: %s\n", src_path, gf_error_to_string(e) ));
} else {
shader = glCreateShader(shader_type);
if (visual_3d_compile_shader(shader, (shader_type == GL_FRAGMENT_SHADER) ? "fragment" : "vertex", shader_src)==GF_FALSE) {
glDeleteShader(shader);
shader = 0;
}
gf_free(shader_src);
}
return shader;
}
#ifdef GPAC_CONFIG_IOS
#include <errno.h>
#include <sys/sysctl.h>
#endif
static GF_SHADERID visual_3d_shader_with_flags(const char *src_path, u32 shader_type, u32 flags, u32 pixfmt) {
GF_SHADERID shader = 0;
char *defs, szKey[100];
defs = gf_strdup(GLES_VERSION_STRING);
if (flags & GF_GL_HAS_LIGHT) {
sprintf(szKey, "#define GF_GL_HAS_LIGHT\n#define LIGHTS_MAX %d\n", GF_MAX_GL_LIGHTS);
gf_dynstrcat(&defs, szKey, NULL);
}
if (flags & GF_GL_HAS_COLOR) {
gf_dynstrcat(&defs, "#define GF_GL_HAS_COLOR \n", NULL);
}
if (flags & GF_GL_HAS_TEXTURE) {
gf_dynstrcat(&defs, "#define GF_GL_HAS_TEXTURE \n", NULL);
}
if (flags & GF_GL_HAS_CLIP) {
/*clipping is always enabled*/
sprintf(szKey, "#define CLIPS_MAX %d\n#define GF_GL_HAS_CLIP\n", GF_MAX_GL_CLIPS);
gf_dynstrcat(&defs, szKey, NULL);
}
if (shader_type==GL_FRAGMENT_SHADER) {
if (flags & GF_GL_IS_YUV) {
gf_dynstrcat(&defs, "#define GF_GL_IS_YUV \n", NULL);
}
if (flags & GF_GL_IS_ExternalOES) {
gf_dynstrcat(&defs, "#define GF_GL_IS_ExternalOES \n", NULL);
}
}
char *shader_src;
u32 size;
GF_Err e = gf_file_load_data(src_path ,(u8 **) &shader_src, &size);
if (e) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to open shader file %s: %s\n", src_path, gf_error_to_string(e)));
} else {
char *final_src = NULL;
gf_dynstrcat(&final_src, defs, NULL);
if ((shader_type==GL_FRAGMENT_SHADER) && (flags & GF_GL_HAS_TEXTURE)) {
char *sep = strstr(shader_src, "void main(");
if (sep) sep[0] = 0;
gf_dynstrcat(&final_src, shader_src, NULL);
//add texture code
gf_gl_txw_insert_fragment_shader(pixfmt, "maintx", &final_src, GF_FALSE);
//append the rest
if (sep) {
sep[0] = 'v';
gf_dynstrcat(&final_src, sep, "\n");
}
} else {
gf_dynstrcat(&final_src, shader_src, "\n");
}
shader = glCreateShader(shader_type);
if (visual_3d_compile_shader(shader, (shader_type == GL_FRAGMENT_SHADER) ? "fragment" : "vertex", final_src)==GF_FALSE) {
glDeleteShader(shader);
shader = 0;
}
gf_free(shader_src);
gf_free(final_src);
gf_free(defs);
}
return shader;
}
void visual_3d_init_stereo_shaders(GF_VisualManager *visual)
{
GLint linked;
Bool res;
if (!visual->compositor->gl_caps.has_shaders) return;
if (visual->autostereo_glsl_program) return;
visual->autostereo_glsl_program = glCreateProgram();
res = GF_TRUE;
if (!visual->base_glsl_vertex) {
visual->base_glsl_vertex = glCreateShader(GL_VERTEX_SHADER);
res = visual_3d_compile_shader(visual->base_glsl_vertex, "vertex", glsl_autostereo_vertex);
}
if (res) {
switch (visual->autostereo_type) {
case GF_3D_STEREO_COLUMNS:
visual->autostereo_glsl_fragment = glCreateShader(GL_FRAGMENT_SHADER);
res = visual_3d_compile_shader(visual->autostereo_glsl_fragment, "fragment", glsl_view_columns);
break;
case GF_3D_STEREO_ROWS:
visual->autostereo_glsl_fragment = glCreateShader(GL_FRAGMENT_SHADER);
res = visual_3d_compile_shader(visual->autostereo_glsl_fragment, "fragment", glsl_view_rows);
break;
case GF_3D_STEREO_ANAGLYPH:
visual->autostereo_glsl_fragment = glCreateShader(GL_FRAGMENT_SHADER);
res = visual_3d_compile_shader(visual->autostereo_glsl_fragment, "fragment", glsl_view_anaglyph);
break;
case GF_3D_STEREO_5VSP19:
visual->autostereo_glsl_fragment = glCreateShader(GL_FRAGMENT_SHADER);
res = visual_3d_compile_shader(visual->autostereo_glsl_fragment, "fragment", glsl_view_5VSP19);
break;
case GF_3D_STEREO_8VALIO:
visual->autostereo_glsl_fragment = glCreateShader(GL_FRAGMENT_SHADER);
res = visual_3d_compile_shader(visual->autostereo_glsl_fragment, "fragment", glsl_view_8VAlio);
break;
case GF_3D_STEREO_CUSTOM:
visual->autostereo_glsl_fragment = visual_3d_shader_from_source_file(visual->compositor->mvshader, GL_FRAGMENT_SHADER);
if (visual->autostereo_glsl_fragment) res = GF_TRUE;
break;
}
}
if (res) {
glAttachShader(visual->autostereo_glsl_program, visual->base_glsl_vertex);
glAttachShader(visual->autostereo_glsl_program, visual->autostereo_glsl_fragment);
glLinkProgram(visual->autostereo_glsl_program);
glGetProgramiv(visual->autostereo_glsl_program, GL_LINK_STATUS, &linked);
if (!linked) {
int i32CharsWritten, i32InfoLogLength;
char pszInfoLog[2048];
glGetProgramiv(visual->autostereo_glsl_program, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
glGetProgramInfoLog(visual->autostereo_glsl_program, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, (pszInfoLog));
res = GF_FALSE;
}
GL_CHECK_ERR()
}
if (!res) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[V3D:GLSL] Autostereo vertex shader failed - disabling stereo support\n"));
visual->autostereo_type = 0;
visual->nb_views = 1;
}
}
#define DEL_SHADER(_a) if (_a) { glDeleteShader(_a); _a = 0; }
#define DEL_PROGRAM(_a) if (_a) { glDeleteProgram(_a); _a = 0; }
#if 0
static GLint gf_glGetUniformLocation(GF_SHADERID glsl_program, const char *uniform_name)
{
GLint loc = glGetUniformLocation(glsl_program, uniform_name);
if (loc<0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[V3D:GLSL] Cannot find uniform \"%s\" in GLSL program\n", uniform_name));
}
return loc;
}
#endif
#if 0
static GLint gf_glGetAttribLocation(GF_SHADERID glsl_program, const char *attrib_name)
{
GLint loc = glGetAttribLocation(glsl_program, attrib_name);
if (loc<0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[V3D:GLSL] Cannot find attrib \"%s\" in GLSL program\n", attrib_name));
}
return loc;
}
#endif
//The following functions were used for shader testing [in ES2.0]
#if 0
static void gf_glQueryProgram(GF_SHADERID progObj)
{
GLint err_log = -10;
GL_CHECK_ERR()
glValidateProgram(progObj);
GL_CHECK_ERR()
glGetProgramiv(progObj, GL_VALIDATE_STATUS, &err_log);
printf("GL_VALIDATE_STATUS: %d \n ",err_log);
glGetProgramiv(progObj, GL_LINK_STATUS, &err_log);
printf("GL_LINK_STATUS: %d \n ",err_log);
glGetProgramiv(progObj, GL_ATTACHED_SHADERS, &err_log);
printf("GL_ATTACHED_SHADERS: %d \n ",err_log);
glGetProgramiv(progObj, GL_ACTIVE_UNIFORMS, &err_log);
printf("GL_ACTIVE_UNIFORMS: %d \n ",err_log);
}
static void gf_glQueryUniform(GF_SHADERID progObj, const char *name, int index)
{
GLint loc, i;
GLfloat res[16];
loc = glGetUniformLocation(progObj, name);
if(loc<0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("failed to locate uniform. exiting\n"));
return;
}
glGetUniformfv(progObj, loc, (GLfloat *) res);
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("uniform %s has value of: ", name));
for (i =0; i<index; i++)
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("%f ", res[i]));
}
//same here
static void gf_glQueryAttrib(GF_SHADERID progObj, const char *name, int index, GLenum param)
{
GLint loc, i;
GLfloat res[16];
loc = glGetAttribLocation(progObj, name);
if (loc<0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("failed to locate attribute. exiting\n"));
return;
}
glGetVertexAttribfv(loc, param, (GLfloat *) res);
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("attribute %s has value of: ", name));
for( i =0; i<index; i++)
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("%f ", res[i]));
}
static void gf_glQueryUniforms(GF_SHADERID progObj)
{
GLint maxUniformLen;
GLint numUniforms;
char *uniformName;
GLint index;
glGetProgramiv(progObj, GL_ACTIVE_UNIFORMS, &numUniforms);
glGetProgramiv(progObj, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen);
uniformName = gf_malloc(sizeof(char) * maxUniformLen);
for(index = 0; index < numUniforms; index++) {
GLint size;
GLenum type;
GLint location;
// Get the Uniform Info
glGetActiveUniform(progObj, index, maxUniformLen, NULL, &size, &type, uniformName);
// Get the uniform location
location = glGetUniformLocation(progObj, uniformName);
if(location) printf("uniform %s is: ",uniformName);
switch(type) {
case GL_FLOAT:
printf("float \n");
break;
case GL_FLOAT_VEC2:
printf("floatvec2 \n");
break;
case GL_FLOAT_VEC3:
printf("floatvec3 \n");
break;
case GL_FLOAT_VEC4:
printf("floatvec4 \n");
break;
case GL_INT:
printf("int \n");
break;
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
printf("intVec \n");
break;
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
printf("fmat \n");
break;
case GL_SAMPLER_2D:
printf("samp2D \n");
break;
case GL_SAMPLER_CUBE:
printf("sampCube \n");
break;
default:
printf("other \n");
break;
}
}
gf_free(uniformName);
}
static void gf_glQueryAttributes(GF_SHADERID progObj)
{
GLint maxAttributeLen;
GLint numAttributes;
char *attributeName;
GLint index;
printf("Listing Attribs... \n");
glGetProgramiv(progObj, GL_ACTIVE_ATTRIBUTES, &numAttributes);
glGetProgramiv(progObj, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttributeLen);
attributeName = gf_malloc(sizeof(char) * maxAttributeLen);
for(index = 0; index < numAttributes; index++) {
GLint size;
GLenum type;
GLint location;
// Get the Attribute Info
glGetActiveAttrib(progObj, index, maxAttributeLen, NULL, &size, &type, attributeName);
// Get the attribute location
location = glGetAttribLocation(progObj, attributeName);
if(location) printf("attrib %s is: ",attributeName);
switch(type) {
case GL_FLOAT:
printf("float \n");
break;
case GL_FLOAT_VEC2:
printf("floatvec2 \n");
break;
case GL_FLOAT_VEC3:
printf("floatvec3 \n");
break;
case GL_FLOAT_VEC4:
printf("floatvec4 \n");
break;
case GL_INT:
printf("int \n");
break;
case GL_INT_VEC2:
case GL_INT_VEC3:
case GL_INT_VEC4:
printf("intVec \n");
break;
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3:
case GL_FLOAT_MAT4:
printf("fmat \n");
break;
case GL_SAMPLER_2D:
printf("samp2D \n");
break;
case GL_SAMPLER_CUBE:
printf("sampCube \n");
break;
default:
printf("other \n");
break;
}
}
gf_free(attributeName);
}
#endif
static GF_GLProgInstance *visual_3d_build_program(GF_VisualManager *visual, u32 flags, u32 pix_fmt)
{
s32 linked;
GF_GLProgInstance *pi;
GF_SAFEALLOC(pi, GF_GLProgInstance);
if (!pi) return NULL;
pi->flags = flags;
pi->pix_fmt = pix_fmt;
glGetError();
pi->prog = glCreateProgram();
if (!pi->prog) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to create program\n"));
gf_free(pi);
return NULL;
}
pi->vertex = visual_3d_shader_with_flags(visual->compositor->vertshader , GL_VERTEX_SHADER, flags, 0);
if (!pi->vertex) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to compile vertex shader\n"));
DEL_PROGRAM(pi->prog)
gf_free(pi);
return NULL;
}
if (pix_fmt == GF_PIXEL_GL_EXTERNAL) {
flags |= GF_GL_IS_ExternalOES;
}
pi->fragment = visual_3d_shader_with_flags(visual->compositor->fragshader , GL_FRAGMENT_SHADER, flags, pix_fmt);
if (!pi->fragment) {
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to compile fragment shader\n"));
DEL_PROGRAM(pi->prog)
DEL_SHADER(pi->vertex);
gf_free(pi);
return NULL;
}
glAttachShader(pi->prog, pi->vertex);
GL_CHECK_ERR();
glAttachShader(pi->prog, pi->fragment);
GL_CHECK_ERR();
glLinkProgram(pi->prog);
GL_CHECK_ERR();
glGetProgramiv(pi->prog, GL_LINK_STATUS, &linked);
if (!linked) {
int i32CharsWritten, i32InfoLogLength;
char pszInfoLog[2048];
glGetProgramiv(pi->prog, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
glGetProgramInfoLog(pi->prog, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, (pszInfoLog));
DEL_PROGRAM(pi->prog)
DEL_SHADER(pi->vertex);
DEL_SHADER(pi->fragment);
gf_free(pi);
return NULL;
}
GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] fragment shader compiled fine\n"));
return pi;
}
static Bool visual_3d_init_generic_shaders(GF_VisualManager *visual)
{
GF_GLProgInstance *pi;
if (gf_list_count(visual->compiled_programs))
return GF_TRUE;
if (!gf_file_exists(visual->compositor->vertshader))
return GF_FALSE;
if (!gf_file_exists(visual->compositor->fragshader))
return GF_FALSE;
pi = visual_3d_build_program(visual, 0, 0);
if (!pi) return GF_FALSE;
glDeleteShader(pi->vertex);
glDeleteShader(pi->fragment);
glDeleteProgram(pi->prog);
gf_free(pi);
return GF_TRUE;
}
void visual_3d_init_shaders(GF_VisualManager *visual)
{
if (visual->compositor->visual != visual)
return;
if (!visual->compositor->gl_caps.has_shaders)
return;
if (!visual->compositor->shader_mode_disabled) {
//If we fail to configure shaders, force 2D mode
if (! visual_3d_init_generic_shaders(visual)) {
visual->compositor->hybrid_opengl = GF_FALSE;
visual->compositor->force_opengl_2d = GF_FALSE;
/*force resetup*/
visual->compositor->root_visual_setup = 0;
/*force texture setup when switching to OpenGL*/
gf_sc_reset_graphics(visual->compositor);
/*force redraw*/
gf_sc_next_frame_state(visual->compositor, GF_SC_DRAW_FRAME);
}
}
}
#endif // !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
void visual_3d_reset_graphics(GF_VisualManager *visual)
{
#if !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
if (visual->compositor->visual != visual)
return;
DEL_SHADER(visual->base_glsl_vertex);
DEL_SHADER(visual->autostereo_glsl_fragment);
DEL_PROGRAM(visual->autostereo_glsl_program );
if (visual->gl_textures) {
glDeleteTextures(visual->nb_views, visual->gl_textures);
gf_free(visual->gl_textures);
visual->gl_textures = NULL;
}
if (visual->autostereo_mesh) {
mesh_free(visual->autostereo_mesh);
visual->autostereo_mesh = NULL;
}
while (gf_list_count(visual->compiled_programs)) {
GF_GLProgInstance *gi = gf_list_pop_back(visual->compiled_programs);
DEL_SHADER(gi->vertex);
DEL_SHADER(gi->fragment);
DEL_PROGRAM(gi->prog);
gf_free(gi);
}
#endif
}
void visual_3d_set_clipper_scissor(GF_VisualManager *visual, GF_TraverseState *tr_state)
{
#ifndef GPAC_USE_TINYGL
if (visual->has_clipper_2d) {
u32 x, y;
u32 dw, dh;
glEnable(GL_SCISSOR_TEST);
if (visual->offscreen) {
dw = visual->width;
dh = visual->height;
} else {
dw = visual->compositor->display_width;
dh = visual->compositor->display_height;
}
if (visual->center_coords) {
x = visual->clipper_2d.x + dw / 2;
y = dh / 2 + visual->clipper_2d.y - visual->clipper_2d.height;
} else {
x = visual->clipper_2d.x;
y = dh - visual->clipper_2d.y;
}
glScissor(x, y, visual->clipper_2d.width, visual->clipper_2d.height);
} else {
glDisable(GL_SCISSOR_TEST);
}
#endif
}
#if !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
static void visual_3d_load_matrix_shaders(GF_SHADERID program, Fixed *mat, const char *name)
{
GLint loc;
#ifdef GPAC_FIXED_POINT
Float _mat[16];
u32 i;
#endif
loc = glGetUniformLocation(program, name);
if(loc<0) {
GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("GL Error (file %s line %d): Invalid matrix name", __FILE__, __LINE__));
return;
}
GL_CHECK_ERR()
#ifdef GPAC_FIXED_POINT
for (i=0; i<16; i++) _mat[i] = FIX2FLT(mat[i]);
glUniformMatrix4fv(loc, 1, GL_FALSE, (GLfloat *) _mat);
#else
glUniformMatrix4fv(loc, 1, GL_FALSE, mat);
#endif
GL_CHECK_ERR()
}
#endif
GF_Err visual_3d_init_autostereo(GF_VisualManager *visual)
{
#if !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
u32 bw, bh;
SFVec2f s;
Bool use_npot = visual->compositor->gl_caps.npot_texture;
if (visual->gl_textures) return GF_OK;
visual->gl_textures = gf_malloc(sizeof(GLuint) * visual->nb_views);
glGenTextures(visual->nb_views, visual->gl_textures);
bw = visual->width;
bh = visual->height;
/*main (not offscreen) visual*/
if (visual->compositor->visual==visual) {
bw = visual->compositor->output_width;
bh = visual->compositor->output_height;
}
#ifdef GPAC_USE_GLES2
use_npot = GF_TRUE;
#endif
if (use_npot) {
visual->auto_stereo_width = bw;
visual->auto_stereo_height = bh;
} else {
visual->auto_stereo_width = 2;
while (visual->auto_stereo_width < bw) visual->auto_stereo_width *= 2;
visual->auto_stereo_height = 2;
while (visual->auto_stereo_height < bh) visual->auto_stereo_height *= 2;
}
visual->autostereo_mesh = new_mesh();
s.x = INT2FIX(bw);
s.y = INT2FIX(bh);
mesh_new_rectangle(visual->autostereo_mesh, s, NULL, 0);
if (! use_npot) {
u32 i;
Fixed max_u = INT2FIX(bw) / visual->auto_stereo_width;
Fixed max_v = INT2FIX(bh) / visual->auto_stereo_height;
for (i=0; i<visual->autostereo_mesh->v_count; i++) {
if (visual->autostereo_mesh->vertices[i].texcoords.x == FIX_ONE) {
visual->autostereo_mesh->vertices[i].texcoords.x = max_u;
}
if (visual->autostereo_mesh->vertices[i].texcoords.y == FIX_ONE) {
visual->autostereo_mesh->vertices[i].texcoords.y = max_v;
}
}
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Visual3D] AutoStereo initialized - width %d height %d\n", visual->auto_stereo_width, visual->auto_stereo_height) );
visual_3d_init_stereo_shaders(visual);
#endif // !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
return GF_OK;
}
void visual_3d_end_auto_stereo_pass(GF_VisualManager *visual)
{
#if !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
u32 i;
GLint loc, loc_vertex_attrib, loc_texcoord_attrib;
Fixed hw, hh;
GF_Matrix mx;
glFlush();
GL_CHECK_ERR()
#ifndef GPAC_USE_GLES2
glEnable(GL_TEXTURE_2D);
#endif
glBindTexture(GL_TEXTURE_2D, visual->gl_textures[visual->current_view]);
#ifndef GPAC_USE_GLES2
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
#else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
#endif
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, visual->auto_stereo_width, visual->auto_stereo_height, 0);
GL_CHECK_ERR()
#ifndef GPAC_USE_GLES2
glDisable(GL_TEXTURE_2D);
#endif
glClear(GL_DEPTH_BUFFER_BIT);
GL_CHECK_ERR()
if (visual->current_view+1<visual->nb_views) return;
hw = INT2FIX(visual->width);
hh = INT2FIX(visual->height);
/*main (not offscreen) visual*/
if (visual->compositor->visual==visual) {
hw = INT2FIX(visual->compositor->output_width);
hh = INT2FIX(visual->compositor->output_height);
}
glViewport(0, 0, (GLsizei) hw, (GLsizei) hh );
hw /= 2;
hh /= 2;
/*use our program*/
glUseProgram(visual->autostereo_glsl_program);
//load projection
gf_mx_ortho(&mx, -hw, hw, -hh, hh, -10, 100);
visual_3d_load_matrix_shaders(visual->autostereo_glsl_program, mx.m, "gfProjectionMatrix");
//no need for modelview (identifty)
/*push number of views if shader uses it*/
loc = glGetUniformLocation(visual->autostereo_glsl_program, "gfViewCount");
if (loc != -1) glUniform1i(loc, visual->nb_views);
loc_texcoord_attrib = -1;
//setup vertex attrib
loc_vertex_attrib = glGetAttribLocation(visual->autostereo_glsl_program, "gfVertex");
if (loc_vertex_attrib>=0) {
glVertexAttribPointer(loc_vertex_attrib, 3, GL_FLOAT, GL_FALSE, sizeof(GF_Vertex), &visual->autostereo_mesh->vertices[0].pos);
glEnableVertexAttribArray(loc_vertex_attrib);
GL_CHECK_ERR()
//setup texcoord location
loc_texcoord_attrib = glGetAttribLocation(visual->autostereo_glsl_program, "gfTextureCoordinates");
if (loc_texcoord_attrib>=0) {
char szTex[100];
glVertexAttribPointer(loc_texcoord_attrib, 2, GL_FLOAT, GL_FALSE, sizeof(GF_Vertex), &visual->autostereo_mesh->vertices[0].texcoords);
glEnableVertexAttribArray(loc_texcoord_attrib);
GL_CHECK_ERR()
/*bind all our textures*/
for (i=0; i<visual->nb_views; i++) {
sprintf(szTex, "gfView%d", i+1);
loc = glGetUniformLocation(visual->autostereo_glsl_program, szTex);
if (loc == -1) continue;
glActiveTexture(GL_TEXTURE0 + i);
#ifndef GPAC_USE_GLES2
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
#endif
GL_CHECK_ERR()
glBindTexture(GL_TEXTURE_2D, visual->gl_textures[i]);
GL_CHECK_ERR()
glUniform1i(loc, i);
GL_CHECK_ERR()
}
//draw
#if defined(GPAC_USE_GLES2)
glDrawElements(GL_TRIANGLES, visual->autostereo_mesh->i_count, GL_UNSIGNED_SHORT, visual->autostereo_mesh->indices);
#else
glDrawElements(GL_TRIANGLES, visual->autostereo_mesh->i_count, GL_UNSIGNED_INT, visual->autostereo_mesh->indices);
#endif
GL_CHECK_ERR()
}
}
if (loc_vertex_attrib>=0) glDisableVertexAttribArray(loc_vertex_attrib);
if (loc_texcoord_attrib>=0) glDisableVertexAttribArray(loc_texcoord_attrib);
GL_CHECK_ERR()
glUseProgram(0);
#ifndef GPAC_USE_GLES2
/*not sure why this is needed but it prevents a texturing bug on XP on parallels*/
glActiveTexture(GL_TEXTURE0);
GL_CHECK_ERR()
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
#endif
GL_CHECK_ERR()
#endif // !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
}
static void visual_3d_setup_quality(GF_VisualManager *visual)
{
#ifndef GPAC_USE_GLES2
if (visual->compositor->fast) {
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
#ifdef GL_POLYGON_SMOOTH_HINT
glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
#endif
} else {
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
#ifdef GL_POLYGON_SMOOTH_HINT
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
#endif
}
if (visual->compositor->aa == GF_ANTIALIAS_FULL) {
glEnable(GL_LINE_SMOOTH);
#ifndef GPAC_USE_GLES1X
if (visual->compositor->paa)
glEnable(GL_POLYGON_SMOOTH);
else
glDisable(GL_POLYGON_SMOOTH);
#endif
} else {
glDisable(GL_LINE_SMOOTH);
#ifndef GPAC_USE_GLES1X
glDisable(GL_POLYGON_SMOOTH);
#endif
}
#endif
}
void visual_3d_setup(GF_VisualManager *visual)
{
//in non-player mode, we might not be the only ones using the gl context !!
if (visual->compositor->player && visual->gl_setup) {
visual->has_fog = GF_FALSE;
glClear(GL_DEPTH_BUFFER_BIT);
return;
}
#ifndef GPAC_USE_TINYGL
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LEQUAL);
#endif
glEnable(GL_DEPTH_TEST);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
#ifdef GPAC_USE_GLES1X
glClearDepthx(FIX_ONE);
glLightModelx(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, FLT2FIX(0.2f * 128) );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#elif defined(GPAC_USE_GLES2)
glClearDepthf(1.0f);
#else
glClearDepth(1.0f);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_FALSE);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, (float) (0.2 * 128));
#endif
#ifndef GPAC_USE_GLES2
glDisable(GL_TEXTURE_2D);
glShadeModel(GL_SMOOTH);
glGetIntegerv(GL_MAX_LIGHTS, (GLint*)&visual->max_lights);
if (visual->max_lights>GF_MAX_GL_LIGHTS)
visual->max_lights=GF_MAX_GL_LIGHTS;
#ifdef GL_MAX_CLIP_PLANES
glGetIntegerv(GL_MAX_CLIP_PLANES, (GLint*)&visual->max_clips);
if (visual->max_clips>GF_MAX_GL_CLIPS)
visual->max_clips=GF_MAX_GL_CLIPS;
#endif
glDisable(GL_POINT_SMOOTH);
glDisable(GL_COLOR_MATERIAL);
glDisable(GL_LIGHTING);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
glDisable(GL_FOG);
/*Note: we cannot enable/disable normalization on the fly, because we have no clue when the GL implementation
will actually compute the related fragments. Since a typical world always use scaling, we always turn normalization on
to avoid tracking scale*/
glEnable(GL_NORMALIZE);
#endif //GLES2
visual_3d_setup_quality(visual);
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
visual->has_fog = GF_FALSE;
visual->max_lights=GF_MAX_GL_LIGHTS;
visual->max_clips=GF_MAX_GL_CLIPS;
visual->gl_setup = GF_TRUE;
glClear(GL_DEPTH_BUFFER_BIT);
}
void visual_3d_set_background_state(GF_VisualManager *visual, Bool on)
{
#ifndef GPAC_USE_GLES2
if (on) {
glDisable(GL_LIGHTING);
glDisable(GL_FOG);
glDisable(GL_LINE_SMOOTH);
glDisable(GL_BLEND);
#ifndef GPAC_USE_GLES1X
glDisable(GL_POLYGON_SMOOTH);
#endif
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
} else {
visual_3d_setup_quality(visual);
}
#endif
visual_3d_enable_depth_buffer(visual, ! on);
}
void visual_3d_enable_antialias(GF_VisualManager *visual, Bool bOn)
{
#ifndef GPAC_USE_GLES2
if (bOn) {
glEnable(GL_LINE_SMOOTH);
#ifndef GPAC_USE_GLES1X
if (visual->compositor->paa)
glEnable(GL_POLYGON_SMOOTH);
else
glDisable(GL_POLYGON_SMOOTH);
#endif
} else {
glDisable(GL_LINE_SMOOTH);
#ifndef GPAC_USE_GLES1X
glDisable(GL_POLYGON_SMOOTH);
#endif
/* glDisable(GL_DITHER);
glDisable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH, GL_DONT_CARE);
glHint(GL_LINE_SMOOTH, GL_DONT_CARE);
glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
glDisable( GL_MULTISAMPLE_ARB);
*/
}
#endif
}
void visual_3d_enable_depth_buffer(GF_VisualManager *visual, Bool on)
{
if (on) {
glEnable(GL_DEPTH_TEST);
} else {
glDisable(GL_DEPTH_TEST);
}
}
void visual_3d_set_viewport(GF_VisualManager *visual, GF_Rect vp)
{
glViewport(FIX2INT(vp.x), FIX2INT(vp.y), FIX2INT(vp.width), FIX2INT(vp.height));
}
void visual_3d_set_scissor(GF_VisualManager *visual, GF_Rect *vp)
{
#ifndef GPAC_USE_TINYGL
if (vp) {
glEnable(GL_SCISSOR_TEST);
glScissor(FIX2INT(vp->x), FIX2INT(vp->y), FIX2INT(vp->width), FIX2INT(vp->height));
} else {
glDisable(GL_SCISSOR_TEST);
}
#endif
}
void visual_3d_clear_depth(GF_VisualManager *visual)
{
glClear(GL_DEPTH_BUFFER_BIT);
}
static void visual_3d_draw_aabb_node(GF_TraverseState *tr_state, GF_Mesh *mesh, u32 prim_type, GF_Plane *fplanes, u32 *p_indices, AABBNode *n, void *idx_addr)
{
u32 i;
/*if not leaf do cull*/
if (n->pos) {
u32 p_idx, cull;
SFVec3f vertices[8];
/*get box vertices*/
gf_bbox_get_vertices(n->min, n->max, vertices);
cull = CULL_INSIDE;
for (i=0; i<6; i++) {
p_idx = p_indices[i];
/*check p-vertex: if not in plane, we're out (since p-vertex is the closest point to the plane)*/
if (gf_plane_get_distance(&fplanes[i], &vertices[p_idx])<0) {
cull = CULL_OUTSIDE;
break;
}
/*check n-vertex: if not in plane, we're intersecting*/
if (gf_plane_get_distance(&fplanes[i], &vertices[7-p_idx])<0) {
cull = CULL_INTERSECTS;
break;
}
}
if (cull==CULL_OUTSIDE) return;
if (cull==CULL_INTERSECTS) {
visual_3d_draw_aabb_node(tr_state, mesh, prim_type, fplanes, p_indices, n->pos, idx_addr);
visual_3d_draw_aabb_node(tr_state, mesh, prim_type, fplanes, p_indices, n->neg, idx_addr);
return;
}
}
/*the good thing about the structure of the aabb tree is that the primitive idx is valid for both
leaf and non-leaf nodes, so we can use it as soon as we have a CULL_INSIDE.
However we must push triangles one by one since primitive order may have been swapped when
building the AABB tree*/
for (i=0; i<n->nb_idx; i++) {
u32 idx = 3*n->indices[i];
void *vbi_addr;
if (!idx_addr) vbi_addr = (void *) PTR_TO_U_CAST ( sizeof(IDX_TYPE) * idx );
else vbi_addr = &mesh->indices[idx];
#if defined(GPAC_USE_GLES1X) || defined(GPAC_USE_GLES2)
glDrawElements(prim_type, 3, GL_UNSIGNED_SHORT, vbi_addr);
#else
glDrawElements(prim_type, 3, GL_UNSIGNED_INT, vbi_addr);
#endif
}
}
#ifndef GPAC_USE_GLES2
static void visual_3d_matrix_load(GF_VisualManager *visual, Fixed *mat)
{
#if defined(GPAC_FIXED_POINT)
Float _mat[16];
u32 i;
#endif
if (!mat) {
glLoadIdentity();
return;
}
#if defined(GPAC_USE_GLES1X) && defined(GPAC_FIXED_POINT)
glLoadMatrixx(mat);
#elif defined(GPAC_FIXED_POINT)
for (i=0; i<16; i++) _mat[i] = FIX2FLT(mat[i]);
glLoadMatrixf(_mat);
#else
glLoadMatrixf(mat);
#endif
}
static void visual_3d_update_matrices(GF_TraverseState *tr_state)
{
GF_Matrix mx;
if (!tr_state || !tr_state->camera) return;
if (tr_state->visual->needs_projection_matrix_reload) {
tr_state->visual->needs_projection_matrix_reload = 0;
glMatrixMode(GL_PROJECTION);
visual_3d_matrix_load(tr_state->visual, tr_state->camera->projection.m);
glMatrixMode(GL_MODELVIEW);
}
gf_mx_copy(mx, tr_state->camera->modelview.m);
gf_mx_add_matrix(&mx, &tr_state->model_matrix);
visual_3d_matrix_load(tr_state->visual, (Fixed *) &mx);
}
static void visual_3d_set_clippers(GF_VisualManager *visual, GF_TraverseState *tr_state)
{
#ifdef GL_MAX_CLIP_PLANES
u32 i;
GF_Matrix inv_mx;
if (!visual->num_clips)
return;
gf_mx_copy(inv_mx, tr_state->model_matrix);
gf_mx_inverse(&inv_mx);
for (i=0; i<visual->num_clips; i++) {
u32 idx = GL_CLIP_PLANE0 + i;
#ifdef GPAC_USE_GLES1X
Fixed g[4];
#else
Double g[4];
#endif
GF_Matrix mx;
GF_Plane p = visual->clippers[i].p;
if (visual->clippers[i].is_2d_clip) {
visual_3d_matrix_load(tr_state->visual, tr_state->camera->modelview.m);
} else {
gf_mx_copy(mx, inv_mx);
if (visual->clippers[i].mx_clipper != NULL) {
gf_mx_add_matrix(&mx, visual->clippers[i].mx_clipper);
}
gf_mx_apply_plane(&mx, &p);
}
#if defined(GPAC_USE_GLES1X) && defined(GPAC_FIXED_POINT)
g[0] = p.normal.x;
g[1] = p.normal.y;
g[2] = p.normal.z;
g[3] = p.d;
glClipPlanex(idx, g);
#else
g[0] = FIX2FLT(p.normal.x);
g[1] = FIX2FLT(p.normal.y);
g[2] = FIX2FLT(p.normal.z);
g[3] = FIX2FLT(p.d);
#if defined(GPAC_USE_GLES1X)
glClipPlanef(idx, g);
#else
glClipPlane(idx, g);
#endif
#endif
glEnable(idx);
if (visual->clippers[i].is_2d_clip) {
visual_3d_update_matrices(tr_state);
}
}
#endif
}
static void visual_3d_reset_clippers(GF_VisualManager *visual)
{
#ifdef GL_MAX_CLIP_PLANES
u32 i;
for (i=0; i<visual->num_clips; i++) {
glDisable(GL_CLIP_PLANE0 + i);
}
#endif
}
void visual_3d_reset_lights(GF_VisualManager *visual)
{
u32 i;
if (!visual->num_lights) return;
for (i=0; i<visual->num_lights; i++) {
glDisable(GL_LIGHT0 + i);
}
glDisable(GL_LIGHTING);
}
static void visual_3d_set_lights(GF_VisualManager *visual)
{
u32 i;
#if defined(GPAC_USE_GLES1X) && defined(GPAC_FIXED_POINT)
Fixed vals[4], exp;
#else
Float vals[4], intensity, cutOffAngle, beamWidth, ambientIntensity, exp;
#endif
if (!visual->num_lights) return;
for (i=0; i<visual->num_lights; i++) {
GF_Matrix mx;
GF_LightInfo *li = &visual->lights[i];
GLint iLight = GL_LIGHT0 + i;
if(li->type==3) {
gf_mx_init(mx);
} else {
gf_mx_copy(mx, visual->camera.modelview.m);
gf_mx_add_matrix(&mx, &li->light_mx);
}
visual_3d_matrix_load(visual, mx.m);
glEnable(iLight);
switch (li->type) {
//directional light
case 0:
case 3:
#if defined(GPAC_USE_GLES1X) && defined(GPAC_FIXED_POINT)
vals[0] = -li->direction.x;
vals[1] = -li->direction.y;
vals[2] = -li->direction.z;
vals[3] = 0;
glLightxv(iLight, GL_POSITION, vals);
vals[0] = gf_mulfix(li->color.red, li->intensity);
vals[1] = gf_mulfix(li->color.green, li->intensity);
vals[2] = gf_mulfix(li->color.blue, li->intensity);
vals[3] = FIX_ONE;
glLightxv(iLight, GL_DIFFUSE, vals);
glLightxv(iLight, GL_SPECULAR, vals);
vals[0] = gf_mulfix(li->color.red, li->ambientIntensity);
vals[1] = gf_mulfix(li->color.green, li->ambientIntensity);
vals[2] = gf_mulfix(li->color.blue, li->ambientIntensity);
vals[3] = FIX_ONE;
glLightxv(iLight, GL_AMBIENT, vals);
glLightx(iLight, GL_CONSTANT_ATTENUATION, FIX_ONE);
glLightx(iLight, GL_LINEAR_ATTENUATION, 0);
glLightx(iLight, GL_QUADRATIC_ATTENUATION, 0);
glLightx(iLight, GL_SPOT_CUTOFF, INT2FIX(180) );
#else
ambientIntensity = FIX2FLT(li->ambientIntensity);
intensity = FIX2FLT(li->intensity);
vals[0] = -FIX2FLT(li->direction.x);
vals[1] = -FIX2FLT(li->direction.y);
vals[2] = -FIX2FLT(li->direction.z);
vals[3] = 0;
glLightfv(iLight, GL_POSITION, vals);
vals[0] = FIX2FLT(li->color.red)*intensity;
vals[1] = FIX2FLT(li->color.green)*intensity;
vals[2] = FIX2FLT(li->color.blue)*intensity;
vals[3] = 1;
glLightfv(iLight, GL_DIFFUSE, vals);
glLightfv(iLight, GL_SPECULAR, vals);
vals[0] = FIX2FLT(li->color.red)*ambientIntensity;
vals[1] = FIX2FLT(li->color.green)*ambientIntensity;
vals[2] = FIX2FLT(li->color.blue)*ambientIntensity;
vals[3] = 1;
glLightfv(iLight, GL_AMBIENT, vals);
glLightf(iLight, GL_CONSTANT_ATTENUATION, 1.0f);
glLightf(iLight, GL_LINEAR_ATTENUATION, 0);
glLightf(iLight, GL_QUADRATIC_ATTENUATION, 0);
glLightf(iLight, GL_SPOT_CUTOFF, 180);
#endif
break;
//spot light
case 1:
#ifndef GPAC_USE_GLES1X
ambientIntensity = FIX2FLT(li->ambientIntensity);
intensity = FIX2FLT(li->intensity);
cutOffAngle = FIX2FLT(li->cutOffAngle);
beamWidth = FIX2FLT(li->beamWidth);
#endif
#if defined(GPAC_USE_GLES1X) && defined(GPAC_FIXED_POINT)
vals[0] = li->direction.x;
vals[1] = li->direction.y;
vals[2] = li->direction.z;
vals[3] = FIX_ONE;
glLightxv(iLight, GL_SPOT_DIRECTION, vals);
vals[0] = li->position.x;
vals[1] = li->position.y;
vals[2] = li->position.z;
vals[3] = FIX_ONE;
glLightxv(iLight, GL_POSITION, vals);
glLightx(iLight, GL_CONSTANT_ATTENUATION, li->attenuation.x ? li->attenuation.x : FIX_ONE);
glLightx(iLight, GL_LINEAR_ATTENUATION, li->attenuation.y);
glLightx(iLight, GL_QUADRATIC_ATTENUATION, li->attenuation.z);
vals[0] = gf_mulfix(li->color.red, li->intensity);
vals[1] = gf_mulfix(li->color.green, li->intensity);
vals[2] = gf_mulfix(li->color.blue, li->intensity);
vals[3] = FIX_ONE;
glLightxv(iLight, GL_DIFFUSE, vals);
glLightxv(iLight, GL_SPECULAR, vals);
vals[0] = gf_mulfix(li->color.red, li->ambientIntensity);
vals[1] = gf_mulfix(li->color.green, li->ambientIntensity);
vals[2] = gf_mulfix(li->color.blue, li->ambientIntensity);
vals[3] = FIX_ONE;
glLightxv(iLight, GL_AMBIENT, vals);
if (!li->beamWidth) exp = FIX_ONE;
else if (li->beamWidth > li->cutOffAngle) exp = 0;
else {
exp = FIX_ONE - gf_cos(li->beamWidth);
if (exp>FIX_ONE) exp = FIX_ONE;
}
glLightx(iLight, GL_SPOT_EXPONENT, exp*128);
glLightx(iLight, GL_SPOT_CUTOFF, gf_divfix(180*li->cutOffAngle, GF_PI) );
#else
vals[0] = FIX2FLT(li->direction.x);
vals[1] = FIX2FLT(li->direction.y);
vals[2] = FIX2FLT(li->direction.z);
vals[3] = 1;
glLightfv(iLight, GL_SPOT_DIRECTION, vals);
vals[0] = FIX2FLT(li->position.x);
vals[1] = FIX2FLT(li->position.y);
vals[2] = FIX2FLT(li->position.z);
vals[3] = 1;
glLightfv(iLight, GL_POSITION, vals);
glLightf(iLight, GL_CONSTANT_ATTENUATION, li->attenuation.x ? FIX2FLT(li->attenuation.x) : 1.0f);
glLightf(iLight, GL_LINEAR_ATTENUATION, FIX2FLT(li->attenuation.y));
glLightf(iLight, GL_QUADRATIC_ATTENUATION, FIX2FLT(li->attenuation.z));
vals[0] = FIX2FLT(li->color.red)*intensity;
vals[1] = FIX2FLT(li->color.green)*intensity;
vals[2] = FIX2FLT(li->color.blue)*intensity;
vals[3] = 1;
glLightfv(iLight, GL_DIFFUSE, vals);
glLightfv(iLight, GL_SPECULAR, vals);
vals[0] = FIX2FLT(li->color.red)*ambientIntensity;
vals[1] = FIX2FLT(li->color.green)*ambientIntensity;
vals[2] = FIX2FLT(li->color.blue)*ambientIntensity;
vals[3] = 1;
glLightfv(iLight, GL_AMBIENT, vals);
//glLightf(iLight, GL_SPOT_EXPONENT, 0.5f * (beamWidth+0.001f) /*(Float) (0.5 * log(0.5) / log(cos(beamWidth)) ) */);
if (!beamWidth) exp = 1;
else if (beamWidth>cutOffAngle) exp = 0;
else {
exp = 1.0f - (Float) cos(beamWidth);
if (exp>1) exp = 1;
}
glLightf(iLight, GL_SPOT_EXPONENT, exp*128);
glLightf(iLight, GL_SPOT_CUTOFF, 180*cutOffAngle/FIX2FLT(GF_PI));
#endif
break;
//point light
case 2:
#if defined(GPAC_USE_GLES1X) && defined(GPAC_FIXED_POINT)
vals[0] = li->position.x;
vals[1] = li->position.y;
vals[2] = li->position.z;
vals[3] = FIX_ONE;
glLightxv(iLight, GL_POSITION, vals);
glLightx(iLight, GL_CONSTANT_ATTENUATION, li->attenuation.x ? li->attenuation.x : FIX_ONE);
glLightx(iLight, GL_LINEAR_ATTENUATION, li->attenuation.y);
glLightx(iLight, GL_QUADRATIC_ATTENUATION, li->attenuation.z);
vals[0] = gf_mulfix(li->color.red, li->intensity);
vals[1] = gf_mulfix(li->color.green, li->intensity);
vals[2] = gf_mulfix(li->color.blue, li->intensity);
vals[3] = FIX_ONE;
glLightxv(iLight, GL_DIFFUSE, vals);
glLightxv(iLight, GL_SPECULAR, vals);
vals[0] = gf_mulfix(li->color.red, li->ambientIntensity);
vals[1] = gf_mulfix(li->color.green, li->ambientIntensity);
vals[2] = gf_mulfix(li->color.blue, li->ambientIntensity);
vals[3] = FIX_ONE;
glLightxv(iLight, GL_AMBIENT, vals);
glLightx(iLight, GL_SPOT_EXPONENT, 0);
glLightx(iLight, GL_SPOT_CUTOFF, INT2FIX(180) );
#else
ambientIntensity = FIX2FLT(li->ambientIntensity);
intensity = FIX2FLT(li->intensity);
vals[0] = FIX2FLT(li->position.x);
vals[1] = FIX2FLT(li->position.y);
vals[2] = FIX2FLT(li->position.z);
vals[3] = 1;
glLightfv(iLight, GL_POSITION, vals);
glLightf(iLight, GL_CONSTANT_ATTENUATION, li->attenuation.x ? FIX2FLT(li->attenuation.x) : 1.0f);
glLightf(iLight, GL_LINEAR_ATTENUATION, FIX2FLT(li->attenuation.y));
glLightf(iLight, GL_QUADRATIC_ATTENUATION, FIX2FLT(li->attenuation.z));
vals[0] = FIX2FLT(li->color.red)*intensity;
vals[1] = FIX2FLT(li->color.green)*intensity;
vals[2] = FIX2FLT(li->color.blue)*intensity;
vals[3] = 1;
glLightfv(iLight, GL_DIFFUSE, vals);
glLightfv(iLight, GL_SPECULAR, vals);
vals[0] = FIX2FLT(li->color.red)*ambientIntensity;
vals[1] = FIX2FLT(li->color.green)*ambientIntensity;
vals[2] = FIX2FLT(li->color.blue)*ambientIntensity;
vals[3] = 1;
glLightfv(iLight, GL_AMBIENT, vals);
glLightf(iLight, GL_SPOT_EXPONENT, 0);
glLightf(iLight, GL_SPOT_CUTOFF, 180);
#endif
break;
}
}
glEnable(GL_LIGHTING);
}
void visual_3d_enable_fog(GF_VisualManager *visual)
{
#ifndef GPAC_USE_TINYGL
#if defined(GPAC_USE_GLES1X) && defined(GPAC_FIXED_POINT)
Fixed vals[4];
glEnable(GL_FOG);
if (!visual->fog_type) glFogx(GL_FOG_MODE, GL_LINEAR);
else if (visual->fog_type==1) glFogx(GL_FOG_MODE, GL_EXP);
else if (visual->fog_type==2) glFogx(GL_FOG_MODE, GL_EXP2);
glFogx(GL_FOG_DENSITY, visual->fog_density);
glFogx(GL_FOG_START, 0);
glFogx(GL_FOG_END, visual->fog_visibility);
vals[0] = visual->fog_color.red;
vals[1] = visual->fog_color.green;
vals[2] = visual->fog_color.blue;
vals[3] = FIX_ONE;
glFogxv(GL_FOG_COLOR, vals);
glHint(GL_FOG_HINT, visual->compositor->fast ? GL_FASTEST : GL_NICEST);
#else
Float vals[4];
glEnable(GL_FOG);
#if defined(GPAC_USE_GLES1X)
if (!visual->fog_type) glFogf(GL_FOG_MODE, GL_LINEAR);
else if (visual->fog_type==1) glFogf(GL_FOG_MODE, GL_EXP);
else if (visual->fog_type==2) glFogf(GL_FOG_MODE, GL_EXP2);
#else
if (!visual->fog_type) glFogi(GL_FOG_MODE, GL_LINEAR);
else if (visual->fog_type==1) glFogi(GL_FOG_MODE, GL_EXP);
else if (visual->fog_type==2) glFogi(GL_FOG_MODE, GL_EXP2);
#endif
glFogf(GL_FOG_DENSITY, FIX2FLT(visual->fog_density));
glFogf(GL_FOG_START, 0);
glFogf(GL_FOG_END, FIX2FLT(visual->fog_visibility));
vals[0] = FIX2FLT(visual->fog_color.red);
vals[1] = FIX2FLT(visual->fog_color.green);
vals[2] = FIX2FLT(visual->fog_color.blue);
vals[3] = 1;
glFogfv(GL_FOG_COLOR, vals);
glHint(GL_FOG_HINT, visual->compositor->fast ? GL_FASTEST : GL_NICEST);
#endif
#endif
}
#endif // ! GPAC_USE_GLES2
static void visual_3d_do_draw_mesh(GF_TraverseState *tr_state, GF_Mesh *mesh)
{
u32 prim_type;
GF_Matrix mx;
u32 i, p_idx[6];
void *idx_addr = NULL;
GF_Plane fplanes[6];
switch (mesh->mesh_type) {
case MESH_LINESET:
prim_type = GL_LINES;
break;
case MESH_POINTSET:
prim_type = GL_POINTS;
break;
default:
prim_type = GL_TRIANGLES;
break;
}
if (mesh->vbo_idx) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->vbo_idx);
} else {
idx_addr = mesh->indices;
}
/*if inside or no aabb for the mesh draw vertex array*/
if (!tr_state->visual->compositor->cull || (tr_state->cull_flag==CULL_INSIDE) || !mesh->aabb_root || !mesh->aabb_root->pos) {
#if defined(GPAC_USE_GLES1X) || defined(GPAC_USE_GLES2)
glDrawElements(prim_type, mesh->i_count, GL_UNSIGNED_SHORT, idx_addr);
#else
glDrawElements(prim_type, mesh->i_count, GL_UNSIGNED_INT, idx_addr);
#endif
} else {
/*otherwise cull aabb against frustum - after some testing it appears (as usual) that there must
be a compromise: we're slowing down the compositor here, however the gain is really appreciable for
large meshes, especially terrains/elevation grids*/
/*first get transformed frustum in local space*/
gf_mx_copy(mx, tr_state->model_matrix);
gf_mx_inverse(&mx);
for (i=0; i<6; i++) {
fplanes[i] = tr_state->camera->planes[i];
gf_mx_apply_plane(&mx, &fplanes[i]);
p_idx[i] = gf_plane_get_p_vertex_idx(&fplanes[i]);
}
/*then recursively cull & draw AABB tree*/
visual_3d_draw_aabb_node(tr_state, mesh, prim_type, fplanes, p_idx, mesh->aabb_root->pos, idx_addr);
visual_3d_draw_aabb_node(tr_state, mesh, prim_type, fplanes, p_idx, mesh->aabb_root->neg, idx_addr);
}
if (mesh->vbo_idx) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
static Bool visual_3d_bind_buffer(GF_Compositor *compositor, GF_Mesh *mesh, void **base_address)
{
*base_address = NULL;
if ((compositor->reset_graphics==2) && mesh->vbo) {
/*we lost OpenGL context at previous frame, recreate VBO*/
mesh->vbo = 0;
mesh->vbo_idx = 0;
}
/*rebuild VBO for large ojects only (we basically filter quads out)*/
if (!mesh->vbo && compositor->gl_caps.vbo
#ifndef GPAC_USE_GLES2
&& (mesh->v_count>4)
#endif
) {
glGenBuffers(1, &mesh->vbo);
if (mesh->vbo) {
glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
glBufferData(GL_ARRAY_BUFFER, mesh->v_count * sizeof(GF_Vertex) , mesh->vertices, (mesh->vbo_dynamic) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
mesh->vbo_dirty = 0;
} else {
return GF_FALSE;
}
glGenBuffers(1, &mesh->vbo_idx);
if (mesh->vbo_idx) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->vbo_idx);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->i_count*sizeof(IDX_TYPE), mesh->indices, (mesh->vbo_dynamic) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
} else {
return GF_FALSE;
}
}
if (mesh->vbo) {
*base_address = NULL;
glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
} else {
*base_address = &mesh->vertices[0].pos;
}
if (mesh->vbo_dirty) {
glBufferSubData(GL_ARRAY_BUFFER, 0, mesh->v_count * sizeof(GF_Vertex) , mesh->vertices);
if (mesh->vbo_idx) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->vbo_idx);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh->i_count*sizeof(IDX_TYPE), mesh->indices, (mesh->vbo_dynamic) ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
mesh->vbo_dirty = 0;
}
return GF_TRUE;
}
#if !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
static void visual_3d_update_matrices_shaders(GF_TraverseState *tr_state)
{
GF_Matrix mx;
if (tr_state->visual->needs_projection_matrix_reload) {
tr_state->visual->needs_projection_matrix_reload = 0;
visual_3d_load_matrix_shaders(tr_state->visual->glsl_program, (Fixed *) tr_state->camera->projection.m, "gfProjectionMatrix");
}
//calculate ModelView matix (camera.view * model)
gf_mx_copy(mx, tr_state->camera->modelview);
gf_mx_add_matrix(&mx, &tr_state->model_matrix);
visual_3d_load_matrix_shaders(tr_state->visual->glsl_program, (Fixed *) &mx.m, "gfModelViewMatrix");
}
static void visual_3d_set_lights_shaders(GF_TraverseState *tr_state)
{
u32 i;
GF_LightInfo *li;
GF_Vec pt;
Float ambientIntensity, intensity, vals[4];
GLint loc;
GF_Matrix mx;
GF_VisualManager *visual = tr_state->visual;
char szName[100];
loc = glGetUniformLocation(visual->glsl_program, "gfNumLights");
if (loc>=0)
glUniform1i(loc, visual->num_lights);
GL_CHECK_ERR()
/*
* Equivalent to glLightModel(GL_LIGHTMODEL_TWO_SIDE, GL_TRUE);
*/
loc = glGetUniformLocation(visual->glsl_program, "gfLightTwoSide");
if (loc>=0)
glUniform1i(loc, 1);
GL_CHECK_ERR()
li = &visual->lights[0];
pt = li->direction;
gf_vec_norm(&pt);
vals[0] = -FIX2FLT(pt.x);
vals[1] = -FIX2FLT(pt.y);
vals[2] = -FIX2FLT(pt.z);
vals[3] = 0;
ambientIntensity = FIX2FLT(li->ambientIntensity);
intensity = FIX2FLT(li->intensity);
for (i = 0; i < (int) visual->num_lights; i++) {
GF_Vec orig;
li = &visual->lights[i];
if (li->type==3) { //we have a headlight
gf_mx_init(mx);
} else {
//update mx according to the light mx
gf_mx_copy(mx, visual->camera.modelview);
gf_mx_add_matrix(&mx, &li->light_mx);
}
sprintf(szName, "%s%d%s", "lights[", i, "].type");
loc = glGetUniformLocation(visual->glsl_program, szName); //Uniform name lights[i].type
if (loc>=0) {
if (li->type==3) {
glUniform1i(loc, 0); //headlight; Set type 0-directional
} else {
glUniform1i(loc, (GLint) li->type); //Set type 0-directional 1-spot 2-point
}
}
//set for light direction (assuming origin = 0,0,0)
pt = li->direction;
orig.x = orig.y = orig.z = 0;
gf_mx_apply_vec(&mx, &pt);
gf_mx_apply_vec(&mx, &orig);
gf_vec_diff(pt, pt, orig);
gf_vec_norm(&pt);
vals[0] = -FIX2FLT(pt.x);
vals[1] = -FIX2FLT(pt.y);
vals[2] = -FIX2FLT(pt.z);
vals[3] = 0;
sprintf(szName, "%s%d%s", "lights[", i, "].direction");
loc = glGetUniformLocation(visual->glsl_program, szName);
if (loc>=0)
glUniform4fv(loc, 1, vals); //Set direction
if ((li->type==0) || (li->type==3) ) {
pt = li->direction;
vals[0] = -FIX2FLT(pt.x);
vals[1] = -FIX2FLT(pt.y);
vals[2] = -FIX2FLT(pt.z);
vals[3] = 0;
} else { //we have a spot or point light
pt = li->position;
gf_mx_apply_vec(&mx, &pt);
vals[0] = FIX2FLT(pt.x);
vals[1] = FIX2FLT(pt.y);
vals[2] = FIX2FLT(pt.z);
vals[3] = 1.0;
}
sprintf(szName, "%s%d%s", "lights[", i, "].position");
loc = glGetUniformLocation(visual->glsl_program, szName);
if (loc>=0)
glUniform4fv(loc, 1, vals);
pt = li->attenuation;
if (li->type && !li->attenuation.x) {
vals[0]=1.0;
} else {
vals[0] = FIX2FLT(pt.x);
}
vals[1] = FIX2FLT(pt.y);
vals[2] = FIX2FLT(pt.z);
sprintf(szName, "%s%d%s", "lights[", i, "].attenuation");
loc = glGetUniformLocation(visual->glsl_program, szName);
if (loc>=0)
glUniform3fv(loc, 1, vals);
vals[0] = FIX2FLT(li->color.red);
vals[1] = FIX2FLT(li->color.green);
vals[2] = FIX2FLT(li->color.blue);
vals[3] = 0;
sprintf(szName, "%s%d%s", "lights[", i, "].color");
loc = glGetUniformLocation(visual->glsl_program, szName);
if (loc>=0)
glUniform4fv(loc, 1, vals);
sprintf(szName, "%s%d%s", "lights[", i, "].intensity");
loc = glGetUniformLocation(visual->glsl_program, szName);
if (loc>=0)
glUniform1f(loc, li->intensity);
sprintf(szName, "%s%d%s", "lights[", i, "].cutOffAngle");
loc = glGetUniformLocation(visual->glsl_program, szName);
if (loc>=0)
glUniform1f(loc, li->cutOffAngle);
}
vals[0] = FIX2FLT(li->color.red)*intensity;
vals[1] = FIX2FLT(li->color.green)*intensity;
vals[2] = FIX2FLT(li->color.blue)*intensity;
vals[3] = 1;
loc = glGetUniformLocation(visual->glsl_program, "gfLightDiffuse");
if (loc>=0) glUniform4fv(loc, 1, vals);
loc = glGetUniformLocation(visual->glsl_program, "gfLightSpecular");
if (loc>=0) glUniform4fv(loc, 1, vals);
vals[0] = FIX2FLT(li->color.red)*ambientIntensity;
vals[1] = FIX2FLT(li->color.green)*ambientIntensity;
vals[2] = FIX2FLT(li->color.blue)*ambientIntensity;
vals[3] = 1;
loc = glGetUniformLocation(visual->glsl_program, "gfLightAmbient");
if (loc>=0) glUniform4fv(loc, 1, vals);
GL_CHECK_ERR()
}
static void visual_3d_set_fog_shaders(GF_VisualManager *visual)
{
GLint loc;
if (visual->has_fog) {
loc = glGetUniformLocation(visual->glsl_program, "gfFogEnabled");
if(loc>=0)
glUniform1i(loc, GL_TRUE);
loc = glGetUniformLocation(visual->glsl_program, "gfFogColor");
if(loc>=0)
glUniform3fv(loc, 1, (GLfloat *) &visual->fog_color);
loc = glGetUniformLocation(visual->glsl_program, "gfFogDensity");
if(loc>=0)
glUniform1f(loc, visual->fog_density );
loc = glGetUniformLocation(visual->glsl_program, "gfFogType");
if(loc>=0)
glUniform1i(loc, (GLuint) visual->fog_type );
loc = glGetUniformLocation(visual->glsl_program, "gfFogVisibility");
if(loc>=0)
glUniform1f(loc, visual->fog_visibility);
} else {
loc = glGetUniformLocation(visual->glsl_program, "gfFogEnabled");
if(loc>=0)
glUniform1i(loc, GL_FALSE);
}
GL_CHECK_ERR()
}
static void visual_3d_set_clippers_shaders(GF_VisualManager *visual, GF_TraverseState *tr_state)
{
Fixed vals[4];
char szName[100];
GLint loc;
u32 i;
GF_Matrix inv_mx, eye_mx;
if (!visual->num_clips) return;
gf_mx_copy(inv_mx, tr_state->model_matrix);
gf_mx_inverse(&inv_mx);
gf_mx_copy(eye_mx, tr_state->camera->modelview);
gf_mx_add_matrix(&eye_mx, &tr_state->model_matrix);
loc = glGetUniformLocation(visual->glsl_program, "gfNumClippers");
if (loc>=0)
glUniform1i(loc, visual->num_clips);
for (i = 0; i < visual->num_clips; i++) {
GF_Matrix mx;
GF_Plane p;
p = visual->clippers[i].p;
//compute clip plane in eye coordinates
if (! visual->clippers[i].is_2d_clip) {
gf_mx_copy(mx, inv_mx);
if (visual->clippers[i].mx_clipper != NULL) {
gf_mx_add_matrix(&mx, visual->clippers[i].mx_clipper);
}
//plane in local coordinates
gf_mx_apply_plane(&mx, &p);
//plane in eye coordinates
gf_mx_apply_plane(&eye_mx, &p);
}
sprintf(szName, "%s%d%s", "clipPlane[", i, "]"); //parse plane values
loc = glGetUniformLocation(visual->glsl_program, szName);
if (loc>=0) {
vals[0] = p.normal.x;
vals[1] = p.normal.y;
vals[2] = p.normal.z;
vals[3] = p.d;
glUniform4fv(loc, 1, vals); //Set Plane (w = distance)
}
}
}
GF_GLProgInstance *visual_3d_check_program_exists(GF_VisualManager *root_visual, u32 flags, u32 pix_fmt)
{
u32 i, count;
GF_GLProgInstance *prog = NULL;
if (root_visual->compositor->shader_mode_disabled)
return NULL;
count = gf_list_count(root_visual->compiled_programs);
for (i=0; i<count; i++) {
prog = gf_list_get(root_visual->compiled_programs, i);
if ((prog->flags==flags) && (prog->pix_fmt==pix_fmt)) break;
prog = NULL;
}
if (!prog) {
prog = visual_3d_build_program(root_visual, flags, pix_fmt);
if (!prog) return NULL;
gf_list_add(root_visual->compiled_programs, prog);
}
return prog;
}
static void visual_3d_draw_mesh_shader_only(GF_TraverseState *tr_state, GF_Mesh *mesh)
{
void *vertex_buffer_address;
GF_VisualManager *visual = tr_state->visual;
GF_VisualManager *root_visual = visual->compositor->visual;
GLint loc, loc_vertex_array, loc_color_array, loc_normal_array, loc_textcoord_array;
u32 flags;
u32 num_lights = visual->num_lights;
Bool is_debug_bounds = GF_FALSE;
GF_GLProgInstance *prog = NULL;
flags = root_visual->active_glsl_flags;
if (!mesh) {
is_debug_bounds = GF_TRUE;
mesh = tr_state->visual->compositor->unit_bbox;
flags = 0;
}
if (visual->has_material_2d) {
num_lights = 0;
}
if (!tr_state->mesh_num_textures && (mesh->flags & MESH_HAS_COLOR)) {
flags |= GF_GL_HAS_COLOR;
visual->has_material_2d = GF_FALSE;
}
else if (tr_state->mesh_num_textures && (mesh->mesh_type==MESH_TRIANGLES) && !(mesh->flags & MESH_NO_TEXTURE)) {
flags |= GF_GL_HAS_TEXTURE;
} else {
flags &= ~GF_GL_HAS_TEXTURE;
flags &= ~GF_GL_IS_YUV;
flags &= ~GF_GL_IS_ExternalOES;
}
if (num_lights && !is_debug_bounds) {
flags |= GF_GL_HAS_LIGHT;
} else {
flags &= ~GF_GL_HAS_LIGHT;
}
if (visual->num_clips) {
flags |= GF_GL_HAS_CLIP;
} else {
flags &= ~GF_GL_HAS_CLIP;
}
root_visual->active_glsl_flags = visual->active_glsl_flags = flags;
prog = visual_3d_check_program_exists(root_visual, flags, root_visual->bound_tx_pix_fmt);
if (!prog) return;
//check if we are using a different program than last time, if so force matrices updates
if (visual->glsl_program != prog->prog) {
tr_state->visual->needs_projection_matrix_reload = GF_TRUE;
}
GL_CHECK_ERR()
visual->glsl_program = prog->prog;
glUseProgram(visual->glsl_program);
GL_CHECK_ERR()
if (! visual_3d_bind_buffer(visual->compositor, mesh, &vertex_buffer_address)) {
glUseProgram(0);
return;
}
if (visual->state_blend_on)
glEnable(GL_BLEND);
visual_3d_update_matrices_shaders(tr_state);
loc_color_array = loc_normal_array = loc_textcoord_array = -1;
//setup vertext array location (always true)
loc_vertex_array = glGetAttribLocation(visual->glsl_program, "gfVertex");
if (loc_vertex_array<0)
return;
glEnableVertexAttribArray(loc_vertex_array);
GL_CHECK_ERR()
#if defined(GPAC_FIXED_POINT)
glVertexAttribPointer(loc_vertex_array, 3, GL_FIXED, GL_TRUE, sizeof(GF_Vertex), vertex_buffer_address);
#else
glVertexAttribPointer(loc_vertex_array, 3, GL_FLOAT, GL_FALSE, sizeof(GF_Vertex), vertex_buffer_address);
#endif
//setup scissor
visual_3d_set_clipper_scissor(visual, tr_state);
//setup clippers
visual_3d_set_clippers_shaders(visual, tr_state);
if (is_debug_bounds) {
Float cols[4];
loc = glGetUniformLocation(visual->glsl_program, "gfEmissionColor");
cols[0] = 1.0f - FIX2FLT(visual->mat_2d.red);
cols[1] = 1.0f - FIX2FLT(visual->mat_2d.green);
cols[2] = 1.0f - FIX2FLT(visual->mat_2d.blue);
cols[3] = 1.0f;
if (loc>=0)
glUniform4fv(loc, 1, (GLfloat *) cols);
GL_CHECK_ERR()
loc = glGetUniformLocation(visual->glsl_program, "hasMaterial2D");
if (loc>=0)
glUniform1i(loc, 1);
GL_CHECK_ERR()
}
/* Material2D does not have any lights, color used is "gfEmissionColor" uniform */
else if (visual->has_material_2d) {
//for YUV manually set alpha
if (flags & GF_GL_IS_YUV) {
loc = glGetUniformLocation(visual->glsl_program, "alpha");
if(loc>=0)
glUniform1f(loc, FIX2FLT(visual->mat_2d.alpha));
}
//otherwise handle alpha with blend if no texture
else if (!tr_state->mesh_num_textures) {
if (visual->mat_2d.alpha < FIX_ONE) {
glEnable(GL_BLEND);
} else {
glDisable(GL_BLEND);
}
}
loc = glGetUniformLocation(visual->glsl_program, "gfEmissionColor");
if (loc>=0)
glUniform4fv(loc, 1, (GLfloat *) & visual->mat_2d);
GL_CHECK_ERR()
loc = glGetUniformLocation(visual->glsl_program, "hasMaterial2D");
if (loc>=0)
glUniform1i(loc, 1);
GL_CHECK_ERR()
} else {
loc = glGetUniformLocation(visual->glsl_program, "hasMaterial2D");
if (loc>=0)
glUniform1i(loc, 0);
GL_CHECK_ERR()
//for YUV manually set alpha
if (flags & GF_GL_IS_YUV) {
loc = glGetUniformLocation(visual->glsl_program, "alpha");
if(loc>=0)
glUniform1f(loc, 1.0);
GL_CHECK_ERR()
}
}
/* if lighting is on, setup material */
if ((flags & GF_GL_HAS_LIGHT) && visual->has_material && !visual->has_material_2d) {
u32 i;
for(i =0; i<4; i++) {
Fixed *rgba = (Fixed *) & visual->materials[i];
#if defined(GPAC_FIXED_POINT)
Float _rgba[4];
_rgba[0] = FIX2FLT(rgba[0]);
_rgba[1] = FIX2FLT(rgba[1]);
_rgba[2] = FIX2FLT(rgba[2]);
_rgba[3] = FIX2FLT(rgba[3]);
#elif defined(GPAC_USE_GLES1X)
Fixed *_rgba = (Fixed *) rgba;
#else
Float *_rgba = (Float *) rgba;
#endif
switch (i) {
case 0:
loc = glGetUniformLocation(visual->glsl_program, "gfAmbientColor");
break;
case 1:
loc = glGetUniformLocation(visual->glsl_program, "gfDiffuseColor");
break;
case 2:
loc = glGetUniformLocation(visual->glsl_program, "gfSpecularColor");
break;
case 3:
loc = glGetUniformLocation(visual->glsl_program, "gfEmissionColor");
break;
}
if (loc>=0)
glUniform4fv(loc, 1, _rgba);
}
//TO CHECK: if this does not work as it is supposed to, try: visual->shininess * 128
loc = glGetUniformLocation(visual->glsl_program, "gfShininess");
if (loc>=0)
glUniform1f(loc, FIX2FLT(visual->shininess));
glDisable(GL_CULL_FACE); //Enable for performance; if so, check glFrontFace()
}
//setup mesh color vertex attribute - only available for some shaders
if (!tr_state->mesh_num_textures && (mesh->flags & MESH_HAS_COLOR) && !is_debug_bounds) {
loc_color_array = glGetAttribLocation(visual->glsl_program, "gfMeshColor");
if (loc_color_array >= 0) {
//for now colors are 8bit/comp RGB(A), so used GL_UNSIGNED_BYTE and GL_TRUE for normalizing values
if (mesh->flags & MESH_HAS_ALPHA) {
glEnable(GL_BLEND);
tr_state->mesh_is_transparent = 1;
glVertexAttribPointer(loc_color_array, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GF_Vertex), ((char *)vertex_buffer_address + MESH_COLOR_OFFSET));
} else {
glVertexAttribPointer(loc_color_array, 3, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GF_Vertex), ((char *)vertex_buffer_address + MESH_COLOR_OFFSET));
}
glEnableVertexAttribArray(loc_color_array);
}
GL_CHECK_ERR()
}
if (flags & GF_GL_HAS_LIGHT) {
visual_3d_set_fog_shaders(visual);
}
//setup mesh normal vertex attribute - only available for some shaders
if (!visual->has_material_2d && num_lights && (mesh->mesh_type==MESH_TRIANGLES) ) {
GF_Matrix normal_mx;
gf_assert(flags & GF_GL_HAS_LIGHT);
gf_mx_copy(normal_mx, tr_state->camera->modelview);
gf_mx_add_matrix(&normal_mx, &tr_state->model_matrix);
normal_mx.m[12] = normal_mx.m[13] = normal_mx.m[14] = 0;
gf_mx_inverse(&normal_mx);
gf_mx_transpose(&normal_mx);
visual_3d_load_matrix_shaders(tr_state->visual->glsl_program, (Fixed *) &normal_mx.m, "gfNormalMatrix");
loc_normal_array = glGetAttribLocation(visual->glsl_program, "gfNormal");
if (loc_normal_array>=0) {
#ifdef MESH_USE_FIXED_NORMAL
glVertexAttribPointer(loc_normal_array, 3, GL_FLOAT, GL_FALSE, sizeof(GF_Vertex), ((char *)vertex_buffer_address + MESH_NORMAL_OFFSET) );
#else
glVertexAttribPointer(loc_normal_array, 3, GL_BYTE, GL_FALSE, sizeof(GF_Vertex), ((char *)vertex_buffer_address + MESH_NORMAL_OFFSET) );
#endif
glEnableVertexAttribArray(loc_normal_array);
}
GL_CHECK_ERR()
loc = glGetUniformLocation(visual->glsl_program, "gfNumLights");
if (loc>=0)
glUniform1i(loc, num_lights);
visual_3d_set_lights_shaders(tr_state);
GL_CHECK_ERR()
}
//setup mesh normal vertex attribute - only available for some shaders
if (tr_state->mesh_num_textures && (mesh->mesh_type==MESH_TRIANGLES) && !(mesh->flags & MESH_NO_TEXTURE)) {
if (visual->has_tx_matrix) {
//parsing texture matrix
loc = glGetUniformLocation(visual->glsl_program, "gfTextureMatrix");
if (loc>=0)
glUniformMatrix4fv(loc, 1, GL_FALSE, visual->tx_matrix.m);
GL_CHECK_ERR()
loc = glGetUniformLocation(visual->glsl_program, "hasTextureMatrix");
if (loc>=0) glUniform1i(loc, 1);
} else {
loc = glGetUniformLocation(visual->glsl_program, "hasTextureMatrix");
if (loc>=0) glUniform1i(loc, 0);
}
//parsing texture coordinates
loc_textcoord_array = glGetAttribLocation(visual->glsl_program, "gfMultiTexCoord");
if (loc_textcoord_array>=0) {
glVertexAttribPointer(loc_textcoord_array, 2, GL_FLOAT, GL_FALSE, sizeof(GF_Vertex), ((char *)vertex_buffer_address + MESH_TEX_OFFSET));
glEnableVertexAttribArray(loc_textcoord_array);
GL_CHECK_ERR()
}
if (flags & GF_GL_IS_YUV) {
loc = glGetUniformLocation(visual->glsl_program, "yuvPixelFormat");
if (loc>=0) {
int yuv_mode = 0;
switch (visual->yuv_pixelformat_type) {
case GF_PIXEL_NV21:
yuv_mode = 1;
break;
case GF_PIXEL_NV12:
yuv_mode = 2;
break;
}
glUniform1i(loc, yuv_mode);
}
GL_CHECK_ERR()
}
}
//
if (mesh->mesh_type != MESH_TRIANGLES) {
//According to the spec we should pass a 0,0,1 Normal and disable lights. we just disable lights
if(flags & GF_GL_HAS_LIGHT) {
loc = glGetUniformLocation(visual->glsl_program, "gfNumLights");
if (loc>=0) glUniform1i(loc, 0);
}
glDisable(GL_CULL_FACE);
#if !defined(GPAC_USE_TINYGL) && !defined(GL_ES_CL_PROFILE)
glLineWidth(1.0f);
#endif
} else {
if (visual->compositor->bcull
&& (!tr_state->mesh_is_transparent || (visual->compositor->bcull ==GF_BACK_CULL_ALPHA) )
&& (mesh->flags & MESH_IS_SOLID)) {
glEnable(GL_CULL_FACE);
if (tr_state->reverse_backface) {
glFrontFace((mesh->flags & MESH_IS_CW) ? GL_CCW : GL_CW);
} else {
glFrontFace((mesh->flags & MESH_IS_CW) ? GL_CW : GL_CCW);
}
} else {
glDisable(GL_CULL_FACE);
}
}
GL_CHECK_ERR()
//We have a Colour Matrix to be applied
if(!tr_state->color_mat.identity) {
GF_Matrix toBeParsed; //4x4 RGBA Color Matrix
Fixed translateV[4]; //Vec4 holding translation property of color_mat
int row,col;
gf_mx_init(toBeParsed);
//Copy values from Color Matrix
for(row=0; row<4; row++) {
for(col=0; col<4; col++) {
toBeParsed.m[col+(row*4)]=tr_state->color_mat.m[col+(row*5)];
}
translateV[row] = tr_state->color_mat.m[4+(row*5)];
}
//the rest of the values form the translation vector
loc = glGetUniformLocation(visual->glsl_program, "gfTranslationVector");
if (loc>=0)
glUniform4fv(loc, 1, (GLfloat *) &translateV);
GL_CHECK_ERR()
loc = glGetUniformLocation(visual->glsl_program, "hasColorMatrix");
if(loc>=0) glUniform1i(loc, 1);
loc = glGetUniformLocation(visual->glsl_program, "gfColorMatrix");
if (loc>=0)
glUniformMatrix4fv(loc, 1, GL_FALSE, toBeParsed.m);
GL_CHECK_ERR()
} else {
loc = glGetUniformLocation(visual->glsl_program, "hasColorMatrix");
if(loc>=0) glUniform1i(loc, 0);
}
//We have a Colour Key to be applied
if(tr_state->col_key) {
Float vals[3];
Float eightbit = 255; //used for mapping values between 0.0 and 1.0
glEnable(GL_BLEND); //normally we shouldn have to need this, but we do
loc = glGetUniformLocation(visual->glsl_program, "hasColorKey");
if(loc>=0) glUniform1i(loc, 1);
vals[0] = tr_state->col_key->r/eightbit;
vals[1] = tr_state->col_key->g/eightbit;
vals[2] = tr_state->col_key->b/eightbit;
loc = glGetUniformLocation(visual->glsl_program, "gfKeyColor");
if(loc>=0)glUniform3fv(loc, 1, vals);
loc = glGetUniformLocation(visual->glsl_program, "gfKeyLow");
if(loc>=0) glUniform1f(loc, tr_state->col_key->low/eightbit);
loc = glGetUniformLocation(visual->glsl_program, "gfKeyHigh");
if(loc>=0) glUniform1f(loc, tr_state->col_key->high/eightbit);
loc = glGetUniformLocation(visual->glsl_program, "gfKeyAlpha");
if(loc>=0) glUniform1f(loc, tr_state->col_key->alpha/eightbit);
} else {
loc = glGetUniformLocation(visual->glsl_program, "hasColorKey");
if(loc>=0) glUniform1i(loc, 0);
}
visual_3d_do_draw_mesh(tr_state, mesh);
GL_CHECK_ERR()
//We drawn, now we Reset
if (mesh->vbo)
glBindBuffer(GL_ARRAY_BUFFER, 0);
if (loc_vertex_array>=0) glDisableVertexAttribArray(loc_vertex_array);
if (loc_color_array>=0) glDisableVertexAttribArray(loc_color_array);
if (loc_normal_array>=0) glDisableVertexAttribArray(loc_normal_array);
if (loc_textcoord_array>=0) glDisableVertexAttribArray(loc_textcoord_array);
//instead of visual_3d_reset_lights(visual);
if(root_visual->active_glsl_flags & GF_GL_HAS_LIGHT) {
loc = glGetUniformLocation(visual->glsl_program, "gfNumLights");
if (loc>=0) glUniform1i(loc, 0);
GL_CHECK_ERR()
}
if (visual->has_clipper_2d) {
glDisable(GL_SCISSOR_TEST);
}
visual->has_material_2d = GF_FALSE;
visual->active_glsl_flags = root_visual->active_glsl_flags;
root_visual->active_glsl_flags &= ~ GF_GL_HAS_COLOR;
visual->has_material = 0;
visual->state_color_on = 0;
if (tr_state->mesh_is_transparent) glDisable(GL_BLEND);
tr_state->mesh_is_transparent = 0;
GL_CHECK_ERR()
glUseProgram(0);
GL_CHECK_ERR()
}
#endif // !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
//#endif //GPAC_USE_GLES2
static void visual_3d_draw_mesh(GF_TraverseState *tr_state, GF_Mesh *mesh)
{
#ifndef GPAC_USE_GLES2
GF_Compositor *compositor = tr_state->visual->compositor;
GF_VisualManager *visual = tr_state->visual;
Bool has_col, has_tx, has_norm;
void *base_address = NULL;
Bool is_debug_bounds = GF_FALSE;
#if defined(GPAC_FIXED_POINT) && !defined(GPAC_USE_GLES1X)
Float *color_array = NULL;
Float fix_scale = 1.0f;
fix_scale /= FIX_ONE;
#endif
#endif
if (mesh) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[V3D] Drawing mesh %p\n", mesh));
}
//clear error
glGetError();
GL_CHECK_ERR()
#ifdef GPAC_USE_GLES2
visual_3d_draw_mesh_shader_only(tr_state, mesh);
return;
#else
#if !defined(GPAC_CONFIG_ANDROID) && !defined(GPAC_CONFIG_IOS) && !defined(GPAC_FIXED_POINT)
if (!visual->compositor->shader_mode_disabled) {
visual_3d_draw_mesh_shader_only(tr_state, mesh);
return;
}
#endif
if (!mesh) {
mesh = tr_state->visual->compositor->unit_bbox;
is_debug_bounds = GF_TRUE;
}
if (! visual_3d_bind_buffer(compositor, mesh, &base_address)) {
#if! defined(GPAC_USE_GLES1X) && !defined(GPAC_USE_TINYGL)
glUseProgram(0);
#endif
return;
}
has_col = has_tx = has_norm = 0;
if (!is_debug_bounds) {
//set lights before pushing modelview matrix
visual_3d_set_lights(visual);
}
visual_3d_update_matrices(tr_state);
/*enable states*/
if (!is_debug_bounds) {
if (visual->has_fog) visual_3d_enable_fog(visual);
if (visual->state_color_on) glEnable(GL_COLOR_MATERIAL);
else glDisable(GL_COLOR_MATERIAL);
if (visual->state_blend_on) glEnable(GL_BLEND);
}
//setup scissor
visual_3d_set_clipper_scissor(visual, tr_state);
visual_3d_set_clippers(visual, tr_state);
glEnableClientState(GL_VERTEX_ARRAY);
#if defined(GPAC_USE_GLES1X)
glVertexPointer(3, GL_FIXED, sizeof(GF_Vertex), base_address);
#elif defined(GPAC_FIXED_POINT)
/*scale modelview matrix*/
glPushMatrix();
glScalef(fix_scale, fix_scale, fix_scale);
glVertexPointer(3, GL_INT, sizeof(GF_Vertex), base_address);
#else
glVertexPointer(3, GL_FLOAT, sizeof(GF_Vertex), base_address);
#endif
/*
* Enable colors:
if mat2d is set, use mat2d and no lighting
*/
if (visual->has_material_2d) {
glDisable(GL_LIGHTING);
if (visual->mat_2d.alpha != FIX_ONE) {
glEnable(GL_BLEND);
visual_3d_enable_antialias(visual, 0);
} else {
//disable blending only if no texture !
if (!tr_state->mesh_num_textures)
glDisable(GL_BLEND);
visual_3d_enable_antialias(visual, visual->compositor->aa ? 1 : 0);
}
#ifdef GPAC_USE_GLES1X
glColor4x( FIX2INT(visual->mat_2d.red * 255), FIX2INT(visual->mat_2d.green * 255), FIX2INT(visual->mat_2d.blue * 255), FIX2INT(visual->mat_2d.alpha * 255));
#elif defined(GPAC_FIXED_POINT)
glColor4f(FIX2FLT(visual->mat_2d.red), FIX2FLT(visual->mat_2d.green), FIX2FLT(visual->mat_2d.blue), FIX2FLT(visual->mat_2d.alpha));
#else
glColor4f(visual->mat_2d.red, visual->mat_2d.green, visual->mat_2d.blue, visual->mat_2d.alpha);
#endif
}
//setup material color
if (visual->has_material) {
u32 i;
GL_CHECK_ERR()
for (i=0; i<4; i++) {
GLenum mode;
Fixed *rgba = (Fixed *) & visual->materials[i];
#if defined(GPAC_USE_GLES1X)
Fixed *_rgba = (Fixed *) rgba;
#elif defined(GPAC_FIXED_POINT)
Float _rgba[4];
_rgba[0] = FIX2FLT(rgba[0]);
_rgba[1] = FIX2FLT(rgba[1]);
_rgba[2] = FIX2FLT(rgba[2]);
_rgba[3] = FIX2FLT(rgba[3]);
#else
Float *_rgba = (Float *) rgba;
#endif
switch (i) {
case 0:
mode = GL_AMBIENT;
break;
case 1:
mode = GL_DIFFUSE;
break;
case 2:
mode = GL_SPECULAR;
break;
default:
mode = GL_EMISSION;
break;
}
#if defined(GPAC_USE_GLES1X) && defined(GPAC_FIXED_POINT)
glMaterialxv(GL_FRONT_AND_BACK, mode, _rgba);
#else
glMaterialfv(GL_FRONT_AND_BACK, mode, _rgba);
#endif
GL_CHECK_ERR()
}
#ifdef GPAC_USE_GLES1X
glMaterialx(GL_FRONT_AND_BACK, GL_SHININESS, visual->shininess * 128);
#else
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, FIX2FLT(visual->shininess) * 128);
#endif
GL_CHECK_ERR()
}
//otherwise setup mesh color
if (!tr_state->mesh_num_textures && (mesh->flags & MESH_HAS_COLOR)) {
glEnable(GL_COLOR_MATERIAL);
#if !defined (GPAC_USE_GLES1X)
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
#endif
glEnableClientState(GL_COLOR_ARRAY);
has_col = 1;
#if defined (GPAC_USE_GLES1X)
if (mesh->flags & MESH_HAS_ALPHA) {
glEnable(GL_BLEND);
tr_state->mesh_is_transparent = 1;
}
#ifdef MESH_USE_SFCOLOR
/*glES only accepts full RGBA colors*/
glColorPointer(4, GL_FIXED, sizeof(GF_Vertex), ((char *)base_address + MESH_COLOR_OFFSET));
#else
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(GF_Vertex), ((char *)base_address + MESH_COLOR_OFFSET));
#endif /*MESH_USE_SFCOLOR*/
#elif defined (GPAC_FIXED_POINT)
#ifdef MESH_USE_SFCOLOR
/*this is a real pain: we cannot "scale" colors through OpenGL, and our components are 16.16 (32 bytes) ranging
from [0 to 65536] mapping to [0, 1.0], but OpenGL assumes for s32 a range from [-2^31 2^31] mapping to [0, 1.0]
we must thus rebuild a dedicated array...*/
if (mesh->flags & MESH_HAS_ALPHA) {
u32 i;
color_array = gf_malloc(sizeof(Float)*4*mesh->v_count);
for (i=0; i<mesh->v_count; i++) {
color_array[4*i] = FIX2FLT(mesh->vertices[i].color.red);
color_array[4*i+1] = FIX2FLT(mesh->vertices[i].color.green);
color_array[4*i+2] = FIX2FLT(mesh->vertices[i].color.blue);
color_array[4*i+3] = FIX2FLT(mesh->vertices[i].color.alpha);
}
glEnable(GL_BLEND);
glColorPointer(4, GL_FLOAT, 4*sizeof(Float), color_array);
tr_state->mesh_is_transparent = 1;
} else {
color_array = gf_malloc(sizeof(Float)*3*mesh->v_count);
for (i=0; i<mesh->v_count; i++) {
color_array[3*i] = FIX2FLT(mesh->vertices[i].color.red);
color_array[3*i+1] = FIX2FLT(mesh->vertices[i].color.green);
color_array[3*i+2] = FIX2FLT(mesh->vertices[i].color.blue);
}
glColorPointer(3, GL_FLOAT, 3*sizeof(Float), color_array);
}
#else
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(GF_Vertex), ((char *)base_address + MESH_COLOR_OFFSET));
#endif /*MESH_USE_SFCOLOR*/
#else
#ifdef MESH_USE_SFCOLOR
if (mesh->flags & MESH_HAS_ALPHA) {
glEnable(GL_BLEND);
glColorPointer(4, GL_FLOAT, sizeof(GF_Vertex), ((char *)base_address + MESH_COLOR_OFFSET));
tr_state->mesh_is_transparent = 1;
} else {
glColorPointer(3, GL_FLOAT, sizeof(GF_Vertex), ((char *)base_address + MESH_COLOR_OFFSET));
}
#else
if (mesh->flags & MESH_HAS_ALPHA) {
glEnable(GL_BLEND);
tr_state->mesh_is_transparent = 1;
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(GF_Vertex), ((char *)base_address + MESH_COLOR_OFFSET));
} else {
glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(GF_Vertex), ((char *)base_address + MESH_COLOR_OFFSET));
}
#endif /*MESH_USE_SFCOLOR*/
#endif
}
if (tr_state->mesh_num_textures && (mesh->mesh_type==MESH_TRIANGLES) && !(mesh->flags & MESH_NO_TEXTURE)) {
has_tx = 1;
glMatrixMode(GL_TEXTURE);
if (visual->has_tx_matrix) {
visual_3d_matrix_load(visual, visual->tx_matrix.m);
} else {
glLoadIdentity();
}
glMatrixMode(GL_MODELVIEW);
#if defined(GPAC_USE_GLES1X)
glTexCoordPointer(2, GL_FIXED, sizeof(GF_Vertex), ((char *)base_address + MESH_TEX_OFFSET));
glEnableClientState(GL_TEXTURE_COORD_ARRAY );
#elif defined(GPAC_FIXED_POINT)
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glScalef(fix_scale, fix_scale, fix_scale);
glMatrixMode(GL_MODELVIEW);
glTexCoordPointer(2, GL_INT, sizeof(GF_Vertex), ((char *)base_address + MESH_TEX_OFFSET));
glEnableClientState(GL_TEXTURE_COORD_ARRAY );
#else
#ifndef GPAC_USE_TINYGL
if (tr_state->mesh_num_textures>1) {
u32 i;
for (i=0; i<tr_state->mesh_num_textures; i++) {
glClientActiveTexture(GL_TEXTURE0 + i);
glTexCoordPointer(2, GL_FLOAT, sizeof(GF_Vertex), ((char *)base_address + MESH_TEX_OFFSET));
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
} else
#endif //GPAC_USE_TINYGL
{
glTexCoordPointer(2, GL_FLOAT, sizeof(GF_Vertex), ((char *)base_address + MESH_TEX_OFFSET));
glEnableClientState(GL_TEXTURE_COORD_ARRAY );
}
#endif
}
if (mesh->mesh_type != MESH_TRIANGLES) {
#ifdef GPAC_USE_GLES1X
glNormal3x(0, 0, FIX_ONE);
#else
glNormal3f(0, 0, 1.0f);
#endif
glDisable(GL_CULL_FACE);
glDisable(GL_LIGHTING);
if (mesh->mesh_type==MESH_LINESET) glDisable(GL_LINE_SMOOTH);
else glDisable(GL_POINT_SMOOTH);
#if !defined(GPAC_USE_TINYGL) && !defined(GL_ES_CL_PROFILE)
glLineWidth(1.0f);
#endif
} else {
u32 normal_type = GL_FLOAT;
has_norm = 1;
glEnableClientState(GL_NORMAL_ARRAY );
#ifdef MESH_USE_FIXED_NORMAL
#if defined(GPAC_USE_GLES1X)
normal_type = GL_FIXED;
#elif defined(GPAC_FIXED_POINT)
normal_type = GL_INT;
#else
normal_type = GL_FLOAT;
#endif
#else /*MESH_USE_FIXED_NORMAL*/
/*normals are stored on signed bytes*/
normal_type = GL_BYTE;
#endif
glNormalPointer(normal_type, sizeof(GF_Vertex), ((char *)base_address + MESH_NORMAL_OFFSET));
if (mesh->mesh_type==MESH_TRIANGLES) {
if (compositor->bcull
&& (!tr_state->mesh_is_transparent || (compositor->bcull ==GF_BACK_CULL_ALPHA) )
&& (mesh->flags & MESH_IS_SOLID)) {
glEnable(GL_CULL_FACE);
glFrontFace((mesh->flags & MESH_IS_CW) ? GL_CW : GL_CCW);
} else {
glDisable(GL_CULL_FACE);
}
}
}
GL_CHECK_ERR()
visual_3d_do_draw_mesh(tr_state, mesh);
GL_CHECK_ERR()
glDisableClientState(GL_VERTEX_ARRAY);
if (has_col) glDisableClientState(GL_COLOR_ARRAY);
glDisable(GL_COLOR_MATERIAL);
if (has_tx) glDisableClientState(GL_TEXTURE_COORD_ARRAY);
if (has_norm) glDisableClientState(GL_NORMAL_ARRAY);
if (mesh->vbo)
glBindBuffer(GL_ARRAY_BUFFER, 0);
#if defined(GPAC_FIXED_POINT) && !defined(GPAC_USE_GLES1X)
if (color_array) gf_free(color_array);
if (tr_state->mesh_num_textures && (mesh->mesh_type==MESH_TRIANGLES) && !(mesh->flags & MESH_NO_TEXTURE)) {
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
glPopMatrix();
#endif
if (visual->has_clipper_2d) {
glDisable(GL_SCISSOR_TEST);
}
visual_3d_reset_lights(visual);
glDisable(GL_COLOR_MATERIAL);
//reset all our states
visual_3d_reset_clippers(visual);
visual->has_material_2d = GF_FALSE;
visual->has_material = 0;
visual->state_color_on = 0;
if (tr_state->mesh_is_transparent) glDisable(GL_BLEND);
tr_state->mesh_is_transparent = 0;
GL_CHECK_ERR()
#endif
}
static void visual_3d_set_debug_color(u32 col)
{
#ifndef GPAC_USE_GLES2
#ifdef GPAC_USE_GLES1X
glColor4x( (col ? GF_COL_R(col) : 255) , (col ? GF_COL_G(col) : 0) , (col ? GF_COL_B(col) : 255), 255);
#else
glColor4f(col ? GF_COL_R(col)/255.0f : 1, col ? GF_COL_G(col)/255.0f : 0, col ? GF_COL_B(col)/255.0f : 1, 1);
#endif
#endif //GPAC_USE_GLES2
}
#if !defined(GPAC_USE_GLES2)
/*note we don't perform any culling for normal drawing...*/
static void visual_3d_draw_normals(GF_TraverseState *tr_state, GF_Mesh *mesh)
{
#if !defined( GPAC_USE_TINYGL)
GF_Vec pt, end;
u32 i, j;
Fixed scale = mesh->bounds.radius / 4;
#ifdef GPAC_USE_GLES1X
GF_Vec va[2];
u16 indices[2];
glEnableClientState(GL_VERTEX_ARRAY);
#endif
visual_3d_set_debug_color(0);
#if !defined(GPAC_DISABLE_3D) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES1X)
//in shader mode force pushing projection and modelview using fixed pipeline API
if (!tr_state->visual->compositor->shader_mode_disabled) {
tr_state->visual->needs_projection_matrix_reload=GF_TRUE;
visual_3d_update_matrices(tr_state);
}
#endif
if (tr_state->visual->compositor->norms==GF_NORMALS_VERTEX) {
IDX_TYPE *idx = mesh->indices;
for (i=0; i<mesh->i_count; i+=3) {
for (j=0; j<3; j++) {
pt = mesh->vertices[idx[j]].pos;
MESH_GET_NORMAL(end, mesh->vertices[idx[j]]);
end = gf_vec_scale(end, scale);
gf_vec_add(end, pt, end);
#ifdef GPAC_USE_GLES1X
va[0] = pt;
va[1] = end;
indices[0] = 0;
indices[1] = 1;
glVertexPointer(3, GL_FIXED, 0, va);
glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, indices);
#else
glBegin(GL_LINES);
glVertex3f(FIX2FLT(pt.x), FIX2FLT(pt.y), FIX2FLT(pt.z));
glVertex3f(FIX2FLT(end.x), FIX2FLT(end.y), FIX2FLT(end.z));
glEnd();
#endif
}
idx+=3;
}
} else {
IDX_TYPE *idx = mesh->indices;
for (i=0; i<mesh->i_count; i+=3) {
gf_vec_add(pt, mesh->vertices[idx[0]].pos, mesh->vertices[idx[1]].pos);
gf_vec_add(pt, pt, mesh->vertices[idx[2]].pos);
pt = gf_vec_scale(pt, FIX_ONE/3);
MESH_GET_NORMAL(end, mesh->vertices[idx[0]]);
end = gf_vec_scale(end, scale);
gf_vec_add(end, pt, end);
#ifdef GPAC_USE_GLES1X
va[0] = pt;
va[1] = end;
indices[0] = 0;
indices[1] = 1;
glVertexPointer(3, GL_FIXED, 0, va);
glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, indices);
#else
glBegin(GL_LINES);
glVertex3f(FIX2FLT(pt.x), FIX2FLT(pt.y), FIX2FLT(pt.z));
glVertex3f(FIX2FLT(end.x), FIX2FLT(end.y), FIX2FLT(end.z));
glEnd();
#endif
idx += 3;
}
}
#ifdef GPAC_USE_GLES1X
glDisableClientState(GL_VERTEX_ARRAY);
#endif
#endif /*GPAC_USE_TINYGL*/
}
#endif //GPAC_USE_GLES2
void visual_3d_draw_aabb_nodeBounds(GF_TraverseState *tr_state, AABBNode *node)
{
if (node->pos) {
visual_3d_draw_aabb_nodeBounds(tr_state, node->pos);
visual_3d_draw_aabb_nodeBounds(tr_state, node->neg);
} else {
GF_Matrix mx;
SFVec3f c, s;
gf_vec_diff(s, node->max, node->min);
c = gf_vec_scale(s, FIX_ONE/2);
gf_vec_add(c, node->min, c);
gf_mx_copy(mx, tr_state->model_matrix);
gf_mx_add_translation(&tr_state->model_matrix, c.x, c.y, c.z);
gf_mx_add_scale(&tr_state->model_matrix, s.x, s.y, s.z);
visual_3d_draw_mesh(tr_state, NULL);
gf_mx_copy(tr_state->model_matrix, mx);
}
}
void visual_3d_draw_bbox(GF_TraverseState *tr_state, GF_BBox *box, Bool is_debug)
{
GF_Matrix mx;
SFVec3f c, s;
if (! is_debug) {
visual_3d_set_debug_color(tr_state->visual->compositor->hlline);
}
gf_vec_diff(s, box->max_edge, box->min_edge);
c.x = box->min_edge.x + s.x/2;
c.y = box->min_edge.y + s.y/2;
c.z = box->min_edge.z + s.z/2;
gf_mx_copy(mx, tr_state->model_matrix);
gf_mx_add_translation(&tr_state->model_matrix, c.x, c.y, c.z);
gf_mx_add_scale(&tr_state->model_matrix, s.x, s.y, s.z);
visual_3d_draw_mesh(tr_state, NULL);
gf_mx_copy(tr_state->model_matrix, mx);
}
static void visual_3d_draw_bounds(GF_TraverseState *tr_state, GF_Mesh *mesh)
{
visual_3d_set_debug_color(0);
if (mesh->aabb_root && (tr_state->visual->compositor->bvol==GF_BOUNDS_AABB)) {
visual_3d_draw_aabb_nodeBounds(tr_state, mesh->aabb_root);
} else {
visual_3d_draw_bbox(tr_state, &mesh->bounds, GF_TRUE);
}
}
void visual_3d_mesh_paint(GF_TraverseState *tr_state, GF_Mesh *mesh)
{
#if !defined(GPAC_USE_GLES2)
Bool mesh_drawn = 0;
#endif
GL_CHECK_ERR()
glGetError();
GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[V3D] Drawing mesh %p\n", mesh));
if (tr_state->visual->compositor->wire != GF_WIREFRAME_ONLY) {
visual_3d_draw_mesh(tr_state, mesh);
#if !defined(GPAC_USE_GLES2)
mesh_drawn = 1;
#endif
}
#if !defined(GPAC_USE_GLES2)
if (tr_state->visual->compositor->norms) {
if (!mesh_drawn) {
visual_3d_update_matrices(tr_state);
mesh_drawn=1;
}
visual_3d_draw_normals(tr_state, mesh);
}
if ((mesh->mesh_type==MESH_TRIANGLES) && (tr_state->visual->compositor->wire != GF_WIREFRAME_NONE)) {
glDisable(GL_LIGHTING);
visual_3d_set_debug_color(0xFFFFFFFF);
if (!mesh_drawn)
visual_3d_update_matrices(tr_state);
glEnableClientState(GL_VERTEX_ARRAY);
#ifdef GPAC_USE_GLES1X
glVertexPointer(3, GL_FIXED, sizeof(GF_Vertex), &mesh->vertices[0].pos);
glDrawElements(GL_LINES, mesh->i_count, GL_UNSIGNED_SHORT, mesh->indices);
#else
glVertexPointer(3, GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].pos);
glDrawElements(GL_LINES, mesh->i_count, GL_UNSIGNED_INT, mesh->indices);
#endif
glDisableClientState(GL_VERTEX_ARRAY);
}
#endif
if (tr_state->visual->compositor->bvol)
visual_3d_draw_bounds(tr_state, mesh);
GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[V3D] Done drawing mesh %p\n", mesh));
glGetError();
}
#if !defined(GPAC_USE_GLES1X) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES2)
static GLubyte hatch_horiz[] = {
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
};
static GLubyte hatch_vert[] = {
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
};
static GLubyte hatch_up[] = {
0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30,
0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03,
0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30,
0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03,
0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30,
0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03,
0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30,
0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03,
0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30,
0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03,
0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30,
0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03,
0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30,
0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03,
0xc0, 0xc0, 0xc0, 0xc0, 0x30, 0x30, 0x30, 0x30,
0x0c, 0x0c, 0x0c, 0x0c, 0x03, 0x03, 0x03, 0x03
};
static GLubyte hatch_down[] = {
0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c,
0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0,
0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c,
0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0,
0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c,
0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0,
0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c,
0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0,
0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c,
0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0,
0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c,
0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0,
0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c,
0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0,
0x03, 0x03, 0x03, 0x03, 0x0c, 0x0c, 0x0c, 0x0c,
0x30, 0x30, 0x30, 0x30, 0xc0, 0xc0, 0xc0, 0xc0
};
static GLubyte hatch_cross[] = {
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c
};
void visual_3d_mesh_hatch(GF_TraverseState *tr_state, GF_Mesh *mesh, u32 hatchStyle, SFColor hatchColor)
{
if (mesh->mesh_type != MESH_TRIANGLES) return;
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].pos);
if (mesh->flags & MESH_IS_2D) {
glDisableClientState(GL_NORMAL_ARRAY);
glNormal3f(0, 0, 1.0f);
glDisable(GL_CULL_FACE);
} else {
glEnableClientState(GL_NORMAL_ARRAY );
glNormalPointer(GL_FLOAT, sizeof(GF_Vertex), &mesh->vertices[0].normal);
/*if mesh is transparent DON'T CULL*/
if (!tr_state->mesh_is_transparent && (mesh->flags & MESH_IS_SOLID)) {
glEnable(GL_CULL_FACE);
glFrontFace((mesh->flags & MESH_IS_CW) ? GL_CW : GL_CCW);
} else {
glDisable(GL_CULL_FACE);
}
}
glEnable(GL_POLYGON_STIPPLE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/*can't access ISO International Register of Graphical Items www site :)*/
switch (hatchStyle) {
case 5:
glPolygonStipple(hatch_cross);
break;
case 4:
glPolygonStipple(hatch_up);
break;
case 3:
glPolygonStipple(hatch_down);
break;
case 2:
glPolygonStipple(hatch_vert);
break;
case 1:
glPolygonStipple(hatch_horiz);
break;
default:
glDisable(GL_POLYGON_STIPPLE);
break;
}
glColor3f(FIX2FLT(hatchColor.red), FIX2FLT(hatchColor.green), FIX2FLT(hatchColor.blue));
glDrawElements(GL_TRIANGLES, mesh->i_count, GL_UNSIGNED_INT, mesh->indices);
glDisable(GL_POLYGON_STIPPLE);
}
#endif
/*only used for ILS/ILS2D or IFS2D outline*/
void visual_3d_mesh_strike(GF_TraverseState *tr_state, GF_Mesh *mesh, Fixed width, Fixed line_scale, u32 dash_style)
{
#if !defined(GPAC_USE_GLES1X) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES2)
u16 style;
#endif
if (mesh->mesh_type != MESH_LINESET) return;
if (line_scale) width = gf_mulfix(width, line_scale);
width/=2;
#if !defined(GPAC_USE_TINYGL) && !defined(GL_ES_CL_PROFILE)
glLineWidth( FIX2FLT(width));
#endif
#if !defined(GPAC_USE_GLES1X) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES2)
switch (dash_style) {
case GF_DASH_STYLE_DASH:
style = 0x1F1F;
break;
case GF_DASH_STYLE_DOT:
style = 0x3333;
break;
case GF_DASH_STYLE_DASH_DOT:
style = 0x6767;
break;
case GF_DASH_STYLE_DASH_DASH_DOT:
style = 0x33CF;
break;
case GF_DASH_STYLE_DASH_DOT_DOT:
style = 0x330F;
break;
default:
style = 0;
break;
}
if (style) {
u32 factor = FIX2INT(width);
if (!factor) factor = 1;
glEnable(GL_LINE_STIPPLE);
glLineStipple(factor, style);
visual_3d_mesh_paint(tr_state, mesh);
glDisable (GL_LINE_STIPPLE);
} else
#endif
visual_3d_mesh_paint(tr_state, mesh);
}
void visual_3d_clear(GF_VisualManager *visual, SFColor color, Fixed alpha)
{
#ifdef GPAC_USE_GLES1X
glClearColorx(color.red, color.green, color.blue, alpha);
#else
glClearColor(FIX2FLT(color.red), FIX2FLT(color.green), FIX2FLT(color.blue), FIX2FLT(alpha));
#endif
glClear(GL_COLOR_BUFFER_BIT);
}
void visual_3d_fill_rect(GF_VisualManager *visual, GF_Rect rc, SFColorRGBA color)
{
//TODOk - code this for GLES2 ?
#ifdef GPAC_USE_GLES2
#else
glDisable(GL_BLEND | GL_LIGHTING | GL_TEXTURE_2D);
#if defined(GPAC_USE_GLES1X)
glNormal3x(0, 0, FIX_ONE);
if (color.alpha!=FIX_ONE) glEnable(GL_BLEND);
glColor4x(color.red, color.green, color.blue, color.alpha);
{
Fixed v[8];
u16 indices[3];
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
v[0] = rc.x;
v[1] = rc.y;
v[2] = rc.x+rc.width;
v[3] = rc.y-rc.height;
v[4] = rc.x+rc.width;
v[5] = rc.y;
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FIXED, 0, v);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, indices);
v[4] = rc.x;
v[5] = rc.y-rc.height;
glVertexPointer(2, GL_FIXED, 0, v);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, indices);
glDisableClientState(GL_VERTEX_ARRAY);
}
#else
glNormal3f(0, 0, 1);
if (color.alpha!=FIX_ONE) {
glEnable(GL_BLEND);
glColor4f(FIX2FLT(color.red), FIX2FLT(color.green), FIX2FLT(color.blue), FIX2FLT(color.alpha));
} else {
glColor3f(FIX2FLT(color.red), FIX2FLT(color.green), FIX2FLT(color.blue));
}
glBegin(GL_QUADS);
glVertex3f(FIX2FLT(rc.x), FIX2FLT(rc.y), 0);
glVertex3f(FIX2FLT(rc.x), FIX2FLT(rc.y-rc.height), 0);
glVertex3f(FIX2FLT(rc.x+rc.width), FIX2FLT(rc.y-rc.height), 0);
glVertex3f(FIX2FLT(rc.x+rc.width), FIX2FLT(rc.y), 0);
glEnd();
glDisable(GL_COLOR_MATERIAL | GL_COLOR_MATERIAL_FACE);
#endif
glDisable(GL_BLEND);
#endif
}
//note that we always use glReadPixel even if the actual rendering target is a texture
//this is the recommended way by khronos any way
GF_Err compositor_3d_get_screen_buffer(GF_Compositor *compositor, GF_VideoSurface *fb, u32 depth_dump_mode)
{
u32 i;
fb->width = compositor->display_width;
fb->height = compositor->display_height;
/*depthmap-only dump*/
if (depth_dump_mode==1) {
//depth reading not supported on gles <= 1.1
#ifdef GPAC_USE_GLES1X
return GF_NOT_SUPPORTED;
#else
Float *depthp;
Float zFar, zNear;
fb->pitch_x = 0;
fb->pitch_y = compositor->vp_width;
if (compositor->screen_buffer_alloc_size < fb->pitch_y * fb->height) {
compositor->screen_buffer_alloc_size = fb->pitch_y * fb->height;
compositor->screen_buffer = gf_realloc(compositor->screen_buffer, compositor->screen_buffer_alloc_size);
}
fb->video_buffer = compositor->screen_buffer;
//read as float
depthp = (Float*)gf_malloc(sizeof(Float)* fb->pitch_y * fb->height);
fb->pixel_format = GF_PIXEL_GREYSCALE;
#ifndef GPAC_USE_TINYGL
//glPixelTransferf(GL_DEPTH_SCALE, FIX2FLT(compositor->OGLDepthGain) );
//glPixelTransferf(GL_DEPTH_BIAS, FIX2FLT(compositor->OGLDepthOffset) );
#endif
glReadPixels(compositor->vp_x, compositor->vp_y, fb->width, fb->height, GL_DEPTH_COMPONENT, GL_FLOAT, depthp);
//linearize z buffer, from 0 (zfar) to 1 (znear)
zFar = FIX2FLT(compositor->visual->camera.z_far);
zNear = FIX2FLT(compositor->visual->camera.z_near);
for (i=0; i<fb->height*fb->width; i++) {
Float res = ( (2.0f * zNear) / (zFar + zNear - depthp[i] * (zFar - zNear)) ) ;
fb->video_buffer[i] = (u8) ( 255.0 * (1.0 - res));
}
gf_free(depthp);
#endif /*GPAC_USE_GLES1X*/
}
/* RGBDS or RGBD dump*/
else if (depth_dump_mode==2 || depth_dump_mode==3) {
#ifdef GPAC_USE_GLES1X
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor]: RGB+Depth format not implemented in OpenGL ES\n"));
return GF_NOT_SUPPORTED;
#else
char *depth_data=NULL;
u32 size;
fb->pitch_x = 4;
fb->pitch_y = compositor->vp_width*4; /* 4 bytes for each rgbds pixel */
#ifndef GPAC_USE_TINYGL
size = fb->pitch_y * fb->height;
#else
size = 2 * fb->pitch_y * fb->height;
#endif
if (compositor->screen_buffer_alloc_size < size) {
compositor->screen_buffer_alloc_size = size;
compositor->screen_buffer = gf_realloc(compositor->screen_buffer, compositor->screen_buffer_alloc_size);
}
fb->video_buffer = compositor->screen_buffer;
#ifndef GPAC_USE_TINYGL
glReadPixels(0, 0, fb->width, fb->height, GL_RGBA, GL_UNSIGNED_BYTE, fb->video_buffer);
/*
glPixelTransferf(GL_DEPTH_SCALE, FIX2FLT(compositor->OGLDepthGain));
glPixelTransferf(GL_DEPTH_BIAS, FIX2FLT(compositor->OGLDepthOffset));
*/
depth_data = (char*) gf_malloc(sizeof(char)*fb->width*fb->height);
glReadPixels(0, 0, fb->width, fb->height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, depth_data);
if (depth_dump_mode==2) {
fb->pixel_format = GF_PIXEL_RGBDS;
/*this corresponds to the RGBDS ordering*/
for (i=0; i<fb->height*fb->width; i++) {
u8 ds;
/* erase lowest-weighted depth bit */
u8 depth = depth_data[i] & 0xfe;
/*get alpha*/
ds = (fb->video_buffer[i*4 + 3]);
/* if heaviest-weighted alpha bit is set (>128) , turn on shape bit*/
if (ds & 0x80) depth |= 0x01;
fb->video_buffer[i*4+3] = depth; /*insert depth onto alpha*/
}
/*this corresponds to RGBD ordering*/
} else if (depth_dump_mode==3) {
fb->pixel_format = GF_PIXEL_RGBD;
for (i=0; i<fb->height*fb->width; i++)
fb->video_buffer[i*4+3] = depth_data[i];
}
#else
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor]: RGB+Depth format not implemented in TinyGL\n"));
return GF_NOT_SUPPORTED;
#endif
#endif /*GPAC_USE_GLES1X*/
} else { /*if (compositor->user && (compositor->user->init_flags & GF_VOUT_WINDOW_TRANSPARENT))*/
u32 size;
fb->pitch_x = 3;
if (!compositor->opfmt && compositor->forced_alpha) {
fb->pitch_x = 4;
} else if (compositor->opfmt==GF_PIXEL_RGBA) {
fb->pitch_x = 4;
}
fb->pitch_y = fb->pitch_x * fb->width;
size = fb->pitch_y * fb->height;
if (compositor->screen_buffer_alloc_size < size) {
compositor->screen_buffer_alloc_size = size;
compositor->screen_buffer = gf_realloc(compositor->screen_buffer, compositor->screen_buffer_alloc_size);
compositor->line_buffer = gf_realloc(compositor->line_buffer, fb->pitch_y);
}
fb->video_buffer = compositor->screen_buffer;
fb->pixel_format = (fb->pitch_x == 4) ? GF_PIXEL_RGBA : GF_PIXEL_RGB;
if (compositor->fbo_id) compositor_3d_enable_fbo(compositor, GF_TRUE);
glReadPixels(0, 0, fb->width, fb->height, (fb->pitch_x == 4) ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, fb->video_buffer);
if (compositor->fbo_id) compositor_3d_enable_fbo(compositor, GF_FALSE);
/* } else {
fb->pitch_x = 3;
fb->pitch_y = 3*compositor->vp_width;
fb->video_buffer = (char*)gf_malloc(sizeof(char) * fb->pitch_y * fb->height);
fb->pixel_format = GF_PIXEL_RGB;
glReadPixels(compositor->vp_x, compositor->vp_y, fb->width, fb->height, GL_RGB, GL_UNSIGNED_BYTE, fb->video_buffer);
*/
}
#ifndef GPAC_USE_TINYGL
/*flip image (OpenGL always handle image data bottom to top) */
u32 hy = fb->height/2;
for (i=0; i<hy; i++) {
memcpy(compositor->line_buffer, fb->video_buffer+ i*fb->pitch_y, fb->pitch_y);
memcpy(fb->video_buffer + i*fb->pitch_y, fb->video_buffer + (fb->height - 1 - i) * fb->pitch_y, fb->pitch_y);
memcpy(fb->video_buffer + (fb->height - 1 - i) * fb->pitch_y, compositor->line_buffer, fb->pitch_y);
}
#endif
return GF_OK;
}
GF_Err compositor_3d_release_screen_buffer(GF_Compositor *compositor, GF_VideoSurface *framebuffer)
{
framebuffer->video_buffer = 0;
return GF_OK;
}
GF_Err compositor_3d_get_offscreen_buffer(GF_Compositor *compositor, GF_VideoSurface *fb, u32 view_idx, u32 depth_dump_mode)
{
//TODOk - habdle offscreen buffers through frameBuffer objects, no read back j
#if !defined(GPAC_USE_GLES1X) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES2)
char *tmp;
u32 hy, i;
/*not implemented yet*/
if (depth_dump_mode) return GF_NOT_SUPPORTED;
if (view_idx>=compositor->visual->nb_views) return GF_BAD_PARAM;
fb->width = compositor->visual->auto_stereo_width;
fb->height = compositor->visual->auto_stereo_height;
fb->pixel_format = GF_PIXEL_RGB;
fb->pitch_y = 3*fb->width;
fb->video_buffer = gf_malloc(sizeof(char)*3*fb->width*fb->height);
if (!fb->video_buffer) return GF_OUT_OF_MEM;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, compositor->visual->gl_textures[view_idx]);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, fb->video_buffer);
glDisable(GL_TEXTURE_2D);
/*flip image (OpenGL always handle image data bottom to top) */
tmp = (char*)gf_malloc(sizeof(char)*fb->pitch_y);
hy = fb->height/2;
for (i=0; i<hy; i++) {
memcpy(tmp, fb->video_buffer+ i*fb->pitch_y, fb->pitch_y);
memcpy(fb->video_buffer + i*fb->pitch_y, fb->video_buffer + (fb->height - 1 - i) * fb->pitch_y, fb->pitch_y);
memcpy(fb->video_buffer + (fb->height - 1 - i) * fb->pitch_y, tmp, fb->pitch_y);
}
gf_free(tmp);
return GF_OK;
#else
return GF_NOT_SUPPORTED;
#endif
}
void visual_3d_point_sprite(GF_VisualManager *visual, Drawable *stack, GF_TextureHandler *txh, GF_TraverseState *tr_state)
{
//todo - allow point sprites for GLES2 ?
#if !defined(GPAC_USE_GLES1X) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_GLES2)
u32 w, h;
u32 pixel_format, stride;
u8 *data;
Float r, g, b;
Fixed x, y;
Float inc, scale;
Bool in_strip;
GF_Node *txtrans = NULL;
if ((visual->compositor->depth_gl_type==GF_SC_DEPTH_GL_POINTS) && visual->compositor->gl_caps.point_sprite) {
Float z;
static GLfloat none[3] = { 1.0f, 0, 0 };
data = (u8 *) gf_sc_texture_get_data(txh, &pixel_format);
if (!data) return;
if (pixel_format!=GF_PIXEL_RGBD) return;
stride = txh->stride;
if (txh->pixelformat==GF_PIXEL_YUVD) stride *= 4;
glPointSize(1.0f * visual->compositor->zoom);
glDepthMask(GL_FALSE);
glPointParameterfv(GL_DISTANCE_ATTENUATION_EXT, none);
glPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE_EXT, 0.0);
glEnable(GL_POINT_SMOOTH);
glDisable(GL_LIGHTING);
// scale = FIX2FLT(visual->compositor->depth_gl_scale);
inc = 1;
if (!tr_state->pixel_metrics) inc /= FIX2FLT(tr_state->min_hsize);
// x = 0;
y = 1;
y = gf_mulfix(y, INT2FIX(txh->height/2));
if (!tr_state->pixel_metrics) y = gf_divfix(y, tr_state->min_hsize);
glBegin(GL_POINTS);
for (h=0; h<txh->height; h++) {
x = -1;
x = gf_mulfix(x, INT2FIX(txh->width/2));
if (!tr_state->pixel_metrics) x = gf_divfix(x, tr_state->min_hsize);
for (w=0; w<txh->width; w++) {
u8 *p = data + h*stride + w*4;
r = p[0];
r /= 255;
g = p[1];
g /= 255;
b = p[2];
b /= 255;
z = p[3];
z = z / 255;
glColor4f(r, g, b, 1.0);
glVertex3f(FIX2FLT(x), FIX2FLT(y), FIX2FLT(-z)*60);
x += FLT2FIX(inc);
}
y -= FLT2FIX(inc);
}
glEnd();
glDepthMask(GL_TRUE);
return;
}
if (visual->compositor->depth_gl_type==GF_SC_DEPTH_GL_STRIPS) {
u32 first_pass;
Float delta = FIX2FLT(visual->compositor->depth_gl_strips_filter);
if (!delta) first_pass = 2;
else first_pass = 1;
data = (u8 *) gf_sc_texture_get_data(txh, &pixel_format);
if (!data) return;
if (pixel_format!=GF_PIXEL_RGBD) return;
stride = txh->stride;
if (txh->pixelformat==GF_PIXEL_YUVD) stride *= 4;
glDepthMask(GL_FALSE);
glDisable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glDisable(GL_POINT_SMOOTH);
glDisable(GL_FOG);
restart:
scale = FIX2FLT(visual->compositor->depth_gl_scale);
inc = 1;
if (!tr_state->pixel_metrics) inc /= FIX2FLT(tr_state->min_hsize);
// x = 0;
y = 1;
y = gf_mulfix(y, INT2FIX(txh->height/2));
if (!tr_state->pixel_metrics) y = gf_divfix(y, tr_state->min_hsize);
in_strip = 0;
for (h=0; h<txh->height - 1; h++) {
u8 *src = data + h*stride;
x = -1;
x = gf_mulfix(x, INT2FIX(txh->width/2));
if (!tr_state->pixel_metrics) x = gf_divfix(x, tr_state->min_hsize);
for (w=0; w<txh->width; w++) {
u8 *p1 = src + w*4;
u8 *p2 = src + w*4 + stride;
Float z1 = p1[3];
Float z2 = p2[3];
if (first_pass==1) {
if ((z1>delta) || (z2>delta))
{
#if 0
if (in_strip) {
glEnd();
in_strip = 0;
}
#endif
x += FLT2FIX(inc);
continue;
}
} else if (first_pass==0) {
if ((z1<=delta) || (z2<=delta))
{
if (in_strip) {
glEnd();
in_strip = 0;
}
x += FLT2FIX(inc);
continue;
}
}
z1 = z1 / 255;
z2 = z2 / 255;
r = p1[0];
r /= 255;
g = p1[1];
g /= 255;
b = p1[2];
b /= 255;
if (!in_strip) {
glBegin(GL_TRIANGLE_STRIP);
in_strip = 1;
}
glColor3f(r, g, b);
glVertex3f(FIX2FLT(x), FIX2FLT(y), FIX2FLT(z1)*scale);
r = p2[0];
r /= 255;
g = p2[1];
g /= 255;
b = p2[2];
b /= 255;
glColor3f(r, g, b);
glVertex3f(FIX2FLT(x), FIX2FLT(y)-inc, FIX2FLT(z2)*scale);
x += FLT2FIX(inc);
}
if (in_strip) {
glEnd();
in_strip = 0;
}
y -= FLT2FIX(inc);
}
if (first_pass==1) {
first_pass = 0;
goto restart;
}
return;
}
glColor3f(1.0, 0.0, 0.0);
/*render using vertex array*/
if (!stack->mesh) {
stack->mesh = new_mesh();
stack->mesh->vbo_dynamic = 1;
inc = 1;
if (!tr_state->pixel_metrics) inc /= FIX2FLT(tr_state->min_hsize);
// x = 0;
y = 1;
y = gf_mulfix(y, FLT2FIX(txh->height/2));
if (!tr_state->pixel_metrics) y = gf_divfix(y, tr_state->min_hsize);
if (txh->width>1 && txh->height>1) {
for (h=0; h<txh->height; h++) {
u32 idx_offset = h ? ((h-1)*txh->width) : 0;
x = -1;
x = gf_mulfix(x, FLT2FIX(txh->width/2));
if (tr_state->min_hsize && !tr_state->pixel_metrics) x = gf_divfix(x, tr_state->min_hsize);
for (w=0; w<txh->width; w++) {
mesh_set_vertex(stack->mesh, x, y, 0, 0, 0, -FIX_ONE, INT2FIX(w / (txh->width-1)), INT2FIX((txh->height - h -1) / (txh->height-1)) );
x += FLT2FIX(inc);
/*set triangle*/
if (h && w) {
u32 first_idx = idx_offset + w - 1;
mesh_set_triangle(stack->mesh, first_idx, first_idx+1, txh->width + first_idx +1);
mesh_set_triangle(stack->mesh, first_idx, txh->width + first_idx, txh->width + first_idx +1);
}
}
y -= FLT2FIX(inc);
}
/*force recompute of Z*/
txh->needs_refresh = 1;
}
}
/*texture has been updated, recompute Z*/
if (txh->needs_refresh) {
Fixed f_scale = FLT2FIX(visual->compositor->depth_gl_scale);
txh->needs_refresh = 0;
data = (u8 *) gf_sc_texture_get_data(txh, &pixel_format);
if (!data) return;
if (pixel_format!=GF_PIXEL_RGB_DEPTH) return;
data += txh->height*txh->width*3;
for (h=0; h<txh->height; h++) {
u8 *src = data + h * txh->width;
for (w=0; w<txh->width; w++) {
u8 d = src[w];
Fixed z = INT2FIX(d);
z = gf_mulfix(z / 255, f_scale);
stack->mesh->vertices[w + h*txh->width].pos.z = z;
}
}
stack->mesh->vbo_dirty = 1;
}
#ifndef GPAC_DISABLE_VRML
if (tr_state->appear) txtrans = ((M_Appearance *)tr_state->appear)->textureTransform;
#endif
tr_state->mesh_num_textures = gf_sc_texture_enable(txh, txtrans);
visual_3d_draw_mesh(tr_state, stack->mesh);
visual_3d_disable_texture(tr_state);
#endif //GPAC_USE_GLES1X
}
GF_Err compositor_3d_setup_fbo(u32 width, u32 height, u32 *fbo_id, u32 *tx_id, u32 *depth_id)
{
#if defined(GPAC_USE_GLES1X)
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to setup FBO object: not supported on OpenGL ES 1.x\n"));
return GF_NOT_SUPPORTED;
#else
if (! *tx_id) {
glGenTextures(1, tx_id);
}
glBindTexture(GL_TEXTURE_2D, *tx_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
if (! *fbo_id)
glGenFramebuffers(1, fbo_id);
glBindFramebuffer(GL_FRAMEBUFFER, *fbo_id);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *tx_id, 0);
if (! *depth_id)
glGenRenderbuffers(1, depth_id);
glBindRenderbuffer(GL_RENDERBUFFER, *depth_id);
#if defined(GPAC_CONFIG_IOS) || defined(GPAC_CONFIG_ANDROID) || defined(GPAC_USE_GLES2)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
#else
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
#endif
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *depth_id);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch (status) {
case GL_FRAMEBUFFER_COMPLETE:
break;
default:
GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to setup FBO object: FBO status %08x\n", status));
return GF_NOT_SUPPORTED;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return GF_OK;
#endif
}
void compositor_3d_delete_fbo(u32 *fbo_id, u32 *fbo_tx_id, u32 *fbo_depth_id, Bool keep_tx_id)
{
#ifndef GPAC_USE_GLES1X
if (*fbo_id) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, fbo_id);
*fbo_id = 0;
}
if (*fbo_depth_id) {
glDeleteRenderbuffers(1, fbo_depth_id);
*fbo_depth_id = 0;
}
if (*fbo_tx_id && !keep_tx_id) {
glDeleteTextures(1, fbo_tx_id);
*fbo_tx_id = 0;
}
#endif
}
u32 compositor_3d_get_fbo_pixfmt()
{
return GL_TEXTURE_2D;
}
void compositor_3d_enable_fbo(GF_Compositor *compositor, Bool enable)
{
#ifndef GPAC_USE_GLES1X
glBindFramebuffer(GL_FRAMEBUFFER, enable ? compositor->fbo_id : 0);
if (!enable)
glBindTexture(GL_TEXTURE_2D, 0);
#endif
}
void visual_3d_clean_state(GF_VisualManager *visual)
{
glGetError();
}
#endif //!defined(GPAC_DISABLE_3D) && !defined(GPAC_DISABLE_COMPOSITOR)