chore: calculate tangents for xmodel vertices

This commit is contained in:
Jan 2024-09-05 19:11:08 +02:00
parent 26f4640194
commit db5e53e60d
No known key found for this signature in database
GPG Key ID: 44B581F78FF5C57C
3 changed files with 329 additions and 17 deletions

View File

@ -15,6 +15,8 @@
#include <Eigen>
#pragma warning(pop)
#include "XModel/Tangentspace.h"
#include <algorithm>
#include <filesystem>
#include <format>
@ -136,6 +138,57 @@ namespace
std::unordered_map<std::string, unsigned> m_part_classifications;
};
class TangentData
{
public:
void CreateTangentData(const XModelCommon& common)
{
if (common.m_vertices.empty())
return;
const auto vertexCount = common.m_vertices.size();
m_tangents.resize(vertexCount);
m_binormals.resize(vertexCount);
auto triCount = 0u;
for (const auto& object : common.m_objects)
triCount += object.m_faces.size();
std::vector<uint16_t> indices(triCount * 3u);
auto triOffset = 0u;
for (const auto& object : common.m_objects)
{
for (const auto& face : object.m_faces)
{
indices[triOffset++] = static_cast<uint16_t>(face.vertexIndex[0]);
indices[triOffset++] = static_cast<uint16_t>(face.vertexIndex[1]);
indices[triOffset++] = static_cast<uint16_t>(face.vertexIndex[2]);
}
}
const auto& firstVertex = common.m_vertices[0];
tangent_space::VertexData vertexData{
firstVertex.coordinates,
sizeof(XModelVertex),
firstVertex.normal,
sizeof(XModelVertex),
firstVertex.uv,
sizeof(XModelVertex),
m_tangents.data(),
sizeof(float) * 3,
m_binormals.data(),
sizeof(float) * 3,
indices.data(),
};
tangent_space::CalculateTangentSpace(vertexData, triCount, vertexCount);
}
std::vector<std::array<float, 3>> m_tangents;
std::vector<std::array<float, 3>> m_binormals;
};
class JsonLoader
{
public:
@ -401,18 +454,19 @@ namespace
return true;
}
static void CreateVertex(GfxPackedVertex& vertex, const XModelVertex& commonVertex)
static void
CreateVertex(GfxPackedVertex& vertex, const XModelVertex& commonVertex, const std::array<float, 3>& tangent, const std::array<float, 3>& binormal)
{
constexpr float wrongTangent[]{1, 0, 0};
float tangent_[]{tangent[0], tangent[1], tangent[2]};
vertex.xyz.x = commonVertex.coordinates[0];
vertex.xyz.y = commonVertex.coordinates[1];
vertex.xyz.z = commonVertex.coordinates[2];
vertex.binormalSign = 1.0f; // TODO: Fill with actual value
vertex.binormalSign = binormal[0] > 0.0f ? 1.0f : -1.0f;
vertex.color = Common::Vec4PackGfxColor(commonVertex.color);
vertex.texCoord = Common::Vec2PackTexCoords(commonVertex.uv);
vertex.normal = Common::Vec3PackUnitVec(commonVertex.normal);
vertex.tangent = Common::Vec3PackUnitVec(wrongTangent); // TODO: Fill with actual value
vertex.tangent = Common::Vec3PackUnitVec(tangent_);
}
static size_t GetRigidBoneForVertex(const size_t vertexIndex, const XModelCommon& common)
@ -577,7 +631,8 @@ namespace
vertexIndices = std::move(reorderLookup);
}
bool CreateXSurface(XSurface& surface, const XModelObject& commonObject, const XModelCommon& common, unsigned& vertexOffset)
bool CreateXSurface(
XSurface& surface, const XModelObject& commonObject, const XModelCommon& common, const TangentData& tangentData, unsigned& vertexOffset)
{
std::vector<size_t> xmodelToCommonVertexIndexLookup;
std::unordered_map<size_t, size_t> usedVertices;
@ -616,8 +671,9 @@ namespace
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
{
const auto& commonVertex = common.m_vertices[xmodelToCommonVertexIndexLookup[vertexIndex]];
CreateVertex(surface.verts0[vertexIndex], commonVertex);
const auto commonVertexIndex = xmodelToCommonVertexIndexLookup[vertexIndex];
const auto& commonVertex = common.m_vertices[commonVertexIndex];
CreateVertex(surface.verts0[vertexIndex], commonVertex, tangentData.m_binormals[commonVertexIndex], tangentData.m_binormals[commonVertexIndex]);
}
if (!common.m_bone_weight_data.weights.empty())
@ -682,17 +738,20 @@ namespace
}
auto vertexOffset = 0u;
const auto surfaceCreationSuccessful = std::ranges::all_of(common->m_objects,
[this, &common, &materialAssets, &vertexOffset](const XModelObject& commonObject)
{
XSurface surface{};
if (!CreateXSurface(surface, commonObject, *common, vertexOffset))
return false;
TangentData tangentData;
tangentData.CreateTangentData(*common);
const auto surfaceCreationSuccessful =
std::ranges::all_of(common->m_objects,
[this, &common, &materialAssets, &tangentData, &vertexOffset](const XModelObject& commonObject)
{
XSurface surface{};
if (!CreateXSurface(surface, commonObject, *common, tangentData, vertexOffset))
return false;
m_surfaces.emplace_back(surface);
m_materials.push_back(materialAssets[commonObject.materialIndex]);
return true;
});
m_surfaces.emplace_back(surface);
m_materials.push_back(materialAssets[commonObject.materialIndex]);
return true;
});
if (!surfaceCreationSuccessful)
return false;

