diff --git a/src/mod_converter/fragment_names.py b/src/mod_converter/fragment_names.py new file mode 100644 index 0000000..494f635 --- /dev/null +++ b/src/mod_converter/fragment_names.py @@ -0,0 +1,6 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + +import sys + +print(sorted(set(open(sys.argv[1]).readlines()))) \ No newline at end of file diff --git a/src/mod_converter/mod_converter.cpp b/src/mod_converter/mod_converter.cpp index a48ff1b..dee3973 100644 --- a/src/mod_converter/mod_converter.cpp +++ b/src/mod_converter/mod_converter.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -28,8 +29,6 @@ using namespace std; void convert_model(string fn) { - printf("%s\n", fn.c_str()); - buffer b(readFile(fn)); model m; m.load(b); @@ -41,44 +40,45 @@ void convert_model(string fn) throw std::logic_error(ss.str()); } - m.writeObj(fn + ".obj"); + m.writeObj(fn); } int main(int argc, char *argv[]) try { -#ifdef NDEBUG if (argc != 2) { - cout << "Usage:\n" << argv[0] << " model_file" << "\n"; + printf("Usage: %s model_file \n", argv[0]); return 1; } - read_model(argv[1]); -#else - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_GL_M1_A_ATTACKER"); - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_GL_M1_B_BASE"); - - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_BLD_BASE1"); - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_GL_S4_SINIGR"); - - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_GL_FIRE"); - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_GL_FARA"); - - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_FX_ANTI_MATER_GUN"); - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_UNFL_STONE01"); - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_L1_KUST"); - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_L6_KUST_12"); - convert_model("h:\\Games\\AIM\\data\\res0.pak.dir\\Data\\Models\\MOD_GL_M1_A_ATTACKER_DAMAGED"); -#endif + convert_model(argv[1]); return 0; } +catch (std::runtime_error &e) +{ + string error; + if (argv[1]) + error += argv[1]; + error += "\n"; + error += "fatal error: "; + error += e.what(); + error += "\n"; + if (argv[1]) + { + ofstream ofile(string(argv[1]) + ".error.txt"); + ofile << error; + } + return 1; +} catch (std::exception &e) { + printf("%s\n", argv[1]); printf("error: %s\n", e.what()); return 1; } catch (...) { + printf("%s\n", argv[1]); printf("error: unknown exception\n"); return 1; -} \ No newline at end of file +} diff --git a/src/mod_converter/mod_converter.py b/src/mod_converter/mod_converter.py index 07f4699..ede48bb 100644 --- a/src/mod_converter/mod_converter.py +++ b/src/mod_converter/mod_converter.py @@ -5,6 +5,11 @@ import argparse import os import subprocess +banned_ext = [ + '.obj', + '.txt' +] + def main(): parser = argparse.ArgumentParser(description='Batch models converter') parser.add_argument('--dir', dest='dir', help='path to directory with models') @@ -14,10 +19,10 @@ def main(): run(pargs.dir) def run(dir): - for file in os.listdir(dir): - if os.path.isdir(file): + for file in sorted(os.listdir(dir)): + if os.path.isdir(file) or os.path.splitext(file)[1] in banned_ext: continue - p = subprocess.Popen(['mod_converter.exe', file]) + p = subprocess.Popen(['mod_converter.exe', dir + '/' + file]) p.communicate() if __name__ == '__main__': diff --git a/src/mod_converter/model.cpp b/src/mod_converter/model.cpp index 2624100..5eebc20 100644 --- a/src/mod_converter/model.cpp +++ b/src/mod_converter/model.cpp @@ -19,6 +19,7 @@ #include "model.h" #include +#include #include #include @@ -33,8 +34,8 @@ void vertex::load(buffer &b, uint32_t flags) READ(b, vZ); READ(b, vY); - if (flags & F_WIND) - READ(b, wind); + if (flags & F_UNK0) + READ(b, unk0); READ(b, nX); READ(b, nZ); @@ -65,34 +66,58 @@ std::string vertex::printTex() const return s; } -void fragment::load(buffer &b) +void block::load(buffer &b) { // header READ(b, type); - READ(b, name0); - READ(b, name1); - READ(b, name2); - READ(b, name3); - READ(b, name4); - READ(b, unk0); + READ(b, name); + READ(b, tex_mask); + READ(b, tex_spec); + READ(b, tex3); + READ(b, tex4); + READ(b, LODs); READ(b, unk1); READ(b, unk2); READ(b, unk3); READ(b, size); READ(b, unk4); + if (size == 0) // critical error!!! cannot survive + throw std::runtime_error("model file has bad block size field"); + // data - buffer data(b, size); - READ(data, n_segments); - segments.resize(n_segments); - READ(data, header); - READ(data, triangles_mult_7); + buffer data = buffer(b, size); + + // we cannot process this type at the moment + if (type == BlockType::ParticleEmitter) + return; + + READ(data, n_animations); + animations.resize(n_animations); + READ(data, material); + + // unk + READ(data, unk_flags0); + READ(data, unk7); + READ(data, unk9); READ(data, unk10); + READ(data, auto_animation); + READ(data, animation_cycle); + READ(data, unk8); + READ(data, unk11); + READ(data, unk12); + READ(data, triangles_mult_7); + // + + READ(data, additional_params); + READ(data, n_damage_models); + damage_models.resize(n_damage_models); + READ(data, rot); READ(data, flags); READ(data, n_vertex); vertices.resize(n_vertex); READ(data, n_triangles); - if (triangles_mult_7) + if (triangles_mult_7 && (flags & F_UNK0) && !unk11) n_triangles *= 7; triangles.resize(n_triangles); for (auto &v : vertices) @@ -100,76 +125,31 @@ void fragment::load(buffer &b) for (auto &t : triangles) READ(data, t); - // segments - for (auto &seg : segments) + // animations + for (auto &a : animations) + a.load(data); + for (auto &dm : damage_models) + dm.load(data); + + if (!data.eof() && triangles_mult_7) { - uint32_t type; - READ(data, type); - switch (type) - { - case 1: - seg = new segment1; - break; - case 2: - seg = new segment2; - break; - case 6: - seg = new segment6; - break; - default: - throw std::logic_error("unknown segment type " + std::to_string(type)); - } - seg->type = type; - seg->load(data); + // unknown end of block + auto triangles2 = triangles; + triangles2.resize((data.getSize() - data.getIndex()) / sizeof(triangle)); + for (auto &t : triangles2) + READ(data, t); } - if (!data.eof()) - throw std::logic_error("extraction error: fragment #" + std::string(name0)); + throw std::logic_error("extraction error: block #" + std::string(name)); } -void segment1::load(buffer &b) +void damage_model::load(buffer &b) { - READ(b, name); - READ(b, unk0); - triangles.resize(unk0[0][0]); - unk1.resize(unk0[0][0]); - for (int i = 0; i < 2; i++) - { - for (auto &t : triangles) - READ(b, t); - for (auto &unk: unk1) - READ(b, unk); - } -} - -void segment2::load(buffer &b) -{ - READ(b, name); - READ(b, unk0); - triangles.resize(unk0[0][0]); - unk1.resize(unk0[0][0]); - unk1_1.resize(unk0[0][0]); - for (auto &t : triangles) - READ(b, t); - for (auto &unk : unk1) - READ(b, unk); - for (auto &unk : unk1_1) - READ(b, unk); - while (!b.eof()) - { - repeater r; - r.load(b); - unk2.push_back(r); - } -} - -void segment2::repeater::load(buffer &b) -{ - READ(b, unk2); - triangles2.resize(unk2); + READ(b, n_polygons); + polygons.resize(n_polygons); READ(b, unk8); - READ(b, unk3); - for (auto &t : triangles2) + READ(b, name); + for (auto &t : polygons) READ(b, t); READ(b, unk6); READ(b, flags); @@ -183,60 +163,87 @@ void segment2::repeater::load(buffer &b) READ(b, t); } -void segment6::load(buffer &b) +void animation::load(buffer &b) { + READ(b, type); READ(b, name); + for (auto &s : segments) + s.loadHeader(b); + if (segments[0].n) + for (auto &s : segments) + s.loadData(b); +} + +void animation::segment::loadHeader(buffer &b) +{ + READ(b, n); READ(b, unk0); - triangles.resize(unk0[0][0]); - for (int i = 0; i < 1; i++) + READ(b, unk1); +} + +void animation::segment::loadData(buffer &b) +{ + if (n == 0) + return; + if (unk0) { + triangles.resize(n); for (auto &t : triangles) READ(b, t); - char unk1[0x30]; // some 6 floats - for (int i = 0; i < unk0[0][0]; i++) - READ(b, unk1); } + unk2.resize(n); + for (auto &unk : unk2) + READ(b, unk); } void model::load(buffer &b) { - READ(b, n_fragments); + READ(b, n_blocks); + if (n_blocks > 1000) // probably bad file + throw std::runtime_error("Model file has bad block count (should be <= 1000). Probably not a model."); READ(b, header); - fragments.resize(n_fragments); - for (auto &f : fragments) + blocks.resize(n_blocks); + for (auto &f : blocks) f.load(b); } void model::writeObj(std::string fn) { - ofstream o(fn); - o << "# " << "\n"; - o << "# A.I.M. Model Converter (ver. " << version() << ")\n"; - o << "# " << "\n"; - o << "\n"; - - if (fragments.empty()) - return; - - const auto &f = fragments[0]; - for (auto &v : f.vertices) - o << v.printVertex() << "\n"; - o << "\n"; - for (auto &v : f.vertices) - o << v.printNormal() << "\n"; - o << "\n"; - for (auto &v : f.vertices) - o << v.printTex() << "\n"; - o << "\n"; - - for (int i = 0; i <= f.n_triangles - 3; i += 3) + for (auto &f : blocks) { - auto x = to_string(f.triangles[i] + 1); - auto y = to_string(f.triangles[i + 2] + 1); - auto z = to_string(f.triangles[i + 1] + 1); - x += "/" + x; - y += "/" + y; - z += "/" + z; - o << "f " << x << " " << y << " " << z << "\n"; + ofstream o(fn + "." + f.name + ".obj"); + o << "#" << "\n"; + o << "# A.I.M. Model Converter (ver. " << version() << ")\n"; + o << "#" << "\n"; + o << "\n"; + o << "o " << f.name << "\n"; + o << "g " << f.name << "\n"; + o << "s off\n"; + o << "\n"; + + for (auto &v : f.vertices) + o << v.printVertex() << "\n"; + o << "\n"; + for (auto &v : f.vertices) + o << v.printNormal() << "\n"; + o << "\n"; + for (auto &v : f.vertices) + o << v.printTex() << "\n"; + o << "\n"; + + if (f.n_triangles) + for (int i = 0; i <= f.n_triangles - 3; i += 3) + { + auto x = to_string(f.triangles[i] + 1); + auto y = to_string(f.triangles[i + 2] + 1); + auto z = to_string(f.triangles[i + 1] + 1); + x += "/" + x; + y += "/" + y; + z += "/" + z; + o << "f " << x << " " << y << " " << z << "\n"; + } + + o << "\n"; + o << "\n"; } } diff --git a/src/mod_converter/model.h b/src/mod_converter/model.h index 3937f2f..21128b0 100644 --- a/src/mod_converter/model.h +++ b/src/mod_converter/model.h @@ -26,7 +26,38 @@ class buffer; enum { - F_WIND = 0x4, + F_UNK0 = 0x4, +}; + +enum class AdditionalParameter : uint32_t +{ + None, + DetalizationCoefficient +}; + +enum class ModelRotation : uint32_t +{ + None, + Vertical, + Horizontal, + Other +}; + +enum class BlockType : uint32_t +{ + VisibleObject, + HelperObject, + BitmapAlpha, + BitmapGrass, + ParticleEmitter +}; + +struct Vector4 +{ + float x; + float y; + float z; + float w; }; struct vertex @@ -35,7 +66,7 @@ struct vertex float vZ; float vY; - float wind; + float unk0; float nX; float nZ; @@ -53,76 +84,96 @@ struct vertex typedef uint16_t triangle; -struct unk_float3x4 +struct animation { - float unk[4][3]; -}; - -struct unk_float6 -{ - float unk[6]; -}; - -struct segment -{ - uint32_t type; - - virtual void load(buffer &b) = 0; -}; - -struct segment1 : public segment -{ - char name[0xC]; - uint32_t unk0[4][3]; - std::vector triangles; - std::vector unk1; - - virtual void load(buffer &b); -}; - -struct segment2 : public segment -{ - struct repeater + // +1 +0.5 -0.5 +1 + struct segment { - uint32_t unk2; - float unk8[3]; - char unk3[0x3C]; - std::vector triangles2; - uint8_t unk6; - uint32_t flags; - uint32_t n_vertex; - uint32_t n_triangles; - std::vector vertices; - std::vector triangles; - - virtual void load(buffer &b); + struct unk_float6 + { + float unk[6]; + }; + + uint32_t n; + uint32_t unk0; + uint32_t unk1; + + std::vector triangles; + std::vector unk2; + + void loadHeader(buffer &b); + void loadData(buffer &b); }; + uint32_t type; char name[0xC]; - uint32_t unk0[4][3]; - std::vector triangles; - std::vector unk1; - std::vector unk1_1; - std::vector unk2; - + segment segments[4]; + virtual void load(buffer &b); }; -struct segment6 : public segment1 +struct damage_model { + uint32_t n_polygons; + float unk8[3]; + char name[0x3C]; + std::vector polygons; + uint8_t unk6; + uint32_t flags; + uint32_t n_vertex; + uint32_t n_triangles; + std::vector vertices; + std::vector triangles; + virtual void load(buffer &b); }; -struct fragment +struct material +{ + Vector4 ambient; + Vector4 diffuse; + Vector4 specular; + Vector4 emissive; + float power; +}; + +struct rotation +{ + ModelRotation type; + float speed; + // center of rotating axis + float x; + float y; + float z; +}; + +struct additional_parameters +{ + AdditionalParameter params; + float detalization_koef; +}; + +struct block { // header - uint32_t type; - char name0[0x20]; - char name1[0x20]; - char name2[0x20]; - char name3[0x20]; - char name4[0x20]; - uint32_t unk0; + BlockType type; + char name[0x20]; + char tex_mask[0x20]; + char tex_spec[0x20]; + char tex3[0x20]; + char tex4[0x20]; + union // LODs + { + struct + { + uint8_t lod1 : 1; + uint8_t lod2 : 1; + uint8_t lod3 : 1; + uint8_t lod4 : 1; + uint8_t : 4; + } _; + uint32_t LODs; + }; uint32_t unk1; uint32_t unk2[2]; uint32_t unk3; @@ -130,27 +181,43 @@ struct fragment uint32_t unk4[10]; // data - uint32_t n_segments; - char header[0x68]; + uint32_t n_animations; + material material; + + //unk (anim + transform settings?) + uint32_t unk_flags0; + uint32_t unk7; + float unk9; + uint32_t unk10; + uint32_t auto_animation; + float animation_cycle; + float unk8; + uint32_t unk11; + uint32_t unk12; uint32_t triangles_mult_7; - char unk10[0x20]; + // + + additional_parameters additional_params; + uint32_t n_damage_models; + rotation rot; uint32_t flags; uint32_t n_vertex; uint32_t n_triangles; std::vector vertices; std::vector triangles; - // segments - std::vector segments; + // animations + std::vector animations; + std::vector damage_models; void load(buffer &b); }; struct model { - int n_fragments; + int n_blocks; char header[0x40]; - std::vector fragments; + std::vector blocks; void load(buffer &b); void writeObj(std::string fn);