Merge branch 'mikktspace'

This commit is contained in:
Robert Beckebans 2020-12-21 17:31:11 +01:00
commit 7f168d6415
15 changed files with 2297 additions and 30 deletions

View file

@ -91,6 +91,7 @@ I started this project in 2012 and focused on making this code being future proo
* Merged LordHavoc's image compression progress bar which shows up in the map loading screen
when loading and compressing new images from mods
* Added support for Mikkelsen tangent space standard for new assets (thanks to Stephen Pridham)
If you want to start mod from a directory, you should first specify your mod directory adding the following command to the launcher:

View file

@ -95,6 +95,8 @@ The main goal is that the new content looks the same in RBDOOM-3-BFG as in Blend
* Bumped the static vertex cache limit of 31 MB to roughly ~ 128 MB to help with some custom models and maps by the Doom 3 community
* Added support for Mikkelsen tangent space standard for new assets (thanks to Stephen Pridham)
_______________________________________

View file

@ -478,6 +478,9 @@ file(GLOB IRRXML_SOURCES libs/irrxml/src/*.cpp)
file(GLOB IMGUI_INCLUDES libs/imgui/*.h)
file(GLOB IMGUI_SOURCES libs/imgui/*.cpp)
file(GLOB MIKKTSPACE_INCLUDES libs/mikktspace/*.h)
file(GLOB MIKKTSPACE_SOURCES libs/mikktspace/*.c)
if (NOT JPEG_FOUND)
set(JPEG_INCLUDES
libs/jpeg-6/jchuff.h
@ -1088,6 +1091,9 @@ source_group("libs\\irrxml" FILES ${IRRXML_SOURCES})
source_group("libs\\imgui" FILES ${IMGUI_INCLUDES})
source_group("libs\\imgui" FILES ${IMGUI_SOURCES})
source_group("libs\\mikktspace" FILES ${MIKKTSPACE_INCLUDES})
source_group("libs\\mikktspace" FILES ${MIKKTSPACE_SOURCES})
source_group("libs\\jpeg-6" FILES ${JPEG_INCLUDES})
source_group("libs\\jpeg-6" FILES ${JPEG_SOURCES})
@ -1263,6 +1269,7 @@ set(RBDOOM3_INCLUDES
${IRRXML_INCLUDES}
${FRAMEWORK_IMGUI_INCLUDES}
${IMGUI_INCLUDES}
${MIKKTSPACE_INCLUDES}
${JPEG_INCLUDES}
${PNG_INCLUDES}
${ZLIB_INCLUDES}
@ -1303,6 +1310,7 @@ set(RBDOOM3_SOURCES
${IRRXML_SOURCES}
${FRAMEWORK_IMGUI_SOURCES}
${IMGUI_SOURCES}
${MIKKTSPACE_SOURCES}
${JPEG_SOURCES}
${PNG_SOURCES}
${ZLIB_SOURCES}
@ -1503,7 +1511,7 @@ if(MSVC)
if(USE_PRECOMPILED_HEADERS)
set(RBDOOM3_PRECOMPILED_SOURCES ${RBDOOM3_SOURCES})
list(REMOVE_ITEM RBDOOM3_PRECOMPILED_SOURCES ${TIMIDITY_SOURCES} ${JPEG_SOURCES} ${PNG_SOURCES} ${ZLIB_SOURCES} ${GLEW_SOURCES} ${BINKDEC_SOURCES} ${IMGUI_SOURCES})
list(REMOVE_ITEM RBDOOM3_PRECOMPILED_SOURCES ${TIMIDITY_SOURCES} ${JPEG_SOURCES} ${PNG_SOURCES} ${ZLIB_SOURCES} ${GLEW_SOURCES} ${BINKDEC_SOURCES} ${IMGUI_SOURCES} ${MIKKTSPACE_SOURCES})
list(REMOVE_ITEM RBDOOM3_PRECOMPILED_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/libs/zlib/minizip/ioapi.c)
list(REMOVE_ITEM RBDOOM3_PRECOMPILED_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/renderer/DXT/DXTDecoder.cpp)
list(REMOVE_ITEM RBDOOM3_PRECOMPILED_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/renderer/DXT/DXTEncoder.cpp)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,151 @@
/** \file mikktspace/mikktspace.h
* \ingroup mikktspace
*/
/**
* Copyright (C) 2011 by Morten S. Mikkelsen
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef __MIKKTSPACE_H__
#define __MIKKTSPACE_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Author: Morten S. Mikkelsen
* Version: 1.0
*
* The files mikktspace.h and mikktspace.c are designed to be
* stand-alone files and it is important that they are kept this way.
* Not having dependencies on structures/classes/libraries specific
* to the program, in which they are used, allows them to be copied
* and used as is into any tool, program or plugin.
* The code is designed to consistently generate the same
* tangent spaces, for a given mesh, in any tool in which it is used.
* This is done by performing an internal welding step and subsequently an order-independent evaluation
* of tangent space for meshes consisting of triangles and quads.
* This means faces can be received in any order and the same is true for
* the order of vertices of each face. The generated result will not be affected
* by such reordering. Additionally, whether degenerate (vertices or texture coordinates)
* primitives are present or not will not affect the generated results either.
* Once tangent space calculation is done the vertices of degenerate primitives will simply
* inherit tangent space from neighboring non degenerate primitives.
* The analysis behind this implementation can be found in my master's thesis
* which is available for download --> http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf
* Note that though the tangent spaces at the vertices are generated in an order-independent way,
* by this implementation, the interpolated tangent space is still affected by which diagonal is
* chosen to split each quad. A sensible solution is to have your tools pipeline always
* split quads by the shortest diagonal. This choice is order-independent and works with mirroring.
* If these have the same length then compare the diagonals defined by the texture coordinates.
* XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin
* and also quad triangulator plugin.
*/
typedef int tbool;
typedef struct SMikkTSpaceContext SMikkTSpaceContext;
typedef struct {
// SP begin
void* (*m_alloc)(int bytes);
void (*m_free)(void* mem);
// SP end
// Returns the number of faces (triangles/quads) on the mesh to be processed.
int (*m_getNumFaces)(const SMikkTSpaceContext * pContext);
// Returns the number of vertices on face number iFace
// iFace is a number in the range {0, 1, ..., getNumFaces()-1}
int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace);
// returns the position/normal/texcoord of the referenced face of vertex number iVert.
// iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads.
void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert);
void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert);
void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert);
// either (or both) of the two setTSpace callbacks can be set.
// The call-back m_setTSpaceBasic() is sufficient for basic normal mapping.
// This function is used to return the tangent and fSign to the application.
// fvTangent is a unit length vector.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert);
// This function is used to return tangent space results to the application.
// fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their
// true magnitudes which can be used for relief mapping effects.
// fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent.
// However, both are perpendicular to the vertex normal.
// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level.
// fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
// bitangent = fSign * cross(vN, tangent);
// Note that the results are returned unindexed. It is possible to generate a new index list
// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results.
// DO NOT! use an already existing index list.
void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT,
const tbool bIsOrientationPreserving, const int iFace, const int iVert);
} SMikkTSpaceInterface;
struct SMikkTSpaceContext
{
SMikkTSpaceInterface * m_pInterface; // initialized with callback functions
void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call)
};
// these are both thread safe!
tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled)
tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold);
// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the
// normal map sampler must use the exact inverse of the pixel shader transformation.
// The most efficient transformation we can possibly do in the pixel shader is
// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN.
// pixel shader (fast transform out)
// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN );
// where vNt is the tangent space normal. The normal map sampler must likewise use the
// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader.
// sampler does (exact inverse of pixel shader):
// float3 row0 = cross(vB, vN);
// float3 row1 = cross(vN, vT);
// float3 row2 = cross(vT, vB);
// float fSign = dot(vT, row0)<0 ? -1 : 1;
// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) );
// where vNout is the sampled normal in some chosen 3D space.
//
// Should you choose to reconstruct the bitangent in the pixel shader instead
// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also.
// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of
// quads as your renderer then problems will occur since the interpolated tangent spaces will differ
// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before
// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier.
// However, this must be used both by the sampler and your tools/rendering pipeline.
#ifdef __cplusplus
}
#endif
#endif