View File

@ -0,0 +1,231 @@
#include "Tangentspace.h"
#include <cassert>
#include <cmath>
#include <numbers>
namespace tangent_space
{
constexpr float NULL_VEC3[3]{0, 0, 0};
typedef float tvec2[2];
typedef float tvec3[3];
const tvec2& GetVec2(const void* dest, const size_t index, const size_t stride)
{
return *reinterpret_cast<const tvec2*>(static_cast<const char*>(dest) + stride * index);
}
const tvec3& GetVec3(const void* dest, const size_t index, const size_t stride)
{
return *reinterpret_cast<const tvec3*>(static_cast<const char*>(dest) + stride * index);
}
tvec3& GetVec3(void* dest, const size_t index, const size_t stride)
{
return *reinterpret_cast<tvec3*>(static_cast<char*>(dest) + stride * index);
}
void SetVec3(void* dest, const size_t index, const size_t stride, const tvec3& data)
{
auto* out = reinterpret_cast<float(*)[3]>(static_cast<char*>(dest) + stride * index);
(*out)[0] = data[0];
(*out)[1] = data[1];
(*out)[2] = data[2];
}
bool Vec3_IsNormalized(const tvec3& a1)
{
const auto len = a1[0] * a1[0] + a1[1] * a1[1] + a1[2] * a1[2];
const auto error = fabs(len - 1.0f);
return error < 0.0020000001;
}
float Vec3_Normalize(tvec3& vector)
{
float length = std::sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
if (-length >= 0.0f)
length = 1.0f;
const auto lengthInv = 1.0f / length;
vector[0] = lengthInv * vector[0];
vector[1] = lengthInv * vector[1];
vector[2] = lengthInv * vector[2];
return length;
}
void Vec3_Cross(const tvec3& v0, const tvec3& v1, tvec3& cross)
{
cross[0] = v0[1] * v1[2] - v0[2] * v1[1];
cross[1] = v0[2] * v1[0] - v0[0] * v1[2];
cross[2] = v0[0] * v1[1] - v1[0] * v0[1];
}
float AngleBetweenOriginVectors(const tvec3& a1, const tvec3& a2)
{
const auto v4 = a1[0] * a2[0] + a1[1] * a2[1] + a1[2] * a2[2];
if (v4 <= -1.0)
return -std::numbers::pi_v<float>;
if (v4 >= 1.0)
return std::numbers::pi_v<float>;
return acos(v4);
}
void sub_10022E80(const VertexData& vertexData, const uint16_t i0, const uint16_t i1, const uint16_t i2, tvec3& outVector, tvec3& outCross)
{
const auto& i0_uv = GetVec2(vertexData.uvData, i0, vertexData.uvDataStride);
const auto& i1_uv = GetVec2(vertexData.uvData, i1, vertexData.uvDataStride);
const auto& i2_uv = GetVec2(vertexData.uvData, i2, vertexData.uvDataStride);
const auto& i0_position = GetVec3(vertexData.positionData, i0, vertexData.positionDataStride);
const auto& i1_position = GetVec3(vertexData.positionData, i1, vertexData.positionDataStride);
const auto& i2_position = GetVec3(vertexData.positionData, i2, vertexData.positionDataStride);
const auto uv0_1m0 = i1_uv[0] - i0_uv[0];
const auto uv0_2m0 = i2_uv[0] - i0_uv[0];
const auto uv1_1m0 = i1_uv[1] - i0_uv[1];
const auto uv1_2m0 = i2_uv[1] - i0_uv[1];
const auto p0_1m0 = i1_position[0] - i0_position[0];
const auto p0_2m0 = i2_position[0] - i0_position[0];
const auto p1_1m0 = i1_position[1] - i0_position[1];
const auto p1_2m0 = i2_position[1] - i0_position[1];
const auto p2_1m0 = i1_position[2] - i0_position[2];
const auto p2_2m0 = i2_position[2] - i0_position[2];
if (uv1_2m0 * uv0_1m0 >= uv1_1m0 * uv0_2m0)
{
outVector[0] = p0_1m0 * uv1_2m0 - p0_2m0 * uv1_1m0;
outCross[0] = p0_2m0 * uv0_1m0 - p0_1m0 * uv0_2m0;
outVector[1] = p1_1m0 * uv1_2m0 - p1_2m0 * uv1_1m0;
outCross[1] = p1_2m0 * uv0_1m0 - p1_1m0 * uv0_2m0;
outVector[2] = uv1_2m0 * p2_1m0 - uv1_1m0 * p2_2m0;
outCross[2] = uv0_1m0 * p2_2m0 - uv0_2m0 * p2_1m0;
}
else
{
outVector[0] = p0_2m0 * uv1_1m0 - p0_1m0 * uv1_2m0;
outCross[0] = p0_1m0 * uv0_2m0 - p0_2m0 * uv0_1m0;
outVector[1] = p1_2m0 * uv1_1m0 - p1_1m0 * uv1_2m0;
outCross[1] = p1_1m0 * uv0_2m0 - p1_2m0 * uv0_1m0;
outVector[2] = uv1_1m0 * p2_2m0 - uv1_2m0 * p2_1m0;
outCross[2] = uv0_2m0 * p2_1m0 - p2_2m0 * uv0_1m0;
}
Vec3_Normalize(outVector);
Vec3_Normalize(outCross);
}
void GetExteriorAnglesOfTri(const VertexData& vertexData, tvec3& outExteriorAngles, const uint16_t i0, const uint16_t i1, const uint16_t i2)
{
tvec3 L21;
tvec3 L02;
tvec3 L10;
const auto* index0Position = GetVec3(vertexData.positionData, i0, vertexData.positionDataStride);
const auto* index1Position = GetVec3(vertexData.positionData, i1, vertexData.positionDataStride);
const auto* index2Position = GetVec3(vertexData.positionData, i2, vertexData.positionDataStride);
L10[0] = index0Position[0] - index1Position[0];
L10[1] = index0Position[1] - index1Position[1];
L10[2] = index0Position[2] - index1Position[2];
L21[0] = index1Position[0] - index2Position[0];
L21[1] = index1Position[1] - index2Position[1];
L21[2] = index1Position[2] - index2Position[2];
L02[0] = index2Position[0] - index0Position[0];
L02[1] = index2Position[1] - index0Position[1];
L02[2] = index2Position[2] - index0Position[2];
Vec3_Normalize(L10);
Vec3_Normalize(L21);
Vec3_Normalize(L02);
outExteriorAngles[0] = AngleBetweenOriginVectors(L10, L02);
outExteriorAngles[1] = AngleBetweenOriginVectors(L21, L10);
outExteriorAngles[2] = AngleBetweenOriginVectors(L02, L21);
}
void sub_10022CA0(const uint16_t index, void* sourcesDest, const tvec3& ecx0, const float exteriorAngle, const size_t stride)
{
auto& vec = GetVec3(sourcesDest, index, stride);
vec[0] = ecx0[0] * exteriorAngle + vec[0];
vec[1] = ecx0[1] * exteriorAngle + vec[1];
vec[2] = ecx0[2] * exteriorAngle + vec[2];
}
void sub_10014EE0(const tvec3& src, tvec3& a2)
{
assert(Vec3_IsNormalized(src));
const auto v4 = src[0] * src[0];
float v5 = src[1] * src[1];
float v6 = src[2] * src[2];
int v3 = v4 > v5;
if (*(&v4 + v3) > v6)
v3 = 2;
const auto srcc = -src[v3];
a2[0] = src[0] * srcc;
a2[1] = src[1] * srcc;
a2[2] = src[2] * srcc;
a2[v3] = a2[v3] + 1.0f;
Vec3_Normalize(a2);
}
void CalculateTangentSpace(const VertexData& vertexData, const size_t triCount, const size_t vertexCount)
{
// Set tangents and binormals to 0
for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++)
{
SetVec3(vertexData.tangentData, vertexIndex, vertexData.tangentDataStride, NULL_VEC3);
SetVec3(vertexData.binormalData, vertexIndex, vertexData.binormalDataStride, NULL_VEC3);
}
for (auto triIndex = 0u; triIndex < triCount; triIndex++)
{
const auto i0 = vertexData.triData[triIndex * 3u + 0u];
const auto i1 = vertexData.triData[triIndex * 3u + 1u];
const auto i2 = vertexData.triData[triIndex * 3u + 2u];
tvec3 vector, cross, exteriorAngles;
sub_10022E80(vertexData, i0, i1, i2, vector, cross);
GetExteriorAnglesOfTri(vertexData, exteriorAngles, i0, i1, i2);
sub_10022CA0(i0, vertexData.tangentData, vector, exteriorAngles[0], vertexData.tangentDataStride);
sub_10022CA0(i1, vertexData.tangentData, vector, exteriorAngles[1], vertexData.tangentDataStride);
sub_10022CA0(i2, vertexData.tangentData, vector, exteriorAngles[2], vertexData.tangentDataStride);
sub_10022CA0(i0, vertexData.binormalData, cross, exteriorAngles[0], vertexData.binormalDataStride);
sub_10022CA0(i1, vertexData.binormalData, cross, exteriorAngles[1], vertexData.binormalDataStride);
sub_10022CA0(i2, vertexData.binormalData, cross, exteriorAngles[2], vertexData.binormalDataStride);
}
for (auto vertexIndex = 0u; vertexIndex < vertexCount; vertexIndex++)
{
const auto& normal = GetVec3(vertexData.normalData, vertexIndex, vertexData.normalDataStride);
auto& tangent = GetVec3(vertexData.tangentData, vertexIndex, vertexData.tangentDataStride);
auto& binormal = GetVec3(vertexData.binormalData, vertexIndex, vertexData.binormalDataStride);
const auto dot_normal_tangent = normal[0] * tangent[0] + normal[1] * tangent[1] + normal[2] * tangent[2];
tangent[0] = normal[0] * -dot_normal_tangent + tangent[0];
tangent[1] = normal[1] * -dot_normal_tangent + tangent[1];
tangent[2] = normal[2] * -dot_normal_tangent + tangent[2];
if (Vec3_Normalize(tangent) < 0.001f)
{
Vec3_Cross(binormal, normal, tangent);
if (Vec3_Normalize(tangent) < 0.001)
sub_10014EE0(normal, tangent);
}
tvec3 cross;
Vec3_Cross(normal, tangent, cross);
const auto sourcesc = binormal[0] * cross[0] + cross[1] * binormal[1] + cross[2] * binormal[2];
if (sourcesc >= 0.0)
{
binormal[0] = cross[0];
binormal[1] = cross[1];
binormal[2] = cross[2];
}
else
{
binormal[0] = -cross[0];
binormal[1] = -cross[1];
binormal[2] = -cross[2];
}
}
}
} // namespace tangent_space

View File

@ -0,0 +1,22 @@
#pragma once
#include <cstdint>
namespace tangent_space
{
struct VertexData
{
const void* positionData;
size_t positionDataStride;
const void* normalData;
size_t normalDataStride;
const void* uvData;
size_t uvDataStride;
void* tangentData;
size_t tangentDataStride;
void* binormalData;
size_t binormalDataStride;
const uint16_t* triData;
};
void CalculateTangentSpace(const VertexData& vertexData, size_t triCount, size_t vertexCount);
} // namespace tangent_space