Ticket #1429: bumpy2.diff

File bumpy2.diff, 113.0 KB (added by myconid, 12 months ago)

Adds normal, parallax, specular and self-illumination mapping

  • source/graphics/Material.h

     
    3838 
    3939    void SetDiffuseTexture(const CTexturePtr& texture); 
    4040    const CTexturePtr& GetDiffuseTexture() const { return m_DiffuseTexture; } 
     41     
     42    void SetNormalTexture(const CTexturePtr& texture); 
     43    const CTexturePtr& GetNormalTexture() const { return m_NormalTexture; } 
     44     
     45    void SetSpecularTexture(const CTexturePtr& texture); 
     46    const CTexturePtr& GetSpecularTexture() const { return m_SpecularTexture; } 
    4147 
    4248    void SetShaderEffect(const CStr& effect); 
    4349    CStrIntern GetShaderEffect() const { return m_ShaderEffect; } 
     
    5056 
    5157private: 
    5258    CTexturePtr m_DiffuseTexture; 
     59    CTexturePtr m_NormalTexture; 
     60    CTexturePtr m_SpecularTexture; 
    5361    CStrIntern m_ShaderEffect; 
    5462    CShaderDefines m_ShaderDefines; 
    5563    CShaderUniforms m_StaticUniforms; 
  • source/graphics/ObjectEntry.h

     
    4747    CObjectBase* m_Base; 
    4848 
    4949    VfsPath m_TextureName; 
     50    VfsPath m_NormalName; 
     51    VfsPath m_SpecularName; 
    5052    // model name 
    5153    VfsPath m_ModelName; 
    5254    // colour (used when doing alpha-channel colouring, but not doing player-colour) 
  • source/graphics/mikktspace.h

     
     1/** \file mikktspace/mikktspace.h 
     2 *  \ingroup mikktspace 
     3 */ 
     4/** 
     5 *  Copyright (C) 2011 by Morten S. Mikkelsen 
     6 * 
     7 *  This software is provided 'as-is', without any express or implied 
     8 *  warranty.  In no event will the authors be held liable for any damages 
     9 *  arising from the use of this software. 
     10 * 
     11 *  Permission is granted to anyone to use this software for any purpose, 
     12 *  including commercial applications, and to alter it and redistribute it 
     13 *  freely, subject to the following restrictions: 
     14 * 
     15 *  1. The origin of this software must not be misrepresented; you must not 
     16 *     claim that you wrote the original software. If you use this software 
     17 *     in a product, an acknowledgment in the product documentation would be 
     18 *     appreciated but is not required. 
     19 *  2. Altered source versions must be plainly marked as such, and must not be 
     20 *     misrepresented as being the original software. 
     21 *  3. This notice may not be removed or altered from any source distribution. 
     22 */ 
     23 
     24#ifndef __MIKKTSPACE_H__ 
     25#define __MIKKTSPACE_H__ 
     26 
     27 
     28#ifdef __cplusplus 
     29extern "C" { 
     30#endif 
     31 
     32/* Author: Morten S. Mikkelsen 
     33 * Version: 1.0 
     34 * 
     35 * The files mikktspace.h and mikktspace.c are designed to be 
     36 * stand-alone files and it is important that they are kept this way. 
     37 * Not having dependencies on structures/classes/libraries specific 
     38 * to the program, in which they are used, allows them to be copied 
     39 * and used as is into any tool, program or plugin. 
     40 * The code is designed to consistently generate the same 
     41 * tangent spaces, for a given mesh, in any tool in which it is used. 
     42 * This is done by performing an internal welding step and subsequently an order-independent evaluation 
     43 * of tangent space for meshes consisting of triangles and quads. 
     44 * This means faces can be received in any order and the same is true for 
     45 * the order of vertices of each face. The generated result will not be affected 
     46 * by such reordering. Additionally, whether degenerate (vertices or texture coordinates) 
     47 * primitives are present or not will not affect the generated results either. 
     48 * Once tangent space calculation is done the vertices of degenerate primitives will simply 
     49 * inherit tangent space from neighboring non degenerate primitives. 
     50 * The analysis behind this implementation can be found in my master's thesis 
     51 * which is available for download --> http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf 
     52 * Note that though the tangent spaces at the vertices are generated in an order-independent way, 
     53 * by this implementation, the interpolated tangent space is still affected by which diagonal is 
     54 * chosen to split each quad. A sensible solution is to have your tools pipeline always 
     55 * split quads by the shortest diagonal. This choice is order-independent and works with mirroring. 
     56 * If these have the same length then compare the diagonals defined by the texture coordinates. 
     57 * XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin 
     58 * and also quad triangulator plugin. 
     59 */ 
     60 
     61 
     62typedef int tbool; 
     63typedef struct SMikkTSpaceContext SMikkTSpaceContext; 
     64 
     65typedef struct 
     66{ 
     67    // Returns the number of faces (triangles/quads) on the mesh to be processed. 
     68    int (*m_getNumFaces)(const SMikkTSpaceContext * pContext); 
     69 
     70    // Returns the number of vertices on face number iFace 
     71    // iFace is a number in the range {0, 1, ..., getNumFaces()-1} 
     72    int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace); 
     73 
     74    // returns the position/normal/texcoord of the referenced face of vertex number iVert. 
     75    // iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads. 
     76    void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert); 
     77    void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert); 
     78    void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert); 
     79 
     80    // either (or both) of the two setTSpace callbacks can be set. 
     81    // The call-back m_setTSpaceBasic() is sufficient for basic normal mapping. 
     82 
     83    // This function is used to return the tangent and fSign to the application. 
     84    // fvTangent is a unit length vector. 
     85    // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. 
     86    // bitangent = fSign * cross(vN, tangent); 
     87    // Note that the results are returned unindexed. It is possible to generate a new index list 
     88    // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. 
     89    // DO NOT! use an already existing index list. 
     90    void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert); 
     91 
     92    // This function is used to return tangent space results to the application. 
     93    // fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their 
     94    // true magnitudes which can be used for relief mapping effects. 
     95    // fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent. 
     96    // However, both are perpendicular to the vertex normal. 
     97    // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. 
     98    // fSign = bIsOrientationPreserving ? 1.0f : (-1.0f); 
     99    // bitangent = fSign * cross(vN, tangent); 
     100    // Note that the results are returned unindexed. It is possible to generate a new index list 
     101    // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. 
     102    // DO NOT! use an already existing index list. 
     103    void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, 
     104                        const tbool bIsOrientationPreserving, const int iFace, const int iVert); 
     105} SMikkTSpaceInterface; 
     106 
     107struct SMikkTSpaceContext 
     108{ 
     109    SMikkTSpaceInterface * m_pInterface;    // initialized with callback functions 
     110    void * m_pUserData;                     // pointer to client side mesh data etc. (passed as the first parameter with every interface call) 
     111}; 
     112 
     113// these are both thread safe! 
     114tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled) 
     115tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold); 
     116 
     117 
     118// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the 
     119// normal map sampler must use the exact inverse of the pixel shader transformation. 
     120// The most efficient transformation we can possibly do in the pixel shader is 
     121// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN. 
     122// pixel shader (fast transform out) 
     123// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); 
     124// where vNt is the tangent space normal. The normal map sampler must likewise use the 
     125// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader. 
     126// sampler does (exact inverse of pixel shader): 
     127// float3 row0 = cross(vB, vN); 
     128// float3 row1 = cross(vN, vT); 
     129// float3 row2 = cross(vT, vB); 
     130// float fSign = dot(vT, row0)<0 ? -1 : 1; 
     131// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) ); 
     132// where vNout is the sampled normal in some chosen 3D space. 
     133// 
     134// Should you choose to reconstruct the bitangent in the pixel shader instead 
     135// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also. 
     136// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of 
     137// quads as your renderer then problems will occur since the interpolated tangent spaces will differ 
     138// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before 
     139// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier. 
     140// However, this must be used both by the sampler and your tools/rendering pipeline. 
     141 
     142#ifdef __cplusplus 
     143} 
     144#endif 
     145 
     146#endif 
  • source/graphics/weldmesh.cpp

     
     1/** 
     2 *  Copyright (C) 2011 by Morten S. Mikkelsen 
     3 * 
     4 *  This software is provided 'as-is', without any express or implied 
     5 *  warranty.  In no event will the authors be held liable for any damages 
     6 *  arising from the use of this software. 
     7 * 
     8 *  Permission is granted to anyone to use this software for any purpose, 
     9 *  including commercial applications, and to alter it and redistribute it 
     10 *  freely, subject to the following restrictions: 
     11 * 
     12 *  1. The origin of this software must not be misrepresented; you must not 
     13 *     claim that you wrote the original software. If you use this software 
     14 *     in a product, an acknowledgment in the product documentation would be 
     15 *     appreciated but is not required. 
     16 *  2. Altered source versions must be plainly marked as such, and must not be 
     17 *     misrepresented as being the original software. 
     18 *  3. This notice may not be removed or altered from any source distribution. 
     19 */ 
     20 
     21 
     22#include "weldmesh.h" 
     23#include <string.h> 
     24#include <assert.h> 
     25 
     26#ifdef __APPLE__ 
     27#include <stdlib.h>  /* OSX gets its malloc stuff through here */ 
     28#else 
     29#include <malloc.h>  
     30#endif 
     31 
     32static void MergeVertsFast(int * piCurNrUniqueVertices, int * piRemapTable, float * pfVertexDataOut, int * piVertexIDs, 
     33              const float pfVertexDataIn[], const int iNrVerticesIn, const int iFloatsPerVert, 
     34              const int iL_in, const int iR_in, const int iChannelNum); 
     35 
     36int WeldMesh(int * piRemapTable, float * pfVertexDataOut, 
     37              const float pfVertexDataIn[], const int iNrVerticesIn, const int iFloatsPerVert) 
     38{ 
     39    int iUniqueVertices = 0, i=0; 
     40    int * piVertexIDs = NULL; 
     41    if(iNrVerticesIn<=0) return 0; 
     42 
     43 
     44    iUniqueVertices = 0; 
     45    piVertexIDs = (int *) malloc(sizeof(int)*iNrVerticesIn); 
     46    if(piVertexIDs!=NULL) 
     47    { 
     48        for(i=0; i<iNrVerticesIn; i++) 
     49        { 
     50            piRemapTable[i] = -1; 
     51            piVertexIDs[i] = i; 
     52        } 
     53 
     54        MergeVertsFast(&iUniqueVertices, piRemapTable, pfVertexDataOut, piVertexIDs, 
     55                                         pfVertexDataIn, iNrVerticesIn, iFloatsPerVert, 0, iNrVerticesIn-1, 0); 
     56 
     57        free(piVertexIDs); 
     58 
     59        // debug check 
     60        for(i=0; i<iUniqueVertices; i++) 
     61            assert(piRemapTable[i]>=0); 
     62    } 
     63 
     64    return iUniqueVertices; 
     65} 
     66 
     67 
     68 
     69 
     70 
     71static void MergeVertsFast(int * piCurNrUniqueVertices, int * piRemapTable, float * pfVertexDataOut, int * piVertexIDs, 
     72              const float pfVertexDataIn[], const int iNrVerticesIn, const int iFloatsPerVert, 
     73              const int iL_in, const int iR_in, const int iChannelNum) 
     74{ 
     75    const int iCount = iR_in-iL_in+1; 
     76    int l=0; 
     77    float fMin, fMax, fAvg; 
     78    assert(iCount>0); 
     79    // make bbox 
     80    fMin = pfVertexDataIn[ piVertexIDs[iL_in]*iFloatsPerVert + iChannelNum]; fMax = fMin; 
     81    for(l=(iL_in+1); l<=iR_in; l++) 
     82    { 
     83        const int index = piVertexIDs[l]*iFloatsPerVert + iChannelNum; 
     84        const float fVal = pfVertexDataIn[index]; 
     85        if(fMin>fVal) fMin=fVal; 
     86        else if(fMax<fVal) fMax=fVal; 
     87    } 
     88 
     89    // terminate recursion when the separation/average value 
     90    // is no longer strictly between fMin and fMax values. 
     91    fAvg = 0.5f*(fMax + fMin); 
     92    if(fAvg<=fMin || fAvg>=fMax || iCount==1) 
     93    { 
     94        if((iChannelNum+1) == iFloatsPerVert || iCount==1)  // we are done, weld by hand 
     95        { 
     96            int iUniqueNewVertices = 0; 
     97            float * pfNewUniVertsOut = &pfVertexDataOut[ piCurNrUniqueVertices[0]*iFloatsPerVert ]; 
     98 
     99            for(l=iL_in; l<=iR_in; l++) 
     100            { 
     101                const int index = piVertexIDs[l]*iFloatsPerVert; 
     102 
     103                int iFound = 0; // didn't find copy yet. 
     104                int l2=0; 
     105                while(l2<iUniqueNewVertices && iFound==0) 
     106                { 
     107                    const int index2 = l2*iFloatsPerVert; 
     108 
     109                    int iAllSame = 1; 
     110                    int c=0; 
     111                    while(iAllSame!=0 && c<iFloatsPerVert) 
     112                    { 
     113                        iAllSame &= (pfVertexDataIn[index+c] == pfNewUniVertsOut[index2+c] ? 1 : 0); 
     114                        ++c; 
     115                    } 
     116 
     117                    iFound = iAllSame; 
     118                    if(iFound==0) ++l2; 
     119                } 
     120                 
     121                // generate new entry 
     122                if(iFound==0) 
     123                { 
     124                    memcpy(pfNewUniVertsOut+iUniqueNewVertices*iFloatsPerVert, pfVertexDataIn+index, sizeof(float)*iFloatsPerVert); 
     125                    ++iUniqueNewVertices; 
     126                } 
     127 
     128                assert(piRemapTable[piVertexIDs[l]] == -1); // has not yet been assigned 
     129                piRemapTable[piVertexIDs[l]] = piCurNrUniqueVertices[0] + l2; 
     130            } 
     131 
     132            piCurNrUniqueVertices[0] += iUniqueNewVertices; 
     133        } 
     134        else 
     135        { 
     136            MergeVertsFast(piCurNrUniqueVertices, piRemapTable, pfVertexDataOut, piVertexIDs, 
     137                           pfVertexDataIn, iNrVerticesIn, iFloatsPerVert, 
     138                            iL_in, iR_in, iChannelNum+1); 
     139        } 
     140    } 
     141    else 
     142    { 
     143        int iL=iL_in, iR=iR_in, index; 
     144 
     145        // seperate (by fSep) all points between iL_in and iR_in in pTmpVert[] 
     146        while(iL < iR) 
     147        { 
     148            int iReadyLeftSwap = 0; 
     149            int iReadyRightSwap = 0; 
     150            while(iReadyLeftSwap==0 && iL<iR) 
     151            { 
     152                assert(iL>=iL_in && iL<=iR_in); 
     153                index = piVertexIDs[iL]*iFloatsPerVert+iChannelNum; 
     154                iReadyLeftSwap = !(pfVertexDataIn[index]<fAvg) ? 1 : 0; 
     155                if(iReadyLeftSwap==0) ++iL; 
     156            } 
     157            while(iReadyRightSwap==0 && iL<iR) 
     158            { 
     159                assert(iR>=iL_in && iR<=iR_in); 
     160                index = piVertexIDs[iR]*iFloatsPerVert+iChannelNum; 
     161                iReadyRightSwap = pfVertexDataIn[index]<fAvg ? 1 : 0; 
     162                if(iReadyRightSwap==0) --iR; 
     163            } 
     164            assert( (iL<iR) || (iReadyLeftSwap==0 || iReadyRightSwap==0)); 
     165 
     166            if(iReadyLeftSwap!=0 && iReadyRightSwap!=0) 
     167            { 
     168                int iID=0; 
     169                assert(iL<iR); 
     170                iID = piVertexIDs[iL]; 
     171                piVertexIDs[iL] = piVertexIDs[iR]; 
     172                piVertexIDs[iR] = iID; 
     173                ++iL; --iR; 
     174            } 
     175        } 
     176 
     177        assert(iL==(iR+1) || (iL==iR)); 
     178        if(iL==iR) 
     179        { 
     180            const int index = piVertexIDs[iR]*iFloatsPerVert+iChannelNum; 
     181            const int iReadyRightSwap = pfVertexDataIn[index]<fAvg ? 1 : 0; 
     182            if(iReadyRightSwap!=0) ++iL; 
     183            else --iR; 
     184        } 
     185 
     186        // recurse 
     187        if(iL_in <= iR) 
     188            MergeVertsFast(piCurNrUniqueVertices, piRemapTable, pfVertexDataOut, piVertexIDs, 
     189                           pfVertexDataIn, iNrVerticesIn, iFloatsPerVert, iL_in, iR, iChannelNum);  // weld all left of fSep 
     190        if(iL <= iR_in) 
     191            MergeVertsFast(piCurNrUniqueVertices, piRemapTable, pfVertexDataOut, piVertexIDs, 
     192                           pfVertexDataIn, iNrVerticesIn, iFloatsPerVert, iL, iR_in, iChannelNum);  // weld all right of (or equal to) fSep 
     193    } 
     194} 
     195 No newline at end of file 
  • source/graphics/mikktspace.cpp

     
     1/** \file mikktspace/mikktspace.c 
     2 *  \ingroup mikktspace 
     3 */ 
     4/** 
     5 *  Copyright (C) 2011 by Morten S. Mikkelsen 
     6 * 
     7 *  This software is provided 'as-is', without any express or implied 
     8 *  warranty.  In no event will the authors be held liable for any damages 
     9 *  arising from the use of this software. 
     10 * 
     11 *  Permission is granted to anyone to use this software for any purpose, 
     12 *  including commercial applications, and to alter it and redistribute it 
     13 *  freely, subject to the following restrictions: 
     14 * 
     15 *  1. The origin of this software must not be misrepresented; you must not 
     16 *     claim that you wrote the original software. If you use this software 
     17 *     in a product, an acknowledgment in the product documentation would be 
     18 *     appreciated but is not required. 
     19 *  2. Altered source versions must be plainly marked as such, and must not be 
     20 *     misrepresented as being the original software. 
     21 *  3. This notice may not be removed or altered from any source distribution. 
     22 */ 
     23 
     24#include <assert.h> 
     25#include <stdio.h> 
     26#include <math.h> 
     27#include <string.h> 
     28#include <float.h> 
     29#include <stdlib.h> 
     30 
     31#include "mikktspace.h" 
     32 
     33#define TFALSE      0 
     34#define TTRUE       1 
     35 
     36#ifndef M_PI 
     37#define M_PI    3.1415926535897932384626433832795 
     38#endif 
     39 
     40#define INTERNAL_RND_SORT_SEED      39871946 
     41 
     42// internal structure 
     43typedef struct 
     44{ 
     45    float x, y, z; 
     46} SVec3; 
     47 
     48static tbool            veq( const SVec3 v1, const SVec3 v2 ) 
     49{ 
     50    return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); 
     51} 
     52 
     53static SVec3        vadd( const SVec3 v1, const SVec3 v2 ) 
     54{ 
     55    SVec3 vRes; 
     56 
     57    vRes.x = v1.x + v2.x; 
     58    vRes.y = v1.y + v2.y; 
     59    vRes.z = v1.z + v2.z; 
     60 
     61    return vRes; 
     62} 
     63 
     64 
     65static SVec3        vsub( const SVec3 v1, const SVec3 v2 ) 
     66{ 
     67    SVec3 vRes; 
     68 
     69    vRes.x = v1.x - v2.x; 
     70    vRes.y = v1.y - v2.y; 
     71    vRes.z = v1.z - v2.z; 
     72 
     73    return vRes; 
     74} 
     75 
     76static SVec3        vscale(const float fS, const SVec3 v) 
     77{ 
     78    SVec3 vRes; 
     79 
     80    vRes.x = fS * v.x; 
     81    vRes.y = fS * v.y; 
     82    vRes.z = fS * v.z; 
     83 
     84    return vRes; 
     85} 
     86 
     87static float            LengthSquared( const SVec3 v ) 
     88{ 
     89    return v.x*v.x + v.y*v.y + v.z*v.z; 
     90} 
     91 
     92static float            Length( const SVec3 v ) 
     93{ 
     94    return sqrtf(LengthSquared(v)); 
     95} 
     96 
     97static SVec3        Normalize( const SVec3 v ) 
     98{ 
     99    return vscale(1 / Length(v), v); 
     100} 
     101 
     102static float        vdot( const SVec3 v1, const SVec3 v2) 
     103{ 
     104    return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; 
     105} 
     106 
     107 
     108static tbool NotZero(const float fX) 
     109{ 
     110    // could possibly use FLT_EPSILON instead 
     111    return fabsf(fX) > FLT_MIN; 
     112} 
     113 
     114static tbool VNotZero(const SVec3 v) 
     115{ 
     116    // might change this to an epsilon based test 
     117    return NotZero(v.x) || NotZero(v.y) || NotZero(v.z); 
     118} 
     119 
     120 
     121 
     122typedef struct 
     123{ 
     124    int iNrFaces; 
     125    int * pTriMembers; 
     126} SSubGroup; 
     127 
     128typedef struct 
     129{ 
     130    int iNrFaces; 
     131    int * pFaceIndices; 
     132    int iVertexRepresentitive; 
     133    tbool bOrientPreservering; 
     134} SGroup; 
     135 
     136//  
     137#define MARK_DEGENERATE             1 
     138#define QUAD_ONE_DEGEN_TRI          2 
     139#define GROUP_WITH_ANY              4 
     140#define ORIENT_PRESERVING           8 
     141 
     142 
     143 
     144typedef struct 
     145{ 
     146    int FaceNeighbors[3]; 
     147    SGroup * AssignedGroup[3]; 
     148     
     149    // normalized first order face derivatives 
     150    SVec3 vOs, vOt; 
     151    float fMagS, fMagT; // original magnitudes 
     152 
     153    // determines if the current and the next triangle are a quad. 
     154    int iOrgFaceNumber; 
     155    int iFlag, iTSpacesOffs; 
     156    unsigned char vert_num[4]; 
     157} STriInfo; 
     158 
     159typedef struct 
     160{ 
     161    SVec3 vOs; 
     162    float fMagS; 
     163    SVec3 vOt; 
     164    float fMagT; 
     165    int iCounter;   // this is to average back into quads. 
     166    tbool bOrient; 
     167} STSpace; 
     168 
     169static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); 
     170static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); 
     171static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); 
     172static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn); 
     173static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], 
     174                             const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, 
     175                             const SMikkTSpaceContext * pContext); 
     176 
     177static int MakeIndex(const int iFace, const int iVert) 
     178{ 
     179    assert(iVert>=0 && iVert<4 && iFace>=0); 
     180    return (iFace<<2) | (iVert&0x3); 
     181} 
     182 
     183static void IndexToData(int * piFace, int * piVert, const int iIndexIn) 
     184{ 
     185    piVert[0] = iIndexIn&0x3; 
     186    piFace[0] = iIndexIn>>2; 
     187} 
     188 
     189static STSpace AvgTSpace(const STSpace * pTS0, const STSpace * pTS1) 
     190{ 
     191    STSpace ts_res; 
     192 
     193    // this if is important. Due to floating point precision 
     194    // averaging when ts0==ts1 will cause a slight difference 
     195    // which results in tangent space splits later on 
     196    if (pTS0->fMagS==pTS1->fMagS && pTS0->fMagT==pTS1->fMagT && 
     197       veq(pTS0->vOs,pTS1->vOs) && veq(pTS0->vOt, pTS1->vOt)) 
     198    { 
     199        ts_res.fMagS = pTS0->fMagS; 
     200        ts_res.fMagT = pTS0->fMagT; 
     201        ts_res.vOs = pTS0->vOs; 
     202        ts_res.vOt = pTS0->vOt; 
     203    } 
     204    else 
     205    { 
     206        ts_res.fMagS = 0.5f*(pTS0->fMagS+pTS1->fMagS); 
     207        ts_res.fMagT = 0.5f*(pTS0->fMagT+pTS1->fMagT); 
     208        ts_res.vOs = vadd(pTS0->vOs,pTS1->vOs); 
     209        ts_res.vOt = vadd(pTS0->vOt,pTS1->vOt); 
     210        if ( VNotZero(ts_res.vOs) ) ts_res.vOs = Normalize(ts_res.vOs); 
     211        if ( VNotZero(ts_res.vOt) ) ts_res.vOt = Normalize(ts_res.vOt); 
     212    } 
     213 
     214    return ts_res; 
     215} 
     216 
     217 
     218 
     219static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index); 
     220static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index); 
     221static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index); 
     222 
     223 
     224// degen triangles 
     225static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris); 
     226static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris); 
     227 
     228 
     229tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext) 
     230{ 
     231    return genTangSpace(pContext, 180.0f); 
     232} 
     233 
     234tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold) 
     235{ 
     236    // count nr_triangles 
     237    int * piTriListIn = NULL, * piGroupTrianglesBuffer = NULL; 
     238    STriInfo * pTriInfos = NULL; 
     239    SGroup * pGroups = NULL; 
     240    STSpace * psTspace = NULL; 
     241    int iNrTrianglesIn = 0, f=0, t=0, i=0; 
     242    int iNrTSPaces = 0, iTotTris = 0, iDegenTriangles = 0, iNrMaxGroups = 0; 
     243    int iNrActiveGroups = 0, index = 0; 
     244    const int iNrFaces = pContext->m_pInterface->m_getNumFaces(pContext); 
     245    tbool bRes = TFALSE; 
     246    const float fThresCos = (float) cos((fAngularThreshold*(float)M_PI)/180.0f); 
     247 
     248    // verify all call-backs have been set 
     249    if ( pContext->m_pInterface->m_getNumFaces==NULL || 
     250        pContext->m_pInterface->m_getNumVerticesOfFace==NULL || 
     251        pContext->m_pInterface->m_getPosition==NULL || 
     252        pContext->m_pInterface->m_getNormal==NULL || 
     253        pContext->m_pInterface->m_getTexCoord==NULL ) 
     254        return TFALSE; 
     255 
     256    // count triangles on supported faces 
     257    for (f=0; f<iNrFaces; f++) 
     258    { 
     259        const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); 
     260        if (verts==3) ++iNrTrianglesIn; 
     261        else if(verts==4) iNrTrianglesIn += 2; 
     262    } 
     263    if (iNrTrianglesIn<=0) return TFALSE; 
     264 
     265    // allocate memory for an index list 
     266    piTriListIn = (int *) malloc(sizeof(int)*3*iNrTrianglesIn); 
     267    pTriInfos = (STriInfo *) malloc(sizeof(STriInfo)*iNrTrianglesIn); 
     268    if (piTriListIn==NULL || pTriInfos==NULL) 
     269    { 
     270        if (piTriListIn!=NULL) free(piTriListIn); 
     271        if (pTriInfos!=NULL) free(pTriInfos); 
     272        return TFALSE; 
     273    } 
     274 
     275    // make an initial triangle --> face index list 
     276    iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); 
     277 
     278    // make a welded index list of identical positions and attributes (pos, norm, texc) 
     279    //printf("gen welded index list begin\n"); 
     280    GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn); 
     281    //printf("gen welded index list end\n"); 
     282 
     283    // Mark all degenerate triangles 
     284    iTotTris = iNrTrianglesIn; 
     285    iDegenTriangles = 0; 
     286    for (t=0; t<iTotTris; t++) 
     287    { 
     288        const int i0 = piTriListIn[t*3+0]; 
     289        const int i1 = piTriListIn[t*3+1]; 
     290        const int i2 = piTriListIn[t*3+2]; 
     291        const SVec3 p0 = GetPosition(pContext, i0); 
     292        const SVec3 p1 = GetPosition(pContext, i1); 
     293        const SVec3 p2 = GetPosition(pContext, i2); 
     294        if (veq(p0,p1) || veq(p0,p2) || veq(p1,p2)) // degenerate 
     295        { 
     296            pTriInfos[t].iFlag |= MARK_DEGENERATE; 
     297            ++iDegenTriangles; 
     298        } 
     299    } 
     300    iNrTrianglesIn = iTotTris - iDegenTriangles; 
     301 
     302    // mark all triangle pairs that belong to a quad with only one 
     303    // good triangle. These need special treatment in DegenEpilogue(). 
     304    // Additionally, move all good triangles to the start of 
     305    // pTriInfos[] and piTriListIn[] without changing order and 
     306    // put the degenerate triangles last. 
     307    DegenPrologue(pTriInfos, piTriListIn, iNrTrianglesIn, iTotTris); 
     308 
     309     
     310    // evaluate triangle level attributes and neighbor list 
     311    //printf("gen neighbors list begin\n"); 
     312    InitTriInfo(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); 
     313    //printf("gen neighbors list end\n"); 
     314 
     315     
     316    // based on the 4 rules, identify groups based on connectivity 
     317    iNrMaxGroups = iNrTrianglesIn*3; 
     318    pGroups = (SGroup *) malloc(sizeof(SGroup)*iNrMaxGroups); 
     319    piGroupTrianglesBuffer = (int *) malloc(sizeof(int)*iNrTrianglesIn*3); 
     320    if (pGroups==NULL || piGroupTrianglesBuffer==NULL) 
     321    { 
     322        if (pGroups!=NULL) free(pGroups); 
     323        if (piGroupTrianglesBuffer!=NULL) free(piGroupTrianglesBuffer); 
     324        free(piTriListIn); 
     325        free(pTriInfos); 
     326        return TFALSE; 
     327    } 
     328    //printf("gen 4rule groups begin\n"); 
     329    iNrActiveGroups = 
     330        Build4RuleGroups(pTriInfos, pGroups, piGroupTrianglesBuffer, piTriListIn, iNrTrianglesIn); 
     331    //printf("gen 4rule groups end\n"); 
     332 
     333    // 
     334 
     335    psTspace = (STSpace *) malloc(sizeof(STSpace)*iNrTSPaces); 
     336    if (psTspace==NULL) 
     337    { 
     338        free(piTriListIn); 
     339        free(pTriInfos); 
     340        free(pGroups); 
     341        free(piGroupTrianglesBuffer); 
     342        return TFALSE; 
     343    } 
     344    memset(psTspace, 0, sizeof(STSpace)*iNrTSPaces); 
     345    for (t=0; t<iNrTSPaces; t++) 
     346    { 
     347        psTspace[t].vOs.x=1.0f; psTspace[t].vOs.y=0.0f; psTspace[t].vOs.z=0.0f; psTspace[t].fMagS = 1.0f; 
     348        psTspace[t].vOt.x=0.0f; psTspace[t].vOt.y=1.0f; psTspace[t].vOt.z=0.0f; psTspace[t].fMagT = 1.0f; 
     349    } 
     350 
     351    // make tspaces, each group is split up into subgroups if necessary 
     352    // based on fAngularThreshold. Finally a tangent space is made for 
     353    // every resulting subgroup 
     354    //printf("gen tspaces begin\n"); 
     355    bRes = GenerateTSpaces(psTspace, pTriInfos, pGroups, iNrActiveGroups, piTriListIn, fThresCos, pContext); 
     356    //printf("gen tspaces end\n"); 
     357     
     358    // clean up 
     359    free(pGroups); 
     360    free(piGroupTrianglesBuffer); 
     361 
     362    if (!bRes)  // if an allocation in GenerateTSpaces() failed 
     363    { 
     364        // clean up and return false 
     365        free(pTriInfos); free(piTriListIn); free(psTspace); 
     366        return TFALSE; 
     367    } 
     368 
     369 
     370    // degenerate quads with one good triangle will be fixed by copying a space from 
     371    // the good triangle to the coinciding vertex. 
     372    // all other degenerate triangles will just copy a space from any good triangle 
     373    // with the same welded index in piTriListIn[]. 
     374    DegenEpilogue(psTspace, pTriInfos, piTriListIn, pContext, iNrTrianglesIn, iTotTris); 
     375 
     376    free(pTriInfos); free(piTriListIn); 
     377 
     378    index = 0; 
     379    for (f=0; f<iNrFaces; f++) 
     380    { 
     381        const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); 
     382        if (verts!=3 && verts!=4) continue; 
     383         
     384 
     385        // I've decided to let degenerate triangles and group-with-anythings 
     386        // vary between left/right hand coordinate systems at the vertices. 
     387        // All healthy triangles on the other hand are built to always be either or. 
     388 
     389        /*// force the coordinate system orientation to be uniform for every face. 
     390        // (this is already the case for good triangles but not for 
     391        // degenerate ones and those with bGroupWithAnything==true) 
     392        bool bOrient = psTspace[index].bOrient; 
     393        if (psTspace[index].iCounter == 0)  // tspace was not derived from a group 
     394        { 
     395            // look for a space created in GenerateTSpaces() by iCounter>0 
     396            bool bNotFound = true; 
     397            int i=1; 
     398            while (i<verts && bNotFound) 
     399            { 
     400                if (psTspace[index+i].iCounter > 0) bNotFound=false; 
     401                else ++i; 
     402            } 
     403            if (!bNotFound) bOrient = psTspace[index+i].bOrient; 
     404        }*/ 
     405 
     406        // set data 
     407        for (i=0; i<verts; i++) 
     408        { 
     409            const STSpace * pTSpace = &psTspace[index]; 
     410            float tang[] = {pTSpace->vOs.x, pTSpace->vOs.y, pTSpace->vOs.z}; 
     411            float bitang[] = {pTSpace->vOt.x, pTSpace->vOt.y, pTSpace->vOt.z}; 
     412            if (pContext->m_pInterface->m_setTSpace!=NULL) 
     413                pContext->m_pInterface->m_setTSpace(pContext, tang, bitang, pTSpace->fMagS, pTSpace->fMagT, pTSpace->bOrient, f, i); 
     414            if (pContext->m_pInterface->m_setTSpaceBasic!=NULL) 
     415                pContext->m_pInterface->m_setTSpaceBasic(pContext, tang, pTSpace->bOrient==TTRUE ? 1.0f : (-1.0f), f, i); 
     416 
     417            ++index; 
     418        } 
     419    } 
     420 
     421    free(psTspace); 
     422 
     423     
     424    return TTRUE; 
     425} 
     426 
     427/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
     428 
     429typedef struct 
     430{ 
     431    float vert[3]; 
     432    int index; 
     433} STmpVert; 
     434 
     435const int g_iCells = 2048; 
     436 
     437#ifdef _MSC_VER 
     438    #define NOINLINE __declspec(noinline) 
     439#else 
     440    #define NOINLINE __attribute__ ((noinline)) 
     441#endif 
     442 
     443// it is IMPORTANT that this function is called to evaluate the hash since 
     444// inlining could potentially reorder instructions and generate different 
     445// results for the same effective input value fVal. 
     446NOINLINE int FindGridCell(const float fMin, const float fMax, const float fVal) 
     447{ 
     448    const float fIndex = g_iCells * ((fVal-fMin)/(fMax-fMin)); 
     449    const int iIndex = fIndex<0?0:((int)fIndex); 
     450    return iIndex<g_iCells?iIndex:(g_iCells-1); 
     451} 
     452 
     453static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in); 
     454static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries); 
     455static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); 
     456 
     457static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) 
     458{ 
     459 
     460    // Generate bounding box 
     461    int * piHashTable=NULL, * piHashCount=NULL, * piHashOffsets=NULL, * piHashCount2=NULL; 
     462    STmpVert * pTmpVert = NULL; 
     463    int i=0, iChannel=0, k=0, e=0; 
     464    int iMaxCount=0; 
     465    SVec3 vMin = GetPosition(pContext, 0), vMax = vMin, vDim; 
     466    float fMin, fMax; 
     467    for (i=1; i<(iNrTrianglesIn*3); i++) 
     468    { 
     469        const int index = piTriList_in_and_out[i]; 
     470 
     471        const SVec3 vP = GetPosition(pContext, index); 
     472        if (vMin.x > vP.x) vMin.x = vP.x; 
     473        else if(vMax.x < vP.x) vMax.x = vP.x; 
     474        if (vMin.y > vP.y) vMin.y = vP.y; 
     475        else if(vMax.y < vP.y) vMax.y = vP.y; 
     476        if (vMin.z > vP.z) vMin.z = vP.z; 
     477        else if(vMax.z < vP.z) vMax.z = vP.z; 
     478    } 
     479 
     480    vDim = vsub(vMax,vMin); 
     481    iChannel = 0; 
     482    fMin = vMin.x; fMax=vMax.x; 
     483    if (vDim.y>vDim.x && vDim.y>vDim.z) 
     484    { 
     485        iChannel=1; 
     486        fMin = vMin.y, fMax=vMax.y; 
     487    } 
     488    else if(vDim.z>vDim.x) 
     489    { 
     490        iChannel=2; 
     491        fMin = vMin.z, fMax=vMax.z; 
     492    } 
     493 
     494    // make allocations 
     495    piHashTable = (int *) malloc(sizeof(int)*iNrTrianglesIn*3); 
     496    piHashCount = (int *) malloc(sizeof(int)*g_iCells); 
     497    piHashOffsets = (int *) malloc(sizeof(int)*g_iCells); 
     498    piHashCount2 = (int *) malloc(sizeof(int)*g_iCells); 
     499 
     500    if (piHashTable==NULL || piHashCount==NULL || piHashOffsets==NULL || piHashCount2==NULL) 
     501    { 
     502        if (piHashTable!=NULL) free(piHashTable); 
     503        if (piHashCount!=NULL) free(piHashCount); 
     504        if (piHashOffsets!=NULL) free(piHashOffsets); 
     505        if (piHashCount2!=NULL) free(piHashCount2); 
     506        GenerateSharedVerticesIndexListSlow(piTriList_in_and_out, pContext, iNrTrianglesIn); 
     507        return; 
     508    } 
     509    memset(piHashCount, 0, sizeof(int)*g_iCells); 
     510    memset(piHashCount2, 0, sizeof(int)*g_iCells); 
     511 
     512    // count amount of elements in each cell unit 
     513    for (i=0; i<(iNrTrianglesIn*3); i++) 
     514    { 
     515        const int index = piTriList_in_and_out[i]; 
     516        const SVec3 vP = GetPosition(pContext, index); 
     517        const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z); 
     518        const int iCell = FindGridCell(fMin, fMax, fVal); 
     519        ++piHashCount[iCell]; 
     520    } 
     521 
     522    // evaluate start index of each cell. 
     523    piHashOffsets[0]=0; 
     524    for (k=1; k<g_iCells; k++) 
     525        piHashOffsets[k]=piHashOffsets[k-1]+piHashCount[k-1]; 
     526 
     527    // insert vertices 
     528    for (i=0; i<(iNrTrianglesIn*3); i++) 
     529    { 
     530        const int index = piTriList_in_and_out[i]; 
     531        const SVec3 vP = GetPosition(pContext, index); 
     532        const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z); 
     533        const int iCell = FindGridCell(fMin, fMax, fVal); 
     534        int * pTable = NULL; 
     535 
     536        assert(piHashCount2[iCell]<piHashCount[iCell]); 
     537        pTable = &piHashTable[piHashOffsets[iCell]]; 
     538        pTable[piHashCount2[iCell]] = i;    // vertex i has been inserted. 
     539        ++piHashCount2[iCell]; 
     540    } 
     541    for (k=0; k<g_iCells; k++) 
     542        assert(piHashCount2[k] == piHashCount[k]);  // verify the count 
     543    free(piHashCount2); 
     544 
     545    // find maximum amount of entries in any hash entry 
     546    iMaxCount = piHashCount[0]; 
     547    for (k=1; k<g_iCells; k++) 
     548        if (iMaxCount<piHashCount[k]) 
     549            iMaxCount=piHashCount[k]; 
     550    pTmpVert = (STmpVert *) malloc(sizeof(STmpVert)*iMaxCount); 
     551     
     552 
     553    // complete the merge 
     554    for (k=0; k<g_iCells; k++) 
     555    { 
     556        // extract table of cell k and amount of entries in it 
     557        int * pTable = &piHashTable[piHashOffsets[k]]; 
     558        const int iEntries = piHashCount[k]; 
     559        if (iEntries < 2) continue; 
     560 
     561        if (pTmpVert!=NULL) 
     562        { 
     563            for (e=0; e<iEntries; e++) 
     564            { 
     565                int i = pTable[e]; 
     566                const SVec3 vP = GetPosition(pContext, piTriList_in_and_out[i]); 
     567                pTmpVert[e].vert[0] = vP.x; pTmpVert[e].vert[1] = vP.y; 
     568                pTmpVert[e].vert[2] = vP.z; pTmpVert[e].index = i; 
     569            } 
     570            MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, 0, iEntries-1); 
     571        } 
     572        else 
     573            MergeVertsSlow(piTriList_in_and_out, pContext, pTable, iEntries); 
     574    } 
     575 
     576    if (pTmpVert!=NULL) { free(pTmpVert); } 
     577    free(piHashTable); 
     578    free(piHashCount); 
     579    free(piHashOffsets); 
     580} 
     581 
     582static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in) 
     583{ 
     584    // make bbox 
     585    int c=0, l=0, channel=0; 
     586    float fvMin[3], fvMax[3]; 
     587    float dx=0, dy=0, dz=0, fSep=0; 
     588    for (c=0; c<3; c++) 
     589    {   fvMin[c]=pTmpVert[iL_in].vert[c]; fvMax[c]=fvMin[c];    } 
     590    for (l=(iL_in+1); l<=iR_in; l++) 
     591        for (c=0; c<3; c++) 
     592            if (fvMin[c]>pTmpVert[l].vert[c]) fvMin[c]=pTmpVert[l].vert[c]; 
     593            else if(fvMax[c]<pTmpVert[l].vert[c]) fvMax[c]=pTmpVert[l].vert[c]; 
     594 
     595    dx = fvMax[0]-fvMin[0]; 
     596    dy = fvMax[1]-fvMin[1]; 
     597    dz = fvMax[2]-fvMin[2]; 
     598 
     599    channel = 0; 
     600    if (dy>dx && dy>dz) channel=1; 
     601    else if(dz>dx) channel=2; 
     602 
     603    fSep = 0.5f*(fvMax[channel]+fvMin[channel]); 
     604 
     605    // terminate recursion when the separation/average value 
     606    // is no longer strictly between fMin and fMax values. 
     607    if (fSep>=fvMax[channel] || fSep<=fvMin[channel]) 
     608    { 
     609        // complete the weld 
     610        for (l=iL_in; l<=iR_in; l++) 
     611        { 
     612            int i = pTmpVert[l].index; 
     613            const int index = piTriList_in_and_out[i]; 
     614            const SVec3 vP = GetPosition(pContext, index); 
     615            const SVec3 vN = GetNormal(pContext, index); 
     616            const SVec3 vT = GetTexCoord(pContext, index); 
     617 
     618            tbool bNotFound = TTRUE; 
     619            int l2=iL_in, i2rec=-1; 
     620            while (l2<l && bNotFound) 
     621            { 
     622                const int i2 = pTmpVert[l2].index; 
     623                const int index2 = piTriList_in_and_out[i2]; 
     624                const SVec3 vP2 = GetPosition(pContext, index2); 
     625                const SVec3 vN2 = GetNormal(pContext, index2); 
     626                const SVec3 vT2 = GetTexCoord(pContext, index2); 
     627                i2rec=i2; 
     628 
     629                //if(vP==vP2 && vN==vN2 && vT==vT2) 
     630                if (vP.x==vP2.x && vP.y==vP2.y && vP.z==vP2.z && 
     631                    vN.x==vN2.x && vN.y==vN2.y && vN.z==vN2.z && 
     632                    vT.x==vT2.x && vT.y==vT2.y && vT.z==vT2.z) 
     633                    bNotFound = TFALSE; 
     634                else 
     635                    ++l2; 
     636            } 
     637             
     638            // merge if previously found 
     639            if (!bNotFound) 
     640                piTriList_in_and_out[i] = piTriList_in_and_out[i2rec]; 
     641        } 
     642    } 
     643    else 
     644    { 
     645        int iL=iL_in, iR=iR_in; 
     646        assert((iR_in-iL_in)>0);    // at least 2 entries 
     647 
     648        // separate (by fSep) all points between iL_in and iR_in in pTmpVert[] 
     649        while (iL < iR) 
     650        { 
     651            tbool bReadyLeftSwap = TFALSE, bReadyRightSwap = TFALSE; 
     652            while ((!bReadyLeftSwap) && iL<iR) 
     653            { 
     654                assert(iL>=iL_in && iL<=iR_in); 
     655                bReadyLeftSwap = !(pTmpVert[iL].vert[channel]<fSep); 
     656                if (!bReadyLeftSwap) ++iL; 
     657            } 
     658            while ((!bReadyRightSwap) && iL<iR) 
     659            { 
     660                assert(iR>=iL_in && iR<=iR_in); 
     661                bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep; 
     662                if (!bReadyRightSwap) --iR; 
     663            } 
     664            assert( (iL<iR) || !(bReadyLeftSwap && bReadyRightSwap) ); 
     665 
     666            if (bReadyLeftSwap && bReadyRightSwap) 
     667            { 
     668                const STmpVert sTmp = pTmpVert[iL]; 
     669                assert(iL<iR); 
     670                pTmpVert[iL] = pTmpVert[iR]; 
     671                pTmpVert[iR] = sTmp; 
     672                ++iL; --iR; 
     673            } 
     674        } 
     675 
     676        assert(iL==(iR+1) || (iL==iR)); 
     677        if (iL==iR) 
     678        { 
     679            const tbool bReadyRightSwap = pTmpVert[iR].vert[channel]<fSep; 
     680            if (bReadyRightSwap) ++iL; 
     681            else --iR; 
     682        } 
     683 
     684        // only need to weld when there is more than 1 instance of the (x,y,z) 
     685        if (iL_in < iR) 
     686            MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL_in, iR);    // weld all left of fSep 
     687        if (iL < iR_in) 
     688            MergeVertsFast(piTriList_in_and_out, pTmpVert, pContext, iL, iR_in);    // weld all right of (or equal to) fSep 
     689    } 
     690} 
     691 
     692static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries) 
     693{ 
     694    // this can be optimized further using a tree structure or more hashing. 
     695    int e=0; 
     696    for (e=0; e<iEntries; e++) 
     697    { 
     698        int i = pTable[e]; 
     699        const int index = piTriList_in_and_out[i]; 
     700        const SVec3 vP = GetPosition(pContext, index); 
     701        const SVec3 vN = GetNormal(pContext, index); 
     702        const SVec3 vT = GetTexCoord(pContext, index); 
     703 
     704        tbool bNotFound = TTRUE; 
     705        int e2=0, i2rec=-1; 
     706        while (e2<e && bNotFound) 
     707        { 
     708            const int i2 = pTable[e2]; 
     709            const int index2 = piTriList_in_and_out[i2]; 
     710            const SVec3 vP2 = GetPosition(pContext, index2); 
     711            const SVec3 vN2 = GetNormal(pContext, index2); 
     712            const SVec3 vT2 = GetTexCoord(pContext, index2); 
     713            i2rec = i2; 
     714 
     715            if (veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2)) 
     716                bNotFound = TFALSE; 
     717            else 
     718                ++e2; 
     719        } 
     720         
     721        // merge if previously found 
     722        if (!bNotFound) 
     723            piTriList_in_and_out[i] = piTriList_in_and_out[i2rec]; 
     724    } 
     725} 
     726 
     727static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) 
     728{ 
     729    int iNumUniqueVerts = 0, t=0, i=0; 
     730    for (t=0; t<iNrTrianglesIn; t++) 
     731    { 
     732        for (i=0; i<3; i++) 
     733        { 
     734            const int offs = t*3 + i; 
     735            const int index = piTriList_in_and_out[offs]; 
     736 
     737            const SVec3 vP = GetPosition(pContext, index); 
     738            const SVec3 vN = GetNormal(pContext, index); 
     739            const SVec3 vT = GetTexCoord(pContext, index); 
     740 
     741            tbool bFound = TFALSE; 
     742            int t2=0, index2rec=-1; 
     743            while (!bFound && t2<=t) 
     744            { 
     745                int j=0; 
     746                while (!bFound && j<3) 
     747                { 
     748                    const int index2 = piTriList_in_and_out[t2*3 + j]; 
     749                    const SVec3 vP2 = GetPosition(pContext, index2); 
     750                    const SVec3 vN2 = GetNormal(pContext, index2); 
     751                    const SVec3 vT2 = GetTexCoord(pContext, index2); 
     752                     
     753                    if (veq(vP,vP2) && veq(vN,vN2) && veq(vT,vT2)) 
     754                        bFound = TTRUE; 
     755                    else 
     756                        ++j; 
     757                } 
     758                if (!bFound) ++t2; 
     759            } 
     760 
     761            assert(bFound); 
     762            // if we found our own 
     763            if (index2rec == index) { ++iNumUniqueVerts; } 
     764 
     765            piTriList_in_and_out[offs] = index2rec; 
     766        } 
     767    } 
     768} 
     769 
     770static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) 
     771{ 
     772    int iTSpacesOffs = 0, f=0, t=0; 
     773    int iDstTriIndex = 0; 
     774    for (f=0; f<pContext->m_pInterface->m_getNumFaces(pContext); f++) 
     775    { 
     776        const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); 
     777        if (verts!=3 && verts!=4) continue; 
     778 
     779        pTriInfos[iDstTriIndex].iOrgFaceNumber = f; 
     780        pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs; 
     781 
     782        if (verts==3) 
     783        { 
     784            unsigned char * pVerts = pTriInfos[iDstTriIndex].vert_num; 
     785            pVerts[0]=0; pVerts[1]=1; pVerts[2]=2; 
     786            piTriList_out[iDstTriIndex*3+0] = MakeIndex(f, 0); 
     787            piTriList_out[iDstTriIndex*3+1] = MakeIndex(f, 1); 
     788            piTriList_out[iDstTriIndex*3+2] = MakeIndex(f, 2); 
     789            ++iDstTriIndex; // next 
     790        } 
     791        else 
     792        { 
     793            { 
     794                pTriInfos[iDstTriIndex+1].iOrgFaceNumber = f; 
     795                pTriInfos[iDstTriIndex+1].iTSpacesOffs = iTSpacesOffs; 
     796            } 
     797 
     798            { 
     799                // need an order independent way to evaluate 
     800                // tspace on quads. This is done by splitting 
     801                // along the shortest diagonal. 
     802                const int i0 = MakeIndex(f, 0); 
     803                const int i1 = MakeIndex(f, 1); 
     804                const int i2 = MakeIndex(f, 2); 
     805                const int i3 = MakeIndex(f, 3); 
     806                const SVec3 T0 = GetTexCoord(pContext, i0); 
     807                const SVec3 T1 = GetTexCoord(pContext, i1); 
     808                const SVec3 T2 = GetTexCoord(pContext, i2); 
     809                const SVec3 T3 = GetTexCoord(pContext, i3); 
     810                const float distSQ_02 = LengthSquared(vsub(T2,T0)); 
     811                const float distSQ_13 = LengthSquared(vsub(T3,T1)); 
     812                tbool bQuadDiagIs_02; 
     813                if (distSQ_02<distSQ_13) 
     814                    bQuadDiagIs_02 = TTRUE; 
     815                else if(distSQ_13<distSQ_02) 
     816                    bQuadDiagIs_02 = TFALSE; 
     817                else 
     818                { 
     819                    const SVec3 P0 = GetPosition(pContext, i0); 
     820                    const SVec3 P1 = GetPosition(pContext, i1); 
     821                    const SVec3 P2 = GetPosition(pContext, i2); 
     822                    const SVec3 P3 = GetPosition(pContext, i3); 
     823                    const float distSQ_02 = LengthSquared(vsub(P2,P0)); 
     824                    const float distSQ_13 = LengthSquared(vsub(P3,P1)); 
     825 
     826                    bQuadDiagIs_02 = distSQ_13<distSQ_02 ? TFALSE : TTRUE; 
     827                } 
     828 
     829                if (bQuadDiagIs_02) 
     830                { 
     831                    { 
     832                        unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num; 
     833                        pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=2; 
     834                    } 
     835                    piTriList_out[iDstTriIndex*3+0] = i0; 
     836                    piTriList_out[iDstTriIndex*3+1] = i1; 
     837                    piTriList_out[iDstTriIndex*3+2] = i2; 
     838                    ++iDstTriIndex; // next 
     839                    { 
     840                        unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num; 
     841                        pVerts_B[0]=0; pVerts_B[1]=2; pVerts_B[2]=3; 
     842                    } 
     843                    piTriList_out[iDstTriIndex*3+0] = i0; 
     844                    piTriList_out[iDstTriIndex*3+1] = i2; 
     845                    piTriList_out[iDstTriIndex*3+2] = i3; 
     846                    ++iDstTriIndex; // next 
     847                } 
     848                else 
     849                { 
     850                    { 
     851                        unsigned char * pVerts_A = pTriInfos[iDstTriIndex].vert_num; 
     852                        pVerts_A[0]=0; pVerts_A[1]=1; pVerts_A[2]=3; 
     853                    } 
     854                    piTriList_out[iDstTriIndex*3+0] = i0; 
     855                    piTriList_out[iDstTriIndex*3+1] = i1; 
     856                    piTriList_out[iDstTriIndex*3+2] = i3; 
     857                    ++iDstTriIndex; // next 
     858                    { 
     859                        unsigned char * pVerts_B = pTriInfos[iDstTriIndex].vert_num; 
     860                        pVerts_B[0]=1; pVerts_B[1]=2; pVerts_B[2]=3; 
     861                    } 
     862                    piTriList_out[iDstTriIndex*3+0] = i1; 
     863                    piTriList_out[iDstTriIndex*3+1] = i2; 
     864                    piTriList_out[iDstTriIndex*3+2] = i3; 
     865                    ++iDstTriIndex; // next 
     866                } 
     867            } 
     868        } 
     869 
     870        iTSpacesOffs += verts; 
     871        assert(iDstTriIndex<=iNrTrianglesIn); 
     872    } 
     873 
     874    for (t=0; t<iNrTrianglesIn; t++) 
     875        pTriInfos[t].iFlag = 0; 
     876 
     877    // return total amount of tspaces 
     878    return iTSpacesOffs; 
     879} 
     880 
     881static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index) 
     882{ 
     883    int iF, iI; 
     884    SVec3 res; float pos[3]; 
     885    IndexToData(&iF, &iI, index); 
     886    pContext->m_pInterface->m_getPosition(pContext, pos, iF, iI); 
     887    res.x=pos[0]; res.y=pos[1]; res.z=pos[2]; 
     888    return res; 
     889} 
     890 
     891static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index) 
     892{ 
     893    int iF, iI; 
     894    SVec3 res; float norm[3]; 
     895    IndexToData(&iF, &iI, index); 
     896    pContext->m_pInterface->m_getNormal(pContext, norm, iF, iI); 
     897    res.x=norm[0]; res.y=norm[1]; res.z=norm[2]; 
     898    return res; 
     899} 
     900 
     901static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index) 
     902{ 
     903    int iF, iI; 
     904    SVec3 res; float texc[2]; 
     905    IndexToData(&iF, &iI, index); 
     906    pContext->m_pInterface->m_getTexCoord(pContext, texc, iF, iI); 
     907    res.x=texc[0]; res.y=texc[1]; res.z=1.0f; 
     908    return res; 
     909} 
     910 
     911///////////////////////////////////////////////////////////////////////////////////////////////////// 
     912///////////////////////////////////////////////////////////////////////////////////////////////////// 
     913 
     914typedef union 
     915{ 
     916    struct 
     917    { 
     918        int i0, i1, f; 
     919    }; 
     920    int array[3]; 
     921} SEdge; 
     922 
     923static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn); 
     924static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn); 
     925 
     926// returns the texture area times 2 
     927static float CalcTexArea(const SMikkTSpaceContext * pContext, const int indices[]) 
     928{ 
     929    const SVec3 t1 = GetTexCoord(pContext, indices[0]); 
     930    const SVec3 t2 = GetTexCoord(pContext, indices[1]); 
     931    const SVec3 t3 = GetTexCoord(pContext, indices[2]); 
     932 
     933    const float t21x = t2.x-t1.x; 
     934    const float t21y = t2.y-t1.y; 
     935    const float t31x = t3.x-t1.x; 
     936    const float t31y = t3.y-t1.y; 
     937 
     938    const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x; 
     939 
     940    return fSignedAreaSTx2<0 ? (-fSignedAreaSTx2) : fSignedAreaSTx2; 
     941} 
     942 
     943static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) 
     944{ 
     945    int f=0, i=0, t=0; 
     946    // pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() which is called before this function. 
     947 
     948    // generate neighbor info list 
     949    for (f=0; f<iNrTrianglesIn; f++) 
     950        for (i=0; i<3; i++) 
     951        { 
     952            pTriInfos[f].FaceNeighbors[i] = -1; 
     953            pTriInfos[f].AssignedGroup[i] = NULL; 
     954 
     955            pTriInfos[f].vOs.x=0.0f; pTriInfos[f].vOs.y=0.0f; pTriInfos[f].vOs.z=0.0f; 
     956            pTriInfos[f].vOt.x=0.0f; pTriInfos[f].vOt.y=0.0f; pTriInfos[f].vOt.z=0.0f; 
     957            pTriInfos[f].fMagS = 0; 
     958            pTriInfos[f].fMagT = 0; 
     959 
     960            // assumed bad 
     961            pTriInfos[f].iFlag |= GROUP_WITH_ANY; 
     962        } 
     963 
     964    // evaluate first order derivatives 
     965    for (f=0; f<iNrTrianglesIn; f++) 
     966    { 
     967        // initial values 
     968        const SVec3 v1 = GetPosition(pContext, piTriListIn[f*3+0]); 
     969        const SVec3 v2 = GetPosition(pContext, piTriListIn[f*3+1]); 
     970        const SVec3 v3 = GetPosition(pContext, piTriListIn[f*3+2]); 
     971        const SVec3 t1 = GetTexCoord(pContext, piTriListIn[f*3+0]); 
     972        const SVec3 t2 = GetTexCoord(pContext, piTriListIn[f*3+1]); 
     973        const SVec3 t3 = GetTexCoord(pContext, piTriListIn[f*3+2]); 
     974 
     975        const float t21x = t2.x-t1.x; 
     976        const float t21y = t2.y-t1.y; 
     977        const float t31x = t3.x-t1.x; 
     978        const float t31y = t3.y-t1.y; 
     979        const SVec3 d1 = vsub(v2,v1); 
     980        const SVec3 d2 = vsub(v3,v1); 
     981 
     982        const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x; 
     983        //assert(fSignedAreaSTx2!=0); 
     984        SVec3 vOs = vsub(vscale(t31y,d1), vscale(t21y,d2)); // eq 18 
     985        SVec3 vOt = vadd(vscale(-t31x,d1), vscale(t21x,d2)); // eq 19 
     986 
     987        pTriInfos[f].iFlag |= (fSignedAreaSTx2>0 ? ORIENT_PRESERVING : 0); 
     988 
     989        if ( NotZero(fSignedAreaSTx2) ) 
     990        { 
     991            const float fAbsArea = fabsf(fSignedAreaSTx2); 
     992            const float fLenOs = Length(vOs); 
     993            const float fLenOt = Length(vOt); 
     994            const float fS = (pTriInfos[f].iFlag&ORIENT_PRESERVING)==0 ? (-1.0f) : 1.0f; 
     995            if ( NotZero(fLenOs) ) pTriInfos[f].vOs = vscale(fS/fLenOs, vOs); 
     996            if ( NotZero(fLenOt) ) pTriInfos[f].vOt = vscale(fS/fLenOt, vOt); 
     997 
     998            // evaluate magnitudes prior to normalization of vOs and vOt 
     999            pTriInfos[f].fMagS = fLenOs / fAbsArea; 
     1000            pTriInfos[f].fMagT = fLenOt / fAbsArea; 
     1001 
     1002            // if this is a good triangle 
     1003            if ( NotZero(pTriInfos[f].fMagS) && NotZero(pTriInfos[f].fMagT)) 
     1004                pTriInfos[f].iFlag &= (~GROUP_WITH_ANY); 
     1005        } 
     1006    } 
     1007 
     1008    // force otherwise healthy quads to a fixed orientation 
     1009    while (t<(iNrTrianglesIn-1)) 
     1010    { 
     1011        const int iFO_a = pTriInfos[t].iOrgFaceNumber; 
     1012        const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; 
     1013        if (iFO_a==iFO_b)   // this is a quad 
     1014        { 
     1015            const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; 
     1016            const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; 
     1017             
     1018            // bad triangles should already have been removed by 
     1019            // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false 
     1020            if ((bIsDeg_a||bIsDeg_b)==TFALSE) 
     1021            { 
     1022                const tbool bOrientA = (pTriInfos[t].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 
     1023                const tbool bOrientB = (pTriInfos[t+1].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 
     1024                // if this happens the quad has extremely bad mapping!! 
     1025                if (bOrientA!=bOrientB) 
     1026                { 
     1027                    //printf("found quad with bad mapping\n"); 
     1028                    tbool bChooseOrientFirstTri = TFALSE; 
     1029                    if ((pTriInfos[t+1].iFlag&GROUP_WITH_ANY)!=0) bChooseOrientFirstTri = TTRUE; 
     1030                    else if( CalcTexArea(pContext, &piTriListIn[t*3+0]) >= CalcTexArea(pContext, &piTriListIn[(t+1)*3+0]) ) 
     1031                        bChooseOrientFirstTri = TTRUE; 
     1032 
     1033                    // force match 
     1034                    { 
     1035                        const int t0 = bChooseOrientFirstTri ? t : (t+1); 
     1036                        const int t1 = bChooseOrientFirstTri ? (t+1) : t; 
     1037                        pTriInfos[t1].iFlag &= (~ORIENT_PRESERVING);    // clear first 
     1038                        pTriInfos[t1].iFlag |= (pTriInfos[t0].iFlag&ORIENT_PRESERVING); // copy bit 
     1039                    } 
     1040                } 
     1041            } 
     1042            t += 2; 
     1043        } 
     1044        else 
     1045            ++t; 
     1046    } 
     1047     
     1048    // match up edge pairs 
     1049    { 
     1050        SEdge * pEdges = (SEdge *) malloc(sizeof(SEdge)*iNrTrianglesIn*3); 
     1051        if (pEdges==NULL) 
     1052            BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn); 
     1053        else 
     1054        { 
     1055            BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn); 
     1056     
     1057            free(pEdges); 
     1058        } 
     1059    } 
     1060} 
     1061 
     1062///////////////////////////////////////////////////////////////////////////////////////////////////// 
     1063///////////////////////////////////////////////////////////////////////////////////////////////////// 
     1064 
     1065static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], const int iMyTriIndex, SGroup * pGroup); 
     1066static void AddTriToGroup(SGroup * pGroup, const int iTriIndex); 
     1067 
     1068static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn) 
     1069{ 
     1070    const int iNrMaxGroups = iNrTrianglesIn*3; 
     1071    int iNrActiveGroups = 0; 
     1072    int iOffset = 0, f=0, i=0; 
     1073    for (f=0; f<iNrTrianglesIn; f++) 
     1074    { 
     1075        for (i=0; i<3; i++) 
     1076        { 
     1077            // if not assigned to a group 
     1078            if ((pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 && pTriInfos[f].AssignedGroup[i]==NULL) 
     1079            { 
     1080                tbool bOrPre; 
     1081                int neigh_indexL, neigh_indexR; 
     1082                const int vert_index = piTriListIn[f*3+i]; 
     1083                assert(iNrActiveGroups<iNrMaxGroups); 
     1084                pTriInfos[f].AssignedGroup[i] = &pGroups[iNrActiveGroups]; 
     1085                pTriInfos[f].AssignedGroup[i]->iVertexRepresentitive = vert_index; 
     1086                pTriInfos[f].AssignedGroup[i]->bOrientPreservering = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0; 
     1087                pTriInfos[f].AssignedGroup[i]->iNrFaces = 0; 
     1088                pTriInfos[f].AssignedGroup[i]->pFaceIndices = &piGroupTrianglesBuffer[iOffset]; 
     1089                ++iNrActiveGroups; 
     1090 
     1091                AddTriToGroup(pTriInfos[f].AssignedGroup[i], f); 
     1092                bOrPre = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 
     1093                neigh_indexL = pTriInfos[f].FaceNeighbors[i]; 
     1094                neigh_indexR = pTriInfos[f].FaceNeighbors[i>0?(i-1):2]; 
     1095                if (neigh_indexL>=0) // neighbor 
     1096                { 
     1097                    const tbool bAnswer = 
     1098                        AssignRecur(piTriListIn, pTriInfos, neigh_indexL, 
     1099                                    pTriInfos[f].AssignedGroup[i] ); 
     1100                     
     1101                    const tbool bOrPre2 = (pTriInfos[neigh_indexL].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 
     1102                    const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; 
     1103                    assert(bAnswer || bDiff); 
     1104                } 
     1105                if (neigh_indexR>=0) // neighbor 
     1106                { 
     1107                    const tbool bAnswer = 
     1108                        AssignRecur(piTriListIn, pTriInfos, neigh_indexR, 
     1109                                    pTriInfos[f].AssignedGroup[i] ); 
     1110 
     1111                    const tbool bOrPre2 = (pTriInfos[neigh_indexR].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 
     1112                    const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; 
     1113                    assert(bAnswer || bDiff); 
     1114                } 
     1115 
     1116                // update offset 
     1117                iOffset += pTriInfos[f].AssignedGroup[i]->iNrFaces; 
     1118                // since the groups are disjoint a triangle can never 
     1119                // belong to more than 3 groups. Subsequently something 
     1120                // is completely screwed if this assertion ever hits. 
     1121                assert(iOffset <= iNrMaxGroups); 
     1122            } 
     1123        } 
     1124    } 
     1125 
     1126    return iNrActiveGroups; 
     1127} 
     1128 
     1129static void AddTriToGroup(SGroup * pGroup, const int iTriIndex) 
     1130{ 
     1131    pGroup->pFaceIndices[pGroup->iNrFaces] = iTriIndex; 
     1132    ++pGroup->iNrFaces; 
     1133} 
     1134 
     1135static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], 
     1136                 const int iMyTriIndex, SGroup * pGroup) 
     1137{ 
     1138    STriInfo * pMyTriInfo = &psTriInfos[iMyTriIndex]; 
     1139 
     1140    // track down vertex 
     1141    const int iVertRep = pGroup->iVertexRepresentitive; 
     1142    const int * pVerts = &piTriListIn[3*iMyTriIndex+0]; 
     1143    int i=-1; 
     1144    if (pVerts[0]==iVertRep) i=0; 
     1145    else if(pVerts[1]==iVertRep) i=1; 
     1146    else if(pVerts[2]==iVertRep) i=2; 
     1147    assert(i>=0 && i<3); 
     1148 
     1149    // early out 
     1150    if (pMyTriInfo->AssignedGroup[i] == pGroup) return TTRUE; 
     1151    else if(pMyTriInfo->AssignedGroup[i]!=NULL) return TFALSE; 
     1152    if ((pMyTriInfo->iFlag&GROUP_WITH_ANY)!=0) 
     1153    { 
     1154        // first to group with a group-with-anything triangle 
     1155        // determines it's orientation. 
     1156        // This is the only existing order dependency in the code!! 
     1157        if ( pMyTriInfo->AssignedGroup[0] == NULL && 
     1158            pMyTriInfo->AssignedGroup[1] == NULL && 
     1159            pMyTriInfo->AssignedGroup[2] == NULL ) 
     1160        { 
     1161            pMyTriInfo->iFlag &= (~ORIENT_PRESERVING); 
     1162            pMyTriInfo->iFlag |= (pGroup->bOrientPreservering ? ORIENT_PRESERVING : 0); 
     1163        } 
     1164    } 
     1165    { 
     1166        const tbool bOrient = (pMyTriInfo->iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; 
     1167        if (bOrient != pGroup->bOrientPreservering) return TFALSE; 
     1168    } 
     1169 
     1170    AddTriToGroup(pGroup, iMyTriIndex); 
     1171    pMyTriInfo->AssignedGroup[i] = pGroup; 
     1172 
     1173    { 
     1174        const int neigh_indexL = pMyTriInfo->FaceNeighbors[i]; 
     1175        const int neigh_indexR = pMyTriInfo->FaceNeighbors[i>0?(i-1):2]; 
     1176        if (neigh_indexL>=0) 
     1177            AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup); 
     1178        if (neigh_indexR>=0) 
     1179            AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup); 
     1180    } 
     1181 
     1182 
     1183 
     1184    return TTRUE; 
     1185} 
     1186 
     1187///////////////////////////////////////////////////////////////////////////////////////////////////// 
     1188///////////////////////////////////////////////////////////////////////////////////////////////////// 
     1189 
     1190static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2); 
     1191static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed); 
     1192static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], const SMikkTSpaceContext * pContext, const int iVertexRepresentitive); 
     1193 
     1194static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], 
     1195                             const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, 
     1196                             const SMikkTSpaceContext * pContext) 
     1197{ 
     1198    STSpace * pSubGroupTspace = NULL; 
     1199    SSubGroup * pUniSubGroups = NULL; 
     1200    int * pTmpMembers = NULL; 
     1201    int iMaxNrFaces=0, iUniqueTspaces=0, g=0, i=0; 
     1202    for (g=0; g<iNrActiveGroups; g++) 
     1203        if (iMaxNrFaces < pGroups[g].iNrFaces) 
     1204            iMaxNrFaces = pGroups[g].iNrFaces; 
     1205 
     1206    if (iMaxNrFaces == 0) return TTRUE; 
     1207 
     1208    // make initial allocations 
     1209    pSubGroupTspace = (STSpace *) malloc(sizeof(STSpace)*iMaxNrFaces); 
     1210    pUniSubGroups = (SSubGroup *) malloc(sizeof(SSubGroup)*iMaxNrFaces); 
     1211    pTmpMembers = (int *) malloc(sizeof(int)*iMaxNrFaces); 
     1212    if (pSubGroupTspace==NULL || pUniSubGroups==NULL || pTmpMembers==NULL) 
     1213    { 
     1214        if (pSubGroupTspace!=NULL) free(pSubGroupTspace); 
     1215        if (pUniSubGroups!=NULL) free(pUniSubGroups); 
     1216        if (pTmpMembers!=NULL) free(pTmpMembers); 
     1217        return TFALSE; 
     1218    } 
     1219 
     1220 
     1221    iUniqueTspaces = 0; 
     1222    for (g=0; g<iNrActiveGroups; g++) 
     1223    { 
     1224        const SGroup * pGroup = &pGroups[g]; 
     1225        int iUniqueSubGroups = 0, s=0; 
     1226 
     1227        for (i=0; i<pGroup->iNrFaces; i++)  // triangles 
     1228        { 
     1229            const int f = pGroup->pFaceIndices[i];  // triangle number 
     1230            int index=-1, iVertIndex=-1, iOF_1=-1, iMembers=0, j=0, l=0; 
     1231            SSubGroup tmp_group; 
     1232            tbool bFound; 
     1233            SVec3 n, vOs, vOt; 
     1234            if (pTriInfos[f].AssignedGroup[0]==pGroup) index=0; 
     1235            else if(pTriInfos[f].AssignedGroup[1]==pGroup) index=1; 
     1236            else if(pTriInfos[f].AssignedGroup[2]==pGroup) index=2; 
     1237            assert(index>=0 && index<3); 
     1238 
     1239            iVertIndex = piTriListIn[f*3+index]; 
     1240            assert(iVertIndex==pGroup->iVertexRepresentitive); 
     1241 
     1242            // is normalized already 
     1243            n = GetNormal(pContext, iVertIndex); 
     1244             
     1245            // project 
     1246            vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); 
     1247            vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); 
     1248            if ( VNotZero(vOs) ) vOs = Normalize(vOs); 
     1249            if ( VNotZero(vOt) ) vOt = Normalize(vOt); 
     1250 
     1251            // original face number 
     1252            iOF_1 = pTriInfos[f].iOrgFaceNumber; 
     1253             
     1254            iMembers = 0; 
     1255            for (j=0; j<pGroup->iNrFaces; j++) 
     1256            { 
     1257                const int t = pGroup->pFaceIndices[j];  // triangle number 
     1258                const int iOF_2 = pTriInfos[t].iOrgFaceNumber; 
     1259 
     1260                // project 
     1261                SVec3 vOs2 = vsub(pTriInfos[t].vOs, vscale(vdot(n,pTriInfos[t].vOs), n)); 
     1262                SVec3 vOt2 = vsub(pTriInfos[t].vOt, vscale(vdot(n,pTriInfos[t].vOt), n)); 
     1263                if ( VNotZero(vOs2) ) vOs2 = Normalize(vOs2); 
     1264                if ( VNotZero(vOt2) ) vOt2 = Normalize(vOt2); 
     1265 
     1266                { 
     1267                    const tbool bAny = ( (pTriInfos[f].iFlag | pTriInfos[t].iFlag) & GROUP_WITH_ANY )!=0 ? TTRUE : TFALSE; 
     1268                    // make sure triangles which belong to the same quad are joined. 
     1269                    const tbool bSameOrgFace = iOF_1==iOF_2 ? TTRUE : TFALSE; 
     1270 
     1271                    const float fCosS = vdot(vOs,vOs2); 
     1272                    const float fCosT = vdot(vOt,vOt2); 
     1273 
     1274                    assert(f!=t || bSameOrgFace);   // sanity check 
     1275                    if (bAny || bSameOrgFace || (fCosS>fThresCos && fCosT>fThresCos)) 
     1276                        pTmpMembers[iMembers++] = t; 
     1277                } 
     1278            } 
     1279 
     1280            // sort pTmpMembers 
     1281            tmp_group.iNrFaces = iMembers; 
     1282            tmp_group.pTriMembers = pTmpMembers; 
     1283            if (iMembers>1) 
     1284            { 
     1285                unsigned int uSeed = INTERNAL_RND_SORT_SEED;    // could replace with a random seed? 
     1286                QuickSort(pTmpMembers, 0, iMembers-1, uSeed); 
     1287            } 
     1288 
     1289            // look for an existing match 
     1290            bFound = TFALSE; 
     1291            l=0; 
     1292            while (l<iUniqueSubGroups && !bFound) 
     1293            { 
     1294                bFound = CompareSubGroups(&tmp_group, &pUniSubGroups[l]); 
     1295                if (!bFound) ++l; 
     1296            } 
     1297             
     1298            // assign tangent space index 
     1299            assert(bFound || l==iUniqueSubGroups); 
     1300            //piTempTangIndices[f*3+index] = iUniqueTspaces+l; 
     1301 
     1302            // if no match was found we allocate a new subgroup 
     1303            if (!bFound) 
     1304            { 
     1305                // insert new subgroup 
     1306                int * pIndices = (int *) malloc(sizeof(int)*iMembers); 
     1307                if (pIndices==NULL) 
     1308                { 
     1309                    // clean up and return false 
     1310                    int s=0; 
     1311                    for (s=0; s<iUniqueSubGroups; s++) 
     1312                        free(pUniSubGroups[s].pTriMembers); 
     1313                    free(pUniSubGroups); 
     1314                    free(pTmpMembers); 
     1315                    free(pSubGroupTspace); 
     1316                    return TFALSE; 
     1317                } 
     1318                pUniSubGroups[iUniqueSubGroups].iNrFaces = iMembers; 
     1319                pUniSubGroups[iUniqueSubGroups].pTriMembers = pIndices; 
     1320                memcpy(pIndices, tmp_group.pTriMembers, iMembers*sizeof(int)); 
     1321                pSubGroupTspace[iUniqueSubGroups] = 
     1322                    EvalTspace(tmp_group.pTriMembers, iMembers, piTriListIn, pTriInfos, pContext, pGroup->iVertexRepresentitive); 
     1323                ++iUniqueSubGroups; 
     1324            } 
     1325 
     1326            // output tspace 
     1327            { 
     1328                const int iOffs = pTriInfos[f].iTSpacesOffs; 
     1329                const int iVert = pTriInfos[f].vert_num[index]; 
     1330                STSpace * pTS_out = &psTspace[iOffs+iVert]; 
     1331                assert(pTS_out->iCounter<2); 
     1332                assert(((pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0) == pGroup->bOrientPreservering); 
     1333                if (pTS_out->iCounter==1) 
     1334                { 
     1335                    *pTS_out = AvgTSpace(pTS_out, &pSubGroupTspace[l]); 
     1336                    pTS_out->iCounter = 2;  // update counter 
     1337                    pTS_out->bOrient = pGroup->bOrientPreservering; 
     1338                } 
     1339                else 
     1340                { 
     1341                    assert(pTS_out->iCounter==0); 
     1342                    *pTS_out = pSubGroupTspace[l]; 
     1343                    pTS_out->iCounter = 1;  // update counter 
     1344                    pTS_out->bOrient = pGroup->bOrientPreservering; 
     1345                } 
     1346            } 
     1347        } 
     1348 
     1349        // clean up and offset iUniqueTspaces 
     1350        for (s=0; s<iUniqueSubGroups; s++) 
     1351            free(pUniSubGroups[s].pTriMembers); 
     1352        iUniqueTspaces += iUniqueSubGroups; 
     1353    } 
     1354 
     1355    // clean up 
     1356    free(pUniSubGroups); 
     1357    free(pTmpMembers); 
     1358    free(pSubGroupTspace); 
     1359 
     1360    return TTRUE; 
     1361} 
     1362 
     1363static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], 
     1364                          const SMikkTSpaceContext * pContext, const int iVertexRepresentitive) 
     1365{ 
     1366    STSpace res; 
     1367    float fAngleSum = 0; 
     1368    int face=0; 
     1369    res.vOs.x=0.0f; res.vOs.y=0.0f; res.vOs.z=0.0f; 
     1370    res.vOt.x=0.0f; res.vOt.y=0.0f; res.vOt.z=0.0f; 
     1371    res.fMagS = 0; res.fMagT = 0; 
     1372 
     1373    for (face=0; face<iFaces; face++) 
     1374    { 
     1375        const int f = face_indices[face]; 
     1376 
     1377        // only valid triangles get to add their contribution 
     1378        if ( (pTriInfos[f].iFlag&GROUP_WITH_ANY)==0 ) 
     1379        { 
     1380            SVec3 n, vOs, vOt, p0, p1, p2, v1, v2; 
     1381            float fCos, fAngle, fMagS, fMagT; 
     1382            int i=-1, index=-1, i0=-1, i1=-1, i2=-1; 
     1383            if (piTriListIn[3*f+0]==iVertexRepresentitive) i=0; 
     1384            else if(piTriListIn[3*f+1]==iVertexRepresentitive) i=1; 
     1385            else if(piTriListIn[3*f+2]==iVertexRepresentitive) i=2; 
     1386            assert(i>=0 && i<3); 
     1387 
     1388            // project 
     1389            index = piTriListIn[3*f+i]; 
     1390            n = GetNormal(pContext, index); 
     1391            vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); 
     1392            vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); 
     1393            if ( VNotZero(vOs) ) vOs = Normalize(vOs); 
     1394            if ( VNotZero(vOt) ) vOt = Normalize(vOt); 
     1395 
     1396            i2 = piTriListIn[3*f + (i<2?(i+1):0)]; 
     1397            i1 = piTriListIn[3*f + i]; 
     1398            i0 = piTriListIn[3*f + (i>0?(i-1):2)]; 
     1399 
     1400            p0 = GetPosition(pContext, i0); 
     1401            p1 = GetPosition(pContext, i1); 
     1402            p2 = GetPosition(pContext, i2); 
     1403            v1 = vsub(p0,p1); 
     1404            v2 = vsub(p2,p1); 
     1405 
     1406            // project 
     1407            v1 = vsub(v1, vscale(vdot(n,v1),n)); if( VNotZero(v1) ) v1 = Normalize(v1); 
     1408            v2 = vsub(v2, vscale(vdot(n,v2),n)); if( VNotZero(v2) ) v2 = Normalize(v2); 
     1409 
     1410            // weight contribution by the angle 
     1411            // between the two edge vectors 
     1412            fCos = vdot(v1,v2); fCos=fCos>1?1:(fCos<(-1) ? (-1) : fCos); 
     1413            fAngle = (float) acos(fCos); 
     1414            fMagS = pTriInfos[f].fMagS; 
     1415            fMagT = pTriInfos[f].fMagT; 
     1416 
     1417            res.vOs=vadd(res.vOs, vscale(fAngle,vOs)); 
     1418            res.vOt=vadd(res.vOt,vscale(fAngle,vOt)); 
     1419            res.fMagS+=(fAngle*fMagS); 
     1420            res.fMagT+=(fAngle*fMagT); 
     1421            fAngleSum += fAngle; 
     1422        } 
     1423    } 
     1424 
     1425    // normalize 
     1426    if ( VNotZero(res.vOs) ) res.vOs = Normalize(res.vOs); 
     1427    if ( VNotZero(res.vOt) ) res.vOt = Normalize(res.vOt); 
     1428    if (fAngleSum>0) 
     1429    { 
     1430        res.fMagS /= fAngleSum; 
     1431        res.fMagT /= fAngleSum; 
     1432    } 
     1433 
     1434    return res; 
     1435} 
     1436 
     1437static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2) 
     1438{ 
     1439    tbool bStillSame=TTRUE; 
     1440    int i=0; 
     1441    if (pg1->iNrFaces!=pg2->iNrFaces) return TFALSE; 
     1442    while (i<pg1->iNrFaces && bStillSame) 
     1443    { 
     1444        bStillSame = pg1->pTriMembers[i]==pg2->pTriMembers[i] ? TTRUE : TFALSE; 
     1445        if (bStillSame) ++i; 
     1446    } 
     1447    return bStillSame; 
     1448} 
     1449 
     1450static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed) 
     1451{ 
     1452    int iL, iR, n, index, iMid, iTmp; 
     1453 
     1454    // Random 
     1455    unsigned int t=uSeed&31; 
     1456    t=(uSeed<<t)|(uSeed>>(32-t)); 
     1457    uSeed=uSeed+t+3; 
     1458    // Random end 
     1459 
     1460    iL=iLeft; iR=iRight; 
     1461    n = (iR-iL)+1; 
     1462    assert(n>=0); 
     1463    index = (int) (uSeed%n); 
     1464 
     1465    iMid=pSortBuffer[index + iL]; 
     1466 
     1467 
     1468    do 
     1469    { 
     1470        while (pSortBuffer[iL] < iMid) 
     1471            ++iL; 
     1472        while (pSortBuffer[iR] > iMid) 
     1473            --iR; 
     1474 
     1475        if (iL <= iR) 
     1476        { 
     1477            iTmp = pSortBuffer[iL]; 
     1478            pSortBuffer[iL] = pSortBuffer[iR]; 
     1479            pSortBuffer[iR] = iTmp; 
     1480            ++iL; --iR; 
     1481        } 
     1482    } 
     1483    while (iL <= iR); 
     1484 
     1485    if (iLeft < iR) 
     1486        QuickSort(pSortBuffer, iLeft, iR, uSeed); 
     1487    if (iL < iRight) 
     1488        QuickSort(pSortBuffer, iL, iRight, uSeed); 
     1489} 
     1490 
     1491///////////////////////////////////////////////////////////////////////////////////////////// 
     1492///////////////////////////////////////////////////////////////////////////////////////////// 
     1493 
     1494static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed); 
     1495static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in); 
     1496 
     1497static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn) 
     1498{ 
     1499    // build array of edges 
     1500    unsigned int uSeed = INTERNAL_RND_SORT_SEED;                // could replace with a random seed? 
     1501    int iEntries=0, iCurStartIndex=-1, f=0, i=0; 
     1502    for (f=0; f<iNrTrianglesIn; f++) 
     1503        for (i=0; i<3; i++) 
     1504        { 
     1505            const int i0 = piTriListIn[f*3+i]; 
     1506            const int i1 = piTriListIn[f*3+(i<2?(i+1):0)]; 
     1507            pEdges[f*3+i].i0 = i0 < i1 ? i0 : i1;           // put minimum index in i0 
     1508            pEdges[f*3+i].i1 = !(i0 < i1) ? i0 : i1;        // put maximum index in i1 
     1509            pEdges[f*3+i].f = f;                            // record face number 
     1510        } 
     1511 
     1512    // sort over all edges by i0, this is the pricy one. 
     1513    QuickSortEdges(pEdges, 0, iNrTrianglesIn*3-1, 0, uSeed);    // sort channel 0 which is i0 
     1514 
     1515    // sub sort over i1, should be fast. 
     1516    // could replace this with a 64 bit int sort over (i0,i1) 
     1517    // with i0 as msb in the quicksort call above. 
     1518    iEntries = iNrTrianglesIn*3; 
     1519    iCurStartIndex = 0; 
     1520    for (i=1; i<iEntries; i++) 
     1521    { 
     1522        if (pEdges[iCurStartIndex].i0 != pEdges[i].i0) 
     1523        { 
     1524            const int iL = iCurStartIndex; 
     1525            const int iR = i-1; 
     1526            //const int iElems = i-iL; 
     1527            iCurStartIndex = i; 
     1528            QuickSortEdges(pEdges, iL, iR, 1, uSeed);   // sort channel 1 which is i1 
     1529        } 
     1530    } 
     1531 
     1532    // sub sort over f, which should be fast. 
     1533    // this step is to remain compliant with BuildNeighborsSlow() when 
     1534    // more than 2 triangles use the same edge (such as a butterfly topology). 
     1535    iCurStartIndex = 0; 
     1536    for (i=1; i<iEntries; i++) 
     1537    { 
     1538        if (pEdges[iCurStartIndex].i0 != pEdges[i].i0 || pEdges[iCurStartIndex].i1 != pEdges[i].i1) 
     1539        { 
     1540            const int iL = iCurStartIndex; 
     1541            const int iR = i-1; 
     1542            //const int iElems = i-iL; 
     1543            iCurStartIndex = i; 
     1544            QuickSortEdges(pEdges, iL, iR, 2, uSeed);   // sort channel 2 which is f 
     1545        } 
     1546    } 
     1547 
     1548    // pair up, adjacent triangles 
     1549    for (i=0; i<iEntries; i++) 
     1550    { 
     1551        const int i0=pEdges[i].i0; 
     1552        const int i1=pEdges[i].i1; 
     1553        const int f = pEdges[i].f; 
     1554        tbool bUnassigned_A; 
     1555 
     1556        int i0_A, i1_A; 
     1557        int edgenum_A, edgenum_B=0; // 0,1 or 2 
     1558        GetEdge(&i0_A, &i1_A, &edgenum_A, &piTriListIn[f*3], i0, i1);   // resolve index ordering and edge_num 
     1559        bUnassigned_A = pTriInfos[f].FaceNeighbors[edgenum_A] == -1 ? TTRUE : TFALSE; 
     1560 
     1561        if (bUnassigned_A) 
     1562        { 
     1563            // get true index ordering 
     1564            int j=i+1, t; 
     1565            tbool bNotFound = TTRUE; 
     1566            while (j<iEntries && i0==pEdges[j].i0 && i1==pEdges[j].i1 && bNotFound) 
     1567            { 
     1568                tbool bUnassigned_B; 
     1569                int i0_B, i1_B; 
     1570                t = pEdges[j].f; 
     1571                // flip i0_B and i1_B 
     1572                GetEdge(&i1_B, &i0_B, &edgenum_B, &piTriListIn[t*3], pEdges[j].i0, pEdges[j].i1);   // resolve index ordering and edge_num 
     1573                //assert(!(i0_A==i1_B && i1_A==i0_B)); 
     1574                bUnassigned_B =  pTriInfos[t].FaceNeighbors[edgenum_B]==-1 ? TTRUE : TFALSE; 
     1575                if (i0_A==i0_B && i1_A==i1_B && bUnassigned_B) 
     1576                    bNotFound = TFALSE; 
     1577                else 
     1578                    ++j; 
     1579            } 
     1580 
     1581            if (!bNotFound) 
     1582            { 
     1583                int t = pEdges[j].f; 
     1584                pTriInfos[f].FaceNeighbors[edgenum_A] = t; 
     1585                //assert(pTriInfos[t].FaceNeighbors[edgenum_B]==-1); 
     1586                pTriInfos[t].FaceNeighbors[edgenum_B] = f; 
     1587            } 
     1588        } 
     1589    } 
     1590} 
     1591 
     1592static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn) 
     1593{ 
     1594    int f=0, i=0; 
     1595    for (f=0; f<iNrTrianglesIn; f++) 
     1596    { 
     1597        for (i=0; i<3; i++) 
     1598        { 
     1599            // if unassigned 
     1600            if (pTriInfos[f].FaceNeighbors[i] == -1) 
     1601            { 
     1602                const int i0_A = piTriListIn[f*3+i]; 
     1603                const int i1_A = piTriListIn[f*3+(i<2?(i+1):0)]; 
     1604 
     1605                // search for a neighbor 
     1606                tbool bFound = TFALSE; 
     1607                int t=0, j=0; 
     1608                while (!bFound && t<iNrTrianglesIn) 
     1609                { 
     1610                    if (t!=f) 
     1611                    { 
     1612                        j=0; 
     1613                        while (!bFound && j<3) 
     1614                        { 
     1615                            // in rev order 
     1616                            const int i1_B = piTriListIn[t*3+j]; 
     1617                            const int i0_B = piTriListIn[t*3+(j<2?(j+1):0)]; 
     1618                            //assert(!(i0_A==i1_B && i1_A==i0_B)); 
     1619                            if (i0_A==i0_B && i1_A==i1_B) 
     1620                                bFound = TTRUE; 
     1621                            else 
     1622                                ++j; 
     1623                        } 
     1624                    } 
     1625                     
     1626                    if (!bFound) ++t; 
     1627                } 
     1628 
     1629                // assign neighbors 
     1630                if (bFound) 
     1631                { 
     1632                    pTriInfos[f].FaceNeighbors[i] = t; 
     1633                    //assert(pTriInfos[t].FaceNeighbors[j]==-1); 
     1634                    pTriInfos[t].FaceNeighbors[j] = f; 
     1635                } 
     1636            } 
     1637        } 
     1638    } 
     1639} 
     1640 
     1641static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed) 
     1642{ 
     1643    unsigned int t; 
     1644    int iL, iR, n, index, iMid; 
     1645 
     1646    // early out 
     1647    SEdge sTmp; 
     1648    const int iElems = iRight-iLeft+1; 
     1649    if (iElems<2) return; 
     1650    else if(iElems==2) 
     1651    { 
     1652        if (pSortBuffer[iLeft].array[channel] > pSortBuffer[iRight].array[channel]) 
     1653        { 
     1654            sTmp = pSortBuffer[iLeft]; 
     1655            pSortBuffer[iLeft] = pSortBuffer[iRight]; 
     1656            pSortBuffer[iRight] = sTmp; 
     1657        } 
     1658        return; 
     1659    } 
     1660 
     1661    // Random 
     1662    t=uSeed&31; 
     1663    t=(uSeed<<t)|(uSeed>>(32-t)); 
     1664    uSeed=uSeed+t+3; 
     1665    // Random end 
     1666 
     1667    iL=iLeft, iR=iRight; 
     1668    n = (iR-iL)+1; 
     1669    assert(n>=0); 
     1670    index = (int) (uSeed%n); 
     1671 
     1672    iMid=pSortBuffer[index + iL].array[channel]; 
     1673 
     1674    do 
     1675    { 
     1676        while (pSortBuffer[iL].array[channel] < iMid) 
     1677            ++iL; 
     1678        while (pSortBuffer[iR].array[channel] > iMid) 
     1679            --iR; 
     1680 
     1681        if (iL <= iR) 
     1682        { 
     1683            sTmp = pSortBuffer[iL]; 
     1684            pSortBuffer[iL] = pSortBuffer[iR]; 
     1685            pSortBuffer[iR] = sTmp; 
     1686            ++iL; --iR; 
     1687        } 
     1688    } 
     1689    while (iL <= iR); 
     1690 
     1691    if (iLeft < iR) 
     1692        QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed); 
     1693    if (iL < iRight) 
     1694        QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed); 
     1695} 
     1696 
     1697// resolve ordering and edge number 
     1698static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in) 
     1699{ 
     1700    *edgenum_out = -1; 
     1701     
     1702    // test if first index is on the edge 
     1703    if (indices[0]==i0_in || indices[0]==i1_in) 
     1704    { 
     1705        // test if second index is on the edge 
     1706        if (indices[1]==i0_in || indices[1]==i1_in) 
     1707        { 
     1708            edgenum_out[0]=0;   // first edge 
     1709            i0_out[0]=indices[0]; 
     1710            i1_out[0]=indices[1]; 
     1711        } 
     1712        else 
     1713        { 
     1714            edgenum_out[0]=2;   // third edge 
     1715            i0_out[0]=indices[2]; 
     1716            i1_out[0]=indices[0]; 
     1717        } 
     1718    } 
     1719    else 
     1720    { 
     1721        // only second and third index is on the edge 
     1722        edgenum_out[0]=1;   // second edge 
     1723        i0_out[0]=indices[1]; 
     1724        i1_out[0]=indices[2]; 
     1725    } 
     1726} 
     1727 
     1728 
     1729///////////////////////////////////////////////////////////////////////////////////////////// 
     1730/////////////////////////////////// Degenerate triangles //////////////////////////////////// 
     1731 
     1732static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris) 
     1733{ 
     1734    int iNextGoodTriangleSearchIndex=-1; 
     1735    tbool bStillFindingGoodOnes; 
     1736 
     1737    // locate quads with only one good triangle 
     1738    int t=0; 
     1739    while (t<(iTotTris-1)) 
     1740    { 
     1741        const int iFO_a = pTriInfos[t].iOrgFaceNumber; 
     1742        const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; 
     1743        if (iFO_a==iFO_b)   // this is a quad 
     1744        { 
     1745            const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; 
     1746            const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; 
     1747            if ((bIsDeg_a^bIsDeg_b)!=0) 
     1748            { 
     1749                pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI; 
     1750                pTriInfos[t+1].iFlag |= QUAD_ONE_DEGEN_TRI; 
     1751            } 
     1752            t += 2; 
     1753        } 
     1754        else 
     1755            ++t; 
     1756    } 
     1757 
     1758    // reorder list so all degen triangles are moved to the back 
     1759    // without reordering the good triangles 
     1760    iNextGoodTriangleSearchIndex = 1; 
     1761    t=0; 
     1762    bStillFindingGoodOnes = TTRUE; 
     1763    while (t<iNrTrianglesIn && bStillFindingGoodOnes) 
     1764    { 
     1765        const tbool bIsGood = (pTriInfos[t].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE; 
     1766        if (bIsGood) 
     1767        { 
     1768            if (iNextGoodTriangleSearchIndex < (t+2)) 
     1769                iNextGoodTriangleSearchIndex = t+2; 
     1770        } 
     1771        else 
     1772        { 
     1773            int t0, t1; 
     1774            // search for the first good triangle. 
     1775            tbool bJustADegenerate = TTRUE; 
     1776            while (bJustADegenerate && iNextGoodTriangleSearchIndex<iTotTris) 
     1777            { 
     1778                const tbool bIsGood = (pTriInfos[iNextGoodTriangleSearchIndex].iFlag&MARK_DEGENERATE)==0 ? TTRUE : TFALSE; 
     1779                if (bIsGood) bJustADegenerate=TFALSE; 
     1780                else ++iNextGoodTriangleSearchIndex; 
     1781            } 
     1782 
     1783            t0 = t; 
     1784            t1 = iNextGoodTriangleSearchIndex; 
     1785            ++iNextGoodTriangleSearchIndex; 
     1786            assert(iNextGoodTriangleSearchIndex > (t+1)); 
     1787 
     1788            // swap triangle t0 and t1 
     1789            if (!bJustADegenerate) 
     1790            { 
     1791                int i=0; 
     1792                for (i=0; i<3; i++) 
     1793                { 
     1794                    const int index = piTriList_out[t0*3+i]; 
     1795                    piTriList_out[t0*3+i] = piTriList_out[t1*3+i]; 
     1796                    piTriList_out[t1*3+i] = index; 
     1797                } 
     1798                { 
     1799                    const STriInfo tri_info = pTriInfos[t0]; 
     1800                    pTriInfos[t0] = pTriInfos[t1]; 
     1801                    pTriInfos[t1] = tri_info; 
     1802                } 
     1803            } 
     1804            else 
     1805                bStillFindingGoodOnes = TFALSE; // this is not supposed to happen 
     1806        } 
     1807 
     1808        if (bStillFindingGoodOnes) ++t; 
     1809    } 
     1810 
     1811    assert(bStillFindingGoodOnes);  // code will still work. 
     1812    assert(iNrTrianglesIn == t); 
     1813} 
     1814 
     1815static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris) 
     1816{ 
     1817    int t=0, i=0; 
     1818    // deal with degenerate triangles 
     1819    // punishment for degenerate triangles is O(N^2) 
     1820    for (t=iNrTrianglesIn; t<iTotTris; t++) 
     1821    { 
     1822        // degenerate triangles on a quad with one good triangle are skipped 
     1823        // here but processed in the next loop 
     1824        const tbool bSkip = (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 ? TTRUE : TFALSE; 
     1825 
     1826        if (!bSkip) 
     1827        { 
     1828            for (i=0; i<3; i++) 
     1829            { 
     1830                const int index1 = piTriListIn[t*3+i]; 
     1831                // search through the good triangles 
     1832                tbool bNotFound = TTRUE; 
     1833                int j=0; 
     1834                while (bNotFound && j<(3*iNrTrianglesIn)) 
     1835                { 
     1836                    const int index2 = piTriListIn[j]; 
     1837                    if (index1==index2) bNotFound=TFALSE; 
     1838                    else ++j; 
     1839                } 
     1840 
     1841                if (!bNotFound) 
     1842                { 
     1843                    const int iTri = j/3; 
     1844                    const int iVert = j%3; 
     1845                    const int iSrcVert=pTriInfos[iTri].vert_num[iVert]; 
     1846                    const int iSrcOffs=pTriInfos[iTri].iTSpacesOffs; 
     1847                    const int iDstVert=pTriInfos[t].vert_num[i]; 
     1848                    const int iDstOffs=pTriInfos[t].iTSpacesOffs; 
     1849                     
     1850                    // copy tspace 
     1851                    psTspace[iDstOffs+iDstVert] = psTspace[iSrcOffs+iSrcVert]; 
     1852                } 
     1853            } 
     1854        } 
     1855    } 
     1856 
     1857    // deal with degenerate quads with one good triangle 
     1858    for (t=0; t<iNrTrianglesIn; t++) 
     1859    { 
     1860        // this triangle belongs to a quad where the 
     1861        // other triangle is degenerate 
     1862        if ( (pTriInfos[t].iFlag&QUAD_ONE_DEGEN_TRI)!=0 ) 
     1863        { 
     1864            SVec3 vDstP; 
     1865            int iOrgF=-1, i=0; 
     1866            tbool bNotFound; 
     1867            unsigned char * pV = pTriInfos[t].vert_num; 
     1868            int iFlag = (1<<pV[0]) | (1<<pV[1]) | (1<<pV[2]); 
     1869            int iMissingIndex = 0; 
     1870            if ((iFlag&2)==0) iMissingIndex=1; 
     1871            else if((iFlag&4)==0) iMissingIndex=2; 
     1872            else if((iFlag&8)==0) iMissingIndex=3; 
     1873 
     1874            iOrgF = pTriInfos[t].iOrgFaceNumber; 
     1875            vDstP = GetPosition(pContext, MakeIndex(iOrgF, iMissingIndex)); 
     1876            bNotFound = TTRUE; 
     1877            i=0; 
     1878            while (bNotFound && i<3) 
     1879            { 
     1880                const int iVert = pV[i]; 
     1881                const SVec3 vSrcP = GetPosition(pContext, MakeIndex(iOrgF, iVert)); 
     1882                if (veq(vSrcP, vDstP)==TTRUE) 
     1883                { 
     1884                    const int iOffs = pTriInfos[t].iTSpacesOffs; 
     1885                    psTspace[iOffs+iMissingIndex] = psTspace[iOffs+iVert]; 
     1886                    bNotFound=TFALSE; 
     1887                } 
     1888                else 
     1889                    ++i; 
     1890            } 
     1891            assert(!bNotFound); 
     1892        } 
     1893    } 
     1894} 
  • source/graphics/ObjectBase.h

     
    8080        int m_Frequency; 
    8181        VfsPath m_ModelFilename; 
    8282        VfsPath m_TextureFilename; 
     83        VfsPath m_NormalFilename; 
     84        VfsPath m_SpecularFilename; 
    8385        Decal m_Decal; 
    8486        VfsPath m_Particles; 
    8587        CStr m_Color; 
     
    9193    struct Variation 
    9294    { 
    9395        VfsPath texture; 
     96        VfsPath normal; 
     97        VfsPath specular; 
    9498        VfsPath model; 
    9599        Decal decal; 
    96100        VfsPath particles; 
  • source/graphics/weldmesh.h

     
     1/** 
     2 *  Copyright (C) 2011 by Morten S. Mikkelsen 
     3 * 
     4 *  This software is provided 'as-is', without any express or implied 
     5 *  warranty.  In no event will the authors be held liable for any damages 
     6 *  arising from the use of this software. 
     7 * 
     8 *  Permission is granted to anyone to use this software for any purpose, 
     9 *  including commercial applications, and to alter it and redistribute it 
     10 *  freely, subject to the following restrictions: 
     11 * 
     12 *  1. The origin of this software must not be misrepresented; you must not 
     13 *     claim that you wrote the original software. If you use this software 
     14 *     in a product, an acknowledgment in the product documentation would be 
     15 *     appreciated but is not required. 
     16 *  2. Altered source versions must be plainly marked as such, and must not be 
     17 *     misrepresented as being the original software. 
     18 *  3. This notice may not be removed or altered from any source distribution. 
     19 */ 
     20 
     21 
     22#ifndef __WELDMESH_H__ 
     23#define __WELDMESH_H__ 
     24 
     25 
     26#ifdef __cplusplus 
     27extern "C" { 
     28#endif 
     29 
     30// piRemapTable must be initialized and point to an area in memory 
     31// with the byte size: iNrVerticesIn * sizeof(int). 
     32// pfVertexDataOut must be initialized and point to an area in memory 
     33// with the byte size: iNrVerticesIn * iFloatsPerVert * sizeof(float). 
     34// At the end of the WeldMesh() call the array pfVertexDataOut will contain 
     35// unique vertices only. Each entry in piRemapTable contains the index to 
     36// the new location of the vertex in pfVertexDataOut in units of iFloatsPerVert. 
     37// Note that this code is suitable for welding both unindexed meshes but also 
     38// indexed meshes which need to have duplicates removed. In the latter case 
     39// one simply uses the remap table to convert the old index list to the new vertex array. 
     40// Finally, the return value is the number of unique vertices found. 
     41int WeldMesh(int * piRemapTable, float * pfVertexDataOut, 
     42              const float pfVertexDataIn[], const int iNrVerticesIn, const int iFloatsPerVert); 
     43 
     44#ifdef __cplusplus 
     45} 
     46#endif 
     47 
     48 
     49#endif 
     50 No newline at end of file 
  • source/graphics/Material.cpp

     
    3232    m_DiffuseTexture = texture; 
    3333} 
    3434 
     35void CMaterial::SetNormalTexture(const CTexturePtr& texture) 
     36{ 
     37    m_NormalTexture = texture; 
     38} 
     39 
     40void CMaterial::SetSpecularTexture(const CTexturePtr& texture) 
     41{ 
     42    m_SpecularTexture = texture; 
     43} 
     44 
    3545void CMaterial::SetShaderEffect(const CStr& effect) 
    3646{ 
    3747    m_ShaderEffect = CStrIntern(effect); 
  • source/graphics/ObjectBase.cpp

     
    6262    EL(prop); 
    6363    EL(mesh); 
    6464    EL(texture); 
     65    EL(normal); 
     66    EL(specular); 
    6567    EL(colour); 
    6668    EL(decal); 
    6769    EL(particles); 
     
    151153                    { 
    152154                        currentVariant->m_TextureFilename = VfsPath("art/textures/skins") / option.GetText().FromUTF8(); 
    153155                    } 
     156                    else if (option_name == el_normal) 
     157                    { 
     158                        currentVariant->m_NormalFilename = VfsPath("art/textures/skins") / option.GetText().FromUTF8(); 
     159                    }        
     160                    else if (option_name == el_specular) 
     161                    { 
     162                        currentVariant->m_SpecularFilename = VfsPath("art/textures/skins") / option.GetText().FromUTF8(); 
     163                    }                    
    154164                    else if (option_name == el_decal) 
    155165                    { 
    156166                        XMBAttributeList attrs = option.GetAttributes(); 
     
    399409        if (! var.m_TextureFilename.empty()) 
    400410            variation.texture = var.m_TextureFilename; 
    401411 
     412        if (! var.m_NormalFilename.empty()) 
     413            variation.normal = var.m_NormalFilename; 
     414 
     415        if (! var.m_SpecularFilename.empty()) 
     416            variation.specular = var.m_SpecularFilename;     
     417 
    402418        if (! var.m_ModelFilename.empty()) 
    403419            variation.model = var.m_ModelFilename; 
    404420 
  • source/graphics/ObjectEntry.cpp

     
    6161    // Copy the chosen data onto this model: 
    6262 
    6363    m_TextureName = variation.texture; 
     64    m_NormalName = variation.normal; 
     65    m_SpecularName = variation.specular; 
    6466    m_ModelName = variation.model; 
    6567 
    6668    if (! variation.color.empty()) 
     
    128130    texture->Prefetch(); // if we've loaded this model we're probably going to render it soon, so prefetch its texture 
    129131    model->GetMaterial().SetDiffuseTexture(texture); 
    130132 
     133    CTextureProperties normalProps(m_NormalName); 
     134    normalProps.SetWrap(GL_CLAMP_TO_EDGE); 
     135    CTexturePtr normal = g_Renderer.GetTextureManager().CreateTexture(normalProps); 
     136    //normal->Prefetch(); // if we've loaded this model we're probably going to render it soon, so prefetch its texture 
     137    model->GetMaterial().SetNormalTexture(normal); 
     138 
     139    CTextureProperties specularProps(m_SpecularName); 
     140    specularProps.SetWrap(GL_CLAMP_TO_EDGE); 
     141    CTexturePtr specular = g_Renderer.GetTextureManager().CreateTexture(specularProps); 
     142    //specular->Prefetch(); // if we've loaded this model we're probably going to render it soon, so prefetch its texture 
     143    model->GetMaterial().SetSpecularTexture(specular); 
     144 
    131145    // calculate initial object space bounds, based on vertex positions 
    132146    model->CalcStaticObjectBounds(); 
    133147 
  • source/renderer/RenderModifiers.h

     
    7373     */ 
    7474    virtual void PrepareTexture(const CShaderProgramPtr& shader, CTexture& texture) = 0; 
    7575 
     76    virtual void PrepareNormal(const CShaderProgramPtr& shader, CTexture& normal) = 0; 
     77 
     78    virtual void PrepareSpecular(const CShaderProgramPtr& shader, CTexture& specular) = 0; 
     79 
    7680    /** 
    7781     * PrepareModel: Called before rendering the given model. 
    7882     * 
     
    134138    // Implementation 
    135139    void BeginPass(const CShaderProgramPtr& shader); 
    136140    void PrepareTexture(const CShaderProgramPtr& shader, CTexture& texture); 
     141    void PrepareNormal(const CShaderProgramPtr& shader, CTexture& normal); 
     142    void PrepareSpecular(const CShaderProgramPtr& shader, CTexture& specular); 
    137143    void PrepareModel(const CShaderProgramPtr& shader, CModel* model); 
    138144 
    139145private: 
     
    141147    CShaderProgram::Binding m_BindingShadingColor; 
    142148    CShaderProgram::Binding m_BindingPlayerColor; 
    143149    CShaderProgram::Binding m_BindingBaseTex; 
     150    CShaderProgram::Binding m_BindingNormTex; 
     151    CShaderProgram::Binding m_BindingSpecTex; 
    144152}; 
    145153 
    146154#endif // INCLUDED_RENDERMODIFIERS 
  • source/renderer/ModelRenderer.cpp

     
    3535#include "renderer/ModelVertexRenderer.h" 
    3636#include "renderer/Renderer.h" 
    3737#include "renderer/RenderModifiers.h" 
     38#include "renderer/MikktspaceWrap.h" 
    3839 
    3940#include <boost/weak_ptr.hpp> 
    4041 
     
    5758#endif 
    5859} 
    5960 
     61void ModelRenderer::GenTangents(const CModelDefPtr& mdef, std::vector<float>& newVertices) 
     62{ 
     63    MikkTSpace ms(mdef, newVertices); 
     64 
     65    ms.generate(); 
     66} 
     67 
    6068// Helper function to copy object-space position and normal vectors into arrays. 
    6169void ModelRenderer::CopyPositionAndNormals( 
    6270        const CModelDefPtr& mdef, 
     
    557565                m->vertexRenderer->BeginPass(streamflags); 
    558566 
    559567                CTexture* currentTex = NULL; 
     568                CTexture* currentNorm = NULL; 
     569                CTexture* currentSpec = NULL; 
    560570                CModelDef* currentModeldef = NULL; 
    561571                CShaderUniforms currentStaticUniforms; 
    562572                // (Texture needs to be rebound after binding a new shader, so we 
     
    581591                            modifier->PrepareTexture(shader, *currentTex); 
    582592                        } 
    583593 
     594                        CTexture* newNorm = model->GetMaterial().GetNormalTexture().get(); 
     595                        if (newNorm != currentNorm) 
     596                        { 
     597                            currentNorm = newNorm; 
     598                            modifier->PrepareNormal(shader, *currentNorm); 
     599                        }    
     600 
     601                        CTexture* newSpec = model->GetMaterial().GetSpecularTexture().get(); 
     602                        if (newSpec != currentSpec) 
     603                        { 
     604                            currentSpec = newSpec; 
     605                            modifier->PrepareSpecular(shader, *currentSpec); 
     606                        }    
     607 
    584608                        // Bind modeldef when it changes 
    585609                        CModelDef* newModeldef = model->GetModelDef().get(); 
    586610                        if (newModeldef != currentModeldef) 
  • source/renderer/MikktspaceWrap.h

     
     1#ifndef INCLUDED_MIKKWRAP 
     2#define INCLUDED_MIKKWRAP 
     3 
     4 
     5#include <graphics/mikktspace.h> 
     6 
     7class MikkTSpace 
     8{ 
     9 
     10public: 
     11     
     12    MikkTSpace(const CModelDefPtr& m, std::vector<float>& v); 
     13 
     14    void generate(); 
     15     
     16private: 
     17     
     18    SMikkTSpaceInterface interface; 
     19    SMikkTSpaceContext context; 
     20 
     21    const CModelDefPtr& model; 
     22 
     23    std::vector<float>& newVertices; 
     24     
     25 
     26    // Returns the number of faces (triangles/quads) on the mesh to be processed. 
     27    static int getNumFaces(const SMikkTSpaceContext *pContext); 
     28 
     29 
     30    // Returns the number of vertices on face number iFace 
     31    // iFace is a number in the range {0, 1, ..., getNumFaces()-1} 
     32    static int getNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace); 
     33 
     34 
     35    // returns the position/normal/texcoord of the referenced face of vertex number iVert. 
     36    // iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads. 
     37    static void getPosition(const SMikkTSpaceContext *pContext,  
     38            float fvPosOut[], const int iFace, const int iVert); 
     39 
     40    static void getNormal(const SMikkTSpaceContext *pContext,  
     41            float fvNormOut[], const int iFace, const int iVert); 
     42 
     43    static void getTexCoord(const SMikkTSpaceContext *pContext,  
     44            float fvTexcOut[], const int iFace, const int iVert); 
     45 
     46 
     47    // either (or both) of the two setTSpace callbacks can be set. 
     48    // The call-back m_setTSpaceBasic() is sufficient for basic normal mapping. 
     49 
     50    // This function is used to return the tangent and fSign to the application. 
     51    // fvTangent is a unit length vector. 
     52    // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. 
     53    // bitangent = fSign * cross(vN, tangent); 
     54    // Note that the results are returned unindexed. It is possible to generate a new index list 
     55    // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. 
     56    // DO NOT! use an already existing index list. 
     57    //void setTSpaceBasic(const MikkTSpace *parent, const SMikkTSpaceContext *pContext,  
     58    //      const float fvTangent[], const float fSign, const int iFace, const int iVert); 
     59 
     60 
     61    // This function is used to return tangent space results to the application. 
     62    // fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their 
     63    // true magnitudes which can be used for relief mapping effects. 
     64    // fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent. 
     65    // However, both are perpendicular to the vertex normal. 
     66    // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. 
     67    // fSign = bIsOrientationPreserving ? 1.0f : (-1.0f); 
     68    // bitangent = fSign * cross(vN, tangent); 
     69    // Note that the results are returned unindexed. It is possible to generate a new index list 
     70    // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. 
     71    // DO NOT! use an already existing index list. 
     72    static void setTSpace(const SMikkTSpaceContext * pContext, const float fvTangent[],  
     73            const float fvBiTangent[], const float fMagS, const float fMagT,  
     74            const tbool bIsOrientationPreserving, const int iFace, const int iVert); 
     75 
     76 
     77}; 
     78 
     79 
     80#endif // INCLUDED_MIKKWRAP 
     81 No newline at end of file 
  • source/renderer/InstancingModelRenderer.cpp

     
    3131#include "graphics/LightEnv.h" 
    3232#include "graphics/Model.h" 
    3333#include "graphics/ModelDef.h" 
     34#include "graphics/weldmesh.h" 
    3435 
    3536#include "renderer/InstancingModelRenderer.h" 
    3637#include "renderer/Renderer.h" 
     
    4950    /// Position, normals and UV are all static 
    5051    VertexArray::Attribute m_Position; 
    5152    VertexArray::Attribute m_Normal; 
     53    VertexArray::Attribute m_Tangent; 
    5254    VertexArray::Attribute m_UV; 
    5355    VertexArray::Attribute m_BlendJoints; // valid iff gpuSkinning == true 
    5456    VertexArray::Attribute m_BlendWeights; // valid iff gpuSkinning == true 
     
    7678    m_UV.type = GL_FLOAT; 
    7779    m_UV.elems = 2; 
    7880    m_Array.AddAttribute(&m_UV); 
     81     
     82    m_Tangent.type = GL_FLOAT; 
     83    m_Tangent.elems = 4; 
     84    m_Array.AddAttribute(&m_Tangent); 
    7985 
    8086    if (gpuSkinning) 
    8187    { 
     
    8793        m_BlendWeights.elems = 4; 
    8894        m_Array.AddAttribute(&m_BlendWeights); 
    8995    } 
     96     
    9097 
    91     m_Array.SetNumVertices(numVertices); 
     98    std::vector<float> newVertices; 
     99     
     100    SModelVertex* vertices = mdef->GetVertices(); 
     101     
     102    ModelRenderer::GenTangents(mdef, newVertices); 
     103     
     104    int numVertexAttrs = 3 + 3 + 2 + 4; 
     105     
     106    int vert = newVertices.size() / numVertexAttrs; 
     107     
     108    std::vector<int> remapTable(vert); 
     109    std::vector<float> vertexDataIn(vert * numVertexAttrs);  
     110    std::vector<float> vertexDataOut(vert * numVertexAttrs); 
     111         
     112    vertexDataIn = newVertices; 
     113 
     114    int numVertices2 = WeldMesh(&remapTable[0], &vertexDataOut[0], 
     115              &vertexDataIn[0], vert, numVertexAttrs); 
     116 
     117    m_Array.SetNumVertices(numVertices2); 
    92118    m_Array.Layout(); 
    93119 
    94120    VertexArrayIterator<CVector3D> Position = m_Position.GetIterator<CVector3D>(); 
    95121    VertexArrayIterator<CVector3D> Normal = m_Normal.GetIterator<CVector3D>(); 
    96122    VertexArrayIterator<float[2]> UVit = m_UV.GetIterator<float[2]>(); 
     123    VertexArrayIterator<CVector4D> Tangent = m_Tangent.GetIterator<CVector4D>(); 
    97124 
    98     ModelRenderer::CopyPositionAndNormals(mdef, Position, Normal); 
    99     ModelRenderer::BuildUV(mdef, UVit); 
     125    for (int i = 0; i < numVertices2; i++) 
     126    {    
     127        int p = remapTable[i]; 
     128         
     129        int q = numVertexAttrs * i; 
     130         
     131        Position[i] = CVector3D(vertexDataOut[q + 0], vertexDataOut[q + 1], vertexDataOut[q + 2]); 
     132         
     133        Normal[i] = CVector3D(vertexDataOut[q + 3], vertexDataOut[q + 4], vertexDataOut[q + 5]); 
     134         
     135        UVit[i][0] = vertexDataOut[q + 6]; 
     136        UVit[i][1] = vertexDataOut[q + 7]; 
     137         
     138        Tangent[i] = CVector4D(vertexDataOut[q + 8], vertexDataOut[q + 9], vertexDataOut[q + 10],  
     139                       vertexDataOut[q + 11]); 
     140    } 
    100141 
    101142    if (gpuSkinning) 
    102143    { 
     
    118159 
    119160    m_IndexArray.SetNumVertices(mdef->GetNumFaces()*3); 
    120161    m_IndexArray.Layout(); 
    121     ModelRenderer::BuildIndices(mdef, m_IndexArray.GetIterator()); 
     162     
     163    VertexArrayIterator<u16> Indices = m_IndexArray.GetIterator(); 
     164     
     165    size_t idxidx = 0; 
     166    SModelFace* faces = mdef->GetFaces();    
     167 
     168    for (size_t j = 0; j < mdef->GetNumFaces(); ++j) {   
     169        Indices[idxidx++]=remapTable[j * 3 + 0]; 
     170        Indices[idxidx++]=remapTable[j * 3 + 1]; 
     171        Indices[idxidx++]=remapTable[j * 3 + 2]; 
     172    } 
     173     
     174     
     175     
    122176    m_IndexArray.Upload(); 
    123177    m_IndexArray.FreeBackingStore(); 
    124178} 
     
    211265    if (streamflags & STREAM_UV0) 
    212266        shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, stride, base + m->imodeldef->m_UV.offset); 
    213267 
     268    shader->VertexAttribPointer("a_tangent", 4, GL_FLOAT, GL_TRUE, stride, base + m->imodeldef->m_Tangent.offset); 
     269 
    214270    // GPU skinning requires extra attributes to compute positions/normals 
    215271    if (m->gpuSkinning) 
    216272    { 
  • source/renderer/RenderModifiers.cpp

     
    104104    m_BindingShadingColor = shader->GetUniformBinding("shadingColor"); 
    105105    m_BindingPlayerColor = shader->GetUniformBinding("playerColor"); 
    106106    m_BindingBaseTex = shader->GetTextureBinding("baseTex"); 
     107    m_BindingNormTex = shader->GetTextureBinding("normTex"); 
     108    m_BindingSpecTex = shader->GetTextureBinding("specTex"); 
    107109} 
    108110 
    109111void ShaderRenderModifier::PrepareTexture(const CShaderProgramPtr& shader, CTexture& texture) 
     
    112114        shader->BindTexture(m_BindingBaseTex, texture.GetHandle()); 
    113115} 
    114116 
     117void ShaderRenderModifier::PrepareNormal(const CShaderProgramPtr& shader, CTexture& normal) 
     118{ 
     119    if (m_BindingNormTex.Active()) 
     120        shader->BindTexture(m_BindingNormTex, normal.GetHandle()); 
     121} 
     122 
     123void ShaderRenderModifier::PrepareSpecular(const CShaderProgramPtr& shader, CTexture& specular) 
     124{ 
     125    if (m_BindingSpecTex.Active()) 
     126        shader->BindTexture(m_BindingSpecTex, specular.GetHandle()); 
     127} 
     128 
    115129void ShaderRenderModifier::PrepareModel(const CShaderProgramPtr& shader, CModel* model) 
    116130{ 
    117131    if (m_BindingInstancingTransform.Active()) 
  • source/renderer/MikktspaceWrap.cpp

     
     1 
     2#include "precompiled.h" 
     3 
     4#include <boost/bind.hpp> 
     5 
     6#include "graphics/Color.h" 
     7#include "graphics/LightEnv.h" 
     8#include "graphics/Model.h" 
     9#include "graphics/ModelDef.h" 
     10#include "graphics/ShaderManager.h" 
     11#include "graphics/TextureManager.h" 
     12#include <graphics/mikktspace.h> 
     13 
     14#include <renderer/MikktspaceWrap.h> 
     15 
     16 
     17 
     18MikkTSpace::MikkTSpace(const CModelDefPtr& m, std::vector<float>& v) : model(m), newVertices(v) 
     19{    
     20    interface.m_getNumFaces = getNumFaces; 
     21    interface.m_getNumVerticesOfFace = getNumVerticesOfFace; 
     22    interface.m_getPosition = getPosition; 
     23    interface.m_getNormal = getNormal; 
     24    interface.m_getTexCoord = getTexCoord; 
     25    interface.m_setTSpaceBasic = NULL; 
     26    interface.m_setTSpace = setTSpace; 
     27 
     28    context.m_pInterface = &interface; 
     29    context.m_pUserData = (void*)this; 
     30} 
     31 
     32void MikkTSpace::generate() 
     33{ 
     34    genTangSpaceDefault(&context); 
     35} 
     36 
     37 
     38int MikkTSpace::getNumFaces(const SMikkTSpaceContext *pContext) 
     39{ 
     40    return ((MikkTSpace*)pContext->m_pUserData)->model->GetNumFaces(); 
     41} 
     42 
     43 
     44int MikkTSpace::getNumVerticesOfFace(const SMikkTSpaceContext *pContext, const int iFace) 
     45{ 
     46    return 3; 
     47} 
     48 
     49 
     50void MikkTSpace::getPosition(const SMikkTSpaceContext *pContext,  
     51        float fvPosOut[], const int iFace, const int iVert) 
     52{ 
     53    SModelFace &face = ((MikkTSpace*)pContext->m_pUserData)->model->GetFaces()[iFace]; 
     54    long i = face.m_Verts[iVert]; 
     55    const CVector3D &p = ((MikkTSpace*)pContext->m_pUserData)->model->GetVertices()[i].m_Coords; 
     56 
     57    fvPosOut[0] = p.X; 
     58    fvPosOut[1] = p.Y; 
     59    fvPosOut[2] = p.Z; 
     60} 
     61 
     62 
     63void MikkTSpace::getNormal(const SMikkTSpaceContext *pContext,  
     64        float fvNormOut[], const int iFace, const int iVert) 
     65{ 
     66    SModelFace &face = ((MikkTSpace*)pContext->m_pUserData)->model->GetFaces()[iFace]; 
     67    long i = face.m_Verts[iVert]; 
     68    const CVector3D &n = ((MikkTSpace*)pContext->m_pUserData)->model->GetVertices()[i].m_Norm; 
     69 
     70    fvNormOut[0] = n.X; 
     71    fvNormOut[1] = n.Y; 
     72    fvNormOut[2] = n.Z; 
     73} 
     74 
     75 
     76void MikkTSpace::getTexCoord(const SMikkTSpaceContext *pContext,  
     77        float fvTexcOut[], const int iFace, const int iVert) 
     78{ 
     79    SModelFace &face = ((MikkTSpace*)pContext->m_pUserData)->model->GetFaces()[iFace]; 
     80    long i = face.m_Verts[iVert]; 
     81    SModelVertex &v = ((MikkTSpace*)pContext->m_pUserData)->model->GetVertices()[i]; 
     82 
     83    fvTexcOut[0] = v.m_U; 
     84    fvTexcOut[1] = 1.0-v.m_V;        
     85} 
     86 
     87 
     88void MikkTSpace::setTSpace(const SMikkTSpaceContext * pContext, const float fvTangent[],  
     89        const float fvBiTangent[], const float fMagS, const float fMagT,  
     90        const tbool bIsOrientationPreserving, const int iFace, const int iVert) 
     91{ 
     92    SModelFace &face = ((MikkTSpace*)pContext->m_pUserData)->model->GetFaces()[iFace]; 
     93    long i = face.m_Verts[iVert]; 
     94     
     95    const CVector3D &p = ((MikkTSpace*)pContext->m_pUserData)->model->GetVertices()[i].m_Coords; 
     96    const CVector3D &n = ((MikkTSpace*)pContext->m_pUserData)->model->GetVertices()[i].m_Norm; 
     97    const float u = ((MikkTSpace*)pContext->m_pUserData)->model->GetVertices()[i].m_U; 
     98    const float v = 1.0 - ((MikkTSpace*)pContext->m_pUserData)->model->GetVertices()[i].m_V; 
     99     
     100    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(p.X); 
     101    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(p.Y); 
     102    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(p.Z); 
     103     
     104    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(n.X); 
     105    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(n.Y); 
     106    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(n.Z); 
     107     
     108    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(u); 
     109    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(v); 
     110     
     111    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(fvTangent[0]); 
     112    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(fvTangent[1]); 
     113    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(fvTangent[2]); 
     114    ((MikkTSpace*)pContext->m_pUserData)->newVertices.push_back(bIsOrientationPreserving > 0.5 ? 1.0f : (-1.0f)); 
     115} 
     116 
     117 
     118 
  • source/renderer/ModelRenderer.h

     
    253253    static void BuildIndices( 
    254254            const CModelDefPtr& mdef, 
    255255            const VertexArrayIterator<u16>& Indices); 
     256 
     257 
     258    static void GenTangents(const CModelDefPtr& mdef, std::vector<float>& newVertices); 
    256259}; 
    257260 
    258261 
  • binaries/data/mods/public/art/materials/player_trans_norm.xml

     
     1<?xml version="1.0" encoding="utf-8"?> 
     2<material> 
     3    <shader effect="model_norm"/> 
     4    <define name="USE_PLAYERCOLOR" value="1"/> 
     5</material> 
  • binaries/data/mods/public/art/actors/structures/romans/civic_centre.xml

    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
    
    Property changes on: binaries/data/mods/public/art/textures/skins/structural/rome_struct_spec.png
    ___________________________________________________________________
    Added: svn:mime-type
       + application/octet-stream
    
    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
    
    Property changes on: binaries/data/mods/public/art/textures/skins/structural/rome_struct_norm.png
    ___________________________________________________________________
    Added: svn:mime-type
       + application/octet-stream
    
    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
    
    Property changes on: binaries/data/mods/public/art/textures/skins/structural/rome_struct_arch_spec.png
    ___________________________________________________________________
    Added: svn:mime-type
       + application/octet-stream
    
    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
    
    Property changes on: binaries/data/mods/public/art/textures/skins/structural/rome_struct_arch_norm.png
    ___________________________________________________________________
    Added: svn:mime-type
       + application/octet-stream
    
     
    1515        <prop actor="props/units/weapons/arrow_front.xml" attachpoint="projectile"/> 
    1616      </props> 
    1717      <texture>structural/rome_struct.png</texture> 
     18      <normal>structural/rome_struct_norm.png</normal> 
     19      <specular>structural/rome_struct_spec.png</specular> 
    1820    </variant> 
    1921  </group> 
    2022  <group> 
     
    3638      </props> 
    3739    </variant> 
    3840  </group> 
    39   <material>player_trans.xml</material> 
     41  <material>player_trans_norm.xml</material> 
    4042</actor> 
  • binaries/data/mods/public/art/actors/structures/romans/triumphal_arch.xml

     
    55    <variant frequency="100" name="Roman Triumphal Arch"> 
    66      <mesh>structural/rome_arch.dae</mesh> 
    77      <texture>structural/rome_struct_arch.dds</texture> 
     8      <normal>structural/rome_struct_arch_norm.png</normal> 
     9      <specular>structural/rome_struct_arch_spec.png</specular> 
    810    </variant> 
    911  </group> 
    1012  <group> 
     
    1719      </props> 
    1820    </variant> 
    1921  </group> 
    20   <material>player_trans.xml</material> 
     22  <material>player_trans_norm.xml</material> 
    2123</actor> 
  • binaries/data/mods/public/shaders/effects/model_norm.xml

     
     1<?xml version="1.0" encoding="utf-8"?> 
     2<effect> 
     3 
     4    <technique> 
     5        <require context="MODE_SHADOWCAST || MODE_SILHOUETTEOCCLUDER"/> 
     6        <require shaders="arb"/> 
     7        <pass shader="arb/model_solid"/> 
     8    </technique> 
     9 
     10    <technique> 
     11        <require context="MODE_SHADOWCAST || MODE_SILHOUETTEOCCLUDER"/> 
     12        <require shaders="glsl"/> 
     13        <pass shader="glsl/model_solid"/> 
     14    </technique> 
     15 
     16    <technique> 
     17        <require context="MODE_SHADOWCAST || MODE_SILHOUETTEOCCLUDER"/> 
     18        <require shaders="fixed"/> 
     19        <define name="USE_PLAYERCOLOR" value="0"/> 
     20        <define name="USE_OBJECTCOLOR" value="0"/> 
     21        <pass shader="fixed:model_solid"/> 
     22    </technique> 
     23 
     24 
     25    <technique> 
     26        <require context="MODE_SILHOUETTEDISPLAY || MODE_WIREFRAME"/> 
     27        <require shaders="arb"/> 
     28        <pass shader="arb/model_solid_player"/> 
     29    </technique> 
     30 
     31    <technique> 
     32        <require context="MODE_SILHOUETTEDISPLAY || MODE_WIREFRAME"/> 
     33        <require shaders="glsl"/> 
     34        <pass shader="glsl/model_solid_player"/> 
     35    </technique> 
     36 
     37    <technique> 
     38        <require context="MODE_SILHOUETTEDISPLAY || MODE_WIREFRAME"/> 
     39        <require shaders="fixed"/> 
     40        <define name="USE_PLAYERCOLOR" value="1"/> 
     41        <define name="USE_OBJECTCOLOR" value="0"/> 
     42        <pass shader="fixed:model_solid"/> 
     43    </technique> 
     44 
     45 
     46    <technique> 
     47        <require shaders="arb"/> 
     48        <pass shader="arb/model_common"/> 
     49    </technique> 
     50 
     51    <technique> 
     52        <require shaders="glsl"/> 
     53        <pass shader="glsl/model_common_norm"/> 
     54    </technique> 
     55 
     56    <technique> 
     57        <require context="USE_PLAYERCOLOR || USE_OBJECTCOLOR"/> 
     58        <require shaders="fixed"/> 
     59        <pass shader="fixed:model_color"/> 
     60    </technique> 
     61 
     62    <technique> 
     63        <require shaders="fixed"/> 
     64        <pass shader="fixed:model"/> 
     65    </technique> 
     66 
     67</effect> 
  • binaries/data/mods/public/shaders/glsl/terrain_common.fs

     
    4040        vec4 size = vec4(offset + 1.0, 2.0 - offset); 
    4141        vec4 weight = (vec4(2.0 - 1.0 / size.xy, 1.0 / size.zw - 1.0) + (v_shadow.xy - offset).xyxy) * shadowScale.zwzw; 
    4242        return (1.0/9.0)*dot(size.zxzx*size.wwyy, 
    43           vec4(shadow2D(shadowTex, vec3(weight.zw, v_shadow.z)).a, 
    44                shadow2D(shadowTex, vec3(weight.xw, v_shadow.z)).a, 
    45                shadow2D(shadowTex, vec3(weight.zy, v_shadow.z)).a, 
    46                shadow2D(shadowTex, vec3(weight.xy, v_shadow.z)).a)); 
     43          vec4(shadow2D(shadowTex, vec3(weight.zw, v_shadow.z)).r, 
     44               shadow2D(shadowTex, vec3(weight.xw, v_shadow.z)).r, 
     45               shadow2D(shadowTex, vec3(weight.zy, v_shadow.z)).r, 
     46               shadow2D(shadowTex, vec3(weight.xy, v_shadow.z)).r)); 
    4747      #else 
    48         return shadow2D(shadowTex, v_shadow.xyz).a; 
     48        return shadow2D(shadowTex, v_shadow.xyz).r; 
    4949      #endif 
    5050    #else 
    5151      if (v_shadow.z >= 1.0) 
  • binaries/data/mods/public/shaders/glsl/model_common_norm.vs

     
     1#if USE_GPU_SKINNING 
     2// Skinning requires GLSL 1.30 for ivec4 vertex attributes 
     3#version 120 
     4#else 
     5#version 140 
     6#endif 
     7 
     8uniform mat4 transform; 
     9uniform vec3 cameraPos; 
     10uniform vec3 sunDir; 
     11uniform vec3 sunColor; 
     12uniform vec2 losTransform; 
     13uniform mat4 shadowTransform; 
     14uniform mat4 instancingTransform; 
     15uniform mat4 normalMatrix; 
     16uniform mat4 orientation; 
     17 
     18//varying vec3 v_lighting; 
     19varying vec2 v_tex; 
     20varying vec4 v_shadow; 
     21varying vec2 v_los; 
     22 
     23//#if USE_SPECULAR 
     24  varying vec3 v_half; 
     25//#endif 
     26 
     27attribute vec3 a_vertex; 
     28attribute vec3 a_normal; 
     29attribute vec2 a_uv0; 
     30 
     31attribute vec4 a_tangent; 
     32 
     33 
     34 
     35#if USE_GPU_SKINNING 
     36  const int MAX_INFLUENCES = 4; 
     37  const int MAX_BONES = 64; 
     38  uniform mat4 skinBlendMatrices[MAX_BONES]; 
     39  attribute ivec4 a_skinJoints; 
     40  attribute vec4 a_skinWeights; 
     41#endif 
     42 
     43varying vec4 debugx; 
     44 
     45 
     46 
     47 
     48varying vec3 lightVec; 
     49varying vec3 eyeVec; 
     50 
     51varying vec3 v_normal; 
     52varying vec3 v_tangent; 
     53varying vec3 v_bitangent; 
     54 
     55 
     56varying float sign; 
     57 
     58 
     59void main() 
     60{ 
     61  #if USE_GPU_SKINNING 
     62    vec3 p = vec3(0.0); 
     63    vec3 n = vec3(0.0); 
     64    for (int i = 0; i < MAX_INFLUENCES; ++i) { 
     65      int joint = a_skinJoints[i]; 
     66      if (joint != 0xff) { 
     67        mat4 m = skinBlendMatrices[joint]; 
     68        p += vec3(m * vec4(a_vertex, 1.0)) * a_skinWeights[i]; 
     69        n += vec3(m * vec4(a_normal, 0.0)) * a_skinWeights[i]; 
     70      } 
     71    } 
     72    vec4 position = instancingTransform * vec4(p, 1.0); 
     73    vec3 normal = mat3(instancingTransform) * normalize(n); 
     74  #else 
     75  #if USE_INSTANCING 
     76    vec4 position = instancingTransform * vec4(a_vertex, 1.0); 
     77    vec3 normal = mat3(instancingTransform) * a_normal; 
     78    vec4 tangent = vec4(mat3(instancingTransform) * a_tangent.xyz, a_tangent.w); 
     79  #else 
     80    vec4 position = vec4(a_vertex, 1.0); 
     81    vec3 normal = a_normal; 
     82    vec4 tangent = a_tangent;     
     83  #endif 
     84  #endif 
     85 
     86  gl_Position = transform * position; 
     87 
     88 
     89 
     90 
     91    eyeVec = cameraPos.xyz - position.xyz; 
     92    lightVec = -sunDir; 
     93 
     94 
     95     
     96  
     97    vec3 sunVec = -sunDir; 
     98     
     99    v_half = normalize(sunVec + eyeVec); 
     100 
     101     
     102    v_normal = normal; 
     103    v_tangent = tangent.xyz; 
     104    sign = tangent.w; 
     105    v_bitangent = cross(v_normal, v_tangent); 
     106     
     107 
     108  //v_lighting = max(0.0, dot(normal, -sunDir)) * sunColor; 
     109  v_tex = a_uv0; 
     110  v_shadow = shadowTransform * position; 
     111  v_los = position.xz * losTransform.x + losTransform.y; 
     112} 
  • binaries/data/mods/public/shaders/glsl/model_common_norm.xml

     
     1<?xml version="1.0" encoding="utf-8"?> 
     2<program type="glsl"> 
     3 
     4    <vertex file="glsl/model_common_norm.vs"> 
     5        <stream name="pos"/> 
     6        <stream name="normal"/> 
     7        <stream name="uv0"/>     
     8        <attrib name="a_vertex" semantics="gl_Vertex"/> 
     9        <attrib name="a_normal" semantics="gl_Normal"/> 
     10        <attrib name="a_uv0" semantics="gl_MultiTexCoord0"/>     
     11    <attrib name="a_tangent" semantics="CustomAttribute2"/> 
     12    </vertex> 
     13 
     14    <fragment file="glsl/model_common_norm.fs"/> 
     15 
     16</program> 
  • binaries/data/mods/public/shaders/glsl/model_common.fs

     
    4747        vec4 size = vec4(offset + 1.0, 2.0 - offset); 
    4848        vec4 weight = (vec4(2.0 - 1.0 / size.xy, 1.0 / size.zw - 1.0) + (v_shadow.xy - offset).xyxy) * shadowScale.zwzw; 
    4949        return (1.0/9.0)*dot(size.zxzx*size.wwyy, 
    50           vec4(shadow2D(shadowTex, vec3(weight.zw, v_shadow.z)).a, 
    51                shadow2D(shadowTex, vec3(weight.xw, v_shadow.z)).a, 
    52                shadow2D(shadowTex, vec3(weight.zy, v_shadow.z)).a, 
    53                shadow2D(shadowTex, vec3(weight.xy, v_shadow.z)).a)); 
     50          vec4(shadow2D(shadowTex, vec3(weight.zw, v_shadow.z)).r, 
     51               shadow2D(shadowTex, vec3(weight.xw, v_shadow.z)).r, 
     52               shadow2D(shadowTex, vec3(weight.zy, v_shadow.z)).r, 
     53               shadow2D(shadowTex, vec3(weight.xy, v_shadow.z)).r)); 
    5454      #else 
    55         return shadow2D(shadowTex, v_shadow.xyz).a; 
     55        return shadow2D(shadowTex, v_shadow.xyz).r; 
    5656      #endif 
    5757    #else 
    5858      if (v_shadow.z >= 1.0) 
  • binaries/data/mods/public/shaders/glsl/model_common_norm.fs

     
     1#version 120 
     2 
     3uniform sampler2D baseTex; 
     4uniform sampler2D normTex; 
     5uniform sampler2D losTex; 
     6uniform sampler2D specTex; 
     7 
     8#if USE_SHADOW 
     9  #if USE_SHADOW_SAMPLER 
     10    uniform sampler2DShadow shadowTex; 
     11  #else 
     12    uniform sampler2D shadowTex; 
     13  #endif 
     14#endif 
     15 
     16#if USE_OBJECTCOLOR 
     17  uniform vec3 objectColor; 
     18#else 
     19#if USE_PLAYERCOLOR 
     20  uniform vec3 playerColor; 
     21#endif 
     22#endif 
     23 
     24uniform vec3 shadingColor; 
     25uniform vec3 ambient; 
     26uniform vec4 shadowOffsets1; 
     27uniform vec4 shadowOffsets2; 
     28 
     29//uniform mat4 normalMatrix; 
     30 
     31//varying vec3 v_lighting; 
     32varying vec2 v_tex; 
     33varying vec4 v_shadow; 
     34varying vec2 v_los; 
     35 
     36uniform vec3 sunDir; 
     37uniform vec3 sunColor; 
     38 
     39#if USE_SPECULAR 
     40  uniform float specularPower; 
     41  uniform vec3 specularColor; 
     42  varying vec3 v_normal; 
     43#endif 
     44 
     45varying vec3 v_half; 
     46 
     47varying vec3 lightVec;   
     48varying vec3 eyeVec; 
     49varying vec3 v_normal; 
     50varying vec3 v_tangent; 
     51varying vec3 v_bitangent; 
     52 
     53 
     54varying float sign; 
     55 
     56 
     57float get_shadow() 
     58{ 
     59  #if USE_SHADOW && !DISABLE_RECEIVE_SHADOWS 
     60    #if USE_SHADOW_SAMPLER 
     61      #if USE_SHADOW_PCF 
     62        return 0.25 * ( 
     63          shadow2D(shadowTex, vec3(v_shadow.xy + shadowOffsets1.xy, v_shadow.z)).r + 
     64          shadow2D(shadowTex, vec3(v_shadow.xy + shadowOffsets1.zw, v_shadow.z)).r + 
     65          shadow2D(shadowTex, vec3(v_shadow.xy + shadowOffsets2.xy, v_shadow.z)).r + 
     66          shadow2D(shadowTex, vec3(v_shadow.xy + shadowOffsets2.zw, v_shadow.z)).r 
     67        ); 
     68      #else 
     69        return shadow2D(shadowTex, v_shadow.xyz).r; 
     70      #endif 
     71    #else 
     72      if (v_shadow.z >= 1.0) 
     73        return 1.0; 
     74      #if USE_SHADOW_PCF 
     75        return ( 
     76          (v_shadow.z <= texture2D(shadowTex, v_shadow.xy + shadowOffsets1.xy).x ? 0.25 : 0.0) + 
     77          (v_shadow.z <= texture2D(shadowTex, v_shadow.xy + shadowOffsets1.zw).x ? 0.25 : 0.0) + 
     78          (v_shadow.z <= texture2D(shadowTex, v_shadow.xy + shadowOffsets2.xy).x ? 0.25 : 0.0) + 
     79          (v_shadow.z <= texture2D(shadowTex, v_shadow.xy + shadowOffsets2.zw).x ? 0.25 : 0.0) 
     80        ); 
     81      #else 
     82        return (v_shadow.z <= texture2D(shadowTex, v_shadow.xy).x ? 1.0 : 0.0); 
     83      #endif 
     84    #endif 
     85  #else 
     86    return 1.0; 
     87  #endif 
     88} 
     89 
     90 
     91#define USE_PARALLAX      1 
     92#define USE_NORMAL_MAP    1 
     93#define USE_SPECULAR_MAP  1 
     94#define USE_SELF_LIGHT    1 
     95 
     96void main() 
     97{ 
     98 
     99 
     100    vec3 color; 
     101    mat3 tbn; 
     102    vec2 coord = v_tex; 
     103 
     104 
     105#if USE_PARALLAX 
     106    float h = texture2D(normTex, coord).a; 
     107 
     108    tbn = transpose(mat3(v_tangent, v_bitangent * sign, v_normal)); 
     109 
     110    vec3 eyeDir = normalize( tbn * eyeVec ); 
     111    float dist = length(eyeVec); 
     112 
     113    if (dist < 65 && h < 0.99) 
     114    { 
     115 
     116        float scale = 0.015; 
     117        float height = 0.5; 
     118        float iter; 
     119         
     120        float s; 
     121        vec2 move; 
     122 
     123        iter = (40 - (dist - 25)) / 2 + 5; 
     124 
     125        if (dist < 25) iter = 25; 
     126         
     127 
     128        vec3 tangEye = -eyeDir; 
     129 
     130        iter = mix(iter * 2, iter, tangEye.z); 
     131         
     132        s = 1.0 / iter; 
     133        move = vec2(-eyeDir.x, eyeDir.y) * scale / (eyeDir.z * iter); 
     134 
     135        while (h < height)  
     136        { 
     137            height -= s; 
     138            coord += move; 
     139            h = texture2D(normTex, coord).a; 
     140        } 
     141 
     142    } 
     143#endif 
     144     
     145 
     146 
     147    tbn = transpose(mat3(v_tangent, v_bitangent * -sign, v_normal)); 
     148    vec3 ntex = texture2D(normTex, coord).rgb * 2.0 - 1.0; 
     149    vec3 vNout = normalize(ntex * tbn); 
     150 
     151 
     152 
     153#if USE_NORMAL_MAP 
     154    float bump = max (dot (-sunDir, vNout), 0.0) ; 
     155#else 
     156    float bump = 1.0; 
     157#endif 
     158 
     159 
     160    vec3 specular; 
     161#if USE_SPECULAR_MAP 
     162    vec4 specColour = texture2D(specTex, coord); 
     163    specular = bump * specColour.xyz * sunColor * pow(max(0.0, dot(normalize(vNout), v_half)), 12); 
     164#else 
     165    // if enabled, do normal specular 
     166    specular = vec3(0.0); 
     167#endif 
     168     
     169 
     170    vec4 diffuse = texture2D(baseTex, coord); 
     171 
     172#if USE_TRANSPARENT 
     173    gl_FragColor.a = diffuse.a; 
     174#else 
     175    gl_FragColor.a = 1.0; 
     176#endif 
     177 
     178    vec3 texdiffuse = diffuse.rgb; 
     179 
     180    // Apply-coloring based on texture alpha 
     181#if USE_OBJECTCOLOR 
     182    texdiffuse *= mix(objectColor, vec3(1.0, 1.0, 1.0), diffuse.a); 
     183#else 
     184#if USE_PLAYERCOLOR 
     185    texdiffuse *= mix(playerColor, vec3(1.0, 1.0, 1.0), diffuse.a); 
     186#endif 
     187#endif 
     188 
     189 
     190 
     191 
     192    color = texdiffuse * bump * sunColor * get_shadow() + texdiffuse * ambient + specular; 
     193 
     194#if USE_SELF_LIGHT 
     195    color = mix(texdiffuse, color, specColour.a); 
     196#endif 
     197 
     198     
     199 
     200 
     201#if !IGNORE_LOS 
     202    float los = texture2D(losTex, v_los).a; 
     203    color *= los; 
     204#endif 
     205 
     206   
     207    gl_FragColor.rgb = color; 
     208} 
  • binaries/data/config/default.cfg

     
    5858renderpath = default 
    5959 
    6060; Prefer GLSL shaders over ARB shaders (not recommended) 
    61 preferglsl = false 
     61preferglsl = true 
    6262 
    6363; Replace alpha-blending with alpha-testing, for performance experiments 
    6464forcealphatest = false