Skip to content

Commit

Permalink
Actually render L-systems
Browse files Browse the repository at this point in the history
 - This currently is capable of reading the config file and displaying
   the L-system it specifies.

 - The current config just shows two connected dragon curves of
   different colors.

 - Pressing the up and down arrows on the keyboard increases and
   decreases (respectively) the number of iterations being displayed by
   1.

 - In progress: the model scaling seems a little off. Will probably
   increase it.
  • Loading branch information
yalue committed Feb 16, 2022
1 parent 42eda63 commit 78ffd1f
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 66 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,15 @@ The following list of actions are supported:
- `move_forward_nodraw <distance>`: Like `move_forward`, but does not draw a
line segment.

- `set_color_r <value>`: Sets the turtle's red color channel to the given
value. The value will be clamped to be between 0.0 and 1.0.

- `set_color_g <value>`: The same as `set_color_r`, but for the green channel.

- `set_color_b <value>`: The same as `set_color_r`, but for the blue channel.

- `set_color_a <value>`: The same as `set_color_r`, but for the alpha channel.

Prior to taking any actions, the turtle is initialized at position (0, 0, 0),
facing in the positive-X direction, with its pitch and roll set so that its
"back" is facing upwards; in the positive-Y direction.
Expand Down
18 changes: 17 additions & 1 deletion config.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
# Draw a dragon curve.
init F
init PFRF
F F+G
G F-G

actions

# Make the initial portion purple.
P
set_color_r 0.7
set_color_g 0.2
set_color_b 1.0

B
set_turtle_B

F
move_forward 1.0

Expand All @@ -14,6 +23,13 @@ move_forward 1.0
-
rotate -90.0

# Make the rotated portion green.
R
set_color_r 0.2
set_color_g 0.9
set_color_b 0.2
pitch 90.0

+
# Comments can go anywhere, so long as the '#' is at the start of the line.
rotate 90.0
Expand Down
168 changes: 137 additions & 31 deletions l_system_3d.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glad/glad.h>
Expand Down Expand Up @@ -85,44 +86,148 @@ static int SetupUniformBuffer(ApplicationState *s) {
return CheckGLErrors();
}

static int Rotate90(Turtle3D *t) {
return RotateTurtle(t, 90);
}

// This generates the vertices for the L-system, and updates the mesh. Returns
// 0 on error.
static int GenerateVertices(ApplicationState *s) {
Turtle3D *t = s->turtle;
t->color[0] = 1;
t->color[1] = 0;
t->color[2] = 0;
if (!MoveTurtleForward(t, 1.0)) return 0;
if (!Rotate90(t)) return 0;
t->color[0] = 0;
t->color[1] = 1;
if (!MoveTurtleForward(t, 1.0)) return 0;
if (!Rotate90(t)) return 0;
t->color[1] = 0;
t->color[2] = 1;
if (!MoveTurtleForward(t, 1.0)) return 0;
if (!Rotate90(t)) return 0;
t->color[0] = 1;
t->color[1] = 1;
if (!MoveTurtleForward(t, 1.0)) return 0;
if (!PitchTurtle(t, 90)) return 0;
t->color[0] = 0.7;
t->color[1] = 0.1;
if (!MoveTurtleForward(t, 0.5)) return 0;
ActionRule *r = NULL;
int result;
uint32_t char_index, inst_index;
uint8_t c;
ResetTurtle3D(t);
for (char_index = 0; char_index < s->l_system_length; char_index++) {
c = s->l_system_string[char_index];
r = s->config->actions + c;
for (inst_index = 0; inst_index < r->length; inst_index++) {
result = r->instructions[inst_index](t, r->args[inst_index]);
if (!result) {
printf("Failed running instruction %d for char %c.\n", (int) inst_index,
(char) c);
return 0;
}
}
}

if (!SetMeshVertices(s->mesh, t->vertices, t->vertex_count)) {
printf("Failed setting vertices.\n");
return 0;
}
if (!SetTransformInfo(s->turtle, s->mesh->model, s->mesh->normal,
s->mesh->location_offset)) {
printf("Failed getting transform matrices.\n");
return 0;
}
return 1;
}

