./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 / 145
0.0%
Branches:
0 / 236
0.0%
Functions:
0 / 12
0.0%

libs/io/src/eu/io/mesh.cc
Line Branch Exec Source
1 #include "eu/io/mesh.h"
2
3 #include "eu/core/geom.builder.h"
4
5 #include "eu/io/file.h"
6
7 #include "cgltf.h"
8
9 namespace eu::io
10 {
11
12 namespace
13 {
14
15 struct GltfFile
16 {
17 cgltf_data* data = nullptr;
18
19 ✗ void clear()
20 {
21
0/2
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 4 not taken.
✗ if (data == nullptr)
22 {
23 ✗ return;
24 }
25
26 ✗ cgltf_free(data);
27 ✗ data = nullptr;
28 }
29
30 ✗ bool load_gltf_file(const std::string& path)
31 {
32 ✗ constexpr cgltf_options options = {};
33 ✗ data = nullptr;
34
35 // const auto bytes = bytes_from_file(path);
36
37
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 43 not taken.
✗ cgltf_result result = cgltf_parse_file(&options, path.c_str(), &data);
38 // cgltf_result result = cgltf_parse(&options, bytes.data(), bytes.size(), &data);
39
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 11 not taken.
✗ if (result != cgltf_result_success)
40 {
41
0/2
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 43 not taken.
✗ clear();
42
0/6
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 43 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 33 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 31 not taken.
✗ LOG_ERR("Could not parse file: {}", path);
43 ✗ return false;
44 }
45
46
0/2
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 43 not taken.
✗ result = cgltf_load_buffers(&options, data, path.c_str());
47
0/2
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 20 not taken.
✗ if (result != cgltf_result_success)
48 {
49
0/2
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 43 not taken.
✗ clear();
50
0/6
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 43 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 37 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 35 not taken.
✗ LOG_ERR("Could not load buffers for: {}", path);
51 ✗ return false;
52 }
53
54
0/2
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 43 not taken.
✗ result = cgltf_validate(data);
55
0/2
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 28 not taken.
✗ if (result != cgltf_result_success)
56 {
57
0/2
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 43 not taken.
✗ clear();
58
0/6
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 43 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 41 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 39 not taken.
✗ LOG_ERR("GLTF file validation FAILED: {}", path);
59 ✗ return false;
60 }
61 ✗ return true;
62 }
63 };
64
65
66 ✗ std::optional<std::size_t> find_node_index(
67 cgltf_node* target, cgltf_node* all_nodes, std::size_t node_count
68 )
69 {
70
0/2
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 6 not taken.
✗ if (target == nullptr)
71 {
72 ✗ return std::nullopt;
73 }
74
0/2
✗ Branch 12 → 7 not taken.
✗ Branch 12 → 13 not taken.
✗ for (std::size_t node_index = 0; node_index < node_count; ++node_index)
75 {
76
0/2
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 11 not taken.
✗ if (target == &all_nodes[node_index])
77 {
78 ✗ return node_index;
79 }
80 }
81 ✗ return std::nullopt;
82 }
83
84 ✗ std::vector<float> scalar_values_from_accessor(
85 std::size_t component_count, const cgltf_accessor& accessor
86 )
87 {
88 ✗ std::vector<float> ret;
89
0/2
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 10 not taken.
✗ ret.resize(accessor.count * component_count);
90
91
0/2
✗ Branch 7 → 4 not taken.
✗ Branch 7 → 8 not taken.
✗ for (cgltf_size accessor_index = 0; accessor_index < accessor.count; ++accessor_index)
92 {
93
0/2
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 10 not taken.
✗ cgltf_accessor_read_float(&accessor, accessor_index, &ret[accessor_index * component_count], component_count);
94 }
95 ✗ return ret;
96 ✗ }
97
98 ✗ void extract_mesh_from_attribute(
99 core::geom::Builder& out_mesh,
100 cgltf_attribute& attribute,
101 cgltf_skin* skin,
102 cgltf_node* nodes,
103 unsigned int node_count
104 )
105 {
106 ✗ const cgltf_attribute_type attrib_type = attribute.type;
107 ✗ const cgltf_accessor& accessor = *attribute.data;
108
109 ✗ unsigned int component_count = 0;
110
0/2
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 4 not taken.
✗ if (accessor.type == cgltf_type_vec2)
111 {
112 ✗ component_count = 2;
113 }
114
0/2
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
✗ else if (accessor.type == cgltf_type_vec3)
115 {
116 ✗ component_count = 3;
117 }
118
0/2
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
✗ else if (accessor.type == cgltf_type_vec4)
119 {
120 ✗ component_count = 4;
121 }
122
0/2
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 70 not taken.
✗ const auto values = scalar_values_from_accessor(component_count, accessor);
123 ✗ const auto accessor_count = accessor.count;
124
125
0/2
✗ Branch 52 → 10 not taken.
✗ Branch 52 → 53 not taken.
✗ for (std::size_t accessor_index = 0; accessor_index < accessor_count; ++accessor_index)
126 {
127 ✗ const auto index = accessor_index * component_count;
128
0/6
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 17 not taken.
✗ Branch 10 → 22 not taken.
✗ Branch 10 → 29 not taken.
✗ Branch 10 → 37 not taken.
✗ Branch 10 → 50 not taken.
✗ switch (attrib_type)
129 {
130 ✗ case cgltf_attribute_type_position:
131
0/2
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 55 not taken.
✗ out_mesh.add_position(v3(values[index + 0], values[index + 1], values[index + 2])
132 );
133 ✗ break;
134 ✗ case cgltf_attribute_type_texcoord:
135 // note: eu 2d coordinate system is bottom left going up, gltf is top left going down, `1-y` fixes this
136
0/2
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 56 not taken.
✗ out_mesh.add_text_coord(v2(values[index + 0], 1-values[index + 1]));
137 ✗ break;
138 ✗ case cgltf_attribute_type_weights:
139
0/4
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 57 not taken.
✗ Branch 27 → 28 not taken.
✗ Branch 27 → 57 not taken.
✗ out_mesh.add_weight(v4(values[index + 0], values[index + 1], values[index + 2], values[index + 3]));
140 ✗ break;
141 ✗ case cgltf_attribute_type_normal:
142 {
143 ✗ const auto normal = v3(values[index + 0], values[index + 1], values[index + 2]);
144
0/4
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 58 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 58 not taken.
✗ out_mesh.add_normal(normal.get_normalized().value_or(kk::up));
145 }
146 ✗ break;
147 ✗ case cgltf_attribute_type_joints:
148 {
149
0/2
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 44 not taken.
✗ if (skin != nullptr)
150 {
151 ✗ const auto joint_at = [&](std::size_t joint_index) -> std::size_t
152 {
153 ✗ const auto found = std::lround(values[index + joint_index]);
154 ✗ const auto ret = static_cast<int>(std::max<std::size_t>(
155 ✗ 0, find_node_index(skin->joints[found], nodes, node_count).value_or(0)
156 ✗ ));
157 ✗ return ret;
158 ✗ };
159
0/10
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 61 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 61 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 61 not taken.
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 61 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 61 not taken.
✗ out_mesh.add_influence({joint_at(0), joint_at(1), joint_at(2), joint_at(3)});
160 }
161 else
162 {
163
0/6
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 67 not taken.
✗ Branch 45 → 46 not taken.
✗ Branch 45 → 65 not taken.
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 63 not taken.
✗ LOG_WARN("Mesh has joints but is missing skin");
164 }
165 }
166 ✗ break;
167 ✗ default:
168 //ignore
169 ✗ break;
170 }
171 }
172 ✗ }
173
174 ✗ core::geom::Vertex vert(std::size_t index)
175 {
176 ✗ return core::geom::Vertex{index, index};
177 }
178
179 ✗ core::Geom create_geom_from_gltf_primitive(const cgltf_data& data, const cgltf_node& node, const cgltf_primitive& prim)
180 {
181 ✗ core::geom::Builder mesh;
182
183 ✗ cgltf_node* nodes = data.nodes;
184 ✗ const auto node_count = data.nodes_count;
185
186 ✗ const auto attribute_count = prim.attributes_count;
187
0/2
✗ Branch 5 → 3 not taken.
✗ Branch 5 → 6 not taken.
✗ for (unsigned int attribute_index = 0; attribute_index < attribute_count; ++attribute_index)
188 {
189 ✗ cgltf_attribute* attribute = &prim.attributes[attribute_index];
190 ✗ extract_mesh_from_attribute(
191
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 35 not taken.
✗ mesh, *attribute, node.skin, nodes, static_cast<unsigned int>(node_count)
192 );
193 }
194
0/2
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 23 not taken.
✗ if (prim.indices != nullptr)
195 {
196
0/2
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 35 not taken.
✗ mesh.faces.reserve(prim.indices->count / 3);
197 ✗ const auto index_count = prim.indices->count;
198
199
0/2
✗ Branch 22 → 9 not taken.
✗ Branch 22 → 23 not taken.
✗ for (std::size_t index_index = 0; index_index < index_count; index_index += 3)
200 {
201
0/2
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 35 not taken.
✗ const auto a1 = cgltf_accessor_read_index(prim.indices, index_index);
202
0/2
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 35 not taken.
✗ const auto b2 = cgltf_accessor_read_index(prim.indices, index_index + 1);
203
0/2
✗ Branch 11 → 12 not taken.
✗ Branch 11 → 35 not taken.
✗ const auto c3 = cgltf_accessor_read_index(prim.indices, index_index + 2);
204
0/10
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 33 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 33 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 33 not taken.
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 30 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 28 not taken.
✗ mesh.add_face({ vert(a1), vert(b2), vert(c3) });
205 }
206 }
207
208
0/2
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 35 not taken.
✗ return mesh.to_geom();
209 ✗ }
210
211 ✗ m4 transform_from_node_rec(const cgltf_node& node)
212 {
213
0/4
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 4 not taken.
✗ Branch 3 → 5 not taken.
✗ Branch 3 → 27 not taken.
✗ const m4 parent = node.parent != nullptr ? transform_from_node_rec(*node.parent) : m4_identity;
214
0/6
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 8 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 23 not taken.
✗ Branch 7 → 9 not taken.
✗ Branch 7 → 23 not taken.
✗ const m4 translate = node.has_translation != 0 ? m4::from_translation(v3(node.translation)) : m4_identity;
215
0/4
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 12 not taken.
✗ Branch 11 → 13 not taken.
✗ Branch 11 → 24 not taken.
✗ const m4 rotate = node.has_rotation != 0 ? m4::from(Q(node.rotation)) : m4_identity;
216
0/6
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 16 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 25 not taken.
✗ Branch 15 → 17 not taken.
✗ Branch 15 → 25 not taken.
✗ const m4 scale = node.has_scale != 0 ? m4::from_scale(v3(node.scale)) : m4_identity;
217
218
0/4
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 26 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 26 not taken.
✗ const auto transform = translate * rotate * scale;
219
0/2
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 27 not taken.
✗ return parent * transform;
220 }
221
222 ✗ core::Mesh extract_meshes_from_gltf(const cgltf_data& data, const std::string& file)
223 {
224 ✗ core::Mesh result;
225
226
0/2
✗ Branch 35 → 3 not taken.
✗ Branch 35 → 36 not taken.
✗ for (std::size_t node_index = 0; node_index < data.nodes_count; ++node_index)
227 {
228 ✗ const cgltf_node& node = data.nodes[node_index];
229
0/2
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
✗ if (node.mesh == nullptr)
230 {
231 ✗ continue;
232 }
233
234 ✗ core::TransformedMesh transform;
235
0/4
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 45 not taken.
✗ transform.name = node.mesh->name != nullptr ? node.mesh->name : "unnamed_mesh";
236
0/2
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 45 not taken.
✗ transform.transform = transform_from_node_rec(node);
237
238
0/2
✗ Branch 28 → 12 not taken.
✗ Branch 28 → 29 not taken.
✗ for (std::size_t primtive_index = 0; primtive_index < node.mesh->primitives_count; ++primtive_index)
239 {
240
0/2
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 44 not taken.
✗ const auto mesh = create_geom_from_gltf_primitive(data, node, node.mesh->primitives[primtive_index]);
241
0/6
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 17 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 25 not taken.
✗ if (mesh.faces.empty() || mesh.vertices.empty())
242 {
243
0/6
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 42 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 40 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 38 not taken.
✗ LOG_WARN("Mesh has no mesh data in gltf file: {}", file);
244 }
245 else
246 {
247
0/2
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 42 not taken.
✗ transform.geoms.emplace_back(mesh);
248 }
249 ✗ }
250
251
0/2
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 45 not taken.
✗ result.meshes.emplace_back(std::move(transform));
252 ✗ }
253
254 ✗ return result;
255 ✗ }
256
257 ✗ core::Mesh unable_to_load_geom()
258 {
259 return
260 {
261 .meshes = {
262 core::TransformedMesh
263 {
264 ✗ .name = "unable_to_load_geom",
265 .transform = m4_identity,
266 .geoms = {
267 core::MeshGeom
268 {
269 ✗ .geom = eu::core::geom::create_box(1.0f, 1.0f, 1.0f, core::geom::NormalsFacing::Out).to_geom()
270 }
271 }
272 ✗ }
273 }
274
0/6
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 25 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 15 not taken.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
✗ };
275
0/24
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 49 not taken.
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 39 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 37 not taken.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 32 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 19 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 36 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 44 not taken.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 48 not taken.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 56 not taken.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 56 not taken.
✗ }
276 }
277
278 ✗ core::Mesh mesh_from_file(const std::string& file)
279 {
280 ✗ GltfFile gltf;
281
0/4
✗ Branch 2 → 3 not taken.
✗ Branch 2 → 34 not taken.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 10 not taken.
✗ if (gltf.load_gltf_file(file) == false)
282 {
283
0/6
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 34 not taken.
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 26 not taken.
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 24 not taken.
✗ LOG_ERR("Failed to load gltf file: {}", file);
284
0/2
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 34 not taken.
✗ return unable_to_load_geom();
285 }
286
287
0/2
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 34 not taken.
✗ const auto ret = extract_meshes_from_gltf(*gltf.data, file);
288
0/2
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 19 not taken.
✗ if (ret.meshes.empty())
289 {
290
0/6
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 32 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 30 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 28 not taken.
✗ LOG_ERR("No meshes found in gltf file: {}", file);
291
0/2
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 32 not taken.
✗ return unable_to_load_geom();
292 }
293
294
0/2
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 32 not taken.
✗ return ret;
295 ✗ }
296
297 }
298
299