TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
69 / 69
100.0%
Branches:
101 / 103
98.1%
Functions:
6 / 6
100.0%

mesh/io/obj.cpp
Line Branch Exec Source
1 #include "terraingen/mesh/io/formats.h"
2
3 #define TINYOBJLOADER_IMPLEMENTATION
4 #include <tiny_obj_loader.h>
5
6 #include <array>
7 #include <cstdio>
8 #include <set>
9 #include <stdexcept>
10 #include <string>
11
12 namespace terraingen::detail {
13
14 namespace {
15
16 /// `value` with 9 significant digits, negative zero written as 0.
17 261138 std::string Number(double value) {
18 261138 std::array<char, 32> buffer{};
19
2/2
✓ Branch 0 taken 260383 times.
✓ Branch 1 taken 755 times.
783414 std::snprintf(buffer.data(), buffer.size(), "%.9g", value == 0.0 ? 0.0 : value);
20
1/1
✓ Branch 1 taken 261138 times.
522276 return buffer.data();
21 }
22
23 /// An OBJ line of `tag` and a vector's components.
24 87036 std::string VectorLine(const char* tag, const Vec3d& v) {
25
11/11
✓ Branch 1 taken 87036 times.
✓ Branch 4 taken 87036 times.
✓ Branch 7 taken 87036 times.
✓ Branch 10 taken 87036 times.
✓ Branch 13 taken 87036 times.
✓ Branch 16 taken 87036 times.
✓ Branch 19 taken 87036 times.
✓ Branch 22 taken 87036 times.
✓ Branch 25 taken 87036 times.
✓ Branch 28 taken 87036 times.
✓ Branch 31 taken 87036 times.
261108 return std::string(tag) + " " + Number(v.x) + " " + Number(v.y) + " " + Number(v.z) + "\n";
26 }
27
28 /// The MTL text of every distinct material of `meshes`, by name.
29 10 std::string Materials(const std::vector<SceneMesh>& meshes) {
30 10 std::set<std::string> written;
31
1/1
✓ Branch 1 taken 10 times.
10 std::string mtl = "# TerrainGen\n";
32
2/2
✓ Branch 5 taken 21 times.
✓ Branch 6 taken 10 times.
31 for (const SceneMesh& scene : meshes) {
33 21 const MeshMaterial& material = scene.material;
34
3/3
✓ Branch 1 taken 21 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 10 times.
21 if (!written.insert(material.name).second) {
35 11 continue;
36 }
37 10 const auto& c = material.colour;
38
8/8
✓ Branch 2 taken 10 times.
✓ Branch 6 taken 10 times.
✓ Branch 9 taken 10 times.
✓ Branch 12 taken 10 times.
✓ Branch 15 taken 10 times.
✓ Branch 18 taken 10 times.
✓ Branch 21 taken 10 times.
✓ Branch 24 taken 10 times.
20 mtl += "\nnewmtl " + material.name + "\nKa 0 0 0\nKd " + Number(c[0]) + " " + Number(c[1]) +
39
4/4
✓ Branch 2 taken 10 times.
✓ Branch 5 taken 10 times.
✓ Branch 8 taken 10 times.
✓ Branch 11 taken 10 times.
30 " " + Number(c[2]) + "\nKs 0 0 0\nd 1\nillum 1\n";
40 }
41 20 return mtl;
42 10 }
43
44 /// The OBJ lines of one node: its object, material, vertices, normals and faces, numbered after
45 /// the `offset` vertices written before it.
46 23 std::string NodeObject(const SceneNode& node, std::size_t index, const SceneMesh& scene,
47 std::size_t offset) {
48
5/5
✓ Branch 1 taken 23 times.
✓ Branch 4 taken 23 times.
✓ Branch 7 taken 23 times.
✓ Branch 10 taken 23 times.
✓ Branch 13 taken 23 times.
23 std::string obj = "o node_" + std::to_string(index) + "\nusemtl " + scene.material.name + "\n";
49
2/2
✓ Branch 4 taken 45079 times.
✓ Branch 5 taken 23 times.
45102 for (const Vec3d& p : scene.mesh.positions) {
50
3/3
✓ Branch 1 taken 45079 times.
✓ Branch 5 taken 45079 times.
✓ Branch 8 taken 45079 times.
45079 obj += VectorLine("v", YUp(Placed(node, p)));
51 }
52
2/2
✓ Branch 4 taken 41957 times.
✓ Branch 5 taken 23 times.
41980 for (const Vec3d& n : scene.normals) {
53
3/3
✓ Branch 1 taken 41957 times.
✓ Branch 5 taken 41957 times.
✓ Branch 8 taken 41957 times.
41957 obj += VectorLine("vn", YUp(PlacedNormal(node, n)));
54 }
55
2/2
✓ Branch 5 taken 88715 times.
✓ Branch 6 taken 23 times.
88738 for (const auto& t : scene.mesh.triangles) {
56
1/1
✓ Branch 1 taken 88715 times.
88715 obj += "f";
57
2/2
✓ Branch 2 taken 266145 times.
✓ Branch 3 taken 88715 times.
354860 for (const std::uint32_t v : t) {
58
1/1
✓ Branch 1 taken 266145 times.
266145 const std::string i = std::to_string(offset + v + 1);
59
9/11
✓ Branch 1 taken 18048 times.
✓ Branch 2 taken 248097 times.
✓ Branch 4 taken 18048 times.
✓ Branch 7 taken 248097 times.
✓ Branch 10 taken 248097 times.
✓ Branch 13 taken 266145 times.
✓ Branch 16 taken 266145 times.
✓ Branch 20 taken 248097 times.
✓ Branch 21 taken 18048 times.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
266145 obj += " " + (scene.normals.empty() ? i : i + "//" + i);
60 266145 }
61
1/1
✓ Branch 1 taken 88715 times.
88715 obj += "\n";
62 }
63 23 return obj;
64 }
65
66 } // namespace
67
68 10 void WriteObj(const std::filesystem::path& path, const std::vector<SceneMesh>& meshes,
69 const std::vector<SceneNode>& nodes) {
70
1/1
✓ Branch 1 taken 10 times.
10 std::filesystem::path mtl = path;
71
2/2
✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
10 mtl.replace_extension(".mtl");
72
2/2
✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
10 Save(mtl, Materials(meshes));
73
4/4
✓ Branch 1 taken 10 times.
✓ Branch 4 taken 10 times.
✓ Branch 7 taken 10 times.
✓ Branch 10 taken 10 times.
10 std::string obj = "# TerrainGen\nmtllib " + mtl.filename().string() + "\n";
74 10 std::size_t offset = 0;
75
2/2
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 10 times.
33 for (std::size_t n = 0; n < nodes.size(); ++n) {
76 23 const SceneMesh& scene = meshes[nodes[n].mesh];
77
2/2
✓ Branch 2 taken 23 times.
✓ Branch 5 taken 23 times.
23 obj += NodeObject(nodes[n], n, scene, offset);
78 23 offset += scene.mesh.positions.size();
79 }
80
1/1
✓ Branch 1 taken 10 times.
10 Save(path, obj);
81 10 }
82
83 13 SceneMesh ReadObj(const std::filesystem::path& path) {
84
1/1
✓ Branch 1 taken 13 times.
13 tinyobj::ObjReaderConfig config;
85 13 config.triangulate = true;
86 13 config.vertex_color = false;
87
2/2
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 13 times.
13 config.mtl_search_path = path.parent_path().string();
88 13 tinyobj::ObjReader reader;
89
4/4
✓ Branch 1 taken 13 times.
✓ Branch 4 taken 13 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 11 times.
13 if (!reader.ParseFromFile(path.string(), config)) {
90
5/5
✓ Branch 3 taken 2 times.
✓ Branch 6 taken 2 times.
✓ Branch 9 taken 2 times.
✓ Branch 12 taken 2 times.
✓ Branch 15 taken 2 times.
2 throw std::runtime_error("Cannot read " + path.string() + ": " + reader.Error());
91 }
92
1/1
✓ Branch 1 taken 11 times.
11 SceneMesh read;
93 11 const std::vector<tinyobj::real_t>& vertices = reader.GetAttrib().vertices;
94
2/2
✓ Branch 1 taken 3406 times.
✓ Branch 2 taken 11 times.
3417 for (std::size_t v = 0; v + 2 < vertices.size(); v += 3) {
95
1/1
✓ Branch 5 taken 3406 times.
3406 read.mesh.positions.push_back(ZUp({vertices[v], vertices[v + 1], vertices[v + 2]}));
96 }
97
2/2
✓ Branch 6 taken 22 times.
✓ Branch 7 taken 11 times.
33 for (const tinyobj::shape_t& shape : reader.GetShapes()) {
98 22 const auto& indices = shape.mesh.indices;
99
2/2
✓ Branch 1 taken 6169 times.
✓ Branch 2 taken 22 times.
6191 for (std::size_t i = 0; i + 2 < indices.size(); i += 3) {
100 6169 read.mesh.triangles.push_back(
101 6169 {static_cast<std::uint32_t>(indices[i].vertex_index),
102 6169 static_cast<std::uint32_t>(indices[i + 1].vertex_index),
103
1/1
✓ Branch 2 taken 6169 times.
6169 static_cast<std::uint32_t>(indices[i + 2].vertex_index)});
104 }
105 }
106
2/2
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 2 times.
11 if (!reader.GetMaterials().empty()) {
107 9 const tinyobj::material_t& material = reader.GetMaterials().front();
108 9 read.material = {material.name,
109 9 {material.diffuse[0], material.diffuse[1], material.diffuse[2]}};
110 }
111 22 return read;
112
1/1
✓ Branch 1 taken 9 times.
24 }
113
114 } // namespace terraingen::detail
115