简介

参考链接 https://gamedev.stackexchange.com/questions/26974/repairing-back-facing-triangles-without-user-input

缺陷, 对于非流形的网格会失败

code

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <map>
#include <vector>
#include <queue>
#include <set>
#include <algorithm>
using namespace std;
struct triangle{
int pv1;
int pv2;
int pv3;
triangle(int pv1_, int pv2_, int pv3_):pv1(pv1_), pv2(pv2_), pv3(pv3_){};
bool operator == (const triangle &rhs){
return pv1 == rhs.pv1 && pv2 == rhs.pv2 && pv3 == rhs.pv3;
}
bool operator < (const triangle &rhs) {
return (pv1 + pv2 + pv3) < (rhs.pv1 + rhs.pv2 + rhs.pv3);
}
bool operator < (const triangle &rhs) const {
return (pv1 + pv2 + pv3) < (rhs.pv1 + rhs.pv2 + rhs.pv3);
}
};
int pointNum;
int faceNum;
std::queue<int> triQ;
std::vector<bool> check;
std::vector<triangle> allTriangle;
std::map<int, std::vector<int> > mm;
// 检查a和b的公共边是否序列相等
bool isFlip(triangle b, int a1, int a2){
if(b.pv1 == a1 && b.pv2 == a2){
return true;
}
if(b.pv2 == a1 && b.pv3 == a2){
return true;
}
if(b.pv3 == a1 && b.pv1 == a2){
return true;
}
return false;
}
void fixFlip(triangle &a){
int a1 = a.pv1;
int a2 = a.pv2;
int a3 = a.pv3;
a.pv1 = a3;
a.pv3 = a1;
} /**
* @description:
* @param {set<int> &s1, std::set<int>} &s2
* @param {int} a1
* @param {int} a2
* @return 是否修复了, 或者除了一种情况两个三角形都是true的情况
*/
bool refine(std::set<int> &s1, std::set<int> &s2, int a1, int a2, int &falseTri){
std::set<int> c;
std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), inserter(c, c.begin()));
if(c.size() != 2) { // 对于封闭的曲面而言是对的
std::cout << "[ERROR] c size not equal 2 " << c.size() << "\n";
return false;
}
std::vector<int> cv;
for(auto it:c){
cv.push_back(it);
}
int trueTri = -1;
falseTri = -1;
if(check[cv[0]] == true && check[cv[1]] == false){
trueTri = cv[0];
falseTri = cv[1];
}
else if(check[cv[0]] == false && check[cv[1]] == true) {
trueTri = cv[1];
falseTri = cv[0];
}else{
std::cout << "[DEBUG] maybe this two triangle is true??\n";
return false;
}
if(falseTri != -1) {
if(isFlip(allTriangle[falseTri], a1, a2)){
fixFlip(allTriangle[falseTri]);
}
}
return true;
}
void BFS(int i){
check[i] = true;
triQ.push(i);
while(!triQ.empty()){
int front = triQ.front();
triQ.pop();
triangle t = allTriangle[front];
std::set<int> s1;
std::set<int> s2;
std::set<int> s3;
std::set<int> c;
for(auto it : mm[t.pv1]) {
s1.insert(it);
}
for(auto it : mm[t.pv2]) {
s2.insert(it);
}
for(auto it : mm[t.pv3]) {
s3.insert(it);
}
int falseTri;
if(refine(s1, s2, t.pv1, t.pv2, falseTri)){
triQ.push(falseTri);
check[falseTri] = true;
}
if(refine(s2, s3, t.pv2, t.pv3, falseTri)){
triQ.push(falseTri);
check[falseTri] = true;
}
if(refine(s3, s1, t.pv3, t.pv1, falseTri)){
triQ.push(falseTri);
check[falseTri] = true;
}
}
}
void BFSTraverse(){
// 访问数组初始化
for(int i=0; i<faceNum; i++){
if(!check[i]){
BFS(i);
}
}
}
int main() {
std::string filename;
filename = "test.off";
std::string filenameOut;
filenameOut = "testOut.off";
std::ifstream iff = std::ifstream(filename.c_str(), std::ios::in);
std::ofstream off = std::ofstream(filenameOut.c_str(), std::ios::out);
// Write header
off << "OFF" << std::endl;
if (!off.good()) {
std::cout << "[Error] Could not open file " << filenameOut << " for writing!" << std::endl;
off.close();
return false;
}
if(!iff.good()) {
std::cout << "[ERROR] could not open file " << filename << " for reading!" << std::endl;
iff.close();
return false;
}
std::string line;
getline(iff, line);
if(line != "OFF") {
std::cout << "[ERROR] format not supported" << std::endl;
return false;
}
getline(iff, line);
stringstream sstr;
sstr.str(line); sstr >> pointNum;
sstr >> faceNum;
sstr.clear(); off << line << "\n";
for(int i=0; i<pointNum; i++){
getline(iff, line);
off << line << "\n";
} std::map<triangle, int> mi;
int tmp;
int pv1;
int pv2;
int pv3;
for(int i=0; i<faceNum; i++){
getline(iff, line);
sstr.str(line);
sstr >> tmp;
if(tmp != 3) {
std::cout << "[ERROR] format not supported_" << std::endl;
return false;
}
sstr >> pv1;
sstr >> pv2;
sstr >> pv3;
triangle t(pv1, pv2, pv3);
sstr.clear();
mm[pv1].push_back(i);
mm[pv2].push_back(i);
mm[pv3].push_back(i);
allTriangle.push_back(t);
mi[t] = i;
}
check.resize(allTriangle.size(), false);
BFSTraverse();
for(auto it : allTriangle){
off << "3 " << it.pv1 << " " << it.pv2 << " " << it.pv3 << "\n";
}
off.close();
iff.close();
return 0;
}

