Files
o3de/Code/CryEngine/Cry3DEngine/MeshCompiler/TangentSpaceCalculation.cpp
T
2021-03-08 14:30:57 -08:00

824 lines
24 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include <platform.h>
#include <Cry_Vector2.h>
#include <Cry_Vector3.h>
#include "TangentSpaceCalculation.h"
#include <mikkelsen/mikktspace.h>
struct SMikkVertex
{
Vec3 pos, norm, tang, bitang;
Vec2 magST;
Vec2 texc;
};
struct SMikkFace
{
int vertexOffset;
int nrOriginalFace;
};
struct SMikkMesh
{
std::vector<SMikkVertex> mikkVerts;
std::vector<SMikkFace> mikkFaces;
int mikkNumFaces;
};
CTangentSpaceCalculation::CBase33::CBase33()
{
}
CTangentSpaceCalculation::CBase33::CBase33(const Vec3& Uval, const Vec3& Vval, const Vec3& Nval)
{
u = Uval;
v = Vval;
n = Nval;
}
bool CTangentSpaceCalculation::CVec3PredicateLess::operator() (const Vec3& first, const Vec3& second) const
{
if (first.x < second.x)
{
return true;
}
if (first.x > second.x)
{
return false;
}
if (first.y < second.y)
{
return true;
}
if (first.y > second.y)
{
return false;
}
return (first.z < second.z);
}
bool CTangentSpaceCalculation::CBase33PredicateLess::operator() (const CBase33& first, const CBase33& second) const
{
if (first.n.x < second.n.x)
{
return true;
}
if (first.n.x > second.n.x)
{
return false;
}
if (first.n.y < second.n.y)
{
return true;
}
if (first.n.y > second.n.y)
{
return false;
}
if (first.n.z < second.n.z)
{
return true;
}
if (first.n.z > second.n.z)
{
return false;
}
if (first.u.x < second.u.x)
{
return true;
}
if (first.u.x > second.u.x)
{
return false;
}
if (first.u.y < second.u.y)
{
return true;
}
if (first.u.y > second.u.y)
{
return false;
}
if (first.u.z < second.u.z)
{
return true;
}
if (first.u.z > second.u.z)
{
return false;
}
if (first.v.x < second.v.x)
{
return true;
}
if (first.v.x > second.v.x)
{
return false;
}
if (first.v.y < second.v.y)
{
return true;
}
if (first.v.y > second.v.y)
{
return false;
}
return first.v.z < second.v.z;
}
bool CTangentSpaceCalculation::CBaseIndexOrder::operator() (const CBaseIndex& a, const CBaseIndex& b) const
{
// first sort by position
if (a.m_posIndex < b.m_posIndex)
{
return true;
}
if (a.m_posIndex > b.m_posIndex)
{
return false;
}
// then by normal
if (a.m_normIndex < b.m_normIndex)
{
return true;
}
if (a.m_normIndex > b.m_normIndex)
{
return false;
}
return false;
}
float CTangentSpaceCalculation::CalcAngleBetween(const Vec3& invA, const Vec3& invB)
{
double LengthQ = sqrt(invA.len2() * invB.len2());
// to prevent division by zero
if (LengthQ < 0.00000001)
{
LengthQ = 0.00000001;
}
double f = invA.Dot(invB) / LengthQ;
// acos_tpl need input in the range [-1..1]
if (f > 1.0f)
{
f = 1.0f;
}
else if (f < -1.0f)
{
f = -1.0f;
}
// cosf is not available on every platform
float fRet = (float)acos_tpl(f);
return fRet;
}
void CTangentSpaceCalculation::DebugMesh(const ITriangleInputProxy& proxy) const
{
uint32 dwTriCount = proxy.GetTriangleCount();
// search for polygons that use the same indices (input data problems)
for (uint32 a = 0; a < dwTriCount; a++)
{
uint32 dwAPos[3], dwANorm[3], dwAUV[3];
proxy.GetTriangleIndices(a, dwAPos, dwANorm, dwAUV);
for (uint32 b = a + 1; b < dwTriCount; b++)
{
uint32 dwBPos[3], dwBNorm[3], dwBUV[3];
proxy.GetTriangleIndices(b, dwBPos, dwBNorm, dwBUV);
assert(!(dwAPos[0] == dwBPos[0] && dwAPos[1] == dwBPos[1] && dwAPos[2] == dwBPos[2]));
assert(!(dwAPos[1] == dwBPos[0] && dwAPos[2] == dwBPos[1] && dwAPos[0] == dwBPos[2]));
assert(!(dwAPos[2] == dwBPos[0] && dwAPos[0] == dwBPos[1] && dwAPos[1] == dwBPos[2]));
assert(!(dwAPos[1] == dwBPos[0] && dwAPos[0] == dwBPos[1] && dwAPos[2] == dwBPos[2]));
assert(!(dwAPos[2] == dwBPos[0] && dwAPos[1] == dwBPos[1] && dwAPos[0] == dwBPos[2]));
assert(!(dwAPos[0] == dwBPos[0] && dwAPos[2] == dwBPos[1] && dwAPos[1] == dwBPos[2]));
}
}
}
Vec3 CTangentSpaceCalculation::Rotate(const Vec3& vFrom, const Vec3& vTo, const Vec3& vInput)
{
// no mesh is perfect
// assert(IsNormalized(vFrom));
// no mesh is perfect
// assert(IsNormalized(vTo));
// rotation axis
Vec3 vRotAxis = vFrom.cross(vTo);
float fSin = vRotAxis.len();
float fCos = vFrom.Dot(vTo);
// no rotation
if (fSin < 0.00001f)
{
return vInput;
}
// normalize
vRotAxis = vRotAxis * (1.0f / fSin);
// perpendicular to vRotAxis and vFrom90deg
Vec3 vFrom90deg = (vRotAxis.cross(vFrom)).normalize();
// Base is vFrom,vFrom90deg,vRotAxis
float fXInPlane = vFrom.Dot(vInput);
float fYInPlane = vFrom90deg.Dot(vInput);
Vec3 a = vFrom * (fXInPlane * fCos - fYInPlane * fSin);
Vec3 b = vFrom90deg * (fXInPlane * fSin + fYInPlane * fCos);
Vec3 c = vRotAxis * (vRotAxis.Dot(vInput));
return a + b + c;
}
eCalculateTangentSpaceErrorCode CTangentSpaceCalculation::CalculateTangentSpace(const ITriangleInputProxy& inInput, const bool bUseCustomNormals, string& errorMessage)
{
if (bUseCustomNormals)
{
return CalculateTangentSpaceMikk(inInput, errorMessage);
}
uint32 dwTriCount = inInput.GetTriangleCount();
// not a number in texture coordinates
bool bTextureCoordinatesBroken = false;
// clear result
m_baseVectors.clear();
m_trianglesBaseAssigment.clear();
m_trianglesBaseAssigment.reserve(dwTriCount);
assert(m_baseVectors.empty());
assert(m_trianglesBaseAssigment.empty());
// second=index into m_BaseVectors, generated output data
std::multimap<CBaseIndex, uint32, CBaseIndexOrder> mBaseMap;
// base vectors per triangle
std::vector<CBase33> vTriangleBase;
// calculate the base vectors per triangle -------------------------------------------
{
eCalculateTangentSpaceErrorCode errorCode = CALCULATE_TANGENT_SPACE_NO_ERRORS;
for (uint32 i = 0; i < dwTriCount; i++)
{
// get data from caller ---------------------------
uint32 dwPos[3], dwNorm[3], dwUV[3];
inInput.GetTriangleIndices(i, dwPos, dwNorm, dwUV);
Vec3 vPos[3];
Vec2 vUV[3];
for (int e = 0; e < 3; e++)
{
inInput.GetPos(dwPos[e], vPos[e]);
inInput.GetUV(dwUV[e], vUV[e]);
}
// calculate tangent vectors ---------------------------
Vec3 vA = vPos[1] - vPos[0];
Vec3 vB = vPos[2] - vPos[0];
Vec3 vC = vPos[2] - vPos[1];
if (vA.IsZero())
{
//vert 0 and 1 have the same coordinates
errorMessage.Format("Vertices 0 and 1 have the same coordinate: (%f : %f : %f)\n", vPos[0].x, vPos[0].y, vPos[0].z);
errorCode = VERTICES_SHARING_COORDINATES;
continue;
}
if (vB.IsZero())
{
//vert 2 and 0 have the same coordinates
errorMessage.Format("Vertices 2 and 0 have the same coordinate: (%f : %f : %f)\n", vPos[0].x, vPos[0].y, vPos[0].z);
errorCode = VERTICES_SHARING_COORDINATES;
continue;
}
if (vC.IsZero())
{
//vert 2 and 1 have the same coordinates
errorMessage.Format("Vertices 2 and 1 have the same coordinate: (%f : %f : %f)\n", vPos[1].x, vPos[1].y, vPos[1].z);
errorCode = VERTICES_SHARING_COORDINATES;
continue;
}
float fDeltaU1 = vUV[1].x - vUV[0].x;
float fDeltaU2 = vUV[2].x - vUV[0].x;
float fDeltaV1 = vUV[1].y - vUV[0].y;
float fDeltaV2 = vUV[2].y - vUV[0].y;
float div = (fDeltaU1 * fDeltaV2 - fDeltaU2 * fDeltaV1);
if (_isnan(div))
{
errorMessage.Format("Vertices 0,1,2 have broken texture coordinates v0:(%f : %f : %f) v1:(%f : %f : %f) v2:(%f : %f : %f)\n", vPos[0].x, vPos[0].y, vPos[0].z, vPos[1].x, vPos[1].y, vPos[1].z, vPos[2].x, vPos[2].y, vPos[2].z);
bTextureCoordinatesBroken = true;
div = 0.0f;
}
Vec3 vU, vV, vN = (vA.cross(vB)).normalize();
if (div != 0.0)
{
// 2D triangle area = (u1*v2-u2*v1)/2
float a = fDeltaV2; // /div was removed - no required because of normalize()
float b = -fDeltaV1;
float c = -fDeltaU2;
float d = fDeltaU1;
// /fAreaMul2*fAreaMul2 was optimized away -> small triangles in UV should contribute less and
// less artifacts (no divide and multiply)
vU = (vA * a + vB * b) * fsgnf(div);
vV = (vA * c + vB * d) * fsgnf(div);
}
else
{
vU = Vec3(1, 0, 0);
vV = Vec3(0, 1, 0);
}
vTriangleBase.push_back(CBase33(vU, vV, vN));
}
if (errorCode != CALCULATE_TANGENT_SPACE_NO_ERRORS)
{
return errorCode;
}
}
// distribute the normals to the vertices
{
// we create a new tangent base for every vertex index that has a different normal (later we split further for mirrored use)
// and sum the base vectors (weighted by angle and mirrored if necessary)
for (uint32 i = 0; i < dwTriCount; i++)
{
uint32 e;
// get data from caller ---------------------------
uint32 dwPos[3], dwNorm[3], dwUV[3];
inInput.GetTriangleIndices(i, dwPos, dwNorm, dwUV);
CBase33 TriBase = vTriangleBase[i];
Vec3 vPos[3];
for (e = 0; e < 3; e++)
{
inInput.GetPos(dwPos[e], vPos[e]);
}
// for each triangle vertex
for (e = 0; e < 3; e++)
{
// weight by angle to fix the L-Shape problem
float fWeight = CalcAngleBetween(vPos[(e + 2) % 3] - vPos[e], vPos[(e + 1) % 3] - vPos[e]);
if (fWeight <= 0.0f)
{
fWeight = 0.0001f;
}
AddNormal2Base(mBaseMap, dwPos[e], dwNorm[e], TriBase.n * fWeight);
}
}
}
// distribute the uv vectors to the vertices
{
// we create a new tangent base for every vertex index that has a different normal
// if the base vectors does'nt fit we split as well
for (uint32 i = 0; i < dwTriCount; i++)
{
uint32 e;
// get data from caller ---------------------------
uint32 dwPos[3], dwNorm[3], dwUV[3];
CTriBaseIndex Indx;
inInput.GetTriangleIndices(i, dwPos, dwNorm, dwUV);
CBase33 TriBase = vTriangleBase[i];
Vec3 vPos[3];
for (e = 0; e < 3; e++)
{
inInput.GetPos(dwPos[e], vPos[e]);
}
// for each triangle vertex
for (e = 0; e < 3; e++)
{
// weight by angle to fix the L-Shape problem
float fWeight = CalcAngleBetween(vPos[(e + 2) % 3] - vPos[e], vPos[(e + 1) % 3] - vPos[e]);
Indx.p[e] = AddUV2Base(mBaseMap, dwPos[e], dwNorm[e], TriBase.u * fWeight, TriBase.v * fWeight, TriBase.n.normalize());
}
m_trianglesBaseAssigment.push_back(Indx);
}
}
// adjust the base vectors per vertex -------------------------------------------
{
std::vector<CBase33>::iterator it;
for (it = m_baseVectors.begin(); it != m_baseVectors.end(); ++it)
{
CBase33& ref = (*it);
// rotate u and v in n plane
{
Vec3 vUout, vVout, vNout;
vNout = ref.n;
vNout.normalize();
// project u in n plane
// project v in n plane
vUout = ref.u - vNout * (vNout.Dot(ref.u));
vVout = ref.v - vNout * (vNout.Dot(ref.v));
ref.u = vUout;
ref.u.normalize();
ref.v = vVout;
ref.v.normalize();
ref.n = vNout;
//assert(ref.u.x>=-1 && ref.u.x<=1);
//assert(ref.u.y>=-1 && ref.u.y<=1);
//assert(ref.u.z>=-1 && ref.u.z<=1);
//assert(ref.v.x>=-1 && ref.v.x<=1);
//assert(ref.v.y>=-1 && ref.v.y<=1);
//assert(ref.v.z>=-1 && ref.v.z<=1);
//assert(ref.n.x>=-1 && ref.n.x<=1);
//assert(ref.n.y>=-1 && ref.n.y<=1);
//assert(ref.n.z>=-1 && ref.n.z<=1);
}
}
}
return bTextureCoordinatesBroken ? BROKEN_TEXTURE_COORDINATES : CALCULATE_TANGENT_SPACE_NO_ERRORS;
}
uint32 CTangentSpaceCalculation::AddUV2Base(std::multimap<CBaseIndex, uint32, CBaseIndexOrder>& inMap,
const uint32 indwPosNo, const uint32 indwNormNo, const Vec3& inU, const Vec3& inV, const Vec3& inNormN)
{
CBaseIndex Indx;
Indx.m_posIndex = indwPosNo;
Indx.m_normIndex = indwNormNo;
std::multimap<CBaseIndex, uint32, CBaseIndexOrder>::iterator iFind, iFindEnd;
iFind = inMap.lower_bound(Indx);
assert(iFind != inMap.end());
Vec3 vNormal = m_baseVectors[(*iFind).second].n;
iFindEnd = inMap.upper_bound(Indx);
uint32 dwBaseUVIndex = 0xffffffff; // init with not found
bool bParity = inU.cross(inV).Dot(inNormN) > 0.0f;
for (; iFind != iFindEnd; ++iFind)
{
CBase33& refFound = m_baseVectors[(*iFind).second];
if (!refFound.u.IsZero())
{
bool bParityRef = refFound.u.cross(refFound.v).Dot(refFound.n) > 0.0f;
bool bParityCheck = (bParityRef == bParity);
if (!bParityCheck)
{
continue;
}
// bool bHalfAngleCheck=normalize(inU+inV) * normalize(refFound.u+refFound.v) > 0.0f;
Vec3 normRefFound = refFound.n;
normRefFound.normalize();
Vec3 uvRefSum = refFound.u + refFound.v;
uvRefSum.normalize();
Vec3 vRotHalf = Rotate(normRefFound, inNormN, uvRefSum);
Vec3 uvInSum = inU + inV;
uvInSum.normalize();
bool bHalfAngleCheck = uvInSum.Dot(vRotHalf) > 0.0f;
// bool bHalfAngleCheck=normalize(normalize(inU)+normalize(inV)) * normalize(normalize(refFound.u)+normalize(refFound.v)) > 0.0f;
if (!bHalfAngleCheck)
{
continue;
}
}
dwBaseUVIndex = (*iFind).second;
break;
}
// not found
if (dwBaseUVIndex == 0xffffffff)
{
// otherwise create a new base
CBase33 Base(Vec3(0, 0, 0), Vec3(0, 0, 0), vNormal);
dwBaseUVIndex = m_baseVectors.size();
inMap.insert(std::pair<CBaseIndex, uint32>(Indx, dwBaseUVIndex));
m_baseVectors.push_back(Base);
}
CBase33& refBaseUV = m_baseVectors[dwBaseUVIndex];
refBaseUV.u = refBaseUV.u + inU;
refBaseUV.v = refBaseUV.v + inV;
//no mesh is perfect
if (inU.x != 0.0f || inU.y != 0.0f || inU.z != 0.0f)
{
assert(refBaseUV.u.x != 0.0f || refBaseUV.u.y != 0.0f || refBaseUV.u.z != 0.0f);
}
// no mesh is perfect
if (inV.x != 0.0f || inV.y != 0.0f || inV.z != 0.0f)
{
assert(refBaseUV.v.x != 0.0f || refBaseUV.v.y != 0.0f || refBaseUV.v.z != 0.0f);
}
return dwBaseUVIndex;
}
void CTangentSpaceCalculation::AddNormal2Base(std::multimap<CBaseIndex, uint32, CBaseIndexOrder>& inMap, const uint32 indwPosNo, const uint32 indwNormNo, const Vec3& inNormal)
{
CBaseIndex Indx;
Indx.m_posIndex = indwPosNo;
Indx.m_normIndex = indwNormNo;
std::multimap<CBaseIndex, uint32, CBaseIndexOrder>::iterator iFind = inMap.find(Indx);
uint32 dwBaseNIndex;
if (iFind != inMap.end())
{
dwBaseNIndex = (*iFind).second;
}
else
{
CBase33 Base(Vec3(0, 0, 0), Vec3(0, 0, 0), Vec3(0, 0, 0));
dwBaseNIndex = m_baseVectors.size();
inMap.insert(std::pair<CBaseIndex, uint32>(Indx, dwBaseNIndex));
m_baseVectors.push_back(Base);
}
CBase33& refBaseN = m_baseVectors[dwBaseNIndex];
refBaseN.n = refBaseN.n + inNormal;
}
void CTangentSpaceCalculation::GetBase(const uint32 indwPos, float* outU, float* outV, float* outN)
{
CBase33& base = m_baseVectors[indwPos];
outU[0] = base.u.x;
outV[0] = base.v.x;
outN[0] = base.n.x;
outU[1] = base.u.y;
outV[1] = base.v.y;
outN[1] = base.n.y;
outU[2] = base.u.z;
outV[2] = base.v.z;
outN[2] = base.n.z;
}
void CTangentSpaceCalculation::GetTriangleBaseIndices(const uint32 indwTriNo, uint32 outdwBase[3])
{
assert(indwTriNo < m_trianglesBaseAssigment.size());
CTriBaseIndex& indx = m_trianglesBaseAssigment[indwTriNo];
for (uint32 i = 0; i < 3; i++)
{
outdwBase[i] = indx.p[i];
}
}
size_t CTangentSpaceCalculation::GetBaseCount()
{
return m_baseVectors.size();
}
static int MikkGetNumFaces(const SMikkTSpaceContext* pContext)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
return mikkMesh->mikkNumFaces;
}
static int MikkGetNumVerticesOfFace([[maybe_unused]] const SMikkTSpaceContext* pContext, [[maybe_unused]] const int iFace)
{
return 3;
}
static void MikkGetPosition(const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
const SMikkFace& face = mikkMesh->mikkFaces[iFace];
const Vec3& pos = mikkMesh->mikkVerts[face.vertexOffset + iVert].pos;
fvPosOut[0] = pos.x;
fvPosOut[1] = pos.y;
fvPosOut[2] = pos.z;
}
static void MikkGetNormal(const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
const SMikkFace& face = mikkMesh->mikkFaces[iFace];
const Vec3& normal = mikkMesh->mikkVerts[face.vertexOffset + iVert].norm;
fvNormOut[0] = normal.x;
fvNormOut[1] = normal.y;
fvNormOut[2] = normal.z;
}
static void MikkGetTexCoord(const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
const SMikkFace& face = mikkMesh->mikkFaces[iFace];
const Vec2& tan = mikkMesh->mikkVerts[face.vertexOffset + iVert].texc;
fvTexcOut[0] = tan.x;
fvTexcOut[1] = tan.y;
}
static void MikkSetTSpace(const SMikkTSpaceContext* pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, [[maybe_unused]] const tbool bIsOrientationPreserving, const int iFace, const int iVert)
{
SMikkMesh* mikkMesh = (SMikkMesh*)pContext->m_pUserData;
const SMikkFace& face = mikkMesh->mikkFaces[iFace];
const int index = face.vertexOffset + iVert;
mikkMesh->mikkVerts[index].tang = Vec3(fvTangent[0], fvTangent[1], fvTangent[2]);
mikkMesh->mikkVerts[index].bitang = Vec3(fvBiTangent[0], fvBiTangent[1], fvBiTangent[2]);
mikkMesh->mikkVerts[index].magST.x = fMagS;
mikkMesh->mikkVerts[index].magST.y = fMagT;
}
eCalculateTangentSpaceErrorCode CTangentSpaceCalculation::CalculateTangentSpaceMikk(const ITriangleInputProxy& proxy, string& errorMessage)
{
const uint32 numFaces = proxy.GetTriangleCount();
// prepare the working mesh for mikkelsen algorithm
// when custom normals are specified, we'll use them
SMikkMesh mikkMesh;
mikkMesh.mikkNumFaces = numFaces;
mikkMesh.mikkVerts.resize(numFaces * 3);
mikkMesh.mikkFaces.resize(numFaces);
for (uint32 f = 0; f < numFaces; ++f)
{
uint32 outdwPos[3];
uint32 outdwNorm[3];
uint32 outdwUV[3];
proxy.GetTriangleIndices(f, outdwPos, outdwNorm, outdwUV);
mikkMesh.mikkFaces[f].vertexOffset = f * 3;
mikkMesh.mikkFaces[f].nrOriginalFace = f;
for (uint32 vId = 0; vId < 3; ++vId)
{
SMikkVertex& vert(mikkMesh.mikkVerts[mikkMesh.mikkFaces[f].vertexOffset + vId]);
proxy.GetPos(outdwPos[vId], vert.pos);
proxy.GetNorm(f, vId, vert.norm);
proxy.GetUV(outdwUV[vId], vert.texc);
vert.tang = Vec3(1.0f, 0.0f, 0.0f);
vert.bitang = Vec3(0.0f, 1.0f, 0.0f);
}
}
// prepare mikkelsen interface
SMikkTSpaceInterface mikkInterface;
memset(&mikkInterface, 0, sizeof(SMikkTSpaceInterface));
mikkInterface.m_getNumFaces = MikkGetNumFaces;
mikkInterface.m_getNumVerticesOfFace = MikkGetNumVerticesOfFace;
mikkInterface.m_getPosition = MikkGetPosition;
mikkInterface.m_getNormal = MikkGetNormal;
mikkInterface.m_getTexCoord = MikkGetTexCoord;
mikkInterface.m_setTSpace = MikkSetTSpace;
SMikkTSpaceContext mikkContext;
memset(&mikkContext, 0, sizeof(SMikkTSpaceContext));
mikkContext.m_pUserData = &mikkMesh;
mikkContext.m_pInterface = &mikkInterface;
// generate tangent basis
bool res = genTangSpaceDefault(&mikkContext) != 0;
if (!res)
{
errorMessage = "Failed to allocate memory for Mikkelsen Tangent Basis algorithm.";
return MEMORY_ALLOCATION_FAILED;
}
m_baseVectors.clear();
m_trianglesBaseAssigment.clear();
m_trianglesBaseAssigment.resize(proxy.GetTriangleCount());
std::map<CBase33, int, CBase33PredicateLess> uniqueBaseVectors;
std::map<CBase33, int, CBase33PredicateLess>::const_iterator it;
// remove tangent basis duplicates and add them to the mesh
for (int f = 0; f < mikkMesh.mikkNumFaces; ++f)
{
const SMikkFace& face = mikkMesh.mikkFaces[f];
CTriBaseIndex tbi;
for (int ii = 0; ii < 3; ++ii)
{
const int index = face.vertexOffset + ii;
const SMikkVertex& vert = mikkMesh.mikkVerts[index];
CBase33 base;
base.u = vert.tang;
base.v = vert.bitang;
float fNorm[3];
MikkGetNormal(&mikkContext, &fNorm[0], face.nrOriginalFace, ii);
base.n.x = fNorm[0];
base.n.y = fNorm[1];
base.n.z = fNorm[2];
int val;
it = uniqueBaseVectors.find(base);
if (it != uniqueBaseVectors.end())
{
val = it->second;
}
else
{
val = m_baseVectors.size();
m_baseVectors.push_back(base);
uniqueBaseVectors[base] = val;
}
tbi.p[ii] = val;
}
m_trianglesBaseAssigment[face.nrOriginalFace] = tbi;
}
return CALCULATE_TANGENT_SPACE_NO_ERRORS;
}