diff --git a/main.cpp b/main.cpp index ef705cd..b3d0c01 100644 --- a/main.cpp +++ b/main.cpp @@ -38,9 +38,15 @@ /// - [x] Maybe should clip a triangle on znear zfar plane? /// - [x] Maybe should clip out triangles that are fully z out before draw_triangle /// - [ ] Effects!!! +/// - [ ] Outlines /// - [ ] Lightning -/// - [x] GLOBAL Ilumination +/// - [ ] Proper normal interpolation +/// * https://hero.handmade.network/episode/code/day101/#105 /// - [ ] Phong +/// - [x] diffuse +/// - [x] ambient +/// - [ ] specular +/// * reflecting vectors /// - [ ] Use all materials from OBJ /// - [ ] Point light /// - [ ] Reading PMX files @@ -62,6 +68,7 @@ /// - [ ] UI /// - [x] Labels /// - [x] Settings variables +/// - [x] Signals /// - [ ] Sliders /// - [ ] Groups /// - [x] Gamma correct alpha blending for rectangles and bitmaps @@ -113,10 +120,10 @@ enum Scene { Scene_Count, }; -GLOBAL B32 draw_rects = 0; -GLOBAL Scene scene = Scene_Sponza; -GLOBAL F32 zfar_value = 100000.f; + GLOBAL F32 light_rotation = 0; +GLOBAL F32 zfar_value = 100000.f; + FUNCTION Vec4 srgb_to_almost_linear(Vec4 a) { @@ -380,14 +387,15 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 lig dst_color = { r,g,b,a }; } - F32 light = -dot(norm, light_direction); - { - light = CLAMP(0.1f, light, 1.f); - result_color.r *= light; - result_color.g *= light; - result_color.b *= light; + Vec3 light_color = vec3(0.8,0.8,1); + constexpr F32 ambient_strength = 0.1f; { + Vec3 ambient = ambient_strength * light_color; + Vec3 diffuse = CLAMP_BOT(0, -dot(norm, light_direction)) * light_color; + result_color.rgb *= (ambient+diffuse); } + + result_color = premultiplied_alpha(dst_color, result_color); result_color = almost_linear_to_srgb(result_color); @@ -408,11 +416,7 @@ void draw_triangle_nearest(Bitmap* dst, F32 *depth_buffer, Bitmap *src, Vec3 lig Cy2 -= dx02; destination += dst->x; } - if (draw_rects) { - r_draw_rect(dst, p0.x-4, p0.y-4, 8,8, vec4(1,0,0,1)); - r_draw_rect(dst, p1.x-4, p1.y-4, 8,8, vec4(0,1,0,1)); - r_draw_rect(dst, p2.x-4, p2.y-4, 8,8, vec4(0,0,1,1)); - } + if(os.frame > 60) PROFILE_END(draw_triangle); } @@ -489,11 +493,6 @@ void draw_triangle_bilinear(Bitmap* dst, F32 *depth_buffer, Bitmap *src, F32 lig } } } - if (draw_rects) { - r_draw_rect(dst, p0.x-4, p0.y-4, 8,8, vec4(1,0,0,1)); - r_draw_rect(dst, p1.x-4, p1.y-4, 8,8, vec4(0,1,0,1)); - r_draw_rect(dst, p2.x-4, p2.y-4, 8,8, vec4(0,0,1,1)); - } } FUNCTION @@ -668,6 +667,31 @@ void r_draw_mesh(R_Render *r, S8 scene_name, OBJMaterial *materials, ObjMesh *me } #include "ui.cpp" +F32 speed = 100.f; +F32 rotation = 0; +Obj f22; +Obj sponza; +Obj *obj; +R_Render r = {}; +GLOBAL Scene scene = Scene_Sponza; +UI_SIGNAL_CALLBACK(scene_callback) { + switch(scene) { + case Scene_F22: { + speed = 1; + r.camera_pos = vec3(0,0,-2); + obj = &f22; + } break; + case Scene_Sponza: { + speed = 100; + r.camera_pos = vec3(0,0,-2); + obj = &sponza; + } break; + case Scene_Count: + INVALID_DEFAULT_CASE; + } + scene = (Scene)(((int)scene + 1) % Scene_Count); +} + int main() { os.window_size.x = 320*2; os.window_size.y = 180*2; @@ -691,16 +715,13 @@ int main() { } - Obj f22 = load_obj(os.perm_arena, LIT("assets/f22.obj")); - Obj sponza = load_obj(os.perm_arena, LIT("assets/sponza/sponza.obj")); - Obj *obj = 0; - - F32 speed = 100.f; - F32 rotation = 0; + f22 = load_obj(os.perm_arena, LIT("assets/f22.obj")); + sponza = load_obj(os.perm_arena, LIT("assets/sponza/sponza.obj")); + scene_callback(); int screen_x = 1280/2; int screen_y = 720/2; - R_Render r = {}; + r.camera_pos = {0,0,-2}; 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}; @@ -728,8 +749,7 @@ int main() { S8 frame_data = {}; UISetup setup[] = { - UI_BOOL(LIT("Draw rectangles:"), &draw_rects), - UI_OPTION(LIT("Scene:"), &scene, Scene_Count), + UI_SIGNAL(LIT("Change scene"), scene_callback), UI_IMAGE(&r.plot), UI_LABEL(&frame_data), }; @@ -737,18 +757,7 @@ int main() { B32 ui_mouse_lock = true; while (os_game_loop()) { - switch(scene) { - case Scene_F22: { - speed = 1; - obj = &f22; - } break; - case Scene_Sponza: { - speed = 100; - obj = &sponza; - } break; - case Scene_Count: - INVALID_DEFAULT_CASE; - } + if (ui_mouse_lock == false) { r.camera_yaw.x += os.delta_mouse_pos.x * 0.01f; @@ -757,7 +766,6 @@ int main() { if (os.key[Key_Escape].pressed) os_quit(); if (os.key[Key_O].down) light_rotation += 0.05f; if (os.key[Key_P].down) light_rotation -= 0.05f; - if (os.key[Key_F1].pressed) draw_rects = !draw_rects; if (os.key[Key_F2].pressed) { ui_mouse_lock = !ui_mouse_lock; os_show_cursor(!os.cursor_visible); diff --git a/obj_parser.cpp b/obj_parser.cpp index b15e862..653eba2 100644 --- a/obj_parser.cpp +++ b/obj_parser.cpp @@ -86,287 +86,285 @@ Bitmap load_image(const char* path) { return result; } -namespace obj { - enum class TokenType { - none, word, number, whitespace, end - }; +enum class OBJTokenType { + none, word, number, whitespace, end +}; - struct Token { - TokenType type; - double number; - union { - struct { - char* s; - int len; - }; - S8 s8; +struct OBJToken { + OBJTokenType type; + double number; + union { + struct { + char* s; + int len; }; + S8 s8; }; +}; - FUNCTION Token next_token_raw(char** data) { - Token result = {}; - result.s = *data; - *data += 1; +FUNCTION OBJToken next_token_raw(char** data) { + OBJToken result = {}; + result.s = *data; + *data += 1; - if (is_alphabetic(*result.s)) { - result.type = TokenType::word; - while (!is_whitespace(**data)) { - *data += 1; + if (is_alphabetic(*result.s)) { + result.type = OBJTokenType::word; + while (!is_whitespace(**data)) { + *data += 1; + } + result.len = (int)(*data - result.s); + } + else if (is_number(*result.s) || *result.s == '-') { + result.type = OBJTokenType::number; + while (is_number(**data) || **data == '.' || **data == 'e' || **data == '-') { + *data += 1; + } + result.number = atof(result.s); + result.len = (int)(*data - result.s); + } + else if (*result.s == '#') { + while (**data != '\n') *data += 1; + result = next_token_raw(data); + } + else if (is_whitespace(*result.s)) { + result.type = OBJTokenType::whitespace; + while (is_whitespace(**data)) *data += 1; + result.len = (int)(*data - result.s); + } + else if (*result.s == 0) { + result.type = OBJTokenType::end; + } + else if (*result.s >= '!') { + result.type = (OBJTokenType)*result.s; + } + + return result; +} + +FUNCTION OBJToken next_token(char** data) { + OBJToken result; + do { + result = next_token_raw(data); + } while (result.type == OBJTokenType::whitespace); + return result; +} + +FUNCTION double expect_number(char** data) { + OBJToken t = next_token(data); + TASSERT(t.type == OBJTokenType::number); // @Todo: Error handling, error flag + return t.number; +} + +FUNCTION void expect_token(char** data, char token) { + OBJToken t = next_token(data); + TASSERT(t.type == (OBJTokenType)token); // @Todo: Error handling, error flag +} + +FUNCTION void debug_expect_raw(char** data, OBJTokenType type) { + char* data_temp = *data; + OBJToken t = next_token_raw(&data_temp); + TASSERT(t.type == type); +} + +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 (;;) { + OBJToken token = next_token(&data); + if (token.type == OBJTokenType::end) break; + else if (token.type == OBJTokenType::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); } - result.len = (int)(*data - result.s); - } - else if (is_number(*result.s) || *result.s == '-') { - result.type = TokenType::number; - while (is_number(**data) || **data == '.' || **data == 'e' || **data == '-') { - *data += 1; + else if (string_compare(token.s8, LIT("Ns"))) { + m->shininess = expect_number(&data); } - result.number = atof(result.s); - result.len = (int)(*data - result.s); - } - else if (*result.s == '#') { - while (**data != '\n') *data += 1; - result = next_token_raw(data); - } - else if (is_whitespace(*result.s)) { - result.type = TokenType::whitespace; - while (is_whitespace(**data)) *data += 1; - result.len = (int)(*data - result.s); - } - else if (*result.s == 0) { - result.type = TokenType::end; - } - else if (*result.s >= '!') { - result.type = (TokenType)*result.s; - } - - return result; - } - - FUNCTION Token next_token(char** data) { - Token result; - do { - result = next_token_raw(data); - } while (result.type == TokenType::whitespace); - return result; - } - - FUNCTION double expect_number(char** data) { - Token t = next_token(data); - TASSERT(t.type == TokenType::number); // @Todo: Error handling, error flag - return t.number; - } - - FUNCTION void expect_token(char** data, char token) { - Token t = next_token(data); - TASSERT(t.type == (TokenType)token); // @Todo: Error handling, error flag - } - - FUNCTION void debug_expect_raw(char** data, TokenType type) { - char* data_temp = *data; - Token t = next_token_raw(&data_temp); - TASSERT(t.type == type); - } - - 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); - } + 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"))) { + OBJToken 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"))) { + OBJToken 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"))) { + OBJToken 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"))) { + OBJToken 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(); - ZERO_STRUCT(mesh); - int material_id = -1; +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(); + ZERO_STRUCT(mesh); + int material_id = -1; - for (;; ) { - Token token = next_token(&data); - if (token.type == TokenType::end) break; - else if (token.type == TokenType::word) { - if (string_compare(token.s8, LIT("v"))) { - Vec3 *vertex = result.vertices.push_empty(); - vertex->x = (float)expect_number(&data); - vertex->y = (float)expect_number(&data); - vertex->z = (float)expect_number(&data); - debug_expect_raw(&data, TokenType::whitespace); - } - else if (string_compare(token.s8, LIT("vt"))) { - Vec2 *tex = result.texture_coordinates.push_empty(); - tex->x = (float)expect_number(&data); - tex->y = (float)expect_number(&data); - debug_expect_raw(&data, TokenType::whitespace); - } - else if (string_compare(token.s8, LIT("vn"))) { - Vec3 *norm = result.normals.push_empty(); - norm->x = (float)expect_number(&data); - norm->y = (float)expect_number(&data); - norm->z = (float)expect_number(&data); - debug_expect_raw(&data, TokenType::whitespace); - } - else if (string_compare(token.s8, LIT("mtllib"))) { - Token t = next_token(&data); - S8 path = string_format(mtl_scratch, "%s/%s", path_obj_folder, t.s8); - Result mtl_file = os_read_file(mtl_scratch, path); - if(mtl_file.no_error()) { - PUSH_SIZE(mtl_scratch, 1); - parse_mtl(arena, &result, path_obj_folder, mtl_file.result); - } - } - 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); - TASSERT(t.type == TokenType::word); - if (mesh->indices.len != 0) { - mesh = result.mesh.push_empty(); - ZERO_STRUCT(mesh); - } - else { - U64 len = CLAMP_TOP(t.len, 64); - memory_copy(t.s, mesh->name, len); - } - } - else if (string_compare(token.s8, LIT("s"))) { - Token t = next_token(&data); - if (t.type == TokenType::number) { - smoothing = (int)t.number; - } - else { - TASSERT(t.type == TokenType::word); - if (string_compare(t.s8, LIT("on"))) { - smoothing = 1; - } - else if (string_compare(t.s8, LIT("off"))) { - smoothing = 0; - } - else INVALID_CODEPATH; - } - - } - else if (string_compare(token.s8, LIT("g"))) { - Token t = next_token(&data); - TASSERT(t.type == TokenType::word); - } - else if (string_compare(token.s8, LIT("f"))) { - ObjIndex *i = mesh->indices.push_empty(); - i->smoothing_group_id = smoothing; - i->material_id = material_id; - i->vertex[0] = (int)expect_number(&data); - expect_token(&data, '/'); - i->tex[0] = (int)expect_number(&data); - expect_token(&data, '/'); - i->normal[0] = (int)expect_number(&data); - - i->vertex[1] = (int)expect_number(&data); - expect_token(&data, '/'); - i->tex[1] = (int)expect_number(&data); - expect_token(&data, '/'); - i->normal[1] = (int)expect_number(&data); - - i->vertex[2] = (int)expect_number(&data); - expect_token(&data, '/'); - i->tex[2] = (int)expect_number(&data); - expect_token(&data, '/'); - i->normal[2] = (int)expect_number(&data); - //debug_expect_raw(&data, TokenType::whitespace); + for (;; ) { + OBJToken token = next_token(&data); + if (token.type == OBJTokenType::end) break; + else if (token.type == OBJTokenType::word) { + if (string_compare(token.s8, LIT("v"))) { + Vec3 *vertex = result.vertices.push_empty(); + vertex->x = (float)expect_number(&data); + vertex->y = (float)expect_number(&data); + vertex->z = (float)expect_number(&data); + debug_expect_raw(&data, OBJTokenType::whitespace); + } + else if (string_compare(token.s8, LIT("vt"))) { + Vec2 *tex = result.texture_coordinates.push_empty(); + tex->x = (float)expect_number(&data); + tex->y = (float)expect_number(&data); + debug_expect_raw(&data, OBJTokenType::whitespace); + } + else if (string_compare(token.s8, LIT("vn"))) { + Vec3 *norm = result.normals.push_empty(); + norm->x = (float)expect_number(&data); + norm->y = (float)expect_number(&data); + norm->z = (float)expect_number(&data); + debug_expect_raw(&data, OBJTokenType::whitespace); + } + else if (string_compare(token.s8, LIT("mtllib"))) { + OBJToken t = next_token(&data); + S8 path = string_format(mtl_scratch, "%s/%s", path_obj_folder, t.s8); + Result mtl_file = os_read_file(mtl_scratch, path); + if(mtl_file.no_error()) { + PUSH_SIZE(mtl_scratch, 1); + parse_mtl(arena, &result, path_obj_folder, mtl_file.result); } } + else if (string_compare(token.s8, LIT("usemtl"))) { + OBJToken t = next_token(&data); + TASSERT(t.type == OBJTokenType::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"))) { + OBJToken t = next_token(&data); + TASSERT(t.type == OBJTokenType::word); + if (mesh->indices.len != 0) { + mesh = result.mesh.push_empty(); + ZERO_STRUCT(mesh); + } + else { + U64 len = CLAMP_TOP(t.len, 64); + memory_copy(t.s, mesh->name, len); + } + } + else if (string_compare(token.s8, LIT("s"))) { + OBJToken t = next_token(&data); + if (t.type == OBJTokenType::number) { + smoothing = (int)t.number; + } + else { + TASSERT(t.type == OBJTokenType::word); + if (string_compare(t.s8, LIT("on"))) { + smoothing = 1; + } + else if (string_compare(t.s8, LIT("off"))) { + smoothing = 0; + } + else INVALID_CODEPATH; + } + + } + else if (string_compare(token.s8, LIT("g"))) { + OBJToken t = next_token(&data); + TASSERT(t.type == OBJTokenType::word); + } + else if (string_compare(token.s8, LIT("f"))) { + ObjIndex *i = mesh->indices.push_empty(); + i->smoothing_group_id = smoothing; + i->material_id = material_id; + i->vertex[0] = (int)expect_number(&data); + expect_token(&data, '/'); + i->tex[0] = (int)expect_number(&data); + expect_token(&data, '/'); + i->normal[0] = (int)expect_number(&data); + + i->vertex[1] = (int)expect_number(&data); + expect_token(&data, '/'); + i->tex[1] = (int)expect_number(&data); + expect_token(&data, '/'); + i->normal[1] = (int)expect_number(&data); + + i->vertex[2] = (int)expect_number(&data); + expect_token(&data, '/'); + i->tex[2] = (int)expect_number(&data); + expect_token(&data, '/'); + i->normal[2] = (int)expect_number(&data); + //debug_expect_raw(&data, OBJTokenType::whitespace); + } } - return result; } + return result; +} - 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); - Token t = next_token(&dd); TASSERT(t.type == TokenType::number && t.number > 0.8857); - t = next_token(&dd); TASSERT(t.type == TokenType::number && t.number > 0.0019); - t = next_token(&dd); TASSERT(t.type == TokenType::number && t.number < -0.38); - d = "# Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'\n" - "# www.blender.org\n" - "mtllib f-22.mtl\n" - "o F-22\n"; - dd = (char *)d; - t = next_token(&dd); TASSERT(t.type == TokenType::word && string_compare(t.s8, LIT("mtllib"))); - t = next_token(&dd); TASSERT(t.type == TokenType::word && string_compare(t.s8, LIT("f-22.mtl"))); - t = next_token(&dd); TASSERT(t.type == TokenType::word && string_compare(t.s8, LIT("o"))); - t = next_token(&dd); TASSERT(t.type == TokenType::word && string_compare(t.s8, LIT("F-22"))); - } +FUNCTION void test_lex() { + const char* d = "v 0.885739 0.001910 -0.380334"; + char* dd = (char *)d; + TASSERT(next_token(&dd).type == OBJTokenType::word); + OBJToken t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number > 0.8857); + t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number > 0.0019); + t = next_token(&dd); TASSERT(t.type == OBJTokenType::number && t.number < -0.38); + d = "# Blender v2.79 (sub 0) OBJ File: 'fighters_0.blend'\n" + "# www.blender.org\n" + "mtllib f-22.mtl\n" + "o F-22\n"; + dd = (char *)d; + t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("mtllib"))); + t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("f-22.mtl"))); + t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("o"))); + t = next_token(&dd); TASSERT(t.type == OBJTokenType::word && string_compare(t.s8, LIT("F-22"))); +} - void test() { - test_lex(); - } +void test() { + test_lex(); } FUNCTION Obj load_obj(Arena *arena, S8 file) { @@ -374,7 +372,7 @@ FUNCTION Obj load_obj(Arena *arena, S8 file) { S8 data = os_read_file(scratch, file).error_is_fatal(); PUSH_SIZE(scratch, 1); S8 path = string_chop_last_slash(file); - Obj result = obj::parse(arena, (char *)data.str, path); + Obj result = parse(arena, (char *)data.str, path); result.name = file; return result; } diff --git a/ui.cpp b/ui.cpp index 39038d1..e5b7b5a 100644 --- a/ui.cpp +++ b/ui.cpp @@ -4,9 +4,12 @@ enum UIWidgetKind { UIWidgetKind_Image, UIWidgetKind_Label, UIWidgetKind_Option, + UIWidgetKind_Signal, UIWidgetKind_Group, }; +#define UI_SIGNAL_CALLBACK(name) void name() +typedef UI_SIGNAL_CALLBACK(UISignalCallback); struct UISetup { UIWidgetKind kind; S8 text; @@ -16,6 +19,7 @@ struct UISetup { B32 *b32; S8 *label; I32 *option; + UISignalCallback *signal_callback; }; I32 option_max; }; @@ -23,6 +27,7 @@ struct UISetup { #define UI_IMAGE(x) {UIWidgetKind_Image,string_null,(void*)(x)} #define UI_LABEL(x) {UIWidgetKind_Label,string_null,(void*)(x)} #define UI_OPTION(text,x,option_max){UIWidgetKind_Option,text,(void*)(x),(option_max)} +#define UI_SIGNAL(text,x){UIWidgetKind_Signal,text,(void*)(x)} struct UIWidget { UIWidgetKind kind; @@ -39,6 +44,7 @@ struct UIWidget { B32 *b32; S8 *label; I32 *option; + UISignalCallback *signal_callback; } ptr; }; @@ -96,6 +102,13 @@ FUNCTION UIWidget *ui_push_option(Arena *arena, UIWidget *widget, S8 string, I32 return result; } +FUNCTION UIWidget *ui_push_signal(Arena *arena, UIWidget *widget, S8 string, UISignalCallback *callback) { + UIWidget *result = ui_push_child(arena, widget, UIWidgetKind_Signal); + result->text = string; + result->ptr.signal_callback = callback; + return result; +} + FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) { UI result = {}; result.arena = arena_sub(arena, MiB(16)); @@ -114,6 +127,9 @@ FUNCTION UI ui_make(Arena *arena, UISetup *setup, U64 len) { case UIWidgetKind_Option: { ui_push_option(&result.arena, parent, s->text, s->option, s->option_max); } break; + case UIWidgetKind_Signal: { + ui_push_signal(&result.arena, parent, s->text, s->signal_callback); + } break; INVALID_DEFAULT_CASE; } } @@ -203,6 +219,23 @@ FUNCTION void ui_end_frame(Bitmap *dst, UI *ui, Font *font) { rect = r_draw_string(dst, font, string, pos); pos.y -= rect.height - font->height; } break; + case UIWidgetKind_Signal: { + pos.y -= font->height; + Vec4 color = vec4(0, 0, 0, 1); + S8 string = string_format(scratch, "%s", w->text); + rect = r_get_string_rect(font, string, pos); + B32 clicked = ui_mouse_test(ui, w, rect); + if (clicked) { + w->ptr.signal_callback(); + } + if (ui->hot == w) { + color = vec4(0.4f, 0.4f, 0.4f, 1.f); + } + rect.y = rect.y-font->line_advance / 5; + r_draw_rect(dst, rect.x, rect.y, rect.width, rect.height, color); + rect = r_draw_string(dst, font, string, pos); + pos.y -= rect.height - font->height; + } break; INVALID_DEFAULT_CASE; } }