321 lines
8.9 KiB
C
321 lines
8.9 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "shadergl.h"
|
|
#include "util/memory.h"
|
|
#include "util/string.h"
|
|
#include "assert/assertgl.h"
|
|
#include "display/shader/shaderunlit.h"
|
|
|
|
#ifdef DUSK_OPENGL_LEGACY
|
|
shaderlegacygl_t SHADER_LEGACY = { 0 };
|
|
#endif
|
|
|
|
errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def) {
|
|
assertNotNull(shader, "Shader cannot be null");
|
|
assertNotNull(def, "Shader definition cannot be null");
|
|
memoryZero(shader, sizeof(shadergl_t));
|
|
|
|
#ifdef DUSK_OPENGL_LEGACY
|
|
glm_mat4_identity(shader->view);
|
|
glm_mat4_identity(shader->proj);
|
|
glm_mat4_identity(shader->model);
|
|
|
|
SHADER_LEGACY.boundShader = NULL;
|
|
errorOk();
|
|
#else
|
|
assertNotNull(def->vert, "Vertex shader source cannot be null");
|
|
assertNotNull(def->frag, "Fragment shader source cannot be null");
|
|
|
|
shader->setTexture = def->setTexture;
|
|
|
|
// Create vertex shader
|
|
shader->vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
|
|
errorret_t err = errorGLCheck();
|
|
errorChain(err);
|
|
|
|
glShaderSource(shader->vertexShaderId, 1, &def->vert, NULL);
|
|
err = errorGLCheck();
|
|
if(err.code != ERROR_OK) {
|
|
glDeleteShader(shader->vertexShaderId);
|
|
errorChain(err);
|
|
}
|
|
|
|
glCompileShader(shader->vertexShaderId);
|
|
err = errorGLCheck();
|
|
if(err.code != ERROR_OK) {
|
|
glDeleteShader(shader->vertexShaderId);
|
|
errorChain(err);
|
|
}
|
|
|
|
GLint ok = 0;
|
|
glGetShaderiv(shader->vertexShaderId, GL_COMPILE_STATUS, &ok);
|
|
if(!ok) {
|
|
GLchar log[1024];
|
|
glGetShaderInfoLog(shader->vertexShaderId, sizeof(log), NULL, log);
|
|
glDeleteShader(shader->vertexShaderId);
|
|
errorThrow("Vertex shader compilation failed: %s", log);
|
|
}
|
|
|
|
// Create fragment shader
|
|
shader->fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
|
|
err = errorGLCheck();
|
|
if(err.code != ERROR_OK) {
|
|
glDeleteShader(shader->vertexShaderId);
|
|
errorChain(err);
|
|
}
|
|
|
|
glShaderSource(shader->fragmentShaderId, 1, &def->frag, NULL);
|
|
err = errorGLCheck();
|
|
if(err.code != ERROR_OK) {
|
|
glDeleteShader(shader->vertexShaderId);
|
|
glDeleteShader(shader->fragmentShaderId);
|
|
errorChain(err);
|
|
}
|
|
|
|
glCompileShader(shader->fragmentShaderId);
|
|
err = errorGLCheck();
|
|
if(err.code != ERROR_OK) {
|
|
glDeleteShader(shader->vertexShaderId);
|
|
glDeleteShader(shader->fragmentShaderId);
|
|
errorChain(err);
|
|
}
|
|
|
|
glGetShaderiv(shader->fragmentShaderId, GL_COMPILE_STATUS, &ok);
|
|
if(!ok) {
|
|
GLchar log[1024];
|
|
glGetShaderInfoLog(shader->fragmentShaderId, sizeof(log), NULL, log);
|
|
glDeleteShader(shader->vertexShaderId);
|
|
glDeleteShader(shader->fragmentShaderId);
|
|
errorThrow("Fragment shader compilation failed: %s", log);
|
|
}
|
|
|
|
// Create shader program
|
|
shader->shaderProgramId = glCreateProgram();
|
|
err = errorGLCheck();
|
|
if(err.code != ERROR_OK) {
|
|
glDeleteShader(shader->vertexShaderId);
|
|
glDeleteShader(shader->fragmentShaderId);
|
|
errorChain(err);
|
|
}
|
|
|
|
glAttachShader(shader->shaderProgramId, shader->vertexShaderId);
|
|
err = errorGLCheck();
|
|
if(err.code != ERROR_OK) {
|
|
glDeleteProgram(shader->shaderProgramId);
|
|
glDeleteShader(shader->vertexShaderId);
|
|
glDeleteShader(shader->fragmentShaderId);
|
|
errorChain(err);
|
|
}
|
|
|
|
glAttachShader(shader->shaderProgramId, shader->fragmentShaderId);
|
|
err = errorGLCheck();
|
|
if(err.code != ERROR_OK) {
|
|
glDeleteProgram(shader->shaderProgramId);
|
|
glDeleteShader(shader->vertexShaderId);
|
|
glDeleteShader(shader->fragmentShaderId);
|
|
errorChain(err);
|
|
}
|
|
|
|
glLinkProgram(shader->shaderProgramId);
|
|
err = errorGLCheck();
|
|
if(err.code != ERROR_OK) {
|
|
glDeleteProgram(shader->shaderProgramId);
|
|
glDeleteShader(shader->vertexShaderId);
|
|
glDeleteShader(shader->fragmentShaderId);
|
|
errorChain(err);
|
|
}
|
|
|
|
ok = 0;
|
|
glGetProgramiv(shader->shaderProgramId, GL_LINK_STATUS, &ok);
|
|
if(!ok) {
|
|
GLchar log[1024];
|
|
glGetProgramInfoLog(shader->shaderProgramId, sizeof(log), NULL, log);
|
|
glDeleteProgram(shader->shaderProgramId);
|
|
glDeleteShader(shader->vertexShaderId);
|
|
glDeleteShader(shader->fragmentShaderId);
|
|
errorThrow("Shader program linking failed: %s", log);
|
|
}
|
|
#endif
|
|
|
|
errorOk();
|
|
}
|
|
|
|
errorret_t shaderParamGetLocationGL(
|
|
shadergl_t *shader,
|
|
const char_t *name,
|
|
GLint *location
|
|
) {
|
|
assertNotNull(shader, "Shader cannot be null");
|
|
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
|
assertNotNull(location, "Location cannot be null");
|
|
|
|
#ifdef DUSK_OPENGL_LEGACY
|
|
assertUnreachable("Cannot get uniform locations on legacy opengl.");
|
|
#else
|
|
*location = glGetUniformLocation(shader->shaderProgramId, name);
|
|
errorChain(errorGLCheck());
|
|
if(*location == -1) {
|
|
errorThrow("Uniform '%s' not found in shader.", name);
|
|
}
|
|
#endif
|
|
|
|
errorOk();
|
|
}
|
|
|
|
errorret_t shaderSetMatrixGL(
|
|
shadergl_t *shader,
|
|
const char_t *name,
|
|
mat4 mat
|
|
) {
|
|
assertNotNull(shader, "Shader cannot be null");
|
|
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
|
assertNotNull(mat, "Matrix data cannot be null");
|
|
|
|
#ifdef DUSK_OPENGL_LEGACY
|
|
assertTrue(
|
|
SHADER_LEGACY.boundShader == shader,
|
|
"Shader must be bound to set legacy matrices."
|
|
);
|
|
if(stringCompare(name, SHADER_UNLIT_PROJECTION) == 0) {
|
|
SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_PROJ;
|
|
glm_mat4_copy(mat, shader->proj);
|
|
|
|
} else if(stringCompare(name, SHADER_UNLIT_VIEW) == 0) {
|
|
SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_VIEW;
|
|
glm_mat4_copy(mat, shader->view);
|
|
|
|
} else if(stringCompare(name, SHADER_UNLIT_MODEL) == 0) {
|
|
SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_MODEL;
|
|
glm_mat4_copy(mat, shader->model);
|
|
|
|
} else {
|
|
assertUnreachable("Cannot use a custom matrix on legacy opengl.");
|
|
}
|
|
#else
|
|
GLint location;
|
|
errorChain(shaderParamGetLocationGL(shader, name, &location));
|
|
|
|
glUniformMatrix4fv(location, 1, GL_FALSE, (const GLfloat *)mat);
|
|
errorChain(errorGLCheck());
|
|
#endif
|
|
|
|
errorOk();
|
|
}
|
|
|
|
errorret_t shaderSetTextureGL(
|
|
shadergl_t *shader,
|
|
const char_t *name,
|
|
texture_t *texture
|
|
) {
|
|
assertNotNull(shader, "Shader cannot be null");
|
|
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
|
|
|
#ifdef DUSK_OPENGL_LEGACY
|
|
assertStringEqual(
|
|
name,
|
|
SHADER_UNLIT_TEXTURE,
|
|
"Only one texture supported in legacy opengl."
|
|
);
|
|
|
|
if(texture == NULL) {
|
|
glDisable(GL_TEXTURE_2D);
|
|
errorChain(errorGLCheck());
|
|
errorOk();
|
|
}
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
errorChain(errorGLCheck());
|
|
glBindTexture(GL_TEXTURE_2D, texture->id);
|
|
errorChain(errorGLCheck());
|
|
|
|
#else
|
|
if(shader->setTexture == NULL) {
|
|
assertUnreachable("Shader does not support setting textures.");
|
|
}
|
|
errorChain(shader->setTexture(shader, name, texture));
|
|
#endif
|
|
|
|
errorOk();
|
|
}
|
|
|
|
errorret_t shaderBindGL(shadergl_t *shader) {
|
|
#ifdef DUSK_OPENGL_LEGACY
|
|
assertNotNull(shader, "Cannot bind a null shader.");
|
|
SHADER_LEGACY.boundShader = shader;
|
|
SHADER_LEGACY.dirty = (
|
|
SHADER_LEGACY_DIRTY_MODEL |
|
|
SHADER_LEGACY_DIRTY_PROJ |
|
|
SHADER_LEGACY_DIRTY_VIEW
|
|
);
|
|
#else
|
|
assertNotNull(shader, "Shader cannot be null");
|
|
glUseProgram(shader->shaderProgramId);
|
|
errorChain(errorGLCheck());
|
|
#endif
|
|
|
|
errorOk();
|
|
}
|
|
|
|
errorret_t shaderDisposeGL(shadergl_t *shader) {
|
|
assertNotNull(shader, "Shader cannot be null");
|
|
|
|
#ifdef DUSK_OPENGL_LEGACY
|
|
SHADER_LEGACY.boundShader = NULL;
|
|
#else
|
|
if(shader->shaderProgramId != 0) {
|
|
glDeleteProgram(shader->shaderProgramId);
|
|
}
|
|
|
|
if(shader->vertexShaderId != 0) {
|
|
glDeleteShader(shader->vertexShaderId);
|
|
}
|
|
|
|
if(shader->fragmentShaderId != 0) {
|
|
glDeleteShader(shader->fragmentShaderId);
|
|
}
|
|
|
|
assertNoGLError("Failed disposing shader");
|
|
#endif
|
|
|
|
memoryZero(shader, sizeof(shadergl_t));
|
|
errorOk();
|
|
}
|
|
|
|
#ifdef DUSK_OPENGL_LEGACY
|
|
errorret_t shaderLegacyMatrixUpdate() {
|
|
assertNotNull(SHADER_LEGACY.boundShader, "No shader is currently bound.");
|
|
|
|
if((SHADER_LEGACY.dirty & SHADER_LEGACY_DIRTY_PROJ) != 0) {
|
|
glMatrixMode(GL_PROJECTION);
|
|
errorChain(errorGLCheck());
|
|
glLoadIdentity();
|
|
errorChain(errorGLCheck());
|
|
glMultMatrixf((const GLfloat *)SHADER_LEGACY.boundShader->proj);
|
|
errorChain(errorGLCheck());
|
|
}
|
|
|
|
if((SHADER_LEGACY.dirty & SHADER_LEGACY_DIRTY_VIEW) != 0) {
|
|
glMatrixMode(GL_MODELVIEW);
|
|
errorChain(errorGLCheck());
|
|
glLoadIdentity();
|
|
errorChain(errorGLCheck());
|
|
glMultMatrixf((const GLfloat *)SHADER_LEGACY.boundShader->view);
|
|
errorChain(errorGLCheck());
|
|
}
|
|
|
|
if((SHADER_LEGACY.dirty & SHADER_LEGACY_DIRTY_MODEL) != 0) {
|
|
glMatrixMode(GL_MODELVIEW);
|
|
errorChain(errorGLCheck());
|
|
glMultMatrixf((const GLfloat *)SHADER_LEGACY.boundShader->model);
|
|
errorChain(errorGLCheck());
|
|
}
|
|
|
|
SHADER_LEGACY.dirty = 0;
|
|
errorOk();
|
|
}
|
|
#endif |