diff options
Diffstat (limited to 'intern/mikktspace/mikktspace.c')
-rw-r--r-- | intern/mikktspace/mikktspace.c | 1906 |
1 files changed, 0 insertions, 1906 deletions
diff --git a/intern/mikktspace/mikktspace.c b/intern/mikktspace/mikktspace.c deleted file mode 100644 index e39ac4bb6ef..00000000000 --- a/intern/mikktspace/mikktspace.c +++ /dev/null @@ -1,1906 +0,0 @@ -/* SPDX-License-Identifier: Zlib - * Copyright 2011 by Morten S. Mikkelsen. */ - -/** \file - * \ingroup mikktspace - */ - -#include <assert.h> -#include <float.h> -#include <limits.h> -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "mikktspace.h" - -#define TFALSE 0 -#define TTRUE 1 - -#ifndef M_PI -# define M_PI 3.1415926535897932384626433832795 -#endif - -#define INTERNAL_RND_SORT_SEED 39871946 - -#ifdef _MSC_VER -# define MIKK_INLINE static __forceinline -#else -# define MIKK_INLINE static inline __attribute__((always_inline)) __attribute__((unused)) -#endif - -// internal structure -typedef struct { - float x, y, z; -} SVec3; - -MIKK_INLINE tbool veq(const SVec3 v1, const SVec3 v2) -{ - return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); -} - -MIKK_INLINE SVec3 vadd(const SVec3 v1, const SVec3 v2) -{ - SVec3 vRes; - - vRes.x = v1.x + v2.x; - vRes.y = v1.y + v2.y; - vRes.z = v1.z + v2.z; - - return vRes; -} - -MIKK_INLINE SVec3 vsub(const SVec3 v1, const SVec3 v2) -{ - SVec3 vRes; - - vRes.x = v1.x - v2.x; - vRes.y = v1.y - v2.y; - vRes.z = v1.z - v2.z; - - return vRes; -} - -MIKK_INLINE SVec3 vscale(const float fS, const SVec3 v) -{ - SVec3 vRes; - - vRes.x = fS * v.x; - vRes.y = fS * v.y; - vRes.z = fS * v.z; - - return vRes; -} - -MIKK_INLINE float LengthSquared(const SVec3 v) -{ - return v.x * v.x + v.y * v.y + v.z * v.z; -} - -MIKK_INLINE float Length(const SVec3 v) -{ - return sqrtf(LengthSquared(v)); -} - -#if 0 // UNUSED -MIKK_INLINE SVec3 Normalize(const SVec3 v) -{ - return vscale(1.0f / Length(v), v); -} -#endif - -MIKK_INLINE SVec3 NormalizeSafe(const SVec3 v) -{ - const float len = Length(v); - if (len != 0.0f) { - return vscale(1.0f / len, v); - } - else { - return v; - } -} - -MIKK_INLINE float vdot(const SVec3 v1, const SVec3 v2) -{ - return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; -} - -MIKK_INLINE tbool NotZero(const float fX) -{ - // could possibly use FLT_EPSILON instead - return fabsf(fX) > FLT_MIN; -} - -#if 0 // UNUSED -MIKK_INLINE tbool VNotZero(const SVec3 v) -{ - // might change this to an epsilon based test - return NotZero(v.x) || NotZero(v.y) || NotZero(v.z); -} -#endif - -// Shift operations in C are only defined for shift values which are -// not negative and smaller than sizeof(value) * CHAR_BIT. -// The mask, used with bitwise-and (&), prevents undefined behavior -// when the shift count is 0 or >= the width of unsigned int. -MIKK_INLINE unsigned int rotl(unsigned int value, unsigned int count) -{ - const unsigned int mask = CHAR_BIT * sizeof(value) - 1; - count &= mask; - return (value << count) | (value >> (-count & mask)); -} - -typedef struct { - int iNrFaces; - int *pTriMembers; -} SSubGroup; - -typedef struct { - int iNrFaces; - int *pFaceIndices; - int iVertexRepresentitive; - tbool bOrientPreservering; -} SGroup; - -// -#define MARK_DEGENERATE 1 -#define QUAD_ONE_DEGEN_TRI 2 -#define GROUP_WITH_ANY 4 -#define ORIENT_PRESERVING 8 - -typedef struct { - int FaceNeighbors[3]; - SGroup *AssignedGroup[3]; - - // normalized first order face derivatives - SVec3 vOs, vOt; - float fMagS, fMagT; // original magnitudes - - // determines if the current and the next triangle are a quad. - int iOrgFaceNumber; - int iFlag, iTSpacesOffs; - unsigned char vert_num[4]; -} STriInfo; - -typedef struct { - SVec3 vOs; - float fMagS; - SVec3 vOt; - float fMagT; - int iCounter; // this is to average back into quads. - tbool bOrient; -} STSpace; - -static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], - int piTriList_out[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn); -static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn); -static void InitTriInfo(STriInfo pTriInfos[], - const int piTriListIn[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn); -static int Build4RuleGroups(STriInfo pTriInfos[], - SGroup pGroups[], - int piGroupTrianglesBuffer[], - const int piTriListIn[], - const int iNrTrianglesIn); -static tbool GenerateTSpaces(STSpace psTspace[], - const STriInfo pTriInfos[], - const SGroup pGroups[], - const int iNrActiveGroups, - const int piTriListIn[], - const float fThresCos, - const SMikkTSpaceContext *pContext); - -MIKK_INLINE int MakeIndex(const int iFace, const int iVert) -{ - assert(iVert >= 0 && iVert < 4 && iFace >= 0); - return (iFace << 2) | (iVert & 0x3); -} - -MIKK_INLINE void IndexToData(int *piFace, int *piVert, const int iIndexIn) -{ - piVert[0] = iIndexIn & 0x3; - piFace[0] = iIndexIn >> 2; -} - -static STSpace AvgTSpace(const STSpace *pTS0, const STSpace *pTS1) -{ - STSpace ts_res; - - // this if is important. Due to floating point precision - // averaging when ts0==ts1 will cause a slight difference - // which results in tangent space splits later on - if (pTS0->fMagS == pTS1->fMagS && pTS0->fMagT == pTS1->fMagT && veq(pTS0->vOs, pTS1->vOs) && - veq(pTS0->vOt, pTS1->vOt)) { - ts_res.fMagS = pTS0->fMagS; - ts_res.fMagT = pTS0->fMagT; - ts_res.vOs = pTS0->vOs; - ts_res.vOt = pTS0->vOt; - } - else { - ts_res.fMagS = 0.5f * (pTS0->fMagS + pTS1->fMagS); - ts_res.fMagT = 0.5f * (pTS0->fMagT + pTS1->fMagT); - ts_res.vOs = vadd(pTS0->vOs, pTS1->vOs); - ts_res.vOt = vadd(pTS0->vOt, pTS1->vOt); - ts_res.vOs = NormalizeSafe(ts_res.vOs); - ts_res.vOt = NormalizeSafe(ts_res.vOt); - } - - return ts_res; -} - -MIKK_INLINE SVec3 GetPosition(const SMikkTSpaceContext *pContext, const int index); -MIKK_INLINE SVec3 GetNormal(const SMikkTSpaceContext *pContext, const int index); -MIKK_INLINE SVec3 GetTexCoord(const SMikkTSpaceContext *pContext, const int index); - -// degen triangles -static void DegenPrologue(STriInfo pTriInfos[], - int piTriList_out[], - const int iNrTrianglesIn, - const int iTotTris); -static void DegenEpilogue(STSpace psTspace[], - STriInfo pTriInfos[], - int piTriListIn[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn, - const int iTotTris); - -tbool genTangSpaceDefault(const SMikkTSpaceContext *pContext) -{ - return genTangSpace(pContext, 180.0f); -} - -tbool genTangSpace(const SMikkTSpaceContext *pContext, const float fAngularThreshold) -{ - // count nr_triangles - int *piTriListIn = NULL, *piGroupTrianglesBuffer = NULL; - STriInfo *pTriInfos = NULL; - SGroup *pGroups = NULL; - STSpace *psTspace = NULL; - int iNrTrianglesIn = 0, f = 0, t = 0, i = 0; - int iNrTSPaces = 0, iTotTris = 0, iDegenTriangles = 0, iNrMaxGroups = 0; - int iNrActiveGroups = 0, index = 0; - const int iNrFaces = pContext->m_pInterface->m_getNumFaces(pContext); - tbool bRes = TFALSE; - const float fThresCos = cosf((fAngularThreshold * (float)M_PI) / 180.0f); - - // verify all call-backs have been set - if (pContext->m_pInterface->m_getNumFaces == NULL || - pContext->m_pInterface->m_getNumVerticesOfFace == NULL || - pContext->m_pInterface->m_getPosition == NULL || - pContext->m_pInterface->m_getNormal == NULL || pContext->m_pInterface->m_getTexCoord == NULL) - return TFALSE; - - // count triangles on supported faces - for (f = 0; f < iNrFaces; f++) { - const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); - if (verts == 3) - ++iNrTrianglesIn; - else if (verts == 4) - iNrTrianglesIn += 2; - } - if (iNrTrianglesIn <= 0) - return TFALSE; - - // allocate memory for an index list - piTriListIn = (int *)malloc(sizeof(int[3]) * iNrTrianglesIn); - pTriInfos = (STriInfo *)malloc(sizeof(STriInfo) * iNrTrianglesIn); - if (piTriListIn == NULL || pTriInfos == NULL) { - if (piTriListIn != NULL) - free(piTriListIn); - if (pTriInfos != NULL) - free(pTriInfos); - return TFALSE; - } - - // make an initial triangle --> face index list - iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); - - // make a welded index list of identical positions and attributes (pos, norm, texc) - // printf("gen welded index list begin\n"); - GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn); - // printf("gen welded index list end\n"); - - // Mark all degenerate triangles - iTotTris = iNrTrianglesIn; - iDegenTriangles = 0; - for (t = 0; t < iTotTris; t++) { - const int i0 = piTriListIn[t * 3 + 0]; - const int i1 = piTriListIn[t * 3 + 1]; - const int i2 = piTriListIn[t * 3 + 2]; - const SVec3 p0 = GetPosition(pContext, i0); - const SVec3 p1 = GetPosition(pContext, i1); - const SVec3 p2 = GetPosition(pContext, i2); - if (veq(p0, p1) || veq(p0, p2) || veq(p1, p2)) // degenerate - { - pTriInfos[t].iFlag |= MARK_DEGENERATE; - ++iDegenTriangles; - } - } - iNrTrianglesIn = iTotTris - iDegenTriangles; - - // mark all triangle pairs that belong to a quad with only one - // good triangle. These need special treatment in DegenEpilogue(). - // Additionally, move all good triangles to the start of - // pTriInfos[] and piTriListIn[] without changing order and - // put the degenerate triangles last. - DegenPrologue(pTriInfos, piTriListIn, iNrTrianglesIn, iTotTris); - - // evaluate triangle level attributes and neighbor list - // printf("gen neighbors list begin\n"); - InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); - // printf("gen neighbors list end\n"); - - // based on the 4 rules, identify groups based on connectivity - iNrMaxGroups = iNrTrianglesIn * 3; - pGroups = (SGroup *)malloc(sizeof(SGroup) * iNrMaxGroups); - piGroupTrianglesBuffer = (int *)malloc(sizeof(int[3]) * iNrTrianglesIn); - if (pGroups == NULL || piGroupTrianglesBuffer == NULL) { - if (pGroups != NULL) - free(pGroups); - if (piGroupTrianglesBuffer != NULL) - free(piGroupTrianglesBuffer); - free(piTriListIn); - free(pTriInfos); - return TFALSE; - } - // printf("gen 4rule groups begin\n"); - iNrActiveGroups = Build4RuleGroups( - pTriInfos, pGroups, piGroupTrianglesBuffer, piTriListIn, iNrTrianglesIn); - // printf("gen 4rule groups end\n"); - - // - - psTspace = (STSpace *)malloc(sizeof(STSpace) * iNrTSPaces); - if (psTspace == NULL) { - free(piTriListIn); - free(pTriInfos); - free(pGroups); - free(piGroupTrianglesBuffer); - return TFALSE; - } - memset(psTspace, 0, sizeof(STSpace) * iNrTSPaces); - for (t = 0; t < iNrTSPaces; t++) { - psTspace[t].vOs.x = 1.0f; - psTspace[t].vOs.y = 0.0f; - psTspace[t].vOs.z = 0.0f; - psTspace[t].fMagS = 1.0f; - psTspace[t].vOt.x = 0.0f; - psTspace[t].vOt.y = 1.0f; - psTspace[t].vOt.z = 0.0f; - psTspace[t].fMagT = 1.0f; - } - - // make tspaces, each group is split up into subgroups if necessary - // based on fAngularThreshold. Finally a tangent space is made for - // every resulting subgroup - // printf("gen tspaces begin\n"); - bRes = GenerateTSpaces( - psTspace, pTriInfos, pGroups, iNrActiveGroups, piTriListIn, fThresCos, pContext); - // printf("gen tspaces end\n"); - - // clean up - free(pGroups); - free(piGroupTrianglesBuffer); - - if (!bRes) // if an allocation in GenerateTSpaces() failed - { - // clean up and return false - free(pTriInfos); - free(piTriListIn); - free(psTspace); - return TFALSE; - } - - // degenerate quads with one good triangle will be fixed by copying a space from - // the good triangle to the coinciding vertex. - // all other degenerate triangles will just copy a space from any good triangle - // with the same welded index in piTriListIn[]. - DegenEpilogue(psTspace, pTriInfos, piTriListIn, pContext, iNrTrianglesIn, iTotTris); - - free(pTriInfos); - free(piTriListIn); - - index = 0; - for (f = 0; f < iNrFaces; f++) { - const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); - if (verts != 3 && verts != 4) { - continue; - } - - // I've decided to let degenerate triangles and group-with-anythings - // vary between left/right hand coordinate systems at the vertices. - // All healthy triangles on the other hand are built to always be either or. -#if 0 - // force the coordinate system orientation to be uniform for every face. - // (this is already the case for good triangles but not for - // degenerate ones and those with bGroupWithAnything==true) - bool bOrient = psTspace[index].bOrient; - if (psTspace[index].iCounter == 0) // tspace was not derived from a group - { - // look for a space created in GenerateTSpaces() by iCounter>0 - bool bNotFound = true; - int i=1; - while (i<verts && bNotFound) - { - if (psTspace[index+i].iCounter > 0) bNotFound=false; - else ++i; - } - if (!bNotFound) bOrient = psTspace[index+i].bOrient; - } -#endif - - // set data - for (i = 0; i < verts; i++) { - const STSpace *pTSpace = &psTspace[index]; - float tang[] = {pTSpace->vOs.x, pTSpace->vOs.y, pTSpace->vOs.z}; - float bitang[] = {pTSpace->vOt.x, pTSpace->vOt.y, pTSpace->vOt.z}; - if (pContext->m_pInterface->m_setTSpace != NULL) - pContext->m_pInterface->m_setTSpace( - pContext, tang, bitang, pTSpace->fMagS, pTSpace->fMagT, pTSpace->bOrient, f, i); - if (pContext->m_pInterface->m_setTSpaceBasic != NULL) - pContext->m_pInterface->m_setTSpaceBasic( - pContext, tang, pTSpace->bOrient == TTRUE ? 1.0f : (-1.0f), f, i); - - ++index; - } - } - - free(psTspace); - - return TTRUE; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn); - -typedef unsigned int uint; - -static uint float_as_uint(const float v) -{ - return *((uint *)(&v)); -} - -#define HASH(x, y, z) (((x)*73856093) ^ ((y)*19349663) ^ ((z)*83492791)) -#define HASH_F(x, y, z) HASH(float_as_uint(x), float_as_uint(y), float_as_uint(z)) - -/* Sort comp and data based on comp. - * comp2 and data2 are used as temporary storage. */ -static void radixsort_pair(uint *comp, int *data, uint *comp2, int *data2, int n) -{ - int shift = 0; - for (int pass = 0; pass < 4; pass++, shift += 8) { - int bins[257] = {0}; - /* Count number of elements per bin. */ - for (int i = 0; i < n; i++) { - bins[((comp[i] >> shift) & 0xff) + 1]++; - } - /* Compute prefix sum to find position of each bin in the sorted array. */ - for (int i = 2; i < 256; i++) { - bins[i] += bins[i - 1]; - } - /* Insert the elements in their correct location based on their bin. */ - for (int i = 0; i < n; i++) { - int pos = bins[(comp[i] >> shift) & 0xff]++; - comp2[pos] = comp[i]; - data2[pos] = data[i]; - } - - /* Swap arrays. */ - int *tmpdata = data; - data = data2; - data2 = tmpdata; - uint *tmpcomp = comp; - comp = comp2; - comp2 = tmpcomp; - } -} - -/* Merge identical vertices. - * To find vertices with identical position, normal and texcoord, we calculate a hash of the 9 - * values. Then, by sorting based on that hash, identical elements (having identical hashes) will - * be moved next to each other. Since there might be hash collisions, the elements of each block - * are then compared with each other and duplicates are merged. - */ -static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn) -{ - int numVertices = iNrTrianglesIn * 3; - - uint *hashes = (uint *)malloc(sizeof(uint) * numVertices); - int *indices = (int *)malloc(sizeof(int) * numVertices); - uint *temp_hashes = (uint *)malloc(sizeof(uint) * numVertices); - int *temp_indices = (int *)malloc(sizeof(int) * numVertices); - - if (hashes == NULL || indices == NULL || temp_hashes == NULL || temp_indices == NULL) { - free(hashes); - free(indices); - free(temp_hashes); - free(temp_indices); - - GenerateSharedVerticesIndexListSlow(piTriList_in_and_out, pContext, iNrTrianglesIn); - return; - } - - for (int i = 0; i < numVertices; i++) { - const int index = piTriList_in_and_out[i]; - - const SVec3 vP = GetPosition(pContext, index); - const uint hashP = HASH_F(vP.x, vP.y, vP.z); - - const SVec3 vN = GetNormal(pContext, index); - const uint hashN = HASH_F(vN.x, vN.y, vN.z); - - const SVec3 vT = GetTexCoord(pContext, index); - const uint hashT = HASH_F(vT.x, vT.y, vT.z); - - hashes[i] = HASH(hashP, hashN, hashT); - indices[i] = i; - } - - radixsort_pair(hashes, indices, temp_hashes, temp_indices, numVertices); - - free(temp_hashes); - free(temp_indices); - - /* Process blocks of vertices with the same hash. - * Vertices in the block might still be separate, but we know for sure that - * vertices in different blocks will never be identical. */ - int blockstart = 0; - while (blockstart < numVertices) { - /* Find end of this block (exclusive). */ - uint hash = hashes[blockstart]; - int blockend = blockstart + 1; - for (; blockend < numVertices; blockend++) { - if (hashes[blockend] != hash) - break; - } - - /* If there's only one vertex with this hash, we don't have anything to compare. */ - if (blockend > blockstart + 1) { - for (int i = blockstart; i < blockend; i++) { - int index1 = piTriList_in_and_out[indices[i]]; - const SVec3 vP = GetPosition(pContext, index1); - const SVec3 vN = GetNormal(pContext, index1); - const SVec3 vT = GetTexCoord(pContext, index1); - for (int i2 = i + 1; i2 < blockend; i2++) { - int index2 = piTriList_in_and_out[indices[i2]]; - if (index1 == index2) - continue; - - if (veq(vP, GetPosition(pContext, index2)) && veq(vN, GetNormal(pContext, index2)) && - veq(vT, GetTexCoord(pContext, index2))) { - piTriList_in_and_out[indices[i2]] = index1; - /* Once i2>i has been identified as a duplicate, we can stop since any - * i3>i2>i that is a duplicate of i (and therefore also i2) will also be - * compared to i2 and therefore be identified there anyways. */ - break; - } - } - } - } - - /* Advance to next block. */ - blockstart = blockend; - } - - free(hashes); - free(indices); -} - -static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn) -{ - int iNumUniqueVerts = 0, t = 0, i = 0; - for (t = 0; t < iNrTrianglesIn; t++) { - for (i = 0; i < 3; i++) { - const int offs = t * 3 + i; - const int index = piTriList_in_and_out[offs]; - - const SVec3 vP = GetPosition(pContext, index); - const SVec3 vN = GetNormal(pContext, index); - const SVec3 vT = GetTexCoord(pContext, index); - - tbool bFound = TFALSE; - int t2 = 0, index2rec = -1; - while (!bFound && t2 <= t) { - int j = 0; - while (!bFound && j < 3) { - const int index2 = piTriList_in_and_out[t2 * 3 + j]; - const SVec3 vP2 = GetPosition(pContext, index2); - const SVec3 vN2 = GetNormal(pContext, index2); - const SVec3 vT2 = GetTexCoord(pContext, index2); - - if (veq(vP, vP2) && veq(vN, vN2) && veq(vT, vT2)) - bFound = TTRUE; - else - ++j; - } - if (!bFound) - ++t2; - } - - assert(bFound); - // if we found our own - if (index2rec == index) { - ++iNumUniqueVerts; - } - - piTriList_in_and_out[offs] = index2rec; - } - } -} - -static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], - int piTriList_out[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn) -{ - int iTSpacesOffs = 0, f = 0, t = 0; - int iDstTriIndex = 0; - const int iNrFaces = pContext->m_pInterface->m_getNumFaces(pContext); - for (f = 0; f < iNrFaces; f++) { - const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); - if (verts != 3 && verts != 4) - continue; - - pTriInfos[iDstTriIndex].iOrgFaceNumber = f; - pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs; - - if (verts == 3) { - unsigned char *pVerts = pTriInfos[iDstTriIndex].vert_num; - pVerts[0] = 0; - pVerts[1] = 1; - pVerts[2] = 2; - piTriList_out[iDstTriIndex * 3 + 0] = MakeIndex(f, 0); - piTriList_out[iDstTriIndex * 3 + 1] = MakeIndex(f, 1); - piTriList_out[iDstTriIndex * 3 + 2] = MakeIndex(f, 2); - ++iDstTriIndex; // next - } - else { - { - pTriInfos[iDstTriIndex + 1].iOrgFaceNumber = f; - pTriInfos[iDstTriIndex + 1].iTSpacesOffs = iTSpacesOffs; - } - - { - // need an order independent way to evaluate - // tspace on quads. This is done by splitting - // along the shortest diagonal. - const int i0 = MakeIndex(f, 0); - const int i1 = MakeIndex(f, 1); - const int i2 = MakeIndex(f, 2); - const int i3 = MakeIndex(f, 3); - const SVec3 T0 = GetTexCoord(pContext, i0); - const SVec3 T1 = GetTexCoord(pContext, i1); - const SVec3 T2 = GetTexCoord(pContext, i2); - const SVec3 T3 = GetTexCoord(pContext, i3); - const float distSQ_02 = LengthSquared(vsub(T2, T0)); - const float distSQ_13 = LengthSquared(vsub(T3, T1)); - tbool bQuadDiagIs_02; - if (distSQ_02 < distSQ_13) - bQuadDiagIs_02 = TTRUE; - else if (distSQ_13 < distSQ_02) - bQuadDiagIs_02 = TFALSE; - else { - const SVec3 P0 = GetPosition(pContext, i0); - const SVec3 P1 = GetPosition(pContext, i1); - const SVec3 P2 = GetPosition(pContext, i2); - const SVec3 P3 = GetPosition(pContext, i3); - const float distSQ_02 = LengthSquared(vsub(P2, P0)); - const float distSQ_13 = LengthSquared(vsub(P3, P1)); - - bQuadDiagIs_02 = distSQ_13 < distSQ_02 ? TFALSE : TTRUE; - } - - if (bQuadDiagIs_02) { - { - unsigned char *pVerts_A = pTriInfos[iDstTriIndex].vert_num; - pVerts_A[0] = 0; - pVerts_A[1] = 1; - pVerts_A[2] = 2; - } - piTriList_out[iDstTriIndex * 3 + 0] = i0; - piTriList_out[iDstTriIndex * 3 + 1] = i1; - piTriList_out[iDstTriIndex * 3 + 2] = i2; - ++iDstTriIndex; // next - { - unsigned char *pVerts_B = pTriInfos[iDstTriIndex].vert_num; - pVerts_B[0] = 0; - pVerts_B[1] = 2; - pVerts_B[2] = 3; - } - piTriList_out[iDstTriIndex * 3 + 0] = i0; - piTriList_out[iDstTriIndex * 3 + 1] = i2; - piTriList_out[iDstTriIndex * 3 + 2] = i3; - ++iDstTriIndex; // next - } - else { - { - unsigned char *pVerts_A = pTriInfos[iDstTriIndex].vert_num; - pVerts_A[0] = 0; - pVerts_A[1] = 1; - pVerts_A[2] = 3; - } - piTriList_out[iDstTriIndex * 3 + 0] = i0; - piTriList_out[iDstTriIndex * 3 + 1] = i1; - piTriList_out[iDstTriIndex * 3 + 2] = i3; - ++iDstTriIndex; // next - { - unsigned char *pVerts_B = pTriInfos[iDstTriIndex].vert_num; - pVerts_B[0] = 1; - pVerts_B[1] = 2; - pVerts_B[2] = 3; - } - piTriList_out[iDstTriIndex * 3 + 0] = i1; - piTriList_out[iDstTriIndex * 3 + 1] = i2; - piTriList_out[iDstTriIndex * 3 + 2] = i3; - ++iDstTriIndex; // next - } - } - } - - iTSpacesOffs += verts; - assert(iDstTriIndex <= iNrTrianglesIn); - } - - for (t = 0; t < iNrTrianglesIn; t++) - pTriInfos[t].iFlag = 0; - - // return total amount of tspaces - return iTSpacesOffs; -} - -MIKK_INLINE SVec3 GetPosition(const SMikkTSpaceContext *pContext, const int index) -{ - int iF, iI; - SVec3 res; - float pos[3]; - IndexToData(&iF, &iI, index); - pContext->m_pInterface->m_getPosition(pContext, pos, iF, iI); - res.x = pos[0]; - res.y = pos[1]; - res.z = pos[2]; - return res; -} - -MIKK_INLINE SVec3 GetNormal(const SMikkTSpaceContext *pContext, const int index) -{ - int iF, iI; - SVec3 res; - float norm[3]; - IndexToData(&iF, &iI, index); - pContext->m_pInterface->m_getNormal(pContext, norm, iF, iI); - res.x = norm[0]; - res.y = norm[1]; - res.z = norm[2]; - return res; -} - -MIKK_INLINE SVec3 GetTexCoord(const SMikkTSpaceContext *pContext, const int index) -{ - int iF, iI; - SVec3 res; - float texc[2]; - IndexToData(&iF, &iI, index); - pContext->m_pInterface->m_getTexCoord(pContext, texc, iF, iI); - res.x = texc[0]; - res.y = texc[1]; - res.z = 1.0f; - return res; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// - -typedef union { - struct { - int i0, i1, f; - }; - int array[3]; -} SEdge; - -static void BuildNeighborsFast(STriInfo pTriInfos[], - SEdge *pEdges, - const int piTriListIn[], - const int iNrTrianglesIn); -static void BuildNeighborsSlow(STriInfo pTriInfos[], - const int piTriListIn[], - const int iNrTrianglesIn); - -// returns the texture area times 2 -static float CalcTexArea(const SMikkTSpaceContext *pContext, const int indices[]) -{ - const SVec3 t1 = GetTexCoord(pContext, indices[0]); - const SVec3 t2 = GetTexCoord(pContext, indices[1]); - const SVec3 t3 = GetTexCoord(pContext, indices[2]); - - const float t21x = t2.x - t1.x; - const float t21y = t2.y - t1.y; - const float t31x = t3.x - t1.x; - const float t31y = t3.y - t1.y; - - const float fSignedAreaSTx2 = t21x * t31y - t21y * t31x; - - return fSignedAreaSTx2 < 0 ? (-fSignedAreaSTx2) : fSignedAreaSTx2; -} - -static void InitTriInfo(STriInfo pTriInfos[], - const int piTriListIn[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn) -{ - int f = 0, i = 0, t = 0; - // pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() - // which is called before this function. - - // generate neighbor info list - for (f = 0; f < iNrTrianglesIn; f++) - for (i = 0; i < 3; i++) { - pTriInfos[f].FaceNeighbors[i] = -1; - pTriInfos[f].AssignedGroup[i] = NULL; - - pTriInfos[f].vOs.x = 0.0f; - pTriInfos[f].vOs.y = 0.0f; - pTriInfos[f].vOs.z = 0.0f; - pTriInfos[f].vOt.x = 0.0f; - pTriInfos[f].vOt.y = 0.0f; - pTriInfos[f].vOt.z = 0.0f; - pTriInfos[f].fMagS = 0; - pTriInfos[f].fMagT = 0; - - // assumed bad - pTriInfos[f].iFlag |= GROUP_WITH_ANY; - } - - // evaluate first order derivatives - for (f = 0; f < iNrTrianglesIn; f++) { - // initial values - const SVec3 v1 = GetPosition(pContext, piTriListIn[f * 3 + 0]); - const SVec3 v2 = GetPosition(pContext, piTriListIn[f * 3 + 1]); - const SVec3 v3 = GetPosition(pContext, piTriListIn[f * 3 + 2]); - const SVec3 t1 = GetTexCoord(pContext, piTriListIn[f * 3 + 0]); - const SVec3 t2 = GetTexCoord(pContext, piTriListIn[f * 3 + 1]); - const SVec3 t3 = GetTexCoord(pContext, piTriListIn[f * 3 + 2]); - - const float t21x = t2.x - t1.x; - const float t21y = t2.y - t1.y; - const float t31x = t3.x - t1.x; - const float t31y = t3.y - t1.y; - const SVec3 d1 = vsub(v2, v1); - const SVec3 d2 = vsub(v3, v1); - - const float fSignedAreaSTx2 = t21x * t31y - t21y * t31x; - // assert(fSignedAreaSTx2!=0); - SVec3 vOs = vsub(vscale(t31y, d1), vscale(t21y, d2)); // eq 18 - SVec3 vOt = vadd(vscale(-t31x, d1), vscale(t21x, d2)); // eq 19 - - pTriInfos[f].iFlag |= (fSignedAreaSTx2 > 0 ? ORIENT_PRESERVING : 0); - - if (NotZero(fSignedAreaSTx2)) { - const float fAbsArea = fabsf(fSignedAreaSTx2); - const float fLenOs = Length(vOs); - const float fLenOt = Length(vOt); - const float fS = (pTriInfos[f].iFlag & ORIENT_PRESERVING) == 0 ? (-1.0f) : 1.0f; - if (NotZero(fLenOs)) - pTriInfos[f].vOs = vscale(fS / fLenOs, vOs); - if (NotZero(fLenOt)) - pTriInfos[f].vOt = vscale(fS / fLenOt, vOt); - - // evaluate magnitudes prior to normalization of vOs and vOt - pTriInfos[f].fMagS = fLenOs / fAbsArea; - pTriInfos[f].fMagT = fLenOt / fAbsArea; - - // if this is a good triangle - if (NotZero(pTriInfos[f].fMagS) && NotZero(pTriInfos[f].fMagT)) - pTriInfos[f].iFlag &= (~GROUP_WITH_ANY); - } - } - - // force otherwise healthy quads to a fixed orientation - while (t < (iNrTrianglesIn - 1)) { - const int iFO_a = pTriInfos[t].iOrgFaceNumber; - const int iFO_b = pTriInfos[t + 1].iOrgFaceNumber; - if (iFO_a == iFO_b) // this is a quad - { - const tbool bIsDeg_a = (pTriInfos[t].iFlag & MARK_DEGENERATE) != 0 ? TTRUE : TFALSE; - const tbool bIsDeg_b = (pTriInfos[t + 1].iFlag & MARK_DEGENERATE) != 0 ? TTRUE : TFALSE; - - // bad triangles should already have been removed by - // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false - if ((bIsDeg_a || bIsDeg_b) == TFALSE) { - const tbool bOrientA = (pTriInfos[t].iFlag & ORIENT_PRESERVING) != 0 ? TTRUE : TFALSE; - const tbool bOrientB = (pTriInfos[t + 1].iFlag & ORIENT_PRESERVING) != 0 ? TTRUE : TFALSE; - // if this happens the quad has extremely bad mapping!! - if (bOrientA != bOrientB) { - // printf("found quad with bad mapping\n"); - tbool bChooseOrientFirstTri = TFALSE; - if ((pTriInfos[t + 1].iFlag & GROUP_WITH_ANY) != 0) - bChooseOrientFirstTri = TTRUE; - else if (CalcTexArea(pContext, &piTriListIn[t * 3 + 0]) >= - CalcTexArea(pContext, &piTriListIn[(t + 1) * 3 + 0])) - bChooseOrientFirstTri = TTRUE; - - // force match - { - const int t0 = bChooseOrientFirstTri ? t : (t + 1); - const int t1 = bChooseOrientFirstTri ? (t + 1) : t; - pTriInfos[t1].iFlag &= (~ORIENT_PRESERVING); // clear first - pTriInfos[t1].iFlag |= (pTriInfos[t0].iFlag & ORIENT_PRESERVING); // copy bit - } - } - } - t += 2; - } - else - ++t; - } - - // match up edge pairs - { - SEdge *pEdges = (SEdge *)malloc(sizeof(SEdge[3]) * iNrTrianglesIn); - if (pEdges == NULL) - BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn); - else { - BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn); - - free(pEdges); - } - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// - -static tbool AssignRecur(const int piTriListIn[], - STriInfo psTriInfos[], - const int iMyTriIndex, - SGroup *pGroup); -MIKK_INLINE void AddTriToGroup(SGroup *pGroup, const int iTriIndex); - -static int Build4RuleGroups(STriInfo pTriInfos[], - SGroup pGroups[], - int piGroupTrianglesBuffer[], - const int piTriListIn[], - const int iNrTrianglesIn) -{ - const int iNrMaxGroups = iNrTrianglesIn * 3; - int iNrActiveGroups = 0; - int iOffset = 0, f = 0, i = 0; - (void)iNrMaxGroups; /* quiet warnings in non debug mode */ - for (f = 0; f < iNrTrianglesIn; f++) { - for (i = 0; i < 3; i++) { - // if not assigned to a group - if ((pTriInfos[f].iFlag & GROUP_WITH_ANY) == 0 && pTriInfos[f].AssignedGroup[i] == NULL) { - tbool bOrPre; - int neigh_indexL, neigh_indexR; - const int vert_index = piTriListIn[f * 3 + i]; - assert(iNrActiveGroups < iNrMaxGroups); - pTriInfos[f].AssignedGroup[i] = &pGroups[iNrActiveGroups]; - pTriInfos[f].AssignedGroup[i]->iVertexRepresentitive = vert_index; - pTriInfos[f].AssignedGroup[i]->bOrientPreservering = (pTriInfos[f].iFlag & - ORIENT_PRESERVING) != 0; - pTriInfos[f].AssignedGroup[i]->iNrFaces = 0; - pTriInfos[f].AssignedGroup[i]->pFaceIndices = &piGroupTrianglesBuffer[iOffset]; - ++iNrActiveGroups; - - AddTriToGroup(pTriInfos[f].AssignedGroup[i], f); - bOrPre = (pTriInfos[f].iFlag & ORIENT_PRESERVING) != 0 ? TTRUE : TFALSE; - neigh_indexL = pTriInfos[f].FaceNeighbors[i]; - neigh_indexR = pTriInfos[f].FaceNeighbors[i > 0 ? (i - 1) : 2]; - if (neigh_indexL >= 0) // neighbor - { - const tbool bAnswer = AssignRecur( - piTriListIn, pTriInfos, neigh_indexL, pTriInfos[f].AssignedGroup[i]); - - const tbool bOrPre2 = (pTriInfos[neigh_indexL].iFlag & ORIENT_PRESERVING) != 0 ? TTRUE : - TFALSE; - const tbool bDiff = bOrPre != bOrPre2 ? TTRUE : TFALSE; - assert(bAnswer || bDiff); - (void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */ - } - if (neigh_indexR >= 0) // neighbor - { - const tbool bAnswer = AssignRecur( - piTriListIn, pTriInfos, neigh_indexR, pTriInfos[f].AssignedGroup[i]); - - const tbool bOrPre2 = (pTriInfos[neigh_indexR].iFlag & ORIENT_PRESERVING) != 0 ? TTRUE : - TFALSE; - const tbool bDiff = bOrPre != bOrPre2 ? TTRUE : TFALSE; - assert(bAnswer || bDiff); - (void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */ - } - - // update offset - iOffset += pTriInfos[f].AssignedGroup[i]->iNrFaces; - // since the groups are disjoint a triangle can never - // belong to more than 3 groups. Subsequently something - // is completely screwed if this assertion ever hits. - assert(iOffset <= iNrMaxGroups); - } - } - } - - return iNrActiveGroups; -} - -MIKK_INLINE void AddTriToGroup(SGroup *pGroup, const int iTriIndex) -{ - pGroup->pFaceIndices[pGroup->iNrFaces] = iTriIndex; - ++pGroup->iNrFaces; -} - -static tbool AssignRecur(const int piTriListIn[], - STriInfo psTriInfos[], - const int iMyTriIndex, - SGroup *pGroup) -{ - STriInfo *pMyTriInfo = &psTriInfos[iMyTriIndex]; - - // track down vertex - const int iVertRep = pGroup->iVertexRepresentitive; - const int *pVerts = &piTriListIn[3 * iMyTriIndex + 0]; - int i = -1; - if (pVerts[0] == iVertRep) - i = 0; - else if (pVerts[1] == iVertRep) - i = 1; - else if (pVerts[2] == iVertRep) - i = 2; - assert(i >= 0 && i < 3); - - // early out - if (pMyTriInfo->AssignedGroup[i] == pGroup) - return TTRUE; - else if (pMyTriInfo->AssignedGroup[i] != NULL) - return TFALSE; - if ((pMyTriInfo->iFlag & GROUP_WITH_ANY) != 0) { - // first to group with a group-with-anything triangle - // determines its orientation. - // This is the only existing order dependency in the code!! - if (pMyTriInfo->AssignedGroup[0] == NULL && pMyTriInfo->AssignedGroup[1] == NULL && - pMyTriInfo->AssignedGroup[2] == NULL) { - pMyTriInfo->iFlag &= (~ORIENT_PRESERVING); - pMyTriInfo->iFlag |= (pGroup->bOrientPreservering ? ORIENT_PRESERVING : 0); - } - } - { - const tbool bOrient = (pMyTriInfo->iFlag & ORIENT_PRESERVING) != 0 ? TTRUE : TFALSE; - if (bOrient != pGroup->bOrientPreservering) - return TFALSE; - } - - AddTriToGroup(pGroup, iMyTriIndex); - pMyTriInfo->AssignedGroup[i] = pGroup; - - { - const int neigh_indexL = pMyTriInfo->FaceNeighbors[i]; - const int neigh_indexR = pMyTriInfo->FaceNeighbors[i > 0 ? (i - 1) : 2]; - if (neigh_indexL >= 0) - AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup); - if (neigh_indexR >= 0) - AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup); - } - - return TTRUE; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////////////////////// - -static tbool CompareSubGroups(const SSubGroup *pg1, const SSubGroup *pg2); -static void QuickSort(int *pSortBuffer, int iLeft, int iRight, unsigned int uSeed); -static STSpace EvalTspace(const int face_indices[], - const int iFaces, - const int piTriListIn[], - const STriInfo pTriInfos[], - const SMikkTSpaceContext *pContext, - const int iVertexRepresentitive); - -static tbool GenerateTSpaces(STSpace psTspace[], - const STriInfo pTriInfos[], - const SGroup pGroups[], - const int iNrActiveGroups, - const int piTriListIn[], - const float fThresCos, - const SMikkTSpaceContext *pContext) -{ - STSpace *pSubGroupTspace = NULL; - SSubGroup *pUniSubGroups = NULL; - int *pTmpMembers = NULL; - int iMaxNrFaces = 0, g = 0, i = 0; - for (g = 0; g < iNrActiveGroups; g++) - if (iMaxNrFaces < pGroups[g].iNrFaces) - iMaxNrFaces = pGroups[g].iNrFaces; - - if (iMaxNrFaces == 0) - return TTRUE; - - // make initial allocations - pSubGroupTspace = (STSpace *)malloc(sizeof(STSpace) * iMaxNrFaces); - pUniSubGroups = (SSubGroup *)malloc(sizeof(SSubGroup) * iMaxNrFaces); - pTmpMembers = (int *)malloc(sizeof(int) * iMaxNrFaces); - if (pSubGroupTspace == NULL || pUniSubGroups == NULL || pTmpMembers == NULL) { - if (pSubGroupTspace != NULL) - free(pSubGroupTspace); - if (pUniSubGroups != NULL) - free(pUniSubGroups); - if (pTmpMembers != NULL) - free(pTmpMembers); - return TFALSE; - } - - for (g = 0; g < iNrActiveGroups; g++) { - const SGroup *pGroup = &pGroups[g]; - int iUniqueSubGroups = 0, s = 0; - - for (i = 0; i < pGroup->iNrFaces; i++) // triangles - { - const int f = pGroup->pFaceIndices[i]; // triangle number - int index = -1, iVertIndex = -1, iOF_1 = -1, iMembers = 0, j = 0, l = 0; - SSubGroup tmp_group; - tbool bFound; - SVec3 n, vOs, vOt; - if (pTriInfos[f].AssignedGroup[0] == pGroup) - index = 0; - else if (pTriInfos[f].AssignedGroup[1] == pGroup) - index = 1; - else if (pTriInfos[f].AssignedGroup[2] == pGroup) - index = 2; - assert(index >= 0 && index < 3); - - iVertIndex = piTriListIn[f * 3 + index]; - assert(iVertIndex == pGroup->iVertexRepresentitive); - - // is normalized already - n = GetNormal(pContext, iVertIndex); - - // project - vOs = NormalizeSafe(vsub(pTriInfos[f].vOs, vscale(vdot(n, pTriInfos[f].vOs), n))); - vOt = NormalizeSafe(vsub(pTriInfos[f].vOt, vscale(vdot(n, pTriInfos[f].vOt), n))); - - // original face number - iOF_1 = pTriInfos[f].iOrgFaceNumber; - - iMembers = 0; - for (j = 0; j < pGroup->iNrFaces; j++) { - const int t = pGroup->pFaceIndices[j]; // triangle number - const int iOF_2 = pTriInfos[t].iOrgFaceNumber; - - // project - SVec3 vOs2 = NormalizeSafe(vsub(pTriInfos[t].vOs, vscale(vdot(n, pTriInfos[t].vOs), n))); - SVec3 vOt2 = NormalizeSafe(vsub(pTriInfos[t].vOt, vscale(vdot(n, pTriInfos[t].vOt), n))); - - { - const tbool bAny = ((pTriInfos[f].iFlag | pTriInfos[t].iFlag) & GROUP_WITH_ANY) != 0 ? - TTRUE : - TFALSE; - // make sure triangles which belong to the same quad are joined. - const tbool bSameOrgFace = iOF_1 == iOF_2 ? TTRUE : TFALSE; - - const float fCosS = vdot(vOs, vOs2); - const float fCosT = vdot(vOt, vOt2); - - assert(f != t || bSameOrgFace); // sanity check - if (bAny || bSameOrgFace || (fCosS > fThresCos && fCosT > fThresCos)) - pTmpMembers[iMembers++] = t; - } - } - - // sort pTmpMembers - tmp_group.iNrFaces = iMembers; - tmp_group.pTriMembers = pTmpMembers; - if (iMembers > 1) { - unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? - QuickSort(pTmpMembers, 0, iMembers - 1, uSeed); - } - - // look for an existing match - bFound = TFALSE; - l = 0; - while (l < iUniqueSubGroups && !bFound) { - bFound = CompareSubGroups(&tmp_group, &pUniSubGroups[l]); - if (!bFound) - ++l; - } - - assert(bFound || l == iUniqueSubGroups); - - // if no match was found we allocate a new subgroup - if (!bFound) { - // insert new subgroup - int *pIndices = (int *)malloc(sizeof(int) * iMembers); - if (pIndices == NULL) { - // clean up and return false - int s = 0; - for (s = 0; s < iUniqueSubGroups; s++) - free(pUniSubGroups[s].pTriMembers); - free(pUniSubGroups); - free(pTmpMembers); - free(pSubGroupTspace); - return TFALSE; - } - pUniSubGroups[iUniqueSubGroups].iNrFaces = iMembers; - pUniSubGroups[iUniqueSubGroups].pTriMembers = pIndices; - memcpy(pIndices, tmp_group.pTriMembers, sizeof(int) * iMembers); - pSubGroupTspace[iUniqueSubGroups] = EvalTspace(tmp_group.pTriMembers, - iMembers, - piTriListIn, - pTriInfos, - pContext, - pGroup->iVertexRepresentitive); - ++iUniqueSubGroups; - } - - // output tspace - { - const int iOffs = pTriInfos[f].iTSpacesOffs; - const int iVert = pTriInfos[f].vert_num[index]; - STSpace *pTS_out = &psTspace[iOffs + iVert]; - assert(pTS_out->iCounter < 2); - assert(((pTriInfos[f].iFlag & ORIENT_PRESERVING) != 0) == pGroup->bOrientPreservering); - if (pTS_out->iCounter == 1) { - *pTS_out = AvgTSpace(pTS_out, &pSubGroupTspace[l]); - pTS_out->iCounter = 2; // update counter - pTS_out->bOrient = pGroup->bOrientPreservering; - } - else { - assert(pTS_out->iCounter == 0); - *pTS_out = pSubGroupTspace[l]; - pTS_out->iCounter = 1; // update counter - pTS_out->bOrient = pGroup->bOrientPreservering; - } - } - } - - // clean up - for (s = 0; s < iUniqueSubGroups; s++) - free(pUniSubGroups[s].pTriMembers); - } - - // clean up - free(pUniSubGroups); - free(pTmpMembers); - free(pSubGroupTspace); - - return TTRUE; -} - -static STSpace EvalTspace(const int face_indices[], - const int iFaces, - const int piTriListIn[], - const STriInfo pTriInfos[], - const SMikkTSpaceContext *pContext, - const int iVertexRepresentitive) -{ - STSpace res; - float fAngleSum = 0; - int face = 0; - res.vOs.x = 0.0f; - res.vOs.y = 0.0f; - res.vOs.z = 0.0f; - res.vOt.x = 0.0f; - res.vOt.y = 0.0f; - res.vOt.z = 0.0f; - res.fMagS = 0; - res.fMagT = 0; - - for (face = 0; face < iFaces; face++) { - const int f = face_indices[face]; - - // only valid triangles get to add their contribution - if ((pTriInfos[f].iFlag & GROUP_WITH_ANY) == 0) { - SVec3 n, vOs, vOt, p0, p1, p2, v1, v2; - float fCos, fAngle, fMagS, fMagT; - int i = -1, index = -1, i0 = -1, i1 = -1, i2 = -1; - if (piTriListIn[3 * f + 0] == iVertexRepresentitive) - i = 0; - else if (piTriListIn[3 * f + 1] == iVertexRepresentitive) - i = 1; - else if (piTriListIn[3 * f + 2] == iVertexRepresentitive) - i = 2; - assert(i >= 0 && i < 3); - - // project - index = piTriListIn[3 * f + i]; - n = GetNormal(pContext, index); - vOs = NormalizeSafe(vsub(pTriInfos[f].vOs, vscale(vdot(n, pTriInfos[f].vOs), n))); - vOt = NormalizeSafe(vsub(pTriInfos[f].vOt, vscale(vdot(n, pTriInfos[f].vOt), n))); - - i2 = piTriListIn[3 * f + (i < 2 ? (i + 1) : 0)]; - i1 = piTriListIn[3 * f + i]; - i0 = piTriListIn[3 * f + (i > 0 ? (i - 1) : 2)]; - - p0 = GetPosition(pContext, i0); - p1 = GetPosition(pContext, i1); - p2 = GetPosition(pContext, i2); - v1 = vsub(p0, p1); - v2 = vsub(p2, p1); - - // project - v1 = NormalizeSafe(vsub(v1, vscale(vdot(n, v1), n))); - v2 = NormalizeSafe(vsub(v2, vscale(vdot(n, v2), n))); - - // weight contribution by the angle - // between the two edge vectors - fCos = vdot(v1, v2); - fCos = fCos > 1 ? 1 : (fCos < (-1) ? (-1) : fCos); - fAngle = (float)acos(fCos); - fMagS = pTriInfos[f].fMagS; - fMagT = pTriInfos[f].fMagT; - - res.vOs = vadd(res.vOs, vscale(fAngle, vOs)); - res.vOt = vadd(res.vOt, vscale(fAngle, vOt)); - res.fMagS += (fAngle * fMagS); - res.fMagT += (fAngle * fMagT); - fAngleSum += fAngle; - } - } - - // normalize - res.vOs = NormalizeSafe(res.vOs); - res.vOt = NormalizeSafe(res.vOt); - if (fAngleSum > 0) { - res.fMagS /= fAngleSum; - res.fMagT /= fAngleSum; - } - - return res; -} - -static tbool CompareSubGroups(const SSubGroup *pg1, const SSubGroup *pg2) -{ - tbool bStillSame = TTRUE; - int i = 0; - if (pg1->iNrFaces != pg2->iNrFaces) - return TFALSE; - while (i < pg1->iNrFaces && bStillSame) { - bStillSame = pg1->pTriMembers[i] == pg2->pTriMembers[i] ? TTRUE : TFALSE; - if (bStillSame) - ++i; - } - return bStillSame; -} - -static void QuickSort(int *pSortBuffer, int iLeft, int iRight, unsigned int uSeed) -{ - int iL, iR, n, index, iMid, iTmp; - - // Random - unsigned int t = uSeed & 31; - t = rotl(uSeed, t); - uSeed = uSeed + t + 3; - // Random end - - iL = iLeft; - iR = iRight; - n = (iR - iL) + 1; - assert(n >= 0); - index = (int)(uSeed % (unsigned int)n); - - iMid = pSortBuffer[index + iL]; - - do { - while (pSortBuffer[iL] < iMid) - ++iL; - while (pSortBuffer[iR] > iMid) - --iR; - - if (iL <= iR) { - iTmp = pSortBuffer[iL]; - pSortBuffer[iL] = pSortBuffer[iR]; - pSortBuffer[iR] = iTmp; - ++iL; - --iR; - } - } while (iL <= iR); - - if (iLeft < iR) - QuickSort(pSortBuffer, iLeft, iR, uSeed); - if (iL < iRight) - QuickSort(pSortBuffer, iL, iRight, uSeed); -} - -///////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////// - -static void QuickSortEdges( - SEdge *pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed); -static void GetEdge(int *i0_out, - int *i1_out, - int *edgenum_out, - const int indices[], - const int i0_in, - const int i1_in); - -static void BuildNeighborsFast(STriInfo pTriInfos[], - SEdge *pEdges, - const int piTriListIn[], - const int iNrTrianglesIn) -{ - // build array of edges - unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? - int iEntries = 0, iCurStartIndex = -1, f = 0, i = 0; - for (f = 0; f < iNrTrianglesIn; f++) - for (i = 0; i < 3; i++) { - const int i0 = piTriListIn[f * 3 + i]; - const int i1 = piTriListIn[f * 3 + (i < 2 ? (i + 1) : 0)]; - pEdges[f * 3 + i].i0 = i0 < i1 ? i0 : i1; // put minimum index in i0 - pEdges[f * 3 + i].i1 = !(i0 < i1) ? i0 : i1; // put maximum index in i1 - pEdges[f * 3 + i].f = f; // record face number - } - - // sort over all edges by i0, this is the pricey one. - QuickSortEdges(pEdges, 0, iNrTrianglesIn * 3 - 1, 0, uSeed); // sort channel 0 which is i0 - - // sub sort over i1, should be fast. - // could replace this with a 64 bit int sort over (i0,i1) - // with i0 as msb in the quick-sort call above. - iEntries = iNrTrianglesIn * 3; - iCurStartIndex = 0; - for (i = 1; i < iEntries; i++) { - if (pEdges[iCurStartIndex].i0 != pEdges[i].i0) { - const int iL = iCurStartIndex; - const int iR = i - 1; - // const int iElems = i-iL; - iCurStartIndex = i; - QuickSortEdges(pEdges, iL, iR, 1, uSeed); // sort channel 1 which is i1 - } - } - - // sub sort over f, which should be fast. - // this step is to remain compliant with BuildNeighborsSlow() when - // more than 2 triangles use the same edge (such as a butterfly topology). - iCurStartIndex = 0; - for (i = 1; i < iEntries; i++) { - if (pEdges[iCurStartIndex].i0 != pEdges[i].i0 || pEdges[iCurStartIndex].i1 != pEdges[i].i1) { - const int iL = iCurStartIndex; - const int iR = i - 1; - // const int iElems = i-iL; - iCurStartIndex = i; - QuickSortEdges(pEdges, iL, iR, 2, uSeed); // sort channel 2 which is f - } - } - - // pair up, adjacent triangles - for (i = 0; i < iEntries; i++) { - const int i0 = pEdges[i].i0; - const int i1 = pEdges[i].i1; - const int f = pEdges[i].f; - tbool bUnassigned_A; - - int i0_A, i1_A; - int edgenum_A, edgenum_B = 0; // 0,1 or 2 - GetEdge(&i0_A, - &i1_A, - &edgenum_A, - &piTriListIn[f * 3], - i0, - i1); // resolve index ordering and edge_num - bUnassigned_A = pTriInfos[f].FaceNeighbors[edgenum_A] == -1 ? TTRUE : TFALSE; - - if (bUnassigned_A) { - // get true index ordering - int j = i + 1, t; - tbool bNotFound = TTRUE; - while (j < iEntries && i0 == pEdges[j].i0 && i1 == pEdges[j].i1 && bNotFound) { - tbool bUnassigned_B; - int i0_B, i1_B; - t = pEdges[j].f; - // flip i0_B and i1_B - GetEdge(&i1_B, - &i0_B, - &edgenum_B, - &piTriListIn[t * 3], - pEdges[j].i0, - pEdges[j].i1); // resolve index ordering and edge_num - // assert(!(i0_A==i1_B && i1_A==i0_B)); - bUnassigned_B = pTriInfos[t].FaceNeighbors[edgenum_B] == -1 ? TTRUE : TFALSE; - if (i0_A == i0_B && i1_A == i1_B && bUnassigned_B) - bNotFound = TFALSE; - else - ++j; - } - - if (!bNotFound) { - int t = pEdges[j].f; - pTriInfos[f].FaceNeighbors[edgenum_A] = t; - // assert(pTriInfos[t].FaceNeighbors[edgenum_B]==-1); - pTriInfos[t].FaceNeighbors[edgenum_B] = f; - } - } - } -} - -static void BuildNeighborsSlow(STriInfo pTriInfos[], - const int piTriListIn[], - const int iNrTrianglesIn) -{ - int f = 0, i = 0; - for (f = 0; f < iNrTrianglesIn; f++) { - for (i = 0; i < 3; i++) { - // if unassigned - if (pTriInfos[f].FaceNeighbors[i] == -1) { - const int i0_A = piTriListIn[f * 3 + i]; - const int i1_A = piTriListIn[f * 3 + (i < 2 ? (i + 1) : 0)]; - - // search for a neighbor - tbool bFound = TFALSE; - int t = 0, j = 0; - while (!bFound && t < iNrTrianglesIn) { - if (t != f) { - j = 0; - while (!bFound && j < 3) { - // in rev order - const int i1_B = piTriListIn[t * 3 + j]; - const int i0_B = piTriListIn[t * 3 + (j < 2 ? (j + 1) : 0)]; - // assert(!(i0_A==i1_B && i1_A==i0_B)); - if (i0_A == i0_B && i1_A == i1_B) - bFound = TTRUE; - else - ++j; - } - } - - if (!bFound) - ++t; - } - - // assign neighbors - if (bFound) { - pTriInfos[f].FaceNeighbors[i] = t; - // assert(pTriInfos[t].FaceNeighbors[j]==-1); - pTriInfos[t].FaceNeighbors[j] = f; - } - } - } - } -} - -static void QuickSortEdges( - SEdge *pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed) -{ - unsigned int t; - int iL, iR, n, index, iMid; - - // early out - SEdge sTmp; - const int iElems = iRight - iLeft + 1; - if (iElems < 2) - return; - else if (iElems == 2) { - if (pSortBuffer[iLeft].array[channel] > pSortBuffer[iRight].array[channel]) { - sTmp = pSortBuffer[iLeft]; - pSortBuffer[iLeft] = pSortBuffer[iRight]; - pSortBuffer[iRight] = sTmp; - } - return; - } - else if (iElems < 16) { - int i, j; - for (i = 0; i < iElems - 1; i++) { - for (j = 0; j < iElems - i - 1; j++) { - int index = iLeft + j; - if (pSortBuffer[index].array[channel] > pSortBuffer[index + 1].array[channel]) { - sTmp = pSortBuffer[index]; - pSortBuffer[index] = pSortBuffer[index + 1]; - pSortBuffer[index + 1] = sTmp; - } - } - } - return; - } - - // Random - t = uSeed & 31; - t = rotl(uSeed, t); - uSeed = uSeed + t + 3; - // Random end - - iL = iLeft; - iR = iRight; - n = (iR - iL) + 1; - assert(n >= 0); - index = (int)(uSeed % (unsigned int)n); - - iMid = pSortBuffer[index + iL].array[channel]; - - do { - while (pSortBuffer[iL].array[channel] < iMid) - ++iL; - while (pSortBuffer[iR].array[channel] > iMid) - --iR; - - if (iL <= iR) { - sTmp = pSortBuffer[iL]; - pSortBuffer[iL] = pSortBuffer[iR]; - pSortBuffer[iR] = sTmp; - ++iL; - --iR; - } - } while (iL <= iR); - - if (iLeft < iR) - QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed); - if (iL < iRight) - QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed); -} - -// resolve ordering and edge number -static void GetEdge(int *i0_out, - int *i1_out, - int *edgenum_out, - const int indices[], - const int i0_in, - const int i1_in) -{ - *edgenum_out = -1; - - // test if first index is on the edge - if (indices[0] == i0_in || indices[0] == i1_in) { - // test if second index is on the edge - if (indices[1] == i0_in || indices[1] == i1_in) { - edgenum_out[0] = 0; // first edge - i0_out[0] = indices[0]; - i1_out[0] = indices[1]; - } - else { - edgenum_out[0] = 2; // third edge - i0_out[0] = indices[2]; - i1_out[0] = indices[0]; - } - } - else { - // only second and third index is on the edge - edgenum_out[0] = 1; // second edge - i0_out[0] = indices[1]; - i1_out[0] = indices[2]; - } -} - -///////////////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////// Degenerate triangles //////////////////////////////////// - -static void DegenPrologue(STriInfo pTriInfos[], - int piTriList_out[], - const int iNrTrianglesIn, - const int iTotTris) -{ - int iNextGoodTriangleSearchIndex = -1; - tbool bStillFindingGoodOnes; - - // locate quads with only one good triangle - int t = 0; - while (t < (iTotTris - 1)) { - const int iFO_a = pTriInfos[t].iOrgFaceNumber; - const int iFO_b = pTriInfos[t + 1].iOrgFaceNumber; - if (iFO_a == iFO_b) // this is a quad - { - const tbool bIsDeg_a = (pTriInfos[t].iFlag & MARK_DEGENERATE) != 0 ? TTRUE : TFALSE; - const tbool bIsDeg_b = (pTriInfos[t + 1].iFlag & MARK_DEGENERATE) != 0 ? TTRUE : TFALSE; - if ((bIsDeg_a ^ bIsDeg_b) != 0) { - pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI; - pTriInfos[t + 1].iFlag |= QUAD_ONE_DEGEN_TRI; - } - t += 2; - } - else - ++t; - } - - // reorder list so all degen triangles are moved to the back - // without reordering the good triangles - iNextGoodTriangleSearchIndex = 1; - t = 0; - bStillFindingGoodOnes = TTRUE; - while (t < iNrTrianglesIn && bStillFindingGoodOnes) { - const tbool bIsGood = (pTriInfos[t].iFlag & MARK_DEGENERATE) == 0 ? TTRUE : TFALSE; - if (bIsGood) { - if (iNextGoodTriangleSearchIndex < (t + 2)) - iNextGoodTriangleSearchIndex = t + 2; - } - else { - int t0, t1; - // search for the first good triangle. - tbool bJustADegenerate = TTRUE; - while (bJustADegenerate && iNextGoodTriangleSearchIndex < iTotTris) { - const tbool bIsGood = (pTriInfos[iNextGoodTriangleSearchIndex].iFlag & MARK_DEGENERATE) == - 0 ? - TTRUE : - TFALSE; - if (bIsGood) - bJustADegenerate = TFALSE; - else - ++iNextGoodTriangleSearchIndex; - } - - t0 = t; - t1 = iNextGoodTriangleSearchIndex; - ++iNextGoodTriangleSearchIndex; - assert(iNextGoodTriangleSearchIndex > (t + 1)); - - // swap triangle t0 and t1 - if (!bJustADegenerate) { - int i = 0; - for (i = 0; i < 3; i++) { - const int index = piTriList_out[t0 * 3 + i]; - piTriList_out[t0 * 3 + i] = piTriList_out[t1 * 3 + i]; - piTriList_out[t1 * 3 + i] = index; - } - { - const STriInfo tri_info = pTriInfos[t0]; - pTriInfos[t0] = pTriInfos[t1]; - pTriInfos[t1] = tri_info; - } - } - else - bStillFindingGoodOnes = TFALSE; // this is not supposed to happen - } - - if (bStillFindingGoodOnes) - ++t; - } - - assert(bStillFindingGoodOnes); // code will still work. - assert(iNrTrianglesIn == t); -} - -typedef struct VertReverseLookupContext { - tbool bIsInitialized; - int *pLookup; - int iMaxVertIndex; -} VertReverseLookupContext; - -static void GenerateReverseLookup(const int piTriListIn[], - const int iNrTrianglesIn, - VertReverseLookupContext *pLookupCtx) -{ - int t; - // Figure out what size of lookup array we need. - pLookupCtx->iMaxVertIndex = -1; - for (t = 0; t < 3 * iNrTrianglesIn; t++) { - int iVertIndex = piTriListIn[t]; - if (iVertIndex > pLookupCtx->iMaxVertIndex) { - pLookupCtx->iMaxVertIndex = iVertIndex; - } - } - // Allocate memory. - if (pLookupCtx->iMaxVertIndex < 1) { - // Nothing to allocate, all triangles are degenerate. - return; - } - pLookupCtx->pLookup = malloc(sizeof(int) * (pLookupCtx->iMaxVertIndex + 1)); - if (pLookupCtx->pLookup == NULL) { - // Most likely run out of memory. - return; - } - // Fill in lookup. - for (t = 0; t <= pLookupCtx->iMaxVertIndex; t++) { - pLookupCtx->pLookup[t] = -1; - } - for (t = 0; t < 3 * iNrTrianglesIn; t++) { - int iVertIndex = piTriListIn[t]; - if (pLookupCtx->pLookup[iVertIndex] != -1) { - continue; - } - pLookupCtx->pLookup[iVertIndex] = t; - } -} - -static int LookupVertexIndexFromGoodTriangle(VertReverseLookupContext *pLookupCtx, - int piTriListIn[], - const int iNrTrianglesIn, - const int iVertexIndex) -{ - // Allocate lookup on demand. - if (!pLookupCtx->bIsInitialized) { - GenerateReverseLookup(piTriListIn, iNrTrianglesIn, pLookupCtx); - pLookupCtx->bIsInitialized = TTRUE; - } - // Make sure vertex index is in the mapping. - if (iVertexIndex > pLookupCtx->iMaxVertIndex) { - return -1; - } - if (pLookupCtx->pLookup == NULL) { - return -1; - } - // Perform actual lookup. - return pLookupCtx->pLookup[iVertexIndex]; -} - -static void FreeReverseLookup(VertReverseLookupContext *pLookupCtx) -{ - if (!pLookupCtx->bIsInitialized) { - return; - } - if (pLookupCtx->pLookup != NULL) { - free(pLookupCtx->pLookup); - } -} - -static void DegenEpilogue(STSpace psTspace[], - STriInfo pTriInfos[], - int piTriListIn[], - const SMikkTSpaceContext *pContext, - const int iNrTrianglesIn, - const int iTotTris) -{ - int t = 0, i = 0; - VertReverseLookupContext lookupCtx = {TFALSE}; - // deal with degenerate triangles - // punishment for degenerate triangles is O(iNrTrianglesIn) extra memory. - for (t = iNrTrianglesIn; t < iTotTris; t++) { - // degenerate triangles on a quad with one good triangle are skipped - // here but processed in the next loop - const tbool bSkip = (pTriInfos[t].iFlag & QUAD_ONE_DEGEN_TRI) != 0 ? TTRUE : TFALSE; - if (bSkip) { - continue; - } - - for (i = 0; i < 3; i++) { - const int index1 = piTriListIn[t * 3 + i]; - int j = LookupVertexIndexFromGoodTriangle(&lookupCtx, piTriListIn, iNrTrianglesIn, index1); - if (j < 0) { - // Matching vertex from good triangle is not found. - continue; - } - - const int iTri = j / 3; - const int iVert = j % 3; - const int iSrcVert = pTriInfos[iTri].vert_num[iVert]; - const int iSrcOffs = pTriInfos[iTri].iTSpacesOffs; - const int iDstVert = pTriInfos[t].vert_num[i]; - const int iDstOffs = pTriInfos[t].iTSpacesOffs; - // copy tspace - psTspace[iDstOffs + iDstVert] = psTspace[iSrcOffs + iSrcVert]; - } - } - FreeReverseLookup(&lookupCtx); - - // deal with degenerate quads with one good triangle - for (t = 0; t < iNrTrianglesIn; t++) { - // this triangle belongs to a quad where the - // other triangle is degenerate - if ((pTriInfos[t].iFlag & QUAD_ONE_DEGEN_TRI) != 0) { - SVec3 vDstP; - int iOrgF = -1, i = 0; - tbool bNotFound; - unsigned char *pV = pTriInfos[t].vert_num; - int iFlag = (1 << pV[0]) | (1 << pV[1]) | (1 << pV[2]); - int iMissingIndex = 0; - if ((iFlag & 2) == 0) - iMissingIndex = 1; - else if ((iFlag & 4) == 0) - iMissingIndex = 2; - else if ((iFlag & 8) == 0) - iMissingIndex = 3; - - iOrgF = pTriInfos[t].iOrgFaceNumber; - vDstP = GetPosition(pContext, MakeIndex(iOrgF, iMissingIndex)); - bNotFound = TTRUE; - i = 0; - while (bNotFound && i < 3) { - const int iVert = pV[i]; - const SVec3 vSrcP = GetPosition(pContext, MakeIndex(iOrgF, iVert)); - if (veq(vSrcP, vDstP) == TTRUE) { - const int iOffs = pTriInfos[t].iTSpacesOffs; - psTspace[iOffs + iMissingIndex] = psTspace[iOffs + iVert]; - bNotFound = TFALSE; - } - else - ++i; - } - assert(!bNotFound); - } - } -} |