off 表面三角网格翻转问题解决的更多相关文章

  1. 三角网格(Triangle Mesh)的理解

    最简单的情形,多边形网格不过是一个多边形列表:三角网格就是全部由三角形组成的多边形网格.多边形和三角网格在图形学和建模中广泛使用,用来模拟复杂物体的表面,如建筑.车辆.人体,当然还有茶壶等.图14.1 ...

  2. 三角网格上的寻路算法Part.1—Dijkstra算法

    背景 最近在研究中产生了这样的需求:在三角网格(Mesh)表示的地形图上给出两个点,求得这两个点之间的地面距离,这条距离又叫做"测地线距离(Geodesic)".计算三角网格模型表 ...

  3. bullet物理引擎与OpenGL结合 导入3D模型进行碰撞检测 以及画三角网格的坑

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11681069.html 一.初始化世界以及模型 /// 冲突配置包含内存的默认设置,冲突设置. ...

  4. 三角网格上的寻路算法Part.2—A*算法

    背景 继上一篇三角网格Dijkstra寻路算法之后,本篇将继续介绍一种更加智能,更具效率的寻路算法-A*算法,本文将首先介绍该算法的思想原理,再通过对比来说明二者之间的相同与不同之处,然后采用类似Di ...

  5. PCL: 根据几何规则的曲面剖分-贪婪法表面重建三角网格

    点云场景中进行物体识别,使用全局特征的方法严重依赖于点云分割,难以适应杂乱场景.使用局部特征,即对点云进行提取类似于3D SURF.ROPS之类的局部特征,需要寻找离散点云块的局部显著性. 点云的基本 ...

  6. 关于TRIANGLE二维三角网格生成器在windows下的配置说明

    近期须要用到三角网格生成的一些东西,所以就把TRIANGLE这个库编译了一下,发现编译过程还是略微有些纠结,于是就想到写下来.希望以后有些童鞋看到少走一些弯路. 首先很感谢eryar的帮助,很感谢! ...

  7. 三角网格上的寻路算法Part.1—Dijkstra算法 等

    http://www.cnblogs.com/chnhideyoshi/p/AStar.html

  8. 转: Meshlab简介

    本文翻译自Meshlab主页:http://www.meshlab.net/ MeshLab是用于处理和编辑3D三角形网格的开源系统.它提供了一组用于编辑,清理,修复,检查,渲染,纹理和转换网格的工具 ...

  9. 从零开始一起学习SLAM | 点云到网格的进化

    点击公众号"计算机视觉life"关注,置顶星标更快接收消息! 本文编程练习框架及数据获取方法见文末获取方式 菜单栏点击"知识星球"查看「从零开始学习SLAM」一 ...

  10. 图像数据到网格数据-1——MarchingCubes算法

    原文:http://blog.csdn.net/u013339596/article/details/19167907 概述 之前的博文已经完整的介绍了三维图像数据和三角形网格数据.在实际应用中,利用 ...

