AMCAX Kernel 1.0.0.0
A PolyMesh Modeling Sample

Overview

This tutorial provides the basic usage of using polygonal meshes for subdivision surface modeling. Subdivision modeling generally starts with creating a primitive and generates a dense mesh approximation of a surface through stretching, transformation, subdivision, etc.

Construction of primitive shapes

The module supports the construction of various primitive geometries, for example, makeing a cube with a side length of 3 and dividing 2 segments in each direction. The code is as follows.

AMCAX::SubD::MeshMakeCube makeCube(frame, 3, 3, 3, 2, 2, 2);
AMCAX::SubD::PolyMesh* mesh = makeCube.BuildMesh();
Class of PolyMesh API for make a cube.
Definition: MeshMakeCube.hpp:15
Class of TMSpline structure The Low Level API functions are not exported for this version.
Definition: PolyMesh.hpp:23

You can also use the MeshMakeCone to construct a frustum with a base radius of 4, an upper radius of 2, and a height of 4. By default, it is divided into 8 segments along the rotation direction and 4 segments along the height direction

AMCAX::SubD::MeshMakeCone makeCone(frame, 4, 2, 4);
AMCAX::SubD::PolyMesh* mesh = makeCone.BuildMesh();
Class of PolyMesh API for make a cone.
Definition: MeshMakeCone.hpp:15

Build a vortex model

Model Preview

This tutorial uses a simple vortex model to show some of the main modeling functions in the polymesh subdivision modeling module The model is shown in the figure below:

The vortex model

Using namespace

using namespace AMCAX;
using namespace SubD;
Namespace of all interface in the AMCAX kernel.
Definition: misc.docu:8

Build Bottom mesh

Start by constructing a plane.

Frame3 frame;
MeshMakeRectangle mkRect(frame, 3, 3, 3, 3);
PolyMesh* mesh = mkRect.BuildMesh();
FrameT< double, 3 > Frame3
3D frame
Definition: FrameT.hpp:841

Click here example1 to get the complete source code for the above example, which you can download according to your learning needs.

Extrude faces and apply transformations

Extrude the middle face to form a peak. Only change the topology, the new result is coincide the old result, and use MeshTransform to move the new result

std::vector<int> face_id = {4};
std::vector<int> face_id_new;
MeshExtrude::ExtrudeFace(mesh, face_id, face_id_new);

Apply translation and rotation transformations to the newly generated faces

Vector3 vh(frame.Direction().Coord() * h);
MeshTransform trsfF;
Transformation3 trsfMove;
trsfMove.SetTranslation(vh);
trsfF.SetTransformation(trsfMove);
trsfF.TransformMeshFaces(mesh, face_id_new);
trsfRot.SetRotation(Axis3(Point3(1.5, 1.5, 0.), Direction3(0., 0., 1.)), M_PI / 4);
trsfF.SetTransformation(trsfRot);
trsfF.TransformMeshFaces(mesh, face_id_new);
void SetTranslation(const VectorT< OtherScalar, DIM > &vec)
Set the transformation as the translation.
Definition: TransformationT.hpp:311
void SetRotation(const PointT< OtherScalar, DIM > &point, const OtherScalar2 &angle)
Set the transformation as rotation around a point with an angle in 2D.
Definition: TransformationT.hpp:138
VectorT< double, 3 > Vector3
3D vector
Definition: VectorT.hpp:698
AxisT< double, 3 > Axis3
3D axis
Definition: AxisT.hpp:423
PointT< double, 3 > Point3
3D point
Definition: PointT.hpp:459
DirectionT< double, 3 > Direction3
3D direction
Definition: DirectionT.hpp:569
TransformationT< double, 3 > Transformation3
3D transformation
Definition: TransformationT.hpp:1054

Click here example2 to get the complete source code for the above example, which you can download according to your learning needs.

Delete top face

Can freely delete any number of faces and leave holes

MeshReduce::DeleteFaces(mesh, face_id_new);

Click here example3 to get the complete source code for the above example, which you can download according to your learning needs.

Extrude edges and apply transformations

Extrude the edges left by the deleted face towards all sides to construct a new top faces.

std::vector<int> edgeid;
std::vector<int> 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);
void SetScale(const PointT< OtherScalar, DIM > &point, const OtherScalar2 &s)
Set the transformation as the scaling from a point.
Definition: TransformationT.hpp:224

Click here example4 to get the complete source code for the above example, which you can download according to your learning needs.

Perform thickening

Thicken the shape to construct a solid.

MeshOffset::ThickenMesh(mesh, 0.2);

Click here example5 to get the complete source code for the above example, which you can download according to your learning needs.

Apply face split

Split the faces that connect the upper and lower parts with significant changes makes the results smoother. By split loop an edge, all faces that are topologically parallel to that edge are split in half.

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;
}
}

Click here example6 to get the complete source code for the above example, which you can download according to your learning needs.

Set crease edge features

You can select some edges to set the sharpness level, which is between 0 and 1. The larger the value, the sharper the final shape will be at the edge. When the value is greater than 1, the final result will show sharp feature edges

std::vector<int> 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<double> creaseLevel(edgeidCrese.size(), 1.0);
MeshCreaseTool::AddCreaseEdge(mesh, edgeidCrese, creaseLevel);

Do Subdivision

This module provides two subdivision methods, the Catmull-Clark subdivision supports arbitrary meshes, the Loop subdivision only supports triangular meshes. The subdivided shape can still be edited again.

MeshSubdivideHE::CatmullClark(mesh, 3);

Save Mesh

This module provides the option to save result as OBJ files and OFF files

std::string fileNameOBJ = "sampleResult.obj";
PolyMeshIO::WriteMesh(fileNameOBJ, mesh);

Click here example7 to obtain the complete source code of the polygonal mesh subdivision modeling example, you can download it according to your learning needs.

Appendix

The complete code of this sample is listed here:

void MakeTornado()
{
double h = 2;
Frame3 frame;
MeshMakeRectangle mkRect(frame, 3, 3, 3, 3);
PolyMesh* mesh = mkRect.BuildMesh();
std::vector<int> face_id = {4};
std::vector<int> 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.)), M_PI / 4);
trsfF.SetTransformation(trsfRot);
trsfF.TransformMeshFaces(mesh, face_id_new);
MeshReduce::DeleteFaces(mesh, face_id_new);
std::vector<int> edgeid;
std::vector<int> 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<int> 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<double> 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;
}