GCC Code Coverage Report


Directory: ./
File: src/mdlx_library/converter_obj.cpp
Date: 2026-04-01 15:09:43
Exec Total Coverage
Lines: 76 87 87.4%
Functions: 6 6 100.0%
Branches: 57 106 53.8%

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