简介

没有 cc 细分好。

参考链接

http://graphics.stanford.edu/courses/cs468-10-fall/LectureSlides/10_Subdivision.pdf 非常好对于细分的介绍

细分示意图



codeTip

代码实现过程,比较潦草。主要看第一张示意图,由于openmesh在加面的时候需要判断面的法向,做的比较粗糙,但是对于立方体的细分是支持的。

code

#include "doo_sabin_surface_subdivision.h"
#include <iostream>
#include <unordered_map>
#include <QInputDialog> /**
* @description: 构造函数
* @param {type}
* @return {type}
*/
DS::DS(Data* data_) : Strategy(data_) {
times = 1;
genCube();
} /**
* @description: 析构函数
* @param {type}
* @return {type}
*/
DS::~DS() {} /*!
* Computes the weight factor for the ith vertex of a face with k
* vertices. The formula of Doo and Sabin is used.
*
* @param i Index of vertex in face (0, 1, ..., k-1)
* @param k Number of vertices
*
* @return Weight
*/
inline double DS::weights_doo_sabin(size_t k, size_t i)
{
if (i == 0)
return(0.25 + 5.0 / (4.0 * k));
else
return((3.0 + 2.0 * cos(2 * M_PI * i / k)) / (4.0 * k));
} /**
* @description: 一整套流程
* @param {type}
* @return {int} 管线运行是否成功
*/
bool DS::Run() {
times = QInputDialog::getInt(this, "Surface Mesh", "Please input times",
1, 1, 1000, 1);
for (int i = 0; i <= times; i++) {
if (i == 0) {
char a[20];
sprintf(a, "output%d.off", i);
genMesh(a);
}
else {
genFacePoint();
genEdgePoint();
genVertexPoint();
connectPoints();
char a[20];
sprintf(a, "output%d.off", i);
genMesh(a);
}
// 清空变量
std::cout << "[DEBUG] 迭代了第 " << i << " 次。" << std::endl;
}
getResult(); return true;
} /**
* @description: 生成一个立方体(四边形网格)
* @param {type}
* @return {type}
*/
void DS::genCube()
{
MyMesh::VertexHandle vhandle[9];
vhandle[0] = mesh.add_vertex(MyMesh::Point(-1, -1, 1));
vhandle[1] = mesh.add_vertex(MyMesh::Point(1, -1, 1));
vhandle[2] = mesh.add_vertex(MyMesh::Point(1, 1, 1));
vhandle[3] = mesh.add_vertex(MyMesh::Point(-1, 1, 1));
vhandle[4] = mesh.add_vertex(MyMesh::Point(-1, -1, -1));
vhandle[5] = mesh.add_vertex(MyMesh::Point(1, -1, -1));
vhandle[6] = mesh.add_vertex(MyMesh::Point(1, 1, -1));
vhandle[7] = mesh.add_vertex(MyMesh::Point(-1, 1, -1)); std::vector<MyMesh::VertexHandle> face_vhandles;
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[3]);
mesh.add_face(face_vhandles); face_vhandles.clear();
face_vhandles.push_back(vhandle[7]);
face_vhandles.push_back(vhandle[6]);
face_vhandles.push_back(vhandle[5]);
face_vhandles.push_back(vhandle[4]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[4]);
face_vhandles.push_back(vhandle[5]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[1]);
face_vhandles.push_back(vhandle[5]);
face_vhandles.push_back(vhandle[6]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[3]);
face_vhandles.push_back(vhandle[2]);
face_vhandles.push_back(vhandle[6]);
face_vhandles.push_back(vhandle[7]);
mesh.add_face(face_vhandles);
face_vhandles.clear();
face_vhandles.push_back(vhandle[0]);
face_vhandles.push_back(vhandle[3]);
face_vhandles.push_back(vhandle[7]);
face_vhandles.push_back(vhandle[4]);
mesh.add_face(face_vhandles);
} /**
* @description: 生成所有面点
* @param {type} TODO
* @return {type}
*/
void DS::genFacePoint()
{
facePoints.clear();
for (const auto& fh : mesh.faces())
{
OpenMesh::Vec3d facePoint(0, 0, 0);
int facePointsNumber = 0;
for (const auto& fvh : mesh.fv_range(fh))
{
OpenMesh::DefaultTraits::Point point = mesh.point(fvh);
facePoint += point;
facePointsNumber++;
}
facePoint /= facePointsNumber;
facePoints[fh.idx()] = facePoint;
}
} /**
* @description: 生成所有的边点暂时不考虑hole 就是全是日子结构的
* @param {type}
* @return {type}
*/
void DS::genEdgePoint()
{
edgePoints.clear();
for (auto e_it = mesh.edges_begin(); e_it != mesh.edges_end(); ++e_it)
{
// 得到边所代表的半边
OpenMesh::HalfedgeHandle heh1 = mesh.halfedge_handle(*e_it, 0); // 默认一个方向的半边
OpenMesh::Vec3d edgePoint(0, 0, 0);
int edgePointsNumber = 0;
OpenMesh::DefaultTraits::Point pointV = mesh.point(mesh.from_vertex_handle(heh1)); // 这条(半)边的起点
OpenMesh::DefaultTraits::Point pointW = mesh.point(mesh.to_vertex_handle(heh1)); // 这条(半)边的终点 edgePoints[heh1.idx()] = (pointV + pointW) / 2.0;
}
} /**
* @description: 生成新的顶点
* @param {type}
* @return {type}
*/
void DS::genVertexPoint()
{
vertexPoints.clear();
vertexPoints_.clear();
// 原始点接触的面的所有的面点的均值
for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); v_it++)
{
OpenMesh::Vec3d originPoint = (OpenMesh::Vec3d)mesh.point(*v_it);
OpenMesh::Vec3d facePoint(0, 0, 0);
int faceNumber = 0;
std::vector<OpenMesh::Vec3d> facePoints_;
std::vector<OpenMesh::Vec3d> edgePoints_;
for (auto vf_it = mesh.vf_iter(*v_it); vf_it.is_valid(); ++vf_it)
{ //这个顶点所带有的面迭代器
facePoints_.push_back(facePoints[(*vf_it).idx()]);
} // 原始点接触的边的中间点的值的均值 * 2
OpenMesh::Vec3d edgePoint(0, 0, 0);
int edgeNumber = 0;
for (auto vv_it = mesh.vv_begin(*v_it); vv_it != mesh.vv_end(*v_it); vv_it++) { // 为啥还有孤立点?
OpenMesh::Vec3d point = (OpenMesh::Vec3d)mesh.point(*vv_it);
edgePoints_.push_back((point + originPoint) / 2.0);
}
int index = 0;
for (auto vf_it = mesh.vf_iter(*v_it); vf_it.is_valid(); ++vf_it)
{ //这个顶点所带有的面迭代器
vertexPoints_[(*v_it).idx()].push_back(1.0/4.0*(originPoint + edgePoints_[index] +
edgePoints_[(index - 1 + edgePoints_.size())% edgePoints_.size()] + facePoints_[index]));
index++;
}
}
} /**
* @description: 连接面点和边点 (要严格的封闭的四边形)
* @param {type}
* @return {type}
*/
void DS::connectPoints() {
if (!mesh.has_vertex_status()) mesh.request_vertex_status();
if (!mesh.has_face_status()) mesh.request_face_status();
if (!mesh.has_edge_status()) mesh.request_edge_status(); std::vector<MyMesh::VertexHandle> vertexDelHandle;// 存储将要删除的顶点
for (const auto& v : mesh.vertices()) {
vertexDelHandle.push_back(v);
} // 加入所有新增节点
std::vector<MyMesh::VertexHandle> facePointsHandle;
std::vector<std::vector<MyMesh::VertexHandle>> faceHandles;
std::map<OpenMesh::Vec3d, MyMesh::VertexHandle> out;
for (const auto& v : vertexPoints_) {
std::vector<MyMesh::VertexHandle> facePointHandle;
for (const auto& vv : v.second) {
facePointHandle.push_back(mesh.add_vertex((OpenMesh::DefaultTraits::Point)vv));
out[vv] = facePointHandle[facePointHandle.size() - 1];
}
faceHandles.push_back(facePointHandle);
}
std::vector<std::vector<MyMesh::VertexHandle>> faceEdgeHandles;
std::vector<std::vector<MyMesh::VertexHandle>> faceFaceHandles;
// 距离边最近的四个顶点构成一个面
int iindex = 0;
for (auto e_it = mesh.edges_begin(); e_it != mesh.edges_end(); ++e_it)
{
// 得到边所代表的半边
OpenMesh::HalfedgeHandle heh1 = mesh.halfedge_handle(*e_it, 0); // 默认一个方向的半边
OpenMesh::Vec3d edgePoint(0, 0, 0);
int edgePointsNumber = 0;
OpenMesh::DefaultTraits::Point pointV = mesh.point(mesh.from_vertex_handle(heh1)); // 这条(半)边的起点
OpenMesh::DefaultTraits::Point pointW = mesh.point(mesh.to_vertex_handle(heh1)); // 这条(半)边的终点
std::vector<OpenMesh::Vec3d> vs;
for (const auto& vt : vertexPoints_[mesh.from_vertex_handle(heh1).idx()]) {
vs.push_back(vt);
}
for (const auto& vt : vertexPoints_[mesh.to_vertex_handle(heh1).idx()]) {
vs.push_back(vt);
} sort(vs.begin(), vs.end(), [=](const auto& a, const auto& b) {
return (a - edgePoints[heh1.idx()]).norm() < (b - edgePoints[heh1.idx()]).norm();
});
// 再对这些顶点进行排序, 按照
sort(vs.begin(), vs.begin()+4, [=](const auto& a, const auto& b) {
return (a - vs[0]).norm() < (b - vs[0]).norm();
}); std::vector<MyMesh::VertexHandle> vvs; faceEdgeHandles.push_back(vvs);
faceEdgeHandles[iindex].push_back(out[vs[0]]);
faceEdgeHandles[iindex].push_back(out[vs[2]]);
faceEdgeHandles[iindex].push_back(out[vs[3]]);
faceEdgeHandles[iindex].push_back(out[vs[1]]);
iindex++;
}
iindex = 0;
for (const auto& fh : mesh.faces())
{
OpenMesh::Vec3d facePoint(0, 0, 0);
int facePointsNumber = 0;
std::vector<OpenMesh::Vec3d> vs;
int ix = 0;
for (const auto& fvh : mesh.fv_range(fh))
{
ix++;
for (const auto& vt : vertexPoints_[(fvh).idx()]) {
vs.push_back(vt);
}
}
// 选取最近的四个顶点
sort(vs.begin(), vs.end(), [=](const auto& a, const auto& b) {
return (a - facePoints[fh.idx()]).norm() < (b - facePoints[fh.idx()]).norm();
});
// 再对这些顶点进行排序, 按照
sort(vs.begin(), vs.begin() + 4, [=](const auto& a, const auto& b) {
return (a - vs[0]).norm() < (b - vs[0]).norm();
}); std::vector<MyMesh::VertexHandle> vvs; faceFaceHandles.push_back(vvs);
if (ix == 3) {
faceFaceHandles[iindex].push_back(out[vs[0]]);
faceFaceHandles[iindex].push_back(out[vs[1]]);
faceFaceHandles[iindex].push_back(out[vs[2]]);
}
else {
faceFaceHandles[iindex].push_back(out[vs[0]]);
faceFaceHandles[iindex].push_back(out[vs[2]]);
faceFaceHandles[iindex].push_back(out[vs[3]]);
faceFaceHandles[iindex].push_back(out[vs[1]]);
} iindex++;
} std::vector<std::vector<MyMesh::VertexHandle>> v;
int i = 0;
std::vector<MyMesh::FaceHandle> faceDelHandle;// 存储将要删除的面
for (const auto& fh : mesh.faces()) {
faceDelHandle.push_back(fh);
} // 开始新增顶点的面
for (const auto& fvh : faceHandles) {
mesh.add_face(fvh);
}
// 开始新增边的面
int indexx = 0;
for (const auto& fvh : faceEdgeHandles) {
if (mesh.add_face(fvh)==OpenMesh::FaceHandle(-1)) {
auto tmp = faceEdgeHandles[indexx][1];
faceEdgeHandles[indexx][1] = faceEdgeHandles[indexx][3];
faceEdgeHandles[indexx][3] = tmp;
mesh.add_face(fvh);
}
indexx++;
}
// 开始新增面的面
indexx = 0;
for (const auto& fvh : faceFaceHandles) {
if (mesh.add_face(fvh) == OpenMesh::FaceHandle(-1)) {
if (faceFaceHandles[indexx].size() == 4) {
auto tmp = faceFaceHandles[indexx][1];
faceFaceHandles[indexx][1] = faceFaceHandles[indexx][3];
faceFaceHandles[indexx][3] = tmp;
}
else if(faceFaceHandles[indexx].size() == 3){
auto tmp = faceFaceHandles[indexx][1];
faceFaceHandles[indexx][1] = faceFaceHandles[indexx][2];
faceFaceHandles[indexx][2] = tmp;
}
mesh.add_face(fvh);
}
indexx++;
}
//// 开始删除面
for (int i = 0; i < faceDelHandle.size(); i++) {
mesh.delete_face(faceDelHandle[i], true);
}
//开始删除顶点
for (int i = 0; i < vertexDelHandle.size(); i++) {
mesh.delete_vertex(vertexDelHandle[i], true);
} mesh.garbage_collection();
if (mesh.has_vertex_status()) mesh.release_vertex_status();
if (mesh.has_face_status()) mesh.release_face_status();
if (mesh.has_edge_status()) mesh.release_edge_status();
} /**
* @description: 输出新网格
* @param {string} name 文件名称 默认 output.obj
* @return {type}
*/
void DS::genMesh(std::string name) {
if (name == "") {
name = "output.off";
}
try {
if (!OpenMesh::IO::write_mesh(mesh, name)) {
std::cerr << "Cannot write mesh to file 'output.off'" << std::endl;
return;
}
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
return;
}
} void DS::getResult() {
//if (getData()->edges.size() == 0) {
// getData()->edges.push_back(std::vector<V3f>());
//}
getData()->edges.push_back(std::vector<V3f>());
for (auto e_it = mesh.edges_begin(); e_it != mesh.edges_end(); ++e_it)
{
// 得到边所代表的半边
OpenMesh::HalfedgeHandle heh1 = mesh.halfedge_handle(*e_it, 0); // 默认一个方向的半边
OpenMesh::Vec3d edgePoint(0, 0, 0);
int edgePointsNumber = 0;
OpenMesh::DefaultTraits::Point pointV = mesh.point(mesh.from_vertex_handle(heh1)); // 这条(半)边的起点
OpenMesh::DefaultTraits::Point pointW = mesh.point(mesh.to_vertex_handle(heh1)); // 这条(半)边的终点
getData()->edges[0].push_back({ pointV[0], pointV[1], pointV[2] });
getData()->edges[0].push_back({ pointW[0], pointW[1], pointW[2] });
}
}
#pragma once
#include "strategy.h"
#include "qwidget.h"
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <map> //typedef struct FFACE {
// int faceId;
// int edgeId1;
// int vertexId;
// int edgeId2;
//}FFACE; class DS :public Strategy, public QWidget
{
Q_OBJECT
private:
typedef OpenMesh::PolyMesh_ArrayKernelT<> MyMesh;
MyMesh mesh;
std::map<int, OpenMesh::Vec3d> facePoints;
std::map<int, OpenMesh::Vec3d> edgePoints;
std::map<int, OpenMesh::Vec3d> vertexPoints;
std::map<int, std::vector<OpenMesh::Vec3d>> vertexPoints_;
int times;
public:
DS(Data* data_);
~DS();
double weights_doo_sabin(size_t k, size_t i);
void genCube();
void genFacePoint();
void genEdgePoint();
void genVertexPoint();
void connectPoints();
void genMesh(std::string name);
bool Run();
void getResult();
};

