summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/camera.c14
-rw-r--r--src/camera.h3
-rw-r--r--src/core.c5
-rw-r--r--src/core.h3
-rw-r--r--src/empty.c3
-rw-r--r--src/maths/maths.h40
-rw-r--r--src/platform/file.c2
-rw-r--r--src/platform/path.c15
-rw-r--r--src/platform/path.h16
-rw-r--r--src/renderer/backends/backend_opengl.c82
-rw-r--r--src/renderer/render.c254
-rw-r--r--src/renderer/render.h22
-rw-r--r--src/renderer/render_backend.h7
-rw-r--r--src/renderer/render_types.h110
-rw-r--r--src/resources/loaders.h4
-rw-r--r--src/resources/obj.c385
-rw-r--r--src/std/containers/darray.h12
-rw-r--r--src/std/str.c2
-rw-r--r--src/std/str.h14
19 files changed, 970 insertions, 23 deletions
diff --git a/src/camera.c b/src/camera.c
index c2b864d..8ec1251 100644
--- a/src/camera.c
+++ b/src/camera.c
@@ -2,10 +2,16 @@
#include "maths.h"
-void camera_view_projection(camera *c, f32 screen_height, f32 screen_width, mat4 *out_view_proj) {
- mat4 proj = mat4_perspective(c->fov * 3.14 / 180.0, screen_width / screen_height, 0.1, 100.0);
+camera camera_create(vec3 pos, vec3 front, vec3 up, f32 fov) {
+ camera c = { .position = pos, .front = front, .up = up, .fov = fov };
+ return c;
+}
+
+void camera_view_projection(camera *c, f32 screen_height, f32 screen_width, mat4 *out_view,
+ mat4 *out_proj) {
+ mat4 proj = mat4_perspective(c->fov, screen_width / screen_height, 0.1, 100.0);
vec3 camera_direction = vec3_add(c->position, c->front);
mat4 view = mat4_look_at(c->position, camera_direction, c->up);
- mat4 out_mat = mat4_mult(view, proj);
- *out_view_proj = out_mat;
+ *out_view = view;
+ *out_proj = proj;
} \ No newline at end of file
diff --git a/src/camera.h b/src/camera.h
index 226f80e..f7bc6eb 100644
--- a/src/camera.h
+++ b/src/camera.h
@@ -23,4 +23,5 @@ typedef struct camera {
camera camera_create(vec3 pos, vec3 front, vec3 up, f32 fov);
/** @brief get a 4x4 transform matrix for the view and perspective projection */
-void camera_view_projection(camera *c, f32 screen_height, f32 screen_width, mat4 *out_view_proj); \ No newline at end of file
+void camera_view_projection(camera *c, f32 screen_height, f32 screen_width, mat4 *out_view,
+ mat4 *out_proj); \ No newline at end of file
diff --git a/src/core.c b/src/core.c
index affd8c8..024b2d7 100644
--- a/src/core.c
+++ b/src/core.c
@@ -45,8 +45,7 @@ core* core_bringup() {
}
*/
- // c->underworld.models = model_darray_new(10);
- // c->underworld.renderables = render_entity_darray_new(10);
+ c->models = model_darray_new(10);
return c;
-} \ No newline at end of file
+}
diff --git a/src/core.h b/src/core.h
index 8a3d037..1b3e28b 100644
--- a/src/core.h
+++ b/src/core.h
@@ -13,10 +13,11 @@ typedef struct core {
input_state input;
text_system_state text;
screenspace_state screenspace;
+ model_darray* models;
} core;
// --- Lifecycle
core* core_bringup();
void core_shutdown(core* core);
-void core_input_update(core* core); \ No newline at end of file
+void core_input_update(core* core);
diff --git a/src/empty.c b/src/empty.c
new file mode 100644
index 0000000..b40cc85
--- /dev/null
+++ b/src/empty.c
@@ -0,0 +1,3 @@
+// For some reason on Mac we need an empty file so that 'ar' has something
+// to run.
+int add(int a, int b) { return a + b; } \ No newline at end of file
diff --git a/src/maths/maths.h b/src/maths/maths.h
index 7352aeb..d832739 100644
--- a/src/maths/maths.h
+++ b/src/maths/maths.h
@@ -11,10 +11,16 @@
#include <math.h>
#include "maths_types.h"
+// --- Helpers
+#define deg_to_rad(x) (x * 3.14 / 180.0)
+#define min(a, b) (a < b ? a : b)
+#define max(a, b) (a > b ? a : b)
+
// --- Vector Implementations
// Dimension 3
static inline vec3 vec3_create(f32 x, f32 y, f32 z) { return (vec3){ x, y, z }; }
+#define vec3(x, y, z) (vec3_create(x, y, z))
static inline vec3 vec3_add(vec3 a, vec3 b) { return (vec3){ a.x + b.x, a.y + b.y, a.z + b.z }; }
static inline vec3 vec3_sub(vec3 a, vec3 b) { return (vec3){ a.x - b.x, a.y - b.y, a.z - b.z }; }
static inline vec3 vec3_mult(vec3 a, f32 s) { return (vec3){ a.x * s, a.y * s, a.z * s }; }
@@ -48,6 +54,18 @@ static inline vec2 vec2_create(f32 x, f32 y) { return (vec2){ x, y }; }
// TODO: Dimension 4
#define VEC4_ZERO ((vec4){ .x = 0.0, .y = 0.0, .z = 0.0, .w = 0.0 })
+// --- Quaternion Implementations
+
+static inline f32 quat_dot(quat a, quat b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; }
+
+static inline quat quat_normalise(quat a) {
+ f32 length = sqrtf(quat_dot(a, a) // same as len squared
+ );
+ return (quat){ a.x / length, a.y / length, a.z / length, a.w / length };
+}
+
+static inline quat quat_ident() { return (quat){ .x = 0.0, .y = 0.0, .z = 0.0, .w = 1.0 }; }
+
// --- Matrix Implementations
static inline mat4 mat4_ident() {
@@ -70,6 +88,26 @@ static inline mat4 mat4_scale(f32 scale) {
return out_matrix;
}
+// TODO: double check this
+static inline mat4 mat4_rotation(quat rotation) {
+ mat4 out_matrix = mat4_ident();
+ quat n = quat_normalise(rotation);
+
+ out_matrix.data[0] = 1.0f - 2.0f * n.y * n.y - 2.0f * n.z * n.z;
+ out_matrix.data[1] = 2.0f * n.x * n.y - 2.0f * n.z * n.w;
+ out_matrix.data[2] = 2.0f * n.x * n.z + 2.0f * n.y * n.w;
+
+ out_matrix.data[4] = 2.0f * n.x * n.y + 2.0f * n.z * n.w;
+ out_matrix.data[5] = 1.0f - 2.0f * n.x * n.x - 2.0f * n.z * n.z;
+ out_matrix.data[6] = 2.0f * n.y * n.z - 2.0f * n.x * n.w;
+
+ out_matrix.data[8] = 2.0f * n.x * n.z - 2.0f * n.y * n.w;
+ out_matrix.data[9] = 2.0f * n.y * n.z + 2.0f * n.x * n.w;
+ out_matrix.data[10] = 1.0f - 2.0f * n.x * n.x - 2.0f * n.y * n.y;
+
+ return out_matrix;
+}
+
static inline mat4 mat4_mult(mat4 lhs, mat4 rhs) {
mat4 out_matrix = mat4_ident();
@@ -156,8 +194,6 @@ static inline mat4 mat4_look_at(vec3 position, vec3 target, vec3 up) {
// ...
-// --- Quaternion Implementations
-
// --- Transform Implementations
#define TRANSFORM_DEFAULT \
diff --git a/src/platform/file.c b/src/platform/file.c
index 44aa9d0..ec9259a 100644
--- a/src/platform/file.c
+++ b/src/platform/file.c
@@ -60,4 +60,4 @@ str8_opt str8_from_file(arena *a, str8 path) {
result.has_value = true;
return result;
-} \ No newline at end of file
+}
diff --git a/src/platform/path.c b/src/platform/path.c
new file mode 100644
index 0000000..e67102b
--- /dev/null
+++ b/src/platform/path.c
@@ -0,0 +1,15 @@
+#include "path.h"
+
+#include <libgen.h>
+#include <string.h>
+#include "str.h"
+
+#if defined(CEL_PLATFORM_LINUX) || defined(CEL_PLATFORM_MAC)
+path_opt path_parent(const char* path) {
+ char* path_dirname = dirname(path);
+ return (path_opt){ .path = str8_cstr_view(path_dirname), .has_value = true };
+}
+#endif
+#ifdef CEL_PLATFORM_WINDOWS
+// TODO: path_opt path_parent(const char* path)
+#endif \ No newline at end of file
diff --git a/src/platform/path.h b/src/platform/path.h
new file mode 100644
index 0000000..0ec6993
--- /dev/null
+++ b/src/platform/path.h
@@ -0,0 +1,16 @@
+/**
+ * @file path.h
+ * @brief
+ * @date 2024-03-11
+ * @copyright Copyright (c) 2024
+ */
+#pragma once
+
+#include "str.h"
+
+typedef struct path_opt {
+ str8 path;
+ bool has_value;
+} path_opt;
+
+path_opt path_parent(const char* path); // TODO: convert to using str8 \ No newline at end of file
diff --git a/src/renderer/backends/backend_opengl.c b/src/renderer/backends/backend_opengl.c
index 6022dbf..ea6cb00 100644
--- a/src/renderer/backends/backend_opengl.c
+++ b/src/renderer/backends/backend_opengl.c
@@ -2,6 +2,7 @@
#define CEL_PLATFORM_LINUX
#include "defines.h"
+#include "file.h"
#include "log.h"
#include "maths_types.h"
#include "render_types.h"
@@ -59,4 +60,85 @@ void clear_screen(vec3 colour) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
+void bind_texture(shader s, texture *tex, u32 slot) {
+ // printf("bind texture slot %d with texture id %d \n", slot, tex->texture_id);
+ glActiveTexture(GL_TEXTURE0 + slot);
+ glBindTexture(GL_TEXTURE_2D, tex->texture_id);
+}
+
+void bind_mesh_vertex_buffer(void *_backend, mesh *mesh) { glBindVertexArray(mesh->vao); }
+
+static inline GLenum to_gl_prim_topology(enum cel_primitive_topology primitive) {
+ switch (primitive) {
+ case CEL_PRIMITIVE_TOPOLOGY_TRIANGLE:
+ return GL_TRIANGLES;
+ case CEL_PRIMITIVE_TOPOLOGY_POINT:
+ case CEL_PRIMITIVE_TOPOLOGY_LINE:
+ case CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP:
+ case CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
+ case CEL_PRIMITIVE_TOPOLOGY_COUNT:
+ break;
+ }
+}
+
+void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count) {
+ u32 gl_primitive = to_gl_prim_topology(primitive);
+ glDrawArrays(gl_primitive, start_index, count);
+}
+
+shader shader_create_separate(const char *vert_shader, const char *frag_shader) {
+ INFO("Load shaders at %s and %s", vert_shader, frag_shader);
+ int success;
+ char info_log[512];
+
+ u32 vertex = glCreateShader(GL_VERTEX_SHADER);
+ const char *vertex_shader_src = string_from_file(vert_shader);
+ if (vertex_shader_src == NULL) {
+ ERROR("EXIT: couldnt load shader");
+ exit(-1);
+ }
+ glShaderSource(vertex, 1, &vertex_shader_src, NULL);
+ glCompileShader(vertex);
+ glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(vertex, 512, NULL, info_log);
+ printf("%s\n", info_log);
+ ERROR("EXIT: vertex shader compilation failed");
+ exit(-1);
+ }
+
+ // fragment shader
+ u32 fragment = glCreateShader(GL_FRAGMENT_SHADER);
+ const char *fragment_shader_src = string_from_file(frag_shader);
+ if (fragment_shader_src == NULL) {
+ ERROR("EXIT: couldnt load shader");
+ exit(-1);
+ }
+ glShaderSource(fragment, 1, &fragment_shader_src, NULL);
+ glCompileShader(fragment);
+ glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
+ if (!success) {
+ glGetShaderInfoLog(fragment, 512, NULL, info_log);
+ printf("%s\n", info_log);
+ ERROR("EXIT: fragment shader compilation failed");
+ exit(-1);
+ }
+
+ u32 shader_prog;
+ shader_prog = glCreateProgram();
+
+ glAttachShader(shader_prog, vertex);
+ glAttachShader(shader_prog, fragment);
+ glLinkProgram(shader_prog);
+ glDeleteShader(vertex);
+ glDeleteShader(fragment);
+ free((char *)vertex_shader_src);
+ free((char *)fragment_shader_src);
+
+ shader s = { .program_id = shader_prog };
+ return s;
+}
+
+void set_shader(shader s) { glUseProgram(s.program_id); }
+
#endif \ No newline at end of file
diff --git a/src/renderer/render.c b/src/renderer/render.c
index 4e9ad89..7884db6 100644
--- a/src/renderer/render.c
+++ b/src/renderer/render.c
@@ -1,16 +1,39 @@
+#define STB_IMAGE_IMPLEMENTATION
+#include <stb_image.h>
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#include <stb_truetype.h>
+
#include "render.h"
+#include "render_types.h"
+#include <glad/glad.h>
#include <glfw3.h>
+#include "defines.h"
#include "log.h"
+#include "maths.h"
#include "render_backend.h"
+// FIXME: get rid of these and store dynamic screen realestate
+// in renderer
+#define SCR_WIDTH 1080
+#define SCR_HEIGHT 800
+
+material DEFAULT_MATERIAL = { 0 };
+
bool renderer_init(renderer* ren) {
INFO("Renderer init");
// NOTE: all platforms use GLFW at the moment but thats subject to change
glfwInit();
+ DEBUG("init graphics api (OpenGL) backend");
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
+
// glfw window creation
GLFWwindow* window = glfwCreateWindow(ren->config.scr_width, ren->config.scr_height,
ren->config.window_name, NULL, NULL);
@@ -28,6 +51,9 @@ bool renderer_init(renderer* ren) {
return false;
}
+ ren->blinn_phong =
+ shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag");
+
return true;
}
@@ -39,4 +65,232 @@ void render_frame_end(renderer* ren) {
// present frame
glfwSwapBuffers(ren->window);
glfwPollEvents();
+}
+
+void default_material_init() {
+ INFO("Load default material")
+ DEFAULT_MATERIAL.ambient_colour = (vec3){ 0.5, 0.5, 0.5 };
+ DEFAULT_MATERIAL.diffuse = (vec3){ 0.8, 0.8, 0.8 };
+ DEFAULT_MATERIAL.specular = (vec3){ 1.0, 1.0, 1.0 };
+ DEFAULT_MATERIAL.diffuse_texture = texture_data_load("assets/textures/white1x1.png", false);
+ DEFAULT_MATERIAL.specular_texture = texture_data_load("assets/textures/black1x1.png", false);
+ DEFAULT_MATERIAL.spec_exponent = 32.0;
+ strcpy(DEFAULT_MATERIAL.name, "Default");
+ texture_data_upload(&DEFAULT_MATERIAL.diffuse_texture);
+ texture_data_upload(&DEFAULT_MATERIAL.specular_texture);
+}
+
+void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene) {
+ // TRACE("Drawing model: %s", model->name);
+ mat4 view;
+ mat4 proj;
+ camera_view_projection(camera, SCR_HEIGHT, SCR_WIDTH, &view, &proj);
+
+ set_shader(ren->blinn_phong);
+
+ // set camera uniform
+ uniform_vec3f(ren->blinn_phong.program_id, "viewPos", &camera->position);
+ // set light uniforms
+ dir_light_upload_uniforms(ren->blinn_phong, &scene->dir_light);
+ for (int i = 0; i < scene->n_point_lights; i++) {
+ point_light_upload_uniforms(ren->blinn_phong, &scene->point_lights[i], '0' + i);
+ }
+
+ for (size_t i = 0; i < mesh_darray_len(model->meshes); i++) {
+ mesh* m = &model->meshes->data[i];
+ if (vertex_darray_len(m->vertices) == 0) {
+ continue;
+ }
+ // TRACE("Drawing mesh %d", i);
+ material* mat = &model->materials->data[m->material_index];
+ draw_mesh(ren, m, tf, mat, &view, &proj);
+ }
+}
+
+void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, mat4* proj) {
+ shader lighting_shader = ren->blinn_phong;
+
+ // bind buffer
+ bind_mesh_vertex_buffer(ren->backend_state, mesh);
+
+ // bind textures
+ bind_texture(lighting_shader, &mat->diffuse_texture, 0); // bind to slot 0
+ bind_texture(lighting_shader, &mat->specular_texture, 1); // bind to slot 1
+ uniform_f32(lighting_shader.program_id, "material.shininess", 32.);
+
+ // upload model transform
+ mat4 trans = mat4_translation(tf.position);
+ mat4 rot = mat4_rotation(tf.rotation);
+ mat4 scale = mat4_scale(tf.scale);
+ mat4 model_tf = mat4_mult(trans, mat4_mult(rot, scale));
+
+ uniform_mat4f(lighting_shader.program_id, "model", &model_tf);
+ // upload view & projection matrices
+ uniform_mat4f(lighting_shader.program_id, "view", view);
+ uniform_mat4f(lighting_shader.program_id, "projection", proj);
+
+ // draw triangles
+ u32 num_vertices = vertex_darray_len(mesh->vertices);
+ draw_primitives(CEL_PRIMITIVE_TOPOLOGY_TRIANGLE, 0, num_vertices);
+}
+
+void model_upload_meshes(renderer* ren, model* model) {
+ INFO("Upload mesh vertex data to GPU for model %s", model->name);
+
+ size_t num_meshes = mesh_darray_len(model->meshes);
+ u32 VBOs[num_meshes];
+ u32 VAOs[num_meshes];
+ glGenBuffers(num_meshes, VBOs);
+ glGenVertexArrays(num_meshes, VAOs);
+
+ u64 total_verts = 0;
+
+ TRACE("num meshes %d", num_meshes);
+
+ // upload each mesh to the GPU
+ for (int mesh_i = 0; mesh_i < num_meshes; mesh_i++) {
+ model->meshes->data[mesh_i].vao = VAOs[mesh_i];
+ model->meshes->data[mesh_i].vbo = VBOs[mesh_i];
+ // 3. bind buffers
+ glBindBuffer(GL_ARRAY_BUFFER, VBOs[mesh_i]);
+
+ size_t num_vertices = vertex_darray_len(model->meshes->data[mesh_i].vertices);
+ // TRACE("Uploading vertex array data: %d verts", num_vertices);
+ total_verts += num_vertices;
+
+ // TODO: convert this garbage into a function
+ f32 verts[num_vertices * 8];
+ // for each face
+ for (int i = 0; i < (num_vertices / 3); i++) {
+ // for each vert in face
+ for (int j = 0; j < 3; j++) {
+ size_t stride = (i * 24) + j * 8;
+ // printf("i: %d, stride: %ld, loc %d\n", i, stride, i * 3 + j);
+ vertex vert = model->meshes->data[mesh_i].vertices->data[i];
+ // printf("pos %f %f %f\n", vert.position.x, vert.position.y, vert.position.z);
+ // printf("norm %f %f %f\n", vert.normal.x, vert.normal.y, vert.normal.z);
+ // printf("tex %f %f\n", vert.uv.x, vert.uv.y);
+ verts[stride + 0] =
+ ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.x;
+ verts[stride + 1] =
+ ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.y;
+ verts[stride + 2] =
+ ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].position.z;
+ verts[stride + 3] =
+ ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.x;
+ verts[stride + 4] =
+ ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.y;
+ verts[stride + 5] =
+ ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].normal.z;
+ verts[stride + 6] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.x;
+ verts[stride + 7] = ((vertex*)model->meshes->data[mesh_i].vertices->data)[i * 3 + j].uv.y;
+ }
+ }
+
+ // 4. upload data
+ glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
+
+ // 5. cont. set mesh vertex layout
+ glBindVertexArray(model->meshes->data[mesh_i].vao);
+ // position attribute
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
+ glEnableVertexAttribArray(0);
+ // normal vector attribute
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
+ glEnableVertexAttribArray(1);
+ // tex coords
+ glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
+ glEnableVertexAttribArray(2);
+ }
+
+ INFO("Uploaded %d submeshes with a total of %d vertices\n", num_meshes, total_verts);
+
+ // 6. reset buffer
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+texture texture_data_load(const char* path, bool invert_y) {
+ TRACE("Load texture %s", path);
+
+ // load the file data
+ // texture loading
+ int width, height, num_channels;
+ stbi_set_flip_vertically_on_load(invert_y);
+
+#pragma GCC diagnostic ignored "-Wpointer-sign"
+ char* data = stbi_load(path, &width, &height, &num_channels, 0);
+ if (data) {
+ DEBUG("loaded texture: %s", path);
+ } else {
+ WARN("failed to load texture");
+ }
+
+ unsigned int channel_type;
+ if (num_channels == 4) {
+ channel_type = GL_RGBA;
+ } else {
+ channel_type = GL_RGB;
+ }
+
+ return (texture){ .texture_id = 0,
+ .width = width,
+ .height = height,
+ .channel_count = num_channels,
+ .channel_type = channel_type,
+ .name = "TODO: Texture names",
+ .image_data = data };
+}
+
+void texture_data_upload(texture* tex) {
+ printf("Texture name %s\n", tex->name);
+ TRACE("Upload texture data");
+ u32 texture_id;
+ glGenTextures(1, &texture_id);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ tex->texture_id = texture_id;
+
+ // set the texture wrapping parameters
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ // set texture filtering parameters
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex->width, tex->height, 0, tex->channel_type,
+ GL_UNSIGNED_BYTE, tex->image_data);
+ glGenerateMipmap(GL_TEXTURE_2D);
+ DEBUG("Freeing texture image data after uploading to GPU");
+ // stbi_image_free(tex->image_data); // data is on gpu now so we dont need it around
+}
+
+void dir_light_upload_uniforms(shader shader, directional_light* light) {
+ uniform_vec3f(shader.program_id, "dirLight.direction", &light->direction);
+ uniform_vec3f(shader.program_id, "dirLight.ambient", &light->ambient);
+ uniform_vec3f(shader.program_id, "dirLight.diffuse", &light->diffuse);
+ uniform_vec3f(shader.program_id, "dirLight.specular", &light->specular);
+}
+
+void point_light_upload_uniforms(shader shader, point_light* light, char index) {
+ char position_str[] = "pointLights[x].position";
+ position_str[12] = (char)index;
+ char ambient_str[] = "pointLights[x].ambient";
+ ambient_str[12] = (char)index;
+ char diffuse_str[] = "pointLights[x].diffuse";
+ diffuse_str[12] = (char)index;
+ char specular_str[] = "pointLights[x].specular";
+ specular_str[12] = (char)index;
+ char constant_str[] = "pointLights[x].constant";
+ constant_str[12] = (char)index;
+ char linear_str[] = "pointLights[x].linear";
+ linear_str[12] = (char)index;
+ char quadratic_str[] = "pointLights[x].quadratic";
+ quadratic_str[12] = (char)index;
+ uniform_vec3f(shader.program_id, position_str, &light->position);
+ uniform_vec3f(shader.program_id, ambient_str, &light->ambient);
+ uniform_vec3f(shader.program_id, diffuse_str, &light->diffuse);
+ uniform_vec3f(shader.program_id, specular_str, &light->specular);
+ uniform_f32(shader.program_id, constant_str, light->constant);
+ uniform_f32(shader.program_id, linear_str, light->linear);
+ uniform_f32(shader.program_id, quadratic_str, light->quadratic);
} \ No newline at end of file
diff --git a/src/renderer/render.h b/src/renderer/render.h
index c89c364..10702e3 100644
--- a/src/renderer/render.h
+++ b/src/renderer/render.h
@@ -1,5 +1,7 @@
#pragma once
+#include "camera.h"
+#include "loaders.h"
#include "render_types.h"
// --- Lifecycle
@@ -13,4 +15,22 @@ void renderer_shutdown(renderer* ren);
void render_frame_begin(renderer* ren);
void render_frame_end(renderer* ren);
-// --- \ No newline at end of file
+// --- models meshes
+void model_upload_meshes(renderer* ren, model* model);
+void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene);
+void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, mat4* proj);
+
+// ---
+texture texture_data_load(const char* path, bool invert_y); // #frontend
+void texture_data_upload(texture* tex); // #backend
+
+// --- Uniforms
+
+/** @brief upload a vec3 of f32 to a uniform */
+void uniform_vec3f(u32 program_id, const char* uniform_name, vec3* value);
+/** @brief upload a single f32 to a uniform */
+void uniform_f32(u32 program_id, const char* uniform_name, f32 value);
+/** @brief upload a integer to a uniform */
+void uniform_i32(u32 program_id, const char* uniform_name, i32 value);
+/** @brief upload a mat4 of f32 to a uniform */
+void uniform_mat4f(u32 program_id, const char* uniform_name, mat4* value); \ No newline at end of file
diff --git a/src/renderer/render_backend.h b/src/renderer/render_backend.h
index 61c7ab5..7e1c16c 100644
--- a/src/renderer/render_backend.h
+++ b/src/renderer/render_backend.h
@@ -12,4 +12,11 @@ void gfx_backend_shutdown(renderer* ren);
void clear_screen(vec3 colour);
+void bind_texture(shader s, texture* tex, u32 slot);
+void bind_mesh_vertex_buffer(void* backend, mesh* mesh);
+void draw_primitives(cel_primitive_topology primitive, u32 start_index, u32 count);
+
+shader shader_create_separate(const char* vert_shader, const char* frag_shader);
+void set_shader(shader s);
+
// --- Uniforms
diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h
index e24fc24..483e392 100644
--- a/src/renderer/render_types.h
+++ b/src/renderer/render_types.h
@@ -13,7 +13,11 @@
struct GLFWwindow;
+#define MAX_MATERIAL_NAME_LEN 256
+#define MAX_TEXTURE_NAME_LEN 256
+
#ifndef RESOURCE_HANDLE_DEFS
+CORE_DEFINE_HANDLE(model_handle);
CORE_DEFINE_HANDLE(texture_handle);
#define RESOURCE_HANDLE_DEFS
#endif
@@ -34,8 +38,67 @@ typedef struct renderer {
struct GLFWwindow *window; /** Currently all platforms use GLFW*/
void *backend_state; /** Graphics API-specific state */
renderer_config config;
+ // shaders
+ shader blinn_phong;
} renderer;
+// --- Lighting & Materials
+
+typedef struct texture {
+ u32 texture_id;
+ char name[MAX_TEXTURE_NAME_LEN];
+ void *image_data;
+ u32 width;
+ u32 height;
+ u8 channel_count;
+ u32 channel_type;
+} texture;
+
+typedef struct blinn_phong_material {
+ char name[MAX_MATERIAL_NAME_LEN];
+ texture diffuse_texture;
+ char diffuse_tex_path[256];
+ texture specular_texture;
+ char specular_tex_path[256];
+ vec3 ambient_colour;
+ vec3 diffuse;
+ vec3 specular;
+ f32 spec_exponent;
+ bool is_loaded;
+ bool is_uploaded;
+} blinn_phong_material;
+typedef blinn_phong_material material; // when we start using PBR, this will no longer be the case
+
+// the default blinn-phong material. MUST be initialised with the function below
+extern material DEFAULT_MATERIAL;
+void default_material_init();
+
+#ifndef TYPED_MATERIAL_ARRAY
+KITC_DECL_TYPED_ARRAY(material) // creates "material_darray"
+#define TYPED_MATERIAL_ARRAY
+#endif
+
+// lights
+typedef struct point_light {
+ vec3 position;
+ f32 constant, linear, quadratic;
+ vec3 ambient;
+ vec3 diffuse;
+ vec3 specular;
+} point_light;
+
+typedef struct directional_light {
+ vec3 direction;
+ vec3 ambient;
+ vec3 diffuse;
+ vec3 specular;
+} directional_light;
+
+void point_light_upload_uniforms(shader shader, point_light *light, char index);
+void dir_light_upload_uniforms(shader shader, directional_light *light);
+
+// --- Models & Meshes
+
/** @brief Vertex format for a static mesh */
typedef struct vertex {
vec3 position;
@@ -48,8 +111,6 @@ KITC_DECL_TYPED_ARRAY(vertex) // creates "vertex_darray"
#define TYPED_VERTEX_ARRAY
#endif
-// --- Models & Meshes
-
typedef struct mesh {
vertex_darray *vertices;
u32 vertex_size; /** size in bytes of each vertex including necessary padding */
@@ -57,7 +118,7 @@ typedef struct mesh {
u32 *indices;
u32 indices_len;
size_t material_index;
- u32 vbo, vao; /** OpenGL data */
+ u32 vbo, vao; /** OpenGL data. TODO: dont leak OpenGL details */
} mesh;
#ifndef TYPED_MESH_ARRAY
@@ -67,9 +128,50 @@ KITC_DECL_TYPED_ARRAY(mesh) // creates "mesh_darray"
typedef struct model {
str8 name;
+ mesh_darray *meshes;
+ aabb_3d bbox;
+ material_darray *materials;
+ bool is_loaded;
+ bool is_uploaded;
} model;
#ifndef TYPED_MODEL_ARRAY
KITC_DECL_TYPED_ARRAY(model) // creates "model_darray"
#define TYPED_MODEL_ARRAY
-#endif \ No newline at end of file
+#endif
+
+// --- Scene
+
+// NOTE: This struct won't stay like this for a long time. It's somewhat temporary
+// in order to get a basic scene working without putting burden on the caller of
+// draw_model()
+typedef struct scene {
+ directional_light dir_light;
+ point_light point_lights[4];
+ size_t n_point_lights;
+} scene;
+
+// --- Graphics API related
+
+typedef enum cel_primitive_topology {
+ CEL_PRIMITIVE_TOPOLOGY_POINT,
+ CEL_PRIMITIVE_TOPOLOGY_LINE,
+ CEL_PRIMITIVE_TOPOLOGY_LINE_STRIP,
+ CEL_PRIMITIVE_TOPOLOGY_TRIANGLE,
+ CEL_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
+ CEL_PRIMITIVE_TOPOLOGY_COUNT
+} cel_primitive_topology;
+
+typedef enum gpu_texture_type {
+ TEXTURE_TYPE_2D,
+ TEXTURE_TYPE_3D,
+ TEXTURE_TYPE_2D_ARRAY,
+ TEXTURE_TYPE_CUBE_MAP,
+ TEXTURE_TYPE_COUNT
+} gpu_texture_type;
+
+typedef enum gpu_texture_format {
+ TEXTURE_FORMAT_8_8_8_8_RGBA_UNORM,
+ TEXTURE_FORMAT_DEPTH_DEFAULT,
+ TEXTURE_FORMAT_COUNT
+} gpu_texture_format;
diff --git a/src/resources/loaders.h b/src/resources/loaders.h
index ba38ec4..858e4d1 100644
--- a/src/resources/loaders.h
+++ b/src/resources/loaders.h
@@ -1,9 +1,9 @@
#pragma once
#include "defines.h"
+#include "render_types.h"
struct core;
-typedef u32 model_handle;
model_handle model_load_obj(struct core *core, const char *path, bool invert_texture_y);
-model_handle model_load_gltf(struct core *core, const char *path, bool invert_texture_y); \ No newline at end of file
+model_handle model_load_gltf(struct core *core, const char *path, bool invert_texture_y);
diff --git a/src/resources/obj.c b/src/resources/obj.c
index b646f58..710d5f0 100644
--- a/src/resources/obj.c
+++ b/src/resources/obj.c
@@ -1 +1,384 @@
-// TODO: Port code from old repo \ No newline at end of file
+/**
+ * @file obj.c
+ * @brief Wavefront OBJ loader.
+ * @copyright Copyright (c) 2024
+ */
+#include <ctype.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "core.h"
+#include "darray.h"
+#include "file.h"
+#include "log.h"
+#include "maths.h"
+#include "path.h"
+#include "render.h"
+#include "render_types.h"
+#include "str.h"
+
+struct face {
+ u32 vertex_indices[3];
+ u32 normal_indices[3];
+ u32 uv_indices[3];
+};
+typedef struct face face;
+
+KITC_DECL_TYPED_ARRAY(vec3)
+KITC_DECL_TYPED_ARRAY(vec2)
+KITC_DECL_TYPED_ARRAY(face)
+
+// Forward declarations
+void create_submesh(mesh_darray *meshes, vec3_darray *tmp_positions, vec3_darray *tmp_normals,
+ vec2_darray *tmp_uvs, face_darray *tmp_faces, material_darray *materials,
+ bool material_loaded, char current_material_name[256]);
+bool load_material_lib(const char *path, str8 relative_path, material_darray *materials);
+bool model_load_obj_str(const char *file_string, str8 relative_path, model *out_model,
+ bool invert_textures_y);
+
+model_handle model_load_obj(core *core, const char *path, bool invert_textures_y) {
+ TRACE("Loading model at Path %s\n", path);
+ path_opt relative_path = path_parent(path);
+ if (!relative_path.has_value) {
+ WARN("Couldnt get a relative path for the path to use for loading materials & textures later");
+ }
+ printf("Relative path: %s\n", relative_path.path.buf);
+ const char *file_string = string_from_file(path);
+
+ // TODO: store the relative path without the name.obj at the end
+
+ model model = { 0 };
+ model.name = str8_cstr_view(path);
+ model.meshes = mesh_darray_new(1);
+ model.materials = material_darray_new(1);
+
+ bool success = model_load_obj_str(file_string, relative_path.path, &model, invert_textures_y);
+
+ if (!success) {
+ FATAL("Couldnt load OBJ file at path %s", path);
+ ERROR_EXIT("Load fails are considered crash-worthy right now. This will change later.\n");
+ }
+
+ u32 index = model_darray_len(core->models);
+ model_darray_push(core->models, model);
+ return (model_handle){ .raw = index };
+}
+
+bool model_load_obj_str(const char *file_string, str8 relative_path, model *out_model,
+ bool invert_textures_y) {
+ TRACE("Load OBJ from string");
+
+ // Setup temps
+ vec3_darray *tmp_positions = vec3_darray_new(1000);
+ vec3_darray *tmp_normals = vec3_darray_new(1000);
+ vec2_darray *tmp_uvs = vec2_darray_new(1000);
+ face_darray *tmp_faces = face_darray_new(1000);
+ // TODO: In the future I'd like these temporary arrays to be allocated from an arena provided
+ // by the function one level up, model_load_obj. That way we can just `return false;` anywhere in
+ // this code to indicate an error, and be sure that all that memory will be cleaned up without
+ // having to call vec3_darray_free in every single error case before returning.
+
+ // Other state
+ bool object_set = false;
+ bool material_loaded = false;
+ char current_material_name[64];
+
+ char *pch;
+ char *rest = file_string;
+ pch = strtok_r((char *)file_string, "\n", &rest);
+
+ int line_num = 0;
+ char last_char_type = 'a';
+
+ while (pch != NULL) {
+ line_num++;
+ char line_header[128];
+ int offset = 0;
+
+ // skip whitespace
+ char *p = pch;
+
+ skip_space(pch);
+
+ if (*p == '\0') {
+ /* the string is empty */
+ } else {
+ // read the first word of the line
+ int res = sscanf(pch, "%s %n", line_header, &offset);
+ /* printf("header: %s, offset : %d res: %d\n",line_header, offset, res); */
+ if (res != 1) {
+ break;
+ }
+
+ if (strcmp(line_header, "o") == 0 || strcmp(line_header, "g") == 0) {
+ // if we're currently parsing one
+ if (!object_set) {
+ object_set = true;
+ } else {
+ create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces,
+ out_model->materials, material_loaded, current_material_name);
+ object_set = false;
+ }
+ } else if (strcmp(line_header, "v") == 0) {
+ // special logic: if we went from faces back to vertices trigger a mesh output.
+ // PS: I hate OBJ
+ if (last_char_type == 'f') {
+ create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces,
+ out_model->materials, material_loaded, current_material_name);
+ object_set = false;
+ }
+
+ last_char_type = 'v';
+ vec3 vertex;
+ sscanf(pch + offset, "%f %f %f", &vertex.x, &vertex.y, &vertex.z);
+
+ vec3_darray_push(tmp_positions, vertex);
+ } else if (strcmp(line_header, "vt") == 0) {
+ last_char_type = 't';
+ vec2 uv;
+ char copy[1024];
+ memcpy(copy, pch + offset, strlen(pch + offset) + 1);
+ char *p = pch + offset;
+ while (isspace((unsigned char)*p)) ++p;
+
+ // I can't remember what is going on here
+ memset(copy, 0, 1024);
+ memcpy(copy, pch + offset, strlen(pch + offset) + 1);
+ int res = sscanf(copy, "%f %f", &uv.x, &uv.y);
+ memset(copy, 0, 1024);
+ memcpy(copy, pch + offset, strlen(pch + offset) + 1);
+ if (res != 1) {
+ // da frick? some .obj files have 3 uvs instead of 2
+ f32 dummy;
+ int res2 = sscanf(copy, "%f %f %f", &uv.x, &uv.y, &dummy);
+ }
+
+ if (invert_textures_y) {
+ uv.y = -uv.y; // flip Y axis to be consistent with how other PNGs are being handled
+ // `texture_load` will flip it again
+ }
+ vec2_darray_push(tmp_uvs, uv);
+ } else if (strcmp(line_header, "vn") == 0) {
+ last_char_type = 'n';
+ vec3 normal;
+ sscanf(pch + offset, "%f %f %f", &normal.x, &normal.y, &normal.z);
+ vec3_darray_push(tmp_normals, normal);
+ } else if (strcmp(line_header, "f") == 0) {
+ last_char_type = 'f';
+ struct face f;
+ sscanf(pch + offset, "%d/%d/%d %d/%d/%d %d/%d/%d", &f.vertex_indices[0], &f.uv_indices[0],
+ &f.normal_indices[0], &f.vertex_indices[1], &f.uv_indices[1], &f.normal_indices[1],
+ &f.vertex_indices[2], &f.uv_indices[2], &f.normal_indices[2]);
+ // printf("f %d/%d/%d %d/%d/%d %d/%d/%d\n", f.vertex_indices[0], f.uv_indices[0],
+ // f.normal_indices[0],
+ // f.vertex_indices[1], f.uv_indices[1], f.normal_indices[1],
+ // f.vertex_indices[2], f.uv_indices[2], f.normal_indices[2]);
+ face_darray_push(tmp_faces, f);
+ } else if (strcmp(line_header, "mtllib") == 0) {
+ char filename[1024];
+ sscanf(pch + offset, "%s", filename);
+ char mtllib_path[1024];
+ snprintf(mtllib_path, sizeof(mtllib_path), "%s/%s", relative_path.buf, filename);
+ if (!load_material_lib(mtllib_path, relative_path, out_model->materials)) {
+ ERROR("couldnt load material lib");
+ return false;
+ }
+ } else if (strcmp(line_header, "usemtl") == 0) {
+ material_loaded = true;
+ sscanf(pch + offset, "%s", current_material_name);
+ }
+ }
+
+ pch = strtok_r(NULL, "\n", &rest);
+ }
+
+ // last mesh or if one wasnt created with 'o' directive
+ if (face_darray_len(tmp_faces) > 0) {
+ TRACE("Last leftover mesh");
+ create_submesh(out_model->meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces,
+ out_model->materials, material_loaded, current_material_name);
+ }
+
+ // Free data
+ free((char *)file_string);
+ vec3_darray_free(tmp_positions);
+ vec3_darray_free(tmp_normals);
+ vec2_darray_free(tmp_uvs);
+ face_darray_free(tmp_faces);
+ TRACE("Freed temporary OBJ loading data");
+
+ if (mesh_darray_len(out_model->meshes) > 256) {
+ printf("num meshes: %ld\n", mesh_darray_len(out_model->meshes));
+ }
+
+ // TODO: bounding box calculation for each mesh
+ // TODO: bounding box calculation for model
+
+ return true;
+}
+
+/**
+ * @brief Takes the current positions, normals, uvs arrays and constructs the vertex array
+ * from those indices.
+ */
+void create_submesh(mesh_darray *meshes, vec3_darray *tmp_positions, vec3_darray *tmp_normals,
+ vec2_darray *tmp_uvs, face_darray *tmp_faces, material_darray *materials,
+ bool material_loaded, char current_material_name[256]) {
+ size_t num_verts = face_darray_len(tmp_faces) * 3;
+ vertex_darray *out_vertices = vertex_darray_new(num_verts);
+
+ face_darray_iter face_iter = face_darray_iter_new(tmp_faces);
+ struct face *f;
+
+ while ((f = face_darray_iter_next(&face_iter))) {
+ for (int j = 0; j < 3; j++) {
+ vertex vert = { 0 };
+ vert.position = tmp_positions->data[f->vertex_indices[j] - 1];
+ if (vec3_darray_len(tmp_normals) == 0) {
+ vert.normal = vec3_create(0.0, 0.0, 0.0);
+ } else {
+ vert.normal = tmp_normals->data[f->normal_indices[j] - 1];
+ }
+ vert.uv = tmp_uvs->data[f->uv_indices[j] - 1];
+ vertex_darray_push(out_vertices, vert);
+ }
+ }
+
+ DEBUG("Loaded submesh\n vertices: %zu\n uvs: %zu\n normals: %zu\n faces: %zu",
+ vec3_darray_len(tmp_positions), vec2_darray_len(tmp_uvs), vec3_darray_len(tmp_normals),
+ face_darray_len(tmp_faces));
+
+ // Clear current object faces
+ face_darray_clear(tmp_faces);
+
+ mesh m = { .vertices = out_vertices };
+ if (material_loaded) {
+ // linear scan to find material
+ bool found = false;
+ DEBUG("Num of materials : %ld", material_darray_len(materials));
+ material_darray_iter mat_iter = material_darray_iter_new(materials);
+ blinn_phong_material *cur_material;
+ while ((cur_material = material_darray_iter_next(&mat_iter))) {
+ if (strcmp(cur_material->name, current_material_name) == 0) {
+ DEBUG("Found match");
+ m.material_index = mat_iter.current_idx - 1;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // TODO: default material
+ m.material_index = 0;
+ DEBUG("Set default material");
+ }
+ }
+ mesh_darray_push(meshes, m);
+}
+
+bool load_material_lib(const char *path, str8 relative_path, material_darray *materials) {
+ TRACE("BEGIN load material lib at %s", path);
+
+ const char *file_string = string_from_file(path);
+ if (file_string == NULL) {
+ ERROR("couldnt load %s", path);
+ return false;
+ }
+
+ char *pch;
+ char *saveptr;
+ pch = strtok_r((char *)file_string, "\n", &saveptr);
+
+ material current_material = DEFAULT_MATERIAL;
+
+ bool material_set = false;
+
+ while (pch != NULL) {
+ char line_header[128];
+ int offset = 0;
+ // read the first word of the line
+ int res = sscanf(pch, "%s %n", line_header, &offset);
+ if (res != 1) {
+ break;
+ }
+
+ // When we see "newmtl", start a new material, or flush the previous one
+ if (strcmp(line_header, "newmtl") == 0) {
+ if (material_set) {
+ // a material was being parsed, so flush that one and start a new one
+ material_darray_push(materials, current_material);
+ DEBUG("pushed material with name %s", current_material.name);
+ WARN("Reset current material");
+ current_material = DEFAULT_MATERIAL;
+ } else {
+ material_set = true;
+ }
+ // scan the new material name
+ char material_name[64];
+ sscanf(pch + offset, "%s", current_material.name);
+ DEBUG("material name %s\n", current_material.name);
+ // current_material.name = material_name;
+ } else if (strcmp(line_header, "Ka") == 0) {
+ // ambient
+ sscanf(pch + offset, "%f %f %f", &current_material.ambient_colour.x,
+ &current_material.ambient_colour.y, &current_material.ambient_colour.z);
+ } else if (strcmp(line_header, "Kd") == 0) {
+ // diffuse
+ sscanf(pch + offset, "%f %f %f", &current_material.diffuse.x, &current_material.diffuse.y,
+ &current_material.diffuse.z);
+ } else if (strcmp(line_header, "Ks") == 0) {
+ // specular
+ sscanf(pch + offset, "%f %f %f", &current_material.specular.x, &current_material.specular.y,
+ &current_material.specular.z);
+ } else if (strcmp(line_header, "Ns") == 0) {
+ // specular exponent
+ sscanf(pch + offset, "%f", &current_material.spec_exponent);
+ } else if (strcmp(line_header, "map_Kd") == 0) {
+ char diffuse_map_filename[1024];
+ sscanf(pch + offset, "%s", diffuse_map_filename);
+ char diffuse_map_path[1024];
+ snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf,
+ diffuse_map_filename);
+ printf("load from %s\n", diffuse_map_path);
+
+ // --------------
+ texture diffuse_texture = texture_data_load(diffuse_map_path, true);
+ current_material.diffuse_texture = diffuse_texture;
+ strcpy(current_material.diffuse_tex_path, diffuse_map_path);
+ texture_data_upload(&current_material.diffuse_texture);
+ // --------------
+ } else if (strcmp(line_header, "map_Ks") == 0) {
+ // char specular_map_path[1024] = "assets/";
+ // sscanf(pch + offset, "%s", specular_map_path + 7);
+ char specular_map_filename[1024];
+ sscanf(pch + offset, "%s", specular_map_filename);
+ char specular_map_path[1024];
+ snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf,
+ specular_map_filename);
+ printf("load from %s\n", specular_map_path);
+ // --------------
+ texture specular_texture = texture_data_load(specular_map_path, true);
+ current_material.specular_texture = specular_texture;
+ strcpy(current_material.specular_tex_path, specular_map_path);
+ texture_data_upload(&current_material.specular_texture);
+ // --------------
+ } else if (strcmp(line_header, "map_Bump") == 0) {
+ // TODO
+ }
+
+ pch = strtok_r(NULL, "\n", &saveptr);
+ }
+
+ TRACE("end load material lib");
+
+ // last mesh or if one wasnt created with 'o' directive
+ // TRACE("Last leftover material");
+ material_darray_push(materials, current_material);
+
+ INFO("Loaded %ld materials", material_darray_len(materials));
+ TRACE("END load material lib");
+ return true;
+}
diff --git a/src/std/containers/darray.h b/src/std/containers/darray.h
index 729b4cf..25bf846 100644
--- a/src/std/containers/darray.h
+++ b/src/std/containers/darray.h
@@ -33,14 +33,22 @@
#define PREFIX static
+/* if (arena != NULL) {\ */
+/* d = arena_alloc(arena, sizeof(T##_darray));\ */
+/* data = arena_alloc(arena, starting_capacity * sizeof(T));\ */
+/* } else {\ */
+/* }\ */
+
#define KITC_DECL_TYPED_ARRAY(T) \
typedef typed_array(T) T##_darray; \
typedef typed_array_iterator(T) T##_darray_iter; \
\
/* Create a new one growable array */ \
PREFIX T##_darray *T##_darray_new(size_t starting_capacity) { \
- T##_darray *d = malloc(sizeof(T##_darray)); \
- T *data = malloc(starting_capacity * sizeof(T)); \
+ T##_darray *d; \
+ T *data; \
+ d = malloc(sizeof(T##_darray)); \
+ data = malloc(starting_capacity * sizeof(T)); \
\
d->len = 0; \
d->capacity = starting_capacity; \
diff --git a/src/std/str.c b/src/std/str.c
index 1c687fa..07a8e73 100644
--- a/src/std/str.c
+++ b/src/std/str.c
@@ -5,6 +5,8 @@
str8 str8_create(u8* buf, size_t len) { return (str8){ .buf = buf, .len = len }; }
+str8 str8_cstr_view(char* string) { return str8_create((u8*)string, strlen(string)); }
+
bool str8_equals(str8 a, str8 b) {
if (a.len != b.len) {
return false;
diff --git a/src/std/str.h b/src/std/str.h
index 735b88e..1ebecac 100644
--- a/src/std/str.h
+++ b/src/std/str.h
@@ -10,6 +10,8 @@
*/
#pragma once
+#include <ctype.h>
+
#include "defines.h"
#include "mem.h"
@@ -36,6 +38,11 @@ char* str8_to_cstr(arena* a, str8 s);
#define cstr(a, s) (str8_to_cstr(a, s)) // Shorthand
+/** @brief Return a str8 that references a statically allocated string.
+ `string` therefore must already be null-terminated.
+ @note The backing `string` cannot be modified. */
+str8 str8_cstr_view(char* string);
+
// --- Comparisons
/** @brief Compare two strings for exact equality */
@@ -70,4 +77,9 @@ str8 str8_concat(arena* a, str8 left, str8 right);
static inline bool str8_is_null_term(str8 a) {
return a.buf[a.len] == 0; // This doesn't seem safe. YOLO
-} \ No newline at end of file
+}
+
+// TODO: move or delete this and replace with handling using our internal type
+static void skip_space(char* p) {
+ while (isspace((unsigned char)*p)) ++p;
+}