Exporting classes containing std:: objects (vector, map, etc) from a dll

本文详细阐述了在DLL中导出基于STL类型的类时可能遇到的问题,以及提供了一种优雅的解决方案,即通过提供STL模板实例的包装类来避免静态成员不一致导致的不可预测行为和随机崩溃。

From: http://www.abstraction.net/ViewArticle.aspx?articleID=83


Related readings:

http://stackoverflow.com/questions/767579/exporting-classes-containing-std-objects-vector-map-etc-from-a-dll

http://www.cplusplus.com/forum/general/78467/


STL and DLLs

 

By George Mihaescu

 

This article is now obsolete: the described behavior applies only up to (and including) Visual Studio 5.0. After that version the STL and compiler implementations have changed so that the behavior described here is not an issue anymore.

 

Summary: this article describes potential problems when exporting from a DLL classes that are based on (or contain public members based on) STL types and provides a solution.

The problem

The use of various classes from STL in DLLs raises a major problem: STL template instantiations cannot be exported directly from the DLL, the main reason being the fact that a few of the STL templates make use of static members. By exposing in a header an STL template instantiation, the DLL and the user of the DLL (that includes the exposed header) get different copies of the static members, and this leads to unpredictable behavior and random crashes.

This problem is documented in the MSDN and various means to resolve it are described.

The solution

The most elegant manner of solving the problem is to provide wrapper classes for the STL template instantiations, and those wrappers are exported from the DLL, as shown below.

For instance, instead of exposing a header from a DLL like this:

 

#include <map>             //for STL map

 

using namespace std;

 

class __declspec(dllexport) MyClass

{

};

 

typedef __declspec (dllexport)  map <int, MyClass>   AMap;

 

 

use a header like this:

 

 

#include <map>             //for STL map

 

using namespace std;

 

class __declspec(dllexport) MyClass

{

};

 

//wrapper class for the map

class __declspec(dllexport) AMap

{

       public:

              AMap ();

              ~AMap ();

 

//wrapper methods for the aggregated map:

              void Insert (const MyClass& m);

              const MyClass* Find (int key);

 

       private:

              typedef map <int, MyClass>   _AMap;

 

       AMap   m_map; //the wrapped map

};

 

 

When implementing the header exposed by the DLL this way, the compiler will generate a warning similar to “class std::map <int, MyClass> must have DLL interface to be used by clients of MyDll.dll”. This is usually with no consequences (it means that friends of the class AMap -if any- will not be able to directly access the wrapped map because it is not exported from the DLL). However, the direct usage of the map by clients of the DLL was not possible anyway due to the problem we were trying to solve in the first place - so this warning is of no concern.


