From 8cfe5086292d3c369809c82548f1b49f154959ed Mon Sep 17 00:00:00 2001 From: Krzosa Karol Date: Tue, 1 Mar 2022 00:36:03 +0100 Subject: [PATCH] Loading mtl textures! --- .gitignore | 8 ++- main.cpp | 54 ++++++++----------- obj_parser.cpp | 142 ++++++++++++++++++++++++++++++++++++++++++++----- ui.cpp | 2 - 4 files changed, 158 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index afd7eab..87f553a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,12 @@ assets/ stats/ data.txt +*.ilk +*.exp +*.exe +*.lib +*.pdb *.sln *.vcxproj* -*.rdbg \ No newline at end of file +*.rdbg +*.sublime* \ No newline at end of file diff --git a/main.cpp b/main.cpp index dc1facb..90be09e 100644 --- a/main.cpp +++ b/main.cpp @@ -25,8 +25,11 @@ /// - [x] FPS Camera /// - [x] Reading OBJ models /// - [ ] Reading more OBJ formats -/// - [ ] Reading OBJ .mtl files +/// - [x] Reading OBJ .mtl files +/// - [x] Loading materials +/// - [x] Rendering textures obj models /// - [x] Reading complex obj models (sponza) +/// - [ ] Fix sponza uv coordinates /// - [ ] Reading PMX files /// - [ ] Rendering multiple objects, queue renderer /// - [x] Simple function to render a mesh @@ -92,8 +95,8 @@ struct R_Render { F32 *depth320; }; -#include "obj_parser.cpp" #include "stb_image.h" +#include "obj_parser.cpp" #include GLOBAL B32 draw_rects = 0; @@ -291,6 +294,9 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 ligh F32 v = tex0.y * (w1 / p0.w) + tex1.y * (w2 / p1.w) + tex2.y * (w3 / p2.w); u /= interpolated_w; v /= interpolated_w; + u = CLAMP(0, u, 1); + v = CLAMP(0, v, 1); + if(u > 1 || v > 1 || u < 0 || v < 0) continue; // @Note: We could do: interpolated_w = 1.f / interpolated_w to get proper depth // but why waste an instruction, the smaller the depth value the farther the object F32* depth = depth_buffer + (x + y * dst->x); @@ -411,26 +417,6 @@ FUNCTION } } -FUNCTION -Bitmap load_image(const char* path) { - int x, y, n; - unsigned char* data = stbi_load(path, &x, &y, &n, 4); - Bitmap result = { (U32*)data, x, y }; -#if PREMULTIPLIED_ALPHA_BLENDING - U32 *p = result.pixels; - for (int Y = 0; Y < y; Y++) { - for (int X = 0; X < x; X++) { - Vec4 color = vec4abgr(*p); - color.r *= color.a; - color.g *= color.a; - color.b *= color.a; - *p++ = vec4_to_u32abgr(color); - } - } -#endif - return result; -} - FN void r_scatter_plot(Bitmap *dst, F64 *data, I64 data_len) { F64 min = F32MAX; F64 max = FLT_MIN; @@ -453,9 +439,14 @@ FN void r_scatter_plot(Bitmap *dst, F64 *data, I64 data_len) { } S8 scenario_name = string_null; -FN void r_draw_mesh(R_Render *r, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords, Vec3 *normals) { +FN void r_draw_mesh(R_Render *r, OBJMaterial *materials, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords, Vec3 *normals) { for (int i = 0; i < mesh->indices.len; i++) { ObjIndex *index = mesh->indices.e + i; + OBJMaterial *material = materials + index->material_id; + Bitmap *image = &material->texture_ambient; + if(image->pixels == 0) { + image = &r->img; + } R_Vertex vert[] = { { vertices[index->vertex[0] - 1], @@ -572,10 +563,9 @@ FN void r_draw_mesh(R_Render *r, ObjMesh *mesh, Vec3 *vertices, Vec2 *tex_coords in[j].pos.y += r->screen320.y / 2; } - - draw_triangle_nearest(&r->screen320, r->depth320, &r->img, light, in[0].pos, in[1].pos, in[2].pos, in[0].tex, in[1].tex, in[2].tex); + draw_triangle_nearest(&r->screen320, r->depth320, image, light, in[0].pos, in[1].pos, in[2].pos, in[0].tex, in[1].tex, in[2].tex); if (in_count > 3) { - draw_triangle_nearest(&r->screen320, r->depth320, &r->img, light, in[0].pos, in[2].pos, in[3].pos, in[0].tex, in[2].tex, in[3].tex); + draw_triangle_nearest(&r->screen320, r->depth320, image, light, in[0].pos, in[2].pos, in[3].pos, in[0].tex, in[2].tex, in[3].tex); } @@ -618,17 +608,17 @@ int main() { } - scenario_name = LIT("assets/f22.obj"); + // scenario_name = LIT("assets/f22.obj"); //scenario_name = LIT("assets/cube.obj"); //scenario_name = LIT("assets/AnyConv.com__White.obj"); - //scenario_name = LIT("assets/sponza/sponza.obj"); - Obj obj = load_obj(scenario_name); + scenario_name = LIT("assets/sponza/sponza.obj"); + Obj obj = load_obj(os.perm_arena, scenario_name); Vec3* vertices = (Vec3 *)obj.vertices.e; Vec2* tex_coords = (Vec2*)obj.texture_coordinates.e; Vec3 *normals = (Vec3 *)obj.normals.e; ObjMesh *mesh = obj.mesh.e; - F32 speed = 5.f; + F32 speed = 100.f; F32 rotation = 0; int screen_x = 320; @@ -638,7 +628,7 @@ int main() { r.screen320 = {(U32 *)PUSH_SIZE(os.perm_arena, screen_x*screen_y*sizeof(U32)), screen_x, screen_y}; r.plot = {(U32 *)PUSH_SIZE(os.perm_arena, 1280*720*sizeof(U32)), 1280, 720}; r.depth320 = (F32 *)PUSH_SIZE(os.perm_arena, sizeof(F32) * screen_x * screen_y); - r.img = load_image("assets/cat.png"); + r.img = load_image("assets/bricksx64.png"); /* @Note: Transparent texture */ { #if 0 @@ -712,7 +702,7 @@ int main() { r.transform = mat4_rotation_z(rotation); r.transform = r.transform * mat4_rotation_y(rotation); for (int i = 0; i < obj.mesh.len; i++) { - r_draw_mesh(&r, mesh+i, vertices, tex_coords, normals); + r_draw_mesh(&r, obj.materials.e, mesh+i, vertices, tex_coords, normals); } diff --git a/obj_parser.cpp b/obj_parser.cpp index 8ef45f9..ca719a6 100644 --- a/obj_parser.cpp +++ b/obj_parser.cpp @@ -38,13 +38,53 @@ struct ObjMesh { DynamicArray indices; }; +struct OBJMaterial { + char name[64]; + U32 name_len; + Bitmap texture_ambient; // map_Ka + Bitmap texture_diffuse; // map_Kd + Bitmap texture_dissolve; // map_d + Bitmap texture_displacment; // map_Disp + F32 non_transparency; // d + F32 transparency; // Tr + F32 optical_density; // Ni + F32 shininess; // Ns + I32 illumination_model; // illum + Vec3 ambient_color; // Ka + Vec3 diffuse_color; // Kd + Vec3 specular_color; // Ks +}; + struct Obj { DynamicArray vertices; DynamicArray texture_coordinates; DynamicArray normals; DynamicArray mesh; + DynamicArray materials; }; +FUNCTION +Bitmap load_image(const char* path) { + int x, y, n; + unsigned char* data = stbi_load(path, &x, &y, &n, 4); + Bitmap result = { (U32*)data, x, y }; +#if PREMULTIPLIED_ALPHA_BLENDING + if(data) { + U32 *p = result.pixels; + for (int Y = 0; Y < y; Y++) { + for (int X = 0; X < x; X++) { + Vec4 color = vec4abgr(*p); + color.r *= color.a; + color.g *= color.a; + color.b *= color.a; + *p++ = vec4_to_u32abgr(color); + } + } + } +#endif + return result; +} + namespace obj { enum class TokenType { none, word, number, whitespace, end @@ -62,7 +102,7 @@ namespace obj { }; }; - FN Token next_token_raw(char** data) { + FUNCTION Token next_token_raw(char** data) { Token result = {}; result.s = *data; *data += 1; @@ -101,7 +141,7 @@ namespace obj { return result; } - FN Token next_token(char** data) { + FUNCTION Token next_token(char** data) { Token result; do { result = next_token_raw(data); @@ -109,25 +149,91 @@ namespace obj { return result; } - FN double expect_number(char** data) { + FUNCTION double expect_number(char** data) { Token t = next_token(data); TASSERT(t.type == TokenType::number); // @Todo: Error handling, error flag return t.number; } - FN void expect_token(char** data, char token) { + FUNCTION void expect_token(char** data, char token) { Token t = next_token(data); TASSERT(t.type == (TokenType)token); // @Todo: Error handling, error flag } - FN void debug_expect_raw(char** data, TokenType type) { + FUNCTION void debug_expect_raw(char** data, TokenType type) { char* data_temp = *data; Token t = next_token_raw(&data_temp); TASSERT(t.type == type); } - // Each face needs to have own material_id, group_id, smoothing .. - Obj parse(char* data) { + FUNCTION void parse_mtl(Arena *arena, Obj* obj, S8 path_obj_folder, S8 mtl_file) { + Scratch scratch; + char *data = (char *)mtl_file.str; + OBJMaterial *m = 0; + for (;;) { + Token token = next_token(&data); + if (token.type == TokenType::end) break; + else if (token.type == TokenType::word) { + if (string_compare(token.s8, LIT("newmtl"))) { + token = next_token(&data); + m = obj->materials.push_empty(); + ZERO_STRUCT(m); + m->name_len = CLAMP_TOP(token.len, 64); + memory_copy(token.s8.str, m->name, m->name_len); + } + else if (string_compare(token.s8, LIT("Ns"))) { + m->shininess = expect_number(&data); + } + else if (string_compare(token.s8, LIT("Ka"))) { + m->ambient_color.x = expect_number(&data); + m->ambient_color.y = expect_number(&data); + m->ambient_color.z = expect_number(&data); + } + else if (string_compare(token.s8, LIT("Kd"))) { + m->diffuse_color.x = expect_number(&data); + m->diffuse_color.y = expect_number(&data); + m->diffuse_color.z = expect_number(&data); + } + else if (string_compare(token.s8, LIT("Ks"))) { + m->specular_color.x = expect_number(&data); + m->specular_color.y = expect_number(&data); + m->specular_color.z = expect_number(&data); + } + else if (string_compare(token.s8, LIT("Ni"))) { + m->optical_density = expect_number(&data); + } + else if (string_compare(token.s8, LIT("d"))) { + m->non_transparency = expect_number(&data); + } + else if (string_compare(token.s8, LIT("illum"))) { + m->illumination_model = (I32)expect_number(&data); + } + else if (string_compare(token.s8, LIT("map_Kd"))) { + Token t = next_token(&data); + S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8); + m->texture_diffuse = load_image((const char *)path.str); + } + else if (string_compare(token.s8, LIT("map_Ka"))) { + Token t = next_token(&data); + S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8); + m->texture_ambient = load_image((const char *)path.str); + } + else if (string_compare(token.s8, LIT("map_d"))) { + Token t = next_token(&data); + S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8); + m->texture_dissolve = load_image((const char *)path.str); + } + else if (string_compare(token.s8, LIT("map_Disp"))) { + Token t = next_token(&data); + S8 path = string_format(scratch, "%s/%s\0", path_obj_folder, t.s8); + m->texture_displacment = load_image((const char *)path.str); + } + } + } + } + + FUNCTION Obj parse(Arena *arena, char* data, S8 path_obj_folder) { + Scratch mtl_scratch; Obj result = {}; int smoothing = 0; ObjMesh *mesh = result.mesh.push_empty(); @@ -158,13 +264,23 @@ namespace obj { norm->z = (float)expect_number(&data); debug_expect_raw(&data, TokenType::whitespace); } - else if (string_compare(token.s8, LIT("mtlib"))) { + else if (string_compare(token.s8, LIT("mtllib"))) { Token t = next_token(&data); - TASSERT(t.type == TokenType::word); + S8 path = string_format(mtl_scratch, "%s/%s", path_obj_folder, t.s8); + S8 mtl_file = os_read_file(mtl_scratch, path).error_is_fatal(); + PUSH_SIZE(mtl_scratch, 1); + parse_mtl(arena, &result, path_obj_folder, mtl_file); } else if (string_compare(token.s8, LIT("usemtl"))) { Token t = next_token(&data); TASSERT(t.type == TokenType::word); + for(U64 i = 0; i < result.materials.len; i++) { + OBJMaterial *m = result.materials.e + i; + if(string_compare(string_make((U8 *)m->name, m->name_len), t.s8)) { + material_id = i; + break; + } + } } else if (string_compare(token.s8, LIT("o"))) { Token t = next_token(&data); @@ -227,7 +343,7 @@ namespace obj { return result; } - FN void test_lex() { + FUNCTION void test_lex() { const char* d = "v 0.885739 0.001910 -0.380334"; char* dd = (char *)d; TASSERT(next_token(&dd).type == TokenType::word); @@ -250,11 +366,11 @@ namespace obj { } } -FUNCTION -Obj load_obj(S8 file) { +FUNCTION Obj load_obj(Arena *arena, S8 file) { Scratch scratch; S8 data = os_read_file(scratch, file).error_is_fatal(); PUSH_SIZE(scratch, 1); - Obj result = obj::parse((char *)data.str); + S8 path = string_chop_last_slash(file); + Obj result = obj::parse(arena, (char *)data.str, path); return result; } diff --git a/ui.cpp b/ui.cpp index 1cf88f0..9a3a9f7 100644 --- a/ui.cpp +++ b/ui.cpp @@ -170,7 +170,5 @@ FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) { } break; INVALID_DEFAULT_CASE; } - - } } \ No newline at end of file