// Copyright 2016 Chris Conway (Koderz). All Rights Reserved. #pragma once #include "RuntimeMeshBuilder.h" /** * */ class TessellationUtilities { public: static void CalculateTessellationIndices(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices, FRuntimeMeshIndicesBuilder* TessellationIndices); private: struct Vertex { FVector Position; FVector2D TexCoord; Vertex() { } Vertex(const FVector& InPosition, const FVector2D& InTexCoord) : Position(InPosition), TexCoord(InTexCoord) { } FORCEINLINE bool operator==(const Vertex& Other) const { return Position == Other.Position; } FORCEINLINE bool operator<(const Vertex& Other) const { return Position.X < Other.Position.X || Position.Y < Other.Position.Y || Position.Z < Other.Position.Z; } }; static FORCEINLINE uint32 HashValue(const FVector& Vec) { return 31337 * GetTypeHash(Vec.X) + 13 * GetTypeHash(Vec.Y) + 3 * GetTypeHash(Vec.Z); } static FORCEINLINE uint32 HashValue(const Vertex& Vert) { return HashValue(Vert.Position); } struct Edge { private: uint32 IndexFrom; uint32 IndexTo; Vertex VertexFrom; Vertex VertexTo; uint32 CachedHash; public: Edge() : CachedHash(0) { } Edge(uint32 InIndexFrom, uint32 InIndexTo, const Vertex& InVertexFrom, const Vertex& InVertexTo) : IndexFrom(InIndexFrom), IndexTo(InIndexTo), VertexFrom(InVertexFrom), VertexTo(InVertexTo) { // Hash should only consider position, not index. // We want values with different indices to compare true. CachedHash = 7 * HashValue(VertexFrom) + 2 * HashValue(VertexTo); } Vertex GetVertex(uint32 I) const { switch (I) { case 0: return VertexFrom; case 1: return VertexTo; default: checkNoEntry(); return Vertex(); } } uint32 GetIndex(uint32 I) const { switch (I) { case 0: return IndexFrom; case 1: return IndexTo; default: checkNoEntry(); return 0; } } Edge GetReverse() const { return Edge(IndexTo, IndexFrom, VertexTo, VertexFrom); } FORCEINLINE bool operator<(const Edge& Other) const { // Quick out, otherwise we have to compare vertices if (IndexFrom == Other.IndexFrom && IndexTo == Other.IndexTo) { return false; } return VertexFrom < Other.VertexFrom || VertexTo < Other.VertexTo; } FORCEINLINE bool operator==(const Edge& Other) const { return (IndexFrom == Other.IndexFrom && IndexTo == Other.IndexTo) || (VertexFrom == Other.VertexFrom && VertexTo == Other.VertexTo); } friend FORCEINLINE uint32 GetTypeHash(const Edge& E) { return E.CachedHash; } }; struct Corner { uint32 Index; FVector2D TexCoord; Corner() : Index(0) { } Corner(uint32 InIndex, FVector2D InTexCoord) : Index(InIndex), TexCoord(InTexCoord) { } }; class Triangle { Edge Edge0; Edge Edge1; Edge Edge2; public: Triangle(uint32 Index0, uint32 Index1, uint32 Index2, const Vertex& Vertex0, const Vertex& Vertex1, const Vertex& Vertex2) : Edge0(Index0, Index1, Vertex0, Vertex1) , Edge1(Index1, Index2, Vertex1, Vertex2) , Edge2(Index2, Index0, Vertex2, Vertex0) { } FORCEINLINE bool operator<(const Triangle& Other) const { return Edge0 < Other.Edge0 || Edge1 < Other.Edge1 || Edge2 < Other.Edge2; } FORCEINLINE const Edge& GetEdge(uint32 I) { return ((Edge*)&Edge0)[I]; } FORCEINLINE uint32 GetIndex(uint32 I) { return GetEdge(I).GetIndex(0); } }; using EdgeDictionary = TMap; using PositionDictionary = TMap; static void AddIfLeastUV(PositionDictionary& PosDict, const Vertex& Vert, uint32 Index); static void ReplacePlaceholderIndices(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices, EdgeDictionary& EdgeDict, PositionDictionary& PosDict, FRuntimeMeshIndicesBuilder* OutIndices); static void ExpandIB(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices, EdgeDictionary& OutEdgeDict, PositionDictionary& OutPosDict, FRuntimeMeshIndicesBuilder* OutIndices); };