You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Geometry: Expose Remesh function in Geometry Script, and add basic convergence check to queue remesher
- add FQueueRemesher::MinActiveEdgeFraction. If fraction of modified/total edges in remesh pass is below this parameter (default 1%), consider result converged. - Expose as option in FRemeshMeshOp, and add sane defaults for all parameters of Op - move CalculateTargetEdgeLength function from RemeshMeshTool to static function in FRemeshMeshOp, update Tool - add Geometry Script function ApplyUniformRemesh #rb jimmy.andrews #preflight 62a0f1923f1e313c6ad23c21 [CL 20562425 by Ryan Schmidt in ue5-main branch]
This commit is contained in:
+148
@@ -0,0 +1,148 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "GeometryScript/MeshRemeshFunctions.h"
|
||||
|
||||
#include "DynamicMesh/DynamicMesh3.h"
|
||||
#include "DynamicMesh/DynamicMeshAABBTree3.h"
|
||||
#include "DynamicMesh/DynamicMeshAttributeSet.h"
|
||||
#include "DynamicMesh/MeshAttributeUtil.h"
|
||||
#include "MeshConstraintsUtil.h"
|
||||
#include "ProjectionTargets.h"
|
||||
#include "DynamicMesh/MeshNormals.h"
|
||||
#include "CleaningOps/RemeshMeshOp.h"
|
||||
|
||||
#include "UDynamicMesh.h"
|
||||
|
||||
using namespace UE::Geometry;
|
||||
|
||||
#define LOCTEXT_NAMESPACE "UGeometryScriptLibrary_MeshRemeshFunctions"
|
||||
|
||||
|
||||
|
||||
static EEdgeRefineFlags MakeEdgeRefineFlagsFromConstraintType(EGeometryScriptRemeshEdgeConstraintType ConstraintType)
|
||||
{
|
||||
switch (ConstraintType)
|
||||
{
|
||||
case EGeometryScriptRemeshEdgeConstraintType::Fixed: return EEdgeRefineFlags::FullyConstrained;
|
||||
case EGeometryScriptRemeshEdgeConstraintType::Refine: return EEdgeRefineFlags::SplitsOnly;
|
||||
case EGeometryScriptRemeshEdgeConstraintType::Free: return EEdgeRefineFlags::NoFlip;
|
||||
case EGeometryScriptRemeshEdgeConstraintType::Ignore: return EEdgeRefineFlags::NoConstraint;
|
||||
}
|
||||
return EEdgeRefineFlags::NoFlip;
|
||||
}
|
||||
|
||||
|
||||
UDynamicMesh* UGeometryScriptLibrary_RemeshingFunctions::ApplyUniformRemesh(
|
||||
UDynamicMesh* TargetMesh,
|
||||
FGeometryScriptRemeshOptions RemeshOptions,
|
||||
FGeometryScriptUniformRemeshOptions UniformOptions,
|
||||
UGeometryScriptDebug* Debug)
|
||||
{
|
||||
if (TargetMesh == nullptr)
|
||||
{
|
||||
UE::Geometry::AppendError(Debug, EGeometryScriptErrorType::InvalidInputs, LOCTEXT("ApplyUniformRemesh_InvalidInput", "ApplyUniformRemesh: TargetMesh is Null"));
|
||||
return TargetMesh;
|
||||
}
|
||||
|
||||
TargetMesh->EditMesh([&](FDynamicMesh3& EditMesh)
|
||||
{
|
||||
TSharedPtr<FDynamicMesh3> SourceMesh = MakeShared<FDynamicMesh3>(MoveTemp(EditMesh));
|
||||
|
||||
TSharedPtr<FDynamicMeshAABBTree3> SourceSpatial;
|
||||
if (RemeshOptions.bReprojectToInputMesh)
|
||||
{
|
||||
SourceSpatial = MakeShared<FDynamicMeshAABBTree3>(SourceMesh.Get(), true);
|
||||
}
|
||||
|
||||
FRemeshMeshOp RemeshOp;
|
||||
RemeshOp.OriginalMesh = SourceMesh;
|
||||
RemeshOp.OriginalMeshSpatial = SourceSpatial;
|
||||
|
||||
RemeshOp.bDiscardAttributes = RemeshOptions.bDiscardAttributes;
|
||||
|
||||
RemeshOp.RemeshType = (RemeshOptions.bUseFullRemeshPasses) ? ERemeshType::FullPass : ERemeshType::Standard;
|
||||
|
||||
RemeshOp.RemeshIterations = RemeshOptions.RemeshIterations;
|
||||
RemeshOp.MaxRemeshIterations = RemeshOptions.RemeshIterations;
|
||||
RemeshOp.ExtraProjectionIterations = 0; // unused for regular remeshing
|
||||
RemeshOp.TriangleCountHint = 0; // unused for regular remeshing
|
||||
|
||||
// smoothing options
|
||||
RemeshOp.SmoothingStrength = FMath::Clamp(RemeshOptions.SmoothingRate, 0.0f, 1.0f);
|
||||
switch (RemeshOptions.SmoothingType)
|
||||
{
|
||||
case EGeometryScriptRemeshSmoothingType::Mixed:
|
||||
RemeshOp.SmoothingType = ERemeshSmoothingType::MeanValue;
|
||||
break;
|
||||
case EGeometryScriptRemeshSmoothingType::UVPreserving:
|
||||
RemeshOp.SmoothingType = ERemeshSmoothingType::Cotangent;
|
||||
break;
|
||||
case EGeometryScriptRemeshSmoothingType::Uniform:
|
||||
default:
|
||||
RemeshOp.SmoothingType = ERemeshSmoothingType::Uniform;
|
||||
break;
|
||||
}
|
||||
|
||||
if (UniformOptions.TargetType == EGeometryScriptUniformRemeshTargetType::TriangleCount)
|
||||
{
|
||||
RemeshOp.TargetEdgeLength = FRemeshMeshOp::CalculateTargetEdgeLength(SourceMesh.Get(), UniformOptions.TargetTriangleCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemeshOp.TargetEdgeLength = UniformOptions.TargetEdgeLength;
|
||||
}
|
||||
|
||||
// currently not exposing this option. It seems to control multiple things that should be independent...
|
||||
RemeshOp.bPreserveSharpEdges = (RemeshOp.bDiscardAttributes == false);
|
||||
RemeshOp.bFlips = RemeshOptions.bAllowFlips;
|
||||
RemeshOp.bSplits = RemeshOptions.bAllowSplits;
|
||||
RemeshOp.bCollapses = RemeshOptions.bAllowCollapses;
|
||||
RemeshOp.bPreventNormalFlips = RemeshOptions.bPreventNormalFlips;
|
||||
RemeshOp.bPreventTinyTriangles = RemeshOptions.bPreventTinyTriangles;
|
||||
|
||||
RemeshOp.MeshBoundaryConstraint = MakeEdgeRefineFlagsFromConstraintType(RemeshOptions.MeshBoundaryConstraint);
|
||||
RemeshOp.GroupBoundaryConstraint = MakeEdgeRefineFlagsFromConstraintType(RemeshOptions.GroupBoundaryConstraint);
|
||||
RemeshOp.MaterialBoundaryConstraint = MakeEdgeRefineFlagsFromConstraintType(RemeshOptions.MaterialBoundaryConstraint);
|
||||
|
||||
RemeshOp.bReproject = RemeshOptions.bReprojectToInputMesh;
|
||||
RemeshOp.ProjectionTarget = nullptr;
|
||||
RemeshOp.ProjectionTargetSpatial = nullptr;
|
||||
|
||||
|
||||
RemeshOp.bReprojectConstraints = false;
|
||||
RemeshOp.BoundaryCornerAngleThreshold = 45.0;
|
||||
|
||||
|
||||
RemeshOp.TargetMeshLocalToWorld = FTransformSRT3d::Identity();
|
||||
RemeshOp.ToolMeshLocalToWorld = FTransformSRT3d::Identity();
|
||||
RemeshOp.bUseWorldSpace = false;
|
||||
RemeshOp.bParallel = true;
|
||||
|
||||
RemeshOp.CalculateResult(nullptr);
|
||||
if (RemeshOp.GetResultInfo().Result == EGeometryResultType::Success)
|
||||
{
|
||||
TUniquePtr<FDynamicMesh3> ResultMesh = RemeshOp.ExtractResult();
|
||||
EditMesh = MoveTemp(*ResultMesh);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditMesh = MoveTemp(*SourceMesh);
|
||||
UE::Geometry::AppendError(Debug, EGeometryScriptErrorType::InvalidInputs, LOCTEXT("ApplyUniformRemesh_ComputeError", "ApplyUniformRemesh: Error computing result, returning input mesh"));
|
||||
}
|
||||
|
||||
// if we discarded attributes, re-enable the standard attributes
|
||||
if (RemeshOptions.bDiscardAttributes)
|
||||
{
|
||||
EditMesh.EnableTriangleGroups();
|
||||
EditMesh.EnableAttributes();
|
||||
EditMesh.Attributes()->EnableMaterialID();
|
||||
}
|
||||
|
||||
}, EDynamicMeshChangeType::GeneralEdit, EDynamicMeshAttributeChangeFlags::Unknown, false);
|
||||
|
||||
return TargetMesh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "GeometryScript/GeometryScriptTypes.h"
|
||||
#include "MeshRemeshFunctions.generated.h"
|
||||
|
||||
class UDynamicMesh;
|
||||
|
||||
|
||||
/** Goal types for Uniform Remeshing */
|
||||
UENUM(BlueprintType)
|
||||
enum class EGeometryScriptUniformRemeshTargetType : uint8
|
||||
{
|
||||
/** Approximate Desired Triangle Count. This is used to compute a Target Edge Length, and is not an explicit target */
|
||||
TriangleCount = 0,
|
||||
/** Attempt to Remesh such that all edges have approximately this length */
|
||||
TargetEdgeLength = 1
|
||||
};
|
||||
|
||||
/** Types of edge constraints, specified for different mesh attributes */
|
||||
UENUM(BlueprintType)
|
||||
enum class EGeometryScriptRemeshEdgeConstraintType : uint8
|
||||
{
|
||||
/** Constrained edges cannot be flipped, split or collapsed, and vertices will not move */
|
||||
Fixed = 0,
|
||||
/** Constrained edges can be split, but not flipped or collapsed. Vertices will not move. */
|
||||
Refine = 1,
|
||||
/** Constrained edges cannot be flipped, but otherwise are free to move */
|
||||
Free = 2,
|
||||
/** Edges are not constrained, ie the Attribute used to derive the Constraints will not be considered */
|
||||
Ignore = 3
|
||||
};
|
||||
|
||||
/** The Vertex Smoothing strategy used in a Remeshing operation */
|
||||
UENUM(BlueprintType)
|
||||
enum class EGeometryScriptRemeshSmoothingType : uint8
|
||||
{
|
||||
/** Vertices move towards their 3D one-ring centroids, UVs are ignored. This produces the most regular mesh possible. */
|
||||
Uniform = 0,
|
||||
/** Vertices move towards the projection of their one-ring centroids onto their normal vectors, preserving UVs */
|
||||
UVPreserving = 1,
|
||||
/** Similar to UV Preserving, but allows some tangential drift (causing UV distortion) when vertices would otherwise be "stuck" */
|
||||
Mixed = 2
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Standard Remeshing Options
|
||||
*/
|
||||
USTRUCT(Blueprintable)
|
||||
struct GEOMETRYSCRIPTINGCORE_API FGeometryScriptRemeshOptions
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
/** When enabled, all mesh attributes are discarded, so UV and Normal Seams can be freely */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
bool bDiscardAttributes = false;
|
||||
|
||||
/** When enabled, mesh vertices are projected back onto the input mesh surface during Remeshing, preserving the shape */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
bool bReprojectToInputMesh = true;
|
||||
|
||||
/** Type of 3D Mesh Smoothing to apply during Remeshing. Disable by setting SmoothingRate = 0 */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
EGeometryScriptRemeshSmoothingType SmoothingType = EGeometryScriptRemeshSmoothingType::Mixed;
|
||||
|
||||
/** Smoothing Rate/Speed. Faster Smoothing results in a more regular mesh, but also more potential for undesirable 3D shape change and UV distortion */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options, meta = (UIMin = 0, UIMax = 1, ClampMin = 0, ClampMax = 1))
|
||||
float SmoothingRate = 0.25f;
|
||||
|
||||
/** Constraints on the open mesh boundary/border edges */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
EGeometryScriptRemeshEdgeConstraintType MeshBoundaryConstraint = EGeometryScriptRemeshEdgeConstraintType::Free;
|
||||
|
||||
/** Constraints on the mesh boundary/border edges between different PolyGroups of the Mesh */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
EGeometryScriptRemeshEdgeConstraintType GroupBoundaryConstraint = EGeometryScriptRemeshEdgeConstraintType::Free;
|
||||
|
||||
/** Constraints on the mesh boundary/border edges between different Material Results of the Mesh */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
EGeometryScriptRemeshEdgeConstraintType MaterialBoundaryConstraint = EGeometryScriptRemeshEdgeConstraintType::Free;
|
||||
|
||||
/** Enable/Disable Edge Flips during Remeshing. Disabling flips will significantly reduce the output mesh quality */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
bool bAllowFlips = true;
|
||||
|
||||
/** Enable/Disable Edge Splits during Remeshing. Disabling Splits will prevent the mesh density from increasing. */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
bool bAllowSplits = true;
|
||||
|
||||
/** Enable/Disable Edge Collapses during Remeshing. Disabling Collapses will prevent the mesh density from decreasing. */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
bool bAllowCollapses = true;
|
||||
|
||||
/** When Enabled, Flips and Collapses will be skipped if they would flip any triangle face normals */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
bool bPreventNormalFlips = true;
|
||||
|
||||
/** When Enabled, Flips and Collapses will be skipped if they would create tiny degenerate triangles */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
bool bPreventTinyTriangles = true;
|
||||
|
||||
/** By default, remeshing is accelerated by tracking a queue of edges that need to be processed. This is signficantly faster but can produce a lower quality output. Enable this option to use a more expensive strategy that guarantees maximum quality. */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
bool bUseFullRemeshPasses = false;
|
||||
|
||||
/** Maximum Number of iterations of the Remeshing Strategy to apply to the Mesh. More iterations are generally more expensive (much moreso with bUseFullRemeshPasses = true) */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options, meta = (UIMin = 0, ClampMin = 0))
|
||||
int32 RemeshIterations = 20;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Uniform Remeshing Options
|
||||
*/
|
||||
USTRUCT(Blueprintable)
|
||||
struct GEOMETRYSCRIPTINGCORE_API FGeometryScriptUniformRemeshOptions
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
/** Method used to define target/goal of Uniform Remeshing */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options)
|
||||
EGeometryScriptUniformRemeshTargetType TargetType = EGeometryScriptUniformRemeshTargetType::TriangleCount;
|
||||
|
||||
/** Approximate Target Triangle Count, combined with mesh surface area to derive a TargetEdgeLength */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options, meta = (UIMin = 0, ClampMin = 0, EditCondition = "TargetType == EGeometryScriptUniformRemeshTargetType::TriangleCount"))
|
||||
int32 TargetTriangleCount = 5000;
|
||||
|
||||
/** Explicit Target Edge Length that is desired in the output uniform mesh */
|
||||
UPROPERTY(BlueprintReadWrite, Category = Options, meta = (UIMin = 0, ClampMin = 0, EditCondition = "TargetType == EGeometryScriptUniformRemeshTargetType::TargetEdgeLength"))
|
||||
float TargetEdgeLength = 1.0f;
|
||||
};
|
||||
|
||||
|
||||
|
||||
UCLASS(meta = (ScriptName = "GeometryScript_Remeshing"))
|
||||
class GEOMETRYSCRIPTINGCORE_API UGeometryScriptLibrary_RemeshingFunctions : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
|
||||
/**
|
||||
* Apply Uniform Remeshing to the TargetMesh.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "GeometryScript|Simplification", meta=(ScriptMethod))
|
||||
static UPARAM(DisplayName = "Target Mesh") UDynamicMesh*
|
||||
ApplyUniformRemesh(
|
||||
UDynamicMesh* TargetMesh,
|
||||
FGeometryScriptRemeshOptions RemeshOptions,
|
||||
FGeometryScriptUniformRemeshOptions UniformOptions,
|
||||
UGeometryScriptDebug* Debug = nullptr);
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user