Skip to content

Commit

Permalink
Add push/pop, fix a bug
Browse files Browse the repository at this point in the history
 - The length of the L-system wasn't being reset properly when the
   number of iterations was decreased.  Fixed.

 - Added push and pop instructions.
  • Loading branch information
Nathan O authored and Nathan O committed Feb 17, 2022
1 parent ec76fec commit 83947bf
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 34 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,15 @@ The following list of actions are supported:

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

- `push_position 0.0`: Pushes the turtle's current position onto a stack of
positions, so that it can be restored later. The "0.0" is ignored, but
required to simplify internal config-parsing logic.

- `pop_position 0.0`: Restores the turtle's current position from whichever
position is on top of the stack. Removes the position on top of the stack.
It is an error to encounter this instruction if the stack is already empty.
Once again, the "0.0" argument is ignored, but required in the config.

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
3 changes: 1 addition & 2 deletions l_system_3d.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,13 @@ static int DecreaseIterations(ApplicationState *s) {
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_length = strlen(s->config->init);
s->l_system_iterations = 0;
for (i = 0; i < target_iterations; i++) {
if (!IncreaseIterations(s)) return 0;
Expand Down
4 changes: 4 additions & 0 deletions parse_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ static int ParseActionRules(LSystemConfig *config) {
"set_color_g",
"set_color_b",
"set_color_a",
"push_position",
"pop_position",
};
TurtleInstruction action_fns[] = {
MoveTurtleForward,
Expand All @@ -303,6 +305,8 @@ static int ParseActionRules(LSystemConfig *config) {
SetTurtleGreen,
SetTurtleBlue,
SetTurtleAlpha,
PushTurtlePosition,
PopTurtlePosition,
};
char *current_line = NULL;
uint8_t current_char = 0;
Expand Down
1 change: 0 additions & 1 deletion shared_uniforms.glsl
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// There are no shared uniforms so far. This file will contain them later.
layout(std140) uniform SharedUniforms {
mat4 projection;
mat4 view;
Expand Down
114 changes: 90 additions & 24 deletions turtle_3d.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <cglm/cglm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand All @@ -8,12 +9,32 @@
// The number of vertices the turtle initially allocates space for.
#define INITIAL_TURTLE_CAPACITY (1024)

// The initial capacity of the turtle's position stack.
#define INITIAL_STACK_CAPACITY (32)

// The minimum spacing (squared) between two subsequent positions for them to
// be considered identical.
#define MIN_POSITION_DIST (1.0e-6)

#define PI (3.1415926536)

// Initializes the given stack of turtle positions. Returns 0 on error.
static int InitializePositionStack(PositionStack *s) {
s->size = 0;
s->buffer = (TurtlePosition *) calloc(INITIAL_STACK_CAPACITY,
sizeof(TurtlePosition));
if (!s->buffer) return 0;
s->capacity = INITIAL_STACK_CAPACITY;
return 1;
}

// Cleans up the stack of positions. Doesn't free s itself; just the buffer it
// wraps.
static void FreePositionStack(PositionStack *s) {
free(s->buffer);
memset(s, 0, sizeof(*s));
}

Turtle3D* CreateTurtle3D(void) {
Turtle3D *to_return = NULL;
to_return = (Turtle3D *) calloc(1, sizeof(*to_return));
Expand All @@ -28,6 +49,12 @@ Turtle3D* CreateTurtle3D(void) {
free(to_return);
return NULL;
}
if (!InitializePositionStack(&(to_return->position_stack))) {
printf("Failed initializing stack of turtle positions.\n");
free(to_return->vertices);
free(to_return);
return NULL;
}
to_return->vertex_capacity = INITIAL_TURTLE_CAPACITY;
ResetTurtle3D(to_return);
return to_return;
Expand All @@ -37,25 +64,28 @@ void ResetTurtle3D(Turtle3D *t) {
// Opaque white color
glm_vec4_one(t->color);
// Start at the origin
glm_vec3_zero(t->position);
glm_vec3_zero(t->prev_position);
glm_vec3_zero(t->p.position);
glm_vec3_zero(t->p.prev_position);
// Face in positive X direction, with up towards positive Y.
glm_vec3_zero(t->forward);
t->forward[0] = 1;
glm_vec3_zero(t->up);
t->up[1] = 1;
glm_vec3_zero(t->p.forward);
t->p.forward[0] = 1;
glm_vec3_zero(t->p.up);
t->p.up[1] = 1;
// The bounding cube is empty and ill-defined to begin with.
glm_vec3_zero(t->min_bounds);
glm_vec3_zero(t->max_bounds);
// We don't reallocate the array or zero it out here so we can reuse the
// turtle to draw another iteration without needing to clear all the
// vertices.
t->vertex_count = 0;
// Clear the stack. As with the vertex array, don't free it, though.
t->position_stack.size = 0;
}

void DestroyTurtle3D(Turtle3D *t) {
if (!t) return;
free(t->vertices);
FreePositionStack(&(t->position_stack));
memset(t, 0, sizeof(*t));
free(t);
}
Expand Down Expand Up @@ -105,7 +135,7 @@ int SetTransformInfo(Turtle3D *t, mat4 model, mat3 normal, vec3 loc_offset) {
// Checks if the internal array has space for two more vertices (another line
// segment). If not, this attempts to reallocate the turtle's internal array of
// vertices, doubling its capacity. Returns 0 on error.
static int IncreateCapacityIfNeeded(Turtle3D *t) {
static int IncreaseCapacityIfNeeded(Turtle3D *t) {
void *new_buffer = NULL;
uint32_t new_capacity;
uint32_t required_capacity = t->vertex_count + 2;
Expand All @@ -121,7 +151,7 @@ static int IncreateCapacityIfNeeded(Turtle3D *t) {
}
new_buffer = realloc(t->vertices, new_capacity * sizeof(MeshVertex));
if (!new_buffer) {
printf("Unable to increate number of vertices: out of memory.\n");
printf("Unable to increase number of vertices: out of memory.\n");
return 0;
}
t->vertices = (MeshVertex *) new_buffer;
Expand All @@ -134,34 +164,34 @@ static int IncreateCapacityIfNeeded(Turtle3D *t) {
static int AppendSegment(Turtle3D *t) {
MeshVertex *v = NULL;
vec3 direction;
if (!IncreateCapacityIfNeeded(t)) return 0;
if (!IncreaseCapacityIfNeeded(t)) return 0;
// If the two positions are too close together, then we'll just set the
// direction vector to the turtle's current direction. Otherwise, the
// direction in the line segment points from the previous point to the
// current position.
glm_vec3_sub(t->position, t->prev_position, direction);
glm_vec3_sub(t->p.position, t->p.prev_position, direction);
if (glm_vec3_norm2(direction) < MIN_POSITION_DIST) {
glm_vec3_copy(t->forward, direction);
glm_vec3_copy(t->p.forward, direction);
} else {
glm_vec3_normalize(direction);
}
v = t->vertices + t->vertex_count;
glm_vec3_copy(t->prev_position, v->location);
glm_vec3_copy(t->p.prev_position, v->location);
glm_vec3_copy(direction, v->forward);
glm_vec3_copy(t->up, v->up);
glm_vec3_copy(t->p.up, v->up);
glm_vec4_copy(t->color, v->color);
// The only difference between the two vertices in the line segment is the
// position.
*(v + 1) = *v;
glm_vec3_copy(t->position, (v + 1)->location);
glm_vec3_copy(t->p.position, (v + 1)->location);
t->vertex_count += 2;
return 1;
}

// Updates min_bounds and max_bounds to contain the turtle's current position.
static void UpdateBounds(Turtle3D *t) {
float *p = t->position;
glm_vec3_copy(t->position, p);
float *p = t->p.position;
glm_vec3_copy(t->p.position, p);
if (p[0] > t->max_bounds[0]) {
t->max_bounds[0] = p[0];
}
Expand All @@ -184,9 +214,9 @@ static void UpdateBounds(Turtle3D *t) {

int MoveTurtleForwardNoDraw(Turtle3D *t, float distance) {
vec3 change;
glm_vec3_copy(t->position, t->prev_position);
glm_vec3_scale(t->forward, distance, change);
glm_vec3_add(t->position, change, t->position);
glm_vec3_copy(t->p.position, t->p.prev_position);
glm_vec3_scale(t->p.forward, distance, change);
glm_vec3_add(t->p.position, change, t->p.position);
UpdateBounds(t);
return 1;
}
Expand All @@ -201,20 +231,20 @@ static float ToRadians(float degrees) {
}

int RotateTurtle(Turtle3D *t, float angle) {
glm_vec3_rotate(t->forward, ToRadians(angle), t->up);
glm_vec3_rotate(t->p.forward, ToRadians(angle), t->p.up);
return 1;
}

int PitchTurtle(Turtle3D *t, float angle) {
vec3 right;
glm_vec3_cross(t->forward, t->up, right);
glm_vec3_rotate(t->up, ToRadians(angle), right);
glm_vec3_rotate(t->forward, ToRadians(angle), right);
glm_vec3_cross(t->p.forward, t->p.up, right);
glm_vec3_rotate(t->p.up, ToRadians(angle), right);
glm_vec3_rotate(t->p.forward, ToRadians(angle), right);
return 1;
}

int RollTurtle(Turtle3D *t, float angle) {
glm_vec3_rotate(t->up, ToRadians(angle), t->forward);
glm_vec3_rotate(t->p.up, ToRadians(angle), t->p.forward);
return 1;
}

Expand Down Expand Up @@ -243,3 +273,39 @@ int SetTurtleAlpha(Turtle3D *t, float alpha) {
t->color[3] = ClampColor(alpha);
return 1;
}

int PushTurtlePosition(Turtle3D *t, float ignored) {
PositionStack *s = &(t->position_stack);
TurtlePosition *new_buf = NULL;
uint32_t new_cap = 0;
// Expand capacity if necessary.
if (s->size >= s->capacity) {
new_cap = s->capacity * 2;
if (new_cap < s->capacity) {
printf("Turtle position stack overflow.\n");
return 0;
}
new_buf = (TurtlePosition *) realloc(s->buffer, new_cap *
sizeof(TurtlePosition));
if (!new_buf) {
printf("Failed expanding position stack.\n");
return 0;
}
s->buffer = new_buf;
s->capacity = new_cap;
}
s->buffer[s->size] = t->p;
s->size++;
return 1;
}

int PopTurtlePosition(Turtle3D *t, float ignored) {
PositionStack *s = &(t->position_stack);
if (s->size == 0) {
printf("Turtle position stack is empty.\n");
return 0;
}
t->p = s->buffer[s->size - 1];
s->size--;
return 1;
}
37 changes: 30 additions & 7 deletions turtle_3d.h
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
#ifndef TURTLE_3D_H
#define TURTLE_3D_H
#include <cglm/cglm.h>
#include <stdint.h>
#include "l_system_mesh.h"

// Holds the state of a 3D "turtle" that follows instructions relative to
// itself in 3D space.
// Holds the turtle's position and orientation information.
typedef struct {
// The turtle's current location.
vec3 position;
// The turtle's previous location. Can be arbitrary if there is no previous
// position.
vec3 prev_position;
// The current with which the turtle draws lines.
vec4 color;
// This determines the turtle's current orientation. Must always be
// normalized and orthogonal.
vec3 forward;
vec3 up;
} TurtlePosition;

// Holds a stack of turtle positions.
typedef struct {
// The number of positions currently in the stack.
uint32_t size;
// The max size of the buffer before it needs to be expanded.
uint32_t capacity;
// The actual buffer being used as a stack.
TurtlePosition *buffer;
} PositionStack;

// Holds the state of a 3D "turtle" that follows instructions relative to
// itself in 3D space.
typedef struct {
TurtlePosition p;
// The current with which the turtle draws lines.
vec4 color;

// Defines a cube bounding the turtle's entire path so far. This *must* be
// well-formed, i.e. each min component must be less than each max component.
Expand All @@ -31,11 +47,9 @@ typedef struct {
MeshVertex *vertices;
uint32_t vertex_count;
uint32_t vertex_capacity;
PositionStack position_stack;
} Turtle3D;

// TODO: add push and pop instructions that keep the turtle's position,
// previous position, orientation, and color on a stack.

// Allocates a new turtle, at position 0, 0, 0, with no vertices. Returns NULL
// on error. The turtle starts out facing right, with up in the positive Y
// direction.
Expand Down Expand Up @@ -84,5 +98,14 @@ int SetTurtleGreen(Turtle3D *t, float green);
int SetTurtleBlue(Turtle3D *t, float blue);
int SetTurtleAlpha(Turtle3D *t, float alpha);

// Saves the turtle's position on a stack. Returns 0 on error. The argument is
// ignored.
int PushTurtlePosition(Turtle3D *t, float ignored);

// Pops the turtle's position off the stack of positions. Returns 0 on error.
// It's an error if this instruction is issued when the stack is empty. The
// argument is ignored.
int PopTurtlePosition(Turtle3D *t, float ignored);

#endif // TURTLE_3D_H

0 comments on commit 83947bf

Please sign in to comment.