aboutsummaryrefslogtreecommitdiff
path: root/09-september/tomcat
diff options
context:
space:
mode:
authorThomas Guillermo Albers Raviola <thomas@thomaslabs.org>2026-01-16 23:02:32 +0100
committerThomas Guillermo Albers Raviola <thomas@thomaslabs.org>2026-01-16 23:02:32 +0100
commit6b8af9cf83851c075c6c9514b1deaa931c2b19a4 (patch)
tree428986b49c32e21d3f7a3c2dfa41858ae0153209 /09-september/tomcat
Initial commit
Diffstat (limited to '09-september/tomcat')
-rw-r--r--09-september/tomcat/gui/widget.c12
-rw-r--r--09-september/tomcat/gui/widget.h13
-rw-r--r--09-september/tomcat/input.c21
-rw-r--r--09-september/tomcat/input.h11
-rw-r--r--09-september/tomcat/math/math_util.c10
-rw-r--r--09-september/tomcat/math/math_util.h8
-rw-r--r--09-september/tomcat/math/matrix4x4.c326
-rw-r--r--09-september/tomcat/math/matrix4x4.h39
-rw-r--r--09-september/tomcat/math/vector.c177
-rw-r--r--09-september/tomcat/math/vector.h47
-rw-r--r--09-september/tomcat/not_in_use/fbo.c108
-rw-r--r--09-september/tomcat/not_in_use/fbo.h35
-rw-r--r--09-september/tomcat/particles/particles.c153
-rw-r--r--09-september/tomcat/particles/particles.h62
-rw-r--r--09-september/tomcat/renderer/camera.c95
-rw-r--r--09-september/tomcat/renderer/camera.h32
-rw-r--r--09-september/tomcat/renderer/entity.c33
-rw-r--r--09-september/tomcat/renderer/mesh.c146
-rw-r--r--09-september/tomcat/renderer/mesh.h22
-rw-r--r--09-september/tomcat/renderer/model.c314
-rw-r--r--09-september/tomcat/renderer/model.h21
-rw-r--r--09-september/tomcat/renderer/renderer.c430
-rw-r--r--09-september/tomcat/renderer/renderer.h86
-rw-r--r--09-september/tomcat/renderer/renderer_types.h47
-rw-r--r--09-september/tomcat/renderer/shader.c187
-rw-r--r--09-september/tomcat/renderer/shader.h59
-rw-r--r--09-september/tomcat/renderer/texture.c188
-rw-r--r--09-september/tomcat/renderer/texture.h30
-rw-r--r--09-september/tomcat/renderer/vertex.h15
-rw-r--r--09-september/tomcat/renderer/window.c49
-rw-r--r--09-september/tomcat/renderer/window.h20
-rw-r--r--09-september/tomcat/shared.h13
-rw-r--r--09-september/tomcat/terrain.c155
-rw-r--r--09-september/tomcat/terrain.h27
-rw-r--r--09-september/tomcat/util/array.c102
-rw-r--r--09-september/tomcat/util/array.h23
-rw-r--r--09-september/tomcat/util/str.c121
-rw-r--r--09-september/tomcat/util/str.h29
-rw-r--r--09-september/tomcat/util/util.c138
-rw-r--r--09-september/tomcat/util/util.h27
-rw-r--r--09-september/tomcat/util/util_time.c52
-rw-r--r--09-september/tomcat/util/util_time.h13
42 files changed, 3496 insertions, 0 deletions
diff --git a/09-september/tomcat/gui/widget.c b/09-september/tomcat/gui/widget.c
new file mode 100644
index 0000000..507173a
--- /dev/null
+++ b/09-september/tomcat/gui/widget.c
@@ -0,0 +1,12 @@
+#include "widget.h"
+#include <stdlib.h>
+
+widget_t *Widget_CreateImage(Texture *texture, int x, int y, int w, int h)
+{
+ return NULL;
+}
+
+void Widget_Destroy(widget_t *widget)
+{
+ free(widget);
+}
diff --git a/09-september/tomcat/gui/widget.h b/09-september/tomcat/gui/widget.h
new file mode 100644
index 0000000..1b1c62d
--- /dev/null
+++ b/09-september/tomcat/gui/widget.h
@@ -0,0 +1,13 @@
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include "GL/glew.h"
+#include "../renderer/renderer_types.h"
+
+typedef struct
+{
+ int x, y, w, h;
+ Texture *texture;
+} widget_t;
+
+#endif // WIDGET_H
diff --git a/09-september/tomcat/input.c b/09-september/tomcat/input.c
new file mode 100644
index 0000000..8384ceb
--- /dev/null
+++ b/09-september/tomcat/input.c
@@ -0,0 +1,21 @@
+#include "input.h"
+
+static bool key_buffer[MAX_KEY_BUFFER_SIZE] = { false };
+
+void Input_PressKey(unsigned int key)
+{
+ if(key > 256 || key < 0) return;
+ key_buffer[key] = true;
+}
+
+void Input_ReleaseKey(unsigned int key)
+{
+ if(key > 256 || key < 0) return;
+ key_buffer[key] = false;
+}
+
+bool Input_isKeyPressed(unsigned int key)
+{
+ if(key > 256 || key < 0) return false;
+ return key_buffer[key];
+}
diff --git a/09-september/tomcat/input.h b/09-september/tomcat/input.h
new file mode 100644
index 0000000..92876ca
--- /dev/null
+++ b/09-september/tomcat/input.h
@@ -0,0 +1,11 @@
+#ifndef INPUT_H
+#define INPUT_H
+
+#include <stdbool.h>
+#define MAX_KEY_BUFFER_SIZE 256
+
+void Input_PressKey(unsigned int key);
+void Input_ReleaseKey(unsigned int key);
+bool Input_isKeyPressed(unsigned int key);
+
+#endif // INPUT_H
diff --git a/09-september/tomcat/math/math_util.c b/09-september/tomcat/math/math_util.c
new file mode 100644
index 0000000..1285f3d
--- /dev/null
+++ b/09-september/tomcat/math/math_util.c
@@ -0,0 +1,10 @@
+#include "math_util.h"
+
+float baryCentric(Vec3 *p1, Vec3 *p2, Vec3 *p3, Vec2 *pos)
+{
+ float det = (p2->z - p3->z) * (p1->x - p3->x) + (p3->x - p2->x) * (p1->z - p3->z);
+ float l1 = ((p2->z - p3->z) * (pos->x - p3->x) + (p3->x - p2->x) * (pos->y - p3->z)) / det;
+ float l2 = ((p3->z - p1->z) * (pos->x - p3->x) + (p1->x - p3->x) * (pos->y - p3->z)) / det;
+ float l3 = 1.0f - l1 - l2;
+ return l1 * p1->y + l2 * p2->y + l3 * p3->y;
+}
diff --git a/09-september/tomcat/math/math_util.h b/09-september/tomcat/math/math_util.h
new file mode 100644
index 0000000..5582dc7
--- /dev/null
+++ b/09-september/tomcat/math/math_util.h
@@ -0,0 +1,8 @@
+#ifndef MATH_UTIL_H
+#define MATH_UTIL_H
+
+#include "vector.h"
+
+extern float baryCentric(Vec3 *p1, Vec3 *p2, Vec3 *p3, Vec2 *pos);
+
+#endif // MATH_UTIL_H
diff --git a/09-september/tomcat/math/matrix4x4.c b/09-september/tomcat/math/matrix4x4.c
new file mode 100644
index 0000000..d4bea71
--- /dev/null
+++ b/09-september/tomcat/math/matrix4x4.c
@@ -0,0 +1,326 @@
+#include "matrix4x4.h"
+#include "../util/util.h"
+#include <SDL2/SDL.h>
+
+void mat4_identity(Mat4 *a)
+{
+ int i;
+ for(i = 0; i < 4*4; i++)
+ {
+ a->data[i] = 0.0f;
+ }
+ a->data[0 + 0 * 4] = 1.0f;
+ a->data[1 + 1 * 4] = 1.0f;
+ a->data[2 + 2 * 4] = 1.0f;
+ a->data[3 + 3 * 4] = 1.0f;
+}
+
+/** TODO: Preprocess these operation (1 + 1 * 4) -> 5 **/
+Mat4 mat4_inverse(const Mat4 *a)
+{
+ Mat4 inv;
+ int i;
+
+ /** Remember that inverted matrix is (1/det) * cofactor(transposed) **/
+ inv.data[0 + 0 * 4] = (a->data[1 + 1 * 4] * a->data[2 + 2 * 4] * a->data[3 + 3 * 4] +
+ a->data[2 + 1 * 4] * a->data[3 + 2 * 4] * a->data[1 + 3 * 4] +
+ a->data[3 + 1 * 4] * a->data[1 + 2 * 4] * a->data[2 + 3 * 4] -
+ a->data[1 + 3 * 4] * a->data[2 + 2 * 4] * a->data[3 + 1 * 4] -
+ a->data[2 + 3 * 4] * a->data[3 + 2 * 4] * a->data[1 + 1 * 4] -
+ a->data[3 + 3 * 4] * a->data[1 + 2 * 4] * a->data[2 + 1 * 4]);
+
+ inv.data[0 + 1 * 4] = -(a->data[0 + 1 * 4] * a->data[2 + 2 * 4] * a->data[3 + 3 * 4] +
+ a->data[2 + 1 * 4] * a->data[3 + 2 * 4] * a->data[0 + 3 * 4] +
+ a->data[3 + 1 * 4] * a->data[0 + 2 * 4] * a->data[2 + 3 * 4] -
+ a->data[0 + 3 * 4] * a->data[2 + 2 * 4] * a->data[3 + 1 * 4] -
+ a->data[2 + 3 * 4] * a->data[3 + 2 * 4] * a->data[0 + 1 * 4] -
+ a->data[3 + 3 * 4] * a->data[0 + 2 * 4] * a->data[2 + 1 * 4]);
+
+ inv.data[0 + 2 * 4] = (a->data[0 + 1 * 4] * a->data[1 + 2 * 4] * a->data[3 + 3 * 4] +
+ a->data[1 + 1 * 4] * a->data[3 + 2 * 4] * a->data[0 + 3 * 4] +
+ a->data[3 + 1 * 4] * a->data[0 + 2 * 4] * a->data[1 + 3 * 4] -
+ a->data[0 + 3 * 4] * a->data[1 + 2 * 4] * a->data[3 + 1 * 4] -
+ a->data[1 + 3 * 4] * a->data[3 + 2 * 4] * a->data[0 + 1 * 4] -
+ a->data[3 + 3 * 4] * a->data[0 + 2 * 4] * a->data[1 + 1 * 4]);
+
+ inv.data[0 + 3 * 4] = -(a->data[0 + 1 * 4] * a->data[1 + 2 * 4] * a->data[2 + 3 * 4] +
+ a->data[1 + 1 * 4] * a->data[2 + 2 * 4] * a->data[0 + 3 * 4] +
+ a->data[2 + 1 * 4] * a->data[0 + 2 * 4] * a->data[1 + 3 * 4] -
+ a->data[0 + 3 * 4] * a->data[1 + 2 * 4] * a->data[2 + 1 * 4] -
+ a->data[1 + 3 * 4] * a->data[2 + 2 * 4] * a->data[0 + 1 * 4] -
+ a->data[2 + 3 * 4] * a->data[0 + 2 * 4] * a->data[1 + 1 * 4]);
+
+ inv.data[1 + 0 * 4] = -(a->data[1 + 0 * 4] * a->data[2 + 2 * 4] * a->data[3 + 3 * 4] +
+ a->data[2 + 0 * 4] * a->data[3 + 2 * 4] * a->data[1 + 3 * 4] +
+ a->data[3 + 0 * 4] * a->data[1 + 2 * 4] * a->data[2 + 3 * 4] -
+ a->data[1 + 3 * 4] * a->data[2 + 2 * 4] * a->data[3 + 0 * 4] -
+ a->data[2 + 3 * 4] * a->data[3 + 2 * 4] * a->data[1 + 0 * 4] -
+ a->data[3 + 3 * 4] * a->data[1 + 2 * 4] * a->data[2 + 0 * 4]);
+
+ inv.data[1 + 1 * 4] = (a->data[0 + 0 * 4] * a->data[2 + 2 * 4] * a->data[3 + 3 * 4] +
+ a->data[2 + 0 * 4] * a->data[3 + 2 * 4] * a->data[0 + 3 * 4] +
+ a->data[3 + 0 * 4] * a->data[0 + 2 * 4] * a->data[2 + 3 * 4] -
+ a->data[0 + 3 * 4] * a->data[2 + 2 * 4] * a->data[3 + 0 * 4] -
+ a->data[2 + 3 * 4] * a->data[3 + 2 * 4] * a->data[0 + 0 * 4] -
+ a->data[3 + 3 * 4] * a->data[0 + 2 * 4] * a->data[2 + 0 * 4]);
+
+ inv.data[1 + 2 * 4] = -(a->data[0 + 0 * 4] * a->data[1 + 2 * 4] * a->data[3 + 3 * 4] +
+ a->data[1 + 0 * 4] * a->data[3 + 2 * 4] * a->data[0 + 3 * 4] +
+ a->data[3 + 0 * 4] * a->data[0 + 2 * 4] * a->data[1 + 3 * 4] -
+ a->data[0 + 3 * 4] * a->data[1 + 2 * 4] * a->data[3 + 0 * 4] -
+ a->data[1 + 3 * 4] * a->data[3 + 2 * 4] * a->data[0 + 0 * 4] -
+ a->data[3 + 3 + 4] * a->data[0 + 2 * 4] * a->data[1 + 0 * 4]);
+
+ inv.data[1 + 3 * 4] = (a->data[0 + 0 * 4] * a->data[1 + 2 * 4] * a->data[2 + 3 * 4] +
+ a->data[1 + 0 * 4] * a->data[2 + 2 * 4] * a->data[0 + 3 * 4] +
+ a->data[2 + 0 * 4] * a->data[0 + 2 * 4] * a->data[1 + 3 * 4] -
+ a->data[0 + 3 * 4] * a->data[1 + 2 * 4] * a->data[2 + 0 * 4] -
+ a->data[1 + 3 * 4] * a->data[2 + 2 * 4] * a->data[0 + 0 * 4] -
+ a->data[2 + 3 * 4] * a->data[0 + 2 * 4] * a->data[1 + 0 * 4]);
+
+ inv.data[2 + 0 * 4] = (a->data[1 + 0 * 4] * a->data[2 + 1 * 4] * a->data[3 + 3 * 4] +
+ a->data[2 + 0 * 4] * a->data[3 + 1 * 4] * a->data[1 + 3 * 4] +
+ a->data[3 + 0 * 4] * a->data[1 + 1 * 4] * a->data[2 + 3 * 4] -
+ a->data[1 + 3 * 4] * a->data[2 + 1 * 4] * a->data[3 + 0 * 4] -
+ a->data[2 + 3 * 4] * a->data[3 + 1 * 4] * a->data[1 + 0 * 4] -
+ a->data[3 + 3 * 4] * a->data[1 + 1 * 4] * a->data[2 + 0 * 4]);
+
+ inv.data[2 + 1 * 4] = -(a->data[0 + 0 * 4] * a->data[2 + 1 * 4] * a->data[3 + 3 * 4] +
+ a->data[2 + 0 * 4] * a->data[3 + 1 * 4] * a->data[0 + 3 * 4] +
+ a->data[3 + 0 * 4] * a->data[0 + 1 * 4] * a->data[2 + 3 * 4] -
+ a->data[0 + 3 * 4] * a->data[2 + 1 * 4] * a->data[3 + 0 * 4] -
+ a->data[2 + 3 * 4] * a->data[3 + 1 * 4] * a->data[0 + 0 * 4] -
+ a->data[3 + 3 * 4] * a->data[0 + 1 * 4] * a->data[2 + 0 * 4]);
+
+ inv.data[2 + 2 * 4] = (a->data[0 + 0 * 4] * a->data[1 + 1 * 4] * a->data[3 + 3 * 4] +
+ a->data[1 + 0 * 4] * a->data[3 + 1 * 4] * a->data[0 + 3 * 4] +
+ a->data[3 + 0 * 4] * a->data[0 + 1 * 4] * a->data[1 + 3 * 4] -
+ a->data[0 + 3 * 4] * a->data[1 + 1 * 4] * a->data[3 + 0 * 4] -
+ a->data[1 + 3 * 4] * a->data[3 + 1 * 4] * a->data[0 + 0 * 4] -
+ a->data[3 + 3 * 4] * a->data[0 + 1 * 4] * a->data[1 + 0 * 4]);
+
+ inv.data[2 + 3 * 4] = -(a->data[0 + 0 * 4] * a->data[1 + 1 * 4] * a->data[2 + 3 * 4] +
+ a->data[1 + 0 * 4] * a->data[2 + 1 * 4] * a->data[0 + 3 * 4] +
+ a->data[2 + 0 * 4] * a->data[0 + 1 * 4] * a->data[1 + 3 * 4] -
+ a->data[0 + 3 * 4] * a->data[1 + 1 * 4] * a->data[2 + 0 * 4] -
+ a->data[1 + 3 * 4] * a->data[2 + 1 * 4] * a->data[0 + 0 * 4] -
+ a->data[2 + 3 * 4] * a->data[0 + 1 * 4] * a->data[1 + 0 * 4]);
+
+ inv.data[3 + 0 * 4] = -(a->data[1 + 0 * 4] * a->data[2 + 1 * 4] * a->data[3 + 2 * 4] +
+ a->data[2 + 0 * 4] * a->data[3 + 1 * 4] * a->data[1 + 2 * 4] +
+ a->data[3 + 0 * 4] * a->data[1 + 1 * 4] * a->data[2 + 2 * 4] -
+ a->data[1 + 2 * 4] * a->data[2 + 1 * 4] * a->data[3 + 0 * 4] -
+ a->data[2 + 2 * 4] * a->data[3 + 1 * 4] * a->data[1 + 0 * 4] -
+ a->data[3 + 2 * 4] * a->data[1 + 1 * 4] * a->data[2 + 0 * 4]);
+
+ inv.data[3 + 1 * 4] = (a->data[0 + 0 * 4] * a->data[2 + 1 * 4] * a->data[3 + 2 * 4] +
+ a->data[2 + 0 * 4] * a->data[3 + 1 * 4] * a->data[0 + 2 * 4] +
+ a->data[3 + 0 * 4] * a->data[0 + 1 * 4] * a->data[2 + 2 * 4] -
+ a->data[0 + 2 * 4] * a->data[2 + 1 * 4] * a->data[3 + 0 * 4] -
+ a->data[2 + 2 * 4] * a->data[3 + 1 * 4] * a->data[0 + 0 * 4] -
+ a->data[3 + 2 * 4] * a->data[0 + 1 * 4] * a->data[2 + 0 * 4]);
+
+ inv.data[3 + 2 * 4] = -(a->data[0 + 0 * 4] * a->data[1 + 1 * 4] * a->data[3 + 2 * 4] +
+ a->data[1 + 0 * 4] * a->data[3 + 1 * 4] * a->data[0 + 2 * 4] +
+ a->data[3 + 0 * 4] * a->data[0 + 1 * 4] * a->data[1 + 2 * 4] -
+ a->data[0 + 2 * 4] * a->data[1 + 1 * 4] * a->data[3 + 0 * 4] -
+ a->data[1 + 2 * 4] * a->data[3 + 1 * 4] * a->data[0 + 0 * 4] -
+ a->data[3 + 2 * 4] * a->data[0 + 1 * 4] * a->data[1 + 0 * 4]);
+
+ inv.data[3 + 3 * 4] = (a->data[0 + 0 * 4] * a->data[1 + 1 * 4] * a->data[2 + 2 * 4] +
+ a->data[1 + 0 * 4] * a->data[2 + 1 * 4] * a->data[0 + 2 * 4] +
+ a->data[2 + 0 * 4] * a->data[0 + 1 * 4] * a->data[1 + 2 * 4] -
+ a->data[0 + 2 * 4] * a->data[1 + 1 * 4] * a->data[2 + 0 * 4] -
+ a->data[1 + 2 * 4] * a->data[2 + 1 * 4] * a->data[0 + 0 * 4] -
+ a->data[2 + 2 * 4] * a->data[0 + 1 * 4] * a->data[1 + 0 * 4]);
+
+ /** Remember that our matrix is already transposed and we also got the minors(inside each "inv" entry),
+ so we just use them. We use a->data[0 + 1 * 4] * inv.data[1 + 0 * 4] becouse inv.data is transposed **/
+ float det = a->data[0 + 0 * 4] * inv.data[0 + 0 * 4] + a->data[0 + 1 * 4] * inv.data[1 + 0 * 4] +
+ a->data[0 + 2 * 4] * inv.data[2 + 0 * 4] + a->data[0 + 3 * 4] * inv.data[3 + 0 * 4];
+
+ if(det == 0.0f)
+ return *a;
+
+ det = 1.0f / det;
+
+ for(i = 0; i < 16; i++)
+ inv.data[i] *= det;
+
+ return inv;
+}
+
+Mat4 mat4_mul(const Mat4 *a, const Mat4 *b)
+{
+ int i, j, k;
+ GLfloat sum = 0.0f;
+ Mat4 c;
+ mat4_identity(&c);
+ for(i = 0; i < 4; i++)
+ for(j = 0; j < 4; j++){
+ for(k = 0; k < 4; k++){
+ sum += a->data[i + k * 4] * b->data[k + j * 4];
+ }
+ c.data[i + j * 4] = sum;
+ sum = 0.0f;
+ }
+
+ return c;
+}
+
+Mat4 mat4_translate(const Vec3 *a)
+{
+ Mat4 b;
+ mat4_identity(&b);
+ b.data[0 + 3 * 4] = a->x;
+ b.data[1 + 3 * 4] = a->y;
+ b.data[2 + 3 * 4] = a->z;
+ return b;
+}
+
+Mat4 mat4_scale(GLfloat x, GLfloat y, GLfloat z)
+{
+ Mat4 b;
+ mat4_identity(&b);
+ b.data[0 + 0 * 4] = x;
+ b.data[1 + 1 * 4] = y;
+ b.data[2 + 2 * 4] = z;
+ return b;
+}
+
+Mat4 mat4_rotate(GLfloat degrees, const Vec3 *a)
+{
+ Mat4 b;
+ mat4_identity(&b);
+ GLfloat c = SDL_cosf(toRadians(degrees)), s = SDL_sinf(toRadians(degrees)), o = 1-c;
+ b.data[0 + 0 * 4] = a->x * a->x * o + c;
+ b.data[0 + 1 * 4] = a->x * a->y * o - a->z * s;
+ b.data[0 + 2 * 4] = a->x * a->z * o + a->y * s;
+
+ b.data[1 + 0 * 4] = a->x * a->y * o + a->z * s;
+ b.data[1 + 1 * 4] = a->y * a->y * o + c;
+ b.data[1 + 2 * 4] = a->y * a->z * o - a->x * s;
+
+ b.data[2 + 0 * 4] = a->x * a->z * o - a->y * s;
+ b.data[2 + 1 * 4] = a->y * a->z * o + a->x * s;
+ b.data[2 + 2 * 4] = a->z * a->z * o + c;
+ return b;
+}
+
+Mat4 mat4_rotate_x(GLfloat degrees)
+{
+ Mat4 a;
+ mat4_identity(&a);
+ GLfloat c = SDL_cosf(toRadians(degrees)), s = SDL_sinf(toRadians(degrees));
+ a.data[1 + 1 * 4] = c;
+ a.data[2 + 1 * 4] = s;
+ a.data[1 + 2 * 4] = -s;
+ a.data[2 + 2 * 4] = c;
+ return a;
+}
+
+Mat4 mat4_rotate_y(GLfloat degrees)
+{
+ Mat4 a;
+ mat4_identity(&a);
+ GLfloat c = SDL_cosf(toRadians(degrees)), s = SDL_sinf(toRadians(degrees));
+ a.data[0 + 0 * 4] = c;
+ a.data[2 + 0 * 4] = -s;
+ a.data[0 + 2 * 4] = s;
+ a.data[2 + 2 * 4] = c;
+ return a;
+}
+
+Mat4 mat4_rotate_z(GLfloat degrees)
+{
+ Mat4 a;
+ mat4_identity(&a);
+ GLfloat c = SDL_cosf(toRadians(degrees)), s = SDL_sinf(toRadians(degrees));
+ a.data[0 + 0 * 4] = c;
+ a.data[1 + 0 * 4] = s;
+ a.data[0 + 1 * 4] = -s;
+ a.data[1 + 1 * 4] = c;
+ return a;
+}
+
+Mat4 mat4_perspective(GLfloat fov, GLfloat aspect, GLfloat zNear, GLfloat zFar)
+{
+ Mat4 a;
+ GLubyte i, j;
+ for(i = 0; i < 4; i++)
+ for(j = 0; j < 4; j++)
+ a.data[i + j * 4] = 0.0f;
+ fov = toRadians(fov); // To radians
+
+ a.data[0 + 0 * 4] = ( (1.0f / SDL_tanf( fov/2.0f )) / aspect);
+ a.data[1 + 1 * 4] = (1.0f / SDL_tanf( fov/2.0f ));
+ a.data[2 + 2 * 4] = -( (zFar + zNear) / (zFar - zNear) );
+ a.data[2 + 3 * 4] = -( ( 2.0f * zFar * zNear) / (zFar - zNear) );
+ a.data[3 + 2 * 4] = -1.0f;
+
+ return a;
+}
+
+Mat4
+mat4_orthographic(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far)
+{
+ Mat4 a;
+ mat4_identity(&a);
+ a.data[0 + 0 * 4] = 2.0f / (right - left);
+ a.data[1 + 1 * 4] = 2.0f / (top - bottom);
+ a.data[2 + 2 * 4] = 2.0f / (near - far);
+
+ a.data[0 + 3 * 4] = (left + right) / (left - right);
+ a.data[1 + 3 * 4] = (bottom + top) / (bottom - top);
+ a.data[2 + 3 * 4] = (far + near) / (far - near);
+
+ return a;
+}
+
+Mat4 mat4_lookAt(Vec3 *eye, Vec3 *center, Vec3 *up)
+{
+ Vec3 temp = vec3_sub(center, eye);
+
+ Vec3 f = vec3_normalize(&temp);
+ Vec3 u = vec3_normalize(up);
+ temp = vec3_cross_mul(&f, &u);
+ Vec3 s = vec3_normalize(&temp);
+ u = vec3_cross_mul(&s, &f);
+
+ Mat4 a;
+ mat4_identity(&a);
+
+ a.data[0 + 0 * 4] = s.x;
+ a.data[0 + 1 * 4] = s.y;
+ a.data[0 + 2 * 4] = s.z;
+ a.data[1 + 0 * 4] = u.x;
+ a.data[1 + 1 * 4] = u.y;
+ a.data[1 + 2 * 4] = u.z;
+ a.data[2 + 0 * 4] =-f.x;
+ a.data[2 + 1 * 4] =-f.y;
+ a.data[2 + 2 * 4] =-f.z;
+ a.data[0 + 3 * 4] =-vec3_dot_mul(&s, eye);
+ a.data[1 + 3 * 4] =-vec3_dot_mul(&u, eye);
+ a.data[2 + 3 * 4] = vec3_dot_mul(&f, eye);
+
+ return a;
+}
+
+Vec3 mat4_mul_vec3(const Mat4 *a, const Vec3 *b)
+{
+ Vec3 c;
+ c.x = a->data[0 + 0 * 4] * b->x + a->data[0 + 1 * 4] * b->y + a->data[0 + 2 * 4] * b->z + a->data[0 + 3 * 4];
+ c.y = a->data[1 + 0 * 4] * b->x + a->data[1 + 1 * 4] * b->y + a->data[1 + 2 * 4] * b->z + a->data[1 + 3 * 4];
+ c.z = a->data[2 + 0 * 4] * b->x + a->data[2 + 1 * 4] * b->y + a->data[2 + 2 * 4] * b->z + a->data[2 + 3 * 4];
+ return c;
+}
+
+Vec4 mat4_mul_vec4(const Mat4 *a, const Vec4 *b)
+{
+ Vec4 c;
+ c.x = a->data[0 + 0 * 4] * b->x + a->data[0 + 1 * 4] * b->y + a->data[0 + 2 * 4] * b->z + a->data[0 + 3 * 4] * b->w;
+ c.y = a->data[1 + 0 * 4] * b->x + a->data[1 + 1 * 4] * b->y + a->data[1 + 2 * 4] * b->z + a->data[1 + 3 * 4] * b->w;
+ c.z = a->data[2 + 0 * 4] * b->x + a->data[2 + 1 * 4] * b->y + a->data[2 + 2 * 4] * b->z + a->data[2 + 3 * 4] * b->w;
+ c.w = a->data[3 + 0 * 4] * b->x + a->data[3 + 1 * 4] * b->y + a->data[3 + 2 * 4] * b->z + a->data[3 + 3 * 4] * b->w;
+ return c;
+}
diff --git a/09-september/tomcat/math/matrix4x4.h b/09-september/tomcat/math/matrix4x4.h
new file mode 100644
index 0000000..74918fe
--- /dev/null
+++ b/09-september/tomcat/math/matrix4x4.h
@@ -0,0 +1,39 @@
+#ifndef MATRIX4X4_H
+#define MATRIX4X4_H
+
+#include <GL/glew.h>
+#include "vector.h"
+
+/* accesing data: row + column * width */
+typedef struct
+{
+ GLfloat data[16];
+} Mat4;
+
+extern void mat4_identity(Mat4 *a);
+extern Mat4 mat4_inverse(const Mat4 *a);
+extern Mat4 mat4_mul(const Mat4 *a, const Mat4 *b);
+
+extern Mat4 mat4_translate(const Vec3 *a);
+extern Mat4 mat4_scale(GLfloat x, GLfloat y, GLfloat z);
+extern Mat4 mat4_rotate_x(GLfloat degrees);
+extern Mat4 mat4_rotate_y(GLfloat degrees);
+extern Mat4 mat4_rotate_z(GLfloat degrees);
+extern Mat4 mat4_rotate(GLfloat degrees, const Vec3 *a);
+
+extern Mat4
+mat4_perspective(GLfloat fov, GLfloat aspect, GLfloat zNear, GLfloat zFar);
+
+extern Mat4
+mat4_orthographic(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far);
+
+extern Mat4
+mat4_lookAt(Vec3 *eye, Vec3 *center, Vec3 *up);
+
+extern Vec3
+mat4_mul_vec3(const Mat4 *a, const Vec3 *b);
+
+extern Vec4
+mat4_mul_vec4(const Mat4 *a, const Vec4 *b);
+
+#endif // MATRIX4X4_H
diff --git a/09-september/tomcat/math/vector.c b/09-september/tomcat/math/vector.c
new file mode 100644
index 0000000..230de51
--- /dev/null
+++ b/09-september/tomcat/math/vector.c
@@ -0,0 +1,177 @@
+#include "vector.h"
+#include <SDL2/SDL.h>
+
+Vec4 vec4_add(const Vec4 *a, const Vec4 *b)
+{
+ Vec4 c;
+ c.x = a->x + b->x;
+ c.y = a->y + b->y;
+ c.z = a->z + b->z;
+ c.w = a->w + b->w;
+ return c;
+}
+
+Vec4 vec4_sub(const Vec4 *a, const Vec4 *b)
+{
+ Vec4 c;
+ c.x = a->x - b->x;
+ c.y = a->y - b->y;
+ c.z = a->z - b->z;
+ c.w = a->w - b->w;
+ return c;
+}
+
+Vec4 vec4_scalar_mul(const Vec4 *a, GLfloat scalar)
+{
+ Vec4 c;
+ c.x = a->x * scalar;
+ c.y = a->y * scalar;
+ c.z = a->z * scalar;
+ c.w = a->w * scalar;
+ return c;
+}
+
+GLfloat vec4_dot_mul(const Vec4 *a, const Vec4 *b)
+{
+ return ( (a->x * b->x) + (a->y * b->y) + (a->z * b->z) + (a->w * b->w) );
+}
+
+GLfloat vec4_length(Vec4 *a)
+{
+ return SDL_sqrtf(SDL_pow(a->x, 2.0f) + SDL_pow(a->y, 2.0f) + SDL_pow(a->z, 2.0f) + SDL_pow(a->w, 2.0f) );
+}
+
+GLfloat vec4_length2(Vec4 *a)
+{
+ return ( SDL_pow(a->x, 2.0f) + SDL_pow(a->y, 2.0f) + SDL_pow(a->z, 2.0f) + SDL_pow(a->w, 2.0f) );
+}
+
+Vec4 vec4_normalize(Vec4 *a)
+{
+ Vec4 b;
+ GLfloat length = vec4_length(a);
+ b.x = a->x / length;
+ b.y = a->y / length;
+ b.z = a->z / length;
+ b.w = a->w / length;
+ return b;
+}
+
+Vec3 vec3_add(const Vec3 *a, const Vec3 *b)
+{
+ Vec3 c;
+ c.x = a->x + b->x;
+ c.y = a->y + b->y;
+ c.z = a->z + b->z;
+ return c;
+}
+
+Vec3 vec3_sub(const Vec3 *a, const Vec3 *b)
+{
+ Vec3 c;
+ c.x = a->x - b->x;
+ c.y = a->y - b->y;
+ c.z = a->z - b->z;
+ return c;
+}
+
+Vec3 vec3_scalar_mul(const Vec3 *a, GLfloat scalar)
+{
+ Vec3 c;
+ c.x = a->x * scalar;
+ c.y = a->y * scalar;
+ c.z = a->z * scalar;
+ return c;
+}
+
+GLfloat vec3_dot_mul(const Vec3 *a, const Vec3 *b)
+{
+ return ( (a->x * b->x) + (a->y * b->y) + (a->z * b->z) );
+}
+
+
+Vec3 vec3_cross_mul(const Vec3 *a, const Vec3 *b)
+{
+ Vec3 c;
+ c.x = (a->y * b->z) - (a->z * b->y);
+ c.y = (a->z * b->x) - (a->x * b->z);
+ c.z = (a->x * b->y) - (a->y * b->x);
+ return c;
+}
+
+GLfloat vec3_length(Vec3 *a)
+{
+ return SDL_sqrtf(SDL_pow(a->x, 2.0f) + SDL_pow(a->y, 2.0f) + SDL_pow(a->z, 2.0f));
+}
+
+GLfloat vec3_length2(Vec3 *a)
+{
+ return ( SDL_pow(a->x, 2.0f) + SDL_pow(a->y, 2.0f) + SDL_pow(a->z, 2.0f) );
+}
+
+Vec3 vec3_normalize(Vec3 *a)
+{
+ Vec3 b;
+ GLfloat length = vec3_length(a);
+ b.x = a->x / length;
+ b.y = a->y / length;
+ b.z = a->z / length;
+ return b;
+}
+
+Vec2 vec2_add(const Vec2 *a, const Vec2 *b)
+{
+ Vec2 c;
+ c.x = a->x + b->x;
+ c.y = a->y + b->y;
+ return c;
+}
+
+Vec2 vec2_sub(const Vec2 *a, const Vec2 *b)
+{
+ Vec2 c;
+ c.x = a->x - b->x;
+ c.y = a->y - b->y;
+ return c;
+}
+
+Vec2 vec2_scalar_mul(const Vec2 *a, GLfloat scalar)
+{
+ Vec2 c;
+ c.x = a->x * scalar;
+ c.y = a->y * scalar;
+ return c;
+}
+
+GLfloat vec2_dot_mul(const Vec2 *a, const Vec2 *b)
+{
+ return ( (a->x * b->x) + (a->y * b->y) );
+}
+
+
+Vec2 vec2_cross_mul(const Vec2 *a, const Vec2 *b)
+{
+ Vec2 c;
+ c.x = (a->x * b->y) - (a->y * b->x);
+ c.y = (a->y * b->x) - (a->x * b->y);
+ return c;
+}
+
+GLfloat vec2_length(Vec2 *a)
+{
+ return SDL_sqrtf(SDL_pow(a->x, 2.0f) + SDL_pow(a->y, 2.0f) );
+}
+
+GLfloat vec2_length2(Vec2 *a)
+{
+ return ( SDL_pow(a->x, 2.0f) + SDL_pow(a->y, 2.0f) );
+}
+
+Vec2 vec2_normalize(Vec2 *a)
+{
+ Vec2 b;
+ GLfloat length = vec2_length(a);
+ b.x = a->x / length;
+ b.y = a->y / length;
+ return b;
+}
diff --git a/09-september/tomcat/math/vector.h b/09-september/tomcat/math/vector.h
new file mode 100644
index 0000000..173fc11
--- /dev/null
+++ b/09-september/tomcat/math/vector.h
@@ -0,0 +1,47 @@
+#ifndef VECTOR4F_H
+#define VECTOR4F_H
+
+#include <GL/glew.h>
+
+typedef struct _Vec4
+{
+ GLfloat x, y, z, w;
+} Vec4;
+
+extern Vec4 vec4_add(const Vec4 *a, const Vec4 *b);
+extern Vec4 vec4_sub(const Vec4 *a, const Vec4 *b);
+extern Vec4 vec4_scalar_mul(const Vec4 *a, GLfloat scalar);
+extern GLfloat vec4_dot_mul(const Vec4 *a, const Vec4 *b);
+extern GLfloat vec4_length(Vec4 *a);
+extern GLfloat vec4_length2(Vec4 *a);
+extern Vec4 vec4_normalize(Vec4 *a);
+
+typedef struct _Vec3
+{
+ GLfloat x, y, z;
+} Vec3;
+
+extern Vec3 vec3_add(const Vec3 *a, const Vec3 *b);
+extern Vec3 vec3_sub(const Vec3 *a, const Vec3 *b);
+extern Vec3 vec3_scalar_mul(const Vec3 *a, GLfloat scalar);
+extern GLfloat vec3_dot_mul(const Vec3 *a, const Vec3 *b);
+extern Vec3 vec3_cross_mul(const Vec3 *a, const Vec3 *b);
+extern GLfloat vec3_length(Vec3 *a);
+extern GLfloat vec3_length2(Vec3 *a);
+extern Vec3 vec3_normalize(Vec3 *a);
+
+typedef struct _Vec2
+{
+ GLfloat x, y;
+} Vec2;
+
+extern Vec2 vec2_add(const Vec2 *a, const Vec2 *b);
+extern Vec2 vec2_sub(const Vec2 *a, const Vec2 *b);
+extern Vec2 vec2_scalar_mul(const Vec2 *a, GLfloat scalar);
+extern GLfloat vec2_dot_mul(const Vec2 *a, const Vec2 *b);
+extern Vec2 vec2_cross_mul(const Vec2 *a, const Vec2 *b);
+extern GLfloat vec2_length(Vec2 *a);
+extern GLfloat vec2_length2(Vec2 *a);
+extern Vec2 vec2_normalize(Vec2 *a);
+
+#endif // VECTOR4F_H
diff --git a/09-september/tomcat/not_in_use/fbo.c b/09-september/tomcat/not_in_use/fbo.c
new file mode 100644
index 0000000..5880276
--- /dev/null
+++ b/09-september/tomcat/not_in_use/fbo.c
@@ -0,0 +1,108 @@
+#include "fbo.h"
+#include "renderer.h"
+#include "../util/util.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+Fbo *fbo_new(const char *name, GLint width, GLint height)
+{
+ Fbo *fbo;
+
+ if(strlen(name) >= MAX_PATH_LENGTH)
+ Util_FatalError("Fbo name is too long: %s\n", name);
+
+ if(render.num_fbos >= MAX_FBOS)
+ return NULL;
+
+ fbo = malloc(sizeof(Fbo));
+ memset(fbo, 0, sizeof(Fbo));
+
+ glGenFramebuffers(1, &fbo->frame_buffer);
+
+ return fbo;
+}
+
+void fbo_attach_buffer(Fbo *fbo, GLenum format, int index)
+{
+ GLenum attachment;
+ GLuint *buffer;
+
+ switch(format)
+ {
+ case GL_RGB:
+ case GL_RGBA:
+ fbo->color_format = format;
+ buffer = &fbo->color_buffer[index];
+ attachment = GL_COLOR_ATTACHMENT0 + index;
+ break;
+
+ case GL_DEPTH_COMPONENT:
+ fbo->depth_format = format;
+ buffer = &fbo->depth_buffer;
+ attachment = GL_DEPTH_ATTACHMENT;
+ break;
+
+ default:
+ Util_FatalError("Invalid fbo buffer format\n");
+ }
+
+ if(*buffer == 0)
+ {
+ glGenRenderbuffers(1, buffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, *buffer);
+ glRenderbufferStorage(GL_RENDERBUFFER, format, fbo->width, fbo->height);
+
+ fbo_bind(fbo);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, *buffer);
+ fbo_bind(NULL);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ }
+}
+
+void fbo_attach_texture(Fbo *fbo, Texture *t, GLenum attachment)
+{
+ int index;
+
+ fbo_bind(fbo);
+ if(attachment >= GL_COLOR_ATTACHMENT0 && attachment < GL_COLOR_ATTACHMENT0 + 8)
+ {
+ glFramebufferTexture(GL_FRAMEBUFFER, attachment, t->tex_id, 0);
+ glDrawBuffers(1, &attachment);
+
+ index = attachment - GL_COLOR_ATTACHMENT0;
+ fbo->color_textures[index] = t;
+ }
+
+ fbo_bind(NULL);
+}
+
+void fbo_bind(Fbo *fbo)
+{
+ if(fbo)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo->frame_buffer);
+ glViewport(0, 0, fbo->width, fbo->height);
+ }
+ else
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glViewport(0, 0, render.window->Width, render.window->Height);
+ }
+}
+
+void fbo_purge(Fbo *fbo)
+{
+ int i;
+
+ for(i = 0; i < 8; i++)
+ if(fbo->color_buffer[i])
+ {
+ glDeleteRenderbuffers(1, &fbo->color_buffer[i]);
+ }
+ if(fbo->depth_buffer)
+ glDeleteRenderbuffers(1, &fbo->depth_buffer);
+
+ if(fbo->frame_buffer)
+ glDeleteFramebuffers(1, &fbo->frame_buffer);
+}
diff --git a/09-september/tomcat/not_in_use/fbo.h b/09-september/tomcat/not_in_use/fbo.h
new file mode 100644
index 0000000..244cb7f
--- /dev/null
+++ b/09-september/tomcat/not_in_use/fbo.h
@@ -0,0 +1,35 @@
+#ifndef FBO_H
+#define FBO_H
+
+#include "../shared.h"
+
+struct _Texture;
+
+typedef struct _Fbo
+{
+ char _name[MAX_PATH_LENGTH];
+
+ GLuint frame_buffer; /** Actual fbo object**/
+
+ /** color_texture when you want to render to a
+ texture and color_buffer when you want to render
+ to a RenderBuffer **/
+ struct _Texture *color_textures[8];
+ GLuint color_buffer[8];
+ GLenum color_format;
+
+ struct _Texture *depth_texture;
+ GLuint depth_buffer;
+ GLenum depth_format;
+
+ GLint width;
+ GLint height;
+} Fbo;
+
+extern Fbo *fbo_new(const char *name, GLint width, GLint height);
+extern void fbo_attach_buffer(Fbo *fbo, GLenum format, int index);
+extern void fbo_attach_texture(Fbo *fbo, struct _Texture *t, GLenum attachment);
+extern void fbo_bind(Fbo *fbo);
+extern void fbo_purge(Fbo *fbo);
+
+#endif // FBO_H
diff --git a/09-september/tomcat/particles/particles.c b/09-september/tomcat/particles/particles.c
new file mode 100644
index 0000000..5e99e06
--- /dev/null
+++ b/09-september/tomcat/particles/particles.c
@@ -0,0 +1,153 @@
+#include "particles.h"
+#include "../util/util_time.h"
+#include "../util/util.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+ParticleManager particles;
+
+static int compare_particles(const void *_a, const void *_b);
+
+void Particles_Init()
+{
+ memset( &particles, 0, sizeof(ParticleManager) );
+}
+
+ParticleSystem *Particles_AddSystem()
+{
+ if(particles.num_systems >= MAX_PARTICLES_SYSTEMS)
+ return NULL;
+
+ ParticleSystem *s = malloc( sizeof(ParticleSystem) );
+ particles.systems[particles.num_systems] = s;
+ memset( s, 0, sizeof(ParticleSystem) );
+
+ particles.num_systems += 1;
+
+ return s;
+}
+
+void Particles_RemoveSystem(ParticleSystem *system)
+{
+ int index = &system - particles.systems;
+
+ if(index > MAX_PARTICLES_SYSTEMS || index < 0)
+ {
+ fprintf(stderr, "Invalid particle system!\n");
+ return;
+ }
+ /* TODO: Finish this function */
+}
+
+void Particles_EmitParticle(ParticleSystem *system, Particle *p)
+{
+ if(system->num_particles >= MAX_PARTICLES_PER_SYSTEM)
+ return;
+
+ Particle *dest = &system->particles[system->num_particles];
+ memcpy(dest, p, sizeof(Particle));
+
+ system->num_particles += 1;
+}
+
+void Particles_Update(const Vec3 *camera_position)
+{
+ Particle *c = NULL; /* Current particle */
+ ParticleSystem *s = NULL; /* Current particle system */
+ Texture *t = NULL; /* Texture of the current system */
+ Vec3 d; /* Vector for calculating the distance to the camera */
+ Vec3 velocity;
+
+ int i, j;
+ for(i = 0; i < particles.num_systems; i++)
+ {
+ s = particles.systems[i];
+ t = s->texture;
+
+ /* We ceil it so we dont truncate the decimal part and get always 0 */
+ int particles_to_emit = (int)ceil( s->emit_rate * Time_GetFrameTime() );
+
+ /* Emit the automatically emited particles */
+ for(j = 0; j < particles_to_emit; j++)
+ {
+ velocity.x = Util_RandomF(0.0f, 50.0f);
+ velocity.y = Util_RandomF(0.0f, 50.0f);
+ velocity.z = Util_RandomF(0.0f, 50.0f);
+ velocity = vec3_normalize(&velocity);
+
+ if(s->num_particles >= MAX_PARTICLES_PER_SYSTEM)
+ break; /* So we dont overflow */
+
+ c = &s->particles[s->num_particles ++];
+ memset(c, 0, sizeof(Particle));
+
+ c->life_length = s->life_length;
+ c->position = s->position;
+ c->scale = 10.0f;
+ c->weight = s->weight;
+ c->velocity = vec3_scalar_mul(&velocity, s->speed);
+ }
+
+ for(j = 0; j < s->num_particles; j++)
+ {
+ c = &s->particles[j];
+
+ /* Update each particle -.- */
+ c->velocity.y += c->weight * Time_GetFrameTime();
+ Vec3 velocity = vec3_scalar_mul(&c->velocity, Time_GetFrameTime());
+ c->position = vec3_add(&c->position, &velocity);
+
+ /* Update animations
+ * How far into its life the particle is */
+ float life_factor = c->elapsed_time / c->life_length;
+ /* How many different stages there are */
+ int stage_count = t->number_of_rows * t->number_of_rows;
+ float atlas_progression = life_factor * stage_count;
+ int index = floor(atlas_progression);
+
+ int column = index % t->number_of_rows;
+ int row = index / t->number_of_rows;
+ c->tex_offset.x = (float)column / t->number_of_rows;
+ c->tex_offset.y = (float)row / t->number_of_rows;
+ /* End of animation update */
+
+ c->elapsed_time += Time_GetFrameTime();
+
+ if(c->elapsed_time > c->life_length)
+ {
+ /* "Kill" the particle by overriding it with the last particle on the array */
+ memmove( c, &s->particles[s->num_particles], sizeof(Particle) );
+ s->num_particles -= 1;
+ }
+ else
+ {
+ d = vec3_sub(camera_position, &c->position);
+ c->distance_to_camera = vec3_length2(&d);
+ }
+ }
+ /*Sort all particles on system by distance to camera */
+ qsort(s->particles, s->num_particles, sizeof(Particle), compare_particles);
+ }
+}
+
+void Particles_Shutdown()
+{
+ int i;
+ for(i = 0; i < particles.num_systems; i++)
+ {
+ if(particles.systems[i])
+ free(particles.systems[i]);
+ }
+ memset(&particles, 0, sizeof(ParticleManager));
+}
+
+static int compare_particles(const void *_a, const void *_b)
+{
+ Particle *a = (Particle *)_a;
+ Particle *b = (Particle *)_b;
+
+ if(a->distance_to_camera < b->distance_to_camera) return -1;
+ if(a->distance_to_camera > b->distance_to_camera) return 1;
+ return 0;
+}
diff --git a/09-september/tomcat/particles/particles.h b/09-september/tomcat/particles/particles.h
new file mode 100644
index 0000000..8404ff4
--- /dev/null
+++ b/09-september/tomcat/particles/particles.h
@@ -0,0 +1,62 @@
+#ifndef PARTICLES_H
+#define PARTICLES_H
+
+#include "../renderer/renderer_types.h"
+#include "../math/vector.h"
+
+#define MAX_PARTICLES_PER_SYSTEM 10000
+#define MAX_PARTICLES_SYSTEMS 10
+
+typedef struct _Particle
+{
+ Vec3 position;
+ Vec3 velocity;
+
+ Vec2 tex_offset; /* Used for animating */
+
+ GLfloat weight; /* How does the gravity affect the particle (In theory it should be a vector)*/
+ GLfloat life_length;
+ GLfloat rotation;
+ GLfloat scale;
+ GLfloat distance_to_camera; /* Used for sorting the systems */
+
+ GLfloat elapsed_time;
+} Particle;
+
+typedef struct _ParticleSystem
+{
+ Vec3 position;
+
+ GLint emit_rate;
+ GLfloat speed;
+ GLfloat weight;
+ GLfloat life_length;
+
+ bool additive; /* Additive vs alpha blending */
+
+ Texture *texture;
+ Particle particles[MAX_PARTICLES_PER_SYSTEM];
+ int num_particles;
+
+} ParticleSystem;
+
+typedef struct
+{
+ ParticleSystem *systems[MAX_PARTICLES_SYSTEMS];
+ int num_systems;
+} ParticleManager;
+
+extern ParticleManager particles;
+
+extern void Particles_Init();
+
+extern ParticleSystem *Particles_AddSystem();
+extern void Particles_RemoveSystem(ParticleSystem *system);
+/* Used for manual emission */
+extern void Particles_EmitParticle(ParticleSystem *system, Particle *p);
+/* Removes a particle if it "dies" */
+extern void Particles_Update(const Vec3 *camera_position);
+/* We need to delete all dynamically allocated particles that still remains */
+extern void Particles_Shutdown();
+
+#endif // PARTICLES_H
diff --git a/09-september/tomcat/renderer/camera.c b/09-september/tomcat/renderer/camera.c
new file mode 100644
index 0000000..8a0c620
--- /dev/null
+++ b/09-september/tomcat/renderer/camera.c
@@ -0,0 +1,95 @@
+#include "camera.h"
+
+#include "../util/util_time.h"
+
+static const float movementSpeed = 40.0f;
+
+Camera *camera_new()
+{
+ Camera *camera;
+ camera = malloc( sizeof(Camera) );
+
+ camera->position = (Vec3){ 0.0f, 6.0f, 0.0f };
+ camera->viewDirection = (Vec3){ 0.5f, 0.0f, -1.0f };
+ camera->needsUpdate = true;
+ camera->up = (Vec3){ 0.0f, 1.0f, 0.0f };
+
+ return camera;
+}
+
+void camera_move_up(Camera* camera)
+{
+ Vec3 temp = vec3_scalar_mul(&camera->up, movementSpeed * Time_GetFrameTime());
+ camera->position = vec3_add(&camera->position, &temp);
+}
+
+void camera_move_down(Camera* camera)
+{
+ Vec3 temp = vec3_scalar_mul(&camera->up, -movementSpeed * Time_GetFrameTime());
+ camera->position = vec3_add(&camera->position, &temp);
+}
+
+void camera_move_left(Camera* camera)
+{
+ Vec3 strafeDirection = vec3_cross_mul(&camera->viewDirection, &camera->up);
+ strafeDirection = vec3_scalar_mul(&strafeDirection, -movementSpeed * Time_GetFrameTime());
+ camera->position = vec3_add(&camera->position, &strafeDirection);
+}
+
+void camera_move_right(Camera* camera)
+{
+ Vec3 strafeDirection = vec3_cross_mul(&camera->viewDirection, &camera->up);
+ strafeDirection = vec3_scalar_mul(&strafeDirection, movementSpeed * Time_GetFrameTime());
+ camera->position = vec3_add(&camera->position, &strafeDirection);
+}
+
+void camera_move_foward(Camera* camera)
+{
+ Vec3 temp = vec3_scalar_mul(&camera->viewDirection, movementSpeed * Time_GetFrameTime());
+ camera->position = vec3_add(&camera->position, &temp);
+}
+
+void camera_move_backward(Camera* camera)
+{
+ Vec3 temp = vec3_scalar_mul(&camera->viewDirection, -movementSpeed * Time_GetFrameTime());
+ camera->position = vec3_add(&camera->position, &temp);
+}
+
+void camera_mouse_update(Camera *camera, const Vec2 *newMousePosition)
+{
+ Vec2 mouseDelta = vec2_sub(newMousePosition, &camera->mousePosition);
+
+ /* El if evita que el mouse se teletrasporte al cambiar de posicion muy rapido */
+ if(vec2_length(&mouseDelta) > 50.0f){
+ camera->mousePosition = *newMousePosition;
+ return;
+ }
+
+ Vec3 verticalRotation = vec3_cross_mul(&camera->viewDirection, &camera->up);
+
+ Mat4 temp = mat4_rotate(mouseDelta.x * -0.5f, &camera->up);
+
+ camera->viewDirection = mat4_mul_vec3(&temp, &camera->viewDirection);
+
+ temp = mat4_rotate(mouseDelta.y * -0.5f, &verticalRotation);
+ camera->viewDirection = mat4_mul_vec3(&temp, &camera->viewDirection);
+
+ camera->mousePosition = *newMousePosition;
+
+ camera->needsUpdate = true;
+}
+
+void camera_free(Camera *camera)
+{
+ free(camera);
+}
+
+Mat4 camera_get_model_to_view_matrix(Camera* camera)
+{
+ if(camera->needsUpdate)
+ {
+ Vec3 temp = vec3_add(&camera->position, &camera->viewDirection);
+ camera->viewMatrix = mat4_lookAt(&camera->position, &temp, &camera->up);
+ }
+ return camera->viewMatrix;
+}
diff --git a/09-september/tomcat/renderer/camera.h b/09-september/tomcat/renderer/camera.h
new file mode 100644
index 0000000..0c63178
--- /dev/null
+++ b/09-september/tomcat/renderer/camera.h
@@ -0,0 +1,32 @@
+#ifndef CAMERA_H
+#define CAMERA_H
+
+#include "../math/matrix4x4.h"
+#include "../math/vector.h"
+#include <stdbool.h>
+
+typedef struct
+{
+ Vec3 position;
+ Vec3 viewDirection;
+ Vec3 up;
+ Mat4 projectionMatrix;
+ Mat4 viewMatrix;
+ Vec2 mousePosition;
+ bool needsUpdate;
+} Camera;
+
+extern Camera *camera_new();
+extern void camera_move_up(Camera *camera);
+extern void camera_move_down(Camera *camera);
+extern void camera_move_left(Camera *camera);
+extern void camera_move_right(Camera *camera);
+extern void camera_move_foward(Camera *camera);
+extern void camera_move_backward(Camera *camera);
+extern void camera_mouse_update(Camera *camera, const Vec2 *newMousePosition);
+extern void camera_free(Camera *camera);
+
+/* Consigue la viewMatrix */
+extern Mat4 camera_get_model_to_view_matrix(Camera *camera);
+
+#endif // CAMERA_H
diff --git a/09-september/tomcat/renderer/entity.c b/09-september/tomcat/renderer/entity.c
new file mode 100644
index 0000000..d5e7429
--- /dev/null
+++ b/09-september/tomcat/renderer/entity.c
@@ -0,0 +1,33 @@
+#include "renderer.h"
+
+Mat4 Entity_GetModelTransform(Entity* entity)
+{
+ Mat4 temp;
+ Mat4 rotation = mat4_rotate_x(entity->rotX);
+ temp = mat4_rotate_y(entity->rotY);
+ rotation = mat4_mul(&rotation, &temp);
+ temp = mat4_rotate_z(entity->rotZ);
+ rotation = mat4_mul(&rotation, &temp);
+
+ temp = mat4_translate(&entity->position);
+
+ Mat4 modelTransform = mat4_mul(&temp, &rotation);
+ temp = mat4_scale(entity->scale[0], entity->scale[1], entity->scale[2]);
+ modelTransform = mat4_mul(&modelTransform, &temp);
+
+ return modelTransform;
+}
+
+Vec2 Entity_GetTexOffset(Entity *entity)
+{
+ /* Offset inside a texture atlas should default to (0, 0)*/
+ Vec2 tex_offset;
+ Texture *t = entity->texture;
+
+ int column = entity->index % t->number_of_rows;
+ int row = entity->index / t->number_of_rows;
+ tex_offset.x = (float)column / t->number_of_rows;
+ tex_offset.y = (float)row / t->number_of_rows;
+
+ return tex_offset;
+}
diff --git a/09-september/tomcat/renderer/mesh.c b/09-september/tomcat/renderer/mesh.c
new file mode 100644
index 0000000..7ef27dc
--- /dev/null
+++ b/09-september/tomcat/renderer/mesh.c
@@ -0,0 +1,146 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "mesh.h"
+#include "renderer.h"
+
+#define NUM_ARRAY_ELEMENTS(a) sizeof(a) / sizeof(*a)
+
+Mesh *mesh_new(vertex_t* vertices, GLsizeiptr vertexBuffersize,
+ GLushort* indices, GLsizeiptr indexBuffersize)
+{
+ Mesh *mesh;
+ mesh = malloc( sizeof(Mesh) );
+ memset(mesh, 0, sizeof(Mesh) );
+
+ render.meshes[render.num_meshes] = mesh;
+ render.num_meshes += 1;
+
+ mesh->num_indices = ( indexBuffersize / sizeof(GLushort) );
+
+ glGenVertexArrays(1, &mesh->vao);
+ glGenBuffers(1, &mesh->vbo);
+ glGenBuffers(1, &mesh->ebo);
+
+ glBindVertexArray(mesh->vao);
+
+ glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
+ glBufferData(GL_ARRAY_BUFFER, vertexBuffersize, vertices, GL_STATIC_DRAW);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->ebo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBuffersize, indices, GL_STATIC_DRAW);
+
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+ glEnableVertexAttribArray(2);
+ glEnableVertexAttribArray(3);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (const void*)offsetof(vertex_t, position) );
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (const void*)offsetof(vertex_t, texCoord) );
+ glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (const void*)offsetof(vertex_t, normal) );
+ glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(vertex_t), (const void*)offsetof(vertex_t, tangent) );
+
+ glBindVertexArray(0);
+ return mesh;
+}
+
+Mesh *mesh_make_skybox(float size)
+{
+ Mesh *mesh;
+ mesh = malloc( sizeof(Mesh) );
+ memset(mesh, 0, sizeof(Mesh) );
+
+ render.meshes[render.num_meshes] = mesh;
+ render.num_meshes += 1;
+
+ Vec3 positions[] =
+ {
+ {-size, size, -size}, {-size, -size, -size}, {+size, -size, -size},
+ {+size, -size, -size}, {+size, +size, -size}, {-size, +size, -size},
+
+ {-size, -size, +size}, {-size, -size, -size}, {-size, +size, -size},
+ {-size, +size, -size}, {-size, +size, +size}, {-size, -size, +size},
+
+ {+size, -size, -size}, {+size, -size, +size}, {+size, +size, +size},
+ {+size, +size, +size}, {+size, +size, -size}, {+size, -size, -size},
+
+ {-size, -size, +size}, {-size, +size, +size}, {+size, +size, +size},
+ {+size, +size, +size}, {+size, -size, +size}, {-size, -size, +size},
+
+ {-size, +size, -size}, {+size, +size, -size}, {+size, +size, +size},
+ {+size, +size, +size}, {-size, +size, +size}, {-size, +size, -size},
+
+ {-size, -size, -size}, {-size, -size, +size}, {+size, -size, -size},
+ {+size, -size, -size}, {-size, -size, +size}, {+size, -size, +size}
+ };
+
+ mesh->num_indices = 0;
+ mesh->num_vertices = NUM_ARRAY_ELEMENTS(positions);
+
+ glGenVertexArrays(1, &mesh->vao);
+ glGenBuffers(1, &mesh->vbo);
+ mesh->ebo = 0;
+
+ glBindVertexArray(mesh->vao);
+
+ glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
+
+ glEnableVertexAttribArray(0);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0 );
+
+ glBindVertexArray(0);
+ return mesh;
+}
+
+Mesh *mesh_make_quad()
+{
+ Mesh *mesh;
+ mesh = malloc( sizeof(Mesh) );
+ memset(mesh, 0, sizeof(Mesh) );
+
+ render.meshes[render.num_meshes] = mesh;
+ render.num_meshes += 1;
+
+ Vec3 positions[] =
+ {
+ { -0.5f, +0.5f, +0.0f}, { -0.5f, -0.5f, +0.0f},
+ { +0.5f, +0.5f, +0.0f}, { +0.5f, -0.5f, +0.0f}
+ };
+
+ mesh->num_vertices = NUM_ARRAY_ELEMENTS(positions);
+ mesh->num_indices = 0;
+
+ glGenVertexArrays(1, &mesh->vao);
+ glGenBuffers(1, &mesh->vbo);
+ mesh->ebo = 0;
+
+ glBindVertexArray(mesh->vao);
+
+ glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
+
+ glEnableVertexAttribArray(0);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0 );
+
+ glBindVertexArray(0);
+ return mesh;
+}
+
+void mesh_purge(Mesh *mesh)
+{
+ if(mesh)
+ {
+ if(mesh->vbo)
+ glDeleteBuffers(1, &mesh->vbo);
+ if(mesh->ebo)
+ glDeleteBuffers(1, &mesh->ebo);
+ if(mesh->vao)
+ glDeleteVertexArrays(1, &mesh->vao);
+
+ memset(mesh, 0, sizeof(Mesh));
+ }
+}
diff --git a/09-september/tomcat/renderer/mesh.h b/09-september/tomcat/renderer/mesh.h
new file mode 100644
index 0000000..711a82e
--- /dev/null
+++ b/09-september/tomcat/renderer/mesh.h
@@ -0,0 +1,22 @@
+#ifndef MESH_H
+#define MESH_H
+
+#include "vertex.h"
+
+typedef struct _Mesh
+{
+ GLuint num_indices;
+ GLuint num_vertices;
+ GLuint vbo, ebo, vao;
+} Mesh;
+
+/** Mesh Factory **/
+extern Mesh *mesh_new(vertex_t* vertices, GLsizeiptr vertexBuffersize,
+ GLushort* indices, GLsizeiptr indexBuffersize);
+
+extern Mesh *mesh_make_skybox(float size);
+extern Mesh *mesh_make_quad();
+
+extern void mesh_purge(Mesh *mesh);
+
+#endif // MESH_H
diff --git a/09-september/tomcat/renderer/model.c b/09-september/tomcat/renderer/model.c
new file mode 100644
index 0000000..3c65ad3
--- /dev/null
+++ b/09-september/tomcat/renderer/model.c
@@ -0,0 +1,314 @@
+#include "model.h"
+#include "renderer.h"
+#include "../util/array.h"
+#include "../util/util.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define MAX_HASH_MODELS 1024
+static Model *model_hash_table[MAX_HASH_MODELS];
+
+static const int BUFFER_SIZE = 128;
+
+static Model *_model_alloc(const char *name)
+{
+ if(strlen(name) > MAX_PATH_LENGTH)
+ Util_FatalError("File following model name is too long: %s", name);
+
+ if(render.num_models >= MAX_MODELS)
+ return NULL;
+
+ Model *model;
+
+ unsigned int hash_ = Util_Hash( name );
+ hash_ %= MAX_HASH_MODELS;
+
+ model = malloc( sizeof(Model) );
+ memset(model, 0, sizeof(Model) );
+
+ render.models[render.num_models] = model;
+ render.num_models += 1;
+
+ strcpy(model->_name, name);
+ model->_hash_next = model_hash_table[hash_];
+ model_hash_table[hash_] = model;
+
+ model->_hash = hash_;
+
+ return model;
+}
+
+Model *model_get(const char *name)
+{
+ Model *model;
+
+ unsigned int hash_ = Util_Hash( name );
+ hash_ %= MAX_HASH_MODELS;
+
+ if(model_hash_table[hash_] != NULL)
+ {
+ for(model = model_hash_table[hash_]; model; model = model->_hash_next)
+ {
+ if( model->_hash == hash_ )
+ return model;
+ }
+ }
+
+ return NULL;
+}
+
+typedef struct
+{
+ vertex_t *data;
+ GLushort *indices;
+
+ Array *positions;
+ Array *textures;
+ Array *normals;
+ Array *tangents;
+
+ unsigned int vertex_count, index_count;
+ unsigned int index_pointer;
+
+ GLubyte hasTextCoords, hasNormals;
+} OBJ_Mesh;
+
+static vertex_t *search_index(vertex_t *pkey, vertex_t *pelem, unsigned int vertex_count)
+{
+ int i;
+ for(i = 0; i < vertex_count; i++)
+ {
+ if(pelem[i].position.x == pkey->position.x &&
+ pelem[i].position.y == pkey->position.y &&
+ pelem[i].position.z == pkey->position.z &&
+ pelem[i].texCoord.x == pkey->texCoord.x &&
+ pelem[i].texCoord.y == pkey->texCoord.y &&
+ pelem[i].normal.x == pkey->normal.x &&
+ pelem[i].normal.y == pkey->normal.y &&
+ pelem[i].normal.z == pkey->normal.z)
+ {
+ return &pelem[i];
+ }
+ }
+ return NULL;
+}
+
+static void calculate_tangents(OBJ_Mesh *mesh)
+{
+ int i;
+ for(i = 0; i < mesh->index_count; i += 3)
+ {
+ int i0 = i;
+ int i1 = i + 1;
+ int i2 = i + 2;
+
+ Vec3 *v0 = ( (Vec3 *)mesh->positions->data) + i0;
+ Vec3 *v1 = ( (Vec3 *)mesh->positions->data) + i1;
+ Vec3 *v2 = ( (Vec3 *)mesh->positions->data) + i2;
+
+ Vec2 *uv0 = ( (Vec2 *)mesh->textures->data) + i0;
+ Vec2 *uv1 = ( (Vec2 *)mesh->textures->data) + i1;
+ Vec2 *uv2 = ( (Vec2 *)mesh->textures->data) + i2;
+
+ Vec3 deltaPos1 = vec3_sub(v1, v0);
+ Vec3 deltaPos2 = vec3_sub(v2, v0);
+
+ Vec2 deltaUV1 = vec2_sub(uv1, uv0);
+ Vec2 deltaUV2 = vec2_sub(uv2, uv0);
+
+ GLfloat r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
+
+ deltaPos1 = vec3_scalar_mul(&deltaPos1, deltaUV2.y);
+ deltaPos2 = vec3_scalar_mul(&deltaPos2, deltaUV1.y);
+
+ Vec3 tangent = vec3_sub(&deltaPos1, &deltaPos2);
+ tangent = vec3_scalar_mul(&tangent, r);
+
+ ( (Vec3 *)mesh->tangents->data)[i2] = tangent;
+ ( (Vec3 *)mesh->tangents->data)[i1] = tangent;
+ ( (Vec3 *)mesh->tangents->data)[i0] = tangent;
+ }
+ /*
+ We normalize the tangents at the end of the parse_obj_index loop
+ for(i = 0; i < mesh.index_count; i++)
+ {
+ mesh.tangents[i] = vec3_normalize(&mesh.tangents[i]);
+ }
+ */
+}
+
+static void parse_obj_index(OBJ_Mesh *mesh, vertex_t *current_vertex)
+{
+ vertex_t *indexOnArray = search_index(current_vertex, mesh->data, mesh->vertex_count);
+
+ /* We check if the vertex was already loaded, so the index points to the created vertex instead of repeating data*/
+ if(indexOnArray == NULL)
+ {
+ mesh->data = (vertex_t*) realloc( mesh->data, sizeof(vertex_t) * (++mesh->vertex_count) );
+
+ /* We make the index point to the last vertex added */
+ mesh->indices[mesh->index_pointer] = mesh->vertex_count - 1;
+
+ mesh->data[mesh->vertex_count - 1] = *current_vertex;
+ }
+ else
+ {
+ GLushort index = (GLushort)(indexOnArray - mesh->data);
+ mesh->data[index].tangent = vec3_add( &mesh->data[index].tangent,
+ &current_vertex->tangent );
+
+ /* We make the index point to the previus vertex added instead of creating a new one */
+ mesh->indices[mesh->index_pointer] = index;
+ }
+
+ mesh->index_pointer += 1;
+}
+
+Model *model_obj_new(const char *path)
+{
+ Model *model;
+ model = model_get(path);
+
+ if(model)
+ return model;
+
+ OBJ_Mesh obj_mesh;
+ memset( &obj_mesh, 0, sizeof(OBJ_Mesh) );
+
+ obj_mesh.positions = array_create( sizeof(Vec3) );
+ obj_mesh.normals = array_create( sizeof(Vec3) );
+ obj_mesh.textures = array_create( sizeof(Vec2) );
+
+ Array *positions = array_create( sizeof(Vec3) );
+ Array *normals = array_create( sizeof(Vec3) );
+ Array *textures = array_create( sizeof(Vec2) );
+
+ vertex_t current_vertex;
+
+ Vec3 current_position;
+ Vec3 current_normal;
+ Vec2 current_texture;
+
+ int count = 0, i;
+ int texture[3], normal[3], verts[3];
+
+ FILE *file = fopen(path, "r");
+ if(file == NULL)
+ Util_FatalError("%s file could not be loaded!", path);
+
+ char buffer[BUFFER_SIZE];
+ while( !feof(file) )
+ {
+ fgets(buffer, BUFFER_SIZE, file);
+ switch(buffer[0])
+ {
+ case 'v':
+ if(buffer[1] == 't')
+ {
+ count = sscanf(buffer, "vt %f %f\n", &current_texture.x, &current_texture.y);
+
+ array_append(textures, &current_texture);
+
+ if(count != 2)
+ Util_FatalError("Bad texture coordinates on .obj file");
+ }
+ else if(buffer[1] == 'n')
+ {
+ count = sscanf(buffer, "vn %f %f %f\n", &current_normal.x,
+ &current_normal.y,
+ &current_normal.z);
+ array_append(normals, &current_normal);
+
+ if(count != 3)
+ Util_FatalError("Bad normals data on .obj file");
+ }
+ else
+ {
+ count = sscanf(buffer, "v %f %f %f\n", &current_position.x,
+ &current_position.y,
+ &current_position.z);
+
+ array_append(positions, &current_position);
+
+ if(count != 3)
+ Util_FatalError("Bad vertices data on .obj file");
+ }
+ break;
+ case 'f':
+ obj_mesh.index_count += 3;
+
+ count = sscanf(buffer, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
+ &verts[0], &texture[0], &normal[0],
+ &verts[1], &texture[1], &normal[1],
+ &verts[2], &texture[2], &normal[2]);
+
+ if(count != 9)
+ Util_FatalError("Bad face data on .obj file");
+
+ array_append(obj_mesh.positions, &( (Vec3 *)positions->data )[ verts[0] -1 ] );
+ array_append(obj_mesh.positions, &( (Vec3 *)positions->data )[ verts[1] -1 ] );
+ array_append(obj_mesh.positions, &( (Vec3 *)positions->data )[ verts[2] -1 ] );
+
+ array_append(obj_mesh.normals, &( (Vec3 *)normals->data )[ normal[0] - 1 ] );
+ array_append(obj_mesh.normals, &( (Vec3 *)normals->data )[ normal[1] - 1 ] );
+ array_append(obj_mesh.normals, &( (Vec3 *)normals->data )[ normal[2] - 1 ] );
+
+ array_append(obj_mesh.textures, &( (Vec2 *)textures->data )[ texture[0] - 1 ] );
+ array_append(obj_mesh.textures, &( (Vec2 *)textures->data )[ texture[1] - 1 ] );
+ array_append(obj_mesh.textures, &( (Vec2 *)textures->data )[ texture[2] - 1 ] );
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ obj_mesh.indices = malloc( obj_mesh.index_count * sizeof(GLushort) );
+ obj_mesh.tangents = array_create_by_size( sizeof(Vec3), obj_mesh.index_count );
+ calculate_tangents(&obj_mesh);
+
+ for(i = 0; i < obj_mesh.index_count; i++)
+ {
+ current_vertex.position = ( (Vec3 *)obj_mesh.positions->data )[i];
+ current_vertex.texCoord = ( (Vec2 *)obj_mesh.textures->data )[i];
+ current_vertex.normal = ( (Vec3 *)obj_mesh.normals->data )[i];
+ current_vertex.tangent = ( (Vec3 *)obj_mesh.tangents->data )[i];
+
+ parse_obj_index(&obj_mesh, &current_vertex);
+ }
+
+ for(i = 0; i < obj_mesh.vertex_count; i++)
+ {
+ obj_mesh.data[i].tangent = vec3_normalize(&obj_mesh.data[i].tangent);
+ }
+
+ array_free(obj_mesh.positions);
+ array_free(obj_mesh.normals);
+ array_free(obj_mesh.textures);
+ array_free(obj_mesh.tangents);
+
+ array_free(positions);
+ array_free(textures);
+ array_free(normals);
+
+ fclose(file);
+
+ GLsizeiptr vertexBuffersize = obj_mesh.vertex_count * sizeof(vertex_t);
+ GLsizeiptr indexBuffersize = obj_mesh.index_count * sizeof(GLushort);
+
+ model = _model_alloc(path);
+
+ model->mesh = mesh_new(obj_mesh.data, vertexBuffersize, obj_mesh.indices, indexBuffersize);
+
+ free(obj_mesh.data);
+ free(obj_mesh.indices);
+
+ return model;
+}
+
+void model_purge(Model *model)
+{
+ mesh_purge(model->mesh);
+}
diff --git a/09-september/tomcat/renderer/model.h b/09-september/tomcat/renderer/model.h
new file mode 100644
index 0000000..6b514dd
--- /dev/null
+++ b/09-september/tomcat/renderer/model.h
@@ -0,0 +1,21 @@
+#ifndef MODEL_H
+#define MODEL_H
+
+#include "../shared.h"
+#include "mesh.h"
+
+typedef struct _Model
+{
+ Mesh *mesh;
+ /* Material? */
+
+ char _name[MAX_PATH_LENGTH];
+ struct _Model *_hash_next;
+ unsigned int _hash;
+} Model;
+
+extern Model *model_obj_new(const char *path);
+extern Model *model_get(const char *name);
+extern void model_purge(Model *model);
+
+#endif // MODEL_H
diff --git a/09-september/tomcat/renderer/renderer.c b/09-september/tomcat/renderer/renderer.c
new file mode 100644
index 0000000..307c543
--- /dev/null
+++ b/09-september/tomcat/renderer/renderer.c
@@ -0,0 +1,430 @@
+#include "renderer.h"
+#include "../util/util_time.h"
+#include "../util/util.h"
+
+#include <string.h>
+
+#define MAX_LIGHTS 4
+
+Renderer render;
+
+void Render_Init(Window *window)
+{
+ memset( &render, 0, sizeof(Renderer) );
+
+ if(glewInit() != GLEW_OK)
+ Util_FatalError("Glew could no be started!");
+
+ strcpy( render.info.version, (const char *) glGetString(GL_VERSION) );
+ strcpy( render.info.vendor, (const char *) glGetString(GL_VENDOR) );
+ strcpy( render.info.shading_version, (const char *) glGetString(GL_SHADING_LANGUAGE_VERSION) );
+
+ fprintf(stderr, "%s\n", render.info.version);
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ glClearColor(0.0f, 0.0f, 0.39f, 1.0f);
+
+ //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+
+ render.default_quad = mesh_make_quad();
+
+ render.default_shader = shader_new("pass_shader", "resources/shaders/passShader.vert",
+ "resources/shaders/passShader.frag");
+ render.default_shader->totalTransform = shader_get_uniform_location(render.default_shader, "M_MVP");
+ render.default_shader->Texture = shader_get_uniform_location(render.default_shader, "Texture");
+
+
+ glBindVertexArray(render.default_quad->vao);
+ glCreateBuffers(1, &render.instance_vbo);
+
+ glBindBuffer(GL_ARRAY_BUFFER, render.instance_vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 18 * 10000, NULL, GL_STREAM_DRAW);
+
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 18 * sizeof(GLfloat), (const void *)(sizeof(float) * 0));
+ glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 18 * sizeof(GLfloat), (const void *)(sizeof(float) * 2) );
+ glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 18 * sizeof(GLfloat), (const void *)(sizeof(float) * 6) );
+ glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 18 * sizeof(GLfloat), (const void *)(sizeof(float) * 10) );
+ glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 18 * sizeof(GLfloat), (const void *)(sizeof(float) * 14) );
+
+ glEnableVertexAttribArray(1);
+ glEnableVertexAttribArray(2);
+ glEnableVertexAttribArray(3);
+ glEnableVertexAttribArray(4);
+ glEnableVertexAttribArray(5);
+
+ glVertexAttribDivisor(1, 1);
+ glVertexAttribDivisor(2, 1);
+ glVertexAttribDivisor(3, 1);
+ glVertexAttribDivisor(4, 1);
+ glVertexAttribDivisor(5, 1);
+
+ glBindVertexArray(0);
+
+ render.window = window;
+ render.shadow_width = 1024;
+ render.shadow_height = 1024;
+
+ /** TEMP **/
+
+ Vec3 center = {0.0f, 0.0f, 0.0f};
+ Vec3 up = {0.0f, 1.0f, 0.0f};
+ render.inv_light_dir = (Vec3){11.54f, 66.74f, 9.93f};
+ Mat4 shadow_ortho_mat = mat4_orthographic(-100.0f, 100.0f, -100.0f, 100.0f, -100.0f, 200.0f);
+ Mat4 shadow_view_mat = mat4_lookAt(&render.inv_light_dir, &center, &up);
+ Mat4 shadow_model;
+ mat4_identity(&shadow_model);
+
+ render.shadow_mvp = mat4_mul(&shadow_ortho_mat, &shadow_view_mat);
+ render.shadow_mvp = mat4_mul(&render.shadow_mvp, &shadow_model);
+
+ render.shadow_shader = shader_new("s_shader", "resources/shaders/shadow_shader.vert",
+ "resources/shaders/shadow_shader.frag");
+ render.shadow_shader->totalTransform = shader_get_uniform_location(render.shadow_shader, "M_MVP");
+
+ /** TEMP **/
+
+ glGenFramebuffers(1, &render.shadow_fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, render.shadow_fbo);
+
+
+ glGenTextures(1, &render.shadow_map);
+ glBindTexture(GL_TEXTURE_2D, render.shadow_map);
+
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, render.shadow_width,
+ render.shadow_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glGenTextures(1, &render.shadow_color);
+ glBindTexture(GL_TEXTURE_2D, render.shadow_color);
+
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, render.shadow_width,
+ render.shadow_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);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, render.shadow_map, 0);
+ glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, render.shadow_color, 0);
+
+ //glDrawBuffer(GL_NONE);
+ GLenum draw_buffer = GL_COLOR_ATTACHMENT0;
+ glDrawBuffers(1, &draw_buffer);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ Util_FatalError("Could not create a frame buffer object\n");
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glViewport(0, 0, render.window->Width, render.window->Height);
+
+ Util_CheckGLError();
+}
+
+void Render_BeginFrame()
+{
+ glClearColor(0.0f, 0.0f, 0.39f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+
+ glBindFramebuffer(GL_FRAMEBUFFER, render.shadow_fbo);
+ glViewport(0, 0, render.shadow_width, render.shadow_height);
+ glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+}
+
+void Render_LoadLights(Shader *s, const Light *lights, int n)
+{
+ Vec3 light_positions[MAX_LIGHTS];
+ Vec4 light_colors[MAX_LIGHTS];
+ Vec3 attenuation[MAX_LIGHTS];
+ int i;
+
+ /* Default light in case we are not given enough lights (n < 4) */
+ static const Light defaultLight =
+ {
+ { 0.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f }
+ };
+
+ for(i = 0; i < MAX_LIGHTS; i++)
+ {
+ if(i < n)
+ {
+ light_positions[i] = lights[i].position;
+ light_colors[i] = lights[i].color;
+ attenuation[i] = lights[i].attenuation;
+ }
+ else
+ {
+ light_positions[i] = defaultLight.position;
+ light_colors[i] = defaultLight.color;
+ attenuation[i] = defaultLight.attenuation;
+ }
+
+ }
+
+ glUniform3fv(s->lightPosition, MAX_LIGHTS, (float *)light_positions);
+ glUniform4fv(s->lightColor, MAX_LIGHTS, (float *)light_colors);
+ glUniform3fv(s->lightAttenuation, MAX_LIGHTS, (float *)attenuation);
+}
+
+/******************************************************************************
+* *
+* Function Name: Render_DrawEntity *
+* *
+* Specific shader layout *
+* -> extra0 texture atlas number of rows *
+* -> extra1 texture atlas xy offset *
+* *
+*******************************************************************************/
+
+void Render_DrawEntity(Shader *s, Mat4 *projectedViewMatrix, Entity *entity)
+{
+ Mesh *mesh = entity->model->mesh;
+ /*We need the model to world matrix in our shader in order to rotate the normals*/
+ Mat4 modelTransform = Entity_GetModelTransform(entity);
+ Mat4 totalMatrix;
+
+ /** TEMP **/
+
+ /** TEMP**/
+
+ glUniformMatrix4fv(s->modelToWorld, 1, GL_FALSE, modelTransform.data);
+
+ totalMatrix = mat4_mul(projectedViewMatrix, &modelTransform);
+ glUniformMatrix4fv(s->totalTransform, 1, GL_FALSE, totalMatrix.data);
+
+ glUniform1i(s->Texture, 0);
+ texture_bind(entity->texture, 0);
+
+ glUniform1f(s->extra0, entity->texture->number_of_rows);
+ Vec2 tex_offset = Entity_GetTexOffset(entity);
+ glUniform2fv(s->extra1, 1, (float *)&tex_offset);
+
+ glBindVertexArray(mesh->vao);
+ glDrawElements(GL_TRIANGLES, mesh->num_indices, GL_UNSIGNED_SHORT, NULL);
+ glBindVertexArray(0);
+}
+
+/******************************************************************************
+* *
+* Function Name: Render_DrawTerrain *
+* *
+* Specific shader layout *
+* -> extra0 Texture_Background *
+* -> extra1 Texture_R *
+* -> extra2 Texture_G *
+* -> extra3 Texture_B *
+* -> extra4 Texture_BlendMap *
+* *
+*******************************************************************************/
+
+void Render_DrawTerrain(Shader *s, Mat4 *projectedViewMatrix, Terrain *terrain)
+{
+ Mat4 totalMatrix;
+ Mat4 modelTransform;
+
+ /* We need the model to world matrix in our shader in order to rotate the normals */
+ modelTransform = mat4_translate(&terrain->position);
+ glUniformMatrix4fv(s->modelToWorld, 1, GL_FALSE, modelTransform.data);
+
+ totalMatrix = mat4_mul(projectedViewMatrix, &modelTransform);
+ glUniformMatrix4fv(s->totalTransform, 1, GL_FALSE, totalMatrix.data);
+
+ /** Set textures for the terrain **/
+
+ glUniform1i(s->extra0, 0);
+ glUniform1i(s->extra1, 1);
+ glUniform1i(s->extra2, 2);
+ glUniform1i(s->extra3, 3);
+ glUniform1i(s->extra4, 4);
+
+ texture_bind(terrain->textures.texture[0], 0);
+ texture_bind(terrain->textures.texture[1], 1);
+ texture_bind(terrain->textures.texture[2], 2);
+ texture_bind(terrain->textures.texture[3], 3);
+ texture_bind(terrain->blendmap, 4);
+
+ /**********************************/
+
+ glBindVertexArray(terrain->mesh->vao);
+ glDrawElements(GL_TRIANGLES, terrain->mesh->num_indices, GL_UNSIGNED_SHORT, NULL);
+ glBindVertexArray(0);
+}
+
+void Render_DrawSky(Shader *s, Mat4 *viewMatrix, Mat4 *projectionMatrix, Skybox *sky)
+{
+ Mat4 myViewMatrix = *viewMatrix;
+ Mat4 totalTransform;
+ Mat4 rotateMatrix = mat4_rotate_y(sky->rotation);
+
+ sky->rotation += SKYBOX_ROTATION_SPEED * Time_GetFrameTime();
+
+ /* We don't want to move the skybox around (We want it to stay with the camera in the midle),
+ just rotate it with the camera so we remove the translations components of the matrix */
+
+ myViewMatrix.data[0 + 3 * 4] = 0.0f;
+ myViewMatrix.data[1 + 3 * 4] = 0.0f;
+ myViewMatrix.data[2 + 3 * 4] = 0.0f;
+
+ myViewMatrix = mat4_mul(&myViewMatrix, &rotateMatrix);
+ totalTransform = mat4_mul(projectionMatrix, &myViewMatrix);
+
+ glUniformMatrix4fv(s->totalTransform, 1, GL_FALSE, totalTransform.data );
+
+ texture_bind(sky->texture, 0);
+ glUniform1i(s->Texture, 0);
+
+ glBindVertexArray(sky->cube->vao);
+ glDrawArrays(GL_TRIANGLES, 0, 36);
+ glBindVertexArray(0);
+}
+
+/******************************************************************************
+* *
+* Function Name: Render_DrawParticles *
+* *
+* Specific shader layout *
+* -> extra0 texture atlas number of rows *
+* -> extra1 texture atlas xy offset *
+* *
+*******************************************************************************/
+
+void Render_DrawParticles(Shader *s, Mat4 *viewMatrix, Mat4 *projectionMatrix)
+{
+ Mat4 modelTransform, modelViewMatrix, totalTransform, scale;
+ Particle *c = NULL;
+ ParticleSystem *sys = NULL;
+
+ int num_particles = 0;
+ int i, j;
+
+
+ glEnable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+ glUniform1i(s->Texture, 0);
+
+ for(i = 0; i < particles.num_systems; i++)
+ {
+ sys = particles.systems[i];
+
+ texture_bind(sys->texture, 0);
+ glUniform1f(s->extra0, sys->texture->number_of_rows);
+
+ /* Orphane the buffer */
+ glBindBuffer(GL_ARRAY_BUFFER, render.instance_vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 18 * MAX_PARTICLES_PER_SYSTEM, NULL, GL_STREAM_DRAW);
+
+ if(sys->additive)
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ else
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ for(j = 0; j < sys->num_particles; j++)
+ {
+ c = &sys->particles[j];
+
+ modelTransform = mat4_translate(&c->position);
+ scale = mat4_scale(c->scale, c->scale, c->scale);
+ modelTransform = mat4_mul(&modelTransform, &scale);
+ /*
+ We eliminate the rotation from the view matrix so the particle is always facing the camera,
+ for this we add the transposed matrix from the rotation part of the viewmatrx and put it
+ on the model transform matrix
+ */
+ modelTransform.data[0] = viewMatrix->data[0];
+ modelTransform.data[4] = viewMatrix->data[1];
+ modelTransform.data[8] = viewMatrix->data[2];
+ modelTransform.data[1] = viewMatrix->data[4];
+ modelTransform.data[5] = viewMatrix->data[5];
+ modelTransform.data[9] = viewMatrix->data[6];
+ modelTransform.data[2] = viewMatrix->data[8];
+ modelTransform.data[6] = viewMatrix->data[9];
+ modelTransform.data[10] = viewMatrix->data[10];
+
+ modelViewMatrix = mat4_mul(viewMatrix, &modelTransform);
+ totalTransform = mat4_mul(projectionMatrix, &modelViewMatrix);
+
+ /* Load the texture atlas coods and MVP matrix */
+ glBufferSubData(GL_ARRAY_BUFFER, num_particles * sizeof(GLfloat), sizeof(Vec2), &c->tex_offset);
+ glBufferSubData(GL_ARRAY_BUFFER, (num_particles + 2) * sizeof(GLfloat), sizeof(Mat4), totalTransform.data);
+
+ num_particles += 18;
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glBindVertexArray(render.default_quad->vao);
+ glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, num_particles / 18);
+ }
+
+ glBindVertexArray(0);
+
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+}
+
+void Render_EndFrame()
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glViewport(0, 0, render.window->Width, render.window->Height);
+}
+
+void Render_Shutdown()
+{
+ int i;
+
+ glDeleteTextures(1, &render.shadow_map);
+ glDeleteFramebuffers(1, &render.shadow_fbo);
+
+ glDeleteBuffers(1, &render.instance_vbo);
+
+ /* Destroy all created textures */
+ for(i = 0; i < render.num_textures; i++)
+ {
+ texture_purge( render.textures[i] );
+ free(render.textures[i]);
+ }
+
+ /* Destroy all created models */
+ for(i = 0; i < render.num_models; i++)
+ {
+ model_purge(render.models[i]);
+ free(render.models[i]);
+ }
+
+ for(i = 0; i < render.num_meshes; i++)
+ {
+ mesh_purge(render.meshes[i]);
+ free(render.meshes[i]);
+ }
+
+ /* Destroy all created shaders */
+ for(i = 0; i < render.num_shaders; i++)
+ {
+ shader_purge(render.shaders[i]);
+ free(render.shaders[i]);
+ }
+
+ /* Just for checking */
+ printf("%d textures were deleted!\n", render.num_textures);
+ printf("%d models were deleted!\n", render.num_models);
+ printf("%d shaders were deleted!\n", render.num_shaders);
+ printf("%d meshes were deleted!\n", render.num_meshes);
+
+ /* Clear everything */
+ memset( &render, 0, sizeof(Renderer) );
+}
diff --git a/09-september/tomcat/renderer/renderer.h b/09-september/tomcat/renderer/renderer.h
new file mode 100644
index 0000000..791d65d
--- /dev/null
+++ b/09-september/tomcat/renderer/renderer.h
@@ -0,0 +1,86 @@
+#ifndef RENDERER_H
+#define RENDERER_H
+
+#include "../terrain.h"
+#include "../util/array.h"
+#include "vertex.h"
+#include "../particles/particles.h"
+#include "renderer_types.h"
+#include "stdint.h"
+#include "window.h"
+
+#define MAX_STRING_LENGTH 128
+
+#define MAX_TEXTURES 2048
+#define MAX_MODELS 2048
+#define MAX_MESHES 2048
+#define MAX_SHADERS 32
+
+/** Beginning of rendering functions **/
+
+typedef struct _GlInfo
+{
+ char version[MAX_STRING_LENGTH];
+ char shading_version[MAX_STRING_LENGTH];
+ char vendor[MAX_STRING_LENGTH];
+} GlInfo;
+
+typedef struct _Renderer
+{
+ GlInfo info;
+
+ Mesh *default_quad;
+ Shader *default_shader;
+ GLuint instance_vbo;
+
+ Texture *textures[MAX_TEXTURES]; /* All loaded textures */
+ int num_textures;
+
+ Shader *shaders[MAX_SHADERS]; /* All loaded shaders */
+ int num_shaders;
+
+ Model *models[MAX_MODELS]; /* All created shapes */
+ int num_models;
+
+ Mesh *meshes[MAX_MESHES];
+ int num_meshes;
+
+ Window *window;
+
+ GLuint shadow_fbo; /* Frame buffer for shadow mapping */
+ GLuint shadow_map; /* Texture for rendering the shadow map (shadow mapping) */
+ GLuint shadow_color;
+ GLint shadow_width;
+ GLint shadow_height;
+
+ /** TEMP **/
+ Mat4 shadow_mvp;
+ Vec3 inv_light_dir;
+ Shader *shadow_shader;
+
+ /** TEMP **/
+} Renderer;
+extern Renderer render;
+
+extern void Render_Init(Window *window);
+
+extern void Render_BeginFrame();
+
+/* Load lights into the current shader program */
+extern void Render_LoadLights(Shader *shader, const Light *lights, int n);
+
+extern void Render_DrawEntity(Shader *shader, Mat4 *projectedViewMatrix, Entity *entity);
+extern void Render_DrawTerrain(Shader *shader, Mat4 *projectedViewMatrix, Terrain *terrain);
+extern void Render_DrawSky(Shader *shader, Mat4 *viewMatrix, Mat4 *projectionMatrix, Skybox *sky);
+extern void Render_DrawParticles(Shader *shader, Mat4 *viewMatrix, Mat4 *projectionMatrix);
+
+extern void Render_EndFrame();
+
+extern void Render_Shutdown();
+
+extern Mat4 Entity_GetModelTransform(Entity* entity);
+extern Vec2 Entity_GetTexOffset(Entity *entity);
+
+/** End of rendering functions **/
+
+#endif // RENDERER_H
diff --git a/09-september/tomcat/renderer/renderer_types.h b/09-september/tomcat/renderer/renderer_types.h
new file mode 100644
index 0000000..cd07a20
--- /dev/null
+++ b/09-september/tomcat/renderer/renderer_types.h
@@ -0,0 +1,47 @@
+#ifndef RENDERER_TYPES_H
+#define RENDERER_TYPES_H
+
+#define SKYBOX_ROTATION_SPEED 1.0f
+
+#include "../util/str.h"
+#include "../util/array.h"
+
+#include "../math/vector.h"
+#include "../math/matrix4x4.h"
+
+#include "shader.h"
+#include "texture.h"
+#include "model.h"
+
+typedef struct _Material
+{
+ Texture *normal_map;
+ GLfloat shine_damper;
+ GLfloat reflectivity;
+} Material;
+
+typedef struct
+{
+ Texture *texture;
+ Mesh *cube;
+ GLfloat rotation;
+} Skybox;
+
+typedef struct _Entity
+{
+ Model *model;
+ Texture *texture;
+ Vec3 position;
+ float rotX, rotY, rotZ;
+ int index; /* Index inside a texture atlas should default to 0 */
+ float scale[3];
+} Entity;
+
+typedef struct
+{
+ Vec3 position;
+ Vec4 color;
+ Vec3 attenuation;
+} Light;
+
+#endif // RENDERER_TYPES_H
diff --git a/09-september/tomcat/renderer/shader.c b/09-september/tomcat/renderer/shader.c
new file mode 100644
index 0000000..3f6a5d7
--- /dev/null
+++ b/09-september/tomcat/renderer/shader.c
@@ -0,0 +1,187 @@
+#include "renderer.h"
+#include "../util/util.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_HASH_SHADER 16
+static Shader *shader_hash_table[MAX_HASH_SHADER];
+
+static void CompileShader(const char *source, GLuint shaderID)
+{
+ glShaderSource(shaderID, 1, &source, 0);
+ glCompileShader(shaderID);
+ GLint error;
+ glGetShaderiv(shaderID, GL_COMPILE_STATUS, &error);
+ if(error != GL_TRUE)
+ {
+ GLint logLenth;
+ glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &logLenth);
+ GLchar buffer[logLenth];
+ glGetShaderInfoLog(shaderID, logLenth, &logLenth, buffer);
+ glDeleteShader(shaderID);
+ Util_FatalError("Some shader failed to compile:\n%s", buffer);
+ }
+}
+
+Shader *shader_new(const char *name, const char *vertexShaderPath, const char *fragShaderPath)
+{
+ if(strlen(name) >= MAX_PATH_LENGTH)
+ Util_FatalError("File following shader name is too long: %s", name);
+
+ Shader *s;
+ s = shader_get(name);
+ if(s != NULL)
+ return s;
+
+ char *vertexShaderSource = Util_LoadFile(vertexShaderPath);
+ char *fragmentShaderSource = Util_LoadFile(fragShaderPath);
+
+ GLuint vs = 0, fs = 0, program;
+ vs = glCreateShader(GL_VERTEX_SHADER);
+ fs = glCreateShader(GL_FRAGMENT_SHADER);
+
+ if(vs == 0 || fs == 0)
+ Util_FatalError("Shaders could not be created\n");
+
+ program = glCreateProgram();
+
+ CompileShader(vertexShaderSource, vs);
+ CompileShader(fragmentShaderSource, fs);
+
+ glAttachShader(program, vs);
+ glAttachShader(program, fs);
+
+ glLinkProgram(program);
+
+ GLint error;
+ glGetProgramiv(program, GL_LINK_STATUS, &error);
+
+ if(error != GL_TRUE)
+ {
+ GLint logLength;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
+
+ GLchar buffer[logLength];
+ glGetProgramInfoLog(program, logLength, &logLength, buffer);
+
+ glDeleteProgram(program);
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+
+ Util_FatalError("Shader program failed to link!:\n%s", buffer);
+ }
+ /** Free some usless resources **/
+ glDetachShader(program, vs);
+ glDetachShader(program, fs);
+ glDeleteShader(vs);
+ glDeleteShader(fs);
+
+ free(vertexShaderSource);
+ free(fragmentShaderSource);
+
+ /** Alloc the new texture **/
+ s = malloc( sizeof(Shader) );
+ memset(s, 0, sizeof(Shader) );
+ s->id = program;
+
+ /** Register inside the resource manager **/
+ unsigned int hash_ = Util_Hash( name );
+ hash_ %= MAX_HASH_SHADER;
+
+ render.shaders[render.num_shaders] = s;
+ render.num_shaders += 1;
+
+ strcpy(s->name, name);
+ s->_hash_next = shader_hash_table[hash_];
+ shader_hash_table[hash_] = s;
+
+ s->_hash = hash_;
+
+ /** Return the final result **/
+ return s;
+}
+
+Shader *shader_get(const char *name)
+{
+ Shader *s;
+
+ unsigned int hash_ = Util_Hash( name );
+ hash_ %= MAX_HASH_SHADER;
+
+ if(shader_hash_table[hash_] != NULL)
+ {
+ for(s = shader_hash_table[hash_]; s; s = s->_hash_next)
+ {
+ if( s->_hash == hash_ )
+ return s;
+ }
+ }
+ return NULL;
+}
+
+void shader_purge(Shader *shader)
+{
+ /** Purge the opengl data **/
+ if(shader->id != 0)
+ {
+ glUseProgram(0);
+ glDeleteProgram(shader->id);
+ shader->id = 0;
+ }
+}
+
+GLint shader_get_uniform_location( Shader *s, const char *uniformName )
+{
+ GLint u = glGetUniformLocation(s->id, uniformName);
+ if(u == GL_INVALID_INDEX)
+ Util_FatalError("Uniform \"%s\" could not be found!", uniformName);
+ else
+ return u;
+
+ return 0;
+}
+
+GLint shader_get_attrib_location( Shader *s, const char *attributeName )
+{
+ GLint attrLocation = glGetAttribLocation(s->id, attributeName);
+ if(attrLocation < 0)
+ Util_FatalError("Attribute \"%s\" could not be found!\n", attributeName);
+ return attrLocation;
+}
+
+void shader_set_uniform_mat4( Shader *s, const char *name, const float matrix[16] )
+{
+ GLint location = shader_get_uniform_location(s, name);
+ glUniformMatrix4fv(location, 1, GL_FALSE, matrix);
+}
+
+void shader_set_uniform_float( Shader *s, const char *name, const float val )
+{
+ GLint location = shader_get_uniform_location(s, name);
+ glUniform1f(location, val);
+}
+
+void shader_set_uniform_vec2( Shader *s, const char *name, const float vec[2] )
+{
+ GLint location = shader_get_uniform_location(s, name);
+ glUniform2fv(location, 1, vec);
+}
+
+void shader_set_uniform_vec3( Shader *s, const char *name, const float vec[3] )
+{
+ GLint location = shader_get_uniform_location(s, name);
+ glUniform3fv(location, 1, vec);
+}
+
+void shader_set_uniform_vec4( Shader *s, const char *name, const float vec[4] )
+{
+ GLint location = shader_get_uniform_location(s, name);
+ glUniform4fv(location, 1, vec);
+}
+
+void shader_set_uniform_int( Shader *s, const char *name, const int val )
+{
+ GLint location = shader_get_uniform_location(s, name);
+ glUniform1i(location, val);
+}
diff --git a/09-september/tomcat/renderer/shader.h b/09-september/tomcat/renderer/shader.h
new file mode 100644
index 0000000..e4889f3
--- /dev/null
+++ b/09-september/tomcat/renderer/shader.h
@@ -0,0 +1,59 @@
+#ifndef SHADER_H
+#define SHADER_H
+
+#include "../shared.h"
+
+typedef struct _Shader
+{
+ GLuint id;
+ char name[MAX_PATH_LENGTH];
+
+ /* Layout */
+
+ /* Program Attributes */
+ GLint position;
+ GLint uv;
+ GLint normal;
+ GLint tangent;
+
+ /* Program Uniforms */
+ GLint totalTransform;
+ GLint modelToWorld;
+ GLint lightPosition;
+ GLint ambientLight;
+ GLint lightColor;
+ GLint lightAttenuation;
+ GLint World_eyePosition;
+ GLint Texture;
+ GLint Normal_Map;
+
+ /* Program Multi Purpose Uniforms */
+ GLint extra0;
+ GLint extra1;
+ GLint extra2;
+ GLint extra3;
+ GLint extra4;
+ GLint extra5;
+
+ struct _Shader *_hash_next;
+ unsigned int _hash;
+} Shader;
+
+/** Shaders **/
+extern Shader *shader_new(const char *name, const char *vertexShaderPath, const char *fragShaderPath);
+extern Shader *shader_get(const char *name);
+extern void shader_purge(Shader *shader);
+/** **/
+
+/** Shader functions **/
+extern GLint shader_get_uniform_location( Shader *s, const char *uniformName );
+extern GLint shader_get_attrib_location( Shader *s, const char *attributeName );
+extern void shader_set_uniform_mat4( Shader *s, const char *name, const float matrix[16] );
+extern void shader_set_uniform_float( Shader *s, const char *name, const float val );
+extern void shader_set_uniform_vec2( Shader *s, const char *name, const float vec[2] );
+extern void shader_set_uniform_vec3( Shader *s, const char *name, const float vec[3] );
+extern void shader_set_uniform_vec4( Shader *s, const char *name, const float vec[4] );
+extern void shader_set_uniform_int( Shader *s, const char *name, const int val );
+/** **/
+
+#endif // SHADER_H
diff --git a/09-september/tomcat/renderer/texture.c b/09-september/tomcat/renderer/texture.c
new file mode 100644
index 0000000..e653c5a
--- /dev/null
+++ b/09-september/tomcat/renderer/texture.c
@@ -0,0 +1,188 @@
+#include "../util/util.h"
+#include "renderer.h"
+#include "texture.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+
+#define MAX_HASH_TEXTURES 1024
+static Texture *texture_hash_table[MAX_HASH_TEXTURES];
+
+Texture *texture_new(const char *path)
+{
+ return texture_with_name_new(path, path);
+}
+
+Texture *texture_with_name_new(const char *name, const char *path)
+{
+ if(strlen(name) > MAX_PATH_LENGTH)
+ Util_FatalError("File following texture name is too long: %s", name);
+
+ Texture *tex;
+ tex = texture_get(name);
+
+ if(tex != NULL)
+ return tex;
+
+ if(render.num_textures >= MAX_TEXTURES)
+ return NULL;
+
+ /** Alloc the new texture **/
+ tex = malloc( sizeof(Texture) );
+ memset(tex, 0, sizeof(Texture) );
+ tex->number_of_rows = 1;
+ tex->type = GL_TEXTURE_2D;
+
+ SDL_Surface *data = IMG_Load(path);
+
+ if(data == NULL)
+ Util_FatalError("Texture %s could not be found!\n", path);
+
+ glGenTextures(1, &tex->tex_id);
+ glBindTexture(GL_TEXTURE_2D, tex->tex_id);
+
+ SDL_LockSurface(data);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, data->w, data->h, GL_FALSE, GL_RGBA, GL_UNSIGNED_BYTE, data->pixels);
+ SDL_UnlockSurface(data);
+ SDL_FreeSurface(data);
+
+ /** Configure the texture **/
+ glGenerateMipmap(GL_TEXTURE_2D);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -2.4);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ /** Register inside the resource manager **/
+ unsigned int hash_ = Util_Hash( name );
+ hash_ %= MAX_HASH_TEXTURES;
+
+ render.textures[render.num_textures] = tex;
+ render.num_textures += 1;
+
+ strcpy(tex->_name, name);
+ tex->_hash_next = texture_hash_table[hash_];
+ texture_hash_table[hash_] = tex;
+
+ tex->hash_ = hash_;
+
+ /** Return the final result **/
+ return tex;
+}
+
+Texture *texture_cubemap_new(const char *paths[6])
+{
+ return texture_cubemap_with_name_new(paths[0], paths);
+}
+
+Texture *texture_cubemap_with_name_new(const char *name, const char *paths[6])
+{
+ Texture *tex;
+ tex = texture_get(name);
+
+ if(tex != NULL)
+ {
+ puts("s");
+ return tex;
+ }
+
+ if(render.num_textures >= MAX_TEXTURES)
+ return NULL;
+
+ /** Alloc the new texture **/
+ tex = malloc( sizeof(Texture) );
+ memset(tex, 0, sizeof(Texture) );
+ tex->number_of_rows = 1;
+ tex->type = GL_TEXTURE_CUBE_MAP;
+
+ glGenTextures(1, &tex->tex_id);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, tex->tex_id);
+
+ SDL_Surface *data;
+
+ int i;
+ for(i = 0; i < 6; i++)
+ {
+ data = IMG_Load(paths[i]);
+
+ if(data == NULL)
+ Util_FatalError("Texture %s could not be found!\n", paths[i]);
+
+ SDL_LockSurface(data);
+ /** All the textures sides are linearly stored so we just add "i" **/
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA,
+ data->w, data->h, GL_FALSE, GL_RGBA, GL_UNSIGNED_BYTE, data->pixels);
+
+ SDL_UnlockSurface(data);
+ SDL_FreeSurface(data);
+ }
+
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+
+ /** Register inside the resource manager **/
+ unsigned int hash_ = Util_Hash( name );
+ hash_ %= MAX_HASH_TEXTURES;
+
+ render.textures[render.num_textures] = tex;
+ render.num_textures += 1;
+
+ strcpy(tex->_name, name);
+ tex->_hash_next = texture_hash_table[hash_];
+ texture_hash_table[hash_] = tex;
+
+ tex->hash_ = hash_;
+
+ /** Return the final result **/
+ return tex;
+}
+
+Texture *texture_get(const char *name)
+{
+ Texture *tex;
+
+ unsigned int hash_ = Util_Hash( name );
+ hash_ %= MAX_HASH_TEXTURES;
+
+ if(texture_hash_table[hash_] != NULL)
+ {
+ for(tex = texture_hash_table[hash_]; tex; tex = tex->_hash_next)
+ {
+ if( tex->hash_ == hash_ )
+ return tex;
+ }
+ }
+ return NULL;
+}
+
+void texture_bind(Texture *tex, int slot)
+{
+ glActiveTexture(GL_TEXTURE0 + slot);
+
+ if(tex->type != GL_TEXTURE_2D && tex->type != GL_TEXTURE_CUBE_MAP)
+ return;
+
+
+ glBindTexture(tex->type, tex->tex_id);
+}
+
+void texture_purge(Texture *tex)
+{
+ /** Purge the opengl data **/
+ if(tex->tex_id != 0)
+ {
+ glDeleteTextures(1, &tex->tex_id);
+ tex->tex_id = 0;
+ }
+}
diff --git a/09-september/tomcat/renderer/texture.h b/09-september/tomcat/renderer/texture.h
new file mode 100644
index 0000000..8288c72
--- /dev/null
+++ b/09-september/tomcat/renderer/texture.h
@@ -0,0 +1,30 @@
+#ifndef TEXTURE_H
+#define TEXTURE_H
+
+#include "../shared.h"
+
+typedef struct _Texture
+{
+ GLuint tex_id;
+ GLenum type;
+
+ int number_of_rows; /* used for texture atlases */
+
+ char _name[MAX_PATH_LENGTH];
+ unsigned int hash_;
+ struct _Texture *_hash_next; /* linked list for storing on hash table */
+
+} Texture;
+
+extern Texture *texture_new(const char *name);
+extern Texture *texture_with_name_new(const char *name, const char *path);
+
+extern Texture *texture_cubemap_new(const char *paths[6]);
+extern Texture *texture_cubemap_with_name_new(const char *name, const char *paths[6]);
+
+extern Texture *texture_get(const char *name);
+extern void texture_bind(Texture *tex, int slot);
+
+extern void texture_purge(Texture *tex); /* Clean the texture without freeing the container */
+
+#endif // TEXTURE_H
diff --git a/09-september/tomcat/renderer/vertex.h b/09-september/tomcat/renderer/vertex.h
new file mode 100644
index 0000000..6a79dde
--- /dev/null
+++ b/09-september/tomcat/renderer/vertex.h
@@ -0,0 +1,15 @@
+#ifndef VERTEX_H
+#define VERTEX_H
+
+#include <GL/glew.h>
+#include "../math/vector.h"
+
+typedef struct
+{
+ Vec3 position;
+ Vec2 texCoord;
+ Vec3 normal;
+ Vec3 tangent;
+} vertex_t;
+
+#endif // VERTEX_H
diff --git a/09-september/tomcat/renderer/window.c b/09-september/tomcat/renderer/window.c
new file mode 100644
index 0000000..d5db1eb
--- /dev/null
+++ b/09-september/tomcat/renderer/window.c
@@ -0,0 +1,49 @@
+#include "window.h"
+#include "../util/util.h"
+#include <stdlib.h>
+
+Window* window_new(const char* title, Uint32 width, Uint32 height)
+{
+ Window* window = malloc(sizeof(Window));
+ window->title = title;
+ window->Width = width;
+ window->Height = height;
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+
+ window->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+ width, height, SDL_WINDOW_OPENGL);
+ if(window->window == NULL)
+ Util_FatalError( "The window could not be created:\n%s", SDL_GetError() );
+
+ window->context = SDL_GL_CreateContext(window->window);
+
+ if(window->context == NULL)
+ Util_FatalError( "Context could not be created:\n%s", SDL_GetError() );
+
+ glViewport(0, 0, width, height);
+
+ return window;
+}
+
+void window_resize(Window* window, Uint32 width, Uint32 height)
+{
+ window->Width = width;
+ window->Height = height;
+ SDL_SetWindowSize(window->window, width, height);
+ glViewport(0, 0, width, height);
+}
+
+void window_update(Window* window)
+{
+ SDL_GL_SwapWindow(window->window);
+}
+
+void window_destroy(Window* window)
+{
+ SDL_GL_DeleteContext(window->context);
+ SDL_DestroyWindow(window->window);
+ free(window);
+}
diff --git a/09-september/tomcat/renderer/window.h b/09-september/tomcat/renderer/window.h
new file mode 100644
index 0000000..608937a
--- /dev/null
+++ b/09-september/tomcat/renderer/window.h
@@ -0,0 +1,20 @@
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <SDL2/SDL.h>
+#include <GL/glew.h>
+
+typedef struct
+{
+ SDL_Window* window;
+ SDL_GLContext context;
+ Uint32 Width, Height;
+ const char* title;
+} Window;
+
+extern Window* window_new(const char* title, Uint32 width, Uint32 height);
+extern void window_resize(Window* window, Uint32 width, Uint32 height);
+extern void window_update(Window* window);
+extern void window_destroy(Window* window);
+
+#endif // WINDOW_H
diff --git a/09-september/tomcat/shared.h b/09-september/tomcat/shared.h
new file mode 100644
index 0000000..35fd471
--- /dev/null
+++ b/09-september/tomcat/shared.h
@@ -0,0 +1,13 @@
+#ifndef SHARED_H
+#define SHARED_H
+
+#include "GL/glew.h"
+#define MAX_PATH_LENGTH 64
+
+typedef enum
+{
+ FALSE,
+ TRUE
+} boolean;
+
+#endif // SHARED_H
diff --git a/09-september/tomcat/terrain.c b/09-september/tomcat/terrain.c
new file mode 100644
index 0000000..f23d057
--- /dev/null
+++ b/09-september/tomcat/terrain.c
@@ -0,0 +1,155 @@
+#include "terrain.h"
+#include "math/math_util.h"
+#include "math/vector.h"
+#include "util/util.h"
+#include "renderer/renderer.h"
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+#include <math.h>
+
+#define PLANE_SIZE 128
+#define PLANE_MAX_HEIGHT 10
+
+#define NUM_ARRAY_ELEMENTS(a) sizeof(a) / sizeof(*a)
+
+static float GetHeight(int x, int y, SDL_Surface* surface)
+{
+ if(x < 0 || x >= surface->w || y < 0 || y >= surface->h)
+ return 0.0f;
+
+ Uint32 pixel = ( (Uint32*)surface->pixels )[y * surface->w + x];
+ Uint8 r, g, b;
+ SDL_GetRGB(pixel, surface->format, &r, &g, &b);
+
+ float height = (float)r / 255.0f;
+
+ return height * 40.0f;
+}
+
+static Vec3 GenerateNomal(int x, int y, SDL_Surface* surface)
+{
+ float hLeft = GetHeight(x-1, y, surface);
+ float hRight = GetHeight(x+1, y, surface);
+ float hUp = GetHeight(x, y+1, surface);
+ float hDown = GetHeight(x, y-1, surface);
+
+ Vec3 normal = { hLeft - hRight, 2.0f, hDown - hUp};
+ return vec3_normalize(&normal);
+}
+
+GLfloat Terrain_GetHeightOfTerrain(Terrain *terrain, GLfloat x, GLfloat z)
+{
+ GLfloat terrainX = x - terrain->position.x;
+ GLfloat terrainZ = z - terrain->position.z;
+
+ GLfloat gridSquareSize = (float)terrain->l / ( (float)PLANE_SIZE - 1 );
+
+ GLint gridX = (GLint) floor(terrainX / gridSquareSize);
+ GLint gridZ = (GLint) floor(terrainZ / gridSquareSize);
+
+ if(gridX >= PLANE_SIZE - 1 || gridX < 0 || gridZ >= PLANE_SIZE - 1 || gridZ < 0)
+ {
+ printf("called\n");
+ return 0;
+ }
+
+ GLfloat xCoord = fmod(terrainX, gridSquareSize) / gridSquareSize;
+ GLfloat zCoord = fmod(terrainZ, gridSquareSize) / gridSquareSize;
+ GLfloat answer;
+
+ /* Determine in which triangle of the square are we. "Bary Centric Interpolation"*/
+ if(xCoord <= (1 - zCoord)){
+ /* 0, heights[gridX][gridZ], 0) */
+ Vec3 p1 = { 0, terrain->height[ gridX * PLANE_SIZE + gridZ ], 0 };
+ /* 1, heights[gridX + 1][gridZ], 0) */
+ Vec3 p2 = { 1, terrain->height[ (gridX + 1) * PLANE_SIZE + gridZ ], 0};
+ /* 0, heights[gridX][gridZ + 1], 1) */
+ Vec3 p3 = { 0, terrain->height[ gridX * PLANE_SIZE + (gridZ + 1) ], 1};
+
+ Vec2 pos = {xCoord, zCoord};
+
+ answer = baryCentric(&p1, &p2, &p3, &pos);
+ } else {
+ /* (1, heights[gridX + 1][gridZ], 0) */
+ Vec3 p1 = { 1, terrain->height[ (gridX + 1) * PLANE_SIZE + gridZ ], 0 };
+ /* (1, heights[gridX + 1][gridZ + 1], 1) */
+ Vec3 p2 = { 1, terrain->height[ (gridX + 1) * PLANE_SIZE + (gridZ + 1) ], 1};
+ /* (0, heights[gridX][gridZ + 1], 1) */
+ Vec3 p3 = { 0, terrain->height[ gridX * PLANE_SIZE + (gridZ + 1) ], 1};
+ Vec2 pos = {xCoord, zCoord};
+
+ answer = baryCentric(&p1, &p2, &p3, &pos);
+ }
+
+ return answer;
+}
+
+Terrain *Terrain_Create( int w, int l, const char* heightmap_path, Texture *blendmap, TerrainTexturePack *textures )
+{
+ Terrain *terrain = malloc( sizeof(Terrain) );
+ terrain->height = (GLfloat*) malloc( sizeof(GLfloat) * PLANE_SIZE * PLANE_SIZE);
+
+ terrain->blendmap = blendmap;
+ terrain->w = w; terrain->l = l;
+ terrain->textures = *textures;
+
+ SDL_Surface* surface = IMG_Load(heightmap_path);
+ if(surface == NULL)
+ Util_FatalError("Heightmap file could not be loaded\n");
+
+ vertex_t data[PLANE_SIZE * PLANE_SIZE];
+ int x, y;
+ for(x = 0; x < PLANE_SIZE; x++)
+ {
+ for(y = 0; y < PLANE_SIZE; y++)
+ {
+ vertex_t* v = &data[y + x * PLANE_SIZE];
+ v->position = (Vec3){ (float)x / (float)PLANE_SIZE, 0.0f, (float)y / (float)PLANE_SIZE };
+ /* Heightmap cordinates */
+ int image_x = v->position.x * surface->w, image_y = v->position.z * surface->h;
+
+ v->texCoord = (Vec2){ v->position.x, v->position.z };
+ GLfloat height = GetHeight(image_x, image_y, surface);
+ terrain->height[y + x * PLANE_SIZE] = height;
+ v->position.y = height;
+ v->position.x *= w;
+ v->position.z *= l;
+
+ v->normal = GenerateNomal(image_x, image_y, surface);
+ }
+ }
+
+ int runner = 0;
+ GLushort indices[ (PLANE_SIZE-1) * (PLANE_SIZE-1) * 6 ];
+ for(x = 0; x < PLANE_SIZE-1; x++)
+ {
+ for(y = 0; y < PLANE_SIZE-1; y++)
+ {
+ indices[runner++] = PLANE_SIZE * x + y;
+ indices[runner++] = PLANE_SIZE * x + y + 1;
+ indices[runner++] = PLANE_SIZE * x + y + PLANE_SIZE;
+
+ indices[runner++] = PLANE_SIZE * x + y + 1;
+ indices[runner++] = PLANE_SIZE * x + y + PLANE_SIZE + 1;
+ indices[runner++] = PLANE_SIZE * x + y + PLANE_SIZE;
+ }
+ }
+
+ GLsizeiptr vertexBufferSize = NUM_ARRAY_ELEMENTS(data) * sizeof(vertex_t);
+ GLsizeiptr indexBufferSize = NUM_ARRAY_ELEMENTS(indices) * sizeof(GLushort);
+
+ SDL_FreeSurface(surface);
+
+ terrain->mesh = mesh_new(data, vertexBufferSize, indices, indexBufferSize);
+ return terrain;
+}
+
+void Terrain_Destroy( Terrain *terrain )
+{
+ if(terrain->height)
+ free(terrain->height);
+
+ free(terrain);
+}
+
diff --git a/09-september/tomcat/terrain.h b/09-september/tomcat/terrain.h
new file mode 100644
index 0000000..b321e5f
--- /dev/null
+++ b/09-september/tomcat/terrain.h
@@ -0,0 +1,27 @@
+#ifndef TERRAIN_H
+#define TERRAIN_H
+
+#include "renderer/renderer_types.h"
+#include "math/vector.h"
+
+typedef struct
+{
+ Texture *texture[4];
+} TerrainTexturePack;
+
+typedef struct
+{
+ Mesh *mesh;
+ Texture *blendmap;
+ TerrainTexturePack textures;
+
+ GLfloat *height;
+ int w, l;
+ Vec3 position;
+} Terrain;
+
+extern Terrain *Terrain_Create( int w, int l, const char* heightmap_path, Texture *blendmap, TerrainTexturePack *textures);
+extern GLfloat Terrain_GetHeightOfTerrain(Terrain *terrain, GLfloat x, GLfloat z);
+extern void Terrain_Destroy( Terrain *terrain );
+
+#endif // TERRAIN_H
diff --git a/09-september/tomcat/util/array.c b/09-september/tomcat/util/array.c
new file mode 100644
index 0000000..13660e0
--- /dev/null
+++ b/09-september/tomcat/util/array.c
@@ -0,0 +1,102 @@
+#include "array.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define ARRAY_GROWTH_FACTOR 2
+#define MAX(x, y) ( (x > y) ? x : y )
+
+typedef struct _RealArray
+{
+ char *data;
+ unsigned int length; /* Number of elements */
+
+ unsigned int type_size;
+ unsigned int capacity; /* Array capacity in elements number */
+ bool clear;
+} RealArray;
+
+static void array_check_for_expand(RealArray *arr, unsigned int length);
+
+Array *array_create(unsigned int type_size)
+{
+ return array_create_by_size(type_size, 0);
+}
+
+Array *array_create_by_size(unsigned int type_size, unsigned int reserved_size)
+{
+ RealArray *arr = malloc( sizeof(RealArray) );
+ arr->capacity = 0;
+ arr->data = NULL;
+ arr->length = 0;
+ arr->type_size = type_size;
+
+ if(reserved_size != 0)
+ {
+ array_check_for_expand(arr, reserved_size);
+ }
+
+ return (Array *)arr;
+}
+
+void array_append(Array *arr, void *data)
+{
+ RealArray *r_arr = (RealArray *)arr;
+ array_check_for_expand(r_arr, 1);
+
+ memcpy(r_arr->data + r_arr->length * r_arr->type_size, data, r_arr->type_size);
+ r_arr->length += 1;
+}
+
+void array_insert(Array *arr, int index, void *data)
+{
+ RealArray *r_arr = (RealArray *)arr;
+ array_check_for_expand(r_arr, 1);
+
+ /* Shift everything one place */
+ memmove(r_arr->data + (index + 1) * r_arr->type_size,
+ r_arr->data + index * r_arr->type_size,
+ r_arr->length * r_arr->type_size - index * r_arr->type_size);
+
+ /* Insert the new data */
+ memcpy(r_arr->data + index * r_arr->type_size, data, r_arr->type_size);
+
+ r_arr->length += 1;
+}
+
+void array_remove(Array *arr, int index)
+{
+
+}
+
+void array_reserve(Array *arr, unsigned int length)
+{
+
+}
+
+unsigned int array_get_type_size(Array *arr)
+{
+ RealArray *r_arr = (RealArray *)arr;
+ return r_arr->type_size;
+}
+
+void array_free(Array *arr)
+{
+ RealArray *r_arr = (RealArray *)arr;
+
+ free(r_arr->data);
+ free(r_arr);
+}
+
+static void array_check_for_expand(RealArray *arr, unsigned int length)
+{
+ RealArray *r_arr = (RealArray *)arr;
+
+ unsigned int expected_size = r_arr->length + length;
+
+ if(r_arr->capacity < expected_size)
+ {
+ r_arr->capacity = MAX(ARRAY_GROWTH_FACTOR * r_arr->capacity, expected_size);
+ r_arr->data = realloc(r_arr->data, r_arr->capacity * r_arr->type_size);
+ }
+}
diff --git a/09-september/tomcat/util/array.h b/09-september/tomcat/util/array.h
new file mode 100644
index 0000000..1444a74
--- /dev/null
+++ b/09-september/tomcat/util/array.h
@@ -0,0 +1,23 @@
+#ifndef ARRAY_H
+#define ARRAY_H
+
+#include <stdbool.h>
+
+typedef struct
+{
+ char *data;
+ unsigned int length;
+} Array;
+
+extern Array *array_create(unsigned int type_size);
+extern Array *array_create_by_size(unsigned int type_size, unsigned int reserved_size);
+
+extern void array_append(Array *arr, void *data);
+extern void array_insert(Array *arr, int index, void *data);
+extern void array_remove(Array *arr, int index);
+extern void array_reserve(Array *arr, unsigned int length);
+extern unsigned int array_get_type_size(Array *arr);
+
+extern void array_free(Array *arr);
+
+#endif // ARRAY_H
diff --git a/09-september/tomcat/util/str.c b/09-september/tomcat/util/str.c
new file mode 100644
index 0000000..295b390
--- /dev/null
+++ b/09-september/tomcat/util/str.c
@@ -0,0 +1,121 @@
+#include "str.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/* Note, we add always 1 to length for the \0 character */
+
+static void string_check_for_expand( String *string_, unsigned int length )
+{
+ if(string_->length + length >= string_->allocated_length)
+ {
+ string_->allocated_length = string_->length + length + 1;
+ string_->data = realloc(string_->data, string_->allocated_length * sizeof(char) );
+ }
+}
+
+String *string_create( const char *init )
+{
+ String *str;
+
+ if(init == NULL || *init == '\0')
+ {
+ str = string_create_by_size(1);
+ }
+ else
+ {
+ unsigned int len = strlen(init);
+ str = string_create_by_size(len + 1);
+ string_append(str, init);
+ }
+
+ return str;
+}
+
+String *string_create_by_size( unsigned int reserved_size)
+{
+ String *str = malloc( sizeof(String) );
+
+ str->data = NULL;
+ str->length = 0;
+ str->allocated_length = 0;
+
+ string_check_for_expand(str, reserved_size);
+
+ str->data[0] = 0;
+ return str;
+}
+
+void string_assign( String *string_, const char *val )
+{
+
+}
+
+void string_append( String *string_, const char *val )
+{
+ string_insert(string_, string_->length, val);
+}
+
+void string_append_char( String *string_, char c )
+{
+ string_insert(string_, string_->length, &c);
+}
+
+void string_insert( String *string_, int index, const char *val)
+{
+ if(index > string_->length)
+ return;
+
+ unsigned int length = strlen(val);
+ string_check_for_expand(string_, length);
+
+ if(index == string_->length - 1)
+ {
+ strcpy(string_->data + string_->length, val);
+ }
+ else
+ {
+ memmove(string_->data + index + length,
+ string_->data + index,
+ (string_->length - index) * sizeof(char) );
+
+ memcpy(string_->data + index, val, length * sizeof(char) );
+ }
+
+ string_->length += length;
+ string_->data[string_->length] = '\0';
+}
+
+void string_insert_char( String *string_, int index, char val)
+{
+ string_insert(string_, index, &val);
+}
+
+void string_free( String *string_ )
+{
+ if(string_)
+ {
+ free(string_->data);
+ free(string_);
+ }
+}
+
+unsigned int string_hash( String *string_ )
+{
+ unsigned int hash, i;
+ for(hash = i = 0; i < string_->length; ++i)
+ {
+ hash += string_->data[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+ return hash;
+}
+
+bool string_equal( String *a, String *b )
+{
+ return !strcmp(a->data, b->data);
+}
diff --git a/09-september/tomcat/util/str.h b/09-september/tomcat/util/str.h
new file mode 100644
index 0000000..d8182e1
--- /dev/null
+++ b/09-september/tomcat/util/str.h
@@ -0,0 +1,29 @@
+#ifndef STR_H
+#define STR_H
+
+#include <stdbool.h>
+
+typedef struct _String
+{
+ char *data;
+ unsigned int length;
+ unsigned int allocated_length;
+} String;
+
+extern String *string_create( const char *init );
+extern String *string_create_by_size( unsigned int reserved_size);
+
+extern void string_assign( String *string_, const char *val );
+
+extern void string_append( String *string_, const char *val );
+extern void string_append_char( String *string_, char c );
+
+extern void string_insert( String *string_, int index, const char *val);
+extern void string_insert_char( String *string_, int index, char val);
+
+extern void string_free( String *string_ );
+
+extern unsigned int string_hash( String *string_ );
+extern bool string_equal( String *a, String *b );
+
+#endif // STR_H
diff --git a/09-september/tomcat/util/util.c b/09-september/tomcat/util/util.c
new file mode 100644
index 0000000..22eb0fc
--- /dev/null
+++ b/09-september/tomcat/util/util.c
@@ -0,0 +1,138 @@
+#include "util.h"
+
+#include <SDL2/SDL.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+unsigned int Util_Hash( const char *str )
+{
+ unsigned int hash, i, length;
+ length = strlen(str);
+
+ for(hash = i = 0; i < length; ++i)
+ {
+ hash += str[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ return hash;
+}
+
+void Util_FatalError( const char *fmt, ... )
+{
+ fprintf(stderr, "Fatal Error:\n");
+
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ SDL_Quit();
+ exit(1);
+}
+
+void Util_CheckGLError()
+{
+ GLenum error = glGetError();
+ switch(error)
+ {
+ case GL_INVALID_ENUM:
+ fprintf(stderr, "WARNING: GL_INVALID_ENUM\n");
+ break;
+ case GL_INVALID_VALUE:
+ fprintf(stderr, "WARNING: GL_INVALID_VALUE\n");
+ break;
+ case GL_INVALID_OPERATION:
+ fprintf(stderr, "WARNING: GL_INVALID_OPERATION\n");
+ break;
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ fprintf(stderr, "WARNING: GL_INVALID_FRAMEBUFFER_OPERATION\n");
+ break;
+ case GL_OUT_OF_MEMORY:
+ fprintf(stderr, "WARNING: GL_OUT_OF_MEMORY\n");
+ break;
+ case GL_STACK_UNDERFLOW:
+ fprintf(stderr, "WARNING: GL_STACK_UNDERFLOW\n");
+ break;
+ case GL_STACK_OVERFLOW:
+ fprintf(stderr, "WARNING: GL_STACK_OVERFLOW\n");
+ break;
+
+ default:
+ break;
+ }
+}
+
+char *Util_LoadFile( const char *path )
+{
+ FILE* file = fopen( path, "r" );
+
+ if(file == NULL)
+ {
+ Util_FatalError("File %s could not be found!\n", path);
+ }
+
+ fseek( file, 0, SEEK_END );
+ size_t sizeOfFile = ftell( file );
+ fseek( file, 0, SEEK_SET );
+ char* file_data = malloc( sizeof(char) * sizeOfFile + 1 );
+ fread( file_data, sizeof(char), sizeOfFile, file );
+ file_data[sizeOfFile] = '\0';
+ fclose(file);
+ return file_data;
+}
+
+float Util_RandomF(float min, float max)
+{
+ return ( min + (float)rand() ) / ( (float)RAND_MAX / (max-min) );
+}
+
+int Util_RandomI(int min, int max)
+{
+ return ( rand() % (max-min) ) + min;
+}
+
+Vec3
+Util_GetMouseRay(int screenWidth, int screenHeigth, Mat4 *viewMatrix, Mat4 *projectionMatrix,
+ int mouseX, int mouseY)
+{
+ Vec4 eyeCoords;
+ Vec3 mouseRay;
+ /* Normalized device coords NOTE: -y becouse for SDL y = 0 is the top of the screen */
+ GLfloat normalX = ( 2.0f * (GLfloat)mouseX ) / (GLfloat) screenWidth - 1.0f;
+ GLfloat normalY = 1.0f - (2.0f * (GLfloat)mouseY) / (GLfloat) screenHeigth;
+
+ /* clipCoords include 4th component */
+ Vec4 clipCoords = { normalX, normalY, -1.0f, 1.0f };
+
+ /* Remove perpective */
+ {
+ Mat4 invertedProjection = mat4_inverse(projectionMatrix);
+ eyeCoords = mat4_mul_vec4(&invertedProjection, &clipCoords);
+ eyeCoords.z = -1.0f;
+ eyeCoords.w = 0.0f;
+ }
+
+ /* Remove view matrix*/
+ {
+ Mat4 invertedViewMatrix = mat4_inverse(viewMatrix);
+ Vec4 temp = mat4_mul_vec4(&invertedViewMatrix, &eyeCoords);
+
+ mouseRay.x = temp.x;
+ mouseRay.y = temp.y;
+ mouseRay.z = temp.z;
+
+ mouseRay = vec3_normalize(&mouseRay);
+ }
+
+ /* Return the ray in world coordinates */
+ return mouseRay;
+}
diff --git a/09-september/tomcat/util/util.h b/09-september/tomcat/util/util.h
new file mode 100644
index 0000000..f09f7cb
--- /dev/null
+++ b/09-september/tomcat/util/util.h
@@ -0,0 +1,27 @@
+#ifndef UTIL_H
+#define UTIL_H
+
+#include "../math/matrix4x4.h"
+
+#define toRadians(degrees) (degrees * 3.1415926 / 180.0f)
+#define toDegrees(radians) (radians * 180.0f / 3.1415926)
+
+#ifdef DEBUG
+#include <stdio.h>
+#define myAssert(expr) expr ? 1==1 : fprintf(stderr, "The expresion was not true\n")
+#else
+#define myAssert(expr)
+#endif // DEBUG
+
+extern unsigned int Util_Hash( const char *str );
+extern void Util_FatalError( const char* fmt, ... );
+extern void Util_CheckGLError();
+extern char* Util_LoadFile( const char* path );
+extern float Util_RandomF(float min, float max);
+extern int Util_RandomI(int min, int max);
+
+extern Vec3
+Util_GetMouseRay(int screenWidth, int screenHeigth, Mat4 *viewMatrix, Mat4 *projectionMatrix,
+ int mouseX, int mouseY);
+
+#endif // UTIL_H
diff --git a/09-september/tomcat/util/util_time.c b/09-september/tomcat/util/util_time.c
new file mode 100644
index 0000000..e56d997
--- /dev/null
+++ b/09-september/tomcat/util/util_time.c
@@ -0,0 +1,52 @@
+#include "util_time.h"
+
+static struct
+{
+ float max_ticks_per_frame; //< cuantos ticks (tiempo demora) un frame
+ Uint32 counted_frames; //< cuantos frames han pasado
+ Uint32 start_ticks; //< ticks al iniciar la iteracion del loop
+ Uint32 beg_ticks; //< ticks desde el inicio del juego
+ Uint32 time_per_frame;
+} TIME = {
+ 1000.0f / 60.0f,
+ 0, 0, 0, 0
+};
+
+void Time_Init()
+{
+ TIME.beg_ticks = SDL_GetTicks();
+}
+
+void Time_Begin()
+{
+ TIME.start_ticks = SDL_GetTicks();
+}
+
+float Time_End()
+{
+ TIME.counted_frames += 1;
+ float FPS = (float)TIME.counted_frames / ( (float)(SDL_GetTicks() - TIME.beg_ticks) / 1000.0f );
+
+ float frameTicks = (float)(SDL_GetTicks() - TIME.start_ticks);
+ TIME.time_per_frame = frameTicks;
+ if(frameTicks < TIME.max_ticks_per_frame){
+ SDL_Delay( (Uint32)(TIME.max_ticks_per_frame - frameTicks) );
+ }
+
+ return FPS;
+}
+
+float Time_GetFrameTime()
+{
+ return (float)TIME.time_per_frame / 1000.0f;
+}
+
+void Time_SetMaxFramesPerSecond(Uint32 frames)
+{
+ TIME.max_ticks_per_frame = 1000.0f / (float)frames;
+}
+
+Uint32 Time_GetCountedFrames()
+{
+ return TIME.counted_frames;
+}
diff --git a/09-september/tomcat/util/util_time.h b/09-september/tomcat/util/util_time.h
new file mode 100644
index 0000000..3c3e470
--- /dev/null
+++ b/09-september/tomcat/util/util_time.h
@@ -0,0 +1,13 @@
+#ifndef UTIL_TIME_H
+#define UTIL_TIME_H
+
+#include <SDL2/SDL.h>
+
+extern void Time_Init( void );
+extern void Time_Begin( void );
+extern float Time_End( void );
+extern void Time_SetMaxFramesPerSecond(Uint32 frames);
+extern float Time_GetFrameTime( void );
+extern Uint32 Time_GetCountedFrames( void );
+
+#endif // UTIL_TIME_H