随机推荐

  1. 通过一个DEMO理解MCP(模型上下文协议)的生命周期

    在LLM应用的快速发展中,一个核心挑战始终存在:如何让模型获取最新.最准确的外部知识并有效利用工具? 背景其实很简单:大模型(LLM)再强,也总有不知道的东西,怎么办?让它"查资料" ...

  2. journalctl -u docker 查看日志

    转载注明出处: 1. 查看 Docker 服务的最新日志(实时滚动) sudo journalctl -u docker -f -f 参数表示 跟随(follow),会持续输出最新日志(类似 tail ...

  3. 编译执行与解释执行的区别是什么?JVM 使用哪种方式?

    编译执行与解释执行的区别 1. 编译执行(Compiled Execution) 定义: 将源代码一次性翻译为机器码(目标代码),生成可直接运行的二进制文件. 特点: 翻译只发生一次,生成的目标代码可 ...

  4. DPDI(Dispatch PDI)kettle调度管理平台之实操演练第002讲-最强三件套之Dispatch PDI+PDI+PRD生成DPDI应用数据库数据字典

    DPDI实操演练第002讲 最强三件套之Dispatch PDI+PDI+PRD生成DPDI应用数据库数据字典 1.案例适用范围 Dispatch PDI资源仓库管理使用可参考 Dispatch ...

  5. 【代码】Android|判断asserts下的文件存在与否,以及普通文件存在与否

    作者版本:Android 11及以上 主要是发现网上没有完整的.能跑的代码,不知道怎么回事,GPT给我重写的.我只能保证这个代码尊嘟能跑,不像其他的缺胳膊少腿的. asserts 贴一下结果: boo ...

  6. ubuntu20.04下VSCode无法输入中文解决方法

    解决方法:重新安装VSCode. 我一开始是在ubuntu商店下载的,结果上网查了了下,商店里的VSCode是阉割版的,想要输入中文就要重新安装. 安装流程:先删除再安装. 1.可以在商店里已安装界面 ...

  7. TVM:Object家族

    Object.h概述 命名空间: TVM::runtime 文件中包含的结构: 1.结构体TypeIndex 2.类Object 3.类ObjectPtr 4.类ObjectRef 5.结构体Obje ...

  8. 密码哈希:Bcrypt的魔法与盐值的秘密

    title: 密码哈希:Bcrypt的魔法与盐值的秘密 date: 2025/06/01 16:41:37 updated: 2025/06/01 16:41:37 author: cmdragon ...

  9. python基础—数字,字符串练习题

    1.如有以下变量 n1=5,请使用 int 的提供的方法,得到该变量最少可以用多少个二进制位表示? n1=5 r=n1.bit_lenght() #当前数字的二进制,至少用n位表示.bit_lengh ...

  10. SM30里DEC数据显示0

    需求:DEC数据在维护的时候显示0 1,设置数据元素对于的域带转换历程. 2,写转换历程函数(注意两个历程的输入和输出类型,这个需要修改) FUNCTION conversion_exit_zdays ...