From 2d604425277f56c98a5c8351914178480f68bc23 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 6 Mar 2026 13:30:21 -0800 Subject: Move contrib libraries to contrib repo --- contrib/cgltf-tangents/CMakeLists.txt | 13 - contrib/cgltf-tangents/LICENSE | 79 - contrib/cgltf-tangents/MikkTSpace/README.md | 4 - contrib/cgltf-tangents/MikkTSpace/mikktspace.c | 1899 ------------------------ contrib/cgltf-tangents/MikkTSpace/mikktspace.h | 145 -- contrib/cgltf-tangents/README.md | 42 - contrib/cgltf-tangents/cgltf_tangents.c | 618 -------- contrib/cgltf-tangents/cgltf_tangents.h | 67 - contrib/cgltf-tangents/test/CMakeLists.txt | 11 - contrib/cgltf-tangents/test/main.c | 86 -- 10 files changed, 2964 deletions(-) delete mode 100644 contrib/cgltf-tangents/CMakeLists.txt delete mode 100644 contrib/cgltf-tangents/LICENSE delete mode 100644 contrib/cgltf-tangents/MikkTSpace/README.md delete mode 100644 contrib/cgltf-tangents/MikkTSpace/mikktspace.c delete mode 100644 contrib/cgltf-tangents/MikkTSpace/mikktspace.h delete mode 100644 contrib/cgltf-tangents/README.md delete mode 100644 contrib/cgltf-tangents/cgltf_tangents.c delete mode 100644 contrib/cgltf-tangents/cgltf_tangents.h delete mode 100644 contrib/cgltf-tangents/test/CMakeLists.txt delete mode 100644 contrib/cgltf-tangents/test/main.c (limited to 'contrib/cgltf-tangents') diff --git a/contrib/cgltf-tangents/CMakeLists.txt b/contrib/cgltf-tangents/CMakeLists.txt deleted file mode 100644 index 3f86fc2..0000000 --- a/contrib/cgltf-tangents/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(cgltf-tangents) - -add_library(cgltf-tangents - cgltf_tangents.c - MikkTSpace/mikktspace.c) - -target_include_directories(cgltf-tangents PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(cgltf-tangents PUBLIC - cgltf) diff --git a/contrib/cgltf-tangents/LICENSE b/contrib/cgltf-tangents/LICENSE deleted file mode 100644 index 7796e37..0000000 --- a/contrib/cgltf-tangents/LICENSE +++ /dev/null @@ -1,79 +0,0 @@ -This project has two third-party dependencies: -- MikkTSpace -- cgltf - -The license for this project and its dependencies are included below. - --------------------------------------------------------------------------------- -cgltf-tangents --------------------------------------------------------------------------------- - -Copyright 2022 Marc Sunet - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - --------------------------------------------------------------------------------- -MikkTSpace --------------------------------------------------------------------------------- - -Copyright (C) 2011 by Morten S. Mikkelsen - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - -3. This notice may not be removed or altered from any source distribution. - --------------------------------------------------------------------------------- -cgltf --------------------------------------------------------------------------------- - -Copyright (c) 2018-2021 Johannes Kuhlmann - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/contrib/cgltf-tangents/MikkTSpace/README.md b/contrib/cgltf-tangents/MikkTSpace/README.md deleted file mode 100644 index 9fda155..0000000 --- a/contrib/cgltf-tangents/MikkTSpace/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# MikkTSpace -A common standard for tangent space used in baking tools to produce normal maps. - -More information can be found at http://www.mikktspace.com/. diff --git a/contrib/cgltf-tangents/MikkTSpace/mikktspace.c b/contrib/cgltf-tangents/MikkTSpace/mikktspace.c deleted file mode 100644 index 0342ae0..0000000 --- a/contrib/cgltf-tangents/MikkTSpace/mikktspace.c +++ /dev/null @@ -1,1899 +0,0 @@ -/** \file mikktspace/mikktspace.c - * \ingroup mikktspace - */ -/** - * Copyright (C) 2011 by Morten S. Mikkelsen - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include -#include -#include -#include -#include -#include - -#include "mikktspace.h" - -#define TFALSE 0 -#define TTRUE 1 - -#ifndef M_PI -#define M_PI 3.1415926535897932384626433832795 -#endif - -#define INTERNAL_RND_SORT_SEED 39871946 - -// internal structure -typedef struct { - float x, y, z; -} SVec3; - -static tbool veq( const SVec3 v1, const SVec3 v2 ) -{ - return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); -} - -static 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; -} - - -static 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; -} - -static 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; -} - -static float LengthSquared( const SVec3 v ) -{ - return v.x*v.x + v.y*v.y + v.z*v.z; -} - -static float Length( const SVec3 v ) -{ - return sqrtf(LengthSquared(v)); -} - -static SVec3 Normalize( const SVec3 v ) -{ - return vscale(1 / Length(v), v); -} - -static float vdot( const SVec3 v1, const SVec3 v2) -{ - return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; -} - - -static tbool NotZero(const float fX) -{ - // could possibly use FLT_EPSILON instead - return fabsf(fX) > FLT_MIN; -} - -static tbool VNotZero(const SVec3 v) -{ - // might change this to an epsilon based test - return NotZero(v.x) || NotZero(v.y) || NotZero(v.z); -} - - - -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); - -static int MakeIndex(const int iFace, const int iVert) -{ - assert(iVert>=0 && iVert<4 && iFace>=0); - return (iFace<<2) | (iVert&0x3); -} - -static 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); - if ( VNotZero(ts_res.vOs) ) ts_res.vOs = Normalize(ts_res.vOs); - if ( VNotZero(ts_res.vOt) ) ts_res.vOt = Normalize(ts_res.vOt); - } - - return ts_res; -} - - - -static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index); -static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index); -static 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 = (float) cos((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; fm_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; tm_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. - - /*// 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 0) bNotFound=false; - else ++i; - } - if (!bNotFound) bOrient = psTspace[index+i].bOrient; - }*/ - - // set data - for (i=0; ivOs.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; -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -typedef struct { - float vert[3]; - int index; -} STmpVert; - -static const int g_iCells = 2048; - -#ifdef _MSC_VER -# define NOINLINE __declspec(noinline) -#else -# define NOINLINE __attribute__ ((noinline)) -#endif - -// it is IMPORTANT that this function is called to evaluate the hash since -// inlining could potentially reorder instructions and generate different -// results for the same effective input value fVal. -static NOINLINE int FindGridCell(const float fMin, const float fMax, const float fVal) -{ - const float fIndex = g_iCells * ((fVal-fMin)/(fMax-fMin)); - const int iIndex = (int)fIndex; - return iIndex < g_iCells ? (iIndex >= 0 ? iIndex : 0) : (g_iCells - 1); -} - -static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in); -static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries); -static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); - -static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) -{ - - // Generate bounding box - int * piHashTable=NULL, * piHashCount=NULL, * piHashOffsets=NULL, * piHashCount2=NULL; - STmpVert * pTmpVert = NULL; - int i=0, iChannel=0, k=0, e=0; - int iMaxCount=0; - SVec3 vMin = GetPosition(pContext, 0), vMax = vMin, vDim; - float fMin, fMax; - for (i=1; i<(iNrTrianglesIn*3); i++) - { - const int index = piTriList_in_and_out[i]; - - const SVec3 vP = GetPosition(pContext, index); - if (vMin.x > vP.x) vMin.x = vP.x; - else if (vMax.x < vP.x) vMax.x = vP.x; - if (vMin.y > vP.y) vMin.y = vP.y; - else if (vMax.y < vP.y) vMax.y = vP.y; - if (vMin.z > vP.z) vMin.z = vP.z; - else if (vMax.z < vP.z) vMax.z = vP.z; - } - - vDim = vsub(vMax,vMin); - iChannel = 0; - fMin = vMin.x; fMax=vMax.x; - if (vDim.y>vDim.x && vDim.y>vDim.z) - { - iChannel=1; - fMin = vMin.y; - fMax = vMax.y; - } - else if (vDim.z>vDim.x) - { - iChannel=2; - fMin = vMin.z; - fMax = vMax.z; - } - - // make allocations - piHashTable = (int *) malloc(sizeof(int)*iNrTrianglesIn*3); - piHashCount = (int *) malloc(sizeof(int)*g_iCells); - piHashOffsets = (int *) malloc(sizeof(int)*g_iCells); - piHashCount2 = (int *) malloc(sizeof(int)*g_iCells); - - if (piHashTable==NULL || piHashCount==NULL || piHashOffsets==NULL || piHashCount2==NULL) - { - if (piHashTable!=NULL) free(piHashTable); - if (piHashCount!=NULL) free(piHashCount); - if (piHashOffsets!=NULL) free(piHashOffsets); - if (piHashCount2!=NULL) free(piHashCount2); - GenerateSharedVerticesIndexListSlow(piTriList_in_and_out, pContext, iNrTrianglesIn); - return; - } - memset(piHashCount, 0, sizeof(int)*g_iCells); - memset(piHashCount2, 0, sizeof(int)*g_iCells); - - // count amount of elements in each cell unit - for (i=0; i<(iNrTrianglesIn*3); i++) - { - const int index = piTriList_in_and_out[i]; - const SVec3 vP = GetPosition(pContext, index); - const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z); - const int iCell = FindGridCell(fMin, fMax, fVal); - ++piHashCount[iCell]; - } - - // evaluate start index of each cell. - piHashOffsets[0]=0; - for (k=1; kpTmpVert[l].vert[c]) fvMin[c]=pTmpVert[l].vert[c]; - if (fvMax[c]dx && dy>dz) channel=1; - else if (dz>dx) channel=2; - - fSep = 0.5f*(fvMax[channel]+fvMin[channel]); - - // stop if all vertices are NaNs - if (!isfinite(fSep)) - return; - - // terminate recursion when the separation/average value - // is no longer strictly between fMin and fMax values. - if (fSep>=fvMax[channel] || fSep<=fvMin[channel]) - { - // complete the weld - for (l=iL_in; l<=iR_in; l++) - { - int i = pTmpVert[l].index; - const int index = piTriList_in_and_out[i]; - const SVec3 vP = GetPosition(pContext, index); - const SVec3 vN = GetNormal(pContext, index); - const SVec3 vT = GetTexCoord(pContext, index); - - tbool bNotFound = TTRUE; - int l2=iL_in, i2rec=-1; - while (l20); // at least 2 entries - - // separate (by fSep) all points between iL_in and iR_in in pTmpVert[] - while (iL < iR) - { - tbool bReadyLeftSwap = TFALSE, bReadyRightSwap = TFALSE; - while ((!bReadyLeftSwap) && iL=iL_in && iL<=iR_in); - bReadyLeftSwap = !(pTmpVert[iL].vert[channel]=iL_in && iR<=iR_in); - bReadyRightSwap = pTmpVert[iR].vert[channel]m_pInterface->m_getNumFaces(pContext); 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_02m_pInterface->m_getPosition(pContext, pos, iF, iI); - res.x=pos[0]; res.y=pos[1]; res.z=pos[2]; - return res; -} - -static 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; -} - -static 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; f0 ? 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)*iNrTrianglesIn*3); - 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); -static 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; fiVertexRepresentitive = 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; -} - -static 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 it's 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(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, iUniqueTspaces=0, g=0, i=0; - for (g=0; giNrFaces; 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 = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); - vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); - if ( VNotZero(vOs) ) vOs = Normalize(vOs); - if ( VNotZero(vOt) ) vOt = Normalize(vOt); - - // original face number - iOF_1 = pTriInfos[f].iOrgFaceNumber; - - iMembers = 0; - for (j=0; jiNrFaces; j++) - { - const int t = pGroup->pFaceIndices[j]; // triangle number - const int iOF_2 = pTriInfos[t].iOrgFaceNumber; - - // project - SVec3 vOs2 = vsub(pTriInfos[t].vOs, vscale(vdot(n,pTriInfos[t].vOs), n)); - SVec3 vOt2 = vsub(pTriInfos[t].vOt, vscale(vdot(n,pTriInfos[t].vOt), n)); - if ( VNotZero(vOs2) ) vOs2 = Normalize(vOs2); - if ( VNotZero(vOt2) ) vOt2 = Normalize(vOt2); - - { - 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 (liVertexRepresentitive); - ++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 and offset iUniqueTspaces - for (s=0; s=0 && i<3); - - // project - index = piTriListIn[3*f+i]; - n = GetNormal(pContext, index); - vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); - vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); - if ( VNotZero(vOs) ) vOs = Normalize(vOs); - if ( VNotZero(vOt) ) vOt = Normalize(vOt); - - 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 = vsub(v1, vscale(vdot(n,v1),n)); if ( VNotZero(v1) ) v1 = Normalize(v1); - v2 = vsub(v2, vscale(vdot(n,v2),n)); if ( VNotZero(v2) ) v2 = Normalize(v2); - - // 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 - if ( VNotZero(res.vOs) ) res.vOs = Normalize(res.vOs); - if ( VNotZero(res.vOt) ) res.vOt = Normalize(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 (iiNrFaces && 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=(uSeed<>(32-t)); - uSeed=uSeed+t+3; - // Random end - - iL=iLeft; iR=iRight; - n = (iR-iL)+1; - assert(n>=0); - index = (int) (uSeed%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 pSortBuffer[iRight].array[channel]) - { - sTmp = pSortBuffer[iLeft]; - pSortBuffer[iLeft] = pSortBuffer[iRight]; - pSortBuffer[iRight] = sTmp; - } - return; - } - - // Random - t=uSeed&31; - t=(uSeed<>(32-t)); - uSeed=uSeed+t+3; - // Random end - - iL = iLeft; - iR = iRight; - n = (iR-iL)+1; - assert(n>=0); - index = (int) (uSeed%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 (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); -} - -static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris) -{ - int t=0, i=0; - // deal with degenerate triangles - // punishment for degenerate triangles is O(N^2) - for (t=iNrTrianglesIn; t http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf - * Note that though the tangent spaces at the vertices are generated in an order-independent way, - * by this implementation, the interpolated tangent space is still affected by which diagonal is - * chosen to split each quad. A sensible solution is to have your tools pipeline always - * split quads by the shortest diagonal. This choice is order-independent and works with mirroring. - * If these have the same length then compare the diagonals defined by the texture coordinates. - * XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin - * and also quad triangulator plugin. - */ - - -typedef int tbool; -typedef struct SMikkTSpaceContext SMikkTSpaceContext; - -typedef struct { - // Returns the number of faces (triangles/quads) on the mesh to be processed. - int (*m_getNumFaces)(const SMikkTSpaceContext * pContext); - - // Returns the number of vertices on face number iFace - // iFace is a number in the range {0, 1, ..., getNumFaces()-1} - int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace); - - // returns the position/normal/texcoord of the referenced face of vertex number iVert. - // iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads. - void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert); - void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert); - void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert); - - // either (or both) of the two setTSpace callbacks can be set. - // The call-back m_setTSpaceBasic() is sufficient for basic normal mapping. - - // This function is used to return the tangent and fSign to the application. - // fvTangent is a unit length vector. - // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. - // bitangent = fSign * cross(vN, tangent); - // Note that the results are returned unindexed. It is possible to generate a new index list - // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. - // DO NOT! use an already existing index list. - void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert); - - // This function is used to return tangent space results to the application. - // fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their - // true magnitudes which can be used for relief mapping effects. - // fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent. - // However, both are perpendicular to the vertex normal. - // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. - // fSign = bIsOrientationPreserving ? 1.0f : (-1.0f); - // bitangent = fSign * cross(vN, tangent); - // Note that the results are returned unindexed. It is possible to generate a new index list - // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. - // DO NOT! use an already existing index list. - void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, - const tbool bIsOrientationPreserving, const int iFace, const int iVert); -} SMikkTSpaceInterface; - -struct SMikkTSpaceContext -{ - SMikkTSpaceInterface * m_pInterface; // initialized with callback functions - void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call) -}; - -// these are both thread safe! -tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled) -tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold); - - -// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the -// normal map sampler must use the exact inverse of the pixel shader transformation. -// The most efficient transformation we can possibly do in the pixel shader is -// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN. -// pixel shader (fast transform out) -// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); -// where vNt is the tangent space normal. The normal map sampler must likewise use the -// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader. -// sampler does (exact inverse of pixel shader): -// float3 row0 = cross(vB, vN); -// float3 row1 = cross(vN, vT); -// float3 row2 = cross(vT, vB); -// float fSign = dot(vT, row0)<0 ? -1 : 1; -// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) ); -// where vNout is the sampled normal in some chosen 3D space. -// -// Should you choose to reconstruct the bitangent in the pixel shader instead -// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also. -// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of -// quads as your renderer then problems will occur since the interpolated tangent spaces will differ -// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before -// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier. -// However, this must be used both by the sampler and your tools/rendering pipeline. - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/contrib/cgltf-tangents/README.md b/contrib/cgltf-tangents/README.md deleted file mode 100644 index 2a68b27..0000000 --- a/contrib/cgltf-tangents/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# cgltf-tangents - -A library to compute missing tangent vectors in glTF models using MikkTSpace. - -## Example - -``` -// Load the glTF scene and buffers as usual. -cgltf_result result = cgltf_parse_file(&options, filepath, &data); -cgltf_load_buffers(&options, data, filepath); - -// Compute missing tangents. -cgltfTangentBuffer* tangent_buffers = 0; -cgltf_size num_tangent_buffers = 0; -cgltf_compute_tangents(&options, data, &tangent_buffers, &num_tangent_buffers); -``` - -## About - -This is a single-header/source library that combines -[MikkTSpace](https://github.com/mmikk/MikkTSpace) and -[cgltf](https://github.com/jkuhlmann/cgltf) to compute missing tangent vectors -for models. - -Mesh primitives in glTF may have a normal map but not necessarily tangent -vectors. An example is the -[DamagedHelmet](https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/DamagedHelmet/glTF) -sample. From the -[spec](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes): - -*"When tangents are not specified, client implementations SHOULD calculate -tangents using default MikkTSpace algorithms with the specified vertex -positions, normals, and texture coordinates associated with the normal texture."* - -cgltf-tangents takes an input glTF scene and scans it for mesh primitives that -have a normal map but no tangents. cgltf-tangents then invokes MikkTSpace to -compute tangents for those mesh primitives and outputs an array of tangent -buffers. The client can then upload these buffers to GPU memory for rendering. - -See `test/` for a complete example. - -MikkTSpace is packaged here for convenience. cgltf must be obtained separately. diff --git a/contrib/cgltf-tangents/cgltf_tangents.c b/contrib/cgltf-tangents/cgltf_tangents.c deleted file mode 100644 index 80b1e56..0000000 --- a/contrib/cgltf-tangents/cgltf_tangents.c +++ /dev/null @@ -1,618 +0,0 @@ -/* -Copyright 2022 Marc Sunet - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -#include "cgltf_tangents.h" -#include "cgltf.h" - -#include "MikkTSpace/mikktspace.h" - -#include -#include -#include -#include -#include - -#ifdef CGLTF_TANGENTS_DEBUG -#include -#define DLOG printf -#else -#define DLOG(...) -#endif - -#include // TODO: Remove me. - -#define CGLTF_OPTIONS_MALLOC(size) \ - options->memory.alloc(options->memory.user_data, size) - -#define CGLTF_OPTIONS_FREE(ptr) \ - options->memory.free(options->memory.user_data, ptr) - -static void* cgltf_default_alloc(void* user, cgltf_size size) { - (void)user; - return malloc(size); -} - -static void cgltf_default_free(void* user, void* ptr) { - (void)user; - free(ptr); -} - -static const cgltf_size NUM_TANGENT_COMPONENTS = 4; // X,Y,Z,fSign - -static float normalize_i8(int8_t x) { return (float)x / 128.0; } -static float normalize_u8(uint8_t x) { return (float)x / 255.0; } -static float normalize_i16(int16_t x) { return (float)x / 32768.0; } -static float normalize_u16(uint16_t x) { return (float)x / 65535.0; } -static float normalize_u32(uint32_t x) { return (float)x / 4294967295.0; } - -static cgltf_size num_vertex_attrib_components(cgltf_type type) { - switch (type) { - case cgltf_type_scalar: - return 1; - case cgltf_type_vec2: - return 2; - case cgltf_type_vec3: - return 3; - case cgltf_type_vec4: - return 4; - default: - assert(false); - return 0; - } -} - -static cgltf_size cgltf_component_type_size_bytes(cgltf_component_type type) { - switch (type) { - case cgltf_component_type_r_8: - return 1; - case cgltf_component_type_r_8u: - return 1; - case cgltf_component_type_r_16: - return 2; - case cgltf_component_type_r_16u: - return 2; - case cgltf_component_type_r_32u: - return 4; - case cgltf_component_type_r_32f: - return 4; - default: - assert(false); - return 0; - } -} - -static cgltf_size default_stride(cgltf_type type, - cgltf_component_type component_type) { - return num_vertex_attrib_components(type) * - cgltf_component_type_size_bytes(component_type); -} - -// ----------------------------------------------------------------------------- -// MikkTSpace interface - -// An array of values for a given vertex attribute or for vertex indices. -// For positions and normals, glTF mandates floats. -// Texcoords and indices can be different types and vary in size: 8-bit, 16-bit, -// or 32-bit. -// We store void* pointers so that we can do byte pointer arithmetic. -typedef struct Buffer { - const void* start; // X-coordinate of the first attribute. - const void* end; // One byte past the end of the buffer. - cgltf_size stride_bytes; // Stride in bytes between each value. - cgltf_component_type type; // Type of each value in the buffer. -} Buffer; - -// User data for mesh processing. -// See: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes -// Buffer pointers have the accessor + view offsets baked in so that we do the -// addition only once. -typedef struct SMikkUserData { - const cgltf_primitive* primitive; - // Index buffer may be empty (mesh primitive has no indices). - Buffer indices; - // Vertex attributes. - Buffer positions; - Buffer normals; - Buffer texcoords; - // Output tangents. - void* tangents; -} SMikkUserData; - -static cgltf_size get_vertex_index(const SMikkUserData* data, cgltf_size iFace, - cgltf_size iVert) { - const cgltf_primitive* primitive = data->primitive; - - // First compute a vertex index as if the mesh primitive had no indices. - cgltf_size vertex_idx = 0; - switch (primitive->type) { - case cgltf_primitive_type_triangles: - vertex_idx = iFace * 3 + iVert; - break; - case cgltf_primitive_type_triangle_strip: - // For triangle strips: - // face 0 -> verts 0, 1, 2 - // face 1 -> verts 1, 3, 2 (1, 2, 3 flipped) - // face 2 -> verts 2, 3, 4 - // face 3 -> verts 3, 5, 4 (3, 4, 5 flipped) - // ... - // face N=2k -> verts N, N+1, N+2 - // face N=2k+1 -> verts N, N+2, N+1 - if (iFace & 1) { - // Flip the winding of odd faces so that the is consistent with the even - // ones. - // iVert = 0 -> vert 0 - // iVert = 1 -> vert 2 - // iVert = 2 -> vert 1 - vertex_idx = iFace + (2 - iVert); - } else { - vertex_idx = iFace + iVert; - } - break; - case cgltf_primitive_type_triangle_fan: - // For triangle fans: - // face 0 -> verts 0, 1, 2 - // face 1 -> verts 0, 2, 3 - // face 2 -> verts 0, 3, 4 - // face 3 -> verts 0, 4, 5 - // ... - // face N -> verts 0, N=1, N=2 - if (iVert == 0) { - vertex_idx = 0; - } else { - vertex_idx = iFace + iVert; - } - break; - default: - assert(false); - break; - } - - // If the mesh primitive has vertex indices, then vertex_idx is actually the - // index of the index. Index the index buffer with vertex_idx to find the - // real vertex index. - if (primitive->indices != NULL) { - const void* p_idx = - data->indices.start + vertex_idx * data->indices.stride_bytes; - switch (data->indices.type) { - case cgltf_component_type_r_8: - vertex_idx = *((int8_t*)p_idx); - break; - case cgltf_component_type_r_8u: - vertex_idx = *((uint8_t*)p_idx); - break; - case cgltf_component_type_r_16: - vertex_idx = *((int16_t*)p_idx); - break; - case cgltf_component_type_r_16u: - vertex_idx = *((uint16_t*)p_idx); - break; - case cgltf_component_type_r_32u: - vertex_idx = *((uint32_t*)p_idx); - break; - default: - assert(false); - break; - } - } - - return vertex_idx; -} - -static const void* get_vertex(const Buffer* buffer, cgltf_size index) { - // Stride is the offset in bytes between vertex attributes. - const void* vertex = buffer->start + buffer->stride_bytes * index; - assert(vertex < buffer->end); - return vertex; -} - -static const void* get_position(const SMikkUserData* data, cgltf_size index) { - return get_vertex(&data->positions, index); -} - -static const void* get_normal(const SMikkUserData* data, cgltf_size index) { - return get_vertex(&data->normals, index); -} - -static const void* get_texcoord(const SMikkUserData* data, cgltf_size index) { - return get_vertex(&data->texcoords, index); -} - -static float* get_tangent(void* buffer, cgltf_size index) { - // Tangents are tightly packed. - return (float*)(buffer) + NUM_TANGENT_COMPONENTS * index; -} - -static int SMikk_get_num_faces(const SMikkTSpaceContext* pContext) { - SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - // Find the number of effective vertices (vertices or indices) in the mesh - // primitive. - // - // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes - // - // "All attribute accessors for a given primitive MUST have the same count. - // When indices property is not defined, attribute accessors' count indicates - // the number of vertices to render; when indices property is defined, it - // indicates the upper (exclusive) bound on the index values in the indices - // accessor, i.e., all index values MUST be less than attribute accessors' - // count." - const cgltf_size num_verts = (primitive->indices != NULL) - ? primitive->indices->count - : primitive->attributes_count; - - // Determine the number of faces given the number of vertices. - // - // We use the fact that glTF only supports triangles for faces. - // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes - switch (primitive->type) { - case cgltf_primitive_type_triangles: - return (int)num_verts / 3; - case cgltf_primitive_type_triangle_strip: - case cgltf_primitive_type_triangle_fan: - return (int)num_verts - 2; - default: - return 0; - } -} - -int SMikk_get_num_vertices_of_face(const SMikkTSpaceContext* pContext, - const int iFace) { - // Triangles are the only faces supported by glTF. - // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes - return 3; -} - -void SMikk_get_position(const SMikkTSpaceContext* pContext, float fvPosOut[], - const int iFace, const int iVert) { - const SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - const cgltf_size idx = get_vertex_index(data, iFace, iVert); - const float* coord = get_position(data, idx); - fvPosOut[0] = *coord++; - fvPosOut[1] = *coord++; - fvPosOut[2] = *coord; - DLOG("Position (face: %d, vert: %d): %f, %f, %f; idx: %lu\n", iFace, iVert, - fvPosOut[0], fvPosOut[1], fvPosOut[2], idx); -} - -void SMikk_get_normal(const SMikkTSpaceContext* pContext, float fvNormOut[], - const int iFace, const int iVert) { - const SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - const cgltf_size idx = get_vertex_index(data, iFace, iVert); - const float* coord = get_normal(data, idx); - fvNormOut[0] = *coord++; - fvNormOut[1] = *coord++; - fvNormOut[2] = *coord; - DLOG("Normal (face: %d, vert: %d): %f, %f, %f\n", iFace, iVert, fvNormOut[0], - fvNormOut[1], fvNormOut[2]); -} - -void SMikk_get_texcoord(const SMikkTSpaceContext* pContext, float fvTexcOut[], - const int iFace, const int iVert) { - const SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - const cgltf_size idx = get_vertex_index(data, iFace, iVert); - const void* coord = get_texcoord(data, idx); - switch (data->texcoords.type) { - case cgltf_component_type_r_8: { - const int8_t* c = coord; - fvTexcOut[0] = normalize_i8(*c++); - fvTexcOut[1] = normalize_i8(*c); - break; - } - case cgltf_component_type_r_8u: { - const uint8_t* c = coord; - fvTexcOut[0] = normalize_u8(*c++); - fvTexcOut[1] = normalize_u8(*c); - break; - } - case cgltf_component_type_r_16: { - const int16_t* c = coord; - fvTexcOut[0] = normalize_i16(*c++); - fvTexcOut[1] = normalize_i16(*c); - break; - } - case cgltf_component_type_r_16u: { - const uint16_t* c = coord; - fvTexcOut[0] = normalize_u16(*c++); - fvTexcOut[1] = normalize_u16(*c); - break; - } - case cgltf_component_type_r_32u: { - const uint32_t* c = coord; - fvTexcOut[0] = normalize_u32(*c++); - fvTexcOut[1] = normalize_u32(*c); - break; - } - case cgltf_component_type_r_32f: { - const float* c = coord; - fvTexcOut[0] = *c++; - fvTexcOut[1] = *c; - break; - } - default: - assert(false); - break; - } - DLOG("Texcoord (face: %d, vert: %d): %f, %f\n", iFace, iVert, fvTexcOut[0], - fvTexcOut[1]); -} - -void SMikk_set_TSpace_basic(const SMikkTSpaceContext* pContext, - const float fvTangent[], const float fSign, - const int iFace, const int iVert) { - SMikkUserData* data = (SMikkUserData*)pContext->m_pUserData; - const cgltf_primitive* primitive = data->primitive; - - const cgltf_size idx = get_vertex_index(data, iFace, iVert); - float* coord = get_tangent(data->tangents, idx); - *coord++ = fvTangent[0]; - *coord++ = fvTangent[1]; - *coord++ = fvTangent[2]; - *coord = fSign; - DLOG("Tangent (face: %d, vert: %d): %f, %f, %f; sign: %f\n", iFace, iVert, - fvTangent[0], fvTangent[1], fvTangent[2], fSign); -} - -// ----------------------------------------------------------------------------- - -static bool has_normal_map(const cgltf_primitive* primitive) { - return (primitive->material != NULL) && - (primitive->material->normal_texture.texture != NULL); -} - -static const cgltf_attribute* find_attribute(const cgltf_primitive* primitive, - cgltf_attribute_type type) { - for (cgltf_size i = 0; i < primitive->attributes_count; ++i) { - const cgltf_attribute* attrib = &primitive->attributes[i]; - if (attrib->type == type) { - return attrib; - } - } - return NULL; -} - -static bool has_attribute(const cgltf_primitive* primitive, - cgltf_attribute_type type) { - return find_attribute(primitive, type) != NULL; -} - -static bool has_positions3d(const cgltf_primitive* primitive) { - const cgltf_attribute* attrib = - find_attribute(primitive, cgltf_attribute_type_position); - if (attrib) { - return attrib->data->type == cgltf_type_vec3; - } - return false; -} - -static bool has_normals(const cgltf_primitive* primitive) { - return has_attribute(primitive, cgltf_attribute_type_normal); -} - -static bool has_texcoords(const cgltf_primitive* primitive) { - return has_attribute(primitive, cgltf_attribute_type_texcoord); -} - -static bool has_tangents(const cgltf_primitive* primitive) { - return has_attribute(primitive, cgltf_attribute_type_tangent); -} - -static bool has_indices(const cgltf_primitive* primitive) { - return primitive->indices != 0; -} - -static cgltfTangentBuffer compute_tangents(const cgltf_options* options, - const cgltf_data* data, - cgltf_primitive* primitive) { - cgltfTangentBuffer buffer = {0}; - SMikkUserData user = {0}; - cgltf_size num_verts = 0; - - user.primitive = primitive; - - if (primitive->indices != NULL) { - const cgltf_accessor* accessor = primitive->indices; - const cgltf_buffer_view* view = accessor->buffer_view; - const cgltf_size offset_bytes = accessor->offset + view->offset; - const void* buffer_data = view->buffer->data + offset_bytes; - const void* buffer_end = view->buffer->data + view->offset + view->size; - - user.indices.start = buffer_data; - user.indices.end = buffer_end; - // Indices are tightly packed, stride 0. - user.indices.stride_bytes = - default_stride(accessor->type, accessor->component_type); - user.indices.type = accessor->component_type; - } - - for (cgltf_size i = 0; i < primitive->attributes_count; ++i) { - const cgltf_attribute* attrib = &primitive->attributes[i]; - - if ((attrib->type == cgltf_attribute_type_position) || - (attrib->type == cgltf_attribute_type_normal) || - (attrib->type == cgltf_attribute_type_texcoord)) { - const cgltf_accessor* accessor = attrib->data; - const cgltf_buffer_view* view = accessor->buffer_view; - const cgltf_buffer* buffer = view->buffer; - const cgltf_size offset_bytes = accessor->offset + view->offset; - const cgltf_size stride_bytes = - view->stride > 0 - ? view->stride - : default_stride(accessor->type, accessor->component_type); - // const cgltf_size size_bytes = view->size; - const void* buffer_data = view->buffer->data + offset_bytes; - const void* buffer_end = view->buffer->data + view->offset + view->size; - - Buffer* attrib_buffer = 0; - - if (attrib->type == cgltf_attribute_type_position) { - // glTF currently mandates vec3 for positions. Caller should ensure - // this. - assert(accessor->type == cgltf_type_vec3); - num_verts = attrib->data->count; - attrib_buffer = &user.positions; - } else if (attrib->type == cgltf_attribute_type_normal) { - attrib_buffer = &user.normals; - } else if (attrib->type == cgltf_attribute_type_texcoord) { - attrib_buffer = &user.texcoords; - } - - attrib_buffer->start = buffer_data; - attrib_buffer->end = buffer_end; - attrib_buffer->stride_bytes = stride_bytes; - attrib_buffer->type = accessor->component_type; - } - } - - assert(user.positions.start); - assert(user.positions.end); - assert(user.normals.start); - assert(user.normals.end); - assert(user.texcoords.start); - assert(user.texcoords.end); - assert(num_verts > 0); - - const cgltf_size tangents_size_bytes = - num_verts * NUM_TANGENT_COMPONENTS * sizeof(float); - - user.tangents = CGLTF_OPTIONS_MALLOC(tangents_size_bytes); - if (!user.tangents) { - return buffer; - } - - SMikkTSpaceInterface interface = (SMikkTSpaceInterface){ - .m_getNumFaces = SMikk_get_num_faces, - .m_getNumVerticesOfFace = SMikk_get_num_vertices_of_face, - .m_getPosition = SMikk_get_position, - .m_getNormal = SMikk_get_normal, - .m_getTexCoord = SMikk_get_texcoord, - .m_setTSpaceBasic = SMikk_set_TSpace_basic, - }; - const SMikkTSpaceContext context = (SMikkTSpaceContext){ - .m_pInterface = &interface, - .m_pUserData = &user, - }; - if (!genTangSpaceDefault(&context)) { - return buffer; - } - - buffer.data = user.tangents; - buffer.size_bytes = tangents_size_bytes; - buffer.primitive = primitive; - - return buffer; -} - -static void process_primitive(const cgltf_options* options, - const cgltf_data* data, - cgltf_primitive* primitive, - cgltfTangentBuffer* tangent_buffers, - cgltf_size* num_tangent_buffers) { - DLOG("Processing primitive\n"); - cgltf_size cur_buffer = 0; - // TODO: MikkTSpace should not be used with models with vertex indices. One - // workaround is to unindex the mesh, compute tangents, and then re-index it. - if (((primitive->type == cgltf_primitive_type_triangle_fan) || - (primitive->type == cgltf_primitive_type_triangle_strip) || - (primitive->type == cgltf_primitive_type_triangles)) && - has_normal_map(primitive) && !has_tangents(primitive) && - has_positions3d(primitive) && has_normals(primitive) && - has_texcoords(primitive) && !has_indices(primitive)) { - *num_tangent_buffers += 1; - if (tangent_buffers) { - DLOG("Model with normal map missing tangents detected\n"); - tangent_buffers[cur_buffer] = compute_tangents(options, data, primitive); - if (tangent_buffers[cur_buffer].data) { - DLOG("Tangents computed\n"); - } - cur_buffer++; - } - } -} - -cgltf_result cgltf_compute_tangents(const cgltf_options* input_options, - const cgltf_data* data, - cgltfTangentBuffer** tangent_buffers, - cgltf_size* num_tangent_buffers) { - if ((input_options == NULL) || (data == NULL)) { - return cgltf_result_invalid_options; - } - - DLOG("cgltf_compute_tangents\n"); - - cgltf_options options = *input_options; - if (options.memory.alloc == NULL) { - options.memory.alloc = &cgltf_default_alloc; - } - if (options.memory.free == NULL) { - options.memory.free = &cgltf_default_free; - } - - // First pass: compute the number of tangent buffers to be created. - *num_tangent_buffers = 0; - for (cgltf_size mesh_idx = 0; mesh_idx < data->meshes_count; ++mesh_idx) { - const cgltf_mesh* mesh = &data->meshes[mesh_idx]; - - for (cgltf_size prim_idx = 0; prim_idx < mesh->primitives_count; - ++prim_idx) { - // Pass in null for the tangent buffers to just compute the number of - // buffers. - process_primitive(&options, data, &mesh->primitives[prim_idx], 0, - num_tangent_buffers); - } - } - DLOG("Number of primitives to be patched: %lu\n", *num_tangent_buffers); - - // Second pass: compute the tangents. - if (*num_tangent_buffers > 0) { - *tangent_buffers = - options.memory.alloc(options.memory.user_data, - *num_tangent_buffers * sizeof(cgltfTangentBuffer)); - if (!*tangent_buffers) { - return cgltf_result_out_of_memory; - } - - cgltf_size tangent_buffers_computed = 0; - - for (cgltf_size mesh_idx = 0; mesh_idx < data->meshes_count; ++mesh_idx) { - const cgltf_mesh* mesh = &data->meshes[mesh_idx]; - - for (cgltf_size prim_idx = 0; prim_idx < mesh->primitives_count; - ++prim_idx) { - process_primitive(&options, data, &mesh->primitives[prim_idx], - *tangent_buffers, &tangent_buffers_computed); - } - } - - assert(tangent_buffers_computed == *num_tangent_buffers); - } - - return cgltf_result_success; -} diff --git a/contrib/cgltf-tangents/cgltf_tangents.h b/contrib/cgltf-tangents/cgltf_tangents.h deleted file mode 100644 index 79e3502..0000000 --- a/contrib/cgltf-tangents/cgltf_tangents.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -Copyright 2022 Marc Sunet - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -#ifndef CGLTF_TANGENTS_H_INCLUDED__ -#define CGLTF_TANGENTS_H_INCLUDED__ - -#include - -/// A buffer that holds tangent vectors. -/// -/// Tangent vectors are tightly packed in the array. -/// -/// Tangent vectors have 4 coordinates: (X,Y,Z) for the vector, W for the sign. -/// The usual rules of MikkTSpace apply, namely that the bitangent should be -/// computed as: -/// -/// bitangent = tangent.w * cross(normal, tangent.xyz); -/// -/// Refer to the MikkTSpace documentation for more details. -/// -/// The primitive pointer points to the mesh primitive for which the tangents in -/// this buffer were computed. When your application loads mesh primitives, it -/// can scan the cgltfTangetBuffer array outputed by cgltf_compute_tangents() to -/// see whether tangents were computed for the mesh primitive. -typedef struct cgltfTangentBuffer { - void* data; // X-coordinate of the first tangent vector. - cgltf_size size_bytes; // Total Size of data in bytes. - cgltf_primitive* primitive; // The primitive these tangents belong to. -} cgltfTangentBuffer; - -/// Compute tangent vectors for normal-mapped mesh primitives missing them. -/// -/// cgltf_options can be zeroed out but must be non-null. -/// -/// cgltf_data is the scene previously loaded by cgltf. -/// -/// out_tangent_buffers is an output array of tangent buffers, one buffer per -/// mesh primitive for which tangents were computed. -/// -/// out_num_tangent_buffers is the number of tangent buffers in the output -/// array. -cgltf_result cgltf_compute_tangents(const cgltf_options*, const cgltf_data*, - cgltfTangentBuffer** out_tangent_buffers, - cgltf_size* out_num_tangent_buffers); - -#endif // CGLTF_TANGENTS_H_INCLUDED__ diff --git a/contrib/cgltf-tangents/test/CMakeLists.txt b/contrib/cgltf-tangents/test/CMakeLists.txt deleted file mode 100644 index 422c950..0000000 --- a/contrib/cgltf-tangents/test/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.0) - -project (cgltf-test) - -add_executable(cgltf-test - main.c) - -target_link_libraries(cgltf-test - cgltf - cgltf-tangents - -lm) diff --git a/contrib/cgltf-tangents/test/main.c b/contrib/cgltf-tangents/test/main.c deleted file mode 100644 index 0d70008..0000000 --- a/contrib/cgltf-tangents/test/main.c +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright 2022 Marc Sunet - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -#include -#define CGLTF_IMPLEMENTATION -#include - -#include - -void print_tangent_buffer(const cgltfTangentBuffer* buffer, int max_vectors) { - printf("Tangent buffer for primitive (%p) (%lu bytes):\n", buffer->primitive, - buffer->size_bytes); - - const float* xyzw = (const float*)buffer->data; - const float* end = (const float*)(buffer->data + buffer->size_bytes); - - for (int i = 0; i < max_vectors && xyzw < end; ++i, xyzw += 4) { - printf("(%3.2f, %3.2f, %3.2f, sign: %3.2f)\n", *xyzw, *(xyzw + 1), - *(xyzw + 2), *(xyzw + 3)); - } - printf("--------------------"); -} - -void usage(const char* argv0) { - fprintf(stderr, "Usage: %s \n", argv0); -} - -int main(int argc, const char** argv) { - cgltf_options options = {0}; - cgltf_data* data = NULL; - - if (argc != 2) { - usage(argv[0]); - return 0; - } - - const char* filepath = argv[1]; - - cgltf_result result = cgltf_parse_file(&options, filepath, &data); - if (result != cgltf_result_success) { - cgltf_free(data); - return 1; - } - - // Must call cgltf_load_buffers() to load buffer data. - result = cgltf_load_buffers(&options, data, filepath); - if (result != cgltf_result_success) { - cgltf_free(data); - return 2; - } - - cgltfTangentBuffer* tangent_buffers = 0; - cgltf_size num_tangent_buffers = 0; - cgltf_compute_tangents(&options, data, &tangent_buffers, - &num_tangent_buffers); - - // cgltf scene not needed beyond this point. - cgltf_free(data); - - for (cgltf_size i = 0; i < num_tangent_buffers; ++i) { - print_tangent_buffer(tangent_buffers, 10); - } - - return 0; -} -- cgit v1.2.3