diff --git a/tools/vtx_extract.cpp b/tools/vtx_extract.cpp index 06ca3aa6e..fb306c64b 100644 --- a/tools/vtx_extract.cpp +++ b/tools/vtx_extract.cpp @@ -3,21 +3,35 @@ #include #include #include +#include #include #include #include #include "libmio0.h" -typedef struct { +struct mk64_Vtx{ + short ob[3]; /* x, y, z */ + short tc[2]; /* texture coord */ + unsigned char ca[4]; /* color & alpha */ +}; + +std::istream & operator >> (std::istream &in, mk64_Vtx &vtx) { + // This is highly dependent on the `mk64_Vtx` type having no padding between its elements + // And that is the case, but its worth noting that its kind of brittle + in.read(reinterpret_cast(&vtx), sizeof(mk64_Vtx)); + return in; +} + +struct model_location{ std::string course_name; uint32_t model_rom_offset; uint32_t packed_dl_rom_offset; std::map textures; -} model_location; +}; std::vector all_models = { - { "mario_raceway", 0x88fa10, 0x899104, + { "mario_raceway", 0x88fa10, 0x899104, // This works, but is very painful to setup // We'd probably have to create a master material file // that all courses would reference, rather than one-per-course @@ -54,43 +68,68 @@ std::vector all_models = { { 0x15800, "sign_koopa_air_1" }, } }, - // { 0x89b510, 0x8A55C4, "choco_mountain" }, - // { 0x8a7640, 0x8B59A8, "bowsers_castle" }, - // { 0x8b9630, 0x8BFF18, "banshee_boardwalk" }, - // { 0x8c2510, 0x8CA2A0, "yoshi_valley" }, - // { 0x8cc900, 0x8D6624, "frappe_snowland" }, - // { 0x8d8e50, 0x8E8BC8, "koopa_troopa_beach" }, - // { 0x8ec390, 0x8FAFF0, "royal_raceway" }, - // { 0x8fe640, 0x907E40, "luigi_raceway" }, - // { 0x90b3e0, 0x918ECC, "moo_moo_farm" }, - // { 0x91b980, 0x925F50, "toads_turnpike" }, - // { 0x928c70, 0x934004, "kalimari_desert" }, - // { 0x936fd0, 0x93B9C8, "sherbet_land" }, - // { 0x93cc60, 0x9426BC, "rainbow_road" }, - // { 0x9438c0, 0x94E28C, "wario_stadium" }, - // { 0x951780, 0x953058, "block_fort" }, - // { 0x953890, 0x954F08, "skyscraper" }, - // { 0x955620, 0x9562F4, "double_deck" }, + // { "choco_mountain", 0x89b510, 0x8A55C4, }, + // { "bowsers_castle", 0x8a7640, 0x8B59A8, }, + // { "banshee_boardwalk", 0x8b9630, 0x8BFF18, }, + // { "yoshi_valley", 0x8c2510, 0x8CA2A0, }, + // { "frappe_snowland", 0x8cc900, 0x8D6624, }, + // { "koopa_troopa_beach", 0x8d8e50, 0x8E8BC8, }, + // { "royal_raceway", 0x8ec390, 0x8FAFF0, }, + // { "luigi_raceway", 0x8fe640, 0x907E40, }, + // { "moo_moo_farm", 0x90b3e0, 0x918ECC, }, + // { "toads_turnpike", 0x91b980, 0x925F50, }, + // { "kalimari_desert", 0x928c70, 0x934004, }, + // { "sherbet_land", 0x936fd0, 0x93B9C8, }, + // { "rainbow_road", 0x93cc60, 0x9426BC, }, + // { "wario_stadium", 0x9438c0, 0x94E28C, }, + // { "block_fort", 0x951780, 0x953058, }, + // { "skyscraper", 0x953890, 0x954F08, }, + { "double_deck", 0x955620, 0x9562F4, + { + { 0x00000, "gray_cobblestone" }, + { 0x00800, "texture_642978" }, + } + }, // { 0x956670, 0x960ACC, "dks_jungle_parkway" }, // { 0x963ef0, 0x965A74, "big_donut" }, }; -typedef struct { - short ob[3]; /* x, y, z */ - short tc[2]; /* texture coord */ - unsigned char ca[4]; /* color & alpha */ -} mk64_Vtx; - -std::istream & operator >> (std::istream &in, mk64_Vtx &vtx) { - // This is highly dependent on the `mk64_Vtx` type having no padding between its elements - // And that is the case, but its worth noting that its kind of brittle - in.read(reinterpret_cast(&vtx), sizeof(mk64_Vtx)); - return in; -} - -typedef struct { +struct uv{ float u, v; -} uv; +} ; + +struct node{ + uint32_t address = 0; + std::shared_ptr parent = nullptr; + std::vector> children; + // There's no byte stream, so a stringstream will + // have to do instead + std::stringstream face_string; +}; + +// Emit leaf nodes in tree in DFS order +void parse_tree(std::shared_ptr root, std::stringstream &face_string) { + // Doing this silly check as a way to differentiate + // leaf nodes from traversal nodes. + if (!root->children.empty()) { + /* + Children "top to bottom" in dl file + */ + face_string << "o object" << std::to_string(root->address) << std::endl; + for (auto child : root->children) { + parse_tree(child, face_string); + } + /* + Children "bottom to top" in dl file + for (auto child = std::rbegin(root->children); child != std::rend(root->children); child++) { + parse_tree(*child, face_string); + } + */ + } else { + // face_string << "o object" << std::to_string(root->address) << std::endl; + face_string << root->face_string.str(); + } +} inline short bswap(short in) { return ((in & 0xFF) << 8) | ((in >> 8) & 0xFF); @@ -102,14 +141,12 @@ int main(int argc, char **argv) { int num_tris; uint8_t opcode, data_byte; uint16_t data_short; - uint32_t texture_addr; + uint32_t dl_count, texture_addr; float texture_height, texture_width, texture_extra_multi; short vtx_index; short v1, v2, v3; int object_num = 0; int face_group_num = 0; - std::vector vtx_list; - std::map uv_list; std::ofstream vtx_obj; std::ofstream model_inc; std::ifstream model_bytes; @@ -126,6 +163,10 @@ int main(int argc, char **argv) { std::stringstream vtx_tc_string; std::stringstream model_string; std::stringstream face_string; + std::map uv_list; + std::map> node_map; + std::vector vtx_list; + std::shared_ptr anode; vtx_obj_path = "./courses/" + model.course_name + "/course.obj"; model_inc_path = "./courses/" + model.course_name + "/course.vtx"; @@ -172,7 +213,9 @@ int main(int argc, char **argv) { // Should probably confirm that the file open actually worked model_bytes.open(argv[1], std::ios::in | std::ios::binary); model_bytes.seekg(model.packed_dl_rom_offset); - face_string << "o object" << std::to_string(object_num++) << std::endl; + + dl_count = 0; + anode = std::make_shared(); while(model_bytes.read(reinterpret_cast(&opcode), 1)) { if (opcode == 0xFF) { break; @@ -186,11 +229,13 @@ int main(int argc, char **argv) { **/ if ((0x33 <= opcode) && (opcode <= 0x52)) { // unpack_vtx2, gsSPVertex // 3 bytes including opcode + dl_count += 1; model_bytes.read(reinterpret_cast(&data_short), 2); vtx_index = data_short + 1; - face_string << "g face_group" << std::to_string(face_group_num++) << std::endl; + anode->face_string << "g face_group" << std::to_string(face_group_num++) << std::endl; } else if (opcode == 0x28) { // unpack_vtx1, gsSPVertex // 5 bytes including the opcode + dl_count += 1; model_bytes.read(reinterpret_cast(&data_short), 2); vtx_index = data_short + 1; // Only need the vertex "address", skip the other 2 bytes @@ -202,6 +247,7 @@ int main(int argc, char **argv) { * 0x58: unpack_quadrangle, gsSP2Triangles * 5 bytes including opcode **/ + dl_count += 1; if (opcode == 0x29) { num_tris = 1; } else { @@ -219,39 +265,38 @@ int main(int argc, char **argv) { v1 += vtx_index; v2 += vtx_index; v3 += vtx_index; - face_string << "f "; - // v index / vt index / - face_string << std::to_string(v1) << "/" << std::to_string(v1) << "/" << " "; - face_string << std::to_string(v2) << "/" << std::to_string(v2) << "/" << " "; - face_string << std::to_string(v3) << "/" << std::to_string(v3) << "/" << " "; - face_string << std::endl; + anode->face_string << "f "; + // v index / vt index / + anode->face_string << std::to_string(v1) << "/" << std::to_string(v1) << "/" << " "; + anode->face_string << std::to_string(v2) << "/" << std::to_string(v2) << "/" << " "; + anode->face_string << std::to_string(v3) << "/" << std::to_string(v3) << "/" << " "; + anode->face_string << std::endl; /** * Supposedly, the tc values need to be scaled down into the range 0.0 to 1.0 * However, doing the expected scaling will leave several u/v values well outside that range * Blender doesn't care however. For some reason. **/ - uv_list[v1 - 1].u = (bswap(vtx_list[v1 - 1].tc[0] * texture_extra_multi) / 32.0f) / texture_height; - uv_list[v2 - 1].u = (bswap(vtx_list[v2 - 1].tc[0] * texture_extra_multi) / 32.0f) / texture_height; - uv_list[v3 - 1].u = (bswap(vtx_list[v3 - 1].tc[0] * texture_extra_multi) / 32.0f) / texture_height; - uv_list[v1 - 1].v = 1.0 - ((bswap(vtx_list[v1 - 1].tc[1] * texture_extra_multi) / 32.0f) / texture_width); - uv_list[v2 - 1].v = 1.0 - ((bswap(vtx_list[v2 - 1].tc[1] * texture_extra_multi) / 32.0f) / texture_width); - uv_list[v3 - 1].v = 1.0 - ((bswap(vtx_list[v3 - 1].tc[1] * texture_extra_multi) / 32.0f) / texture_width); + uv_list[v1 - 1].u = ((bswap(vtx_list[v1 - 1].tc[0]) * texture_extra_multi) / 32.0f) / texture_height; + uv_list[v2 - 1].u = ((bswap(vtx_list[v2 - 1].tc[0]) * texture_extra_multi) / 32.0f) / texture_height; + uv_list[v3 - 1].u = ((bswap(vtx_list[v3 - 1].tc[0]) * texture_extra_multi) / 32.0f) / texture_height; + uv_list[v1 - 1].v = 1.0 - (((bswap(vtx_list[v1 - 1].tc[1]) * texture_extra_multi) / 32.0f) / texture_width); + uv_list[v2 - 1].v = 1.0 - (((bswap(vtx_list[v2 - 1].tc[1]) * texture_extra_multi) / 32.0f) / texture_width); + uv_list[v3 - 1].v = 1.0 - (((bswap(vtx_list[v3 - 1].tc[1]) * texture_extra_multi) / 32.0f) / texture_width); } } else if (opcode == 0x2A) { // unpack_end_displaylist, // Only 1 byte, the opcode - face_string << "o object" << std::to_string(object_num++) << std::endl; - } else if ((opcode == 0x26) || (opcode == 0x27)) { - /** - * 0x26: unpack_texture_on, - * 0x27: unpack_texture_off, - **/ + dl_count += 1; + node_map[anode->address] = std::move(anode); + anode = std::make_shared(); + anode->address = dl_count * 8; + } else if (opcode == 0x26) { // unpack_texture_on, // Only 1 byte, the opcode - if (opcode == 0x26) { - texture_extra_multi = 0xFFFF / 65536.0f; - } else { - texture_extra_multi = 0x0001 / 65536.0f; - } - std::cout << std::to_string(texture_extra_multi) << std::endl; + dl_count += 1; + texture_extra_multi = 1.0f; + } else if(opcode == 0x27) { // unpack_texture_off, + // Only 1 byte, the opcode + dl_count += 1; + texture_extra_multi = 0.0f; } else if ( ((0x00 <= opcode) && (opcode <= 0x19)) || ((0x2D <= opcode) && (opcode <= 0x2F)) || @@ -273,18 +318,27 @@ int main(int argc, char **argv) { * 0x57: unpack_clear_geometry_mode, **/ // Only 1 byte, the opcode - ; - } else if ( - ((0x1A <= opcode) && (opcode <= 0x1F)) || - (opcode == 0x2B) || (opcode == 0x2C)) { + if ((0x00 <= opcode) && (opcode <= 0x14)) { + dl_count += 3; + } else { + dl_count += 1; + } + } else if (opcode == 0x2B) { // unpack_displaylist, + // 3 bytes including the opcode + dl_count += 1; + model_bytes.read(reinterpret_cast(&data_short), 2); + node_map[data_short * 8]->parent = anode; + anode->children.push_back(node_map[data_short * 8]); + } else if (((0x1A <= opcode) && (opcode <= 0x1F)) || (opcode == 0x2C)) { /** * 0x1A - 0x1F, 0x2C: unpack_tile_sync, - * 0x2B: unpack_displaylist, **/ // 3 bytes including the opcode + dl_count += 3; model_bytes.seekg(2, std::ios_base::cur); } else if ((0x20 <= opcode) && (opcode <= 0x25)) { // unpack_tile_load_sync, // 4 bytes including the opcode + dl_count += 5; switch (opcode) { case 0x20: texture_height = 32.0f; @@ -314,10 +368,11 @@ int main(int argc, char **argv) { model_bytes.read(reinterpret_cast(&data_byte), 1); texture_addr = data_byte; texture_addr <<= 11; - face_string << "usemtl " << model.textures[texture_addr] << std::endl; + anode->face_string << "usemtl " << model.textures[texture_addr] << std::endl; model_bytes.seekg(2, std::ios_base::cur); } else if (opcode == 0x30) { // unpack_spline_3D // 4 bytes including the opcode + dl_count += 1; model_bytes.seekg(3, std::ios_base::cur); } else { std::cout << "WARNING UNEXPECTED opcode: " << std::to_string(opcode) << std::endl; @@ -326,6 +381,12 @@ int main(int argc, char **argv) { } model_bytes.close(); + for (auto bnode : node_map) { + if (bnode.second->parent == nullptr) { + parse_tree(bnode.second, face_string); + } + } + for (auto tc : uv_list) { vtx_tc_string << "vt " << std::to_string(tc.second.u) << " " << std::to_string(tc.second.v) << std::endl; }