Skip to content

Commit

Permalink
Add capability to draw a basic "mesh"
Browse files Browse the repository at this point in the history
 - Added basic stuff for creating a "mesh" out of line segments.
   Currently shows a test rendering with three different colored line
   segments.  Seems to work so far.
  • Loading branch information
yalue committed Feb 6, 2022
1 parent 7f511bf commit 0f69c92
Show file tree
Hide file tree
Showing 10 changed files with 482 additions and 7 deletions.
1 change: 1 addition & 0 deletions build_windows.bat
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
gcc -Wall -Werror -O3 -o l_system_3d ^
l_system_3d.c ^
l_system_mesh.c ^
utilities.c ^
glad\src\glad.c ^
-I cglm\include ^
Expand Down
90 changes: 88 additions & 2 deletions l_system_3d.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "l_system_3d.h"
#include "l_system_mesh.h"
#include "utilities.h"
#include "l_system_3d.h"

#define DEFAULT_WINDOW_WIDTH (800)
#define DEFAULT_WINDOW_HEIGHT (600)
Expand All @@ -24,20 +25,31 @@ ApplicationState* AllocateApplicationState(void) {

void FreeApplicationState(ApplicationState *s) {
if (!s) return;
if (s->mesh) DestroyLSystemMesh(s->mesh);
glDeleteBuffers(1, &(s->ubo));
if (s->window) glfwDestroyWindow(s->window);
memset(s, 0, sizeof(*s));
free(s);
}

// Recomputes the scene's projection matrix based on s->aspect_ratio.
static void UpdateProjectionMatrix(ApplicationState *s) {
glm_mat4_identity(s->shared_uniforms.projection);
glm_perspective(45.0, s->aspect_ratio, 0.01, 100.0,
s->shared_uniforms.projection);
}

static void FramebufferResizedCallback(GLFWwindow *window, int width,
int height) {
ApplicationState *s = (ApplicationState *) glfwGetWindowUserPointer(window);
s->window_width = width;
s->window_height = height;
s->aspect_ratio = ((float) s->window_width) / ((float) s->window_height);
glViewport(0, 0, width, height);
UpdateProjectionMatrix(s);
}

// Sets up the GLFW window. Returns 0 on error.
static int SetupWindow(ApplicationState *s) {
GLFWwindow *window = NULL;
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
Expand All @@ -56,24 +68,80 @@ static int SetupWindow(ApplicationState *s) {
return 1;
}

// Allocates the ubo, but doesn't populate it with data yet. Returns 0 on
// error.
static int SetupUniformBuffer(ApplicationState *s) {
glGenBuffers(1, &(s->ubo));
glBindBuffer(GL_UNIFORM_BUFFER, s->ubo);
glBufferData(GL_UNIFORM_BUFFER, sizeof(SharedUniforms), NULL,
GL_STATIC_DRAW);
glBindBufferRange(GL_UNIFORM_BUFFER, SHARED_UNIFORMS_BINDING, s->ubo, 0,
sizeof(SharedUniforms));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
return CheckGLErrors();
}

// This generates the vertices for the L-system, and updates the mesh. Returns
// 0 on error.
static int GenerateVertices(ApplicationState *s) {
// TODO: Actually store the vertices in s.
MeshVertex tmp_verts[] = {
{{0, 0, 0}, 0, {1, 1, 1}, 0, {0, 0, 0}, 0, {1, 0, 0, 1}},
{{1, 1, 1}, 0, {0, 0, 0}, 0, {0, 0, 0}, 0, {1, 0, 0, 1}},
// Green line from 0, 0, 0 -> 0, 1, 1
{{0, 0, 0}, 0, {0, 1, 1}, 0, {0, 0, 0}, 0, {0, 1, 0, 1}},
{{0, 1, 1}, 0, {0, 0, 0}, 0, {0, 0, 0}, 0, {0, 1, 0, 1}},
// Blue line from 0, 0, 0, -> 1, 1, 0
{{0, 0, 0}, 0, {1, 1, 0}, 0, {0, 0, 0}, 0, {0, 0, 1, 1}},
{{1, 1, 0}, 0, {0, 0, 0}, 0, {0, 0, 0}, 0, {0, 0, 1, 1}},
};
if (!SetMeshVertices(s->mesh, tmp_verts, 6)) {
printf("Failed setting vertices.\n");
return 0;
}
return 1;
}

static int ProcessInputs(ApplicationState *s) {
if (glfwGetKey(s->window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(s->window, 1);
}
return 1;
}

static void UpdateCamera(ApplicationState *s) {
vec3 position, target, up;
float tmp;
// TODO (eventually): Change camera based on user input; allow flying around.
glm_mat4_identity(s->shared_uniforms.view);
glm_vec3_zero(position);
glm_vec3_zero(target);
glm_vec3_zero(up);
up[1] = 1.0;
tmp = glfwGetTime() / 4.0;
position[0] = sin(tmp) * 4.0;
position[2] = cos(tmp) * 4.0;
glm_lookat(position, target, up, s->shared_uniforms.view);
glm_vec4(position, 0, s->shared_uniforms.camera_position);
}

static int RunMainLoop(ApplicationState *s) {
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glClearColor(0.3f, 0.05f, 0.5f, 1.0f);
glClearColor(0, 0, 0, 1.0);
while (!glfwWindowShouldClose(s->window)) {
if (!ProcessInputs(s)) {
printf("Error processing inputs.\n");
return 0;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
UpdateCamera(s);
glBindBuffer(GL_UNIFORM_BUFFER, s->ubo);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(SharedUniforms),
(void *) &(s->shared_uniforms));
if (!DrawMesh(s->mesh)) return 0;

glfwSwapBuffers(s->window);
glfwPollEvents();
if (!CheckGLErrors()) {
Expand Down Expand Up @@ -108,12 +176,30 @@ int main(int argc, char **argv) {
}
glViewport(0, 0, s->window_width, s->window_height);
glfwSetFramebufferSizeCallback(s->window, FramebufferResizedCallback);
if (!SetupUniformBuffer(s)) {
printf("Failed setting up uniform buffer.\n");
to_return = 1;
goto cleanup;
}
UpdateProjectionMatrix(s);
s->mesh = CreateLSystemMesh();
if (!s->mesh) {
printf("Failed initializing L-system mesh.\n");
to_return = 1;
goto cleanup;
}

if (!CheckGLErrors()) {
printf("OpenGL errors detected during initialization.\n");
to_return = 1;
goto cleanup;
}

if (!GenerateVertices(s)) {
printf("Failed generating vertices.\n");
to_return = 1;
goto cleanup;
}
if (!RunMainLoop(s)) {
printf("Application ended with an error.\n");
to_return = 1;
Expand Down
11 changes: 11 additions & 0 deletions l_system_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@
#include <cglm/cglm.h>
#include <glad/glad.h>

// Uniforms shared with all shaders. Must match the layout in
// shared_uniforms.glsl, and every field must be padded to four floats.
typedef struct {
mat4 projection;
mat4 view;
vec4 camera_position;
} SharedUniforms;

// Maintains global data about the running program.
typedef struct {
GLFWwindow *window;
int window_width;
int window_height;
float aspect_ratio;
LSystemMesh *mesh;
GLuint ubo;
SharedUniforms shared_uniforms;
} ApplicationState;

// Allocates an ApplicationState struct and initializes its values to 0.
Expand Down
203 changes: 203 additions & 0 deletions l_system_mesh.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cglm/cglm.h>
#include <glad/glad.h>
#include "utilities.h"
#include "l_system_mesh.h"

// Loads and compiles a shader from the given file path. Returns 0 on error.
static GLuint LoadShader(const char *path, GLenum shader_type) {
GLuint to_return = 0;
GLint compile_result = 0;
GLchar shader_log[512];
char *shader_src_orig = NULL;
char *shared_uniform_code = NULL;
char *final_src = NULL;

// First do some preprocessing to insert the common uniform definitions in
// place of the special comment in the main shader source.
shader_src_orig = ReadFullFile(path);
if (!shader_src_orig) return 0;
shared_uniform_code = ReadFullFile("./shared_uniforms.glsl");
if (!shared_uniform_code) {
free(shader_src_orig);
return 0;
}
final_src = StringReplace(shader_src_orig, "//INCLUDE_SHARED_UNIFORMS\n",
shared_uniform_code);
if (!final_src) {
printf("Failed preprocessing shader source code.\n");
free(shader_src_orig);
free(shared_uniform_code);
return 0;
}
free(shader_src_orig);
free(shared_uniform_code);
shader_src_orig = NULL;
shared_uniform_code = NULL;

to_return = glCreateShader(shader_type);
glShaderSource(to_return, 1, (const char**) &final_src, NULL);
glCompileShader(to_return);
free(final_src);
final_src = NULL;

// Check compilation success.
memset(shader_log, 0, sizeof(shader_log));
glGetShaderiv(to_return, GL_COMPILE_STATUS, &compile_result);
if (compile_result != GL_TRUE) {
glGetShaderInfoLog(to_return, sizeof(shader_log) - 1, NULL,
shader_log);
printf("Shader %s compile error:\n%s\n", path, shader_log);
glDeleteShader(to_return);
return 0;
}
if (!CheckGLErrors()) {
glDeleteShader(to_return);
return 0;
}
return to_return;
}

// Links and returns the shader program from the given source file names.
static GLuint CreateShaderProgram(const char *vertex_src_file,
const char *fragment_src_file) {
GLchar link_log[512];
GLint link_result = 0;
GLuint vertex_shader, fragment_shader, to_return;

vertex_shader = LoadShader(vertex_src_file, GL_VERTEX_SHADER);
if (!vertex_shader) {
printf("Couldn't load vertex shader.\n");
return 0;
}
fragment_shader = LoadShader(fragment_src_file, GL_FRAGMENT_SHADER);
if (!fragment_shader) {
printf("Couldn't load fragment shader.\n");
glDeleteShader(vertex_shader);
return 0;
}
to_return = glCreateProgram();
glAttachShader(to_return, vertex_shader);
glAttachShader(to_return, fragment_shader);
glLinkProgram(to_return);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
glGetProgramiv(to_return, GL_LINK_STATUS, &link_result);
memset(link_log, 0, sizeof(link_log));
if (link_result != GL_TRUE) {
glGetProgramInfoLog(to_return, sizeof(link_log) - 1, NULL, link_log);
printf("GL program link error:\n%s\n", link_log);
glDeleteProgram(to_return);
return 0;
}
glUseProgram(to_return);
if (!CheckGLErrors()) {
glDeleteProgram(to_return);
return 0;
}
return to_return;
}

// Sets *index to the index of the named uniform in s->shader_program. Returns
// 0 and prints a message on error.
static int UniformIndex(GLuint program, const char *name, GLint *index) {
*index = glGetUniformLocation(program, name);
if (*index < 0) {
printf("Failed getting location of uniform %s.\n", name);
return 0;
}
return 1;
}

// Loads the shaders for the model, and looks up uniform indices. Returns 0 on
// error.
static int SetupShaderProgram(LSystemMesh *m) {
GLuint p;
GLuint block_index;
p = CreateShaderProgram("./mesh_vertex_shader.vert",
"./mesh_fragment_shader.frag");
if (!p) return 0;
m->shader_program = p;
if (!UniformIndex(p, "model", &(m->model_uniform_index))) return 0;
if (!UniformIndex(p, "normal", &(m->normal_uniform_index))) return 0;
block_index = glGetUniformBlockIndex(p, "SharedUniforms");
if (block_index == GL_INVALID_INDEX) {
printf("Failed getting index of shared uniform block.\n");
CheckGLErrors();
return 0;
}
glUniformBlockBinding(p, block_index, SHARED_UNIFORMS_BINDING);
return CheckGLErrors();
}

LSystemMesh* CreateLSystemMesh(void) {
LSystemMesh *m = NULL;

m = (LSystemMesh *) calloc(1, sizeof(*m));
if (!m) {
printf("Failed allocating CPU-side mesh struct.\n");
return NULL;
}

glGenVertexArrays(1, &(m->vao));
glBindVertexArray(m->vao);
glGenBuffers(1, &(m->vbo));
glBindBuffer(GL_ARRAY_BUFFER, m->vbo);
// Setting up the location, direction, orientation, and color attributes
// (respectively).
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
(void *) offsetof(MeshVertex, location));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
(void *) offsetof(MeshVertex, direction));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
(void *) offsetof(MeshVertex, orientation));
glEnableVertexAttribArray(2);
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(MeshVertex),
(void *) offsetof(MeshVertex, color));
glEnableVertexAttribArray(3);
if (!CheckGLErrors()) {
printf("Failed setting up mesh object.\n");
DestroyLSystemMesh(m);
return NULL;
}
if (!SetupShaderProgram(m)) {
DestroyLSystemMesh(m);
return NULL;
}
glm_mat4_identity(m->model);
glm_mat3_identity(m->normal);
return m;
}

void DestroyLSystemMesh(LSystemMesh *m) {
if (!m) return;
glDeleteBuffers(1, &(m->vbo));
glDeleteVertexArrays(1, &(m->vao));
glDeleteProgram(m->shader_program);
memset(m, 0, sizeof(*m));
free(m);
}

int SetMeshVertices(LSystemMesh *m, MeshVertex *vertices, uint32_t count) {
glBindVertexArray(m->vao);
glBindBuffer(GL_ARRAY_BUFFER, m->vbo);
glBufferData(GL_ARRAY_BUFFER, count * sizeof(MeshVertex), vertices,
GL_STATIC_DRAW);
m->vertex_count = count;
return CheckGLErrors();
}

int DrawMesh(LSystemMesh *m) {
glUseProgram(m->shader_program);
glBindVertexArray(m->vao);
glUniformMatrix4fv(m->model_uniform_index, 1, GL_FALSE, (float *) m->model);
glUniformMatrix3fv(m->normal_uniform_index, 1, GL_FALSE,
(float *) m->normal);
glDrawArrays(GL_LINES, 0, m->vertex_count);
return CheckGLErrors();
}
Loading

0 comments on commit 0f69c92

Please sign in to comment.