summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoromniscient <17525998+omnisci3nce@users.noreply.github.com>2024-04-27 11:25:48 +1000
committeromniscient <17525998+omnisci3nce@users.noreply.github.com>2024-04-27 11:25:48 +1000
commit8dae0a94059bd21a5a10e4434cadc843126d8b05 (patch)
treed8d110e0a4a2a03e1e9d6c3b7a2dbf53a9d9e858 /src
parent7a8d93d2b867cab853ecf8c4ec5061cdc5cab665 (diff)
parentf8375e4587612d6a582eb053be5a67694a59993d (diff)
Merge branch 'brainstorming-systems' into ral
Diffstat (limited to 'src')
-rw-r--r--src/defines.h2
-rw-r--r--src/logos/jobs.h3
-rw-r--r--src/maths/maths.h24
-rw-r--r--src/maths/primitives.h86
-rw-r--r--src/renderer/render.c45
-rw-r--r--src/renderer/render.h7
-rw-r--r--src/renderer/render_types.h1
-rw-r--r--src/resources/gltf.c517
-rw-r--r--src/std/mem.c2
-rw-r--r--src/systems/physics.c1
-rw-r--r--src/systems/physics.h33
-rw-r--r--src/transform_hierarchy.c184
-rw-r--r--src/transform_hierarchy.h76
13 files changed, 928 insertions, 53 deletions
diff --git a/src/defines.h b/src/defines.h
index 811064d..d36bb09 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -1,6 +1,6 @@
/**
* @file defines.h
- * @brief
+ * @brief Typedefs for common integer/floating point types and very basic macros
* @date 2024-02-24
* @copyright Copyright (c) 2024
*/
diff --git a/src/logos/jobs.h b/src/logos/jobs.h
new file mode 100644
index 0000000..cc2c8fa
--- /dev/null
+++ b/src/logos/jobs.h
@@ -0,0 +1,3 @@
+/**
+ * Common jobs that get run
+*/ \ No newline at end of file
diff --git a/src/maths/maths.h b/src/maths/maths.h
index c9bcaad..638d9f6 100644
--- a/src/maths/maths.h
+++ b/src/maths/maths.h
@@ -64,13 +64,25 @@ static inline vec4 vec4_create(f32 x, f32 y, f32 z, f32 w) { return (vec4){ x, y
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
- );
+ 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 }; }
+static quat quat_from_axis_angle(vec3 axis, f32 angle, bool normalize) {
+ const f32 half_angle = 0.5f * angle;
+ f32 s = sinf(half_angle);
+ f32 c = cosf(half_angle);
+
+ quat q = (quat){ s * axis.x, s * axis.y, s * axis.z, c };
+ if (normalize) {
+ return quat_normalise(q);
+ }
+ return q;
+}
+
// --- Matrix Implementations
static inline mat4 mat4_ident() {
@@ -246,12 +258,14 @@ static inline mat4 mat4_look_at(vec3 position, vec3 target, vec3 up) {
.is_dirty = false })
static transform transform_create(vec3 pos, quat rot, f32 scale) {
- return (transform){ .position = pos, .rotation = rot, .scale = scale, .is_dirty = false };
+ return (transform){ .position = pos, .rotation = rot, .scale = scale, .is_dirty = true };
}
static inline mat4 transform_to_mat(transform *tf) {
- // TODO: rotation
- return mat4_mult(mat4_translation(tf->position), mat4_scale(tf->scale));
+ mat4 trans = mat4_translation(tf->position);
+ mat4 rot = mat4_rotation(tf->rotation);
+ mat4 scale = mat4_scale(tf->scale);
+ return mat4_mult(trans, mat4_mult(rot, scale));
}
// --- Sizing asserts
diff --git a/src/maths/primitives.h b/src/maths/primitives.h
index 4fb6b4d..ed52c8c 100644
--- a/src/maths/primitives.h
+++ b/src/maths/primitives.h
@@ -34,38 +34,48 @@ static mesh prim_cube_mesh_create() {
(vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 0 } });
vertex_darray_push(
cube.vertices,
- (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1,1 } });
+ (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_NEG_Z, .uv = (vec2){ 1, 1 } });
vertex_darray_push(
cube.vertices,
(vertex){ .position = BACK_BOT_LEFT, .normal = VEC3_NEG_Z, .uv = (vec2){ 0, 1 } });
// front faces
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1} });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 0} });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 1 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 0 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_BOT_LEFT, .normal = VEC3_Z, .uv = (vec2){ 0, 1 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 1 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Z, .uv = (vec2){ 1, 0 } });
// top faces
vertex_darray_push(cube.vertices,
- (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 0 } });
+ (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 1 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } });
vertex_darray_push(cube.vertices,
- (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 0 } });
+ (vertex){ .position = BACK_TOP_LEFT, .normal = VEC3_Y, .uv = (vec2){ 0, 0 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 1 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_Y, .uv = (vec2){ 1, 0 } });
// bottom faces
vertex_darray_push(
@@ -88,18 +98,24 @@ static mesh prim_cube_mesh_create() {
(vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_NEG_Y, .uv = (vec2){ 0 } });
// right faces
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } });
- vertex_darray_push(cube.vertices,
- (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = BACK_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 0 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = BACK_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 1, 1 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_TOP_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 0 } });
+ vertex_darray_push(
+ cube.vertices,
+ (vertex){ .position = FRONT_BOT_RIGHT, .normal = VEC3_X, .uv = (vec2){ 0, 1 } });
// left faces
vertex_darray_push(
diff --git a/src/renderer/render.c b/src/renderer/render.c
index 9dc47c2..2b7f95c 100644
--- a/src/renderer/render.c
+++ b/src/renderer/render.c
@@ -1,3 +1,6 @@
+#include <stdlib.h>
+#include "mem.h"
+#include "transform_hierarchy.h"
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
@@ -58,6 +61,8 @@ bool renderer_init(renderer* ren) {
ren->blinn_phong =
shader_create_separate("assets/shaders/blinn_phong.vert", "assets/shaders/blinn_phong.frag");
+ default_material_init();
+
return true;
}
@@ -86,7 +91,31 @@ void default_material_init() {
texture_data_upload(&DEFAULT_MATERIAL.specular_texture);
}
-void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene) {
+typedef struct draw_ctx {
+ model_darray* models;
+ renderer* ren;
+ camera* cam;
+ scene* scene;
+} draw_ctx;
+bool draw_scene_node(transform_node* node, void* ctx_data) {
+ if (!node || !node->parent) return true;
+ draw_ctx* ctx = ctx_data;
+ model* m = &ctx->models->data[node->model.raw];
+ draw_model(ctx->ren, ctx->cam, m, &node->world_matrix_tf, ctx->scene);
+ return true;
+}
+
+void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera,
+ transform_hierarchy* tfh, scene* scene) {
+ draw_ctx* ctx = arena_alloc(frame, sizeof(draw_ctx));
+ ctx->models = models;
+ ctx->ren = ren;
+ ctx->cam = camera;
+ ctx->scene = scene;
+ transform_hierarchy_dfs(transform_hierarchy_root_node(tfh), draw_scene_node, true, ctx);
+}
+
+void draw_model(renderer* ren, camera* camera, model* model, mat4* model_tf, scene* scene) {
// TRACE("Drawing model: %s", model->name);
mat4 view;
mat4 proj;
@@ -109,11 +138,11 @@ void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene
}
// TRACE("Drawing mesh %d", i);
material* mat = &model->materials->data[m->material_index];
- draw_mesh(ren, m, tf, mat, &view, &proj);
+ draw_mesh(ren, m, model_tf, mat, &view, &proj);
}
}
-void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* view, mat4* proj) {
+void draw_mesh(renderer* ren, mesh* mesh, mat4* model_tf, material* mat, mat4* view, mat4* proj) {
shader lighting_shader = ren->blinn_phong;
// bind buffer
@@ -125,12 +154,12 @@ void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* vie
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));
+ // 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);
+ 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);
diff --git a/src/renderer/render.h b/src/renderer/render.h
index b6dad00..b687ad1 100644
--- a/src/renderer/render.h
+++ b/src/renderer/render.h
@@ -13,6 +13,7 @@
#include "camera.h"
#include "loaders.h"
#include "render_types.h"
+#include "transform_hierarchy.h"
// --- Lifecycle
/** @brief initialise the render system frontend */
@@ -32,8 +33,10 @@ void render_frame_draw(renderer* ren);
// --- 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);
+void draw_model(renderer* ren, camera* camera, model* model, mat4* tf, scene* scene);
+void draw_mesh(renderer* ren, mesh* mesh, mat4* tf, material* mat, mat4* view, mat4* proj);
+void draw_scene(arena* frame, model_darray* models, renderer* ren, camera* camera,
+ transform_hierarchy* tfh, scene* scene);
// ---
texture texture_data_load(const char* path, bool invert_y); // #frontend
diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h
index cbf9d36..6252dee 100644
--- a/src/renderer/render_types.h
+++ b/src/renderer/render_types.h
@@ -19,6 +19,7 @@ struct GLFWwindow;
#ifndef RESOURCE_HANDLE_DEFS
CORE_DEFINE_HANDLE(model_handle);
+#define ABSENT_MODEL_HANDLE 999999999
CORE_DEFINE_HANDLE(texture_handle);
#define RESOURCE_HANDLE_DEFS
#endif
diff --git a/src/resources/gltf.c b/src/resources/gltf.c
index b646f58..b269fcd 100644
--- a/src/resources/gltf.c
+++ b/src/resources/gltf.c
@@ -1 +1,516 @@
-// TODO: Port code from old repo \ No newline at end of file
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "core.h"
+#include "defines.h"
+#include "file.h"
+#include "loaders.h"
+#include "log.h"
+#include "path.h"
+#include "render.h"
+#include "render_types.h"
+#include "str.h"
+
+#define CGLTF_IMPLEMENTATION
+#include <cgltf.h>
+// TODO: Port code from old repo
+
+struct face {
+ cgltf_uint indices[3];
+};
+typedef struct face face;
+
+KITC_DECL_TYPED_ARRAY(vec3)
+KITC_DECL_TYPED_ARRAY(vec2)
+KITC_DECL_TYPED_ARRAY(u32)
+KITC_DECL_TYPED_ARRAY(face)
+
+bool model_load_gltf_str(const char *file_string, const char *filepath, str8 relative_path,
+ model *out_model, bool invert_textures_y);
+
+model_handle model_load_gltf(struct core *core, const char *path, bool invert_texture_y) {
+ size_t arena_size = 1024;
+ arena scratch = arena_create(malloc(arena_size), arena_size);
+
+ TRACE("Loading model at Path %s\n", path);
+ path_opt relative_path = path_parent(&scratch, path);
+ if (!relative_path.has_value) {
+ WARN("Couldnt get a relative path for the path to use for loading materials & textures later");
+ }
+ const char *file_string = string_from_file(path);
+
+ 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_gltf_str(file_string, path, relative_path.path, &model, invert_texture_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);
+
+ arena_free_all(&scratch);
+ arena_free_storage(&scratch);
+ return (model_handle){ .raw = index };
+}
+
+// TODO: Brainstorm how I can make this simpler and break it up into more testable pieces
+
+bool model_load_gltf_str(const char *file_string, const char *filepath, str8 relative_path,
+ model *out_model, bool invert_textures_y) {
+ TRACE("Load GLTF 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);
+
+ cgltf_options options = { 0 };
+ cgltf_data *data = NULL;
+ cgltf_result result = cgltf_parse_file(&options, filepath, &data);
+ if (result != cgltf_result_success) {
+ WARN("gltf load failed");
+ // TODO: cleanup arrays(allocate all from arena ?)
+ return false;
+ }
+
+ cgltf_load_buffers(&options, data, filepath);
+ DEBUG("loaded buffers");
+
+ // --- Materials
+ TRACE("Num materials %d", data->materials_count);
+ size_t num_materials = data->materials_count;
+ for (size_t m = 0; m < num_materials; m++) {
+ cgltf_material gltf_material = data->materials[m];
+ material our_material = DEFAULT_MATERIAL;
+
+ strcpy(our_material.name, gltf_material.name);
+
+ cgltf_pbr_metallic_roughness pbr = gltf_material.pbr_metallic_roughness;
+ if (gltf_material.has_pbr_metallic_roughness) {
+ // we will use base color texture like blinn phong
+ cgltf_texture_view diff_tex_view = pbr.base_color_texture;
+
+ char diffuse_map_path[1024];
+ snprintf(diffuse_map_path, sizeof(diffuse_map_path), "%s/%s", relative_path.buf,
+ diff_tex_view.texture->image->uri);
+
+ strcpy(our_material.diffuse_tex_path, diffuse_map_path);
+ texture diffuse_texture = texture_data_load(our_material.diffuse_tex_path, false);
+ texture_data_upload(&diffuse_texture);
+ our_material.diffuse_texture = diffuse_texture;
+
+ cgltf_texture_view specular_tex_view = pbr.metallic_roughness_texture;
+
+ char specular_map_path[1024];
+ snprintf(specular_map_path, sizeof(specular_map_path), "%s/%s", relative_path.buf,
+ specular_tex_view.texture->image->uri);
+
+ strcpy(our_material.specular_tex_path, specular_map_path);
+ texture specular_texture = texture_data_load(our_material.specular_tex_path, false);
+ texture_data_upload(&specular_texture);
+ our_material.specular_texture = specular_texture;
+ }
+
+ material_darray_push(out_model->materials, our_material);
+ }
+
+ // --- Meshes
+ TRACE("Num meshes %d", data->meshes_count);
+ size_t num_meshes = data->meshes_count;
+ for (size_t m = 0; m < num_meshes; m++) {
+ cgltf_primitive primitive = data->meshes[m].primitives[0];
+ DEBUG("Found %d attributes", primitive.attributes_count);
+
+ for (int a = 0; a < data->meshes[m].primitives[0].attributes_count; a++) {
+ cgltf_attribute attribute = data->meshes[m].primitives[0].attributes[a];
+ if (attribute.type == cgltf_attribute_type_position) {
+ TRACE("Load positions from accessor");
+
+ cgltf_accessor *accessor = attribute.data;
+ assert(accessor->component_type == cgltf_component_type_r_32f);
+ // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Vertex positions should be a vec3");
+
+ for (cgltf_size v = 0; v < accessor->count; ++v) {
+ vec3 pos;
+ cgltf_accessor_read_float(accessor, v, &pos.x, 3);
+ vec3_darray_push(tmp_positions, pos);
+ }
+
+ } else if (attribute.type == cgltf_attribute_type_normal) {
+ TRACE("Load normals from accessor");
+
+ cgltf_accessor *accessor = attribute.data;
+ assert(accessor->component_type == cgltf_component_type_r_32f);
+ // CASSERT_MSG(accessor->type == cgltf_type_vec3, "Normal vectors should be a vec3");
+
+ for (cgltf_size v = 0; v < accessor->count; ++v) {
+ vec3 pos;
+ cgltf_accessor_read_float(accessor, v, &pos.x, 3);
+ vec3_darray_push(tmp_normals, pos);
+ }
+
+ } else if (attribute.type == cgltf_attribute_type_texcoord) {
+ TRACE("Load texture coordinates from accessor");
+ cgltf_accessor *accessor = attribute.data;
+ assert(accessor->component_type == cgltf_component_type_r_32f);
+ // CASSERT_MSG(accessor->type == cgltf_type_vec2, "Texture coordinates should be a vec2");
+
+ for (cgltf_size v = 0; v < accessor->count; ++v) {
+ vec2 tex;
+ bool success = cgltf_accessor_read_float(accessor, v, &tex.x, 2);
+ if (!success) {
+ ERROR("Error loading tex coord");
+ }
+ vec2_darray_push(tmp_uvs, tex);
+ }
+ } else if (attribute.type == cgltf_attribute_type_joints) {
+ // TODO: handle joints
+ } else {
+ WARN("Unhandled cgltf_attribute_type: %s. skipping..", attribute.name);
+ }
+ }
+
+ mesh mesh;
+ mesh.vertices = vertex_darray_new(10);
+
+ cgltf_accessor *indices = primitive.indices;
+ if (primitive.indices > 0) {
+ mesh.has_indices = true;
+
+ mesh.indices = malloc(indices->count * sizeof(u32));
+ mesh.indices_len = indices->count;
+
+ // store indices
+ for (cgltf_size i = 0; i < indices->count; ++i) {
+ cgltf_uint ei;
+ cgltf_accessor_read_uint(indices, i, &ei, 1);
+ mesh.indices[i] = ei;
+ }
+
+ // fetch and store vertices for each index
+ for (cgltf_size i = 0; i < indices->count; ++i) {
+ vertex vert;
+ cgltf_uint index = mesh.indices[i];
+ vert.position = tmp_positions->data[index];
+ vert.normal = tmp_normals->data[index];
+ vert.uv = tmp_uvs->data[index];
+ vertex_darray_push(mesh.vertices, vert);
+ }
+ } else {
+ mesh.has_indices = false;
+ return false; // TODO
+ }
+
+ if (primitive.material != NULL) {
+ for (int i = 0; i < material_darray_len(out_model->materials); i++) {
+ if (strcmp(primitive.material->name, out_model->materials->data[i].name)) {
+ TRACE("Found material");
+ mesh.material_index = i;
+ break;
+ }
+ }
+ }
+
+ mesh_darray_push(out_model->meshes, mesh);
+ }
+
+ for (int i = 0; i < out_model->meshes->len; i++) {
+ u32 mat_idx = out_model->meshes->data[i].material_index;
+ printf("Mesh %d Mat index %d Mat name %s\n", i, mat_idx,
+ out_model->materials->data[mat_idx].name);
+ }
+ return true;
+}
+
+/*
+bool model_load_gltf(const char *path, model *out_model) {
+ TRACE("Load GLTF %s", path);
+
+ // Setup temp arrays
+ kitc_darray *tmp_positions = kitc_darray_new(sizeof(vec3), 1000);
+ kitc_darray *tmp_normals = kitc_darray_new(sizeof(vec3), 1000);
+ kitc_darray *tmp_uvs = kitc_darray_new(sizeof(vec2), 1000);
+
+ // may as well just init with max capacity as we're just gonna free at end of this function anyway
+ bh_material_darray *materials = bh_material_darray_new(MAX_MATERIALS);
+ CASSERT(materials->len == 0);
+
+ cgltf_options options = {0};
+ cgltf_data *data = NULL;
+ cgltf_result result = cgltf_parse_file(&options, path, &data);
+ if (result == cgltf_result_success) {
+ DEBUG("gltf loaded succesfully");
+
+ cgltf_load_buffers(&options, data, path);
+ DEBUG("loaded buffers");
+
+ // -- Load materials.
+ // Each mesh will be handed a material
+ TRACE("Num materials %d", data->materials_count);
+ out_model->num_materials = data->materials_count;
+
+ for (int m = 0; m < data->materials_count; m++) {
+ cgltf_material gltf_material = data->materials[m];
+ bh_material our_material = {0};
+
+ str8 name = str8_copy(gltf_material.name);
+ printf("Material name %s\n", name.buf);
+ our_material.name = name;
+
+ cgltf_pbr_metallic_roughness pbr = gltf_material.pbr_metallic_roughness;
+ if (gltf_material.has_pbr_metallic_roughness) {
+ // we will use base color texture like blinn phong
+ cgltf_texture_view diff_tex = pbr.base_color_texture;
+ strcpy(our_material.diffuse_tex_path, diff_tex.texture->image->uri);
+ }
+
+ bh_material_darray_push(materials, our_material);
+ }
+
+ // -- Load animations.
+ TRACE("Num animations %d", data->animations_count);
+ out_model->num_animations = data->animations_count;
+ for (int anim_idx = 0; anim_idx < data->animations_count; anim_idx++) {
+ cgltf_animation animation = data->animations[anim_idx];
+ animation_clip our_animation = {0};
+
+ // loop through each channel (track)
+ for (int c = 0; c < animation.channels_count; c++) {
+ // each channel (track) has a target and a sampler
+ // for the time being we assume the target is the model itself
+ cgltf_animation_channel channel = animation.channels[c];
+ animation_track our_track = {0};
+ our_track.interpolation = interpolation_fn_from_gltf(channel.sampler->interpolation);
+ our_track.property = anim_prop_from_gltf(channel.target_path);
+
+ // get the actual data out via the "accessor"
+ // input will be the times
+
+ // Keyframe times
+ size_t n_frames = channel.sampler->input->count;
+ our_track.num_keyframes = n_frames;
+ f32 *times = malloc(sizeof(f32) * n_frames);
+ our_track.keyframe_times = times;
+ CASSERT_MSG(channel.sampler->input->component_type == cgltf_component_type_r_32f,
+ "Expected animation sampler input component to be type f32 (keyframe times)");
+ cgltf_accessor_unpack_floats(channel.sampler->input, times, channel.sampler->input->count);
+
+ // printf("keyframe times[\n");
+ // for (int i = 0; i < n_frames; i++) {
+ // printf(" %f\n", times[i]);
+ // }
+ // printf("]\n");
+
+ // Data!
+ if (channel.target_path == cgltf_animation_path_type_rotation) {
+ CASSERT(channel.sampler->output->component_type == cgltf_component_type_r_32f);
+ CASSERT(channel.sampler->output->type == cgltf_type_vec4);
+ }
+
+ our_track.keyframes = malloc(sizeof(keyframe_data) * n_frames);
+ for (cgltf_size v = 0; v < channel.sampler->output->count; ++v) {
+ quat rot;
+ cgltf_accessor_read_float(channel.sampler->output, v, &rot.x, 4);
+ // vectors[v] = rot;
+ // printf("Quat %f %f %f %f\n", rot.x, rot.y, rot.z, rot.w);
+ our_track.keyframes[v].rotation = rot;
+ }
+
+ our_track.min_time = channel.sampler->input->min[0];
+ our_track.max_time = channel.sampler->input->max[0];
+
+ // printf("min time: %f max time %f\n", our_track.min_time, our_track.max_time);
+
+ animation_track_darray_push(&our_animation.tracks, our_track);
+ }
+
+ out_model->animations[anim_idx] = our_animation;
+ }
+
+ // Load meshes
+ TRACE("Num meshes %d", data->meshes_count);
+ out_model->num_meshes = data->meshes_count;
+
+ for (int m = 0; m < data->meshes_count; m++) {
+ // at the moment we only handle one primitives per mesh
+ // CASSERT(data->meshes[m].primitives_count == 1);
+
+ // Load vertex data from FIRST primitive only
+ cgltf_primitive primitive = data->meshes[m].primitives[0];
+ DEBUG("Found %d attributes", primitive.attributes_count);
+ for (int a = 0; a < data->meshes[m].primitives[0].attributes_count; a++) {
+ cgltf_attribute attribute = data->meshes[m].primitives[0].attributes[a];
+ if (attribute.type == cgltf_attribute_type_position) {
+ TRACE("Load positions from accessor");
+
+ cgltf_accessor *accessor = attribute.data;
+ CASSERT(accessor->component_type == cgltf_component_type_r_32f);
+ CASSERT_MSG(accessor->type == cgltf_type_vec3, "Vertex positions should be a vec3");
+
+ for (cgltf_size v = 0; v < accessor->count; ++v) {
+ vec3 pos;
+ cgltf_accessor_read_float(accessor, v, &pos.x, 3);
+ kitc_darray_push(tmp_positions, &pos);
+ }
+
+ } else if (attribute.type == cgltf_attribute_type_normal) {
+ TRACE("Load normals from accessor");
+
+ cgltf_accessor *accessor = attribute.data;
+ CASSERT(accessor->component_type == cgltf_component_type_r_32f);
+ CASSERT_MSG(accessor->type == cgltf_type_vec3, "Normal vectors should be a vec3");
+
+ for (cgltf_size v = 0; v < accessor->count; ++v) {
+ vec3 pos;
+ cgltf_accessor_read_float(accessor, v, &pos.x, 3);
+ kitc_darray_push(tmp_normals, &pos);
+ }
+
+ } else if (attribute.type == cgltf_attribute_type_texcoord) {
+ TRACE("Load texture coordinates from accessor");
+ cgltf_accessor *accessor = attribute.data;
+ CASSERT(accessor->component_type == cgltf_component_type_r_32f);
+ CASSERT_MSG(accessor->type == cgltf_type_vec2, "Texture coordinates should be a vec2");
+
+ for (cgltf_size v = 0; v < accessor->count; ++v) {
+ vec2 tex;
+ bool success = cgltf_accessor_read_float(accessor, v, &tex.x, 2);
+ if (!success) {
+ ERROR("Error loading tex coord");
+ }
+ kitc_darray_push(tmp_uvs, &tex);
+ }
+ } else if (attribute.type == cgltf_attribute_type_joints) {
+ // handle joints
+
+ } else {
+ WARN("Unhandled cgltf_attribute_type: %s. skipping..", attribute.name);
+ }
+ }
+
+ // Create mesh
+ mesh mesh;
+ mesh.vertices =
+ kitc_darray_new(sizeof(mesh_vertex), data->meshes[m].primitives[0].attributes_count);
+
+ // Flatten faces from indices if present otherwise push vertices verbatim
+ cgltf_accessor *indices = primitive.indices;
+ if (primitive.indices > 0) {
+ mesh.has_indices = true;
+
+ kitc_darray *element_indexes = kitc_darray_new(sizeof(cgltf_uint), indices->count);
+ TRACE("Indices count %ld\n", indices->count);
+ for (cgltf_size i = 0; i < indices->count; ++i) {
+ cgltf_uint ei;
+ cgltf_accessor_read_uint(indices, i, &ei, 1);
+ kitc_darray_push(element_indexes, &ei);
+ }
+
+ kitc_darray_iter indices_iter = kitc_darray_iter_new(element_indexes);
+ cgltf_uint *cur;
+ while ((cur = kitc_darray_iter_next(&indices_iter))) {
+ mesh_vertex vert;
+ memcpy(&vert.position, &((vec3 *)tmp_positions->data)[*cur], sizeof(vec3));
+ memcpy(&vert.normal, &((vec3 *)tmp_normals->data)[*cur], sizeof(vec3));
+ memcpy(&vert.tex_coord, &((vec2 *)tmp_uvs->data)[*cur], sizeof(vec2));
+ kitc_darray_push(mesh.vertices, &vert);
+ // mesh_vertex_debug_print(vert);
+ }
+ // printf("indices: %ld, positions: %ld\n", kitc_darray_len(element_indexes),
+ kitc_darray_free(element_indexes);
+ } else {
+ mesh.has_indices = false;
+
+ bool calc_normals = false;
+ if (kitc_darray_len(tmp_normals) == 0) {
+ TRACE("No normals data is present. Normals will be calculated for you.");
+ calc_normals = true;
+ }
+ for (int v = 0; v < kitc_darray_len(tmp_positions); v++) {
+ mesh_vertex vert;
+ memcpy(&vert.position, &((vec3 *)tmp_positions->data)[v], sizeof(vec3));
+ if (!calc_normals) {
+ memcpy(&vert.normal, &((vec3 *)tmp_normals->data)[v], sizeof(vec3));
+ }
+ memcpy(&vert.tex_coord, &((vec2 *)tmp_uvs->data)[v], sizeof(vec2));
+ kitc_darray_push(mesh.vertices, &vert);
+ }
+
+ if (calc_normals) {
+ if (mesh.has_indices) {
+ // generate_normals_nonindexed(mesh.vertices);
+ } else {
+ generate_normals_nonindexed(mesh.vertices);
+ }
+ }
+ }
+
+ // Material
+ if (primitive.material != NULL) {
+ for (int i = 0; i < bh_material_darray_len(materials); i++) {
+ if (strcmp(primitive.material->name, cstr(materials->data->name))) {
+ TRACE("Found material");
+ mesh.material_index = i;
+ break;
+ }
+ }
+ }
+
+ // mesh.material_index = 0; // TODO: make sure DEFAULT_MATERIAL is added at material index 0
+ // TODO: material handling
+ mesh.material_index = bh_material_darray_len(materials) - 1;
+
+ calc_mesh_bounding_box(&mesh);
+ // out_model->meshes.data[m] = mesh;
+ mesh_darray_push(&out_model->meshes, mesh);
+
+ kitc_darray_clear(tmp_positions);
+ kitc_darray_clear(tmp_normals);
+ kitc_darray_clear(tmp_uvs);
+ }
+ // End Load meshes
+
+ // Load animations
+ DEBUG("Num animations %d", data->animations_count);
+ out_model->num_animations = data->animations_count;
+
+ // End Load animations
+
+ cgltf_free(data);
+ } else {
+ ERROR("Load failed");
+ kitc_darray_free(tmp_positions);
+ kitc_darray_free(tmp_normals);
+ kitc_darray_free(tmp_uvs);
+ return false;
+ }
+
+ for (int i = 0; i < materials->len; i++) {
+ out_model->materials[i] = materials->data[i];
+ }
+
+ calc_model_bounding_box(out_model);
+
+ DEBUG("Num meshes %d", out_model->num_meshes);
+ DEBUG("Num materials %d", out_model->num_materials);
+ DEBUG("Num animations %d", out_model->num_animations);
+
+ CASSERT(out_model->num_materials == 1);
+
+ kitc_darray_free(tmp_positions);
+ kitc_darray_free(tmp_normals);
+ kitc_darray_free(tmp_uvs);
+ bh_material_darray_free(materials);
+
+ TRACE("Finished loading GLTF");
+ return true;
+}
+*/ \ No newline at end of file
diff --git a/src/std/mem.c b/src/std/mem.c
index d7c0f4c..00f9c39 100644
--- a/src/std/mem.c
+++ b/src/std/mem.c
@@ -11,7 +11,7 @@
void* arena_alloc_align(arena* a, size_t size, size_t align) {
ptrdiff_t padding = -(uintptr_t)a->curr & (align - 1);
ptrdiff_t available = a->end - a->curr - padding;
- TRACE("Padding %td available %td", padding, available);
+ // TRACE("Padding %td available %td", padding, available);
if (available < 0 || (ptrdiff_t)size > available) {
ERROR_EXIT("Arena ran out of memory\n");
}
diff --git a/src/systems/physics.c b/src/systems/physics.c
new file mode 100644
index 0000000..299c0c1
--- /dev/null
+++ b/src/systems/physics.c
@@ -0,0 +1 @@
+#include "physics.h" \ No newline at end of file
diff --git a/src/systems/physics.h b/src/systems/physics.h
new file mode 100644
index 0000000..5c96c6e
--- /dev/null
+++ b/src/systems/physics.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "maths_types.h"
+
+// 'system' means that it gets called per frame
+
+typedef struct physics_settings {
+ f32 gravity_strength;
+} physics_settings;
+
+enum collider_type {
+ cuboid_collider,
+ sphere_collider,
+};
+
+/** @brief generic collider structure */
+typedef struct physics_collider {
+ u64 id; // ? Replace with handle?
+ enum collider_type shape;
+ transform transform;
+ u8 layer;
+ bool on_ground;
+} physics_collider;
+
+typedef struct physics_world {
+ physics_settings settings;
+} physics_world;
+
+physics_world physics_init(physics_settings settings);
+void physics_shutdown(physics_world* phys_world);
+
+/** @brief perform one or more simulation steps */
+void physics_system_update(physics_world* phys_world, f64 deltatime); \ No newline at end of file
diff --git a/src/transform_hierarchy.c b/src/transform_hierarchy.c
new file mode 100644
index 0000000..f1c859a
--- /dev/null
+++ b/src/transform_hierarchy.c
@@ -0,0 +1,184 @@
+
+/**
+ * @file transform_hierarchy.h
+ */
+#pragma once
+#include "transform_hierarchy.h"
+#include <stdlib.h>
+#include <string.h>
+
+#include "core.h"
+#include "log.h"
+#include "maths.h"
+#include "maths_types.h"
+#include "render_types.h"
+
+struct transform_hierarchy {
+ transform_node root;
+};
+
+transform_hierarchy* transform_hierarchy_create() {
+ transform_hierarchy* tfh = malloc(sizeof(struct transform_hierarchy));
+
+ tfh->root = (transform_node){ .model = { ABSENT_MODEL_HANDLE },
+ .tf = TRANSFORM_DEFAULT,
+ .local_matrix_tf = mat4_ident(),
+ .world_matrix_tf = mat4_ident(),
+ .parent = NULL,
+ .children = { 0 },
+ .n_children = 0,
+ .tfh = tfh };
+ return tfh;
+}
+
+bool free_node(transform_node* node, void* _ctx_data) {
+ if (!node) return true; // leaf node
+ if (node == &node->tfh->root) {
+ WARN("You can't free the root node!");
+ return false;
+ }
+
+ printf("Freed node\n");
+ free(node);
+ return true;
+}
+
+void transform_hierarchy_free(transform_hierarchy* tfh) {
+ transform_hierarchy_dfs(&tfh->root, free_node, false, NULL);
+ free(tfh);
+}
+
+transform_node* transform_hierarchy_root_node(transform_hierarchy* tfh) { return &tfh->root; }
+
+transform_node* transform_hierarchy_add_node(transform_node* parent, model_handle model,
+ transform tf) {
+ if (!parent) {
+ WARN("You tried to add a node to a bad parent (NULL?)");
+ return NULL;
+ }
+ transform_node* node = malloc(sizeof(transform_node));
+ node->model = model;
+ node->tf = tf;
+ node->local_matrix_tf = mat4_ident();
+ node->world_matrix_tf = mat4_ident();
+ node->parent = parent;
+ memset(node->children, 0, sizeof(node->children));
+ node->n_children = 0;
+ node->tfh = parent->tfh;
+
+ // push into parent's children array
+ u32 next_index = parent->n_children;
+ if (next_index == MAX_TF_NODE_CHILDREN) {
+ ERROR("This transform hierarchy node already has MAX children. Dropping.");
+ free(node);
+ } else {
+ parent->children[next_index] = node;
+ parent->n_children++;
+ }
+
+ return node;
+}
+
+void transform_hierarchy_delete_node(transform_node* node) {
+ // delete all children
+ for (u32 i = 0; i < node->n_children; i++) {
+ transform_node* child = node->children[i];
+ transform_hierarchy_dfs(child, free_node, false, NULL);
+ }
+
+ if (node->parent) {
+ for (u32 i = 0; i < node->parent->n_children; i++) {
+ transform_node* child = node->parent->children[i];
+ if (child == node) {
+ node->parent->children[i] = NULL; // HACK: this will leave behind empty slots in the
+ // children array of the parent. oh well.
+ }
+ }
+ }
+
+ free(node);
+}
+
+void transform_hierarchy_dfs(transform_node* start_node,
+ bool (*visit_node)(transform_node* node, void* ctx_data),
+ bool is_pre_order, void* ctx_data) {
+ if (!start_node) return;
+
+ bool continue_traversal = true;
+ if (is_pre_order) {
+ continue_traversal = visit_node(start_node, ctx_data);
+ }
+
+ if (continue_traversal) {
+ for (u32 i = 0; i < start_node->n_children; i++) {
+ transform_node* child = start_node->children[i];
+ transform_hierarchy_dfs(child, visit_node, is_pre_order, ctx_data);
+ }
+ }
+
+ if (!is_pre_order) {
+ // post-order
+ visit_node(start_node, ctx_data);
+ }
+}
+
+// Update matrix for the current node
+bool update_matrix(transform_node* node, void* _ctx_data) {
+ if (!node) return true; // leaf node
+
+ if (node->parent && node->parent->tf.is_dirty) {
+ node->tf.is_dirty = true;
+ }
+
+ if (node->tf.is_dirty) {
+ // invalidates children
+ mat4 updated_local_transform = transform_to_mat(&node->tf);
+ node->local_matrix_tf = updated_local_transform;
+ if (node->parent) {
+ mat4 updated_world_transform =
+ mat4_mult(node->parent->world_matrix_tf, updated_local_transform);
+ node->world_matrix_tf = updated_world_transform;
+ }
+ }
+
+ return true;
+}
+
+void transform_hierarchy_propagate_transforms(transform_hierarchy* tfh) {
+ // kickoff traversal
+ transform_hierarchy_dfs(&tfh->root, update_matrix, false, NULL);
+}
+
+struct print_ctx {
+ core* core;
+ u32 indentation_lvl;
+};
+
+bool print_node(transform_node* node, void* ctx_data) {
+ struct print_ctx* ctx = (struct print_ctx*)ctx_data;
+
+ if (!node) return true;
+ if (!node->parent) {
+ printf("Root Node\n");
+ ctx->indentation_lvl++;
+ return true;
+ }
+
+ // Grab the model
+ model m = ctx->core->models->data[node->model.raw];
+ for (int i = 0; i < ctx->indentation_lvl; i++) {
+ printf(" ");
+ }
+ printf("Node %s\n", m.name.buf);
+ ctx->indentation_lvl++;
+
+ return true;
+}
+
+void transform_hierarchy_debug_print(transform_node* start_node, core* core) {
+ struct print_ctx* ctx = malloc(sizeof(struct print_ctx));
+ ctx->core = core;
+ ctx->indentation_lvl = 0;
+ transform_hierarchy_dfs(start_node, print_node, true, (void*)ctx);
+ free(ctx);
+} \ No newline at end of file
diff --git a/src/transform_hierarchy.h b/src/transform_hierarchy.h
new file mode 100644
index 0000000..af77ee1
--- /dev/null
+++ b/src/transform_hierarchy.h
@@ -0,0 +1,76 @@
+/**
+ * @file transform_hierarchy.h
+ */
+#pragma once
+
+#include "maths_types.h"
+#include "render_types.h"
+
+#define MAX_TF_NODE_CHILDREN \
+ 32 /** TEMP: Make it simpler to manage children in `transform_node`s */
+
+typedef struct transform_hierarchy transform_hierarchy;
+
+struct transform_node {
+ model_handle model; /** A handle back to what model this node represents */
+ transform tf;
+ mat4 local_matrix_tf; /** cached local affine transform */
+ mat4 world_matrix_tf; /** cached world-space affine transform */
+
+ struct transform_node* parent;
+ struct transform_node* children[MAX_TF_NODE_CHILDREN];
+ u32 n_children;
+ struct transform_hierarchy* tfh;
+};
+typedef struct transform_node transform_node;
+
+// --- Lifecycle
+
+/** @brief Allocates and returns an empty transform hierarchy with a root node */
+transform_hierarchy* transform_hierarchy_create();
+
+/**
+ * @brief recursively frees all the children and then finally itself
+ * @note in the future we can use an object pool for the nodes
+ */
+void transform_hierarchy_free(transform_hierarchy* tfh);
+
+// --- Main usecase
+
+/** @brief Updates matrices of any invalidated nodes based on the `is_dirty` flag inside `transform`
+ */
+void transform_hierarchy_propagate_transforms(transform_hierarchy* tfh);
+
+// --- Queries
+
+/** @brief Get a pointer to the root node */
+transform_node* transform_hierarchy_root_node(transform_hierarchy* tfh);
+
+// --- Mutations
+transform_node* transform_hierarchy_add_node(transform_node* parent, model_handle model,
+ transform tf);
+void transform_hierarchy_delete_node(transform_node* node);
+
+// --- Traversal
+
+/**
+ * @brief Perform a depth-first search traversal starting from `start_node`.
+ * @param start_node The starting node of the traversal.
+ * @param visit_node The function to call for each node visited. The callback should return false to
+ stop the traversal early.
+ * @param is_pre_order Indicates whether to do pre-order or post-order traversal i.e. when to call
+ the `visit_node` function.
+ * @param ctx_data An optional pointer to data that is be passed on each call to `visit_node`. Can
+ be used to carry additional information or context.
+ *
+ * @note The main use-cases are:
+ 1. traversing the whole tree to update cached 4x4 affine transform matrices (post-order)
+ 2. freeing child nodes after deleting a node in the tree (post-order)
+ 3. debug pretty printing the whole tree (post-order)
+ */
+void transform_hierarchy_dfs(transform_node* start_node,
+ bool (*visit_node)(transform_node* node, void* ctx_data),
+ bool is_pre_order, void* ctx_data);
+
+struct core;
+void transform_hierarchy_debug_print(transform_node* start_node, struct core* core); \ No newline at end of file