| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <algorithm> | ||
| 2 | #include <expected> | ||
| 3 | #include <spanstream> | ||
| 4 | #include <spdlog/spdlog.h> | ||
| 5 | #include <mdlx/mdlx.hpp> | ||
| 6 | #include <assimp/Exporter.hpp> | ||
| 7 | #include <assimp/scene.h> | ||
| 8 | |||
| 9 | #include "assets_mpq_importer/mdlx.hpp" | ||
| 10 | |||
| 11 | |||
| 12 | namespace assmpq::mdlx { | ||
| 13 | |||
| 14 | // NOLINTBEGIN(cppcoreguidelines-owning-memory, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-bounds-constant-array-index) | ||
| 15 | 1 | static void process_vertices(const wc3lib::mdlx::Geoset& geoset, aiMesh& mesh) | |
| 16 | { | ||
| 17 | 1 | const auto& vertices = geoset.vertices(); | |
| 18 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if(vertices.empty()) { |
| 19 | ✗ | mesh.mNumVertices = 0; | |
| 20 | ✗ | mesh.mVertices = nullptr; | |
| 21 | ✗ | return; | |
| 22 | } | ||
| 23 | |||
| 24 | 1 | mesh.mPrimitiveTypes = aiPrimitiveType_TRIANGLE; | |
| 25 | 1 | mesh.mNumVertices = static_cast<unsigned>(vertices.size()); | |
| 26 |
3/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 1 times.
|
5 | mesh.mVertices = new aiVector3D[vertices.size()]; |
| 27 | |||
| 28 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
|
5 | for(size_t i = 0; i < vertices.size(); ++i) { |
| 29 | 4 | mesh.mVertices[i] = { vertices[i].x(), vertices[i].y(), vertices[i].z() }; | |
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | 1 | static void process_normals(const wc3lib::mdlx::Geoset& geoset, aiMesh& mesh) | |
| 34 | { | ||
| 35 | 1 | const auto& normals = geoset.normals(); | |
| 36 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if(normals.empty()) { |
| 37 | ✗ | mesh.mNormals = nullptr; | |
| 38 | ✗ | return; | |
| 39 | } | ||
| 40 | |||
| 41 |
3/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 1 times.
|
5 | mesh.mNormals = new aiVector3D[normals.size()]; |
| 42 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
|
5 | for(size_t i = 0; i < normals.size(); ++i) { |
| 43 | 4 | mesh.mNormals[i] = { normals[i].x(), normals[i].y(), normals[i].z() }; | |
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | 1 | static void process_texture_patches(const wc3lib::mdlx::Geoset& geoset, aiMesh& mesh) | |
| 48 | { | ||
| 49 | 1 | const auto& uv_patches = geoset.texturePatches(); | |
| 50 | |||
| 51 |
1/2✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | std::ranges::fill(mesh.mTextureCoords, nullptr); |
| 52 | |||
| 53 | 1 | const size_t patches_size = std::min(uv_patches.size(), static_cast<size_t>(AI_MAX_NUMBER_OF_TEXTURECOORDS)); | |
| 54 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | for(size_t i = 0; i < patches_size; ++i) { |
| 55 | 1 | const auto& uv_patch = uv_patches[i]; | |
| 56 | |||
| 57 | 1 | mesh.mNumUVComponents[i] = 2; | |
| 58 |
3/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 1 times.
|
5 | mesh.mTextureCoords[i] = new aiVector3D[uv_patch.size()]; |
| 59 | |||
| 60 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
|
5 | for(size_t j = 0; j < uv_patch.size(); ++j) { |
| 61 | 4 | mesh.mTextureCoords[i][j] = aiVector3D(uv_patch[j].x(), 1.0F - uv_patch[j].y(), 0.0F); | |
| 62 | } | ||
| 63 | } | ||
| 64 | 1 | } | |
| 65 | |||
| 66 | 1 | static void process_faces(const wc3lib::mdlx::Geoset& geoset, aiMesh& mesh) | |
| 67 | { | ||
| 68 |
2/2✓ Branch 15 taken 1 times.
✓ Branch 16 taken 1 times.
|
2 | for(const auto& face_type : geoset.faces()) { |
| 69 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (face_type.type() != wc3lib::mdlx::Faces::Type::Triangles) { |
| 70 | ✗ | spdlog::error("Only triangle primitives are supported."); | |
| 71 | ✗ | continue; | |
| 72 | } | ||
| 73 | |||
| 74 | 1 | const auto& face_verices = face_type.vertices(); | |
| 75 | 1 | const auto triangle_faces_size = face_verices.size() / 3; | |
| 76 | |||
| 77 | 1 | mesh.mNumFaces = static_cast<unsigned>(triangle_faces_size); | |
| 78 |
4/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 1 times.
|
3 | mesh.mFaces = new aiFace[triangle_faces_size]; |
| 79 | |||
| 80 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for(size_t i = 0; i < triangle_faces_size; ++i) { |
| 81 | 2 | mesh.mFaces[i].mNumIndices = 3; | |
| 82 |
1/2✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
2 | mesh.mFaces[i].mIndices = new unsigned int[3]; |
| 83 | 2 | mesh.mFaces[i].mIndices[0] = static_cast<unsigned>(face_verices[(i * 3) + 0]); | |
| 84 | 2 | mesh.mFaces[i].mIndices[1] = static_cast<unsigned>(face_verices[(i * 3) + 1]); | |
| 85 | 2 | mesh.mFaces[i].mIndices[2] = static_cast<unsigned>(face_verices[(i * 3) + 2]); | |
| 86 | } | ||
| 87 | } | ||
| 88 | 1 | } | |
| 89 | |||
| 90 | 1 | static void process_scene(const wc3lib::mdlx::Mdlx& model, aiScene& scene, const std::string& mesh_name) | |
| 91 | { | ||
| 92 | 1 | const size_t geoset_count = model.geosets().size(); | |
| 93 | |||
| 94 |
1/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
1 | scene.mRootNode = new aiNode(); |
| 95 | |||
| 96 | 1 | scene.mRootNode->mNumMeshes = static_cast<unsigned>(geoset_count); | |
| 97 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | scene.mRootNode->mMeshes = new unsigned int[geoset_count]; |
| 98 | |||
| 99 | 1 | scene.mNumMeshes = static_cast<unsigned>(geoset_count); | |
| 100 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | scene.mMeshes = new aiMesh*[geoset_count]; |
| 101 | |||
| 102 | 1 | scene.mNumMaterials = static_cast<unsigned>(geoset_count); | |
| 103 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | scene.mMaterials = new aiMaterial*[geoset_count]; |
| 104 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
|
2 | for (size_t i = 0; i < scene.mNumMaterials; ++i) { |
| 105 |
1/4✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
1 | scene.mMaterials[i] = new aiMaterial; |
| 106 | } | ||
| 107 | |||
| 108 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
|
2 | for (size_t geoset_idx = 0; geoset_idx < geoset_count; ++geoset_idx) { |
| 109 |
1/2✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
1 | const auto& geoset = model.geosets().at(geoset_idx); |
| 110 | |||
| 111 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | aiMesh* mesh_ptr = scene.mMeshes[geoset_idx] = new aiMesh(); |
| 112 | |||
| 113 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (geoset_count > 1) { |
| 114 | ✗ | mesh_ptr->mName = std::format("{}_{}", mesh_name, geoset_idx); | |
| 115 | } else { | ||
| 116 | 1 | mesh_ptr->mName = mesh_name; | |
| 117 | } | ||
| 118 | |||
| 119 | 1 | mesh_ptr->mMaterialIndex = static_cast<unsigned>(geoset_idx); | |
| 120 | |||
| 121 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | process_vertices(geoset, *mesh_ptr); |
| 122 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | process_normals(geoset, *mesh_ptr); |
| 123 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | process_texture_patches(geoset, *mesh_ptr); |
| 124 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | process_faces(geoset, *mesh_ptr); |
| 125 | |||
| 126 | 1 | scene.mRootNode->mMeshes[geoset_idx] = static_cast<unsigned>(geoset_idx); // Index of the mesh in scene.mMeshes | |
| 127 | } | ||
| 128 | 1 | } | |
| 129 | // NOLINTEND(cppcoreguidelines-owning-memory, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-bounds-constant-array-index) | ||
| 130 | |||
| 131 | 1 | auto convert_mdlx_to_obj_mesh(const std::string& mesh_name, const FileData& mdx_file)-> std::expected<FileData, ErrorMessage> | |
| 132 | try { | ||
| 133 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | wc3lib::mdlx::Mdlx model; |
| 134 | |||
| 135 |
1/2✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | std::ispanstream input(mdx_file); |
| 136 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | model.read(input); |
| 137 | |||
| 138 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | aiScene scene; |
| 139 | |||
| 140 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | process_scene(model, scene, mesh_name); |
| 141 | |||
| 142 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | Assimp::Exporter exporter; |
| 143 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | const aiExportDataBlob* blob = exporter.ExportToBlob(&scene, "obj"); |
| 144 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (blob == nullptr) { |
| 145 | ✗ | return std::unexpected(std::format("Error exporting scene: {}", exporter.GetErrorString())); | |
| 146 | } | ||
| 147 | |||
| 148 |
1/2✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
3 | std::vector<char> output_buffer(blob->size); |
| 149 |
1/2✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | std::ospanstream output(output_buffer, std::ios::out | std::ios::binary); |
| 150 |
1/2✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
1 | output.write(static_cast<const char*>(blob->data), static_cast<std::streamsize>(blob->size)); |
| 151 | |||
| 152 | 1 | return output_buffer; | |
| 153 |
0/2✗ Branch 18 not taken.
✗ Branch 19 not taken.
|
1 | } catch (std::exception &e) { |
| 154 | ✗ | return std::unexpected(e.what()); | |
| 155 | ✗ | } | |
| 156 | |||
| 157 | |||
| 158 | } // namespace assmpq::mdlx | ||
| 159 |