189 lines
5.2 KiB
C++
189 lines
5.2 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Operations/ExtrudeMesh.h"
|
|
#include "DynamicMesh/MeshNormals.h"
|
|
#include "DynamicMeshEditor.h"
|
|
#include "Selections/MeshConnectedComponents.h"
|
|
#include "DynamicMesh/MeshIndexUtil.h"
|
|
|
|
using namespace UE::Geometry;
|
|
|
|
FExtrudeMesh::FExtrudeMesh(FDynamicMesh3* mesh) : Mesh(mesh)
|
|
{
|
|
ExtrudedPositionFunc = [this](const FVector3d& Position, const FVector3f& Normal, int VertexID)
|
|
{
|
|
return Position + this->DefaultExtrudeDistance * (FVector3d)Normal;
|
|
};
|
|
}
|
|
|
|
|
|
bool FExtrudeMesh::Apply()
|
|
{
|
|
FMeshNormals Normals;
|
|
bool bHaveVertexNormals = Mesh->HasVertexNormals();
|
|
if (!bHaveVertexNormals)
|
|
{
|
|
Normals = FMeshNormals(Mesh);
|
|
Normals.ComputeVertexNormals();
|
|
}
|
|
|
|
FMeshConnectedComponents MeshRegions(Mesh);
|
|
MeshRegions.FindConnectedTriangles();
|
|
|
|
bool bAllOK = true;
|
|
Extrusions.SetNum(MeshRegions.Num());
|
|
for (int k = 0; k < MeshRegions.Num(); ++k)
|
|
{
|
|
FExtrusionInfo& Extrusion = Extrusions[k];
|
|
Extrusion.InitialTriangles = MoveTemp(MeshRegions.Components[k].Indices);
|
|
if (ApplyExtrude(Extrusion, (bHaveVertexNormals) ? nullptr : &Normals) == false)
|
|
{
|
|
bAllOK = false;
|
|
}
|
|
}
|
|
|
|
return bAllOK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool FExtrudeMesh::ApplyExtrude(FExtrusionInfo& Region, FMeshNormals* UseNormals)
|
|
{
|
|
Region.InitialLoops.SetMesh(Mesh, Region.InitialTriangles);
|
|
bool bOK = Region.InitialLoops.Compute();
|
|
if (bOK == false)
|
|
{
|
|
return false;
|
|
}
|
|
int NumInitialLoops = Region.InitialLoops.GetLoopCount();
|
|
if (bSkipClosedComponents && NumInitialLoops == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
UE::Geometry::TriangleToVertexIDs(Mesh, Region.InitialTriangles, Region.InitialVertices);
|
|
|
|
// duplicate triangles of mesh
|
|
|
|
FDynamicMeshEditor Editor(Mesh);
|
|
|
|
FMeshIndexMappings IndexMap;
|
|
FDynamicMeshEditResult DuplicateResult;
|
|
Editor.DuplicateTriangles(Region.InitialTriangles, IndexMap, DuplicateResult);
|
|
Region.OffsetTriangles = DuplicateResult.NewTriangles;
|
|
Region.OffsetTriGroups = DuplicateResult.NewGroups;
|
|
Region.InitialToOffsetMapV = IndexMap.GetVertexMap().GetForwardMap();
|
|
|
|
// set vertices to new positions
|
|
for (int vid : Region.InitialVertices)
|
|
{
|
|
if ( ! Region.InitialToOffsetMapV.Contains(vid) )
|
|
{
|
|
continue;
|
|
}
|
|
int newvid = Region.InitialToOffsetMapV[vid];
|
|
if ( ! Mesh->IsVertex(newvid) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
FVector3d v = Mesh->GetVertex(vid);
|
|
FVector3f n = (UseNormals != nullptr) ? (FVector3f)(*UseNormals)[vid] : Mesh->GetVertexNormal(vid);
|
|
FVector3d newv = ExtrudedPositionFunc(v, n, vid);
|
|
|
|
Mesh->SetVertex(newvid, newv);
|
|
}
|
|
|
|
// we need to reverse one side
|
|
if (IsPositiveOffset)
|
|
{
|
|
Editor.ReverseTriangleOrientations(Region.InitialTriangles, true);
|
|
}
|
|
else
|
|
{
|
|
Editor.ReverseTriangleOrientations(Region.OffsetTriangles, true);
|
|
}
|
|
|
|
// stitch each loop
|
|
Region.NewLoops.SetNum(NumInitialLoops);
|
|
Region.StitchTriangles.SetNum(NumInitialLoops);
|
|
Region.StitchPolygonIDs.SetNum(NumInitialLoops);
|
|
int LoopIndex = 0;
|
|
for (FEdgeLoop& BaseLoop : Region.InitialLoops.Loops)
|
|
{
|
|
int LoopCount = BaseLoop.GetVertexCount();
|
|
|
|
TArray<int> OffsetLoop;
|
|
OffsetLoop.SetNum(LoopCount);
|
|
for (int k = 0; k < LoopCount; ++k)
|
|
{
|
|
OffsetLoop[k] = Region.InitialToOffsetMapV[BaseLoop.Vertices[k]];
|
|
}
|
|
|
|
FDynamicMeshEditResult StitchResult;
|
|
bool bStitchSuccess;
|
|
if (IsPositiveOffset)
|
|
{
|
|
bStitchSuccess = Editor.StitchVertexLoopsMinimal(OffsetLoop, BaseLoop.Vertices, StitchResult);
|
|
}
|
|
else
|
|
{
|
|
bStitchSuccess = Editor.StitchVertexLoopsMinimal(BaseLoop.Vertices, OffsetLoop, StitchResult);
|
|
}
|
|
|
|
if (bStitchSuccess)
|
|
{
|
|
StitchResult.GetAllTriangles(Region.StitchTriangles[LoopIndex]);
|
|
Region.StitchPolygonIDs[LoopIndex] = StitchResult.NewGroups;
|
|
|
|
// for each polygon we created in stitch, set UVs and normals
|
|
if (Mesh->HasAttributes())
|
|
{
|
|
float AccumUVTranslation = 0;
|
|
|
|
FFrame3d FirstProjectFrame;
|
|
FVector3d FrameUp;
|
|
|
|
int NumNewQuads = StitchResult.NewQuads.Num();
|
|
for (int k = 0; k < NumNewQuads; k++)
|
|
{
|
|
FVector3f Normal = Editor.ComputeAndSetQuadNormal(StitchResult.NewQuads[k], true);
|
|
|
|
// align axis 0 of projection frame to first edge, then for further edges,
|
|
// rotate around 'up' axis to keep normal aligned and frame horizontal
|
|
FFrame3d ProjectFrame;
|
|
if (k == 0)
|
|
{
|
|
FVector3d FirstEdge = Mesh->GetVertex(BaseLoop.Vertices[1]) - Mesh->GetVertex(BaseLoop.Vertices[0]);
|
|
Normalize(FirstEdge);
|
|
FirstProjectFrame = FFrame3d(FVector3d::Zero(), (FVector3d)Normal);
|
|
FirstProjectFrame.ConstrainedAlignAxis(0, FirstEdge, (FVector3d)Normal);
|
|
FrameUp = FirstProjectFrame.GetAxis(1);
|
|
ProjectFrame = FirstProjectFrame;
|
|
}
|
|
else
|
|
{
|
|
ProjectFrame = FirstProjectFrame;
|
|
ProjectFrame.ConstrainedAlignAxis(2, (FVector3d)Normal, FrameUp);
|
|
}
|
|
|
|
if (k > 0)
|
|
{
|
|
AccumUVTranslation +=(float) Distance(Mesh->GetVertex(BaseLoop.Vertices[k]), Mesh->GetVertex(BaseLoop.Vertices[k - 1]));
|
|
}
|
|
|
|
// translate horizontally such that vertical spans are adjacent in UV space (so textures tile/wrap properly)
|
|
float TranslateU = UVScaleFactor * AccumUVTranslation;
|
|
Editor.SetQuadUVsFromProjection(StitchResult.NewQuads[k], ProjectFrame, UVScaleFactor, FVector2f(TranslateU, 0));
|
|
}
|
|
}
|
|
|
|
Region.NewLoops[LoopIndex].InitializeFromVertices(Mesh, OffsetLoop);
|
|
}
|
|
LoopIndex++;
|
|
}
|
|
|
|
return true;
|
|
} |