/* * This file is part of Shimatta OpenGL. * * Foobar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Shimatta OpenGL 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GtkGraphView. If not, see . */ #include #include struct _ShimattaOpenglProgram { GObject super; }; typedef struct { GLuint shader_id; gboolean compiled; char *fragment_file; char *vertex_file; char *geometry_file; char *fragment_src; char *vertex_src; char *geometry_src; } ShimattaOpenglProgramPrivate; G_DEFINE_TYPE_WITH_PRIVATE(ShimattaOpenglProgram, shimatta_opengl_program, G_TYPE_OBJECT); static void shimatta_opengl_program_dispose(GObject *self) { ShimattaOpenglProgram *prog; ShimattaOpenglProgramPrivate *priv; g_return_if_fail(SHIMATTA_IS_OPENGL_PROGRAM(self)); prog = SHIMATTA_OPENGL_PROGRAM(self); priv = shimatta_opengl_program_get_instance_private(prog); if (priv->compiled) { priv->compiled = false; glDeleteProgram(priv->shader_id); } if (priv->fragment_src) g_free(priv->fragment_src); if (priv->geometry_src) g_free(priv->geometry_src); if (priv->vertex_src) g_free(priv->vertex_src); if (priv->vertex_file) g_free(priv->vertex_file); if (priv->fragment_file) g_free(priv->fragment_file); if (priv->geometry_file) g_free(priv->geometry_file); } static void shimatta_opengl_program_class_init(ShimattaOpenglProgramClass *klass) { (void)klass; GObjectClass *oclass; oclass = G_OBJECT_CLASS(klass); oclass->dispose = shimatta_opengl_program_dispose; return; } static void shimatta_opengl_program_init(ShimattaOpenglProgram *self) { ShimattaOpenglProgramPrivate *priv; priv = shimatta_opengl_program_get_instance_private(self); priv->shader_id = 0; priv->vertex_src = NULL; priv->vertex_file = NULL; priv->fragment_src = NULL; priv->geometry_src = NULL; priv->fragment_file = NULL; priv->geometry_file = NULL; priv->compiled = false; } ShimattaOpenglProgram *shimatta_opengl_program_new_from_data(const char *vertex_shader, const char *geometry_shader, const char *fragment_shader) { ShimattaOpenglProgram *ret; ShimattaOpenglProgramPrivate *priv; ret = g_object_new(SHIMATTA_TYPE_OPENGL_PROGRAM, NULL); priv = shimatta_opengl_program_get_instance_private(ret); priv->fragment_src = g_strdup(fragment_shader); priv->geometry_src = g_strdup(geometry_shader); priv->vertex_src= g_strdup(vertex_shader); return ret; } ShimattaOpenglProgram *shimatta_opengl_program_new_from_file(const char *vertex_shader, const char *geometry_shader, const char *fragment_shader) { ShimattaOpenglProgram *ret; ShimattaOpenglProgramPrivate *priv; ret = g_object_new(SHIMATTA_TYPE_OPENGL_PROGRAM, NULL); priv = shimatta_opengl_program_get_instance_private(ret); priv->vertex_file = g_strdup(vertex_shader); priv->geometry_file = g_strdup(geometry_shader); priv->fragment_file = g_strdup(fragment_shader); return ret; } int shimatta_opengl_program_use(ShimattaOpenglProgram *program) { ShimattaOpenglProgramPrivate *priv; g_return_val_if_fail(SHIMATTA_IS_OPENGL_PROGRAM(program), -1001); if (!shimatta_opengl_program_is_compiled(program)) return -1; priv = shimatta_opengl_program_get_instance_private(program); glUseProgram(priv->shader_id); return 0; } gboolean shimatta_opengl_program_is_compiled(ShimattaOpenglProgram *program) { ShimattaOpenglProgramPrivate *priv; g_return_val_if_fail(SHIMATTA_IS_OPENGL_PROGRAM(program), FALSE); priv = shimatta_opengl_program_get_instance_private(program); return priv->compiled; } static char *load_shader_from_source_file(const char *src_file) { } static int compile_shader(const char *src, char *error_text, size_t error_text_size, gboolean use_error, GLuint shader_type, GLuint *shader_id_out) { GLuint shader_id; GLint success; int ret = 0; if (!shader_id_out || !src) return -1000; shader_id = glCreateShader(shader_type); glShaderSource(shader_id, 1, &src, NULL); glCompileShader(shader_id); glGetShaderiv(shader_id, GL_COMPILE_STATUS, &success); if (!success) { ret = -1; if (use_error) { glGetShaderInfoLog(shader_id, error_text_size, NULL, error_text); } glDeleteShader(shader_id); } else { *shader_id_out = shader_id; } return ret; } int shimatta_opengl_program_compile(ShimattaOpenglProgram *program, char *error_text, size_t error_text_size) { gboolean use_error = FALSE; ShimattaOpenglProgramPrivate *priv; char *shader_source_code; int ret_val = 0; int status; GLint success; GLuint vertex_shader, fragment_shader, geometry_shader; gboolean vertex_built = FALSE, fragment_built = FALSE, geometry_built = FALSE; g_return_val_if_fail(SHIMATTA_IS_OPENGL_PROGRAM(program), -1001); if (error_text && error_text_size > 0) use_error = TRUE; priv = shimatta_opengl_program_get_instance_private(program); if (priv->compiled == TRUE) { return 1; } priv->shader_id = glCreateProgram(); if (priv->vertex_src) { shader_source_code = priv->vertex_src; } else if (priv->vertex_file) { shader_source_code = load_shader_from_source_file(priv->vertex_file); if (!shader_source_code) { ret_val = -1; goto return_value; } } else { shader_source_code = NULL; } /* Build the vertex shader */ if (shader_source_code) { status = compile_shader(shader_source_code, error_text, error_text_size, use_error, GL_VERTEX_SHADER, &vertex_shader); free(shader_source_code); if (status) { ret_val = -2; goto return_value; } vertex_built = TRUE; glAttachShader(priv->shader_id, vertex_shader); } /* Build the fragment shader */ if (priv->fragment_src) { shader_source_code = priv->fragment_src; } else if (priv->fragment_file) { shader_source_code = load_shader_from_source_file(priv->fragment_file); if (!shader_source_code) { ret_val = -1; goto return_value; } } else { shader_source_code = NULL; } if (shader_source_code) { status = compile_shader(shader_source_code, error_text, error_text_size, use_error, GL_FRAGMENT_SHADER, &fragment_shader); free(shader_source_code); if (status) { ret_val = -2; goto return_value; } fragment_built = TRUE; glAttachShader(priv->shader_id, fragment_shader); } /* Build the geometry shader */ if (priv->geometry_src) { shader_source_code = priv->geometry_src; } else if (priv->geometry_file) { shader_source_code = load_shader_from_source_file(priv->geometry_file); if (!shader_source_code) { ret_val = -1; goto return_value; } } else { shader_source_code = NULL; } if (shader_source_code) { status = compile_shader(shader_source_code, error_text, error_text_size, use_error, GL_GEOMETRY_SHADER, &geometry_shader); free(shader_source_code); if (status) { ret_val = -2; goto return_value; } geometry_built = TRUE; glAttachShader(priv->shader_id, geometry_shader); } glLinkProgram(priv->shader_id); glGetShaderiv(priv->shader_id, GL_LINK_STATUS, &success); if (!success) { if (use_error) glGetShaderInfoLog(priv->shader_id, error_text_size, NULL, error_text); ret_val = -3; glDeleteProgram(priv->shader_id); } else { priv->compiled = TRUE; } return_value: if (vertex_built) glDeleteShader(vertex_shader); if (geometry_built) glDeleteShader(geometry_shader); if (fragment_built) glDeleteShader(fragment_shader); return ret_val; }