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

641 lines
20 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "DynamicMesh/DynamicMesh3.h"
#include "DynamicMesh/DynamicMeshAABBTree3.h"
#include "DynamicMesh/DynamicMeshAttributeSet.h"
#include "DynamicMesh/MeshTangents.h"
#include "Image/ImageTile.h"
#include "Image/ImageBuilder.h"
#include "MeshCurvature.h"
#include "MeshMapEvaluator.h"
namespace UE
{
namespace Geometry
{
class IMeshBakerDetailSampler;
/**
* Compute base/detail mesh correspondence by raycast
* @return a pointer to the hit mesh, otherwise nullptr.
*/
const void* GetDetailMeshTrianglePoint_Raycast(
const IMeshBakerDetailSampler* DetailSpatial,
const FVector3d& BasePoint,
const FVector3d& BaseNormal,
int32& DetailTriangleOut,
FVector3d& DetailTriBaryCoords,
double Thickness,
bool bFailToNearestPoint
);
/**
* Compute base/detail mesh correspondence by nearest distance
* @return a pointer to the nearest mesh, otherwise nullptr.
*/
const void* GetDetailMeshTrianglePoint_Nearest(
const IMeshBakerDetailSampler* DetailSpatial,
const FVector3d& BasePoint,
int32& DetailTriangleOut,
FVector3d& DetailTriBaryCoords);
/** Image tile storage for map bakes. */
class FMeshMapTileBuffer
{
public:
FMeshMapTileBuffer(const FImageTile& TileIn, const int32 PixelSizeIn)
: Tile(TileIn)
, PixelSize(PixelSizeIn + 1) // + 1 for accumulated pixel weight.
{
Buffer = static_cast<float*>(FMemory::MallocZeroed(sizeof(float) * PixelSize * Tile.Num()));
}
~FMeshMapTileBuffer()
{
FMemory::Free(Buffer);
}
float& GetPixelWeight(const int64 LinearIdx) const
{
checkSlow(LinearIdx >= 0 && LinearIdx < Tile.Num());
return Buffer[LinearIdx * PixelSize];
}
float* GetPixel(const int64 LinearIdx) const
{
checkSlow(LinearIdx >= 0 && LinearIdx < Tile.Num());
return &Buffer[LinearIdx * PixelSize + 1];
}
const FImageTile& GetTile() const
{
return Tile;
}
private:
const FImageTile Tile;
const int32 PixelSize;
float* Buffer;
};
/**
* Base bake detail sampler class.
*
* This class defines the interface for all sample queries that
* FMeshBaseBaker supports including the spatial queries for
* base to detail correspondence sampling.
*/
class IMeshBakerDetailSampler
{
public:
/** (Image, UVLayer) pair for detail textures */
using FBakeDetailTexture = TTuple<const TImageBuilder<FVector4f>*, int>;
enum class EBakeDetailNormalSpace
{
Tangent,
Object
};
/** (Image, NormalSpace, UVLayer) tuple for detail normal textures */
using FBakeDetailNormalTexture = TTuple<const TImageBuilder<FVector4f>*, int, EBakeDetailNormalSpace>;
virtual ~IMeshBakerDetailSampler() = default;
/** Iterate over each mesh in the detail set. */
virtual void ProcessMeshes(TFunctionRef<void(const void*)> ProcessFn) const = 0;
/** @return the triangle count of a given mesh. */
virtual int32 GetTriangleCount(const void* Mesh) const = 0;
/** Set the tangents for a given mesh */
virtual void SetTangents(const void* Mesh, FMeshTangentsd* Tangents)
{
}
/** Associate a texture map and UV layer index for a given mesh in the detail set */
virtual void SetTextureMap(const void* Mesh, const FBakeDetailTexture& Map) = 0;
/** Associate a normal map and UV layer index for a given mesh in the detail set */
virtual void SetNormalTextureMap(const void* Mesh, const FBakeDetailNormalTexture& Map) = 0;
/** Retrieve a texture map and UV layer index from a given mesh in the detail set */
virtual const FBakeDetailTexture* GetTextureMap(const void* Mesh) const = 0;
virtual const FBakeDetailNormalTexture* GetNormalTextureMap(const void* Mesh) const = 0;
/** @return true if identity correspondence is supported */
virtual bool SupportsIdentityCorrespondence() const = 0;
/** @return true if nearest point to triangle distance correspondence is supported */
virtual bool SupportsNearestPointCorrespondence() const = 0;
/** @return true if triangle ray intersection correspondence is supported */
virtual bool SupportsRaycastCorrespondence() const = 0;
/** @return true if a user-defined correspondence test is supported */
virtual bool SupportsCustomCorrespondence() const
{
return false;
}
virtual void* ComputeCustomCorrespondence(const FMeshUVSampleInfo& SampleInfo, FMeshMapEvaluator::FCorrespondenceSample& ValueOut) const
{
return nullptr;
}
virtual bool IsValidCorrespondence(const FMeshMapEvaluator::FCorrespondenceSample& Sample) const
{
return Sample.DetailMesh && IsTriangle(Sample.DetailMesh, Sample.DetailTriID);
}
/**
* @param Point query point
* @param NearestDistSqrOut [Out] returned nearest squared distance, if triangle is found
* @param TriId [Out] ID of triangle nearest to Point within MaxDistance, or InvalidID if not found
* @param TriBaryCoords [Out] barycentric coordinates of the nearest point on the nearest tri.
* @param Options query options
* @return a pointer to the mesh that holds the nearest triangle
*/
virtual const void* FindNearestTriangle(
const FVector3d& Point,
double& NearestDistSqrOut,
int& TriId,
FVector3d& TriBaryCoords,
const IMeshSpatial::FQueryOptions& Options = IMeshSpatial::FQueryOptions()) const
{
return nullptr;
}
/**
* Find nearest triangle from the given ray
* @param Ray query ray
* @param NearestT [Out] parameter of the nearest hit
* @param TriId [Out] ID of triangle intersected by ray within MaxDistance, or InvalidID if not found
* @param TriBaryCoords [Out] barycentric coordinates of intersection point on the hit tri.
* @param Options query options
* @return a pointer to the mesh whose triangle was intersected, nullptr otherwise.
*/
virtual const void* FindNearestHitTriangle(
const FRay3d& Ray,
double& NearestT,
int& TriId,
FVector3d& TriBaryCoords,
const IMeshSpatial::FQueryOptions& Options = IMeshSpatial::FQueryOptions()) const
{
return nullptr;
}
/**
* Return true if any triangles hit by the given ray
* @param Ray query ray
* @param Options query options
* @return true if any triangles hit by the given ray
*/
virtual bool TestAnyHitTriangle(
const FRay3d& Ray,
const IMeshSpatial::FQueryOptions& Options = IMeshSpatial::FQueryOptions()) const
{
return false;
}
/** @return axis aligned bounding box for the detail mesh(es). */
virtual FAxisAlignedBox3d GetBounds() const = 0;
/**
* @param Mesh pointer to mesh to query
* @param TriId the triangle index to test
* @return true if the given triangle index is valid.
*/
virtual bool IsTriangle(const void* Mesh, const int TriId) const = 0;
/**
* @param Mesh pointer to mesh to query
* @param TriId the triangle index to test
* @return 3 vertex indices of the given triangle.
*/
virtual FIndex3i GetTriangle(const void* Mesh, const int TriId) const = 0;
/**
* @param Mesh pointer to mesh to query
* @param TriId the triangle index to test
* @return the normal for the given triangle.
*/
virtual FVector3d GetTriNormal(const void* Mesh, const int TriId) const = 0;
/**
* @param Mesh pointer to mesh to query
* @param TriId the triangle index to test
* @param UVLayer the UVLayer index to query
* @param UV0 the output UV for triangle vertex 0
* @param UV1 the output UV for triangle vertex 1
* @param UV2 the output UV for triangle vertex 2
* @return true if a valid UV was returned, false otherwise.
*/
virtual bool GetTriUVs(const void* Mesh, int TriId, int UVLayer, FVector2f& UV0, FVector2f& UV1, FVector2f& UV2) const = 0;
/**
* @param Mesh pointer to mesh to query
* @param TriId the triangle index to test
* @return the material ID for the given triangle.
*/
virtual int32 GetMaterialID(const void* Mesh, const int TriId) const = 0;
/**
* @param Mesh pointer to mesh to query
* @param TriId the triangle index to test
* @return the group ID for the given triangle.
*/
virtual int32 GetPolyGroupID(const void* Mesh, const int TriId) const = 0;
/**
* @param Mesh pointer to mesh to query
* @return true if this mesh has normals
*/
virtual bool HasNormals(const void* Mesh) const = 0;
/**
* @param Mesh pointer to mesh to query
* @param UVLayer the UV layer to check for existence
* @return true if this mesh has UVs on the given layer
*/
virtual bool HasUVs(const void* Mesh, int UVLayer = 0) const = 0;
/**
* @param Mesh pointer to mesh to query
* @return true if this mesh has tangents
*/
virtual bool HasTangents(const void* Mesh) const = 0;
/**
* @param Mesh pointer to mesh to query
* @return true if this mesh has vertex colors on the given layer
*/
virtual bool HasColors(const void* Mesh) const = 0;
/**
* Compute interpolated Position value inside triangle using barycentric coordinates
* @param Mesh pointer to mesh to query
* @param TriId index of triangle
* @param BaryCoords 3 barycentric coordinates inside triangle
* @return resulting interpolated Position parameter value
*/
virtual FVector3d TriBaryInterpolatePoint(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords) const = 0;
/**
* Compute interpolated Normal value inside triangle using barycentric coordinates
* @param Mesh pointer to mesh to query
* @param TriId index of triangle
* @param BaryCoords 3 barycentric coordinates inside triangle
* @param NormalOut resulting interpolated Normal parameter value
* @return true if a valid normal was returned, false otherwise
*/
virtual bool TriBaryInterpolateNormal(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords,
FVector3f& NormalOut) const = 0;
/**
* Compute interpolated UV value inside triangle using barycentric coordinates
* @param Mesh pointer to mesh to query
* @param TriId index of triangle
* @param BaryCoords 3 barycentric coordinates inside triangle
* @param UVLayer UV layer to query
* @param UVOut resulting interpolated UV parameter value
* @return true if a valid UV was returned, false otherwise
*/
virtual bool TriBaryInterpolateUV(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords,
const int UVLayer,
FVector2f& UVOut ) const = 0;
/**
* Compute interpolated vertex color value inside triangle using barycentric coordinates
* @param Mesh pointer to mesh to query
* @param TriId index of triangle
* @param BaryCoords 3 barycentric coordinates inside triangle
* @param ColorOut resulting interpolated vertex color parameter value
* @return true if a valid vertex color was returned, false otherwise
*/
virtual bool TriBaryInterpolateColor(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords,
FVector4f& ColorOut) const = 0;
/**
* Compute interpolated tangent values inside triangle using barycentric coordinates
* @param Mesh pointer to mesh to query
* @param TriId index of triangle
* @param BaryCoords 3 barycentric coordinates inside triangle
* @param TangentX resulting interpolated tangentX value
* @param TangentY resulting interpolated tangentY value
* @return true if valid tangents were returned, false otherwise
*/
virtual bool TriBaryInterpolateTangents(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords,
FVector3d& TangentX,
FVector3d& TangentY) const = 0;
// TODO: Rework the curvature interface to only compute the minimal set of work.
/**
* @param Mesh pointer to mesh to query
* @param CurvatureCache [Out] the curvature data to populate
*/
virtual void GetCurvature(
const void* Mesh,
FMeshVertexCurvatureCache& CurvatureCache) const = 0;
};
/**
* DynamicMesh bake detail sampler for baking 1 detail mesh to 1 target mesh.
*/
class FMeshBakerDynamicMeshSampler : public IMeshBakerDetailSampler
{
public:
UE_NONCOPYABLE(FMeshBakerDynamicMeshSampler)
FMeshBakerDynamicMeshSampler(const FDynamicMesh3* Mesh, const FDynamicMeshAABBTree3* Spatial, const FMeshTangentsd* Tangents = nullptr)
: DetailMesh(Mesh), DetailSpatial(Spatial), DetailTangents(Tangents)
{
}
virtual void ProcessMeshes(TFunctionRef<void(const void*)> ProcessFn) const override
{
ProcessFn(DetailMesh);
}
virtual int32 GetTriangleCount(const void* Mesh) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
return DynamicMesh->TriangleCount();
}
virtual void SetTangents(const void* Mesh, FMeshTangentsd* Tangents) override
{
DetailTangents = Tangents;
}
virtual void SetTextureMap(const void* Mesh, const FBakeDetailTexture& Map) override
{
DetailTextureMap = Map;
}
virtual void SetNormalTextureMap(const void* Mesh, const FBakeDetailNormalTexture& Map) override
{
DetailNormalTextureMap = Map;
}
virtual const FBakeDetailTexture* GetTextureMap(const void* Mesh) const override
{
return &DetailTextureMap;
}
virtual const FBakeDetailNormalTexture* GetNormalTextureMap(const void* Mesh) const override
{
return &DetailNormalTextureMap;
}
virtual bool SupportsIdentityCorrespondence() const override
{
return true;
}
virtual bool SupportsNearestPointCorrespondence() const override
{
return true;
}
virtual bool SupportsRaycastCorrespondence() const override
{
return true;
}
virtual const void* FindNearestTriangle(
const FVector3d& Point,
double& NearestDistSqrOut,
int& TriId,
FVector3d& TriBaryCoords,
const IMeshSpatial::FQueryOptions& Options = IMeshSpatial::FQueryOptions()) const override
{
TriId = DetailSpatial->FindNearestTriangle(Point, NearestDistSqrOut, Options);
if (DetailMesh->IsTriangle(TriId))
{
const FDistPoint3Triangle3d DistQuery = TMeshQueries<FDynamicMesh3>::TriangleDistance(*DetailMesh, TriId, Point);
TriBaryCoords = DistQuery.TriangleBaryCoords;
}
return DetailMesh;
}
virtual const void* FindNearestHitTriangle(
const FRay3d& Ray,
double& NearestT,
int& TriId,
FVector3d& TriBaryCoords,
const IMeshSpatial::FQueryOptions& Options = IMeshSpatial::FQueryOptions()) const override
{
const bool bHit = DetailSpatial->FindNearestHitTriangle(Ray, NearestT, TriId, TriBaryCoords, Options);
return bHit ? DetailMesh : nullptr;
}
virtual bool TestAnyHitTriangle(
const FRay3d& Ray,
const IMeshSpatial::FQueryOptions& Options = IMeshSpatial::FQueryOptions()) const override
{
return DetailSpatial->TestAnyHitTriangle(Ray, Options);
}
virtual FAxisAlignedBox3d GetBounds() const override
{
return DetailMesh->GetBounds();
}
virtual bool IsTriangle(const void* Mesh, const int TriId) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
return DynamicMesh->IsTriangle(TriId);
}
virtual FIndex3i GetTriangle(const void* Mesh, const int TriId) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
return DynamicMesh->GetTriangle(TriId);
}
virtual FVector3d GetTriNormal(const void* Mesh, const int TriId) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
return DynamicMesh->GetTriNormal(TriId);
}
virtual bool GetTriUVs(const void* Mesh, int TriId, int UVLayer, FVector2f& UV0, FVector2f& UV1, FVector2f& UV2) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
if (const FDynamicMeshAttributeSet* Attributes = DynamicMesh->Attributes())
{
if (const FDynamicMeshUVOverlay* UVOverlay = Attributes->GetUVLayer(UVLayer))
{
if (UVOverlay->IsSetTriangle(TriId))
{
UVOverlay->GetTriElements(TriId, UV0, UV1, UV2);
return true;
}
}
}
return false;
}
virtual int32 GetMaterialID(const void* Mesh, const int TriId) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
const FDynamicMeshMaterialAttribute* MaterialIDOverlay = DynamicMesh->Attributes()->GetMaterialID();
return MaterialIDOverlay ? MaterialIDOverlay->GetValue(TriId) : IndexConstants::InvalidID;
}
virtual int32 GetPolyGroupID(const void* Mesh, const int TriID) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
return DynamicMesh->GetTriangleGroup(TriID);
}
virtual bool HasNormals(const void* Mesh) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
return DynamicMesh && DynamicMesh->HasAttributes() && DynamicMesh->Attributes()->PrimaryNormals();
}
virtual bool HasUVs(const void* Mesh, const int UVLayer = 0) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
return DynamicMesh && DynamicMesh->HasAttributes() && DynamicMesh->Attributes()->GetUVLayer(UVLayer);
}
virtual bool HasTangents(const void* Mesh) const override
{
return DetailTangents != nullptr;
}
virtual bool HasColors(const void* Mesh) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
return DynamicMesh && DynamicMesh->HasAttributes() && DynamicMesh->Attributes()->PrimaryColors();
}
virtual FVector3d TriBaryInterpolatePoint(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
return DynamicMesh->GetTriBaryPoint(TriId, BaryCoords.X, BaryCoords.Y, BaryCoords.Z);
}
virtual bool TriBaryInterpolateNormal(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords,
FVector3f& NormalOut) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
const FDynamicMeshNormalOverlay* NormalOverlay = DynamicMesh->Attributes()->PrimaryNormals();
const bool bValid = NormalOverlay && NormalOverlay->IsSetTriangle(TriId);
if (bValid)
{
FVector3d Normal;
NormalOverlay->GetTriBaryInterpolate(TriId, &BaryCoords.X, &Normal.X);
NormalOut = FVector3f(Normal);
}
return bValid;
}
virtual bool TriBaryInterpolateUV(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords,
const int UVLayer,
FVector2f& UVOut ) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
const FDynamicMeshUVOverlay* UVOverlay = DynamicMesh->Attributes()->GetUVLayer(UVLayer);
const bool bValid = UVOverlay && UVOverlay->IsSetTriangle(TriId);
if (bValid)
{
FVector2d UV;
UVOverlay->GetTriBaryInterpolate(TriId, &BaryCoords.X, &UV.X);
UVOut = FVector2f(UV);
}
return bValid;
}
virtual bool TriBaryInterpolateColor(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords,
FVector4f& ColorOut) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
const FDynamicMeshColorOverlay* ColorOverlay = DynamicMesh->Attributes()->PrimaryColors();
const bool bValid = ColorOverlay && ColorOverlay->IsSetTriangle(TriId);
if (bValid)
{
FVector4d Color;
ColorOverlay->GetTriBaryInterpolate(TriId, &BaryCoords.X, &Color.X);
ColorOut = FVector4f(Color);
}
return bValid;
}
virtual bool TriBaryInterpolateTangents(
const void* Mesh,
const int32 TriId,
const FVector3d& BaryCoords,
FVector3d& TangentX,
FVector3d& TangentY) const override
{
// TODO: Can we use the tangent overlay here for a unified interface? (PrimaryTangents/PrimaryBiTangents)
bool bSuccess = false;
if (DetailTangents)
{
DetailTangents->GetInterpolatedTriangleTangent(TriId, BaryCoords, TangentX, TangentY);
bSuccess = true;
}
return bSuccess;
}
virtual void GetCurvature(
const void* Mesh,
FMeshVertexCurvatureCache& CurvatureCache) const override
{
const FDynamicMesh3* DynamicMesh = static_cast<const FDynamicMesh3*>(Mesh);
CurvatureCache.BuildAll(*DynamicMesh);
ensure(CurvatureCache.Num() == DetailMesh->MaxVertexID());
}
protected:
const FDynamicMesh3* DetailMesh = nullptr;
const FDynamicMeshAABBTree3* DetailSpatial = nullptr;
const FMeshTangentsd* DetailTangents = nullptr;
FBakeDetailTexture DetailTextureMap = FBakeDetailTexture(nullptr, 0);
FBakeDetailNormalTexture DetailNormalTextureMap = FBakeDetailNormalTexture(nullptr, 0, EBakeDetailNormalSpace::Tangent);
};
} // end namespace UE::Geometry
} // end namespace UE