// 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(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*, int>; enum class EBakeDetailNormalSpace { Tangent, Object }; /** (Image, NormalSpace, UVLayer) tuple for detail normal textures */ using FBakeDetailNormalTexture = TTuple*, int, EBakeDetailNormalSpace>; virtual ~IMeshBakerDetailSampler() = default; /** Iterate over each mesh in the detail set. */ virtual void ProcessMeshes(TFunctionRef 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 ProcessFn) const override { ProcessFn(DetailMesh); } virtual int32 GetTriangleCount(const void* Mesh) const override { const FDynamicMesh3* DynamicMesh = static_cast(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::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(Mesh); return DynamicMesh->IsTriangle(TriId); } virtual FIndex3i GetTriangle(const void* Mesh, const int TriId) const override { const FDynamicMesh3* DynamicMesh = static_cast(Mesh); return DynamicMesh->GetTriangle(TriId); } virtual FVector3d GetTriNormal(const void* Mesh, const int TriId) const override { const FDynamicMesh3* DynamicMesh = static_cast(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(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(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(Mesh); return DynamicMesh->GetTriangleGroup(TriID); } virtual bool HasNormals(const void* Mesh) const override { const FDynamicMesh3* DynamicMesh = static_cast(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(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(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(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(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(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(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(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