Files
UnrealEngine/Engine/Source/Runtime/MeshConversion/Public/MeshDescriptionAdapter.h
2025-05-18 13:04:45 +08:00

320 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "MeshDescription.h"
#include "StaticMeshAttributes.h"
#include "Spatial/MeshAABBTree3.h"
#include "MeshAdapter.h"
/**
* Basic struct to adapt a FMeshDescription for use by GeometryProcessing classes that template the mesh type and expect a standard set of basic accessors
* For example, this adapter will let you use a FMeshDescription with GeometryProcessing's TMeshAABBTree3
* See also the Editable version below
*
* Usage example -- given some const FMeshDescription* Mesh:
* FMeshDescriptionAABBAdapter MeshAdapter(Mesh); // adapt the mesh
* TMeshAABBTree3<const FMeshDescriptionTriangleMeshAdapter> AABBTree(&MeshAdapter); // provide the adapter to a templated class like TMeshAABBTree3
*/
struct /*MESHCONVERSION_API*/ FMeshDescriptionTriangleMeshAdapter
{
using FIndex3i = UE::Geometry::FIndex3i;
protected:
const FMeshDescription* Mesh;
TVertexAttributesConstRef<FVector3f> VertexPositions;
TVertexInstanceAttributesConstRef<FVector3f> VertexInstanceNormals;
TVertexInstanceAttributesConstRef<FVector2f> VertexInstanceUVs;
TArrayView<const FVertexInstanceID> TriangleVertexInstanceIndices;
FVector3d BuildScale = FVector3d::One();
bool bScaleNormals = false;
public:
FMeshDescriptionTriangleMeshAdapter(const FMeshDescription* MeshIn) : Mesh(MeshIn)
{
FStaticMeshConstAttributes Attributes(*MeshIn);
VertexPositions = Attributes.GetVertexPositions();
VertexInstanceNormals = Attributes.GetVertexInstanceNormals();
VertexInstanceUVs = Attributes.GetVertexInstanceUVs();
TriangleVertexInstanceIndices = Attributes.GetTriangleVertexInstanceIndices().GetRawArray();
// @todo: can we hold TArrayViews of the attribute arrays here? Do we guarantee not to mutate the mesh description for the duration of this object?
}
void SetBuildScale(const FVector3d& BuildScaleIn, bool bScaleNormalsIn)
{
BuildScale = BuildScaleIn;
bScaleNormals = bScaleNormalsIn;
}
bool IsTriangle(int32 TID) const
{
return TID >= 0 && TID < Mesh->Triangles().Num();
}
bool IsVertex(int32 VID) const
{
return VID >= 0 && VID < Mesh->Vertices().Num();
}
// ID and Count are the same for MeshDescription because it's compact
int32 MaxTriangleID() const
{
return Mesh->Triangles().Num();
}
int32 TriangleCount() const
{
return Mesh->Triangles().Num();
}
int32 MaxVertexID() const
{
return Mesh->Vertices().Num();
}
int32 VertexCount() const
{
return Mesh->Vertices().Num();
}
uint64 GetChangeStamp() const
{
// MeshDescription doesn't provide any mechanism to know if it's been modified so just return 1
// and leave it to the caller to not build an aabb and then change the underlying mesh
return 1;
}
FIndex3i GetTriangle(int32 IDValue) const
{
TArrayView<const FVertexID> TriVertIDs = Mesh->GetTriangleVertices(FTriangleID(IDValue));
return FIndex3i(TriVertIDs[0].GetValue(), TriVertIDs[1].GetValue(), TriVertIDs[2].GetValue());
}
FVector3d GetVertex(int32 IDValue) const
{
const FVector3f& Position = VertexPositions[FVertexID(IDValue)];
return FVector3d(BuildScale.X * (double)Position.X, BuildScale.Y * (double)Position.Y, BuildScale.Z * (double)Position.Z);
}
inline void GetTriVertices(int32 IDValue, FVector3d& V0, FVector3d& V1, FVector3d& V2) const
{
TArrayView<const FVertexID> TriVertIDs = Mesh->GetTriangleVertices(FTriangleID(IDValue));
const FVector3f& A = VertexPositions[TriVertIDs[0]];
V0 = FVector3d(BuildScale.X * (double)A.X, BuildScale.Y * (double)A.Y, BuildScale.Z * (double)A.Z);
const FVector3f& B = VertexPositions[TriVertIDs[1]];
V1 = FVector3d(BuildScale.X * (double)B.X, BuildScale.Y * (double)B.Y, BuildScale.Z * (double)B.Z);
const FVector3f& C = VertexPositions[TriVertIDs[2]];
V2 = FVector3d(BuildScale.X * (double)C.X, BuildScale.Y * (double)C.Y, BuildScale.Z * (double)C.Z);
}
template<typename VectorType>
inline void GetTriVertices(int32 IDValue, VectorType& V0, VectorType& V1, VectorType& V2) const
{
TArrayView<const FVertexID> TriVertIDs = Mesh->GetTriangleVertices(FTriangleID(IDValue));
const FVector3f& A = VertexPositions[TriVertIDs[0]];
V0 = VectorType(BuildScale.X * (double)A.X, BuildScale.Y * (double)A.Y, BuildScale.Z * (double)A.Z);
const FVector3f& B = VertexPositions[TriVertIDs[1]];
V1 = VectorType(BuildScale.X * (double)B.X, BuildScale.Y * (double)B.Y, BuildScale.Z * (double)B.Z);
const FVector3f& C = VertexPositions[TriVertIDs[2]];
V2 = VectorType(BuildScale.X * (double)C.X, BuildScale.Y * (double)C.Y, BuildScale.Z * (double)C.Z);
}
inline bool HasNormals() const
{
return VertexInstanceNormals.IsValid();
}
inline bool IsNormal(int32 NID) const
{
return HasNormals() && NID >= 0 && NID < NormalCount();
}
inline int32 MaxNormalID() const
{
return HasNormals() ? VertexInstanceNormals.GetNumElements() : 0;
}
inline int32 NormalCount() const
{
return HasNormals() ? VertexInstanceNormals.GetNumElements() : 0;
}
/** Get Normal by VertexInstanceID */
FVector3f GetNormal(int32 IDValue) const
{
const FVector3f& InstanceNormal = VertexInstanceNormals[FVertexInstanceID(IDValue)];
return (!bScaleNormals) ? InstanceNormal :
UE::Geometry::Normalized(FVector3f(InstanceNormal.X/BuildScale.X, InstanceNormal.Y/BuildScale.Y, InstanceNormal.Z/BuildScale.Z));
}
/** Get Normals for a given Triangle */
template<typename VectorType>
void GetTriNormals(int32 TriId, VectorType& N0, VectorType& N1, VectorType& N2)
{
N0 = GetNormal(TriangleVertexInstanceIndices[TriId*3]);
N1 = GetNormal(TriangleVertexInstanceIndices[TriId*3+1]);
N2 = GetNormal(TriangleVertexInstanceIndices[TriId*3+2]);
}
inline bool HasUVs(const int32 UVLayer=0) const
{
return VertexInstanceUVs.IsValid() && Mesh && UVLayer >= 0 && UVLayer < Mesh->GetNumUVElementChannels();
}
inline bool IsUV(const int32 UVId) const
{
return HasUVs() && UVId >= 0 && UVId < UVCount();
}
inline int32 MaxUVID() const
{
return HasUVs() ? VertexInstanceUVs.GetNumElements() : 0;
}
inline int32 UVCount() const
{
return HasUVs() ? VertexInstanceUVs.GetNumElements() : 0;
}
/** Get UV by VertexInstanceID for a given UVLayer */
FVector2f GetUV(const int32 IDValue, const int32 UVLayer) const
{
return VertexInstanceUVs.Get(FVertexInstanceID(IDValue), UVLayer);
}
/** Get UVs for a given UVLayer and Triangle */
template<typename VectorType>
void GetTriUVs(const int32 TriId, const int32 UVLayer, VectorType& UV0, VectorType& UV1, VectorType& UV2)
{
UV0 = GetUV(TriangleVertexInstanceIndices[TriId*3], UVLayer);
UV1 = GetUV(TriangleVertexInstanceIndices[TriId*3+1], UVLayer);
UV2 = GetUV(TriangleVertexInstanceIndices[TriId*3+2], UVLayer);
}
};
/**
* Non-const version of the adapter, with non-const storage and setters
* TODO: try to be smarter about sharing code w/ the above const version
*/
struct /*MESHCONVERSION_API*/ FMeshDescriptionEditableTriangleMeshAdapter
{
using FIndex3i = UE::Geometry::FIndex3i;
protected:
FMeshDescription* Mesh;
TVertexAttributesRef<FVector3f> VertexPositions;
TVertexInstanceAttributesRef<FVector3f> VertexInstanceNormals;
public:
FMeshDescriptionEditableTriangleMeshAdapter(FMeshDescription* MeshIn) : Mesh(MeshIn)
{
FStaticMeshAttributes Attributes(*MeshIn);
VertexPositions = Attributes.GetVertexPositions();
VertexInstanceNormals = Attributes.GetVertexInstanceNormals();
}
bool IsTriangle(int32 TID) const
{
return TID >= 0 && TID < Mesh->Triangles().Num();
}
bool IsVertex(int32 VID) const
{
return VID >= 0 && VID < Mesh->Vertices().Num();
}
// ID and Count are the same for MeshDescription because it's compact
int32 MaxTriangleID() const
{
return Mesh->Triangles().Num();
}
int32 TriangleCount() const
{
return Mesh->Triangles().Num();
}
int32 MaxVertexID() const
{
return Mesh->Vertices().Num();
}
int32 VertexCount() const
{
return Mesh->Vertices().Num();
}
uint64 GetChangeStamp() const
{
// MeshDescription doesn't provide any mechanism to know if it's been modified so just return 1
// and leave it to the caller to not build an aabb and then change the underlying mesh
return 1;
}
FIndex3i GetTriangle(int32 IDValue) const
{
TArrayView<const FVertexID> TriVertIDs = Mesh->GetTriangleVertices(FTriangleID(IDValue));
return FIndex3i(TriVertIDs[0].GetValue(), TriVertIDs[1].GetValue(), TriVertIDs[2].GetValue());
}
FVector3d GetVertex(int32 IDValue) const
{
return FVector3d(VertexPositions[FVertexID(IDValue)]);
}
void SetVertex(int32 IDValue, const FVector3d& NewPos)
{
VertexPositions[FVertexID(IDValue)] = (FVector3f)NewPos;
}
inline void GetTriVertices(int32 IDValue, FVector3d& V0, FVector3d& V1, FVector3d& V2) const
{
TArrayView<const FVertexID> TriVertIDs = Mesh->GetTriangleVertices(FTriangleID(IDValue));
V0 = FVector3d(VertexPositions[TriVertIDs[0]]);
V1 = FVector3d(VertexPositions[TriVertIDs[1]]);
V2 = FVector3d(VertexPositions[TriVertIDs[2]]);
}
inline bool HasNormals() const
{
return VertexInstanceNormals.IsValid();
}
inline bool IsNormal(int32 NID) const
{
return HasNormals() && NID >= 0 && NID < NormalCount();
}
inline int32 MaxNormalID() const
{
return HasNormals() ? VertexInstanceNormals.GetNumElements() : 0;
}
inline int32 NormalCount() const
{
return HasNormals() ? VertexInstanceNormals.GetNumElements() : 0;
}
FVector3f GetNormal(int32 IDValue) const
{
return FVector3f(VertexInstanceNormals[FVertexInstanceID(IDValue)]);
}
void SetNormal(int32 IDValue, const FVector3f& Normal)
{
VertexInstanceNormals[FVertexInstanceID(IDValue)] = Normal;
}
};
/**
* TTriangleMeshAdapter version of FMeshDescriptionTriangleMeshAdapter
*/
struct FMeshDescriptionMeshAdapterd : public UE::Geometry::TTriangleMeshAdapter<double>
{
FMeshDescriptionTriangleMeshAdapter ParentAdapter;
FMeshDescriptionMeshAdapterd(const FMeshDescription* MeshIn) : ParentAdapter(MeshIn)
{
IsTriangle = [this](int index) { return ParentAdapter.IsTriangle(index);};
IsVertex = [this](int index) { return ParentAdapter.IsVertex(index); };
MaxTriangleID = [this]() { return ParentAdapter.MaxTriangleID();};
MaxVertexID = [this]() { return ParentAdapter.MaxVertexID();};
TriangleCount = [this]() { return ParentAdapter.TriangleCount();};
VertexCount = [this]() { return ParentAdapter.VertexCount();};
GetChangeStamp = [this]() { return ParentAdapter.GetChangeStamp();};
GetTriangle = [this](int32 TriangleID) { return ParentAdapter.GetTriangle(TriangleID); };
GetVertex = [this](int32 VertexID) { return ParentAdapter.GetVertex(VertexID); };
}
FMeshDescriptionMeshAdapterd(FMeshDescriptionTriangleMeshAdapter ParentAdapterIn) : ParentAdapter(ParentAdapterIn)
{
IsTriangle = [this](int index) { return ParentAdapter.IsTriangle(index);};
IsVertex = [this](int index) { return ParentAdapter.IsVertex(index); };
MaxTriangleID = [this]() { return ParentAdapter.MaxTriangleID();};
MaxVertexID = [this]() { return ParentAdapter.MaxVertexID();};
TriangleCount = [this]() { return ParentAdapter.TriangleCount();};
VertexCount = [this]() { return ParentAdapter.VertexCount();};
GetChangeStamp = [this]() { return ParentAdapter.GetChangeStamp();};
GetTriangle = [this](int32 TriangleID) { return ParentAdapter.GetTriangle(TriangleID); };
GetVertex = [this](int32 VertexID) { return ParentAdapter.GetVertex(VertexID); };
}
};