View file

@ -112,6 +112,7 @@ void idMaterial::CommonInit()
hasSubview = false;
allowOverlays = true;
unsmoothedTangents = false;
mikktspace = false; // RB
gui = NULL;
memset( deformRegisters, 0, sizeof( deformRegisters ) );
editorAlpha = 1.0;
@ -2418,6 +2419,12 @@ void idMaterial::ParseMaterial( idLexer& src )
unsmoothedTangents = true;
continue;
}
// RB: mikktspace
else if( !token.Icmp( "mikktspace" ) )
{
mikktspace = true;
continue;
}
// lightFallofImage <imageprogram>
// specifies the image to use for the third axis of projected
// light volumes

View file

@ -3,7 +3,7 @@
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2014-2016 Robert Beckebans
Copyright (C) 2014-2020 Robert Beckebans
Copyright (C) 2014-2016 Kot in Action Creative Artel
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -498,6 +498,14 @@ public:
return unsmoothedTangents;
}
// RB: characters and models that baked in Blender or Substance designer use the newer
// Mikkelsen tangent space standard.
// see: https://bgolus.medium.com/generating-perfect-normal-maps-for-unity-f929e673fc57
bool UseMikkTSpace() const
{
return mikktspace;
}
// by default, monsters can have blood overlays placed on them, but this can
// be overrided on a per-material basis with the "noOverlays" material command.
// This will always return false for translucent surfaces
@ -891,6 +899,7 @@ private:
bool blendLight;
bool ambientLight;
bool unsmoothedTangents;
bool mikktspace; // RB: use Mikkelsen tangent space standard for normal mapping
bool hasSubview; // mirror, remote render, etc
bool allowOverlays;