基于径向基函数神经网络RBFNN的自适应滑模控制学习(Matlab代码实现)内容概要:本文介绍了基于径向基函数神经网络(RBFNN)的自适应滑模控制方法,并提供了相应的Matlab代码实现。该方法结合了RBF神经网络的非线性逼近能力和滑模控制的强鲁棒性,用于解决复杂系统的控制问题,尤其适用于存在不确定性和外部干扰的动态系统。文中详细阐述了控制算法的设计思路、RBFNN的结构与权重更新机制、滑模面的构建以及自适应律的推导过程,并通过Matlab仿真验证了所提方法的有效性和稳定性。此外,文档还列举了大量相关的科研方向和技术应用,涵盖智能优化算法、机器学习、电力系统、路径规划等多个领域,展示了该技术的广泛应用前景。; 适合人群:具备一定自动控制理论基础和Matlab编程能力的研究生、科研人员及工程技术人员,特别是从事智能控制、非线性系统控制及相关领域的研究人员; 使用场景及目标:①学习和掌握RBF神经网络与滑模控制相结合的自适应控制策略设计方法;②应用于电机控制、机器人轨迹跟踪、电力电子系统等存在模型不确定性或外界扰动的实际控制系统中,提升控制精度与鲁棒性; 阅读建议:建议读者结合提供的Matlab代码进行仿真实践,深入理解算法实现细节,同时可参考文中提及的相关技术方向拓展研究思路,注重理论分析与仿真验证相结合。
// 标准C++库头文件 #include <iostream> // 输入输出流 #include <fstream> // 文件流操作 #include <limits> // 数值限制 #include <string> // 字符串处理 #include <filesystem> // 文件系统操作 #include <unordered_map> // 无序映射容器 #include <list> // 链表容器 #include <utility> // 工具函数 #include <vector> // 向量容器 // Assimp库头文件 - 用于3D模型导入导出 #include <assimp/Importer.hpp> // 模型导入器 #include <assimp/Exporter.hpp> // 模型导出器 #include <assimp/mesh.h> // 网格数据结构 #include <assimp/postprocess.h> // 后处理选项 #include <assimp/scene.h> // 场景数据结构 // CGAL库头文件 - 用于计算几何和网格处理 #include <CGAL/Exact_predicates_inexact_constructions_kernel.h> // 精确谓词内核 #include <CGAL/Polygon_mesh_processing/bbox.h> // 包围盒计算 #include <CGAL/Polygon_mesh_processing/connected_components.h> // 连通分量 #include <CGAL/Polygon_mesh_processing/clip.h> // 网格裁剪 #include <CGAL/Polygon_mesh_processing/corefinement.h> // 核心细化 #include <CGAL/Polygon_mesh_processing/polygon_mesh_to_polygon_soup.h> // 网格转多边形汤 #include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h> // 多边形汤转网格 #include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h> // 修复多边形汤 #include <CGAL/Polygon_mesh_processing/orientation.h> // 方向处理 #include <CGAL/Polygon_mesh_processing/remesh.h> // 重新网格化 #include <CGAL/Surface_mesh.h> // 表面网格 #include <CGAL/Surface_mesh_parameterization/parameterize.h> // 参数化 #include <CGAL/Surface_mesh_parameterization/ARAP_parameterizer_3.h> // ARAP参数化器 #include <CGAL/Surface_mesh_parameterization/Mean_value_coordinates_parameterizer_3.h> // 均值坐标参数化器 #include <CGAL/Surface_mesh_parameterization/LSCM_parameterizer_3.h> // LSCM参数化器 #include <CGAL/Surface_mesh_parameterization/Discrete_authalic_parameterizer_3.h> // 离散等面积参数化器 #include <CGAL/Surface_mesh_parameterization/Barycentric_mapping_parameterizer_3.h> // 重心映射参数化器 #include <CGAL/Surface_mesh_parameterization/Discrete_conformal_map_parameterizer_3.h> // 离散共形映射参数化器 #include <CGAL/Surface_mesh_parameterization/Iterative_authalic_parameterizer_3.h> // 迭代等面积参数化器 #include <CGAL/Optimal_bounding_box/oriented_bounding_box.h> // 最优包围盒 // 使用标准命名空间 using namespace std; // 文件系统命名空间别名 namespace fs = std::filesystem; // CGAL库命名空间别名,简化代码 namespace PMP = CGAL::Polygon_mesh_processing; // 多边形网格处理 namespace SMP = CGAL::Surface_mesh_parameterization; // 表面网格参数化 // 几何内核类型定义 - 使用精确谓词内核,保证数值稳定性 typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; //typedef CGAL::Simple_cartesian<double> Kernel; // 备选简单笛卡尔内核 typedef Kernel::Point_2 Point_2; // 2D点类型 typedef Kernel::Point_3 Point; // 3D点类型 typedef CGAL::Surface_mesh<Point> CGALMesh; // CGAL表面网格类型 typedef CGAL::SM_Vertex_index VertexIndex; // 顶点索引类型 //typedef CGALMesh::Property_map<VertexIndex, Kernel::Point_2> VertexUVPmap; // UV坐标属性映射 // 其他几何类型定义 typedef Kernel::Vector_3 Vector_3; // 3D向量 typedef Kernel::Plane_3 Plane_3; // 3D平面 typedef Kernel::Aff_transformation_3 Aff_transformation_3; // 仿射变换 namespace OB = CGAL::Optimal_bounding_box; // 最优包围盒命名空间 // 网格实体类型 - 包含网格、颜色和名称的元组 using MeshEntity = std::tuple<CGALMesh, aiColor3D, std::string>; /** * 剖切面访问者类 - 用于在网格裁剪过程中收集剖切面 * 这个类实现了CGAL的访问者模式,用于跟踪裁剪操作并生成剖切面 */ struct SectionVisitor { CGALMesh& section; // 存储生成的剖切面网格 std::map<CGALMesh::Vertex_index, CGALMesh::Vertex_index> vmap; // 顶点映射表,记录原网格顶点到剖切面顶点的映射 SectionVisitor(CGALMesh& sm) : section(sm) {} // 构造函数,传入剖切面网格引用 // 图论相关的类型定义,用于网格处理 typedef CGALMesh TriangleMesh; // 三角形网格类型 typedef boost::graph_traits<TriangleMesh> GT; // 图特征类型 typedef typename GT::face_descriptor face_descriptor; // 面描述符 typedef typename GT::halfedge_descriptor halfedge_descriptor; // 半边描述符 typedef typename GT::vertex_descriptor vertex_descriptor; // 顶点描述符 // face visitor functions void before_subface_creations(face_descriptor /*f_old*/, TriangleMesh&) {} void after_subface_creations(TriangleMesh&) {} void before_subface_created(TriangleMesh&) {} void after_subface_created(face_descriptor, TriangleMesh&) {} void before_face_copy(face_descriptor, const TriangleMesh&, TriangleMesh&) {} void after_face_copy(face_descriptor f_old, const TriangleMesh& tm_old, face_descriptor /*f_new*/, TriangleMesh& /*tm_new*/) { std::vector<CGALMesh::Vertex_index> vertices; for (auto v : vertices_around_face(tm_old.halfedge(f_old), tm_old)) { auto it = vmap.find(v); if (it != vmap.end()) { vertices.push_back(it->second); } } if (vertices.size() == 3) { section.add_face(vertices[0], vertices[1], vertices[2]); } } // edge visitor functions void before_edge_split(halfedge_descriptor /* h */, TriangleMesh& /* tm */) {} void edge_split(halfedge_descriptor /* hnew */, TriangleMesh& /* tm */) {} void after_edge_split() {} void add_retriangulation_edge(halfedge_descriptor /* h */, TriangleMesh& /* tm */) {} // edges added during split face retriangulation void before_edge_copy(halfedge_descriptor /*h_old*/, const TriangleMesh&, TriangleMesh&) {} void after_edge_copy(halfedge_descriptor /*h_old*/, const TriangleMesh&, halfedge_descriptor /* f_new */, TriangleMesh&) {} void before_edge_duplicated(halfedge_descriptor /*h_old*/, TriangleMesh&) {} // called before a patch border edge is duplicated void after_edge_duplicated(halfedge_descriptor /*h_old*/, halfedge_descriptor /* f_new */, TriangleMesh&) {} // called after a patch border edge is duplicated void intersection_edge_copy(halfedge_descriptor /* h_old1 */, const TriangleMesh& /* tm1 */, halfedge_descriptor /* h_old2 */, const TriangleMesh& /* tm2 */, halfedge_descriptor /* h_new */, TriangleMesh& /* tm_new */) {} // vertex visitor functions void new_vertex_added(std::size_t /*node_id*/, vertex_descriptor vh, const TriangleMesh& tm) { vmap[vh] = section.add_vertex(tm.point(vh)); } void intersection_point_detected(std::size_t /* node_id */, int /* sdim */, halfedge_descriptor /* principal_edge */, halfedge_descriptor /* additional_edge */, const TriangleMesh& /* tm1 */, const TriangleMesh& /* tm2 */, bool /* is_target_coplanar */, bool /* is_source_coplanar */) {} void before_vertex_copy(vertex_descriptor /*v_src*/, const TriangleMesh& /*tm_src*/, TriangleMesh& /*tm_tgt*/) {} void after_vertex_copy(vertex_descriptor v_src, const TriangleMesh& tm_src, vertex_descriptor /*v_tgt*/, TriangleMesh& /*tm_tgt*/) { vmap[v_src] = section.add_vertex(tm_src.point(v_src)); } // progress tracking void start_filtering_intersections() const {} void progress_filtering_intersections(double) const {} void end_filtering_intersections() const {} void start_triangulating_faces(std::size_t) const {} void triangulating_faces_step() const {} void end_triangulating_faces() const {} void start_handling_intersection_of_coplanar_faces(std::size_t) const {} void intersection_of_coplanar_faces_step() const {} void end_handling_intersection_of_coplanar_faces() const {} void start_handling_edge_face_intersections(std::size_t) const {} void edge_face_intersections_step() const {} void end_handling_edge_face_intersections() const {} void start_building_output() const {} void end_building_output() const {} // Required by Face_graph_output_builder void filter_coplanar_edges() const {} void detect_patches() const {} void classify_patches() const {} void classify_intersection_free_patches(const TriangleMesh&) const {} void out_of_place_operation(PMP::Corefinement::Boolean_operation_type) const {} void in_place_operation(PMP::Corefinement::Boolean_operation_type) const {} void in_place_operations(PMP::Corefinement::Boolean_operation_type, PMP::Corefinement::Boolean_operation_type) const {} }; /** * 读取剖切曲线文件 * @param pth 曲线文件路径 * @return 包含3D点坐标的向量 * * 功能:从文本文件中读取3D点坐标,每行包含x y z三个浮点数 * 文件格式:每行一个3D点,坐标用空格分隔 */ vector<Point> readCurve(const fs::path& pth) { std::vector<Point> curve; // 存储读取的曲线点 std::ifstream file(pth); // 打开文件流 // 检查文件是否成功打开 if (!file.is_open()) { std::cerr << "Failed to open file: " << pth << std::endl; return curve; } double x, y, z; // 临时存储坐标值 int pointCount = 0; // 记录读取的点数 // 逐行读取文件中的坐标 while (file >> x >> y >> z) { // 检查坐标值是否为NaN(非数字) if (std::isnan(x) || std::isnan(y) || std::isnan(z)) { std::cerr << "Invalid curve point: NaN detected" << std::endl; continue; // 跳过无效点 } curve.push_back(Point(x, y, z)); // 将有效点添加到曲线中 pointCount++; } file.close(); // 关闭文件 std::cout << "Read " << pointCount << " points from curve file" << std::endl; // 检查是否读取到有效点 if (curve.empty()) { std::cerr << "Warning: No valid points read from curve file!" << std::endl; } return curve; } /** * 生成剖切面方法1 - 创建垂直延伸的平面 * @param curve 输入曲线点集 * @return 生成的剖切面网格 * * 算法:将曲线上的每个点沿Z轴正负方向延伸,形成垂直的平面 * 每个曲线段生成两个三角形,形成连续的垂直平面 */ CGALMesh generateSurface1(const vector<Point>& curve) { CGALMesh surface; // 创建空网格 double extensionDistance = 5000; // 延伸距离,沿Z轴正负方向各延伸5000单位 // 检查输入曲线是否为空 if (curve.empty()) { std::cerr << "Error: Cannot generate surface from empty curve!" << std::endl; return surface; } std::cout << "Generating surface from " << curve.size() << " curve points" << std::endl; // 为每个曲线点创建两个顶点(上下延伸) for (const auto& p : curve) { Point p1(p.x(), p.y(), p.z() - extensionDistance); // 下方顶点 Point p2(p.x(), p.y(), p.z() + extensionDistance); // 上方顶点 surface.add_vertex(p1); surface.add_vertex(p2); } // 连接相邻点形成三角形面片 for (size_t i = 0; i < curve.size() - 1; ++i) { size_t baseIndex = i * 2; // 当前段的顶点起始索引 // 第一个三角形:当前下顶点 -> 下一上顶点 -> 当前上顶点 surface.add_face( CGALMesh::Vertex_index(baseIndex), CGALMesh::Vertex_index(baseIndex + 1), CGALMesh::Vertex_index(baseIndex + 2) ); // 第二个三角形:当前上顶点 -> 下一上顶点 -> 下一上顶点 surface.add_face( CGALMesh::Vertex_index(baseIndex + 1), CGALMesh::Vertex_index(baseIndex + 3), CGALMesh::Vertex_index(baseIndex + 2) ); } std::cout << "Generated surface with " << surface.number_of_vertices() << " vertices and " << surface.number_of_faces() << " faces" << std::endl; return surface; } /** * 生成剖切面方法2 - 创建更复杂的曲面结构 * @param curve 输入曲线点集 * @return 生成的剖切面网格 * * 算法:为每个曲线点创建三个顶点(下、中、上),形成更丰富的曲面结构 * 每个曲线段生成四个三角形,创建更平滑的曲面过渡 */ CGALMesh generateSurface2(const std::vector<Point>& curve) { CGALMesh surface; // 创建空网格 double extensionDistance = 4000; // 延伸距离 // 为每个曲线点创建三个顶点(下、中、上) for (const auto& p : curve) { Point p_down(p.x(), p.y(), p.z()); // 下方顶点(原始高度) Point p_original(p.x(), p.y(), p.z() + 2000); // 中间顶点(偏移2000) Point p_up(p.x(), p.y(), p.z() + extensionDistance); // 上方顶点(最大高度) surface.add_vertex(p_down); surface.add_vertex(p_original); surface.add_vertex(p_up); } // 连接相邻点形成四个三角形面片(确保逆时针顺序) for (size_t i = 0; i < curve.size() - 1; ++i) { // 当前点和下一个点的顶点索引 size_t curr_base = i * 3; // 当前段的顶点起始索引 size_t next_base = (i + 1) * 3; // 下一段的顶点起始索引 // 连接两个相邻截面形成四个三角形 // 三角形1:当前下 → 下一原 → 当前原 surface.add_face( CGALMesh::Vertex_index(curr_base), CGALMesh::Vertex_index(next_base + 1), CGALMesh::Vertex_index(curr_base + 1) ); // 三角形2:当前原 → 下一原 → 下一上 surface.add_face( CGALMesh::Vertex_index(curr_base + 1), CGALMesh::Vertex_index(next_base + 1), CGALMesh::Vertex_index(next_base + 2) ); // 三角形3:当前下 → 下一原 → 下一上 surface.add_face( CGALMesh::Vertex_index(curr_base), CGALMesh::Vertex_index(next_base + 1), CGALMesh::Vertex_index(next_base + 2) ); // 三角形4:当前下 → 下一上 → 当前上 surface.add_face( CGALMesh::Vertex_index(curr_base), CGALMesh::Vertex_index(next_base + 2), CGALMesh::Vertex_index(curr_base + 2) ); } /* 注释掉的网格修复代码 std::vector<Point> points; std::vector<std::vector<size_t>> polygons; PMP::polygon_mesh_to_polygon_soup(surface, points, polygons); PMP::repair_polygon_soup(points, polygons); PMP::orient_polygon_soup(points, polygons); if (PMP::is_polygon_soup_a_polygon_mesh(polygons)) { PMP::polygon_soup_to_polygon_mesh(points, polygons, surface); }*/ return surface; } /** * 计算网格的包围盒 * @param mesh 输入网格 * @return 网格的3D包围盒 * * 功能:遍历网格的所有顶点,计算整个网格的边界框 */ CGAL::Bbox_3 compute_mesh_bbox(const CGALMesh& mesh) { CGAL::Bbox_3 bbox; // 创建空包围盒 for (auto v : mesh.vertices()) { // 遍历所有顶点 const auto& p = mesh.point(v); // 获取顶点坐标 bbox += p.bbox(); // 将顶点添加到包围盒中 } return bbox; } /** * 计算边界环的包围盒 * @param mesh 输入网格 * @param border_halfedge 边界环的起始半边 * @return 边界环的3D包围盒 * * 功能:沿着边界环遍历所有顶点,计算边界环的边界框 */ CGAL::Bbox_3 compute_border_cycle_bbox(const CGALMesh& mesh, CGALMesh::Halfedge_index border_halfedge) { CGAL::Bbox_3 border_bbox; // 创建边界环包围盒 CGALMesh::Halfedge_index h = border_halfedge; // 从起始半边开始 // 沿着边界环遍历所有半边 do { auto v = mesh.target(h); // 获取当前半边目标顶点 const auto& p = mesh.point(v); // 获取顶点坐标 border_bbox += p.bbox(); // 将顶点添加到边界环包围盒中 h = mesh.next(h); // 移动到下一个半边 } while (h != border_halfedge); // 直到回到起始半边 return border_bbox; } /** * 判断网格是否为外边界封闭网格 * @param mesh 输入网格 * @return 如果是外边界封闭网格返回true,否则返回false * * 功能:判断网格是否只有一个大的外边界环,用于识别需要跳过的网格 * 判断标准: * 1. 网格不能是完全封闭的 * 2. 只能有一个边界环 * 3. 边界环必须足够大(至少20条边) * 4. 边界环的包围盒必须覆盖网格包围盒的90%以上 */ bool is_outer_closed_boundary(const CGALMesh& mesh) { // 如果网格完全封闭,则不是外边界 if (CGAL::is_closed(mesh)) return false; // 获取所有边界环 std::vector<CGALMesh::Halfedge_index> border_cycles; CGAL::Polygon_mesh_processing::extract_boundary_cycles(mesh, std::back_inserter(border_cycles)); // 仅允许单一边界环(排除有多个洞的网格) if (border_cycles.size() != 1) return false; // 检查边界环长度(排除小孔洞) CGALMesh::Halfedge_index border_halfedge = border_cycles.front(); std::size_t edge_count = 0; CGALMesh::Halfedge_index start = border_halfedge; do { edge_count++; border_halfedge = mesh.next(border_halfedge); } while (border_halfedge != start); // 边界环必须足够大(至少20条边) const std::size_t MIN_BORDER_EDGES = 20; if (edge_count < MIN_BORDER_EDGES) return false; // 包围盒验证 - 检查边界环是否覆盖了网格的大部分区域 const CGAL::Bbox_3 mesh_bbox = compute_mesh_bbox(mesh); // 计算整个网格的包围盒 const CGAL::Bbox_3 border_bbox = compute_border_cycle_bbox(mesh, start); // 计算边界环的包围盒 // 计算包围盒在各轴上的覆盖比例 const double x_ratio = (border_bbox.xmax() - border_bbox.xmin()) / (mesh_bbox.xmax() - mesh_bbox.xmin()); const double y_ratio = (border_bbox.ymax() - border_bbox.ymin()) / (mesh_bbox.ymax() - mesh_bbox.ymin()); const double z_ratio = (border_bbox.zmax() - border_bbox.zmin()) / (mesh_bbox.zmax() - mesh_bbox.zmin()); // 设定阈值:边界环包围盒需覆盖整体包围盒的至少90% const double BBOX_COVERAGE_THRESHOLD = 0.9; if (x_ratio < BBOX_COVERAGE_THRESHOLD || y_ratio < BBOX_COVERAGE_THRESHOLD || z_ratio < BBOX_COVERAGE_THRESHOLD) { return false; } return true; // 满足所有条件,是外边界封闭网格 } bool AssimpFormatId(const Assimp::Exporter& exporter, const fs::path& filepath, std::string& formatId, bool binary) { std::string fpstr(filepath.string()); size_t ipos = fpstr.find_last_of('.'); formatId = fpstr.substr(ipos + 1); bool findFormat = false; if (formatId == "gltf" || formatId == "glb") { findFormat = true; formatId += "2"; } else { for (size_t i = 0; i < exporter.GetExportFormatCount(); i++) { const aiExportFormatDesc* desc = exporter.GetExportFormatDescription(i); if (strstr(desc->id, formatId.c_str()) != NULL) { findFormat = true; if (binary) { if (strstr(desc->id, "b") != NULL) { formatId = desc->id; break; } } else break; } } } return findFormat; } /** * 将Assimp网格转换为CGAL网格 * @param imh 输入的Assimp网格 * @param omh 输出的CGAL网格 * @param name 网格名称 * @return 转换成功返回true,失败返回false * * 功能:将Assimp库的网格数据结构转换为CGAL库的网格数据结构 * 包括顶点坐标和面片索引的转换,并进行网格修复和方向调整 */ static bool AiMeshToCgalMesh(const aiMesh& imh, CGALMesh& omh, std::string& name) { // 检查网格是否有效(至少3个顶点和1个面) if (imh.mNumVertices < 3 || imh.mNumFaces < 1) return false; // 获取网格名称 if (imh.mName.length > 0) { name = imh.mName.data; } // 准备顶点和面片数据 std::vector<Point> points; // 存储顶点坐标 std::vector<std::vector<size_t>> polygons; // 存储面片索引 // 转换顶点坐标 points.reserve(imh.mNumVertices); for (uint32_t i = 0; i < imh.mNumVertices; i++) { const aiVector3D& vet = imh.mVertices[i]; // 获取Assimp顶点 points.push_back(Point((double)vet.x, (double)vet.y, (double)vet.z)); // 转换为CGAL点 } // 转换面片索引 polygons.reserve(imh.mNumFaces); for (uint32_t i = 0; i < imh.mNumFaces; i++) { aiFace face = imh.mFaces[i]; // 获取Assimp面片 uint32_t* indices = face.mIndices; // 获取面片顶点索引 polygons.push_back({ (size_t)indices[0], (size_t)indices[1], (size_t)indices[2] }); // 转换为CGAL面片 } // 修复多边形汤(处理重复顶点、退化面片等) PMP::repair_polygon_soup(points, polygons); // 调整多边形汤的方向(确保面片法向量一致) PMP::orient_polygon_soup(points, polygons); // 检查是否可以转换为多边形网格 if (PMP::is_polygon_soup_a_polygon_mesh(polygons)) { PMP::polygon_soup_to_polygon_mesh(points, polygons, omh); // 转换为CGAL网格 return true; } return false; // 转换失败 } static bool CgalMeshToAiMesh(const CGALMesh& imh, const std::string& name, aiMesh& omh) { if (imh.is_empty() || imh.number_of_faces() == 0) return false; omh.mPrimitiveTypes = aiPrimitiveType_TRIANGLE; omh.mNumVertices = imh.number_of_vertices(); omh.mVertices = new aiVector3D[omh.mNumVertices]; std::unordered_map<VertexIndex, uint32_t> vimap; //! avoid order problem auto uvmap = imh.property_map<VertexIndex, Kernel::Point_2>("v:uv"); uint32_t vidx = 0; if (uvmap.second) { omh.mTextureCoords[0] = new aiVector3D[omh.mNumVertices]; omh.mNumUVComponents[0] = 2; uint32_t vidx = 0; for (const auto vd : CGAL::vertices(imh)) { const auto& point = imh.point(vd); omh.mVertices[vidx] = aiVector3D(static_cast<ai_real>(point.x()), static_cast<ai_real>(point.y()), static_cast<ai_real>(point.z())); const auto& uv = boost::get(uvmap.first,vd); omh.mTextureCoords[0][vidx] = aiVector3D(static_cast<ai_real>(uv.x()), static_cast<ai_real>(uv.y()), 0.0); vimap[vd] = vidx++; } } else { omh.mTextureCoords[0] = nullptr; omh.mNumUVComponents[0] = 0; uint32_t vidx = 0; for (const auto vd : CGAL::vertices(imh)) { const auto& point = imh.point(vd); omh.mVertices[vidx] = aiVector3D(static_cast<ai_real>(point.x()), static_cast<ai_real>(point.y()), static_cast<ai_real>(point.z())); vimap[vd] = vidx++; } } omh.mNumFaces = imh.number_of_faces(); omh.mFaces = new aiFace[omh.mNumFaces]; uint32_t fidx = 0; for (const auto& itr : imh.faces()) { CGAL::SM_Halfedge_index heidx = imh.halfedge(itr); const auto& fvids = imh.vertices_around_face(heidx); auto trif = &omh.mFaces[fidx]; trif->mIndices = new uint32_t[3]{ vimap[*fvids.begin()], vimap[*(++fvids.begin())], vimap[*(--fvids.end())] }; trif->mNumIndices = 3; fidx++; } omh.mName = name; return true; } static bool ImportMeshes(const fs::path& pth, std::vector<MeshEntity>& mshes) { Assimp::Importer ipt; uint32_t flags = aiProcess_Triangulate | aiProcess_JoinIdenticalVertices; // read file via ASSIMP const aiScene* scene = ipt.ReadFile(pth.u8string(), flags); // check for errors if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero { std::cerr << "ERROR::ASSIMP:: " << ipt.GetErrorString() << std::endl; return false; } aiColor3D clr; std::vector<aiColor3D> cvec; for (uint32_t i = 0; i < scene->mNumMaterials; i++) { auto mat = scene->mMaterials[i]; if (mat->Get(AI_MATKEY_BASE_COLOR, clr) != aiReturn_SUCCESS) { mat->Get(AI_MATKEY_COLOR_DIFFUSE, clr); } cvec.push_back(clr); } for (uint32_t i = 0; i < scene->mNumMeshes; i++) { auto imsh = scene->mMeshes[i]; CGALMesh omsh; std::string name; if (AiMeshToCgalMesh(*imsh, omsh, name)) { const auto clr = cvec[imsh->mMaterialIndex]; mshes.push_back({ std::move(omsh), clr, name }); } } return true; } static bool ExportMeshes(const fs::path& pth, const std::vector<MeshEntity>& items, bool texture) { if (items.empty()) { std::cerr << "Warning: No meshes to export!" << std::endl; return false; } std::cout << "Exporting " << items.size() << " meshes to: " << pth << std::endl; int midx = 0; std::vector<aiMesh*> meshes; std::vector<aiMaterial*> materials; for (const auto& item : items) { const auto& mesh = std::get<CGALMesh>(item); const auto& name = std::get<std::string>(item); const auto& color = std::get<aiColor3D>(item); std::cout << "Processing mesh '" << name << "' with " << mesh.number_of_vertices() << " vertices and " << mesh.number_of_faces() << " faces" << std::endl; if (mesh.is_empty()) { std::cerr << "Warning: Mesh '" << name << "' is empty, skipping" << std::endl; continue; } aiMesh* omsh = new aiMesh; if (CgalMeshToAiMesh(mesh, name, *omsh)) { aiMaterial* mat = new aiMaterial; if (texture) { mat->AddProperty(new aiString(name + ".png"), AI_MATKEY_TEXTURE_DIFFUSE(0)); } else { mat->AddProperty(new aiColor3D(color), 1, AI_MATKEY_COLOR_DIFFUSE); } mat->AddProperty(new aiString(name), AI_MATKEY_NAME); omsh->mMaterialIndex = midx; meshes.push_back(omsh); materials.push_back(mat); midx++; } else { std::cerr << "Warning: Failed to convert mesh '" << name << "' to Assimp format" << std::endl; delete omsh; } } aiScene* scene = new aiScene(); scene->mNumMeshes = (uint32_t)meshes.size(); scene->mMeshes = new aiMesh * [scene->mNumMeshes]; scene->mNumMaterials = (uint32_t)materials.size(); scene->mMaterials = new aiMaterial * [scene->mNumMaterials]; #ifdef _WIN32 memcpy_s(scene->mMeshes, scene->mNumMeshes * sizeof(aiMesh*), meshes.data(), scene->mNumMeshes * sizeof(aiMesh*)); memcpy_s(scene->mMaterials, scene->mNumMaterials * sizeof(aiMaterial*), materials.data(), scene->mNumMaterials * sizeof(aiMaterial*)); #elif defined __GNUC__ memcpy(scene->mMeshes, meshes.data(), scene->mNumMeshes * sizeof(aiMesh*)); memcpy(scene->mMaterials, materials.data(), scene->mNumMaterials * sizeof(aiMaterial*)); #endif aiNode* node = new aiNode; node->mNumMeshes = (uint32_t)meshes.size(); node->mMeshes = new uint32_t[node->mNumMeshes]; for (uint32_t i = 0; i < node->mNumMeshes; i++) { node->mMeshes[i] = i; } scene->mRootNode = node; std::string formatId; Assimp::Exporter exporter; int flags = aiProcess_GenSmoothNormals; if (!AssimpFormatId(exporter, pth, formatId, false)) return false; return exporter.Export(scene, formatId, pth.u8string(), flags) == aiReturn_SUCCESS; } static bool fnRepair(CGALMesh& mesh) { if (mesh.is_empty()) return false; CGAL::Polygon_mesh_processing::stitch_borders(mesh); std::vector<CGALMesh::Halfedge_index> borders; PMP::extract_boundary_cycles(mesh, std::back_inserter(borders)); if (!borders.empty()) { std::vector<CGALMesh::Face_index> new_faces; CGAL::Polygon_mesh_processing::triangulate_hole( mesh, borders[0], CGAL::parameters::face_output_iterator(std::back_inserter(new_faces))); } return true; } static bool SectionClipping(CGALMesh& msh, CGALMesh& clp, CGALMesh& section) { if (msh.is_empty()) { std::cerr << "Error: Input mesh is empty" << std::endl; return false; } if (clp.is_empty()) { std::cerr << "Error: Clip surface is empty" << std::endl; return false; } std::cout << "Input mesh has " << msh.number_of_vertices() << " vertices and " << msh.number_of_faces() << " faces" << std::endl; std::cout << "Clip surface has " << clp.number_of_vertices() << " vertices and " << clp.number_of_faces() << " faces" << std::endl; //auto fnRepair = [](CGALMesh& mesh) { // CGAL::Polygon_mesh_processing::stitch_borders(mesh); // std::vector<CGALMesh::Halfedge_index> borders; // //PMP::extract_boundary_cycles(mesh, std::back_inserter(borders)); // for (auto h : mesh.halfedges()) { // if (mesh.is_border(h)) borders.push_back(h); // } // if (!borders.empty()) { // std::vector<CGALMesh::Face_index> new_faces; // CGAL::Polygon_mesh_processing::triangulate_hole( // mesh, borders[0], CGAL::parameters::face_output_iterator(std::back_inserter(new_faces))); // } // }; std::vector<CGALMesh> meshes; PMP::split_connected_components(msh, meshes); std::cout << "Split into " << meshes.size() << " connected components" << std::endl; bool status = true; int processedComponents = 0; for (auto& spt : meshes) { size_t nf = spt.number_of_faces(); if (nf < 2) { std::cout << "Skipping component with only " << nf << " faces" << std::endl; continue; } bool is_out = is_outer_closed_boundary(spt); if (is_out) { std::cout << "Skipping outer boundary component" << std::endl; continue; } SectionVisitor visitor(section); if (!CGAL::is_closed(spt)) { std::cout << "Repairing non-closed component" << std::endl; fnRepair(spt); } if (!CGAL::is_closed(spt)) { std::cout << "Component still not closed after repair, skipping" << std::endl; continue; } std::cout << "Clipping component " << processedComponents++ << " with " << spt.number_of_vertices() << " vertices and " << spt.number_of_faces() << " faces" << std::endl; bool clipResult = PMP::clip(spt, clp, CGAL::parameters::clip_volume(true).visitor(visitor)); status = status && clipResult; if (!clipResult) { std::cerr << "Warning: Clip operation failed for component " << processedComponents - 1 << std::endl; } } std::cout << "Section mesh has " << section.number_of_vertices() << " vertices and " << section.number_of_faces() << " faces" << std::endl; return status; } static bool GenerateUVCoord(CGALMesh& target) { if (target.is_empty()) return false; std::vector<Point> points; std::vector<std::vector<size_t>> polygons; PMP::polygon_mesh_to_polygon_soup(target, points, polygons); PMP::repair_polygon_soup(points, polygons); PMP::orient_polygon_soup(points, polygons); if (PMP::is_polygon_soup_a_polygon_mesh(polygons)) { PMP::polygon_soup_to_polygon_mesh(points, polygons, target); } auto uvmap = target.add_property_map<VertexIndex, Kernel::Point_2> ("v:uv", Kernel::Point_2(0.0, 0.0)).first; /*CGALMesh::Halfedge_index bh; for (auto h : target.halfedges()) { if (target.is_border(h)) { bh = h; break; } }*/ std::vector<CGALMesh::Halfedge_index> bhs; PMP::extract_boundary_cycles(target, std::back_inserter(bhs)); //SMP::ARAP_parameterizer_3<CGALMesh> parameterizer; SMP::LSCM_parameterizer_3<CGALMesh> parameterizer; //SMP::Mean_value_coordinates_parameterizer_3<CGALMesh> parameterizer; //SMP::Discrete_authalic_parameterizer_3<CGALMesh> parameterizer; //SMP::Barycentric_mapping_parameterizer_3<CGALMesh> parameterizer; //SMP::Discrete_conformal_map_parameterizer_3<CGALMesh> parameterizer; //SMP::Iterative_authalic_parameterizer_3<CGALMesh> parameterizer; //const unsigned int iterations = 15; for (auto& bh : bhs) { auto ecode = SMP::parameterize(target, parameterizer, bh, uvmap); if (ecode != SMP::OK) { cout << "Parameterize failed" << ecode << endl; return false; } } aiVector2D min(static_cast<ai_real>(std::numeric_limits<double>::max())); aiVector2D max(static_cast<ai_real>(-std::numeric_limits<double>::max())); for (auto pnt : uvmap) { min.x = std::min(static_cast<ai_real>(min.x), static_cast<ai_real>(pnt.x())); min.y = std::min(static_cast<ai_real>(min.y), static_cast<ai_real>(pnt.y())); max.x = std::max(static_cast<ai_real>(max.x), static_cast<ai_real>(pnt.x())); max.y = std::max(static_cast<ai_real>(max.y), static_cast<ai_real>(pnt.y())); } auto extent = max - min; double length = std::max(static_cast<double>(extent.x), static_cast<double>(extent.y)); double step = 1.0 / length; for (const auto vd : CGAL::vertices(target)) { auto pnt = get(uvmap, vd); double u = (static_cast<double>(pnt.x()) - static_cast<double>(min.x)) * step; double v = (static_cast<double>(pnt.y()) - static_cast<double>(min.y)) * step; put(uvmap, vd, Kernel::Point_2(u - std::floor(u), v - std::floor(v))); } return true; } static bool SaveAfterClipOriMesh_UVCoord(const MeshEntity& input, CGALMesh& clp, vector<MeshEntity>& out, bool texture) { const CGALMesh& msh = get<0>(input); if (msh.is_empty()) return false; std::vector<CGALMesh> meshes; PMP::split_connected_components(msh, meshes); for (auto& spt : meshes) { size_t nf = spt.number_of_faces(); if (nf < 2) continue; bool is_out = is_outer_closed_boundary(spt); if (is_out) continue; if (!CGAL::is_closed(spt)) { fnRepair(spt); } if (!CGAL::is_closed(spt)) { continue; } PMP::clip(spt, clp, CGAL::parameters::clip_volume(false)); if (texture) { GenerateUVCoord(spt); } out.push_back({ spt, get<1>(input), get<2>(input) }); } return true; } /** * 主函数 - 3D模型剖切程序入口 * * 程序功能: * 1. 读取剖切曲线文件 * 2. 生成剖切面 * 3. 读取3D模型 * 4. 执行剖切操作 * 5. 导出剖切面和剖切后的模型 * * 命令行参数: * argv[1] - 剖切曲线文件路径 * argv[2] - 输入模型文件路径 * argv[3] - 输出剖切面文件路径 * argv[4] - 输出剖切后模型文件路径 * argv[5] - 可选参数 "withTex" 表示生成纹理坐标 */ int main(int argc, char** argv) { // 检查命令行参数数量 if (argc < 5) { std::cerr << "Usage: " << argv[0] << " <clip_line_file> <model_file> <output_section> <output_clipped> [withTex]" << std::endl; std::cerr << "参数说明:" << std::endl; std::cerr << " clip_line_file: 剖切曲线文件(包含3D点坐标)" << std::endl; std::cerr << " model_file: 输入3D模型文件" << std::endl; std::cerr << " output_section: 输出剖切面文件" << std::endl; std::cerr << " output_clipped: 输出剖切后模型文件" << std::endl; std::cerr << " withTex: 可选参数,生成纹理坐标" << std::endl; return 0; } // 解析命令行参数 fs::path ClipLine_inpath(argv[1]); // 剖切曲线文件路径 fs::path Model_inpath(argv[2]); // 输入模型文件路径 fs::path outpath1(argv[3]); // 输出剖切面文件路径 fs::path outpath2(argv[4]); // 输出剖切后模型文件路径 // 检查是否启用纹理坐标生成 bool withTex = (argc > 5 && strcmp(argv[5], "withTex") == 0); //bool withTex = true; // 可以强制启用纹理坐标生成 // 步骤1:读取剖切曲线 std::cout << "Reading clip line from: " << ClipLine_inpath << std::endl; vector<Point> curve = readCurve(ClipLine_inpath); // 检查曲线是否有效 if (curve.empty()) { std::cerr << "Error: No valid curve points found. Cannot proceed." << std::endl; return 1; } // 步骤2:生成剖切面 std::cout << "Generating clip surface..." << std::endl; CGALMesh clip = generateSurface1(curve); // 使用方法1生成剖切面 // 检查剖切面是否有效 if (clip.is_empty()) { std::cerr << "Error: Generated clip surface is empty!" << std::endl; return 1; } // 步骤3:读取输入模型 std::cout << "Reading model from: " << Model_inpath << std::endl; std::vector<MeshEntity> ims; // 存储导入的网格实体 if (!ImportMeshes(Model_inpath, ims)) { std::cerr << "Error: Failed to import model!" << std::endl; return 1; } std::cout << "Imported " << ims.size() << " mesh entities" << std::endl; // 步骤4:执行剖切操作 std::vector<MeshEntity> oms, ori; // oms存储剖切面,ori存储剖切后的模型 // 处理每个网格实体,生成剖切面 for (const auto& item : ims) { CGALMesh msh = get<0>(item); // 获取网格 CGALMesh clp = clip; // 复制剖切面 CGALMesh outm; // 输出剖切面 std::cout << "Processing mesh: " << std::get<2>(item) << std::endl; // 执行剖切操作 if (SectionClipping(msh, clp, outm)) { if (!outm.is_empty()) { std::cout << "Generated section with " << outm.number_of_vertices() << " vertices and " << outm.number_of_faces() << " faces" << std::endl; // 如果需要,生成纹理坐标 if (withTex) { GenerateUVCoord(outm); } // 将剖切面添加到输出列表 oms.push_back({ outm, std::get<1>(item), std::get<2>(item) }); } else { std::cout << "Warning: Generated section is empty" << std::endl; } } else { std::cout << "Warning: Section clipping failed" << std::endl; } } // 处理每个网格实体,生成剖切后的模型 for (const auto& item : ims) { CGALMesh clp = clip; SaveAfterClipOriMesh_UVCoord(item, clp, ori, false); } /* 注释掉的合并代码 std::vector<MeshEntity> all; all.reserve(oms.size() + ori.size()); all.insert(all.end(), oms.begin(), oms.end()); all.insert(all.end(), ori.begin(), ori.end()); */ // 步骤5:导出结果 std::cout << "Exporting " << oms.size() << " section meshes to: " << outpath1 << std::endl; ExportMeshes(outpath1, oms, withTex); std::cout << "Exporting " << ori.size() << " clipped meshes to: " << outpath2 << std::endl; ExportMeshes(outpath2, ori, false); return 1; // 程序成功完成 }
09-20
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值