static float ToMB(uint64_t bytes) {
float tmp = (float) bytes;
return tmp / (1024.0 * 1024.0);
}

// Iterates the L-system exactly once. Returns 0 on error. Does not update the
// mesh.
static int IncreaseIterations(ApplicationState *s) {
uint32_t new_length = 0;
ReplacementRule *r = NULL;
uint8_t *new_buffer = NULL;
uint8_t *dst = NULL;
uint8_t *loc = s->l_system_string;
uint8_t c;
// First iterate over the string to pre-calcuate the size of the buffer we'll
// need.
while (*loc) {
c = *loc;
loc++;
r = s->config->replacements + c;
if (!r->used) {
new_length++;
continue;
}
new_length += r->length;
}
// +1 to ensure a null terminator.
new_buffer = (uint8_t *) calloc(1, new_length + 1);
if (!new_buffer) {
printf("Failed allocating new %f MB L-system string.\n", ToMB(new_length));
return 0;
}
// Iterate over the string again, this time populating the new buffer.
dst = new_buffer;
loc = s->l_system_string;
while (*loc) {
c = *loc;
loc++;
r = s->config->replacements + c;
if (!r->used) {
// Keep the same char if no replacement was defined.
*dst = c;
dst++;
continue;
}
memcpy(dst, r->replacement, r->length);
dst += r->length;
continue;
}
free(s->l_system_string);
s->l_system_string = new_buffer;
s->l_system_length = new_length;
s->l_system_iterations++;
return 1;
}

// Reduces the L-system iterations by one. Unfortunately, this is implemented
// by recomputing the entire thing. Does nothing if we're already at 0
// iterations.
static int DecreaseIterations(ApplicationState *s) {
uint32_t target_iterations, i;
if (s->l_system_iterations == 0) {
printf("Can't decrease iterations. Already at 0 iterations.\n");
return 1;
}
target_iterations = s->l_system_iterations - 1;
// TODO: DecreaseIterations somestimes seems to segfault. Probably happens
// around here.
free(s->l_system_string);
s->l_system_string = (uint8_t *) strdup(s->config->init);
if (!s->l_system_string) {
printf("Failed copying the initial L-system string.\n");
return 0;
}
s->l_system_iterations = 0;
for (i = 0; i < target_iterations; i++) {
if (!IncreaseIterations(s)) return 0;
}
return 1;
}

