rbdlsim/src/srender.h

663 lines
16 KiB
C
Raw Normal View History

2020-10-17 22:09:41 +02:00
#ifndef SRENDER_H
#define SRENDER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
2020-10-17 23:20:38 +02:00
#include "vectorial/simd4x4f.h"
2020-10-17 22:09:41 +02:00
typedef struct srndr srndr;
typedef struct srview srview;
typedef struct srcmdbuf srcmdbuf;
typedef enum {
2020-10-17 23:20:38 +02:00
SRndrCmdTypeFrame = 0,
SRndrCmdTypeGrid,
2020-10-17 22:09:41 +02:00
SRndrCmdTypeBox,
SRndrCmdTypeLight,
SRndrCmdTypeSphere
} SRndrCmdType;
typedef struct srcmd {
2020-10-23 15:23:03 +02:00
simd4x4f mat;
simd4f color;
2020-10-17 23:20:38 +02:00
SRndrCmdType type;
2020-10-17 22:09:41 +02:00
} srcmd;
//
// Renderer
//
srndr* srndr_create();
void srndr_destroy(srndr* sr);
//
// View
//
srview* srview_create();
void srview_destroy(srview* sv);
2020-10-17 23:20:38 +02:00
void srview_set_proj(srview* sv, simd4x4f proj);
void srview_set_view(srview* sv, simd4x4f view);
2020-10-17 22:09:41 +02:00
void srview_set_size(srview* sv, int width, int height);
void srview_get_output_texture(srview* sv, GLuint* texture);
//
// Command Buffer and Commands
//
void srcmd_clear (srcmd *cmd);
2020-10-17 22:09:41 +02:00
srcmdbuf* srcmdbuf_create(unsigned int size_max);
2020-10-23 15:23:03 +02:00
void srcmdbuf_clear(srcmdbuf* cmdbuf);
bool srcmdbuf_add(srcmdbuf* cmdbuf, srcmd *cmd);
2020-10-17 22:09:41 +02:00
srcmd* srcmd_create(srcmdbuf* cmdbuf);
void srndr_render(srndr* srndr, srview* sview, srcmdbuf* scmdbuf);
//
// srender Implementation
//
#ifdef SRENDER_IMPLEMENTATION
#include "utils.h"
#include <string.h>
#include <assert.h>
typedef struct srndr {
} srndr;
typedef struct srview {
GLuint width;
GLuint height;
GLuint mFrameBufferId;
GLuint mColorTexture;
GLuint mDepthTexture;
simd4x4f proj;
simd4x4f view;
} srview;
typedef struct srcmdbuf {
int nbufsize;
int ncmds;
srcmd* cmds;
} srcmdbuf;
//
// Vertex Data
//
typedef union srvrtxdata {
struct {
float x, y, z, w;
float nx, ny, nz;
float s, t;
GLubyte r, g, b, a;
};
struct {
float pos[4];
float n[4];
float uv[2];
GLubyte color[4];
};
} srvrtxdata;
//
// Vertex Buffer Object for all debug objects (coord frame, grid, cube, ...)
//
const GLint DEBUG_VBO_SIZE = 1024;
GLuint gDebugShapesVBOId = 0;
GLuint gDebugShapesVAId = 0;
//
// Simple Mesh Data
//
typedef struct srmeshdata {
int nvertices;
int nindices;
GLuint vao_id;
GLuint vb_id;
GLuint idb_id;
GLuint count;
GLenum mode;
} srmeshdata;
static srmeshdata gCoordFrameMesh = {0};
static srmeshdata gGridMesh = {0};
typedef struct srshader {
const char* vert_fname;
const char* frag_fname;
GLuint program_id;
GLuint vert_shader_id;
GLuint frag_shader_id;
} srshader;
static srshader gDefaultShader =
{"default_vert.glsl", "default_frag.glsl", 0, 0, 0};
bool srshader_compile(GLuint shader_id, const char* shader_src) {
glShaderSource(shader_id, 1, &shader_src, 0);
glCompileShader(shader_id);
// Check Vertex Shader
GLint result = GL_FALSE;
int log_length = 0;
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &result);
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
char* err_msg = calloc(log_length, sizeof(char));
assert(err_msg);
glGetShaderInfoLog(shader_id, log_length, NULL, err_msg);
gLog("%s", err_msg);
free(err_msg);
return false;
}
return true;
}
bool srshader_load(
srshader* shader,
const char* vert_src,
const char* frag_src) {
shader->vert_shader_id = glCreateShader(GL_VERTEX_SHADER);
if (!srshader_compile(shader->vert_shader_id, vert_src)) {
gLog("Error compiling vertex shader!");
return false;
}
shader->frag_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
if (!srshader_compile(shader->frag_shader_id, frag_src)) {
gLog("Error compiling fragment shader!");
return false;
}
shader->program_id = glCreateProgram();
glAttachShader(shader->program_id, shader->vert_shader_id);
glAttachShader(shader->program_id, shader->frag_shader_id);
// Bind attribute locations
glBindAttribLocation(shader->program_id, 0, "inCoord");
glBindAttribLocation(shader->program_id, 1, "inNormal");
glBindAttribLocation(shader->program_id, 2, "inUV");
glBindAttribLocation(shader->program_id, 3, "inColor");
glLinkProgram(shader->program_id);
GLint result = GL_FALSE;
int log_length = 0;
glGetProgramiv(shader->program_id, GL_LINK_STATUS, &result);
glGetProgramiv(shader->program_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0) {
char* err_msg = calloc(log_length, sizeof(char));
assert(err_msg);
glGetProgramInfoLog(shader->program_id, log_length, NULL, err_msg);
gLog("%s", err_msg);
free(err_msg);
gLog("Error linking shader program!");
return false;
}
return true;
}
bool try_read_file(const char* dir, const char* filename, char** buf) {
assert(*buf == NULL);
int path_len = strlen(dir) + strlen(filename) + 1;
char* path_buffer = calloc(path_len, sizeof(char));
snprintf(path_buffer, path_len, "%s%s", dir, filename);
long file_length = 0;
FILE* file = NULL;
file = fopen(path_buffer, "rb");
if (file) {
fseek(file, 0, SEEK_END);
file_length = ftell(file);
fseek(file, 0, SEEK_SET);
*buf = calloc(file_length, sizeof(char));
assert(*buf);
fread(*buf, 1, file_length, file);
fclose(file);
return true;
}
return false;
}
void init_shaders() {
static const char* shader_search_paths[] = {
"./data/shaders/",
"../data/shaders/",
NULL};
bool found = false;
int search_index = 0;
char* frag_buffer = NULL;
char* vert_buffer = NULL;
while (shader_search_paths[search_index] != NULL) {
const char* dir = shader_search_paths[search_index];
static char path_buf[1024];
long length;
if (vert_buffer == NULL) {
try_read_file(dir, gDefaultShader.vert_fname, &vert_buffer);
}
if (frag_buffer == NULL) {
try_read_file(dir, gDefaultShader.frag_fname, &frag_buffer);
}
if ((vert_buffer != NULL) && (frag_buffer != NULL)) {
srshader_load(&gDefaultShader, vert_buffer, frag_buffer);
free(vert_buffer);
vert_buffer = NULL;
free(frag_buffer);
frag_buffer = NULL;
break;
}
search_index++;
}
if (gDefaultShader.program_id == 0) {
gLog("Error: could not load default shader!");
}
}
void init_debug_meshes() {
assert(gCoordFrameMesh.nvertices == 0);
glGenVertexArrays(1, &gDebugShapesVAId);
glBindVertexArray(gDebugShapesVAId);
glGenBuffers(1, &gDebugShapesVBOId);
glBindBuffer(GL_ARRAY_BUFFER, gDebugShapesVBOId);
glBufferData(GL_ARRAY_BUFFER, sizeof(union srvrtxdata) * DEBUG_VBO_SIZE, NULL, GL_STATIC_DRAW);
GLintptr dbg_vbuf_offset = 0;
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0,
4,
GL_FLOAT,
GL_FALSE,
(sizeof(srvrtxdata)),
(void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(
1,
3,
GL_FLOAT,
GL_FALSE,
(sizeof(srvrtxdata)),
(void*)(sizeof(float) * 4));
// Attribute 2: texture coordinates
glEnableVertexAttribArray(2);
glVertexAttribPointer(
2,
2,
GL_FLOAT,
GL_FALSE,
(sizeof(srvrtxdata)),
(void*)(sizeof(float) * 7));
// Attribute 3: color
glEnableVertexAttribArray(3);
glVertexAttribPointer(
3,
4,
GL_UNSIGNED_BYTE,
GL_TRUE,
(sizeof(srvrtxdata)),
(void*)(sizeof(float) * 9));
//
// Coordinate Frame
//
gCoordFrameMesh.vao_id = gDebugShapesVAId;
gCoordFrameMesh.vb_id = gDebugShapesVBOId;
gCoordFrameMesh.nvertices = 6;
srvrtxdata coord_frame_vertices[] = {
{0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 255, 0, 0, 255},
{1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 255, 0, 0, 255},
{0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 255, 0, 255},
{0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 255, 0, 255},
{0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 255, 255},
{0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, 0, 255, 255}};
GLuint coord_frame_indices[] = {0, 1, 2, 3, 4, 5};
glBufferSubData(
GL_ARRAY_BUFFER,
dbg_vbuf_offset,
sizeof(coord_frame_vertices),
coord_frame_vertices);
dbg_vbuf_offset = sizeof (coord_frame_vertices);
glGenBuffers(1, &gCoordFrameMesh.idb_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gCoordFrameMesh.idb_id);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
sizeof(coord_frame_indices),
coord_frame_indices,
GL_STATIC_DRAW);
gCoordFrameMesh.mode = GL_LINES;
gCoordFrameMesh.count = 6;
//
// Coordinate Frame
//
gGridMesh.vao_id = gDebugShapesVAId;
gGridMesh.vb_id = gDebugShapesVBOId;
gGridMesh.nvertices = 44;
srvrtxdata* grid_vertices = calloc(44, sizeof(srvrtxdata));
srvrtxdata grid_def_vertex = {
0.f, 0.f, 0.f, 1.f,
0.f, 1.f, 0.f,
0.f, 0.f,
255, 255, 255, 255
};
// lines along parallel to x axis
for (int i = 0; i <= 10; i++) {
memcpy (&grid_vertices[2*i], &grid_def_vertex, sizeof(srvrtxdata));
memcpy (&grid_vertices[2*i + 1], &grid_def_vertex, sizeof(srvrtxdata));
grid_vertices[2 * i].pos[0] = -5.0f + i * 1.0f;
grid_vertices[2 * i].pos[2] = 5.0f;
grid_vertices[2 * i + 1].pos[0] = -5.0f + i * 1.0f;
grid_vertices[2 * i + 1].pos[2] = -5.0f;
}
// lines parallel to z axis
for (int i = 11; i <= 21; i++) {
memcpy (&grid_vertices[2*i], &grid_def_vertex, sizeof(srvrtxdata));
memcpy (&grid_vertices[2*i + 1], &grid_def_vertex, sizeof(srvrtxdata));
grid_vertices[2 * i].pos[0] = -5.0f;
grid_vertices[2 * i].pos[2] = -5.0f + (i - 11) * 1.0f;
grid_vertices[2 * i + 1].pos[0] = 5.0f;
grid_vertices[2 * i + 1].pos[2] = -5.0f + (i - 11) * 1.0f;
}
glBindBuffer(GL_ARRAY_BUFFER, gDebugShapesVBOId);
glBufferSubData(
GL_ARRAY_BUFFER,
dbg_vbuf_offset,
sizeof(srvrtxdata) * 44,
grid_vertices);
dbg_vbuf_offset += sizeof (srvrtxdata) * 44;
free(grid_vertices);
GLuint grid_indices[44];
for (int i = 0; i < 22; i++) {
grid_indices[2 * i] = 2*i + 6;
grid_indices[2 * i + 1] = (2*i+1) + 6;
}
glGenBuffers(1, &gGridMesh.idb_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gGridMesh.idb_id);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
sizeof(grid_indices),
grid_indices,
GL_STATIC_DRAW);
gGridMesh.mode = GL_LINES;
gGridMesh.count = 44;
glBindVertexArray(0);
};
//
// Renderer
//
srndr* srndr_create() {
srndr* result = calloc(1, sizeof(srndr));
init_shaders();
init_debug_meshes();
return result;
}
void srndr_destroy(srndr* sr) { free(sr); }
//
// View
//
void srview_get_output_texture(srview* sv, GLuint* texture) {
*texture = sv->mColorTexture;
}
void srview_set_proj(srview* sv, simd4x4f proj) { sv->proj = proj; }
void srview_set_view(srview* sv, simd4x4f view) { sv->view = view; }
void srview_update_framebuffer(srview* sv) {
glGenFramebuffers(1, &sv->mFrameBufferId);
glBindFramebuffer(GL_FRAMEBUFFER, sv->mFrameBufferId);
// Color Texture
glGenTextures(1, &sv->mColorTexture);
glBindTexture(GL_TEXTURE_2D, sv->mColorTexture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGB,
sv->width,
sv->height,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
sv->mColorTexture,
0);
// Depth Texture
glGenTextures(1, &sv->mDepthTexture);
glBindTexture(GL_TEXTURE_2D, sv->mDepthTexture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_DEPTH_COMPONENT,
sv->width,
sv->height,
0,
GL_DEPTH_COMPONENT,
GL_FLOAT,
0);
// Set parameters so that we can set a shadow2DSampler
glTexParameteri(
GL_TEXTURE_2D,
GL_TEXTURE_COMPARE_MODE,
GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float border_color[] = {1.0f, 1.0f, 1.0f, 1.0f};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D,
sv->mDepthTexture,
0);
}
void srview_cleanup_framebuffer(srview* sv) {
if (glIsFramebuffer(sv->mFrameBufferId)) {
glDeleteFramebuffers(1, &sv->mFrameBufferId);
}
if (sv->mColorTexture != -1) {
glDeleteTextures(1, &sv->mColorTexture);
sv->mColorTexture = -1;
}
if (sv->mDepthTexture != -1) {
glDeleteTextures(1, &sv->mDepthTexture);
sv->mDepthTexture = -1;
}
}
void srview_set_size(srview* sv, int width, int height) {
if (sv->width == width && sv->height == height) {
return;
}
sv->width = width;
sv->height = height;
srview_cleanup_framebuffer(sv);
srview_update_framebuffer(sv);
}
srview* srview_create() {
srview* result = calloc(1, sizeof(srview));
return result;
}
void srview_destroy(srview* sv) {
srview_cleanup_framebuffer(sv);
free(sv);
}
void srndr_run_frame_command(const srcmd* cmd) {
glBindVertexArray(gCoordFrameMesh.vao_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gCoordFrameMesh.idb_id);
glDrawElements(gCoordFrameMesh.mode, gCoordFrameMesh.count, GL_UNSIGNED_INT, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void srndr_run_grid_command(const srcmd* cmd) {
glBindVertexArray(gGridMesh.vao_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gGridMesh.idb_id);
glDrawElements(gGridMesh.mode, gGridMesh.count, GL_UNSIGNED_INT, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void srndr_render(srndr* srndr, srview* sview, srcmdbuf* scmdbuf) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, sview->mFrameBufferId);
glViewport(0, 0, sview->width, sview->height);
glClearColor(0.1f, 0.1f, 0.1f, 0.1f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(gDefaultShader.program_id);
GLint view_mat_loc =
glGetUniformLocation(gDefaultShader.program_id, "uViewMatrix");
assert(view_mat_loc != -1);
glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, &sview->view);
GLint proj_mat_loc =
glGetUniformLocation(gDefaultShader.program_id, "uProjectionMatrix");
assert(proj_mat_loc != -1);
glUniformMatrix4fv(proj_mat_loc, 1, GL_FALSE, &sview->proj);
GLint model_mat_loc =
glGetUniformLocation(gDefaultShader.program_id, "uModelMatrix");
assert(model_mat_loc != -1);
for (int i = 0; i < scmdbuf->ncmds; i++) {
const srcmd* cmd = &scmdbuf->cmds[i];
glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, &cmd->mat);
switch (cmd->type) {
case SRndrCmdTypeFrame:
srndr_run_frame_command(cmd);
break;
case SRndrCmdTypeGrid:
srndr_run_grid_command(cmd);
break;
default:
gLog("Invalid command type %d at index %d", cmd->type, i);
break;
}
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
//
// Render Commands
//
void srcmd_clear (srcmd *cmd) {
cmd->color = simd4f_create(1.f, 1.f, 1.f, 1.f);
cmd->type = SRndrCmdTypeFrame;
simd4x4f_identity(&cmd->mat);
}
srcmdbuf* srcmdbuf_create(unsigned int size_max) {
srcmdbuf* result = malloc(sizeof(srcmdbuf));
result->nbufsize = size_max;
result->ncmds = 0;
result->cmds = calloc(sizeof(srcmd), size_max);
return result;
}
void srcmdbuf_clear(srcmdbuf* cmdbuf) { cmdbuf->ncmds = 0; }
bool srcmdbuf_add(srcmdbuf* cmdbuf, srcmd *cmd) {
srcmd *buf_cmd = srcmd_create(cmdbuf);
if (!buf_cmd) {
return false;
}
*buf_cmd = *cmd;
}
srcmd* srcmd_create(srcmdbuf* cmdbuf) {
if (cmdbuf->ncmds == cmdbuf->nbufsize) {
gLog("Warning: number of render commands maxed out!");
return NULL;
}
return &(cmdbuf->cmds[cmdbuf->ncmds++]);
}
#endif // SRENDER_IMPLEMENTATION
2020-10-17 22:09:41 +02:00
#ifdef __cplusplus
}
#endif
#endif // SRENDER_H