Fix mesh tangent and bitangent vectors generation with OpenFBX backend by using MikkTSpace
This commit is contained in:
@@ -11,6 +11,8 @@
|
||||
#include "Engine/Tools/ModelTool/ModelTool.h"
|
||||
#include "Engine/Tools/ModelTool/VertexTriangleAdjacency.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#define USE_MIKKTSPACE 1
|
||||
#include "ThirdParty/MikkTSpace/mikktspace.h"
|
||||
#if USE_ASSIMP
|
||||
#define USE_SPARIAL_SORT 1
|
||||
#define ASSIMP_BUILD_NO_EXPORT
|
||||
@@ -279,6 +281,7 @@ void MeshData::BuildIndexBuffer()
|
||||
REMAP_BUFFER(UVs);
|
||||
REMAP_BUFFER(Normals);
|
||||
REMAP_BUFFER(Tangents);
|
||||
REMAP_BUFFER(BitangentSigns);
|
||||
REMAP_BUFFER(LightmapUVs);
|
||||
REMAP_BUFFER(Colors);
|
||||
REMAP_BUFFER(BlendIndices);
|
||||
@@ -445,6 +448,56 @@ bool MeshData::GenerateNormals(float smoothingAngle)
|
||||
return false;
|
||||
}
|
||||
|
||||
#if USE_MIKKTSPACE
|
||||
namespace
|
||||
{
|
||||
int GetNumFaces(const SMikkTSpaceContext* pContext)
|
||||
{
|
||||
const auto meshData = (MeshData*)pContext->m_pUserData;
|
||||
return meshData->Indices.Count() / 3;
|
||||
}
|
||||
|
||||
int GetNumVerticesOfFace(const SMikkTSpaceContext* pContext, const int iFace)
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
void GetPosition(const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert)
|
||||
{
|
||||
const auto meshData = (MeshData*)pContext->m_pUserData;
|
||||
const auto e = meshData->Positions[meshData->Indices[iFace * 3 + iVert]];
|
||||
fvPosOut[0] = e.X;
|
||||
fvPosOut[1] = e.Y;
|
||||
fvPosOut[2] = e.Z;
|
||||
}
|
||||
|
||||
void GetNormal(const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert)
|
||||
{
|
||||
const auto meshData = (MeshData*)pContext->m_pUserData;
|
||||
const auto e = meshData->Normals[meshData->Indices[iFace * 3 + iVert]];
|
||||
fvNormOut[0] = e.X;
|
||||
fvNormOut[1] = e.Y;
|
||||
fvNormOut[2] = e.Z;
|
||||
}
|
||||
|
||||
void GetTexCoord(const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert)
|
||||
{
|
||||
const auto meshData = (MeshData*)pContext->m_pUserData;
|
||||
const auto e = meshData->UVs[meshData->Indices[iFace * 3 + iVert]];
|
||||
fvTexcOut[0] = e.X;
|
||||
fvTexcOut[1] = e.Y;
|
||||
}
|
||||
|
||||
void SetTSpaceBasic(const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert)
|
||||
{
|
||||
const auto meshData = (MeshData*)pContext->m_pUserData;
|
||||
const auto v = meshData->Indices[iFace * 3 + iVert];
|
||||
meshData->Tangents[v] = Vector3(fvTangent);
|
||||
meshData->BitangentSigns[v] = fSign;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
{
|
||||
if (Positions.IsEmpty() || Indices.IsEmpty())
|
||||
@@ -459,16 +512,25 @@ bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
}
|
||||
|
||||
const auto startTime = Platform::GetTimeSeconds();
|
||||
|
||||
const int32 vertexCount = Positions.Count();
|
||||
const int32 indexCount = Indices.Count();
|
||||
Tangents.Resize(vertexCount, false);
|
||||
smoothingAngle = Math::Clamp(smoothingAngle, 0.0f, 45.0f);
|
||||
|
||||
// Note: this assumes that mesh is in a verbose format
|
||||
// where each triangle has its own set of vertices
|
||||
// and no vertices are shared between triangles (dummy index buffer).
|
||||
|
||||
#if USE_MIKKTSPACE
|
||||
SMikkTSpaceInterface callbacks = {
|
||||
GetNumFaces,
|
||||
GetNumVerticesOfFace,
|
||||
GetPosition,
|
||||
GetNormal,
|
||||
GetTexCoord,
|
||||
SetTSpaceBasic,
|
||||
nullptr
|
||||
};
|
||||
const SMikkTSpaceContext context = { &callbacks, this };
|
||||
BitangentSigns.Resize(vertexCount, false);
|
||||
genTangSpace(&context, 180.0f - smoothingAngle);
|
||||
#else
|
||||
const float angleEpsilon = 0.9999f;
|
||||
BitArray<> vertexDone;
|
||||
vertexDone.Resize(vertexCount);
|
||||
@@ -478,36 +540,35 @@ bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
const Vector2* meshTex = UVs.Get();
|
||||
Vector3* meshTang = Tangents.Get();
|
||||
|
||||
// Calculate the tangent for every face
|
||||
// Calculate the tangent per-triangle
|
||||
Vector3 min, max;
|
||||
min = max = Positions[0];
|
||||
min = max = Positions[Indices[0]];
|
||||
for (int32 i = 0; i < indexCount; i += 3)
|
||||
{
|
||||
const int32 p0 = Indices[i + 0], p1 = Indices[i + 1], p2 = Indices[i + 2];
|
||||
|
||||
const Vector3 v1 = Positions[p0];
|
||||
const Vector3 v2 = Positions[p1];
|
||||
const Vector3 v3 = Positions[p2];
|
||||
const Vector3 v0 = Positions[p0];
|
||||
const Vector3 v1 = Positions[p1];
|
||||
const Vector3 v2 = Positions[p2];
|
||||
|
||||
Vector3::Min(min, v0, min);
|
||||
Vector3::Min(min, v1, min);
|
||||
Vector3::Min(min, v2, min);
|
||||
Vector3::Min(min, v3, min);
|
||||
|
||||
Vector3::Max(max, v0, max);
|
||||
Vector3::Max(max, v1, max);
|
||||
Vector3::Max(max, v2, max);
|
||||
Vector3::Max(max, v3, max);
|
||||
|
||||
// Position differences p1->p2 and p1->p3
|
||||
Vector3 v = v2 - v1, w = v3 - v1;
|
||||
Vector3 v = v1 - v0, w = v2 - v0;
|
||||
|
||||
// Texture offset p1->p2 and p1->p3
|
||||
float sx = meshTex[p1].X - meshTex[p0].X, sy = meshTex[p1].Y - meshTex[p0].Y;
|
||||
float tx = meshTex[p2].X - meshTex[p0].X, ty = meshTex[p2].Y - meshTex[p0].Y;
|
||||
const float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
|
||||
|
||||
// When t1, t2, t3 in same position in UV space, just use default UV direction
|
||||
const float dir = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
|
||||
if (sx * ty == sy * tx)
|
||||
{
|
||||
// Use default UV direction for invalid case
|
||||
sx = 0.0;
|
||||
sy = 1.0;
|
||||
tx = 1.0;
|
||||
@@ -517,19 +578,19 @@ bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
// Tangent points in the direction where to positive X axis of the texture coord's would point in model space
|
||||
// Bitangent's points along the positive Y axis of the texture coord's, respectively
|
||||
Vector3 tangent, bitangent;
|
||||
tangent.X = (w.X * sy - v.X * ty) * dirCorrection;
|
||||
tangent.Y = (w.Y * sy - v.Y * ty) * dirCorrection;
|
||||
tangent.Z = (w.Z * sy - v.Z * ty) * dirCorrection;
|
||||
bitangent.X = (w.X * sx - v.X * tx) * dirCorrection;
|
||||
bitangent.Y = (w.Y * sx - v.Y * tx) * dirCorrection;
|
||||
bitangent.Z = (w.Z * sx - v.Z * tx) * dirCorrection;
|
||||
tangent.X = (w.X * sy - v.X * ty) * dir;
|
||||
tangent.Y = (w.Y * sy - v.Y * ty) * dir;
|
||||
tangent.Z = (w.Z * sy - v.Z * ty) * dir;
|
||||
bitangent.X = (w.X * sx - v.X * tx) * dir;
|
||||
bitangent.Y = (w.Y * sx - v.Y * tx) * dir;
|
||||
bitangent.Z = (w.Z * sx - v.Z * tx) * dir;
|
||||
|
||||
// Store for every vertex of that face
|
||||
// Set tangent frame for every vertex in that triangle
|
||||
for (int32 b = 0; b < 3; b++)
|
||||
{
|
||||
const int32 p = Indices[i + b];
|
||||
|
||||
// Project tangent and bitangent into the plane formed by the vertex' normal
|
||||
// Project tangent and bitangent into the plane formed by the normal
|
||||
Vector3 localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
|
||||
Vector3 localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
|
||||
localTangent.Normalize();
|
||||
@@ -601,10 +662,11 @@ bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
}
|
||||
|
||||
// Smooth the tangents of all vertices that were found to be close enough
|
||||
Vector3 smoothTangent(0, 0, 0);
|
||||
Vector3 smoothTangent(0, 0, 0), smoothBitangent(0, 0, 0);
|
||||
for (int32 b = 0; b < closeVertices.Count(); b++)
|
||||
{
|
||||
smoothTangent += meshTang[closeVertices[b]];
|
||||
auto p = closeVertices[b];
|
||||
smoothTangent += meshTang[p];
|
||||
}
|
||||
smoothTangent.Normalize();
|
||||
|
||||
@@ -614,10 +676,10 @@ bool MeshData::GenerateTangents(float smoothingAngle)
|
||||
meshTang[closeVertices[b]] = smoothTangent;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto endTime = Platform::GetTimeSeconds();
|
||||
LOG(Info, "Generated tangents for mesh in {0}s ({1} vertices, {2} indices)", Utilities::RoundTo2DecimalPlaces(endTime - startTime), vertexCount, indexCount);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -646,14 +708,13 @@ void MeshData::ImproveCacheLocality()
|
||||
Platform::MemoryClear(piCachingStamps, vertexCount * sizeof(uint32));
|
||||
|
||||
// Allocate an empty output index buffer. We store the output indices in one large array.
|
||||
// Since the number of triangles won't change the input faces can be reused. This is how
|
||||
// We save thousands of redundant mini allocations for aiFace::mIndices
|
||||
// Since the number of triangles won't change the input triangles can be reused. This is how
|
||||
const uint32 iIdxCnt = indexCount;
|
||||
uint32* const piIBOutput = NewArray<uint32>(iIdxCnt);
|
||||
uint32* piCSIter = piIBOutput;
|
||||
|
||||
// Allocate the flag array to hold the information
|
||||
// Whether a face has already been emitted or not
|
||||
// Whether a triangle has already been emitted or not
|
||||
std::vector<bool> abEmitted(indexCount / 3, false);
|
||||
|
||||
// Dead-end vertex index stack
|
||||
|
||||
Reference in New Issue
Block a user