View file

@ -3,7 +3,7 @@
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2012-2016 Robert Beckebans
Copyright (C) 2012-2020 Robert Beckebans
Copyright (C) 2014-2016 Kot in Action Creative Artel
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -283,7 +283,7 @@ void idRenderModelStatic::MakeDefaultModel()
tri->generateNormals = true;
AddSurface( surf );
FinishSurfaces();
FinishSurfaces( false );
}
/*
@ -311,28 +311,30 @@ void idRenderModelStatic::InitFromFile( const char* fileName )
// FIXME: load new .proc map format
ID_TIME_T sourceTimeStamp;
name.ExtractFileExtension( extension );
if( extension.Icmp( "ase" ) == 0 )
{
loaded = LoadASE( name );
loaded = LoadASE( name, &sourceTimeStamp );
reloadable = true;
}
// RB: added dae
else if( extension.Icmp( "dae" ) == 0 )
{
loaded = LoadDAE( name );
loaded = LoadDAE( name, &sourceTimeStamp );
reloadable = true;
}
// RB end
else if( extension.Icmp( "lwo" ) == 0 )
{
loaded = LoadLWO( name );
loaded = LoadLWO( name, &sourceTimeStamp );
reloadable = true;
}
else if( extension.Icmp( "ma" ) == 0 )
{
loaded = LoadMA( name );
loaded = LoadMA( name, &sourceTimeStamp );
reloadable = true;
}
else
@ -351,8 +353,19 @@ void idRenderModelStatic::InitFromFile( const char* fileName )
// it is now available for use
purged = false;
// RB: this is 1.1.2016 10:00 AM
// a useful tool for this is https://www.unixtime.de/
const ID_TIME_T min2016 = 1451638800;
bool useMikktspace = false;
if( sourceTimeStamp > min2016 )
{
// HACK: assume this is a newer asset not by id Software and its normalmaps are baked using the mikktspace standard
useMikktspace = true;
}
// create the bounds for culling and dynamic surface creation
FinishSurfaces();
FinishSurfaces( useMikktspace );
}
/*
@ -1126,7 +1139,7 @@ Extends the bounds of deformed surfaces so they don't cull incorrectly at screen
================
*/
void idRenderModelStatic::FinishSurfaces()
void idRenderModelStatic::FinishSurfaces( bool useMikktspace )
{
int i;
int totalVerts, totalIndexes;
@ -1215,7 +1228,9 @@ void idRenderModelStatic::FinishSurfaces()
{
const modelSurface_t* surf = &surfaces[i];
R_CleanupTriangles( surf->geometry, surf->geometry->generateNormals, true, surf->shader->UseUnsmoothedTangents() );
bool mikktspace = useMikktspace || surf->shader->UseMikkTSpace();
R_CleanupTriangles( surf->geometry, surf->geometry->generateNormals, true, surf->shader->UseUnsmoothedTangents(), mikktspace );
if( surf->shader->SurfaceCastsShadow() )
{
totalVerts += surf->geometry->numVerts;
@ -3176,7 +3191,7 @@ bool idRenderModelStatic::ConvertMAToModelSurfaces( const struct maModel_s* ma )
idRenderModelStatic::LoadASE
=================
*/
bool idRenderModelStatic::LoadASE( const char* fileName )
bool idRenderModelStatic::LoadASE( const char* fileName, ID_TIME_T* sourceTimeStamp )
{
aseModel_t* ase;
@ -3186,6 +3201,9 @@ bool idRenderModelStatic::LoadASE( const char* fileName )
return false;
}
// RB
*sourceTimeStamp = ase->timeStamp;
ConvertASEToModelSurfaces( ase );
ASE_Free( ase );
@ -3198,7 +3216,7 @@ bool idRenderModelStatic::LoadASE( const char* fileName )
idRenderModelStatic::LoadLWO
=================
*/
bool idRenderModelStatic::LoadLWO( const char* fileName )
bool idRenderModelStatic::LoadLWO( const char* fileName, ID_TIME_T* sourceTimeStamp )
{
unsigned int failID;
int failPos;
@ -3210,6 +3228,9 @@ bool idRenderModelStatic::LoadLWO( const char* fileName )
return false;
}
// RB
*sourceTimeStamp = fileSystem->GetTimestamp( fileName );
ConvertLWOToModelSurfaces( lwo );
lwFreeObject( lwo );
@ -3222,7 +3243,7 @@ bool idRenderModelStatic::LoadLWO( const char* fileName )
idRenderModelStatic::LoadMA
=================
*/
bool idRenderModelStatic::LoadMA( const char* fileName )
bool idRenderModelStatic::LoadMA( const char* fileName, ID_TIME_T* sourceTimeStamp )
{
maModel_t* ma;
@ -3232,6 +3253,9 @@ bool idRenderModelStatic::LoadMA( const char* fileName )
return false;
}
// RB
*sourceTimeStamp = ma->timeStamp;
ConvertMAToModelSurfaces( ma );
MA_Free( ma );
@ -3240,7 +3264,7 @@ bool idRenderModelStatic::LoadMA( const char* fileName )
}
// RB: added COLLADA support
bool idRenderModelStatic::LoadDAE( const char* fileName )
bool idRenderModelStatic::LoadDAE( const char* fileName, ID_TIME_T* sourceTimeStamp )
{
bool loaded = false;
@ -3251,7 +3275,7 @@ bool idRenderModelStatic::LoadDAE( const char* fileName )
idTimer timer;
timer.Start();
ColladaParser parser( fileName );
ColladaParser parser( fileName, sourceTimeStamp );
loaded = ConvertDAEToModelSurfaces( &parser );
@ -3368,7 +3392,9 @@ void idRenderModelStatic::ReadFromDemoFile( class idDemoFile* f )
this->AddSurface( surf );
}
this->FinishSurfaces();
// RB: don't use mikktspace here because it is slower
this->FinishSurfaces( false );
}
/*

View file

@ -194,7 +194,7 @@ public:
// Creates the duplicated back side geometry for two sided, alpha tested, lit materials
// This does not need to be called if none of the surfaces added with AddSurface require
// light interaction, and all the triangles are already well formed.
virtual void FinishSurfaces() = 0;
virtual void FinishSurfaces( bool useMikktspace ) = 0;
// frees all the data, but leaves the class around for dangling references,
// which can regenerate the data with LoadModel()

View file

@ -57,7 +57,7 @@ using namespace Collada;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
ColladaParser::ColladaParser( const char* pFile )
ColladaParser::ColladaParser( const char* pFile, ID_TIME_T* sourceTimeStamp )
: mFileName( pFile )
{
mRootNode = NULL;
@ -74,6 +74,8 @@ ColladaParser::ColladaParser( const char* pFile )
ThrowException( "Collada: Unable to open file." );
}
*sourceTimeStamp = mReader->getTimestamp();
// start reading
ReadContents();
}

View file

@ -64,7 +64,7 @@ class ColladaParser
protected:
/** Constructor from XML file */
ColladaParser( const char* pFile );
ColladaParser( const char* pFile, ID_TIME_T* sourceTimeStamp );
/** Destructor */
~ColladaParser();

View file

@ -72,7 +72,7 @@ public:
virtual void TouchData();
virtual void InitEmpty( const char* name );
virtual void AddSurface( modelSurface_t surface );
virtual void FinishSurfaces();
virtual void FinishSurfaces( bool useMikktspace );
virtual void FreeVertexCache();
virtual const char* Name() const;
virtual void Print() const;
@ -115,10 +115,10 @@ public:
void MakeDefaultModel();
bool LoadASE( const char* fileName );
bool LoadDAE( const char* fileName ); // RB
bool LoadLWO( const char* fileName );
bool LoadMA( const char* filename );
bool LoadASE( const char* fileName, ID_TIME_T* sourceTimeStamp );
bool LoadDAE( const char* fileName, ID_TIME_T* sourceTimeStamp ); // RB
bool LoadLWO( const char* fileName, ID_TIME_T* sourceTimeStamp );
bool LoadMA( const char* filename, ID_TIME_T* sourceTimeStamp );
bool ConvertDAEToModelSurfaces( const ColladaParser* dae ); // RB
bool ConvertASEToModelSurfaces( const struct aseModel_s* ase );

View file

@ -1454,7 +1454,7 @@ void R_RemoveDegenerateTriangles( srfTriangles_t* tri );
void R_RemoveUnusedVerts( srfTriangles_t* tri );
void R_RangeCheckIndexes( const srfTriangles_t* tri );
void R_CreateVertexNormals( srfTriangles_t* tri ); // also called by dmap
void R_CleanupTriangles( srfTriangles_t* tri, bool createNormals, bool identifySilEdges, bool useUnsmoothedTangents );
void R_CleanupTriangles( srfTriangles_t* tri, bool createNormals, bool identifySilEdges, bool useUnsmoothedTangents, bool useMikktspace );
void R_ReverseTriangles( srfTriangles_t* tri );
// Only deals with vertexes and indexes, not silhouettes, planes, etc.

View file

@ -297,7 +297,8 @@ idRenderModel* idRenderWorldLocal::ParseModel( idLexer* src, const char* mapName
src->ExpectTokenString( "}" );
model->FinishSurfaces();
// RB: FIXME add check for mikktspace
model->FinishSurfaces( false );
if( fileOut != NULL && model->SupportsBinaryModel() && binaryLoadRenderModels.GetBool() )
{

View file

@ -3,6 +3,7 @@
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2020 Stephen Pridham (Mikkelsen tangent space support)
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
@ -31,6 +32,8 @@ If you have questions concerning this license or the applicable additional terms
#include "RenderCommon.h"
#include "libs/mikktspace/mikktspace.h"
/*
==============================================================================
@ -114,6 +117,38 @@ is highly uneven.
// instead of using the texture T vector, cross the normal and S vector for an orthogonal axis
#define DERIVE_UNSMOOTHED_BITANGENT
// SP Begin
// Mikktspace is a standard that should be used for new assets. If you'd like to use the original
// method of calculating tangent spaces for the original game's normal maps, disable mikktspace before
// loading in the model.
// see http://www.mikktspace.com/
//idCVar r_useMikktspace( "r_useMikktspace", "1", CVAR_RENDERER | CVAR_BOOL, "Use the mikktspace standard to derive tangents" );
static void* mkAlloc( int bytes );
static void mkFree( void* mem );
static int mkGetNumFaces( const SMikkTSpaceContext* pContext );
static int mkGetNumVerticesOfFace( const SMikkTSpaceContext* pContext, const int iFace );
static void mkGetPosition( const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert );
static void mkGetNormal( const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert );
static void mkGetTexCoord( const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert );
static void mkSetTSpaceBasic( const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert );
// Helper class for loading in the interface functions for mikktspace.
class idMikkTSpaceInterface
{
public:
idMikkTSpaceInterface();
SMikkTSpaceInterface mkInterface;
};
static idMikkTSpaceInterface mikkTSpaceInterface;
static void SetUpMikkTSpaceContext( SMikkTSpaceContext* context );
// SP end
/*
=================
R_TriSurfMemory
@ -1007,6 +1042,23 @@ static void R_DuplicateMirroredVertexes( srfTriangles_t* tri )
}
}
/*
============
R_DeriveMikktspaceTangents
Derives the tangent space for the given triangles using the Mikktspace standard.
Normals must be calculated beforehand.
============
*/
static bool R_DeriveMikktspaceTangents( srfTriangles_t* tri )
{
SMikkTSpaceContext context;
SetUpMikkTSpaceContext( &context );
context.m_pUserData = tri;
return ( genTangSpaceDefault( &context ) != 0 );
}
/*
============
R_DeriveNormalsAndTangents
@ -1306,8 +1358,23 @@ to save space or speed transforms?
this version only handles bilateral symetry
=================
*/
void R_DeriveTangentsWithoutNormals( srfTriangles_t* tri )
void R_DeriveTangentsWithoutNormals( srfTriangles_t* tri, bool useMikktspace )
{
// SP begin
if( useMikktspace )
{
if( !R_DeriveMikktspaceTangents( tri ) )
{
idLib::Warning( "Mikkelsen tangent space calculation failed" );
}
else
{
tri->tangentsCalculated = true;
return;
}
}
// SP End
idTempArray< idVec3 > triangleTangents( tri->numIndexes / 3 );
idTempArray< idVec3 > triangleBitangents( tri->numIndexes / 3 );
@ -1915,7 +1982,7 @@ R_CleanupTriangles
FIXME: allow createFlat and createSmooth normals, as well as explicit
=================
*/
void R_CleanupTriangles( srfTriangles_t* tri, bool createNormals, bool identifySilEdges, bool useUnsmoothedTangents )
void R_CleanupTriangles( srfTriangles_t* tri, bool createNormals, bool identifySilEdges, bool useUnsmoothedTangents, bool useMikktspace )
{
R_RangeCheckIndexes( tri );
@ -1948,7 +2015,7 @@ void R_CleanupTriangles( srfTriangles_t* tri, bool createNormals, bool identifyS
}
else if( !createNormals )
{
R_DeriveTangentsWithoutNormals( tri );
R_DeriveTangentsWithoutNormals( tri, useMikktspace );
}
else
{
@ -2207,3 +2274,97 @@ void R_CreateStaticBuffersForTri( srfTriangles_t& tri )
#endif
}
}
// SP begin
static void* mkAlloc( int bytes )
{
return R_StaticAlloc( bytes );
}
static void mkFree( void* mem )
{
R_StaticFree( mem );
}
static int mkGetNumFaces( const SMikkTSpaceContext* pContext )
{
srfTriangles_t* tris = reinterpret_cast<srfTriangles_t*>( pContext->m_pUserData );
return tris->numIndexes / 3;
}
static int mkGetNumVerticesOfFace( const SMikkTSpaceContext* pContext, const int iFace )
{
return 3;
}
static void mkGetPosition( const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert )
{
srfTriangles_t* tris = reinterpret_cast<srfTriangles_t*>( pContext->m_pUserData );
const int vertIndex = iFace * 3;
const int index = tris->indexes[vertIndex + iVert];
const idDrawVert& vert = tris->verts[index];
fvPosOut[0] = vert.xyz[0];
fvPosOut[1] = vert.xyz[1];
fvPosOut[2] = vert.xyz[2];
}
static void mkGetNormal( const SMikkTSpaceContext* pContext, float fvNormOut[], const int iFace, const int iVert )
{
srfTriangles_t* tris = reinterpret_cast<srfTriangles_t*>( pContext->m_pUserData );
const int vertIndex = iFace * 3;
const int index = tris->indexes[vertIndex + iVert];
const idDrawVert& vert = tris->verts[index];
const idVec3 norm = vert.GetNormal();
fvNormOut[0] = norm.x;
fvNormOut[1] = norm.y;
fvNormOut[2] = norm.z;
}
static void mkGetTexCoord( const SMikkTSpaceContext* pContext, float fvTexcOut[], const int iFace, const int iVert )
{
srfTriangles_t* tris = reinterpret_cast<srfTriangles_t*>( pContext->m_pUserData );
const int vertIndex = iFace * 3;
const int index = tris->indexes[vertIndex + iVert];
const idDrawVert& vert = tris->verts[index];
const idVec2 texCoord = vert.GetTexCoord();
fvTexcOut[0] = texCoord.x;
fvTexcOut[1] = texCoord.y;
}
static void mkSetTSpaceBasic( const SMikkTSpaceContext* pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert )
{
srfTriangles_t* tris = reinterpret_cast<srfTriangles_t*>( pContext->m_pUserData );
const int vertIndex = iFace * 3;
const int index = tris->indexes[vertIndex + iVert];
const idVec3 tangent( fvTangent[0], fvTangent[1], fvTangent[2] );
tris->verts[index].SetTangent( tangent );
tris->verts[index].SetBiTangentSign( fSign );
}
idMikkTSpaceInterface::idMikkTSpaceInterface()
: mkInterface()
{
mkInterface.m_alloc = mkAlloc;
mkInterface.m_free = mkFree;
mkInterface.m_getNumFaces = mkGetNumFaces;
mkInterface.m_getNumVerticesOfFace = mkGetNumVerticesOfFace;
mkInterface.m_getPosition = mkGetPosition;
mkInterface.m_getNormal = mkGetNormal;
mkInterface.m_getTexCoord = mkGetTexCoord;
mkInterface.m_setTSpaceBasic = mkSetTSpaceBasic;
}
static void SetUpMikkTSpaceContext( SMikkTSpaceContext* context )
{
context->m_pInterface = &mikkTSpaceInterface.mkInterface;
}
// SP end