image

基于 doo-sabin的细分的更多相关文章

  1. Unity3d 使用DX11的曲面细分

    Unity3d surface Shaderswith DX11 Tessellation Unity3d surface shader 在DX11上的曲面细分 I write this articl ...

  2. Google Analytics之增强型电子商务报告

    虽然Google Analytics很多年以前就提供了电子商务报告的功能,但对于电子商务网站来说,这个报告缺失的东西还太多.而Google Analytics即将推出的增强型电子商务报告有望弥补这一短 ...

  3. Vulkan Tutorial 10 图形管线

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 通过接下来的章节,我们将会开启有关图形管线的话题,通过对图 ...

  4. Flexconnect部署

    该记录主要用于针对于无线网络中Flexconnect的部署,可能涉及到的有Flexconnect中的组件,如何部署.(注意:在7.2版本以前,Flexconnect叫做HREAP),目前都称作为Fle ...

  5. 【译】Gartner CWPP市场指南

    https://www.gartner.com/doc/reprints?id=1-1YSHGBQ8&ct=200416&st=sb?utm_source=marketo&ut ...

  6. 2020国防科大综述:3D点云深度学习——综述(3D点云分割部分)

    目录 摘要 1.引言: 2.背景 2.1 数据集 2.2评价指标 3.3D点云分割 3.1 3D语义分割 3.1.1 基于投影的方法 多视图表示 球形表示 3.1.2 基于离散的方法 稠密离散表示 稀 ...

  7. 【Machine Learning】决策树案例:基于python的商品购买能力预测系统

    决策树在商品购买能力预测案例中的算法实现 作者:白宁超 2016年12月24日22:05:42 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本 ...

  8. 基于token的多平台身份认证架构设计

    基于token的多平台身份认证架构设计 1   概述 在存在账号体系的信息系统中,对身份的鉴定是非常重要的事情. 随着移动互联网时代到来,客户端的类型越来越多, 逐渐出现了 一个服务器,N个客户端的格 ...

  9. 记一次企业级爬虫系统升级改造(二):基于AngleSharp实现的抓取服务

    爬虫系统升级改造正式启动: 在第一篇文章,博主主要介绍了本次改造的爬虫系统的业务背景与全局规划构思: 未来Support云系统,不仅仅是爬虫系统,是集爬取数据.数据建模处理统计分析.支持全文检索资源库 ...

  10. 码途有道----基于系统观的核心能力构建-by-韩宏老师

    原文链接:http://blog.sina.com.cn/s/blog_7d5a09f90102v341.html 有感于同学们在大学中如何学习计算机技术有些感概,将我书(老码识途)中的序言整理了一下 ...

