Files
2025-05-18 13:04:45 +08:00

377 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MeshWeights.h"
#include "VectorUtil.h"
using namespace UE::Geometry;
FVector3d FMeshWeights::UniformCentroid(const FDynamicMesh3 & mesh, int32 VertexIndex)
{
FVector3d Centroid;
mesh.GetVtxOneRingCentroid(VertexIndex, Centroid);
return Centroid;
}
FVector3d FMeshWeights::UniformCentroid(const FDynamicMesh3& mesh, int32 VertexIndex, TFunctionRef<FVector3d(int32)> VertexPositionFunc)
{
FVector3d Centroid = FVector3d::Zero();
int32 Count = 0;
for (int32 nbrvid : mesh.VtxVerticesItr(VertexIndex))
{
Centroid += VertexPositionFunc(nbrvid);
Count++;
}
return (Count == 0) ? VertexPositionFunc(VertexIndex) : (Centroid / (double)Count);
}
FVector3d FMeshWeights::FilteredUniformCentroid(const FDynamicMesh3& mesh, int32 VertexIndex,
TFunctionRef<FVector3d(int32)> VertexPositionFunc, TFunctionRef<bool(int32)> VertexFilterFunc)
{
FVector3d Centroid = FVector3d::Zero();
int32 Count = 0;
for (int32 nbrvid : mesh.VtxVerticesItr(VertexIndex))
{
if (VertexFilterFunc(nbrvid))
{
Centroid += VertexPositionFunc(nbrvid);
Count++;
}
}
return (Count == 0) ? VertexPositionFunc(VertexIndex) : (Centroid / (double)Count);
}
template<typename GetPositionFuncType>
FVector3d TMeanValueCentroid(const FDynamicMesh3& mesh, int32 v_i, GetPositionFuncType GetPositionFunc, double WeightClamp)
{
// based on equations in https://www.inf.usi.ch/hormann/papers/Floater.2006.AGC.pdf (formula 9)
// refer to that paper for variable names/etc
FVector3d vSum = FVector3d::Zero();
double wSum = 0;
FVector3d Vi = GetPositionFunc(v_i);
int v_j = FDynamicMesh3::InvalidID, opp_v1 = FDynamicMesh3::InvalidID, opp_v2 = FDynamicMesh3::InvalidID;
int t1 = FDynamicMesh3::InvalidID, t2 = FDynamicMesh3::InvalidID;
for (int eid : mesh.VtxEdgesItr(v_i) )
{
opp_v2 = FDynamicMesh3::InvalidID;
mesh.GetVtxNbrhood(eid, v_i, v_j, opp_v1, opp_v2, t1, t2);
FVector3d Vj = GetPositionFunc(v_j);
FVector3d vVj = (Vj - Vi);
double len_vVj = Normalize(vVj);
// TODO: is this the right thing to do? if vertices are coincident,
// weight of this vertex should be very high!
if (len_vVj < FMathf::ZeroTolerance)
{
double w_ij = FMathd::Clamp(10000.0, 0, WeightClamp);
vSum += w_ij * Vj;
wSum += w_ij;
continue;
}
FVector3d vVdelta = Normalized(GetPositionFunc(opp_v1) - Vi);
double w_ij = VectorUtil::VectorTanHalfAngle(vVj, vVdelta);
if (opp_v2 != FDynamicMesh3::InvalidID)
{
FVector3d vVgamma = Normalized(GetPositionFunc(opp_v2) - Vi);
w_ij += VectorUtil::VectorTanHalfAngle(vVj, vVgamma);
}
w_ij /= len_vVj;
w_ij = FMathd::Clamp(w_ij, 0, WeightClamp);
vSum += w_ij * Vj;
wSum += w_ij;
}
if (wSum < FMathf::ZeroTolerance)
{
return FMeshWeights::UniformCentroid(mesh, v_i, GetPositionFunc);
}
return vSum / wSum;
}
FVector3d FMeshWeights::MeanValueCentroid(const FDynamicMesh3& mesh, int32 v_i, double WeightClamp)
{
return TMeanValueCentroid(mesh, v_i, [&](int32 vid) { return mesh.GetVertex(vid); }, WeightClamp);
}
FVector3d FMeshWeights::MeanValueCentroid(const FDynamicMesh3& mesh, int32 v_i, TFunctionRef<FVector3d(int32)> VertexPositionFunc, double WeightClamp)
{
return TMeanValueCentroid(mesh, v_i, VertexPositionFunc, WeightClamp);
}
template<typename GetPositionFuncType>
FVector3d TCotanCentroid(const FDynamicMesh3& mesh, int32 v_i, GetPositionFuncType GetPositionFunc)
{
// based on equations in http://www.geometry.caltech.edu/pubs/DMSB_III.pdf
FVector3d vSum = FVector3d::Zero();
double wSum = 0;
FVector3d Vi = GetPositionFunc(v_i);
int v_j = FDynamicMesh3::InvalidID, opp_v1 = FDynamicMesh3::InvalidID, opp_v2 = FDynamicMesh3::InvalidID;
int t1 = FDynamicMesh3::InvalidID, t2 = FDynamicMesh3::InvalidID;
bool bAborted = false;
for (int eid : mesh.VtxEdgesItr(v_i))
{
opp_v2 = FDynamicMesh3::InvalidID;
mesh.GetVtxNbrhood(eid, v_i, v_j, opp_v1, opp_v2, t1, t2);
FVector3d Vj = GetPositionFunc(v_j);
FVector3d Vo1 = GetPositionFunc(opp_v1);
double cot_alpha_ij = VectorUtil::VectorCot( (Vi - Vo1), (Vj - Vo1) );
if (cot_alpha_ij == 0)
{
bAborted = true;
break;
}
double w_ij = cot_alpha_ij;
if (opp_v2 != FDynamicMesh3::InvalidID)
{
FVector3d Vo2 = GetPositionFunc(opp_v2);
double cot_beta_ij = VectorUtil::VectorCot( (Vi - Vo2), (Vj - Vo2) );
if (cot_beta_ij == 0) {
bAborted = true;
break;
}
w_ij += cot_beta_ij;
}
vSum += w_ij * Vj;
wSum += w_ij;
}
if (bAborted || fabs(wSum) < FMathd::ZeroTolerance)
{
return Vi;
}
return vSum / wSum;
}
FVector3d FMeshWeights::CotanCentroid(const FDynamicMesh3& mesh, int32 v_i)
{
return TCotanCentroid(mesh, v_i, [&](int32 vid) { return mesh.GetVertex(vid); });
}
FVector3d FMeshWeights::CotanCentroid(const FDynamicMesh3& mesh, int32 v_i, TFunctionRef<FVector3d(int32)> VertexPositionFunc)
{
return TCotanCentroid(mesh, v_i, VertexPositionFunc);
}
template<typename PositionFuncType>
FVector3d TCotanCentroidSafe(const FDynamicMesh3& mesh, int32 v_i, PositionFuncType PositionFunc, double DegenerateTol = 100.0, bool* bOutputIsUniform = nullptr)
{
// based on equations in http://www.geometry.caltech.edu/pubs/DMSB_III.pdf
FVector3d vSum = FVector3d::Zero();
double wSum = 0;
FVector3d Vi = PositionFunc(v_i);
bool bDegenerated = false;
int v_j = FDynamicMesh3::InvalidID, opp_v1 = FDynamicMesh3::InvalidID, opp_v2 = FDynamicMesh3::InvalidID;
int t1 = FDynamicMesh3::InvalidID, t2 = FDynamicMesh3::InvalidID;
for (int eid : mesh.VtxEdgesItr(v_i))
{
opp_v2 = FDynamicMesh3::InvalidID;
mesh.GetVtxNbrhood(eid, v_i, v_j, opp_v1, opp_v2, t1, t2);
FVector3d Vj = PositionFunc(v_j);
FVector3d Vo1 = PositionFunc(opp_v1);
double cot_alpha_ij = VectorUtil::VectorCot((Vi - Vo1), (Vj - Vo1));
double w_ij = cot_alpha_ij;
double cot_beta_ij = 0;
if (opp_v2 != FDynamicMesh3::InvalidID)
{
FVector3d Vo2 = PositionFunc(opp_v2);
cot_beta_ij = VectorUtil::VectorCot((Vi - Vo2), (Vj - Vo2));
}
w_ij += cot_beta_ij;
if (FMathd::Abs(w_ij) > DegenerateTol)
{
bDegenerated = true;
break;
}
vSum += w_ij * Vj;
wSum += w_ij;
}
if (bOutputIsUniform != nullptr)
{
*bOutputIsUniform = bDegenerated;
}
if (bDegenerated || fabs(wSum) < FMathd::ZeroTolerance)
{
return FMeshWeights::UniformCentroid(mesh, v_i);
}
return vSum / wSum;
}
FVector3d FMeshWeights::CotanCentroidSafe(const FDynamicMesh3& mesh, int32 v_i, double DegenerateTol, bool* bFailedToUniform)
{
return TCotanCentroidSafe(mesh, v_i, [&](int32 vid) { return mesh.GetVertex(vid); }, DegenerateTol, bFailedToUniform);
}
FVector3d FMeshWeights::CotanCentroidSafe(const FDynamicMesh3& mesh, int32 v_i, TFunctionRef<FVector3d(int32)> VertexPositionFunc, double DegenerateTol, bool* bFailedToUniform)
{
return TCotanCentroidSafe(mesh, v_i, VertexPositionFunc, DegenerateTol, bFailedToUniform);
}
template<typename PositionFuncType>
void TCotanWeightsBlendSafe(const FDynamicMesh3& mesh, int32 v_i, PositionFuncType PositionFunc, TFunctionRef<void(int32, double)> BlendingFunc, double DegenerateTol = 100.0, bool* bOutputIsUniform = nullptr)
{
// based on equations in http://www.geometry.caltech.edu/pubs/DMSB_III.pdf
FVector3d Vi = PositionFunc(v_i);
bool bDegenerated = false;
TArray<int32, TInlineAllocator<32>> Neighbours;
TArray<double, TInlineAllocator<32>> Weights;
double wSum = 0;
int v_j = FDynamicMesh3::InvalidID, opp_v1 = FDynamicMesh3::InvalidID, opp_v2 = FDynamicMesh3::InvalidID;
int t1 = FDynamicMesh3::InvalidID, t2 = FDynamicMesh3::InvalidID;
for (int eid : mesh.VtxEdgesItr(v_i))
{
opp_v2 = FDynamicMesh3::InvalidID;
mesh.GetVtxNbrhood(eid, v_i, v_j, opp_v1, opp_v2, t1, t2);
FVector3d Vj = PositionFunc(v_j);
FVector3d Vo1 = PositionFunc(opp_v1);
double cot_alpha_ij = VectorUtil::VectorCot((Vi - Vo1), (Vj - Vo1));
double w_ij = cot_alpha_ij;
double cot_beta_ij = 0;
if (opp_v2 != FDynamicMesh3::InvalidID)
{
FVector3d Vo2 = PositionFunc(opp_v2);
cot_beta_ij = VectorUtil::VectorCot((Vi - Vo2), (Vj - Vo2));
}
w_ij += cot_beta_ij;
if (FMathd::Abs(w_ij) > DegenerateTol)
{
bDegenerated = true;
break;
}
Neighbours.Add(v_j);
Weights.Add(w_ij);
wSum += w_ij;
}
if (bOutputIsUniform != nullptr)
{
*bOutputIsUniform = bDegenerated;
}
int32 N = Neighbours.Num();
if (bDegenerated || fabs(wSum) < FMathd::ZeroTolerance)
{
double Weight = 1.0 / (double)N;
for (int32 k = 0; k < N; ++k)
{
BlendingFunc(Neighbours[k], Weight);
}
}
else
{
for (int32 k = 0; k < N; ++k )
{
BlendingFunc(Neighbours[k], Weights[k] / wSum);
}
}
}
void FMeshWeights::CotanWeightsBlendSafe(const FDynamicMesh3& Mesh, int32 VertexIndex, TFunctionRef<void(int32, double)> BlendingFunc, double DegenerateTol, bool* bFailedToUniform)
{
TCotanWeightsBlendSafe(Mesh, VertexIndex, [&](int32 vid) { return Mesh.GetVertex(vid); }, BlendingFunc, DegenerateTol, bFailedToUniform);
}
template<typename GetPositionFuncType>
double TVoronoiArea(const FDynamicMesh3& mesh, int32 v_i, GetPositionFuncType GetPositionFunc, double CotClampRange = 1000 /* about 0.05 deg */ )
{
// based on equations in http://www.geometry.caltech.edu/pubs/DMSB_III.pdf
double areaSum = 0;
FVector3d Vi = GetPositionFunc(v_i);
for (int32 tid : mesh.VtxTrianglesItr(v_i))
{
FIndex3i t = mesh.GetTriangle(tid);
int ti = (t[0] == v_i) ? 0 : ((t[1] == v_i) ? 1 : 2);
FVector3d Vj = GetPositionFunc(t[(ti + 1) % 3]);
FVector3d Vk = GetPositionFunc(t[(ti + 2) % 3]);
if (VectorUtil::IsObtuse(Vi, Vj, Vk))
{
// if triangle is obtuse voronoi area is undefind and we just return portion of triangle area
FVector3d Vij = Vj - Vi;
FVector3d Vik = Vk - Vi;
double Dot = Vij.Dot(Vik);
double areaT = 0.5 * FMathd::Sqrt(Vij.SquaredLength() * Vik.SquaredLength() - Dot * Dot);
Normalize(Vij); Normalize(Vik);
areaSum += (AngleR(Vij, Vik) > FMathd::HalfPi) ? // obtuse at v_i ?
(areaT * 0.5) : (areaT * 0.25);
}
else
{
// voronoi area
FVector3d Vji = Vi - Vj;
FVector3d Vki = Vi - Vk;
FVector3d Vkj = Vj - Vk;
double cot_alpha_ij = VectorUtil::VectorCot(Vki, Vkj);
cot_alpha_ij = FMathd::Clamp(cot_alpha_ij, -CotClampRange, CotClampRange);
double cot_beta_ik = VectorUtil::VectorCot(Vji, -Vkj);
cot_beta_ik = FMathd::Clamp(cot_beta_ik, -CotClampRange, CotClampRange);
areaSum += Vji.SquaredLength() * cot_alpha_ij * 0.125;
areaSum += Vki.SquaredLength() * cot_beta_ik * 0.125;
}
}
return areaSum;
}
double FMeshWeights::VoronoiArea(const FDynamicMesh3& mesh, int32 v_i)
{
return TVoronoiArea(mesh, v_i, [&](int32 vid) { return mesh.GetVertex(vid); });
}
double FMeshWeights::VoronoiArea(const FDynamicMesh3& mesh, int32 v_i, TFunctionRef<FVector3d(int32)> VertexPositionFunc)
{
return TVoronoiArea(mesh, v_i, VertexPositionFunc);
}