九韶内核: 多边形网格细分建模示例

概述

本教程提供了使用多边形网格进行细分曲面建模的基本用法,细分建模一般通过从创建一个基本体开始,通过拉伸,变换,细分等生成一个曲面的的稠密网格近似。

构建基本体

模块支持构建多种基本体,构建一个边长为3,每方向分割2段的立方体代码如下。

AMCAX::Frame3 frame;

AMCAX::SubD::MeshMakeCube makeCube(frame, 3, 3, 3, 2, 2, 2);

AMCAX::SubD::PolyMesh* mesh = makeCube.BuildMesh();

AMCAX::SubD::MeshMakeCube多边形网格接口中用于创建立方体的类定义 MeshMakeCube.hpp:15

AMCAX::SubD::PolyMesh多边形网格结构的类。 当前版本未导出底层的接口功能定义 PolyMesh.hpp:23

AMCAX::Frame3FrameT< double, 3 > Frame3三维标架定义 FrameT.hpp:885

构建一个底面半径为4,上面半径为2,高为4的圆台可以使用圆锥的提供的函数,默认沿着旋转方向分割8段,沿着高的方向分割4段。

AMCAX::Frame3 frame;

AMCAX::SubD::MeshMakeCone makeCone(frame, 4, 2, 4);

AMCAX::SubD::PolyMesh* mesh = makeCone.BuildMesh();

AMCAX::SubD::MeshMakeCone多边形网格接口中用于创建圆锥体的类定义 MeshMakeCone.hpp:15

构建一个漩涡模型

模型预览

本教程使用简单的漩涡模型展示细分建模模块中的一些主要建模功能。模型如下图所示:

漩涡模型

命名空间

为了示例代码清晰,使用命名空间

using namespace AMCAX;

using namespace SubD;

AMCAX::SubDAMCAX SubD 模块提供的所有接口所在的命名空间。定义 misc.docu:63

AMCAXAMCAX 内核提供的所有接口所在的命名空间。定义 misc.docu:8

构建底面

从构建一个平面开始。

Frame3 frame;

MeshMakeRectangle mkRect(frame, 3, 3, 3, 3);

PolyMesh* mesh = mkRect.BuildMesh();

点击这里example1可获得以上示例完整源码,大家根据学习需要自行下载。

拉伸面和变换

拉伸中间的面形成一个峰,注意执行拉伸函数仅修改网格拓扑,没有移动任何元素的位置。

std::vector face_id = {4};

std::vector face_id_new;

MeshExtrude::ExtrudeFace(mesh, face_id, face_id_new);

施加平移和旋转的变换将新生成的面移动到指定位置。

Vector3 vh(frame.Direction().Coord() * h);

MeshTransform trsfF;

Transformation3 trsfMove;

trsfMove.SetTranslation(vh);

trsfF.SetTransformation(trsfMove);

trsfF.TransformMeshFaces(mesh, face_id_new);

Transformation3 trsfRot;

trsfRot.SetRotation(Axis3(Point3(1.5, 1.5, 0.), Direction3(0., 0., 1.)), AMCAX::Constants::pi / 4);

trsfF.SetTransformation(trsfRot);

trsfF.TransformMeshFaces(mesh, face_id_new);

AMCAX::DirectionT::Coordconstexpr const CoordType & Coord() const noexcept获取方向的内在坐标定义 DirectionT.hpp:213

AMCAX::FrameT::Directionconstexpr const DirectionT< Scalar, DIM > & Direction() const noexcept获取三维主方向(z 方向)定义 FrameT.hpp:441

AMCAX::TransformationT::SetRotationvoid SetRotation(const PointT< OtherScalar, DIM > &point, const OtherScalar2 &angle) noexcept设置变换为二维中绕点旋转某一角度定义 TransformationT.hpp:138

AMCAX::TransformationT::SetTranslationconstexpr void SetTranslation(const VectorT< OtherScalar, DIM > &vec) noexcept设置变换为平移定义 TransformationT.hpp:311

AMCAX::Constants::piconstexpr double pi数学常数 Pi,圆的周长与直径之比定义 Constants.hpp:42

AMCAX::Axis3AxisT< double, 3 > Axis3三维轴定义 AxisT.hpp:423

AMCAX::Transformation3TransformationT< double, 3 > Transformation3三维变换定义 TransformationT.hpp:1102

AMCAX::Vector3VectorT< double, 3 > Vector3三维向量定义 VectorT.hpp:707

AMCAX::Direction3DirectionT< double, 3 > Direction3三维方向定义 DirectionT.hpp:587

