diff --git a/shimatta-opengl/include/shimatta-opengl-program.h b/shimatta-opengl/include/shimatta-opengl-program.h index 6ba1bca..b10e902 100644 --- a/shimatta-opengl/include/shimatta-opengl-program.h +++ b/shimatta-opengl/include/shimatta-opengl-program.h @@ -51,7 +51,7 @@ ShimattaOpenglProgram *shimatta_opengl_program_new_from_file(const char *vertex_ * @param program Shader Program * @param error_text Error message from compilation. May be NULL. * @param error_text_size Size in bytes of the error_text buffer. May be 0. - * @return 0 if successful. If Error error text will hold the error + * @return 0 if successful. If Error error text will hold the error. 1 is returned if program is already compiled */ int shimatta_opengl_program_compile(ShimattaOpenglProgram *program, char *error_text, size_t error_text_size); diff --git a/shimatta-opengl/src/shimatta-opengl-program.c b/shimatta-opengl/src/shimatta-opengl-program.c index c7af5a9..3e807f5 100644 --- a/shimatta-opengl/src/shimatta-opengl-program.c +++ b/shimatta-opengl/src/shimatta-opengl-program.c @@ -22,7 +22,7 @@ struct _ShimattaOpenglProgram { }; typedef struct { - GLint shader_id; + GLuint shader_id; gboolean compiled; char *fragment_file; char *vertex_file; @@ -35,9 +35,42 @@ typedef struct { 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; } @@ -73,3 +106,206 @@ ShimattaOpenglProgram *shimatta_opengl_program_new_from_data(const char *vertex_ 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; +}