312 lines
7.6 KiB
C
312 lines
7.6 KiB
C
/*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <shimatta-opengl-program.h>
|
|
#include <epoxy/gl.h>
|
|
|
|
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;
|
|
}
|