static int ProcessInputs(ApplicationState *s) {
int pressed;
if (glfwGetKey(s->window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
glfwSetWindowShouldClose(s->window, 1);
return 1;
}
// s->key_pressed_tmp is used to prevent counting one press multiple times,
// and to prevent up and down from being pressed together.
pressed = glfwGetKey(s->window, GLFW_KEY_UP) == GLFW_PRESS;
if (!s->key_pressed_tmp && pressed) {
// Nothing pressed -> up pressed
s->key_pressed_tmp = GLFW_KEY_UP;
if (!(IncreaseIterations(s) && GenerateVertices(s))) return 0;
printf("New L-system length: %.02f MB.\n", ToMB(s->l_system_length));
} else if ((s->key_pressed_tmp == GLFW_KEY_UP) && !pressed) {
// Up pressed -> up released
s->key_pressed_tmp = 0;
}
pressed = glfwGetKey(s->window, GLFW_KEY_DOWN) == GLFW_PRESS;
if (!s->key_pressed_tmp && pressed) {
// Nothing pressed -> down pressed
s->key_pressed_tmp = GLFW_KEY_DOWN;
if (!(DecreaseIterations(s) && GenerateVertices(s))) return 0;
printf("New L-system length: %.02f MB.\n", ToMB(s->l_system_length));
} else if ((s->key_pressed_tmp == GLFW_KEY_DOWN) && !pressed) {
// Down pressed -> down released
s->key_pressed_tmp = 0;
}
return 1;
}
Expand Down Expand Up @@ -227,12 +332,13 @@ int main(int argc, char **argv) {
goto cleanup;
}
printf("Config loaded OK!\n");
// TODO: Create the actual L-system
// - Parse the config file
// - If the config file is faulty, then display some basic test model, to
// avoid closing the program if someone mistakenly reloads a bad config.
// - Iterate the string. Create an LSystem type, probably.
// - Make the turtle follow the rules.
s->l_system_string = (uint8_t *) strdup(s->config->init);
if (!s->l_system_string) {
printf("Error initializing L-system string.\n");
to_return = 1;
goto cleanup;
}
// TODO: Display a test model if the config is faulty.
if (!GenerateVertices(s)) {
printf("Failed generating vertices.\n");
to_return = 1;
Expand Down
5 changes: 5 additions & 0 deletions l_system_3d.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <stdint.h>
#include <GLFW/glfw3.h>
#include <cglm/cglm.h>
#include <glad/glad.h>
Expand All @@ -24,6 +25,10 @@ typedef struct {
Turtle3D *turtle;
GLuint ubo;
SharedUniforms shared_uniforms;
int key_pressed_tmp;
uint32_t l_system_iterations;
uint32_t l_system_length;
uint8_t *l_system_string;
} ApplicationState;

// Allocates an ApplicationState struct and initializes its values to 0.
Expand Down
6 changes: 6 additions & 0 deletions l_system_mesh.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ static int SetupShaderProgram(LSystemMesh *m) {
m->shader_program = p;
if (!UniformIndex(p, "model", &(m->model_uniform_index))) return 0;
if (!UniformIndex(p, "normal", &(m->normal_uniform_index))) return 0;
if (!UniformIndex(p, "location_offset",
&(m->location_offset_uniform_index))) {
return 0;
}
block_index = glGetUniformBlockIndex(p, "SharedUniforms");
if (block_index == GL_INVALID_INDEX) {
printf("Failed getting index of shared uniform block.\n");
Expand Down Expand Up @@ -215,6 +219,8 @@ int DrawMesh(LSystemMesh *m) {
glUniformMatrix4fv(m->model_uniform_index, 1, GL_FALSE, (float *) m->model);
glUniformMatrix3fv(m->normal_uniform_index, 1, GL_FALSE,
(float *) m->normal);
glUniform3fv(m->location_offset_uniform_index, 1,
(float *) m->location_offset);
glDrawArrays(GL_LINES, 0, m->vertex_count);
return CheckGLErrors();
}
5 changes: 3 additions & 2 deletions l_system_mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ typedef struct {
GLuint vao;
GLuint vbo;
// The model and normal matrices used when drawing this mesh.
// TODO: Try to just make these identity matrices? Or perhaps use these to
// scale the model to fit in a 1x1x1 cube and sit on the floor?
mat4 model;
mat3 normal;
// The offset to add to every vertex's location to center the mesh.
vec3 location_offset;
GLint model_uniform_index;
GLint normal_uniform_index;
GLint location_offset_uniform_index;
} LSystemMesh;

// Prints the vertex's info to stdout.
Expand Down
6 changes: 5 additions & 1 deletion mesh_vertex_shader.vert
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ layout (location = 3) in vec4 color_in;
uniform mat4 model;
uniform mat3 normal;

// Added to the location of each vertex to center the overall mesh on 0,0,0.
uniform vec3 location_offset;

out VS_OUT {
vec3 frag_position;
vec4 color;
Expand All @@ -17,10 +20,11 @@ out VS_OUT {

void main() {
gl_Position = shared_uniforms.projection * shared_uniforms.view * model *
vec4(position_in, 1.0);
vec4(position_in + location_offset, 1.0);
vs_out.color = color_in;

// Prevent optimizing out the normal uniform.
// TODO (eventually): Remove once we actually use the normal uniform.
if (normal[0].x < -1) {
vs_out.color[1] = 1;
}
Expand Down
Loading

0 comments on commit 78ffd1f

Please sign in to comment.