1. Overview
This tutorial provides the basic usage of using AMCAXMeshing for triangle mesh algorithm.
1.1 Trianlge Soup
AMCAXMeshing uses triangle soup to store mesh data. Triangle soup mainly contains the coordinates of mesh points and the index information of faces, and is a template parameter that mesh algorithms rely on.
class TriSoupTraits_Coord
{
public:
using PointT = VecProxy<3, double>;
using VecT = VecProxy<3, double>;
using NormalT = VecProxy<3, double>;
using Tex3D = VecProxy<3, double>;
using Color = VecProxy<3, float>;
using Triangle = VecProxy<3, int>;
using Points = std::vector<PointT>;
using Normals = std::vector<NormalT>;
using Tex3Ds = std::vector<Tex3D>;
using Colors = std::vector<Color>;
using Triangles = std::vector<Triangle>;
};
1.2 Using namespace
For clarity in the example code, using namespace
using namespace Meshing;
Namespace of all interface in the AMCAX kernel.
Definition: misc.docu:8
1.3 Mesh IO
AMCAXMeshing currently provides reading and writing of commonly used mesh file formats such as obj and stl.
mesh IO header file
write "example_utils.h" to set input and output paths
#pragma once
#include <filesystem>
#include <fstream>
#include <stack>
#include <string>
#include <vector>
inline bool make_file_writable(const std::string &filename)
{
std::filesystem::path filepath(filename);
if (std::filesystem::exists(filepath) && std::filesystem::is_regular_file(filepath))
return true;
std::filesystem::path parent_path = filepath.parent_path();
if (std::filesystem::exists(parent_path) && std::filesystem::is_directory(parent_path))
return true;
std::filesystem::create_directories(parent_path);
return false;
}
inline bool make_dir_writable(const std::string &dirname)
{
std::filesystem::path dirpath(dirname);
if (std::filesystem::exists(dirpath) && std::filesystem::is_directory(dirpath))
return true;
std::filesystem::create_directories(dirpath);
return false;
}
#define OUTPUT_DIRECTORY(TEST_SUIT_NAME, TEST_NAME) \
std::string outdir = \
"./data/test_output/" #TEST_SUIT_NAME "/" #TEST_NAME "/"; \
make_dir_writable(outdir)
2. Algorithm Examples
2.1 Mesh Simplify
#include "example_utils.h"
#include <cfloat>
#include <thread>
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Remeshing;
void Remeshing_FastQEM()
{
OUTPUT_DIRECTORY(Remeshing, FastQEM);
bool first = true;
int stage, current, total;
FastQEM<TriSoupTraits_Coord>::ProFn pro_fn =
[&first, &stage, ¤t, &total](int _stage, int _current, int _total,
AMCAXMeshing_UNUSED bool &cancel) -> void
{
stage = _stage;
current = _current;
total = _total;
if (!first)
std::cout << "\r\033[K";
std::cout << "stage: " << _stage << "/"
<< FastQEM<TriSoupTraits_Coord>::Total << ", process: " << current
<< "/" << total;
first = false;
if (stage == FastQEM<TriSoupTraits_Coord>::Stage::Total)
std::cout << std::endl;
};
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits_Coord> obj_reader;
OBJWriter<TriSoupTraits_Coord> obj_writer;
std::string filename = "32770_sf";
obj_reader.read("./data/" + filename + ".obj", io_options);
for (double ratio : {0.01, 0.1, 0.3, 0.6})
{
first = true;
FastQEM<TriSoupTraits_Coord> fast_qem(obj_reader.m_points,
obj_reader.m_triangles);
Logger::elapse_reset();
std::thread simplify_thread(
[&]()
{
fast_qem.simplify(size_t(obj_reader.m_triangles.size() * ratio),
0.1f, 20, pro_fn);
});
simplify_thread.join();
auto elapsed = Logger::elapsed();
Logger::info("FastQEM: ratio " + std::to_string(ratio) + "; time " +
std::to_string(elapsed.count()) + " s");
obj_writer.clear();
obj_writer.m_points = std::move(fast_qem.m_points);
obj_writer.m_triangles = std::move(fast_qem.m_triangles);
std::string out_filename =
outdir + filename + "_FastQEM_" + std::to_string(ratio) + ".obj";
obj_writer.write(out_filename, io_options, 10);
}
}
Fast QEM simplification algorithm.
Namespace of all interface in the AMCAX Meshing module.
Definition: misc.docu:28
Click here example1 to get the complete source code for the above example, which you can download according to your learning needs.
2.2 Mesh Boolean
#include "example_utils.h"
using EIAC = AMCAX::Meshing::Geometry::EIAC;
using TriSoupTraits = AMCAX::Meshing::Mesh::TriSoupTraits_Coord;
using Points = typename TriSoupTraits::Points;
using Triangles = typename TriSoupTraits::Triangles;
void read_mesh(const std::string &filename, Points &points,
Triangles &triangles, IOOptions &io_options
)
{
if (AMCAX::Meshing::ends_with(filename, ".obj"))
{
OBJReader reader;
reader.read(filename, io_options);
points = std::move(reader.m_points);
triangles = std::move(reader.m_triangles);
}
else if (AMCAX::Meshing::ends_with(filename, ".stl"))
{
STLReader reader;
reader.read(filename, io_options);
points = std::move(reader.m_points);
triangles = std::move(reader.m_triangles);
}
}
void write_mesh(const std::string &filename, const Points &points,
const Triangles &triangles, IOOptions &io_options)
{
OBJWriter writer;
writer.m_points = std::move(points);
writer.m_triangles = std::move(triangles);
writer.write(filename, io_options, 10);
};
void MeshBoolean_MeshBoolean()
{
OUTPUT_DIRECTORY(Boolean, Boolean);
IOOptions io_options;
io_options.vertex_has_point = true;
Points points1, points2, result_points;
Triangles triangles1, triangles2, result_triangles;
read_mesh("./data/boolean_data/bunny25k.obj", points1, triangles1, io_options);
read_mesh("./data/boolean_data/cow.obj", points2, triangles2, io_options);
MeshBoolean boolean(true);
boolean.addTriMeshAsInput(points1, triangles1);
boolean.addTriMeshAsInput(points2, triangles2);
boolean.setTriMeshAsOutput(result_points, result_triangles);
boolean.computeLabels();
{
boolean.Union();
make_file_writable(outdir + "AMCAXMeshing_Union.obj");
write_mesh(outdir + "AMCAXMeshing_Union.obj", result_points,
result_triangles, io_options);
}
{
boolean.Intersection();
make_file_writable(outdir + "AMCAXMeshing_Intersection.obj");
write_mesh(outdir + "AMCAXMeshing_Intersection.obj", result_points,
result_triangles, io_options);
}
{
boolean.Xor();
make_file_writable(outdir + "AMCAXMeshing_Xor.obj");
write_mesh(outdir + "AMCAXMeshing_Xor.obj", result_points, result_triangles,
io_options);
}
{
boolean.Subtraction();
make_file_writable(outdir + "AMCAXMeshing_Subtraction.obj");
write_mesh(outdir + "AMCAXMeshing_Subtraction.obj", result_points,
result_triangles, io_options);
}
}
reader triangle soup from STL file.
This file implement writer for STL file.
Repair triangle mesh to clean mesh.
Definition: MeshBoolean.hpp:48
Options about pre-described properties in mesh IO.
Definition: IOOptions.hpp:19
Read triangle soup from an OBJ file.
Definition: OBJReader.hpp:35
Write triangle soup to an OBJ file.
Definition: OBJWriter.hpp:35
Read triangle soup from an STL file.
Definition: STLReader.hpp:33
Write mesh to an STL file.
Definition: STLWriter.hpp:33
Click here example2 to get the complete source code for the above example, which you can download according to your learning needs.
2.3 Mesh Repair
#include "example_utils.h"
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::MeshTools;
void MeshRepair_MeshRepair()
{
OUTPUT_DIRECTORY(MeshRepair, MeshRepair);
bool first = true;
int stage;
[&first, &stage](int _stage, AMCAXMeshing_UNUSED bool &cancel) -> void
{
stage = _stage;
if (!first)
std::cout << "\r\033[K";
std::cout
<< "stage: " << _stage << "/"
first = false;
TriSoupTraits_Coord>::Stage::Total)
std::cout << std::endl;
};
IOOptions io_options;
io_options.vertex_has_point = true;
io_options.face_has_normal = true;
OBJReader<TriSoupTraits_Coord> obj_reader;
STLReader<TriSoupTraits_Coord> stl_reader;
OBJWriter<TriSoupTraits_Coord> obj_writer;
STLWriter<TriSoupTraits_Coord> stl_writer;
std::string modelname = "CamelBox";
TriSoupTraits_Coord::Points result_points;
TriSoupTraits_Coord::Triangles result_triangles;
stl_reader.read("./data/repair_data/" + modelname + ".stl", io_options);
TriMeshRepair<TriSoupTraits_Coord> repair(stl_reader.m_points,
stl_reader.m_triangles);
repair.repair(true, pro_fn);
stl_writer.m_points = repair.m_points;
stl_writer.m_triangles = repair.m_triangles;
make_file_writable(outdir + modelname + "_repaired.stl");
io_options.stl_binary = true;
stl_writer.write(outdir + modelname + "_repaired.stl", io_options, 10);
obj_writer.m_points = repair.m_points;
obj_writer.m_triangles = repair.m_triangles;
make_file_writable(outdir + modelname + "_repaired.obj");
obj_writer.write(outdir + modelname + "_repaired.obj", io_options, 10);
}
Interfaces of mesh arrangements.
Click here example3 to get the complete source code for the above example, which you can download according to your learning needs.
2.4 Mesh Subdivision
2.4.1 ButterFly Subdivision
#include "example_utils.h"
#include <cfloat>
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Subdivision;
using namespace AMCAX::Meshing::Remeshing;
void Subdivision_TriSoup_ButterFly()
{
OUTPUT_DIRECTORY(Subdivision, TriSoup_ButterFly);
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits_Coord> obj_reader;
OBJWriter<TriSoupTraits_Coord> obj_writer;
std::string filename = "kelan";
obj_reader.read("./data/subdivision_data/" + filename + ".obj", io_options);
for (double ratio : {2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.})
{
TriSoup_ButterFlySubdivision<TriSoupTraits_Coord, true> subdivider(
obj_reader.m_points, obj_reader.m_triangles);
Logger::elapse_reset();
size_t sub_times = size_t(std::ceil(ratio / 4.0) + 0.1);
for (size_t i = 0; i < sub_times; i++)
subdivider.subdivide();
FastQEM<TriSoupTraits_Coord> fast_qem(std::move(subdivider.m_points),
std::move(subdivider.m_triangles));
if (size_t(obj_reader.m_triangles.size() * ratio +
fast_qem.m_triangles.size() * 0.01) <
fast_qem.m_triangles.size())
fast_qem.simplify(size_t(obj_reader.m_triangles.size() * ratio), FLT_MAX,
20);
auto elapsed = Logger::elapsed();
Logger::info("ButterFly: ratio " + std::to_string(ratio) + "; time " +
std::to_string(elapsed.count()) + " s");
obj_writer.clear();
obj_writer.m_points = std::move(fast_qem.m_points);
obj_writer.m_triangles = std::move(fast_qem.m_triangles);
std::string out_filename = outdir + filename +
"_TriSoupModifiedButterFly_" +
std::to_string(ratio) + ".obj";
make_file_writable(out_filename);
obj_writer.write(out_filename, io_options, 10);
}
}
Implement ButterFly subdivision algorithm.
Click here example4 to get the complete source code for the above example, which you can download according to your learning needs.
2.4.2 Loop Subdivision
#include "example_utils.h"
#include <cfloat>
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Remeshing;
using namespace AMCAX::Meshing::Subdivision;
void Subdivision_TriSoup_LoopSubdivision()
{
OUTPUT_DIRECTORY(Subdivision, TriSoup_LoopSubdivision);
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits_Coord> obj_reader;
OBJWriter<TriSoupTraits_Coord> obj_writer;
std::string filename = "kelan";
obj_reader.read("./data/subdivision_data/" + filename + ".obj", io_options);
for (double ratio : {2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.})
{
TriSoup_LoopSubdivision<TriSoupTraits_Coord> subdivider(
obj_reader.m_points, obj_reader.m_triangles);
Logger::elapse_reset();
size_t sub_times = size_t(std::ceil(ratio / 4.0) + 0.1);
for (size_t i = 0; i < sub_times; i++)
subdivider.subdivide();
FastQEM<TriSoupTraits_Coord> fast_qem(std::move(subdivider.m_points),
std::move(subdivider.m_triangles));
if (size_t(obj_reader.m_triangles.size() * ratio +
fast_qem.m_triangles.size() * 0.01) <
fast_qem.m_triangles.size())
fast_qem.simplify(size_t(obj_reader.m_triangles.size() * ratio), FLT_MAX,
12);
auto elapsed = Logger::elapsed();
Logger::info("Loop: ratio " + std::to_string(ratio) + "; time " +
std::to_string(elapsed.count()) + " s");
obj_writer.clear();
obj_writer.m_points = std::move(fast_qem.m_points);
obj_writer.m_triangles = std::move(fast_qem.m_triangles);
std::string out_filename =
outdir + filename + "_TriSoupLoopSub_" + std::to_string(ratio) + ".obj";
make_file_writable(out_filename);
obj_writer.write(out_filename, io_options, 10);
}
}
Implement loop subdivision algorithm.
Click here example5 to get the complete source code for the above example, which you can download according to your learning needs.
2.5 Remeshing
2.5.1 Incremental Remeshing
#include "example_utils.h"
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Remeshing;
void Remeshing_Incremental()
{
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits> obj_reader;
OBJWriter<TriSoupTraits> obj_writer;
TriMesh_IncrementalRemeshing<TriSoupTraits> inc_remesher;
std::string filename = "32770_sf";
obj_reader.read("./data/" + filename + ".obj", io_options);
obj_writer.clear();
inc_remesher.setReferenceMesh(obj_reader.m_points, obj_reader.m_triangles);
inc_remesher.setVariableMesh(obj_writer.m_points, obj_writer.m_triangles);
TriMesh_IncrementalRemeshing<TriSoupTraits>::Params param;
param.targetEdgeLength = 0.005;
param.isAdaptive = false;
inc_remesher.remesh(param);
std::string out_filename = filename + "_Incremental_.obj";
obj_writer.write(out_filename, io_options, 10);
}
Interfaces of triMesh incremental remeshing.
Click here example6 to get the complete source code for the above example, which you can download according to your learning needs.
2.6 Mesh parameterisation algorithm
#include "meshModeling/MeshParameterization.hpp"
#include "example_utils.h"
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Parameterization;
void Parameterization_Bijective()
{
OUTPUT_DIRECTORY(Parameterization, Bijective);
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits> obj_reader;
OBJWriter<TriSoupTraits> obj_writer;
obj_reader.read("./data/parameterization_data/cow.obj", io_options);
obj_reader.m_points, obj_reader.m_triangles, obj_writer.m_points);
para.parameterization();
obj_writer.m_triangles = std::move(obj_reader.m_triangles);
std::string out_filename = outdir + "res_parameterization.obj";
obj_writer.write(out_filename, io_options, 10);
}
Definition: MeshParameterization.hpp:23
2.7 Mesh Cutting Algorithm
#include "meshModeling/MeshParameterization.hpp"
#include "example_utils.h"
using namespace AMCAX::Meshing::Mesh;
using namespace AMCAX::Meshing::Parameterization;
using Points = typename TriSoupTraits::Points;
using Triangles = typename TriSoupTraits::Triangles;
void Parameterization_With_MeshCut()
{
OUTPUT_DIRECTORY(Parameterization, Bijective_With_MeshCut);
IOOptions io_options;
io_options.vertex_has_point = true;
OBJReader<TriSoupTraits> obj_reader;
OBJWriter<TriSoupTraits> obj_writer;
std::string in_filename = "./data/boolean_data/bunny25k.obj";
std::string out_cut_filename = outdir + "bunny_cut.obj";
std::string out_para_filename = outdir + "bunny_parameterization.obj";
obj_reader.read(in_filename, io_options);
Points i_points = std::move(obj_reader.m_points);
Triangles i_triangles = std::move(obj_reader.m_triangles);
i_points, i_triangles, true);
Points cut_points;
Triangles cut_triangles;
mesh_cut.cut(cut_points, cut_triangles);
obj_writer.m_points = cut_points;
obj_writer.m_triangles = cut_triangles;
obj_writer.write(out_cut_filename, io_options, 10);
obj_writer.clear();
Points para_points;
cut_points, cut_triangles, para_points, true);
para.parameterization();
obj_writer.m_points = std::move(para_points);
obj_writer.m_triangles = std::move(cut_triangles);
obj_writer.write(out_para_filename, io_options, 10);
}
Greedy cut algorithm for triangle mesh.
Definition: TriMeshCut.hpp:24