You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
393 lines
10 KiB
393 lines
10 KiB
|
|
#include <cassert> |
|
#include <cstddef> |
|
#include <cstdlib> |
|
#include <cstring> |
|
#include <string> |
|
#include <fstream> |
|
#include <iostream> |
|
|
|
#include <glm/gtc/matrix_transform.hpp> |
|
|
|
#include "asset.h" |
|
#define GL_DEBUG_IMPLEMENTATION |
|
#include "GLDebug.h" |
|
#include "shader.h" |
|
|
|
|
|
|
|
// NOTE: forward declarations |
|
|
|
const std::string dumpTextFile(const char* filepath); |
|
bool |
|
parseShaderUniforms(memory_arena* arena, shader_program* s, GLContext* gl_ctx); |
|
bool |
|
parseUniformBlocks(memory_arena* arena, shader_program* s, GLContext* gl_ctx); |
|
bool |
|
parseAttributes(memory_arena* arena, shader_program* s, GLContext* gl_ctx); |
|
void initCTXSizes(GLContext* gl_ctx); |
|
|
|
|
|
// NOTE: interface |
|
|
|
// TODO: clean up this function |
|
bool |
|
addShaderProgram(memory_arena* arena, |
|
GLContext* gl_ctx, |
|
const char* vs, |
|
const char* fs, |
|
const char* name) |
|
{ |
|
if (gl_ctx->num_shaders >= gl_ctx->max_shaders) { |
|
printf("%s(), GLContext->shaders full\n", __FUNCTION__); |
|
return false; |
|
} |
|
|
|
shader_program* s = &gl_ctx->shaders[gl_ctx->num_shaders]; |
|
gl_ctx->num_shaders++; |
|
s->name = arenaCopyCStr(arena, name); |
|
|
|
const u32 max_len = 256; |
|
std::string hash_str = vs; |
|
hash_str += fs; |
|
s->hash = utilFNV64a_str(hash_str.substr(0, max_len).c_str()); |
|
// FIXME: should probably check the hash here against other shaders loaded |
|
|
|
std::string vert = dumpTextFile(vs); |
|
std::string frag = dumpTextFile(fs); |
|
|
|
if (vert.size() > 0 && frag.size() > 0) { |
|
const char* vert_c = vert.c_str(); |
|
const char* frag_c = frag.c_str(); |
|
|
|
GLuint vs_id = glCreateShader(GL_VERTEX_SHADER); |
|
GLuint fs_id = glCreateShader(GL_FRAGMENT_SHADER); |
|
glShaderSource(vs_id, 1, &vert_c, NULL); |
|
glShaderSource(fs_id, 1, &frag_c, NULL); |
|
// TODO: both of these can fail |
|
glCompileShader(vs_id); |
|
glCompileShader(fs_id); |
|
s->prog_id = glCreateProgram(); |
|
glAttachShader(s->prog_id, vs_id); |
|
glAttachShader(s->prog_id, fs_id); |
|
|
|
glLinkProgram(s->prog_id); |
|
GLint is_linked = 0; |
|
glGetProgramiv(s->prog_id, GL_LINK_STATUS, &is_linked); |
|
|
|
if (is_linked) { |
|
// NOTE: need to set context maximums after at least one shader has |
|
// been linked |
|
initCTXSizes(gl_ctx); |
|
#if 0 |
|
dumpShader(s->prog_id); |
|
#endif |
|
s->model_xform_id = glGetUniformLocation(s->prog_id, "model_xform"); |
|
glDetachShader(s->prog_id, vs_id); |
|
glDetachShader(s->prog_id, fs_id); |
|
glDeleteShader(vs_id); |
|
glDeleteShader(fs_id); |
|
|
|
if (parseShaderUniforms(arena, s, gl_ctx) |
|
&& parseUniformBlocks(arena, s, gl_ctx) |
|
&& parseAttributes(arena, s, gl_ctx)) |
|
{ |
|
return true; |
|
} else { |
|
printf("%s(), Error parsing shader component\n", __FUNCTION__); |
|
return false; |
|
} |
|
|
|
} |
|
|
|
printf("%s(), Error linking shader\n", __FUNCTION__); |
|
return false; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void |
|
renderVAO(GLmesh* glmesh) |
|
{ |
|
glUseProgram(glmesh->shader->prog_id); |
|
glBindVertexArray(glmesh->vao_id); |
|
glUniformMatrix4fv(glmesh->shader->model_xform_id, 1, GL_FALSE, |
|
(float*) glmesh->model_xform); |
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, glmesh->idx_buf_id); |
|
glDrawElements( |
|
glmesh->draw_mode, glmesh->num_indices, GL_UNSIGNED_SHORT, 0); |
|
glBindVertexArray(0); |
|
} |
|
|
|
|
|
// NOTE: internal |
|
|
|
const std::string |
|
dumpTextFile(const char* filepath) |
|
{ |
|
std::ifstream fs { filepath }; |
|
std::string s { |
|
std::istreambuf_iterator<char>(fs), |
|
std::istreambuf_iterator<char>()}; |
|
|
|
if (!fs || !fs.good()) |
|
std::cout << "error reading file, " << filepath << "\n"; |
|
|
|
return s; |
|
} |
|
|
|
// NOTE: returns sizes based on GLSL layout std140 |
|
// https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout |
|
u32 |
|
getGLTypeSize(GLenum e) |
|
{ |
|
switch (e) { |
|
case 0x8b51: return 4 * sizeof(GLfloat); // GL_FLOAT_VEC3 |
|
case 0x8b52: return 4 * sizeof(GLfloat); // GL_FLOAT_VEC4 |
|
case 0x8b5c: return 16 * sizeof(GLfloat); // GL_FLOAT_MAT4 |
|
default: |
|
printf("%s(), unknown GLenum\n", __FUNCTION__); |
|
return 0; |
|
} |
|
} |
|
|
|
const gl_uniform |
|
parseUniform(memory_arena* arena, shader_program* s, u32 uniform_idx) |
|
{ |
|
gl_uniform unif = {0}; |
|
GLchar unif_name[256] = {0}; |
|
GLsizei name_len = 0; |
|
|
|
glGetActiveUniform(s->prog_id, |
|
uniform_idx, |
|
sizeof(unif_name), |
|
&name_len, |
|
&unif.num_elements, |
|
&unif.data_type, |
|
unif_name); |
|
glGetActiveUniformsiv(s->prog_id, 1, &uniform_idx, |
|
GL_UNIFORM_BLOCK_INDEX, &unif.block_idx); |
|
|
|
unif.idx = uniform_idx; |
|
unif.data_size = getGLTypeSize(unif.data_type); |
|
assert(unif.data_size > 0); |
|
unif.name = arenaCopyCStr(arena, unif_name); |
|
unif.location = glGetUniformLocation(s->prog_id, unif.name); |
|
|
|
if (unif.data_size == 0) |
|
printf("%s(), error getting data_size\n", __FUNCTION__); |
|
|
|
return unif; |
|
} |
|
|
|
bool |
|
parseShaderUniforms(memory_arena* arena, shader_program* s, GLContext* gl_ctx) |
|
{ |
|
// NOTE: only add uniforms in the default block to the base uniform array |
|
GLint num_uniforms_total = 0; |
|
glGetProgramiv(s->prog_id, GL_ACTIVE_UNIFORMS, &num_uniforms_total); |
|
GLint indices[num_uniforms_total]; |
|
|
|
for (u32 i = 0; i < (u32) num_uniforms_total; i++) { |
|
GLint block_idx = 0; |
|
glGetActiveUniformsiv(s->prog_id, 1, &i, |
|
GL_UNIFORM_BLOCK_INDEX, &block_idx); |
|
|
|
if (block_idx == -1) { |
|
indices[s->num_uniforms] = i; |
|
s->num_uniforms++; |
|
} |
|
} |
|
|
|
s->uniforms = (gl_uniform*) arenaAllocateBlock( |
|
arena, s->num_uniforms * sizeof(gl_uniform)); |
|
|
|
for (u32 i = 0; i < s->num_uniforms; i++) { |
|
const gl_uniform unif = parseUniform(arena, s, indices[i]); |
|
std::memcpy(&s->uniforms[i], &unif, sizeof(unif)); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
i32 |
|
ctxGetUniformBlockBinding(GLContext* gl_ctx, const char* name) |
|
{ |
|
for (u32 i = 0; i < gl_ctx->num_ubos; i++) { |
|
gl_buffer& ubo = gl_ctx->uniform_buffers[i]; |
|
|
|
if (std::strstr(ubo.name, name)) |
|
return ubo.binding_idx; |
|
} |
|
|
|
printf("%s(), no buffer found with name: %s\n", __FUNCTION__, name); |
|
return -1; |
|
} |
|
|
|
bool |
|
parseUniformBlocks(memory_arena* arena, shader_program* s, GLContext* gl_ctx) |
|
{ |
|
glGetProgramiv(s->prog_id, GL_ACTIVE_UNIFORM_BLOCKS, |
|
(GLint*) &s->num_blocks); |
|
s->uniform_blocks = (GLUniformBlock*) arenaAllocateBlock(arena, |
|
s->num_blocks * sizeof(GLUniformBlock)); |
|
|
|
for (u32 i = 0; i < s->num_blocks; i++) { |
|
GLUniformBlock& ub = s->uniform_blocks[i]; |
|
ub.block_id = i; |
|
|
|
GLchar block_name[256] = {0}; |
|
glGetActiveUniformBlockName( |
|
s->prog_id, i, 256, &ub.name_len, block_name); |
|
ub.name_len++; // NOTE: space for null term |
|
ub.name = (char*) arenaAllocateBlock(arena, ub.name_len * sizeof(char)); |
|
std::strncpy(ub.name, block_name, ub.name_len); |
|
|
|
glGetActiveUniformBlockiv(s->prog_id, i, |
|
GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, (GLint*) &ub.num_uniforms); |
|
ub.uniforms = (gl_uniform*) |
|
arenaAllocateBlock(arena, ub.num_uniforms * sizeof(gl_uniform)); |
|
GLint indices[ub.num_uniforms] = {0}; |
|
glGetActiveUniformBlockiv(s->prog_id, i, |
|
GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, (GLint*) &indices); |
|
|
|
for (u32 j = 0; j < ub.num_uniforms; j++) { |
|
const gl_uniform unif = parseUniform(arena, s, indices[j]); |
|
std::memcpy(&ub.uniforms[j], &unif, sizeof(unif)); |
|
} |
|
|
|
ub.binding_idx = ctxGetUniformBlockBinding(gl_ctx, ub.name); |
|
|
|
#if 0 |
|
if (ub.binding_idx < 0) |
|
return false; |
|
|
|
glUniformBlockBinding(s->prog_id, i, ub.binding_idx); |
|
#endif |
|
} |
|
|
|
|
|
return true; |
|
} |
|
|
|
bool |
|
parseAttributes(memory_arena* arena, shader_program* s, GLContext* gl_ctx) |
|
{ |
|
// TODO: parse buffers (vert, normal, uv), glGetActiveAttrib() |
|
/// |
|
printf("%s(), FIXME:\n", __FUNCTION__); |
|
return true; |
|
} |
|
|
|
void |
|
initTransforms(memory_arena* arena, |
|
transforms* xforms, |
|
gl_buffer* xform_ubo, |
|
GLContext* gl_ctx, |
|
float fov, |
|
float near_clip_plane, |
|
float aspect_ratio, |
|
glm::vec3 cam_pos, |
|
glm::vec3 look_pos) |
|
{ |
|
xforms->view_xform = glm::lookAt(cam_pos, look_pos, glm::vec3(0, 1, 0)); |
|
xforms->proj_xform = glm::infinitePerspective( |
|
glm::radians(fov), aspect_ratio, near_clip_plane); |
|
|
|
glGenBuffers(1, &xform_ubo->id); |
|
xform_ubo->target = GL_UNIFORM_BUFFER; |
|
xform_ubo->data_type = GL_FLOAT; |
|
xform_ubo->data_size = sizeof(*xforms); |
|
xform_ubo->name = arenaCopyCStr(arena, "matrices"); |
|
|
|
glBindBuffer(xform_ubo->target, xform_ubo->id); |
|
glBufferData(xform_ubo->target, sizeof(*xforms), xforms, |
|
GL_DYNAMIC_DRAW); |
|
// bindbufferbase |
|
xform_ubo->binding_idx = gl_ctx->binding_count++; |
|
glBindBufferBase(xform_ubo->target, xform_ubo->binding_idx, xform_ubo->id); |
|
glBindBuffer(xform_ubo->target, 0); |
|
} |
|
|
|
void |
|
initCTXSizes(GLContext* gl_ctx) |
|
{ |
|
// NOTE: see https://docs.gl/gl3/glGet for other useful context info |
|
if (gl_ctx->max_binding_points == 0) { |
|
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, |
|
&gl_ctx->max_binding_points); |
|
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &gl_ctx->max_vertex_blocks); |
|
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, |
|
&gl_ctx->max_fragment_blocks); |
|
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &gl_ctx->max_ublock_size); |
|
|
|
#if 1 |
|
printf("%s(), context size info set\n", __FUNCTION__); |
|
printf("GL_MAX_UNIFORM_BUFFER_BINDINGS: %d\n", |
|
gl_ctx->max_binding_points); |
|
printf("GL_MAX_VERTEX_UNIFORM_BLOCKS: %d\n", |
|
gl_ctx->max_vertex_blocks); |
|
printf("GL_MAX_FRAGMENT_UNIFORM_BLOCKS: %d\n", |
|
gl_ctx->max_fragment_blocks); |
|
printf("GL_MAX_UNIFORM_BLOCK_SIZE: %d\n", |
|
gl_ctx->max_ublock_size); |
|
#endif |
|
} |
|
} |
|
|
|
GLmesh |
|
loadGLMesh(shader_program* s, |
|
const mesh& m, |
|
GLenum draw_mode, |
|
const glm::vec3& pos) |
|
{ |
|
GLmesh gm = {0}; |
|
gm.num_indices = m.num_indices; |
|
gm.draw_mode = draw_mode; |
|
// NOTE: okay to store shader_program pointer on GLmesh here because we |
|
// won't ever delete the shader |
|
gm.shader = s; |
|
|
|
glUseProgram(s->prog_id); |
|
glGenVertexArrays(1, &gm.vao_id); |
|
glBindVertexArray(gm.vao_id); |
|
|
|
gm.model_xform = (glm::mat4*) std::calloc(1, sizeof(glm::mat4)); |
|
*gm.model_xform = glm::translate(glm::mat4(1), pos); |
|
glUniformMatrix4fv( |
|
s->model_xform_id, 1, GL_FALSE, (float*) gm.model_xform); |
|
|
|
glGenBuffers(1, &gm.vert_buf_id); |
|
glBindBuffer(GL_ARRAY_BUFFER, gm.vert_buf_id); |
|
glBufferData(GL_ARRAY_BUFFER, |
|
m.num_vertices * 3 * sizeof(GLfloat), |
|
m.vertices, |
|
GL_STATIC_DRAW); |
|
glVertexAttribPointer(POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0); |
|
glEnableVertexAttribArray(POSITION); |
|
|
|
if (m.normals != nullptr) { |
|
glGenBuffers(1, &gm.norm_buf_id); |
|
glBindBuffer(GL_ARRAY_BUFFER, gm.norm_buf_id); |
|
glBufferData(GL_ARRAY_BUFFER, |
|
m.num_vertices * 3 * sizeof(GLfloat), |
|
m.normals, |
|
GL_STATIC_DRAW); |
|
glVertexAttribPointer(NORMAL, 3, GL_FLOAT, GL_FALSE, 0, 0); |
|
glEnableVertexAttribArray(NORMAL); |
|
} |
|
|
|
glGenBuffers(1, &gm.idx_buf_id); |
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gm.idx_buf_id); |
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, |
|
m.num_indices * sizeof(u16), |
|
m.indices, |
|
GL_STATIC_DRAW); |
|
|
|
glBindVertexArray(0); |
|
glUseProgram(0); |
|
|
|
return gm; |
|
} |
|
|
|
|