AMCAX::Point3PointT< double, 3 > Point3三维点定义 PointT.hpp:459

点击这里example2可获得以上示例完整源码,大家根据学习需要自行下载。

删除顶面

可以自由删除任意数目的面并留下洞。

MeshReduce::DeleteFaces(mesh, face_id_new);

点击这里example3可获得以上示例完整源码,大家根据学习需要自行下载。

拉伸边和变换

将删除面留下的边向四周拉伸从而生成新的顶面,先执行拉伸操作,然后施加缩放变换将新生成的边移动到指定位置。

std::vector edgeid;

std::vector edge_id_new;

for (int i = 0; i < mesh->numEdges(); ++i)

{

if (MeshCheck::IsEdgeBoundary(mesh, i))

{

int v0, v1;

MeshTool::EdgeVertexIndexs(mesh, i, v0, v1);

Point3 pv0 = MeshTool::Position(mesh, v0);

Point3 pv1 = MeshTool::Position(mesh, v1);

if (pv0.Z() > 0.9 * h && pv1.Z() > 0.9 * h)

{

edgeid.push_back(i);

}

}

}

MeshExtrude::ExtrudeEdge(mesh, edgeid, edge_id_new);

MeshTransform trsfE;

Transformation3 trsfScale;

trsfScale.SetScale(Point3(1.5, 1.5, h), 3);

trsfE.SetTransformation(trsfScale);

trsfE.TransformMeshEdges(mesh, edge_id_new);

AMCAX::PointT::Zconstexpr const Scalar & Z() const noexcept获取点的 z 坐标,仅在 DIM >= 3 时可用定义 PointT.hpp:136

AMCAX::TransformationT::SetScalevoid SetScale(const PointT< OtherScalar, DIM > &point, const OtherScalar2 &s)设置变换为按一个点进行缩放定义 TransformationT.hpp:224

点击这里example4可获得以上示例完整源码,大家根据学习需要自行下载。

进行加厚

将形状加厚,从而构建出实体。

MeshOffset::ThickenMesh(mesh, 0.2);

点击这里example5可获得以上示例完整源码,大家根据学习需要自行下载。

应用面分割

对连接上下部分,变化比较剧烈的这部分的面进行分割使得结果更平滑,分割通过选择一个条边将与该边拓扑平行的所有面一分为二。

for (int i = 0; i < mesh->numEdges(); ++i)

{

int v0, v1;

MeshTool::EdgeVertexIndexs(mesh, i, v0, v1);

Point3 pv0 = MeshTool::Position(mesh, v0);

Point3 pv1 = MeshTool::Position(mesh, v1);

if (std::fabs(pv1.Z() - pv0.Z()) > 0.8 * h)

{

MeshSplit::SplitLoop(mesh, i);

i = 0;

}

}

点击这里example6可获得以上示例完整源码,大家根据学习需要自行下载。

设置锐边特征

可以选取一些边设置锐利程度,该值在 0-1 之间,值越大,最终形状在边这里就越锐利,该值为 1 时,最终结果中会呈现为锐利的特征边

std::vector edgeidCrese;

for (int i = 0; i < mesh->numEdges(); ++i)

{

int v0, v1;

MeshTool::EdgeVertexIndexs(mesh, i, v0, v1);

Point3 pv0 = MeshTool::Position(mesh, v0);

Point3 pv1 = MeshTool::Position(mesh, v1);

if (pv0.Z() < 0.1 && pv1.Z() < 0.1 && pv0.Distance(Point3(1.5, 1.5, 0.)) > 1 && pv1.Distance(Point3(1.5, 1.5, 0.)) > 1)

{

edgeidCrese.push_back(i);

}

}

std::vector creaseLevel(edgeidCrese.size(), 1.0);

MeshCreaseTool::AddCreaseEdge(mesh, edgeidCrese, creaseLevel);

AMCAX::PointT::Distanceauto Distance(const PointT< OtherScalar, DIM > &other) const noexcept计算该点与其他点的欧几里得距离定义 PointT.hpp:180

执行细分

模块提供了两种细分方式,其中 Catmull-Clark 细分支持任意网格,Loop 细分仅支持三角形网格,细分后的形状仍然可以再次编辑。

MeshSubdivideHE::CatmullClark(mesh, 3);

保存网格

本模块提供将网格保存为 OBJ 文件和 OFF 文件,可以依据需要在任意细分层级保存网格并在之后使用,此外,还支持将网格导出为 TMS 格式。

std::string fileNameOBJ = "sampleResult.obj";

