I'm testing the CGAL library on a Linux machine. As part of my testing, I wanted to build a simple surface mesh of a cube.
This is my Catch2 test:
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <catch2/catch_test_macros.hpp>
#include <cmath>
#include <fstream>
#include <iostream>
using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel;
using Mesh = CGAL::Surface_mesh<Kernel::Point_3>;
// Function to create an approximate parametric sphere
Mesh create_mesh(double size) {
Mesh mesh;
// Cube.
// Define the 8 vertices of the cube
auto v0 = mesh.add_vertex(Kernel::Point_3(0, 0, 0));
auto v1 = mesh.add_vertex(Kernel::Point_3(size, 0, 0));
auto v2 = mesh.add_vertex(Kernel::Point_3(size, size, 0));
auto v3 = mesh.add_vertex(Kernel::Point_3(0, size, 0));
auto v4 = mesh.add_vertex(Kernel::Point_3(0, 0, size));
auto v5 = mesh.add_vertex(Kernel::Point_3(size, 0, size));
auto v6 = mesh.add_vertex(Kernel::Point_3(size, size, size));
auto v7 = mesh.add_vertex(Kernel::Point_3(0, size, size));
// Define the 6 faces of the cube (each face is a quadrilateral made of 2 triangles)
mesh.add_face(v0, v1, v2, v3); // Bottom
mesh.add_face(v4, v5, v6, v7); // Top
mesh.add_face(v0, v4, v7, v3); // Left
mesh.add_face(v1, v5, v6, v2); // Right
mesh.add_face(v0, v4, v5, v1); // Front
mesh.add_face(v3, v7, v6, v2); // Back
return mesh;
}
TEST_CASE("OBJ Export of Cube") {
const std::string filename = "test_output.obj";
// Remove file if it exists, to start fresh
std::remove(filename.c_str());
auto mesh = create_mesh(1);
// Use CGAL to save the combined mesh to OBJ format
CGAL::IO::write_OBJ(filename, mesh);
REQUIRE(std::ifstream(filename).good());
}
To my surprise the cube is not complete. There are only two faces written to the OBJ file for the cube. I don't understand this. What am I doing wrong?
This is the test_output.obj
file:
# file written from a CGAL tool in Wavefront obj format
# 8 vertices
# 16 halfedges
# 2 facets
# 8 vertices
# ------------------------------------------
v 0 0 0
v 1 0 0
v 1 1 0
v 0 1 0
v 0 0 1
v 1 0 1
v 1 1 1
v 0 1 1
# 2 facets
# ------------------------------------------
f 1 2 3 4
f 5 6 7 8
# End of Wavefront obj format #
Your faces have incompatible orientations, and so some of the add_face()
calls in fact do not add any new face.
The class CGAL::Surface_mesh
is an halfedge data structure to represent a polygon mesh. The faces are oriented by the order of the vertices. The polygon mesh must be manifold, and as such you can't have two faces that have the same (directed) halfedge. Here it is your case as for example your "bottom" and "back" faces and v2 --> v3.
Here is a possible fix, with outward face orientations:
// Define the 6 faces of the cube (each face is a quadrilateral made of 2 triangles)
mesh.add_face(v0, v3, v2, v1); // Bottom
mesh.add_face(v4, v5, v6, v7); // Top
mesh.add_face(v0, v4, v7, v3); // Left
mesh.add_face(v1, v2, v6, v5); // Right
mesh.add_face(v0, v1, v5, v4); // Front
mesh.add_face(v3, v7, v6, v2); // Back
and you get the expected result (6 faces).
Note that add_face()
has a return value (the face index) that you can use to check if your face was correctly added since it returns null_face()
if the face could not be added. See the doc: https://doc.cgal.org/latest/Surface_mesh/classCGAL_1_1Surface__mesh.html#af45a68e4c8a0fcb7c4fd899185824cae.