566 lines
17 KiB
C++
566 lines
17 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "AdaptiveTessellator.h"
|
|
|
|
#include "HAL/Platform.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "IndexTypes.h"
|
|
#include "LerpVert.h"
|
|
#include "TriangleUtil.h"
|
|
|
|
namespace Nanite {
|
|
|
|
// Mesh topology implementation for FAdaptiveTesselation2.
|
|
//
|
|
// TriEdgeIndex is always in [0..2] and corresponds to enumerating triangle edges and vertices in order
|
|
// The edge connects the vertices Tri[TriEdgeIndex], Tri[(TriEdgeIndex+1)%3].
|
|
//
|
|
class FMinimalMesh
|
|
{
|
|
public:
|
|
using FIndex3i = UE::Geometry::FIndex3i;
|
|
|
|
using RealType = float;
|
|
using VecType = FVector3f;
|
|
|
|
enum class EEdgeSplitMode
|
|
{
|
|
AlwaysCrack, //< when splitting edges, always duplicate vertex
|
|
CrackOnMaterialSeam, //< only split vertex if there is a material seam
|
|
CrackFree //< never split, always average
|
|
};
|
|
|
|
FMinimalMesh(
|
|
TArray<FLerpVert>& InVerts, //< per-vertex information. original list will be preserved, but new entries will be added
|
|
TArray<uint32>& InIndexes, //< per triangle triplets of vertices pointing into InVerts.
|
|
TArray<int32>& InMaterialIndexes, //< per-triangle material index
|
|
EEdgeSplitMode InEdgeSplitMode, //< see enum
|
|
bool bInTriangleSoup) //< if true, adjacency will be determined by vertex positions, otherwise based on indices (faster)
|
|
: Verts(InVerts)
|
|
, Indexes(InIndexes)
|
|
, MaterialIndexes(InMaterialIndexes)
|
|
, EdgeSplitMode(InEdgeSplitMode)
|
|
, bTriangleSoup(bInTriangleSoup)
|
|
{
|
|
AdjEdges.Init( -1, HalfEdgeCount() );
|
|
|
|
// create the topology. directed edges will map to their opposite directed edge in opposite direction
|
|
if (!bTriangleSoup)
|
|
{
|
|
struct FSortEdge
|
|
{
|
|
size_t EdgeIndex;
|
|
int32 VtxA, VtxB;
|
|
|
|
FORCEINLINE bool operator<(const FSortEdge& other) const
|
|
{
|
|
return VtxA < other.VtxA || (VtxA == other.VtxA && VtxB < other.VtxB);
|
|
}
|
|
};
|
|
|
|
TArray<FSortEdge> SortedHalfEdges;
|
|
SortedHalfEdges.Reserve(AdjEdges.Num());
|
|
|
|
const size_t NumEdges = HalfEdgeCount();
|
|
for( size_t i = 0; i < NumEdges; ++i )
|
|
{
|
|
const int32 V0 = Indexes[i];
|
|
const int32 V1 = Indexes[Cycle3(i)];
|
|
SortedHalfEdges.Emplace( i, V0 < V1 ? V0 : V1, V0 < V1 ? V1 : V0 );
|
|
}
|
|
SortedHalfEdges.Sort();
|
|
|
|
for ( size_t i = 0; i < NumEdges-1; ++i)
|
|
{
|
|
if (SortedHalfEdges[i].VtxA == SortedHalfEdges[i+1].VtxA && SortedHalfEdges[i].VtxB == SortedHalfEdges[i+1].VtxB)
|
|
{
|
|
AdjEdges[SortedHalfEdges[i ].EdgeIndex] = SortedHalfEdges[i+1].EdgeIndex;
|
|
AdjEdges[SortedHalfEdges[i+1].EdgeIndex] = SortedHalfEdges[i ].EdgeIndex;
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FEdgeHash EdgeHash( HalfEdgeCount() );
|
|
for( int32 EdgeIndex = 0; EdgeIndex < HalfEdgeCount(); EdgeIndex++ )
|
|
{
|
|
EdgeHash.ForAllMatching( EdgeIndex, true,
|
|
[ this ]( int32 CornerIndex )
|
|
{
|
|
return Verts[ Indexes[ CornerIndex ] ].Position;
|
|
},
|
|
[&]( int32 EdgeIndex0, int32 EdgeIndex1 )
|
|
{
|
|
if( AdjEdges[ EdgeIndex0 ] < 0 &&
|
|
AdjEdges[ EdgeIndex1 ] < 0 )
|
|
{
|
|
AdjEdges[ EdgeIndex0 ] = EdgeIndex1;
|
|
AdjEdges[ EdgeIndex1 ] = EdgeIndex0;
|
|
}
|
|
} );
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Policy-interface implementation begin
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
FORCEINLINE int32 GetVertexIndex(const int32 TriIndex, const int32 TriEdgeIndex) const
|
|
{
|
|
check(TriEdgeIndex < 3);
|
|
check(TriIndex < MaxTriID());
|
|
|
|
return Indexes[TriIndex * 3 + TriEdgeIndex];
|
|
}
|
|
|
|
FORCEINLINE FIndex3i GetTriangle(const int32 TriIndex) const
|
|
{
|
|
check(TriIndex < MaxTriID());
|
|
check(TriIndex >= 0);
|
|
|
|
check(Indexes[TriIndex * 3 + 0] <= uint32(std::numeric_limits<int32>::max()));
|
|
check(Indexes[TriIndex * 3 + 1] <= uint32(std::numeric_limits<int32>::max()));
|
|
check(Indexes[TriIndex * 3 + 2] <= uint32(std::numeric_limits<int32>::max()));
|
|
|
|
return { int32(Indexes[TriIndex * 3 + 0]), int32(Indexes[TriIndex * 3 + 1]), int32(Indexes[TriIndex * 3 + 2]) };
|
|
}
|
|
|
|
FORCEINLINE FVector3f GetVertexPosition(const int32 TriIndex, const int32 TriEdgeIndex) const
|
|
{
|
|
return Verts[GetVertexIndex(TriIndex, TriEdgeIndex)].Position;
|
|
}
|
|
|
|
FORCEINLINE FVector3f GetVertexPosition(const int32 VertexIndex) const
|
|
{
|
|
return Verts[VertexIndex].Position;
|
|
}
|
|
|
|
FORCEINLINE void SetVertexPosition(const int32 VertexIndex, const FVector3f Position) const
|
|
{
|
|
Verts[VertexIndex].Position = Position;
|
|
}
|
|
|
|
FORCEINLINE void GetTriVertices(const int32 TriIndex, FVector3f& v0, FVector3f& v1, FVector3f& v2) const
|
|
{
|
|
v0 = Verts[GetVertexIndex(TriIndex, 0)].Position;
|
|
v1 = Verts[GetVertexIndex(TriIndex, 1)].Position;
|
|
v2 = Verts[GetVertexIndex(TriIndex, 2)].Position;
|
|
}
|
|
|
|
FORCEINLINE int32 MaxVertexID() const
|
|
{
|
|
return Verts.Num();
|
|
}
|
|
|
|
FORCEINLINE bool IsValidVertex(const int32 VertexID) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FORCEINLINE int32 MaxTriID() const
|
|
{
|
|
return Indexes.Num() / 3;
|
|
}
|
|
|
|
FORCEINLINE bool IsValidTri(const int32 TriIndex) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Return the triangle index on the other side of the half edge given by TriIndex, TriEdgeIndex or IndexConstants::InvalidID on boundary
|
|
FORCEINLINE int32 GetAdjTriangle(const int32 TriIndex, const int32 TriEdgeIndex) const
|
|
{
|
|
const int32 AdjEdge = AdjEdges[TriIndex * 3 + TriEdgeIndex];
|
|
|
|
if (AdjEdge < 0)
|
|
return IndexConstants::InvalidID;
|
|
|
|
return AdjEdge / 3;
|
|
}
|
|
|
|
// Return the triangle index on the other side of the half edge given by TriIndex, TriEdgeIndex or IndexConstants::InvalidID on boundary
|
|
FORCEINLINE TPair<int32,int32> GetAdjEdge(const int32 TriIndex, const int32 TriEdgeIndex) const
|
|
{
|
|
const int32 AdjEdge = AdjEdges[TriIndex * 3 + TriEdgeIndex];
|
|
|
|
if (AdjEdge < 0)
|
|
return { -1, -1 };
|
|
|
|
return { AdjEdge / 3, AdjEdge % 3 };
|
|
}
|
|
|
|
FORCEINLINE bool EdgeManifoldCheck(const int32 TriIndex, const int32 TriEdgeIndex) const
|
|
{
|
|
const int32 EdgeIndex = TriIndex * 3 + TriEdgeIndex;
|
|
const int32 AdjEdgeIndex = AdjEdges[EdgeIndex];
|
|
|
|
if (AdjEdgeIndex < 0)
|
|
return true;
|
|
|
|
// Manifoldness check
|
|
if( Indexes[EdgeIndex] != Indexes[ Cycle3( AdjEdgeIndex ) ] ||
|
|
Indexes[ Cycle3( EdgeIndex ) ] != Indexes[ AdjEdgeIndex ] )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
FORCEINLINE bool AllowEdgeFlip(const int32 TriIndex, const int32 TriEdgeIndex, const int32 AdjTriIndex ) const
|
|
{
|
|
return (MaterialIndexes[TriIndex] == MaterialIndexes[AdjTriIndex]);
|
|
}
|
|
|
|
FORCEINLINE bool AllowEdgeSplit(const int32 TriIndex, const int32 TriEdgeIndex ) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FORCEINLINE FVector3f GetTriangleNormal(const int32 TriIndex) const
|
|
{
|
|
const FVector3f& p0 = Verts[Indexes[TriIndex * 3 + 0]].Position;
|
|
const FVector3f& p1 = Verts[Indexes[TriIndex * 3 + 1]].Position;
|
|
const FVector3f& p2 = Verts[Indexes[TriIndex * 3 + 2]].Position;
|
|
|
|
const FVector3f Edge01 = p1 - p0;
|
|
const FVector3f Edge12 = p2 - p1;
|
|
const FVector3f Edge20 = p0 - p2;
|
|
|
|
return (Edge01 ^ Edge20).GetSafeNormal();
|
|
}
|
|
|
|
// Split given triangle edge. The new position is SplitWeight * V0 + (1-SplitWeight) * V1.
|
|
UE::Geometry::FEdgeSplitInfo SplitEdge(const int32 TriIndex, const int32 TriEdgeIndex, const float SplitWeight);
|
|
|
|
// Introduce new vertex in triangle, connect to all three vertices and split into three new triangles.
|
|
UE::Geometry::FPokeInfo PokeTriangle(const int32 TriIndex, const FVector3f Barycentrics);
|
|
|
|
UE::Geometry::FFlipEdgeInfo FlipEdge(const int32 TriIndex, const int32 TriEdgeIndex);
|
|
|
|
bool IsTriangleSoup() const
|
|
{
|
|
return bTriangleSoup;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Policy-interface implementation end
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const FLerpVert& GetLerpVert(const size_t Idx) const
|
|
{
|
|
return Verts[Idx];
|
|
}
|
|
private:
|
|
|
|
// Added interpolation of corner vertices of triangle according to barycentric coordinates and return new vertex index.
|
|
inline int32 AddInterpolatedVertex(const FVector3f Barycentrics, const int32 TriIndex)
|
|
{
|
|
ensure( FMath::Abs( Barycentrics.X + Barycentrics.Y + Barycentrics.Z - 1.0f ) < 1e-4f );
|
|
ensure( 0.0f <= Barycentrics.X && Barycentrics.X < 1.0f );
|
|
ensure( 0.0f <= Barycentrics.Y && Barycentrics.Y < 1.0f );
|
|
ensure( 0.0f <= Barycentrics.Z && Barycentrics.Z < 1.0f );
|
|
|
|
const FIndex3i Tri = GetTriangle(TriIndex);
|
|
|
|
const FLerpVert& Vert0 = Verts[ Tri.A ];
|
|
const FLerpVert& Vert1 = Verts[ Tri.B ];
|
|
const FLerpVert& Vert2 = Verts[ Tri.C ];
|
|
|
|
FLerpVert NewVert;
|
|
NewVert = Vert0 * Barycentrics.X;
|
|
NewVert += Vert1 * Barycentrics.Y;
|
|
NewVert += Vert2 * Barycentrics.Z;
|
|
|
|
return Verts.Add( NewVert );
|
|
}
|
|
|
|
// Added interpolation of corner vertices of edge according to barycentric coordinates and return new vertex index.
|
|
inline int32 AddInterpolatedVertex(const FVector2f Barycentrics, int32 VertexIndex0, int32 VertexIndex1)
|
|
{
|
|
ensure(FMath::Abs(Barycentrics.X + Barycentrics.Y - 1.0f) < 1e-4f);
|
|
ensure(0.0f <= Barycentrics.X && Barycentrics.X < 1.0f);
|
|
ensure(0.0f <= Barycentrics.Y && Barycentrics.Y < 1.0f);
|
|
|
|
const FLerpVert& Vert0 = Verts[VertexIndex0];
|
|
const FLerpVert& Vert1 = Verts[VertexIndex1];
|
|
|
|
FLerpVert NewVert;
|
|
NewVert = Vert0 * Barycentrics.X;
|
|
NewVert += Vert1 * Barycentrics.Y;
|
|
|
|
return Verts.Add(NewVert);
|
|
}
|
|
|
|
FORCEINLINE int32 HalfEdgeCount() const
|
|
{
|
|
return Indexes.Num();
|
|
}
|
|
|
|
void LinkEdge( int32 EdgeIndex0, int32 EdgeIndex1 )
|
|
{
|
|
AdjEdges[ EdgeIndex0 ] = EdgeIndex1;
|
|
if( EdgeIndex1 >= 0 )
|
|
{
|
|
AdjEdges[ EdgeIndex1 ] = EdgeIndex0;
|
|
|
|
check( Verts[ Indexes[ EdgeIndex0 ] ].Position == Verts[ Indexes[ Cycle3( EdgeIndex1 ) ] ].Position );
|
|
check( Verts[ Indexes[ EdgeIndex1 ] ].Position == Verts[ Indexes[ Cycle3( EdgeIndex0 ) ] ].Position );
|
|
}
|
|
}
|
|
|
|
private:
|
|
|
|
friend class DisplacementPolicyFunctor;
|
|
|
|
TArray<FLerpVert>& Verts; // per-vertex
|
|
TArray<uint32>& Indexes; // triangle as triplets
|
|
TArray<int32>& MaterialIndexes; // per triangle material index
|
|
|
|
TArray<int32> AdjEdges; // half-edge to neighboring half-edge (or -1 on boundary)
|
|
const EEdgeSplitMode EdgeSplitMode; // how to we handle edge splits
|
|
const bool bTriangleSoup; // if true, don't assume the vertex indices provide mesh connectivity
|
|
};
|
|
|
|
|
|
inline UE::Geometry::FEdgeSplitInfo FMinimalMesh::SplitEdge(const int32 TriIndex, const int32 TriEdgeIndex, const float SplitWeight)
|
|
{
|
|
UE::Geometry::FEdgeSplitInfo SplitInfo;
|
|
|
|
const FIndex3i Triangle = GetTriangle(TriIndex);
|
|
int32 NewIndex = AddInterpolatedVertex(FVector2f(SplitWeight, 1.f - SplitWeight),
|
|
GetVertexIndex(TriIndex, TriEdgeIndex),
|
|
GetVertexIndex(TriIndex, Cycle3(TriEdgeIndex)));
|
|
SplitInfo.NewVertIndex[0] = NewIndex;
|
|
|
|
// Split edge
|
|
int32 Edge[2];
|
|
Edge[0] = TriIndex * 3 + TriEdgeIndex;
|
|
Edge[1] = AdjEdges[ Edge[0] ];
|
|
|
|
const int32 NumNewTris = Edge[1] < 0 ? 1 : 2;
|
|
|
|
SplitInfo.OldTriIndex[0] = TriIndex;
|
|
SplitInfo.OldTriIndex[1] = Edge[1] / 3;
|
|
|
|
SplitInfo.NewTriIndex[0] = MaxTriID();
|
|
SplitInfo.NewTriIndex[1] = SplitInfo.NewTriIndex[0] + 1;
|
|
|
|
if( Edge[1] < 0 )
|
|
{
|
|
SplitInfo.OldTriIndex[1] = -1;
|
|
SplitInfo.NewTriIndex[1] = -1;
|
|
SplitInfo.NewVertIndex[1] = -1;
|
|
}
|
|
else
|
|
{
|
|
check( Verts[ Indexes[ Edge[0] ] ].Position == Verts[ Indexes[ Cycle3( Edge[1] ) ] ].Position );
|
|
check( Verts[ Indexes[ Edge[1] ] ].Position == Verts[ Indexes[ Cycle3( Edge[0] ) ] ].Position );
|
|
}
|
|
|
|
/*
|
|
v2
|
|
/|\
|
|
e1/ | \e2
|
|
/ | \
|
|
v1/ 0|1 \v0
|
|
o====+====o
|
|
v0\ 1|0 /v1
|
|
\ | /
|
|
e2\ | /e1
|
|
\|/
|
|
v2
|
|
*/
|
|
for( int32 j = 0; j < NumNewTris; j++ )
|
|
{
|
|
const uint32 e0 = Edge[j];
|
|
const uint32 e1 = Cycle3( Edge[j] );
|
|
const uint32 e2 = Cycle3( Edge[j], 2 );
|
|
|
|
uint32 OldIndex0 = Indexes[ e0 ];
|
|
uint32 OldIndex1 = Indexes[ e1 ];
|
|
uint32 OldIndex2 = Indexes[ e2 ];
|
|
|
|
int32 OldAdjEdge1 = AdjEdges[ e1 ];
|
|
int32 OldAdjEdge2 = AdjEdges[ e2 ];
|
|
|
|
if( j == 1 )
|
|
{
|
|
// only introduce seem when material indices are different.
|
|
if (EdgeSplitMode == EEdgeSplitMode::CrackFree ||
|
|
(EdgeSplitMode == EEdgeSplitMode::CrackOnMaterialSeam && MaterialIndexes[SplitInfo.OldTriIndex[0]] == MaterialIndexes[SplitInfo.OldTriIndex[1]]))
|
|
{
|
|
SplitInfo.NewVertIndex[1] = SplitInfo.NewVertIndex[0];
|
|
}
|
|
else
|
|
{
|
|
NewIndex = AddInterpolatedVertex(FVector2f(SplitWeight, 1.f - SplitWeight), OldIndex1, OldIndex0);
|
|
SplitInfo.NewVertIndex[j] = NewIndex;
|
|
}
|
|
}
|
|
|
|
Indexes.AddUninitialized(3);
|
|
AdjEdges.AddUninitialized(3);
|
|
MaterialIndexes.Add( CopyTemp( MaterialIndexes[ SplitInfo.OldTriIndex[j] ] ) );
|
|
|
|
// replace v0
|
|
uint32 i = SplitInfo.OldTriIndex[j] * 3;
|
|
Indexes[ i + 0 ] = NewIndex;
|
|
Indexes[ i + 1 ] = OldIndex1;
|
|
Indexes[ i + 2 ] = OldIndex2;
|
|
|
|
AdjEdges[ i + 0 ] = SplitInfo.NewTriIndex[j^1] * 3;
|
|
LinkEdge( i + 1, OldAdjEdge1 );
|
|
AdjEdges[ i + 2 ] = SplitInfo.NewTriIndex[j] * 3 + 1;
|
|
|
|
// replace v1
|
|
i = SplitInfo.NewTriIndex[j] * 3;
|
|
Indexes[ i + 0 ] = OldIndex0;
|
|
Indexes[ i + 1 ] = NewIndex;
|
|
Indexes[ i + 2 ] = OldIndex2;
|
|
|
|
AdjEdges[ i + 0 ] = SplitInfo.OldTriIndex[j^1] * 3;
|
|
AdjEdges[ i + 1 ] = SplitInfo.OldTriIndex[j] * 3 + 2;
|
|
LinkEdge( i + 2, OldAdjEdge2 );
|
|
}
|
|
|
|
return SplitInfo;
|
|
}
|
|
|
|
inline UE::Geometry::FPokeInfo FMinimalMesh::PokeTriangle(const int32 TriIndex, const FVector3f Barycentrics)
|
|
{
|
|
UE::Geometry::FPokeInfo PokeInfo;
|
|
|
|
const FIndex3i Tri = GetTriangle(TriIndex);
|
|
const int32 NewIndex = AddInterpolatedVertex(Barycentrics, TriIndex);
|
|
|
|
ensure( FMath::Abs( Barycentrics.X + Barycentrics.Y + Barycentrics.Z - 1.0f ) < 1e-4f );
|
|
ensure( 0.0f <= Barycentrics.X && Barycentrics.X < 1.0f );
|
|
ensure( 0.0f <= Barycentrics.Y && Barycentrics.Y < 1.0f );
|
|
ensure( 0.0f <= Barycentrics.Z && Barycentrics.Z < 1.0f );
|
|
|
|
const FLerpVert& Vert0 = Verts[ Tri.A ];
|
|
const FLerpVert& Vert1 = Verts[ Tri.B ];
|
|
const FLerpVert& Vert2 = Verts[ Tri.C ];
|
|
|
|
FLerpVert NewVert;
|
|
NewVert = Vert0 * Barycentrics.X;
|
|
NewVert += Vert1 * Barycentrics.Y;
|
|
NewVert += Vert2 * Barycentrics.Z;
|
|
|
|
int32 OldAdjEdges[3];
|
|
OldAdjEdges[0] = AdjEdges[ TriIndex * 3 + 0 ];
|
|
OldAdjEdges[1] = AdjEdges[ TriIndex * 3 + 1 ];
|
|
OldAdjEdges[2] = AdjEdges[ TriIndex * 3 + 2 ];
|
|
|
|
PokeInfo.NewTriIndex[0] = TriIndex;
|
|
PokeInfo.NewTriIndex[1] = MaxTriID();
|
|
PokeInfo.NewTriIndex[2] = PokeInfo.NewTriIndex[1] + 1;
|
|
|
|
// add 2 triangles
|
|
Indexes.AddUninitialized(6);
|
|
AdjEdges.AddUninitialized(6);
|
|
MaterialIndexes.AddUninitialized(2);
|
|
|
|
for( uint32 EdgeIndex = 0; EdgeIndex < 3; EdgeIndex++ )
|
|
{
|
|
const uint32 e0 = EdgeIndex;
|
|
const uint32 e1 = (1 << e0) & 3;
|
|
const uint32 e2 = (1 << e1) & 3;
|
|
|
|
uint32 i = PokeInfo.NewTriIndex[ EdgeIndex ] * 3;
|
|
Indexes[ i + 0 ] = Tri[ e0 ];
|
|
Indexes[ i + 1 ] = Tri[ e1 ];
|
|
Indexes[ i + 2 ] = NewIndex;
|
|
|
|
LinkEdge( i + 0, OldAdjEdges[ e0 ] );
|
|
AdjEdges[ i + 1 ] = PokeInfo.NewTriIndex[ e1 ] * 3 + 2;
|
|
AdjEdges[ i + 2 ] = PokeInfo.NewTriIndex[ e2 ] * 3 + 1;
|
|
|
|
MaterialIndexes[ PokeInfo.NewTriIndex[ EdgeIndex ] ] = MaterialIndexes[ TriIndex ];
|
|
}
|
|
|
|
PokeInfo.EdgeIndex[0] = PokeInfo.EdgeIndex[1] = PokeInfo.EdgeIndex[2] = 0;
|
|
PokeInfo.NewVertIndex = NewIndex;
|
|
|
|
return PokeInfo;
|
|
}
|
|
|
|
inline UE::Geometry::FFlipEdgeInfo FMinimalMesh::FlipEdge(const int32 TriIndex, const int32 TriEdgeIndex)
|
|
{
|
|
const int32 EdgeIndex = TriIndex * 3 + TriEdgeIndex;
|
|
const int32 AdjEdge = AdjEdges[ EdgeIndex ];
|
|
check(AdjEdge >= 0);
|
|
|
|
const int32 AdjTriIndex = AdjEdge / 3;
|
|
|
|
/*
|
|
v2 v0,v1 vA
|
|
/\ /|\ /\
|
|
e1/ \e2 e2/ | \e1 eD/ \eA
|
|
/ \ / | \ / \
|
|
v1/ e0 \v0 / | \ / \
|
|
o========o => v2 e0|e0 v2 vD vB
|
|
v0\ e0 /v1 \ | / \ /
|
|
\ / \ | / \ /
|
|
e2\ /e1 e1\ | /e2 eC\ /eB
|
|
\/ \|/ \/
|
|
v2 v1'v0 vC
|
|
*/
|
|
const int32 eA = TriIndex * 3 + ( EdgeIndex + 2 ) % 3;
|
|
const int32 eB = AdjTriIndex * 3 + ( AdjEdge + 1 ) % 3;
|
|
const int32 eC = AdjTriIndex * 3 + ( AdjEdge + 2 ) % 3;
|
|
const int32 eD = TriIndex * 3 + ( EdgeIndex + 1 ) % 3;
|
|
|
|
int32 IndexA = Indexes[ eA ];
|
|
int32 IndexB = Indexes[ eB ];
|
|
int32 IndexC = Indexes[ eC ];
|
|
int32 IndexD = Indexes[ eD ];
|
|
|
|
int32 AdjEdgeA = AdjEdges[ eA ];
|
|
int32 AdjEdgeB = AdjEdges[ eB ];
|
|
int32 AdjEdgeC = AdjEdges[ eC ];
|
|
int32 AdjEdgeD = AdjEdges[ eD ];
|
|
|
|
#ifndef DM3_NANITE_COMPATIBILITY
|
|
// original nanite code
|
|
Indexes[ TriIndex * 3 + 0 ] = IndexC;
|
|
Indexes[ TriIndex * 3 + 1 ] = IndexA;
|
|
Indexes[ TriIndex * 3 + 2 ] = IndexB;
|
|
|
|
Indexes[ AdjTriIndex * 3 + 0 ] = IndexA;
|
|
Indexes[ AdjTriIndex * 3 + 1 ] = IndexC;
|
|
Indexes[ AdjTriIndex * 3 + 2 ] = IndexD;
|
|
|
|
LinkEdge( TriIndex * 3, AdjTriIndex * 3 );
|
|
LinkEdge( TriIndex * 3 + 1, AdjEdgeA );
|
|
LinkEdge( TriIndex * 3 + 2, AdjEdgeB );
|
|
LinkEdge( AdjTriIndex * 3 + 1, AdjEdgeC );
|
|
LinkEdge( AdjTriIndex * 3 + 2, AdjEdgeD );
|
|
|
|
return { { TriIndex, AdjTriIndex }, { 0, 0 } };
|
|
#else
|
|
// dynamic mesh-like
|
|
Indexes[ AdjTriIndex * 3 + 0 ] = IndexC;
|
|
Indexes[ AdjTriIndex * 3 + 1 ] = IndexA;
|
|
Indexes[ AdjTriIndex * 3 + 2 ] = IndexB;
|
|
|
|
Indexes[ TriIndex * 3 + 0 ] = IndexA;
|
|
Indexes[ TriIndex * 3 + 1 ] = IndexC;
|
|
Indexes[ TriIndex * 3 + 2 ] = IndexD;
|
|
|
|
LinkEdge( AdjTriIndex * 3, TriIndex * 3 );
|
|
LinkEdge( TriIndex * 3 + 1, AdjEdgeC );
|
|
LinkEdge( TriIndex * 3 + 2, AdjEdgeD );
|
|
LinkEdge( AdjTriIndex * 3 + 1, AdjEdgeA );
|
|
LinkEdge( AdjTriIndex * 3 + 2, AdjEdgeB );
|
|
|
|
return { { TriIndex, AdjTriIndex }, { 0, 0 } };
|
|
#endif
|
|
}
|
|
|
|
} // namespace Nanite
|