PolyMeshIO::WriteMesh(fileNameOBJ, mesh);

点击这里example7可获得多边形网格细分建模示例完整源码,大家根据学习需要自行下载。

附录

下面列出了此示例的完整代码:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

void MakeTornado()

{

double h = 2;

Frame3 frame;

MeshMakeRectangle mkRect(frame, 3, 3, 3, 3);

PolyMesh* mesh = mkRect.BuildMesh();

std::vector face_id = {4};

std::vector face_id_new;

MeshExtrude::ExtrudeFace(mesh, face_id, face_id_new);

Vector3 vh(frame.Direction().Coord() * h);

MeshTransform trsfF;

Transformation3 trsfMove;

trsfMove.SetTranslation(vh);

trsfF.SetTransformation(trsfMove);

trsfF.TransformMeshFaces(mesh, face_id_new);

Transformation3 trsfRot;

trsfRot.SetRotation(Axis3(Point3(1.5, 1.5, 0.), Direction3(0., 0., 1.)), AMCAX::Constants::pi / 4);

trsfF.SetTransformation(trsfRot);

trsfF.TransformMeshFaces(mesh, face_id_new);

MeshReduce::DeleteFaces(mesh, face_id_new);

std::vector edgeid;

std::vector edge_id_new;

for (int i = 0; i < mesh->numEdges(); ++i)

{

if (MeshCheck::IsEdgeBoundary(mesh, i))

{

int v0, v1;

MeshTool::EdgeVertexIndexs(mesh, i, v0, v1);

Point3 pv0 = MeshTool::Position(mesh, v0);

Point3 pv1 = MeshTool::Position(mesh, v1);

if (pv0.Z() > 0.9 * h && pv1.Z() > 0.9 * h)

{

edgeid.push_back(i);

}

}

}

MeshExtrude::ExtrudeEdge(mesh, edgeid, edge_id_new);

MeshTransform trsfE;

Transformation3 trsfScale;

trsfScale.SetScale(Point3(1.5, 1.5, h), 3);

trsfE.SetTransformation(trsfScale);

trsfE.TransformMeshEdges(mesh, edge_id_new);

MeshOffset::ThickenMesh(mesh, 0.2);

for (int i = 0; i < mesh->numEdges(); ++i)

{

int v0, v1;

MeshTool::EdgeVertexIndexs(mesh, i, v0, v1);

Point3 pv0 = MeshTool::Position(mesh, v0);

Point3 pv1 = MeshTool::Position(mesh, v1);

if (std::fabs(pv1.Z() - pv0.Z()) > 0.8 * h)

{

MeshSplit::SplitLoop(mesh, i);

i = 0;

}

}

std::vector edgeidCrese;

for (int i = 0; i < mesh->numEdges(); ++i)

{

int v0, v1;

MeshTool::EdgeVertexIndexs(mesh, i, v0, v1);

Point3 pv0 = MeshTool::Position(mesh, v0);

Point3 pv1 = MeshTool::Position(mesh, v1);

if (pv0.Z() < 0.1 && pv1.Z() < 0.1 && pv0.Distance(Point3(1.5, 1.5, 0.)) > 1 && pv1.Distance(Point3(1.5, 1.5, 0.)) > 1)

{

edgeidCrese.push_back(i);

}

}

std::vector creaseLevel(edgeidCrese.size(), 1.0);

MeshCreaseTool::AddCreaseEdge(mesh, edgeidCrese, creaseLevel);

MeshSubdivideHE::CatmullClark(mesh, 3);

std::string fileNameOBJ = "sampleResult.obj";

PolyMeshIO::WriteMesh(fileNameOBJ, mesh);

delete mesh;

}

AxisT.hpp轴类

Constants.hpp常用数学常数

FrameT.hpp标架类

MeshCheck.hpp用于检查多边形网格的工具类

MeshCreaseTool.hpp多边形网格接口中用于创建折痕边的类

MeshExtrude.hpp多边形网格接口中用于拉伸的类

MeshMakeRectangle.hpp多边形网格接口中用于创建矩形的类

MeshOffset.hpp多边形网格接口中用于加厚网格的类

MeshReduce.hpp多边形网格接口中用于删除网格的类

MeshSplit.hpp多边形网格接口中用于分割网格的类

MeshSubdivideHE.hpp多边形网格接口中用于网格细分的类,该类使用半边数据结构对网格进行修改

MeshTool.hpp多边形网格的工具类

MeshTransform.hpp对多边形网格进行变换的类

PolyMeshIO.hpp多边形网格接口中用于加载和写入的类

VectorT.hpp向量的类