From 90dd96cbc9b09cdc75977b115a9b61fa7547baef Mon Sep 17 00:00:00 2001 From: Omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sun, 25 Feb 2024 17:35:27 +1100 Subject: boilerplate for more examples --- xmake.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'xmake.lua') diff --git a/xmake.lua b/xmake.lua index e6b77c2..e43d2ff 100644 --- a/xmake.lua +++ b/xmake.lua @@ -94,4 +94,18 @@ target("std") set_group("examples") add_deps("core_static") add_files("examples/standard_lib/ex_std.c") + set_rundir("$(projectdir)") + +target("obj_loading") + set_kind("binary") + set_group("examples") + add_deps("core_static") + add_files("examples/obj_loading/ex_obj_loading.c") + set_rundir("$(projectdir)") + +target("demo") + set_kind("binary") + set_group("examples") + add_deps("core_static") + add_files("examples/demo/demo.c") set_rundir("$(projectdir)") \ No newline at end of file -- cgit v1.2.3-70-g09d2 From b459aa07581a948ee252d7e543c10675b602f146 Mon Sep 17 00:00:00 2001 From: Omniscient Date: Tue, 5 Mar 2024 20:50:37 +1100 Subject: wip: port obj loader --- src/renderer/render.c | 78 ++++++++++- src/renderer/render.h | 4 +- src/resources/obj.c | 359 +++++++++++++++++++++++++++++++++++++++++++++++++- xmake.lua | 3 +- 4 files changed, 440 insertions(+), 4 deletions(-) (limited to 'xmake.lua') diff --git a/src/renderer/render.c b/src/renderer/render.c index 4e9ad89..f12954f 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -1,10 +1,19 @@ +#define STB_IMAGE_IMPLEMENTATION +#include + +#define STB_TRUETYPE_IMPLEMENTATION +#include + #include "render.h" +#include #include #include "log.h" #include "render_backend.h" +material DEFAULT_MATERIAL = { 0 }; + bool renderer_init(renderer* ren) { INFO("Renderer init"); @@ -39,4 +48,71 @@ void render_frame_end(renderer* ren) { // present frame glfwSwapBuffers(ren->window); glfwPollEvents(); -} \ No newline at end of file +} + +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); +} + +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) { + 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 +} diff --git a/src/renderer/render.h b/src/renderer/render.h index c89c364..c4e0dd6 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -13,4 +13,6 @@ void renderer_shutdown(renderer* ren); void render_frame_begin(renderer* ren); void render_frame_end(renderer* ren); -// --- \ No newline at end of file +// --- +texture texture_data_load(const char* path, bool invert_y); // #frontend +void texture_data_upload(texture* tex); // #backend diff --git a/src/resources/obj.c b/src/resources/obj.c index b646f58..9e12f79 100644 --- a/src/resources/obj.c +++ b/src/resources/obj.c @@ -1 +1,358 @@ -// TODO: Port code from old repo \ No newline at end of file +/** + * @file obj.c + * @brief Wavefront OBJ loader. + * @copyright Copyright (c) 2024 + */ +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "darray.h" +#include "log.h" +#include "maths.h" +#include "render.h" +#include "render_types.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, material_darray *materials); +bool model_load_obj_str(const char *file_string, model *out_model, bool invert_textures_y); + +model_handle model_load_obj(core *core, const char *path, bool invert_textures_y) { + const char *file_string = string_from_file(path); + model model; + + bool success = model_load_obj_str(file_string, &model, invert_textures_y); + + if (!success) { + exit(-1); // FIXME: Return some sort of result type? + } + + 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, 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); + + mesh_darray *meshes = mesh_darray_new(1); + material_darray *materials = material_darray_new(1); + + // 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(meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces, 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(meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces, 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 path[1024] = "assets/"; + sscanf(pch + offset, "%s", path + 7); + if (!load_material_lib(path, materials)) { + ERROR("couldnt load material lib"); + } + } 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(meshes, tmp_positions, tmp_normals, tmp_uvs, tmp_faces, 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"); + + memset(out_model, 0, sizeof(model)); + if (mesh_darray_len(meshes) > 256) { + printf("num meshes: %ld\n", mesh_darray_len(meshes)); + } + for (int i = 0; i < mesh_darray_len(meshes); i++) { + mesh_darray_push(&out_model->meshes, ((mesh *)meshes->data)[i]); + // TODO: bounding box calculation for each mesh + } + // TODO: bounding box calculation for model + + for (int i = 0; i < material_darray_len(materials); i++) { + material_darray_push(&out_model->materials, ((material *)materials->data)[i]); + } + + 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))) { + printf("Material name %s vs %s \n", cur_material->name, current_material_name); + 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, 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", ¤t_material.ambient_colour.x, + ¤t_material.ambient_colour.y, ¤t_material.ambient_colour.z); + } else if (strcmp(line_header, "Kd") == 0) { + // diffuse + sscanf(pch + offset, "%f %f %f", ¤t_material.diffuse.x, ¤t_material.diffuse.y, + ¤t_material.diffuse.z); + } else if (strcmp(line_header, "Ks") == 0) { + // specular + sscanf(pch + offset, "%f %f %f", ¤t_material.specular.x, ¤t_material.specular.y, + ¤t_material.specular.z); + } else if (strcmp(line_header, "Ns") == 0) { + // specular exponent + sscanf(pch + offset, "%f", ¤t_material.spec_exponent); + } else if (strcmp(line_header, "map_Kd") == 0) { + char diffuse_map_path[1024] = "assets/"; + sscanf(pch + offset, "%s", diffuse_map_path + 7); + 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(¤t_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); + // -------------- + 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(¤t_material.specular_texture); + // -------------- + } else if (strcmp(line_header, "map_Bump") == 0) { + // TODO + } + + pch = strtok_r(NULL, "\n", &saveptr); + } + + // 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/xmake.lua b/xmake.lua index e43d2ff..39bcab0 100644 --- a/xmake.lua +++ b/xmake.lua @@ -69,6 +69,7 @@ target("core_config") add_files("src/platform/*.c") add_files("src/renderer/*.c") add_files("src/renderer/backends/*.c") + add_files("src/resources/*.c") add_files("src/std/*.c") add_files("src/std/containers/*.c") add_files("src/systems/*.c") @@ -108,4 +109,4 @@ target("demo") set_group("examples") add_deps("core_static") add_files("examples/demo/demo.c") - set_rundir("$(projectdir)") \ No newline at end of file + set_rundir("$(projectdir)") -- cgit v1.2.3-70-g09d2 From 3854bef8b853a369c4fc3a39abff5e2e9cd77625 Mon Sep 17 00:00:00 2001 From: Omniscient Date: Tue, 5 Mar 2024 21:27:34 +1100 Subject: why is it crashing on file loading now? --- examples/obj_loading/ex_obj_loading.c | 3 +++ src/platform/file.c | 6 +++++- src/renderer/render.h | 1 + src/resources/loaders.h | 3 +-- src/resources/obj.c | 4 +++- src/std/str.h | 7 ++++++- xmake.lua | 1 + 7 files changed, 20 insertions(+), 5 deletions(-) (limited to 'xmake.lua') diff --git a/examples/obj_loading/ex_obj_loading.c b/examples/obj_loading/ex_obj_loading.c index 3b2354a..87aeb2d 100644 --- a/examples/obj_loading/ex_obj_loading.c +++ b/examples/obj_loading/ex_obj_loading.c @@ -6,6 +6,9 @@ int main() { core* core = core_bringup(); + // Set up our scene + model_handle cube = model_load_obj(core, "assets/models/obj/cube/cube.obj", true); + // Main loop while (!glfwWindowShouldClose(core->renderer.window)) { input_update(&core->input); diff --git a/src/platform/file.c b/src/platform/file.c index 44aa9d0..4eeee46 100644 --- a/src/platform/file.c +++ b/src/platform/file.c @@ -12,6 +12,7 @@ const char *string_from_file(const char *path) { FILE *f = fopen(path, "rb"); + printf("Hello\n"); if (f == NULL) { ERROR("Error reading file: %s. errno: %d", path, errno); return NULL; @@ -20,6 +21,7 @@ const char *string_from_file(const char *path) { ERROR("Error reading file: %s. errno: %d", path, errno); return NULL; } + printf("Hello2\n"); fseek(f, 0, SEEK_END); long fsize = ftell(f); rewind(f); @@ -29,6 +31,8 @@ const char *string_from_file(const char *path) { fclose(f); string[fsize] = '\0'; + printf("Hello3\n"); + printf("Hello %s\n", string); return string; } @@ -60,4 +64,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/renderer/render.h b/src/renderer/render.h index c4e0dd6..d1de515 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -1,6 +1,7 @@ #pragma once #include "render_types.h" +#include "loaders.h" // --- Lifecycle /** @brief initialise the render system frontend */ diff --git a/src/resources/loaders.h b/src/resources/loaders.h index ba38ec4..8904655 100644 --- a/src/resources/loaders.h +++ b/src/resources/loaders.h @@ -3,7 +3,6 @@ #include "defines.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 9e12f79..05aa96e 100644 --- a/src/resources/obj.c +++ b/src/resources/obj.c @@ -36,7 +36,9 @@ bool load_material_lib(const char *path, material_darray *materials); bool model_load_obj_str(const char *file_string, model *out_model, bool invert_textures_y); model_handle model_load_obj(core *core, const char *path, bool invert_textures_y) { + printf("Path %s\n", path); const char *file_string = string_from_file(path); + printf("Loaded file %s\n", file_string); model model; bool success = model_load_obj_str(file_string, &model, invert_textures_y); @@ -89,7 +91,7 @@ bool model_load_obj_str(const char *file_string, model *out_model, bool invert_t } 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); + /* printf("header: %s, offset : %d res: %d\n",line_header, offset, res); */ if (res != 1) { break; } diff --git a/src/std/str.h b/src/std/str.h index f6f8820..9f96430 100644 --- a/src/std/str.h +++ b/src/std/str.h @@ -70,4 +70,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; +} diff --git a/xmake.lua b/xmake.lua index 39bcab0..450b97d 100644 --- a/xmake.lua +++ b/xmake.lua @@ -60,6 +60,7 @@ target("core_config") add_includedirs("src/platform/", {public = true}) add_includedirs("src/renderer/", {public = true}) add_includedirs("src/renderer/backends/", {public = true}) + add_includedirs("src/resources/", {public = true}) add_includedirs("src/std/", {public = true}) add_includedirs("src/std/containers", {public = true}) add_includedirs("src/systems/", {public = true}) -- cgit v1.2.3-70-g09d2 From d92d5231ae094ae79d95463d54275a3527cf1c2d Mon Sep 17 00:00:00 2001 From: omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Tue, 5 Mar 2024 21:41:08 +1100 Subject: get us running on Mac again with our vendored GLFW --- src/empty.c | 5 +++++ src/resources/obj.c | 1 + src/std/str.h | 2 ++ xmake.lua | 5 +++++ 4 files changed, 13 insertions(+) create mode 100644 src/empty.c (limited to 'xmake.lua') diff --git a/src/empty.c b/src/empty.c new file mode 100644 index 0000000..d58a94f --- /dev/null +++ b/src/empty.c @@ -0,0 +1,5 @@ +// 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/resources/obj.c b/src/resources/obj.c index 05aa96e..d56e212 100644 --- a/src/resources/obj.c +++ b/src/resources/obj.c @@ -11,6 +11,7 @@ #include #include "core.h" +#include "file.h" #include "darray.h" #include "log.h" #include "maths.h" diff --git a/src/std/str.h b/src/std/str.h index 9d30cba..518d508 100644 --- a/src/std/str.h +++ b/src/std/str.h @@ -10,6 +10,8 @@ */ #pragma once +#include + #include "defines.h" #include "mem.h" diff --git a/xmake.lua b/xmake.lua index 450b97d..10d6a80 100644 --- a/xmake.lua +++ b/xmake.lua @@ -24,6 +24,9 @@ elseif is_plat("windows") then add_defines("CEL_PLATFORM_WINDOWS") elseif is_plat("macosx") then add_defines("CEL_PLATFORM_MAC") + add_frameworks("Cocoa", "IOKit", "CoreVideo", "OpenGL") + set_runenv("MTL_DEBUG_LAYER", "1") + -- add_syslinks("GL") end -- Compile GLFW from source @@ -79,6 +82,8 @@ target("core_config") target("core_static") set_kind("static") add_deps("core_config") -- inherit common configurations + set_policy("build.merge_archive", true) + add_files("src/empty.c") target("core_shared") set_kind("shared") -- cgit v1.2.3-70-g09d2 From a1bd74db194880267a73e4d72d011e9a2b8ac0bd Mon Sep 17 00:00:00 2001 From: omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 9 Mar 2024 16:33:49 +1100 Subject: rename target to obj --- xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'xmake.lua') diff --git a/xmake.lua b/xmake.lua index 10d6a80..18db07c 100644 --- a/xmake.lua +++ b/xmake.lua @@ -103,7 +103,7 @@ target("std") add_files("examples/standard_lib/ex_std.c") set_rundir("$(projectdir)") -target("obj_loading") +target("obj") set_kind("binary") set_group("examples") add_deps("core_static") -- cgit v1.2.3-70-g09d2 From f33792a01fa64126e1c481a77b49871dcd413fa1 Mon Sep 17 00:00:00 2001 From: omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 9 Mar 2024 16:46:29 +1100 Subject: pull sources out into a local variable so we can share them easily --- xmake.lua | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'xmake.lua') diff --git a/xmake.lua b/xmake.lua index 18db07c..1b2683f 100644 --- a/xmake.lua +++ b/xmake.lua @@ -47,6 +47,19 @@ package_end() add_requires("local_glfw") +local core_sources = { + "deps/glad/src/glad.c", + "src/*.c", + "src/logos/*.c", + "src/platform/*.c", + "src/renderer/*.c", + "src/renderer/backends/*.c", + "src/resources/*.c", + "src/std/*.c", + "src/std/containers/*.c", + "src/systems/*.c", +} + -- common configuration for both static and shared libraries target("core_config") set_kind("static") -- kind is required but you can ignore it since it's just for common settings @@ -67,27 +80,19 @@ target("core_config") add_includedirs("src/std/", {public = true}) add_includedirs("src/std/containers", {public = true}) add_includedirs("src/systems/", {public = true}) - add_files("deps/glad/src/glad.c") - add_files("src/*.c") - add_files("src/logos/*.c") - add_files("src/platform/*.c") - add_files("src/renderer/*.c") - add_files("src/renderer/backends/*.c") - add_files("src/resources/*.c") - add_files("src/std/*.c") - add_files("src/std/containers/*.c") - add_files("src/systems/*.c") + add_files("src/empty.c") -- for some reason we need this on Mac so it doesnt call 'ar' with no files and error set_default(false) -- prevents standalone building of this target target("core_static") set_kind("static") add_deps("core_config") -- inherit common configurations set_policy("build.merge_archive", true) - add_files("src/empty.c") + add_files(core_sources) target("core_shared") set_kind("shared") add_deps("core_config") -- inherit common configurations + add_files(core_sources) target("main_loop") set_kind("binary") -- cgit v1.2.3-70-g09d2 From 8019f49a4b0d657f882c148ae6582da78a7d2262 Mon Sep 17 00:00:00 2001 From: omnisci3nce <17525998+omnisci3nce@users.noreply.github.com> Date: Sat, 9 Mar 2024 17:21:56 +1100 Subject: add pthreads link flag --- xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'xmake.lua') diff --git a/xmake.lua b/xmake.lua index 1b2683f..15159ef 100644 --- a/xmake.lua +++ b/xmake.lua @@ -19,7 +19,7 @@ end -- Platform defines and system packages if is_plat("linux") then add_defines("CEL_PLATFORM_LINUX") - add_syslinks("dl", "X11") + add_syslinks("dl", "X11", "pthread") elseif is_plat("windows") then add_defines("CEL_PLATFORM_WINDOWS") elseif is_plat("macosx") then -- cgit v1.2.3-70-g09d2 From 6c581ff56dfcc22c25538e305e58efd967dd640a Mon Sep 17 00:00:00 2001 From: omniscient <17525998+omnisci3nce@users.noreply.github.com> Date: Thu, 14 Mar 2024 21:54:55 +1100 Subject: lights, camera, action --- assets/shaders/blinn_phong.frag | 76 ++++++++++++++++++++++++++++++++++- examples/obj_loading/ex_obj_loading.c | 39 ++++++++++++++++-- src/renderer/render.c | 48 +++++++++++++++++++--- src/renderer/render.h | 2 +- src/renderer/render_types.h | 34 +++++++++++++++- src/resources/obj.c | 23 +++++++---- xmake.lua | 3 ++ 7 files changed, 205 insertions(+), 20 deletions(-) (limited to 'xmake.lua') diff --git a/assets/shaders/blinn_phong.frag b/assets/shaders/blinn_phong.frag index a284948..095b19a 100644 --- a/assets/shaders/blinn_phong.frag +++ b/assets/shaders/blinn_phong.frag @@ -9,6 +9,25 @@ struct Material { float shininess; }; +struct DirLight { + vec3 direction; + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +struct PointLight { + vec3 position; + // fall off properties + float constant; + float linear; + float quadratic; + + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + in VS_OUT { vec3 FragPos; vec3 Normal; @@ -16,6 +35,61 @@ in VS_OUT { vec4 FragPosLightSpace; } fs_in; +// --- Uniforms +#define NUM_POINT_LIGHTS 4 +uniform vec3 viewPos; +uniform Material material; +uniform DirLight dirLight; +uniform PointLight pointLights[NUM_POINT_LIGHTS]; + +// --- Function prototypes +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir); +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir); + void main() { - FragColor = vec4(1.0); + vec3 norm = normalize(fs_in.Normal); + vec3 viewDir = normalize(viewPos - fs_in.FragPos); + + vec3 result = CalcDirLight(dirLight, norm, viewDir); + for (int i = 0; i < 4; i++) { + result += CalcPointLight(pointLights[i], norm, fs_in.FragPos, viewDir); + } + + FragColor = vec4(result, 1.0); +} + +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir) +{ + vec3 lightDir = normalize(-light.direction); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // combine result + vec3 ambient = light.ambient * vec3(texture(material.diffuse, fs_in.TexCoords)); + vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, fs_in.TexCoords)); + vec3 specular = light.specular * spec * vec3(texture(material.specular, fs_in.TexCoords)); + return (ambient + diffuse + specular); +} + +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) +{ + vec3 lightDir = normalize(light.position - fragPos); + // diffuse + float diff = max(dot(normal, lightDir), 0.0); + // specular + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + // attentuation + float distance = length(light.position - fragPos); + float attentuation = 1.0 / (light.constant + light.linear * distance * light.quadratic * (distance * distance)); + // result + vec3 ambient = light.ambient * vec3(texture(material.diffuse, fs_in.TexCoords)); + vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, fs_in.TexCoords)); + vec3 specular = light.specular * spec * vec3(texture(material.specular, fs_in.TexCoords)); + ambient *= attentuation; + diffuse *= attentuation; + specular *= attentuation; + return (ambient + diffuse + specular); } \ No newline at end of file diff --git a/examples/obj_loading/ex_obj_loading.c b/examples/obj_loading/ex_obj_loading.c index bdadd7e..02ed606 100644 --- a/examples/obj_loading/ex_obj_loading.c +++ b/examples/obj_loading/ex_obj_loading.c @@ -1,4 +1,5 @@ #include +#include #include "camera.h" #include "core.h" @@ -7,6 +8,14 @@ #include "render.h" #include "render_types.h" +const vec3 pointlight_positions[4] = { + {0.7, 0.2, 2.0}, + {2.3, -3.3, -4.0}, + {-4.0, 2.0, -12.0}, + {0.0, 0.0, -3.0}, +}; +point_light point_lights[4]; + int main() { core* core = core_bringup(); @@ -22,6 +31,30 @@ int main() { vec3 camera_pos = vec3(3., 4., 10.); vec3 camera_front = vec3_normalise(vec3_negate(camera_pos)); camera cam = camera_create(camera_pos, camera_front, VEC3_Y, deg_to_rad(45.0)); + // 4. create lights + + // directional (sun) light setup + directional_light dir_light = {.direction = (vec3){-0.2, -1.0, -0.3}, + .ambient = (vec3){0.2, 0.2, 0.2}, + .diffuse = (vec3){0.5, 0.5, 0.5}, + .specular = (vec3){1.0, 1.0, 1.0}}; + // point lights setup + for (int i = 0; i < 4; i++) { + point_lights[i].position = pointlight_positions[i]; + point_lights[i].ambient = (vec3){0.05, 0.05, 0.05}; + point_lights[i].diffuse = (vec3){0.8, 0.8, 0.8}; + point_lights[i].specular = (vec3){1.0, 1.0, 1.0}; + point_lights[i].constant = 1.0; + point_lights[i].linear = 0.09; + point_lights[i].quadratic = 0.032; + } + + scene our_scene = { + .dir_light = dir_light, + .n_point_lights = 4 + }; + memcpy(&our_scene.point_lights, &point_lights, sizeof(point_light[4])); + // --- Enter Main loop while (!glfwWindowShouldClose(core->renderer.window)) { @@ -30,10 +63,10 @@ int main() { render_frame_begin(&core->renderer); - // Draw the cube - transform cube_tf = + // Draw the backpack + transform model_tf = transform_create(VEC3_ZERO, quat_ident(), 2.0); // make the backpack a bit bigger - draw_model(&core->renderer, &cam, backpack, cube_tf); + draw_model(&core->renderer, &cam, backpack, model_tf, &our_scene); render_frame_end(&core->renderer); } diff --git a/src/renderer/render.c b/src/renderer/render.c index dc541d2..1c979e5 100644 --- a/src/renderer/render.c +++ b/src/renderer/render.c @@ -80,7 +80,7 @@ void default_material_init() { texture_data_upload(&DEFAULT_MATERIAL.specular_texture); } -void draw_model(renderer* ren, camera* camera, model* model, transform tf) { +void draw_model(renderer* ren, camera* camera, model* model, transform tf, scene* scene) { // TRACE("Drawing model: %s", model->name); mat4 view; mat4 proj; @@ -88,13 +88,22 @@ void draw_model(renderer* ren, camera* camera, model* model, transform tf) { 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); - draw_mesh(ren, m, tf, &(DEFAULT_MATERIAL), &view, &proj); + material* mat = &model->materials->data[m->material_index]; + draw_mesh(ren, m, tf, mat, &view, &proj); } } @@ -127,8 +136,6 @@ void draw_mesh(renderer* ren, mesh* mesh, transform tf, material* mat, mat4* vie void model_upload_meshes(renderer* ren, model* model) { INFO("Upload mesh vertex data to GPU for model %s", model->name); - // DEBUG("Loading model with handle %d", model_handle.raw); - // model m = models->data[model_handle.raw]; size_t num_meshes = mesh_darray_len(model->meshes); u32 VBOs[num_meshes]; @@ -148,7 +155,7 @@ void model_upload_meshes(renderer* ren, model* model) { 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); + // TRACE("Uploading vertex array data: %d verts", num_vertices); total_verts += num_vertices; // TODO: convert this garbage into a function @@ -256,3 +263,34 @@ void texture_data_upload(texture* tex) { 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 35c2d91..10702e3 100644 --- a/src/renderer/render.h +++ b/src/renderer/render.h @@ -17,7 +17,7 @@ void render_frame_end(renderer* ren); // --- models meshes void model_upload_meshes(renderer* ren, model* model); -void draw_model(renderer* ren, camera* camera, model* model, transform tf); +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); // --- diff --git a/src/renderer/render_types.h b/src/renderer/render_types.h index 746dfce..483e392 100644 --- a/src/renderer/render_types.h +++ b/src/renderer/render_types.h @@ -78,6 +78,27 @@ 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; @@ -90,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 */ @@ -121,6 +140,17 @@ KITC_DECL_TYPED_ARRAY(model) // creates "model_darray" #define TYPED_MODEL_ARRAY #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 { diff --git a/src/resources/obj.c b/src/resources/obj.c index 658967e..221e3aa 100644 --- a/src/resources/obj.c +++ b/src/resources/obj.c @@ -35,7 +35,7 @@ KITC_DECL_TYPED_ARRAY(face) 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, material_darray *materials); +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); @@ -182,7 +182,7 @@ bool model_load_obj_str(const char *file_string, str8 relative_path, model *out_ 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, out_model->materials)) { + if (!load_material_lib(mtllib_path, relative_path, out_model->materials)) { ERROR("couldnt load material lib"); return false; } @@ -262,7 +262,6 @@ void create_submesh(mesh_darray *meshes, vec3_darray *tmp_positions, vec3_darray material_darray_iter mat_iter = material_darray_iter_new(materials); blinn_phong_material *cur_material; while ((cur_material = material_darray_iter_next(&mat_iter))) { - printf("Material name %s vs %s \n", cur_material->name, current_material_name); if (strcmp(cur_material->name, current_material_name) == 0) { DEBUG("Found match"); m.material_index = mat_iter.current_idx - 1; @@ -280,7 +279,7 @@ void create_submesh(mesh_darray *meshes, vec3_darray *tmp_positions, vec3_darray mesh_darray_push(meshes, m); } -bool load_material_lib(const char *path, material_darray *materials) { +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); @@ -338,9 +337,12 @@ bool load_material_lib(const char *path, material_darray *materials) { // specular exponent sscanf(pch + offset, "%f", ¤t_material.spec_exponent); } else if (strcmp(line_header, "map_Kd") == 0) { - char diffuse_map_path[1024] = "assets/"; - sscanf(pch + offset, "%s", diffuse_map_path + 7); + 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; @@ -348,8 +350,13 @@ bool load_material_lib(const char *path, material_darray *materials) { texture_data_upload(¤t_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_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; diff --git a/xmake.lua b/xmake.lua index 15159ef..611b537 100644 --- a/xmake.lua +++ b/xmake.lua @@ -14,8 +14,11 @@ add_cflags("-Wall", "-Wextra", "-Wundef", "-Wdouble-promotion") if is_mode("debug") then add_cflags("-g") -- Add debug symbols in debug mode +elseif is_mode("release") then + add_defines("CRELEASE") end + -- Platform defines and system packages if is_plat("linux") then add_defines("CEL_PLATFORM_LINUX") -- cgit v1.2.3-70-g09d2