随机推荐

  1. 🎀windows-剪切板

    简介 Windows 剪贴板是一个临时存储区域,它允许用户在不同应用程序之间复制和粘贴文本.图像和其他类型的数据.从 Windows 10 开始,微软引入了一个改进的剪贴板功能,称为剪贴板历史记录,它 ...

  2. window下配置多个Git账号

    三步完成配置一台电脑下多git账号配置 1.生成密钥 git客户端安排好后,打开git Bash,生成SSH key. ssh-keygen -t rsa -C "user1111@emai ...

  3. 应对海量数据挑战,如何基于Euro NCAP标准开展高效智驾测试与评估?

    一.前言 随着自动驾驶技术的快速发展,庞大的测试数据和复杂的场景需求为性能与安全评估带来了巨大挑战.如何高效管理海量数据.挖掘关键场景,并满足以Euro NCAP(European New Car A ...

  4. [开源] Layouter(桌面助手)开源发布

    Layouter(桌面助手)是一款简洁.易用.美观的桌面整理软件,基于.net 6开发,支持Windows 7及以上操作系统.以 Apache-2.0 license 进行开源. 开源地址 https ...

  5. Golang从0到1实现简易版expired LRU cache带图解

    1.支持Put.Get的LRU实现 想要实现一个带过期时间的LRU,从易到难,我们需要先学会如何实现一个普通的LRU,做到O(1)的Get.Put. 想要做到O(1)的Get,我们很容易想到使用哈希表 ...

  6. C#开发的PDF文件浏览器 - 开源研究系列文章 - 个人小作品

    以前个人的PDF浏览主要是用的Adobe DC的PDF,不过它这个打开速度还是一般.后来安装的极速PDF浏览器,速度还是比较快的了.它这个主要是浏览,然后还能够安装编辑器对PDF文件进行编辑,不过就需 ...

  7. Surrounded Regions——LeetCode进阶路

    原题链接https://leetcode.com/problems/surrounded-regions/ 题目描述 Given a 2D board containing 'X' and 'O' ( ...

  8. 计算机网络-TCP/IP知识点

    快速以太网数据帧有效载荷的最小长度为 46 字节(参考:Ethernet Frame Calculations) 实战 (2012 408考研真题 47)(15分)主机 H 通过快速以太网连接 Int ...

  9. 打工人神助攻!2025年最火OKR工具榜单,看板式目标管理必备

    OKR(目标与关键成果)已成为企业目标管理的标准框架,为确保OKR目标可落地,可视化.协作性强的工具需求激增.2025年,哪些OKR工具真正助力团队落地战略?本文盘点当下最火的5款OKR工具,帮助HR ...

  10. k8s在线修改alertmanager.yaml

    简单说明 有些情况下,我们可以进入集群查看某些secret的配置,这里如果想修改secret的配置时,没有secrets的源文件,可以采取如下的方案. 例如我们需要修改alertmanager.yam ...