mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-27 14:42:23 +00:00
79ad905e05
Excluding 3rd party files.
2273 lines
61 KiB
C++
2273 lines
61 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "../idlib/precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "tr_local.h"
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
TRIANGLE MESH PROCESSING
|
|
|
|
The functions in this file have no vertex / index count limits.
|
|
|
|
Truly identical vertexes that match in position, normal, and texcoord can
|
|
be merged away.
|
|
|
|
Vertexes that match in position and texcoord, but have distinct normals will
|
|
remain distinct for all purposes. This is usually a poor choice for models,
|
|
as adding a bevel face will not add any more vertexes, and will tend to
|
|
look better.
|
|
|
|
Match in position and normal, but differ in texcoords are referenced together
|
|
for calculating tangent vectors for bump mapping.
|
|
Artists should take care to have identical texels in all maps (bump/diffuse/specular)
|
|
in this case
|
|
|
|
Vertexes that only match in position are merged for shadow edge finding.
|
|
|
|
Degenerate triangles.
|
|
|
|
Overlapped triangles, even if normals or texcoords differ, must be removed.
|
|
for the silhoette based stencil shadow algorithm to function properly.
|
|
Is this true???
|
|
Is the overlapped triangle problem just an example of the trippled edge problem?
|
|
|
|
Interpenetrating triangles are not currently clipped to surfaces.
|
|
Do they effect the shadows?
|
|
|
|
if vertexes are intended to deform apart, make sure that no vertexes
|
|
are on top of each other in the base frame, or the sil edges may be
|
|
calculated incorrectly.
|
|
|
|
We might be able to identify this from topology.
|
|
|
|
Dangling edges are acceptable, but three way edges are not.
|
|
|
|
Are any combinations of two way edges unacceptable, like one facing
|
|
the backside of the other?
|
|
|
|
|
|
Topology is determined by a collection of triangle indexes.
|
|
|
|
The edge list can be built up from this, and stays valid even under
|
|
deformations.
|
|
|
|
Somewhat non-intuitively, concave edges cannot be optimized away, or the
|
|
stencil shadow algorithm miscounts.
|
|
|
|
Face normals are needed for generating shadow volumes and for calculating
|
|
the silhouette, but they will change with any deformation.
|
|
|
|
Vertex normals and vertex tangents will change with each deformation,
|
|
but they may be able to be transformed instead of recalculated.
|
|
|
|
bounding volume, both box and sphere will change with deformation.
|
|
|
|
silhouette indexes
|
|
shade indexes
|
|
texture indexes
|
|
|
|
shade indexes will only be > silhouette indexes if there is facet shading present
|
|
|
|
lookups from texture to sil and texture to shade?
|
|
|
|
The normal and tangent vector smoothing is simple averaging, no attempt is
|
|
made to better handle the cases where the distribution around the shared vertex
|
|
is highly uneven.
|
|
|
|
|
|
we may get degenerate triangles even with the uniquing and removal
|
|
if the vertexes have different texcoords.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
// this shouldn't change anything, but previously renderbumped models seem to need it
|
|
#define USE_INVA
|
|
|
|
// instead of using the texture T vector, cross the normal and S vector for an orthogonal axis
|
|
#define DERIVE_UNSMOOTHED_BITANGENT
|
|
|
|
const int MAX_SIL_EDGES = 0x10000;
|
|
const int SILEDGE_HASH_SIZE = 1024;
|
|
|
|
static int numSilEdges;
|
|
static silEdge_t * silEdges;
|
|
static idHashIndex silEdgeHash( SILEDGE_HASH_SIZE, MAX_SIL_EDGES );
|
|
static int numPlanes;
|
|
|
|
static idBlockAlloc<srfTriangles_t, 1<<8> srfTrianglesAllocator;
|
|
|
|
#ifdef USE_TRI_DATA_ALLOCATOR
|
|
static idDynamicBlockAlloc<idDrawVert, 1<<20, 1<<10> triVertexAllocator;
|
|
static idDynamicBlockAlloc<glIndex_t, 1<<18, 1<<10> triIndexAllocator;
|
|
static idDynamicBlockAlloc<shadowCache_t, 1<<18, 1<<10> triShadowVertexAllocator;
|
|
static idDynamicBlockAlloc<idPlane, 1<<17, 1<<10> triPlaneAllocator;
|
|
static idDynamicBlockAlloc<glIndex_t, 1<<17, 1<<10> triSilIndexAllocator;
|
|
static idDynamicBlockAlloc<silEdge_t, 1<<17, 1<<10> triSilEdgeAllocator;
|
|
static idDynamicBlockAlloc<dominantTri_t, 1<<16, 1<<10> triDominantTrisAllocator;
|
|
static idDynamicBlockAlloc<int, 1<<16, 1<<10> triMirroredVertAllocator;
|
|
static idDynamicBlockAlloc<int, 1<<16, 1<<10> triDupVertAllocator;
|
|
#else
|
|
static idDynamicAlloc<idDrawVert, 1<<20, 1<<10> triVertexAllocator;
|
|
static idDynamicAlloc<glIndex_t, 1<<18, 1<<10> triIndexAllocator;
|
|
static idDynamicAlloc<shadowCache_t, 1<<18, 1<<10> triShadowVertexAllocator;
|
|
static idDynamicAlloc<idPlane, 1<<17, 1<<10> triPlaneAllocator;
|
|
static idDynamicAlloc<glIndex_t, 1<<17, 1<<10> triSilIndexAllocator;
|
|
static idDynamicAlloc<silEdge_t, 1<<17, 1<<10> triSilEdgeAllocator;
|
|
static idDynamicAlloc<dominantTri_t, 1<<16, 1<<10> triDominantTrisAllocator;
|
|
static idDynamicAlloc<int, 1<<16, 1<<10> triMirroredVertAllocator;
|
|
static idDynamicAlloc<int, 1<<16, 1<<10> triDupVertAllocator;
|
|
#endif
|
|
|
|
|
|
/*
|
|
===============
|
|
R_InitTriSurfData
|
|
===============
|
|
*/
|
|
void R_InitTriSurfData( void ) {
|
|
silEdges = (silEdge_t *)R_StaticAlloc( MAX_SIL_EDGES * sizeof( silEdges[0] ) );
|
|
|
|
// initialize allocators for triangle surfaces
|
|
triVertexAllocator.Init();
|
|
triIndexAllocator.Init();
|
|
triShadowVertexAllocator.Init();
|
|
triPlaneAllocator.Init();
|
|
triSilIndexAllocator.Init();
|
|
triSilEdgeAllocator.Init();
|
|
triDominantTrisAllocator.Init();
|
|
triMirroredVertAllocator.Init();
|
|
triDupVertAllocator.Init();
|
|
|
|
// never swap out triangle surfaces
|
|
triVertexAllocator.SetLockMemory( true );
|
|
triIndexAllocator.SetLockMemory( true );
|
|
triShadowVertexAllocator.SetLockMemory( true );
|
|
triPlaneAllocator.SetLockMemory( true );
|
|
triSilIndexAllocator.SetLockMemory( true );
|
|
triSilEdgeAllocator.SetLockMemory( true );
|
|
triDominantTrisAllocator.SetLockMemory( true );
|
|
triMirroredVertAllocator.SetLockMemory( true );
|
|
triDupVertAllocator.SetLockMemory( true );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_ShutdownTriSurfData
|
|
===============
|
|
*/
|
|
void R_ShutdownTriSurfData( void ) {
|
|
R_StaticFree( silEdges );
|
|
silEdgeHash.Free();
|
|
srfTrianglesAllocator.Shutdown();
|
|
triVertexAllocator.Shutdown();
|
|
triIndexAllocator.Shutdown();
|
|
triShadowVertexAllocator.Shutdown();
|
|
triPlaneAllocator.Shutdown();
|
|
triSilIndexAllocator.Shutdown();
|
|
triSilEdgeAllocator.Shutdown();
|
|
triDominantTrisAllocator.Shutdown();
|
|
triMirroredVertAllocator.Shutdown();
|
|
triDupVertAllocator.Shutdown();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_PurgeTriSurfData
|
|
===============
|
|
*/
|
|
void R_PurgeTriSurfData( frameData_t *frame ) {
|
|
// free deferred triangle surfaces
|
|
R_FreeDeferredTriSurfs( frame );
|
|
|
|
// free empty base blocks
|
|
triVertexAllocator.FreeEmptyBaseBlocks();
|
|
triIndexAllocator.FreeEmptyBaseBlocks();
|
|
triShadowVertexAllocator.FreeEmptyBaseBlocks();
|
|
triPlaneAllocator.FreeEmptyBaseBlocks();
|
|
triSilIndexAllocator.FreeEmptyBaseBlocks();
|
|
triSilEdgeAllocator.FreeEmptyBaseBlocks();
|
|
triDominantTrisAllocator.FreeEmptyBaseBlocks();
|
|
triMirroredVertAllocator.FreeEmptyBaseBlocks();
|
|
triDupVertAllocator.FreeEmptyBaseBlocks();
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_ShowTriMemory_f
|
|
===============
|
|
*/
|
|
void R_ShowTriSurfMemory_f( const idCmdArgs &args ) {
|
|
common->Printf( "%6d kB in %d triangle surfaces\n",
|
|
( srfTrianglesAllocator.GetAllocCount() * sizeof( srfTriangles_t ) ) >> 10,
|
|
srfTrianglesAllocator.GetAllocCount() );
|
|
|
|
common->Printf( "%6d kB vertex memory (%d kB free in %d blocks, %d empty base blocks)\n",
|
|
triVertexAllocator.GetBaseBlockMemory() >> 10, triVertexAllocator.GetFreeBlockMemory() >> 10,
|
|
triVertexAllocator.GetNumFreeBlocks(), triVertexAllocator.GetNumEmptyBaseBlocks() );
|
|
|
|
common->Printf( "%6d kB index memory (%d kB free in %d blocks, %d empty base blocks)\n",
|
|
triIndexAllocator.GetBaseBlockMemory() >> 10, triIndexAllocator.GetFreeBlockMemory() >> 10,
|
|
triIndexAllocator.GetNumFreeBlocks(), triIndexAllocator.GetNumEmptyBaseBlocks() );
|
|
|
|
common->Printf( "%6d kB shadow vert memory (%d kB free in %d blocks, %d empty base blocks)\n",
|
|
triShadowVertexAllocator.GetBaseBlockMemory() >> 10, triShadowVertexAllocator.GetFreeBlockMemory() >> 10,
|
|
triShadowVertexAllocator.GetNumFreeBlocks(), triShadowVertexAllocator.GetNumEmptyBaseBlocks() );
|
|
|
|
common->Printf( "%6d kB tri plane memory (%d kB free in %d blocks, %d empty base blocks)\n",
|
|
triPlaneAllocator.GetBaseBlockMemory() >> 10, triPlaneAllocator.GetFreeBlockMemory() >> 10,
|
|
triPlaneAllocator.GetNumFreeBlocks(), triPlaneAllocator.GetNumEmptyBaseBlocks() );
|
|
|
|
common->Printf( "%6d kB sil index memory (%d kB free in %d blocks, %d empty base blocks)\n",
|
|
triSilIndexAllocator.GetBaseBlockMemory() >> 10, triSilIndexAllocator.GetFreeBlockMemory() >> 10,
|
|
triSilIndexAllocator.GetNumFreeBlocks(), triSilIndexAllocator.GetNumEmptyBaseBlocks() );
|
|
|
|
common->Printf( "%6d kB sil edge memory (%d kB free in %d blocks, %d empty base blocks)\n",
|
|
triSilEdgeAllocator.GetBaseBlockMemory() >> 10, triSilEdgeAllocator.GetFreeBlockMemory() >> 10,
|
|
triSilEdgeAllocator.GetNumFreeBlocks(), triSilEdgeAllocator.GetNumEmptyBaseBlocks() );
|
|
|
|
common->Printf( "%6d kB dominant tri memory (%d kB free in %d blocks, %d empty base blocks)\n",
|
|
triDominantTrisAllocator.GetBaseBlockMemory() >> 10, triDominantTrisAllocator.GetFreeBlockMemory() >> 10,
|
|
triDominantTrisAllocator.GetNumFreeBlocks(), triDominantTrisAllocator.GetNumEmptyBaseBlocks() );
|
|
|
|
common->Printf( "%6d kB mirror vert memory (%d kB free in %d blocks, %d empty base blocks)\n",
|
|
triMirroredVertAllocator.GetBaseBlockMemory() >> 10, triMirroredVertAllocator.GetFreeBlockMemory() >> 10,
|
|
triMirroredVertAllocator.GetNumFreeBlocks(), triMirroredVertAllocator.GetNumEmptyBaseBlocks() );
|
|
|
|
common->Printf( "%6d kB dup vert memory (%d kB free in %d blocks, %d empty base blocks)\n",
|
|
triDupVertAllocator.GetBaseBlockMemory() >> 10, triDupVertAllocator.GetFreeBlockMemory() >> 10,
|
|
triDupVertAllocator.GetNumFreeBlocks(), triDupVertAllocator.GetNumEmptyBaseBlocks() );
|
|
|
|
common->Printf( "%6d kB total triangle memory\n",
|
|
( srfTrianglesAllocator.GetAllocCount() * sizeof( srfTriangles_t ) +
|
|
triVertexAllocator.GetBaseBlockMemory() +
|
|
triIndexAllocator.GetBaseBlockMemory() +
|
|
triShadowVertexAllocator.GetBaseBlockMemory() +
|
|
triPlaneAllocator.GetBaseBlockMemory() +
|
|
triSilIndexAllocator.GetBaseBlockMemory() +
|
|
triSilEdgeAllocator.GetBaseBlockMemory() +
|
|
triDominantTrisAllocator.GetBaseBlockMemory() +
|
|
triMirroredVertAllocator.GetBaseBlockMemory() +
|
|
triDupVertAllocator.GetBaseBlockMemory() ) >> 10 );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_TriSurfMemory
|
|
|
|
For memory profiling
|
|
=================
|
|
*/
|
|
int R_TriSurfMemory( const srfTriangles_t *tri ) {
|
|
int total = 0;
|
|
|
|
if ( !tri ) {
|
|
return total;
|
|
}
|
|
|
|
// used as a flag in interations
|
|
if ( tri == LIGHT_TRIS_DEFERRED ) {
|
|
return total;
|
|
}
|
|
|
|
if ( tri->shadowVertexes != NULL ) {
|
|
total += tri->numVerts * sizeof( tri->shadowVertexes[0] );
|
|
} else if ( tri->verts != NULL ) {
|
|
if ( tri->ambientSurface == NULL || tri->verts != tri->ambientSurface->verts ) {
|
|
total += tri->numVerts * sizeof( tri->verts[0] );
|
|
}
|
|
}
|
|
if ( tri->facePlanes != NULL ) {
|
|
total += tri->numIndexes / 3 * sizeof( tri->facePlanes[0] );
|
|
}
|
|
if ( tri->indexes != NULL ) {
|
|
if ( tri->ambientSurface == NULL || tri->indexes != tri->ambientSurface->indexes ) {
|
|
total += tri->numIndexes * sizeof( tri->indexes[0] );
|
|
}
|
|
}
|
|
if ( tri->silIndexes != NULL ) {
|
|
total += tri->numIndexes * sizeof( tri->silIndexes[0] );
|
|
}
|
|
if ( tri->silEdges != NULL ) {
|
|
total += tri->numSilEdges * sizeof( tri->silEdges[0] );
|
|
}
|
|
if ( tri->dominantTris != NULL ) {
|
|
total += tri->numVerts * sizeof( tri->dominantTris[0] );
|
|
}
|
|
if ( tri->mirroredVerts != NULL ) {
|
|
total += tri->numMirroredVerts * sizeof( tri->mirroredVerts[0] );
|
|
}
|
|
if ( tri->dupVerts != NULL ) {
|
|
total += tri->numDupVerts * sizeof( tri->dupVerts[0] );
|
|
}
|
|
|
|
total += sizeof( *tri );
|
|
|
|
return total;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_FreeStaticTriSurfVertexCaches
|
|
==============
|
|
*/
|
|
void R_FreeStaticTriSurfVertexCaches( srfTriangles_t *tri ) {
|
|
if ( tri->ambientSurface == NULL ) {
|
|
// this is a real model surface
|
|
vertexCache.Free( tri->ambientCache );
|
|
tri->ambientCache = NULL;
|
|
} else {
|
|
// this is a light interaction surface that references
|
|
// a different ambient model surface
|
|
vertexCache.Free( tri->lightingCache );
|
|
tri->lightingCache = NULL;
|
|
}
|
|
if ( tri->indexCache ) {
|
|
vertexCache.Free( tri->indexCache );
|
|
tri->indexCache = NULL;
|
|
}
|
|
if ( tri->shadowCache && ( tri->shadowVertexes != NULL || tri->verts != NULL ) ) {
|
|
// if we don't have tri->shadowVertexes, these are a reference to a
|
|
// shadowCache on the original surface, which a vertex program
|
|
// will take care of making unique for each light
|
|
vertexCache.Free( tri->shadowCache );
|
|
tri->shadowCache = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_ReallyFreeStaticTriSurf
|
|
|
|
This does the actual free
|
|
==============
|
|
*/
|
|
void R_ReallyFreeStaticTriSurf( srfTriangles_t *tri ) {
|
|
if ( !tri ) {
|
|
return;
|
|
}
|
|
|
|
R_FreeStaticTriSurfVertexCaches( tri );
|
|
|
|
if ( tri->verts != NULL ) {
|
|
// R_CreateLightTris points tri->verts at the verts of the ambient surface
|
|
if ( tri->ambientSurface == NULL || tri->verts != tri->ambientSurface->verts ) {
|
|
triVertexAllocator.Free( tri->verts );
|
|
}
|
|
}
|
|
|
|
if ( !tri->deformedSurface ) {
|
|
if ( tri->indexes != NULL ) {
|
|
// if a surface is completely inside a light volume R_CreateLightTris points tri->indexes at the indexes of the ambient surface
|
|
if ( tri->ambientSurface == NULL || tri->indexes != tri->ambientSurface->indexes ) {
|
|
triIndexAllocator.Free( tri->indexes );
|
|
}
|
|
}
|
|
if ( tri->silIndexes != NULL ) {
|
|
triSilIndexAllocator.Free( tri->silIndexes );
|
|
}
|
|
if ( tri->silEdges != NULL ) {
|
|
triSilEdgeAllocator.Free( tri->silEdges );
|
|
}
|
|
if ( tri->dominantTris != NULL ) {
|
|
triDominantTrisAllocator.Free( tri->dominantTris );
|
|
}
|
|
if ( tri->mirroredVerts != NULL ) {
|
|
triMirroredVertAllocator.Free( tri->mirroredVerts );
|
|
}
|
|
if ( tri->dupVerts != NULL ) {
|
|
triDupVertAllocator.Free( tri->dupVerts );
|
|
}
|
|
}
|
|
|
|
if ( tri->facePlanes != NULL ) {
|
|
triPlaneAllocator.Free( tri->facePlanes );
|
|
}
|
|
|
|
if ( tri->shadowVertexes != NULL ) {
|
|
triShadowVertexAllocator.Free( tri->shadowVertexes );
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
memset( tri, 0, sizeof( srfTriangles_t ) );
|
|
#endif
|
|
|
|
srfTrianglesAllocator.Free( tri );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_CheckStaticTriSurfMemory
|
|
==============
|
|
*/
|
|
void R_CheckStaticTriSurfMemory( const srfTriangles_t *tri ) {
|
|
if ( !tri ) {
|
|
return;
|
|
}
|
|
|
|
if ( tri->verts != NULL ) {
|
|
// R_CreateLightTris points tri->verts at the verts of the ambient surface
|
|
if ( tri->ambientSurface == NULL || tri->verts != tri->ambientSurface->verts ) {
|
|
const char *error = triVertexAllocator.CheckMemory( tri->verts );
|
|
assert( error == NULL );
|
|
}
|
|
}
|
|
|
|
if ( !tri->deformedSurface ) {
|
|
if ( tri->indexes != NULL ) {
|
|
// if a surface is completely inside a light volume R_CreateLightTris points tri->indexes at the indexes of the ambient surface
|
|
if ( tri->ambientSurface == NULL || tri->indexes != tri->ambientSurface->indexes ) {
|
|
const char *error = triIndexAllocator.CheckMemory( tri->indexes );
|
|
assert( error == NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( tri->shadowVertexes != NULL ) {
|
|
const char *error = triShadowVertexAllocator.CheckMemory( tri->shadowVertexes );
|
|
assert( error == NULL );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
R_FreeDeferredTriSurfs
|
|
==================
|
|
*/
|
|
void R_FreeDeferredTriSurfs( frameData_t *frame ) {
|
|
srfTriangles_t *tri, *next;
|
|
|
|
if ( !frame ) {
|
|
return;
|
|
}
|
|
|
|
for ( tri = frame->firstDeferredFreeTriSurf; tri; tri = next ) {
|
|
next = tri->nextDeferredFree;
|
|
R_ReallyFreeStaticTriSurf( tri );
|
|
}
|
|
|
|
frame->firstDeferredFreeTriSurf = NULL;
|
|
frame->lastDeferredFreeTriSurf = NULL;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_FreeStaticTriSurf
|
|
|
|
This will defer the free until the current frame has run through the back end.
|
|
==============
|
|
*/
|
|
void R_FreeStaticTriSurf( srfTriangles_t *tri ) {
|
|
frameData_t *frame;
|
|
|
|
if ( !tri ) {
|
|
return;
|
|
}
|
|
|
|
if ( tri->nextDeferredFree ) {
|
|
common->Error( "R_FreeStaticTriSurf: freed a freed triangle" );
|
|
}
|
|
frame = frameData;
|
|
|
|
if ( !frame ) {
|
|
// command line utility, or rendering in editor preview mode ( force )
|
|
R_ReallyFreeStaticTriSurf( tri );
|
|
} else {
|
|
#ifdef ID_DEBUG_MEMORY
|
|
R_CheckStaticTriSurfMemory( tri );
|
|
#endif
|
|
tri->nextDeferredFree = NULL;
|
|
if ( frame->lastDeferredFreeTriSurf ) {
|
|
frame->lastDeferredFreeTriSurf->nextDeferredFree = tri;
|
|
} else {
|
|
frame->firstDeferredFreeTriSurf = tri;
|
|
}
|
|
frame->lastDeferredFreeTriSurf = tri;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
R_AllocStaticTriSurf
|
|
==============
|
|
*/
|
|
srfTriangles_t *R_AllocStaticTriSurf( void ) {
|
|
srfTriangles_t *tris = srfTrianglesAllocator.Alloc();
|
|
memset( tris, 0, sizeof( srfTriangles_t ) );
|
|
return tris;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_CopyStaticTriSurf
|
|
|
|
This only duplicates the indexes and verts, not any of the derived data.
|
|
=================
|
|
*/
|
|
srfTriangles_t *R_CopyStaticTriSurf( const srfTriangles_t *tri ) {
|
|
srfTriangles_t *newTri;
|
|
|
|
newTri = R_AllocStaticTriSurf();
|
|
R_AllocStaticTriSurfVerts( newTri, tri->numVerts );
|
|
R_AllocStaticTriSurfIndexes( newTri, tri->numIndexes );
|
|
newTri->numVerts = tri->numVerts;
|
|
newTri->numIndexes = tri->numIndexes;
|
|
memcpy( newTri->verts, tri->verts, tri->numVerts * sizeof( newTri->verts[0] ) );
|
|
memcpy( newTri->indexes, tri->indexes, tri->numIndexes * sizeof( newTri->indexes[0] ) );
|
|
|
|
return newTri;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AllocStaticTriSurfVerts
|
|
=================
|
|
*/
|
|
void R_AllocStaticTriSurfVerts( srfTriangles_t *tri, int numVerts ) {
|
|
assert( tri->verts == NULL );
|
|
tri->verts = triVertexAllocator.Alloc( numVerts );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AllocStaticTriSurfIndexes
|
|
=================
|
|
*/
|
|
void R_AllocStaticTriSurfIndexes( srfTriangles_t *tri, int numIndexes ) {
|
|
assert( tri->indexes == NULL );
|
|
tri->indexes = triIndexAllocator.Alloc( numIndexes );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AllocStaticTriSurfShadowVerts
|
|
=================
|
|
*/
|
|
void R_AllocStaticTriSurfShadowVerts( srfTriangles_t *tri, int numVerts ) {
|
|
assert( tri->shadowVertexes == NULL );
|
|
tri->shadowVertexes = triShadowVertexAllocator.Alloc( numVerts );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_AllocStaticTriSurfPlanes
|
|
=================
|
|
*/
|
|
void R_AllocStaticTriSurfPlanes( srfTriangles_t *tri, int numIndexes ) {
|
|
if ( tri->facePlanes ) {
|
|
triPlaneAllocator.Free( tri->facePlanes );
|
|
}
|
|
tri->facePlanes = triPlaneAllocator.Alloc( numIndexes / 3 );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_ResizeStaticTriSurfVerts
|
|
=================
|
|
*/
|
|
void R_ResizeStaticTriSurfVerts( srfTriangles_t *tri, int numVerts ) {
|
|
#ifdef USE_TRI_DATA_ALLOCATOR
|
|
tri->verts = triVertexAllocator.Resize( tri->verts, numVerts );
|
|
#else
|
|
assert( false );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_ResizeStaticTriSurfIndexes
|
|
=================
|
|
*/
|
|
void R_ResizeStaticTriSurfIndexes( srfTriangles_t *tri, int numIndexes ) {
|
|
#ifdef USE_TRI_DATA_ALLOCATOR
|
|
tri->indexes = triIndexAllocator.Resize( tri->indexes, numIndexes );
|
|
#else
|
|
assert( false );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_ResizeStaticTriSurfShadowVerts
|
|
=================
|
|
*/
|
|
void R_ResizeStaticTriSurfShadowVerts( srfTriangles_t *tri, int numVerts ) {
|
|
#ifdef USE_TRI_DATA_ALLOCATOR
|
|
tri->shadowVertexes = triShadowVertexAllocator.Resize( tri->shadowVertexes, numVerts );
|
|
#else
|
|
assert( false );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_ReferenceStaticTriSurfVerts
|
|
=================
|
|
*/
|
|
void R_ReferenceStaticTriSurfVerts( srfTriangles_t *tri, const srfTriangles_t *reference ) {
|
|
tri->verts = reference->verts;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_ReferenceStaticTriSurfIndexes
|
|
=================
|
|
*/
|
|
void R_ReferenceStaticTriSurfIndexes( srfTriangles_t *tri, const srfTriangles_t *reference ) {
|
|
tri->indexes = reference->indexes;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_FreeStaticTriSurfSilIndexes
|
|
=================
|
|
*/
|
|
void R_FreeStaticTriSurfSilIndexes( srfTriangles_t *tri ) {
|
|
triSilIndexAllocator.Free( tri->silIndexes );
|
|
tri->silIndexes = NULL;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_RangeCheckIndexes
|
|
|
|
Check for syntactically incorrect indexes, like out of range values.
|
|
Does not check for semantics, like degenerate triangles.
|
|
|
|
No vertexes is acceptable if no indexes.
|
|
No indexes is acceptable.
|
|
More vertexes than are referenced by indexes are acceptable.
|
|
===============
|
|
*/
|
|
void R_RangeCheckIndexes( const srfTriangles_t *tri ) {
|
|
int i;
|
|
|
|
if ( tri->numIndexes < 0 ) {
|
|
common->Error( "R_RangeCheckIndexes: numIndexes < 0" );
|
|
}
|
|
if ( tri->numVerts < 0 ) {
|
|
common->Error( "R_RangeCheckIndexes: numVerts < 0" );
|
|
}
|
|
|
|
// must specify an integral number of triangles
|
|
if ( tri->numIndexes % 3 != 0 ) {
|
|
common->Error( "R_RangeCheckIndexes: numIndexes %% 3" );
|
|
}
|
|
|
|
for ( i = 0 ; i < tri->numIndexes ; i++ ) {
|
|
if ( tri->indexes[i] < 0 || tri->indexes[i] >= tri->numVerts ) {
|
|
common->Error( "R_RangeCheckIndexes: index out of range" );
|
|
}
|
|
}
|
|
|
|
// this should not be possible unless there are unused verts
|
|
if ( tri->numVerts > tri->numIndexes ) {
|
|
// FIXME: find the causes of these
|
|
// common->Printf( "R_RangeCheckIndexes: tri->numVerts > tri->numIndexes\n" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_BoundTriSurf
|
|
=================
|
|
*/
|
|
void R_BoundTriSurf( srfTriangles_t *tri ) {
|
|
SIMDProcessor->MinMax( tri->bounds[0], tri->bounds[1], tri->verts, tri->numVerts );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_CreateSilRemap
|
|
=================
|
|
*/
|
|
static int *R_CreateSilRemap( const srfTriangles_t *tri ) {
|
|
int c_removed, c_unique;
|
|
int *remap;
|
|
int i, j, hashKey;
|
|
const idDrawVert *v1, *v2;
|
|
|
|
remap = (int *)R_ClearedStaticAlloc( tri->numVerts * sizeof( remap[0] ) );
|
|
|
|
if ( !r_useSilRemap.GetBool() ) {
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
remap[i] = i;
|
|
}
|
|
return remap;
|
|
}
|
|
|
|
idHashIndex hash( 1024, tri->numVerts );
|
|
|
|
c_removed = 0;
|
|
c_unique = 0;
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
v1 = &tri->verts[i];
|
|
|
|
// see if there is an earlier vert that it can map to
|
|
hashKey = hash.GenerateKey( v1->xyz );
|
|
for ( j = hash.First( hashKey ); j >= 0; j = hash.Next( j ) ) {
|
|
v2 = &tri->verts[j];
|
|
if ( v2->xyz[0] == v1->xyz[0]
|
|
&& v2->xyz[1] == v1->xyz[1]
|
|
&& v2->xyz[2] == v1->xyz[2] ) {
|
|
c_removed++;
|
|
remap[i] = j;
|
|
break;
|
|
}
|
|
}
|
|
if ( j < 0 ) {
|
|
c_unique++;
|
|
remap[i] = i;
|
|
hash.Add( hashKey, i );
|
|
}
|
|
}
|
|
|
|
return remap;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_CreateSilIndexes
|
|
|
|
Uniquing vertexes only on xyz before creating sil edges reduces
|
|
the edge count by about 20% on Q3 models
|
|
=================
|
|
*/
|
|
void R_CreateSilIndexes( srfTriangles_t *tri ) {
|
|
int i;
|
|
int *remap;
|
|
|
|
if ( tri->silIndexes ) {
|
|
triSilIndexAllocator.Free( tri->silIndexes );
|
|
tri->silIndexes = NULL;
|
|
}
|
|
|
|
remap = R_CreateSilRemap( tri );
|
|
|
|
// remap indexes to the first one
|
|
tri->silIndexes = triSilIndexAllocator.Alloc( tri->numIndexes );
|
|
for ( i = 0; i < tri->numIndexes; i++ ) {
|
|
tri->silIndexes[i] = remap[tri->indexes[i]];
|
|
}
|
|
|
|
R_StaticFree( remap );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_CreateDupVerts
|
|
=====================
|
|
*/
|
|
void R_CreateDupVerts( srfTriangles_t *tri ) {
|
|
int i;
|
|
|
|
int *remap = (int *) _alloca16( tri->numVerts * sizeof( remap[0] ) );
|
|
|
|
// initialize vertex remap in case there are unused verts
|
|
for ( i = 0; i < tri->numVerts; i++ ) {
|
|
remap[i] = i;
|
|
}
|
|
|
|
// set the remap based on how the silhouette indexes are remapped
|
|
for ( i = 0; i < tri->numIndexes; i++ ) {
|
|
remap[tri->indexes[i]] = tri->silIndexes[i];
|
|
}
|
|
|
|
// create duplicate vertex index based on the vertex remap
|
|
int * tempDupVerts = (int *) _alloca16( tri->numVerts * 2 * sizeof( tempDupVerts[0] ) );
|
|
tri->numDupVerts = 0;
|
|
for ( i = 0; i < tri->numVerts; i++ ) {
|
|
if ( remap[i] != i ) {
|
|
tempDupVerts[tri->numDupVerts*2+0] = i;
|
|
tempDupVerts[tri->numDupVerts*2+1] = remap[i];
|
|
tri->numDupVerts++;
|
|
}
|
|
}
|
|
|
|
tri->dupVerts = triDupVertAllocator.Alloc( tri->numDupVerts * 2 );
|
|
memcpy( tri->dupVerts, tempDupVerts, tri->numDupVerts * 2 * sizeof( tri->dupVerts[0] ) );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_DeriveFacePlanes
|
|
|
|
Writes the facePlanes values, overwriting existing ones if present
|
|
=====================
|
|
*/
|
|
void R_DeriveFacePlanes( srfTriangles_t *tri ) {
|
|
idPlane * planes;
|
|
|
|
if ( !tri->facePlanes ) {
|
|
R_AllocStaticTriSurfPlanes( tri, tri->numIndexes );
|
|
}
|
|
planes = tri->facePlanes;
|
|
|
|
#if 1
|
|
|
|
SIMDProcessor->DeriveTriPlanes( planes, tri->verts, tri->numVerts, tri->indexes, tri->numIndexes );
|
|
|
|
#else
|
|
|
|
for ( int i = 0; i < tri->numIndexes; i+= 3, planes++ ) {
|
|
int i1, i2, i3;
|
|
idVec3 d1, d2, normal;
|
|
idVec3 *v1, *v2, *v3;
|
|
|
|
i1 = tri->indexes[i + 0];
|
|
i2 = tri->indexes[i + 1];
|
|
i3 = tri->indexes[i + 2];
|
|
|
|
v1 = &tri->verts[i1].xyz;
|
|
v2 = &tri->verts[i2].xyz;
|
|
v3 = &tri->verts[i3].xyz;
|
|
|
|
d1[0] = v2->x - v1->x;
|
|
d1[1] = v2->y - v1->y;
|
|
d1[2] = v2->z - v1->z;
|
|
|
|
d2[0] = v3->x - v1->x;
|
|
d2[1] = v3->y - v1->y;
|
|
d2[2] = v3->z - v1->z;
|
|
|
|
normal[0] = d2.y * d1.z - d2.z * d1.y;
|
|
normal[1] = d2.z * d1.x - d2.x * d1.z;
|
|
normal[2] = d2.x * d1.y - d2.y * d1.x;
|
|
|
|
float sqrLength, invLength;
|
|
|
|
sqrLength = normal.x * normal.x + normal.y * normal.y + normal.z * normal.z;
|
|
invLength = idMath::RSqrt( sqrLength );
|
|
|
|
(*planes)[0] = normal[0] * invLength;
|
|
(*planes)[1] = normal[1] * invLength;
|
|
(*planes)[2] = normal[2] * invLength;
|
|
|
|
planes->FitThroughPoint( *v1 );
|
|
}
|
|
|
|
#endif
|
|
|
|
tri->facePlanesCalculated = true;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
R_CreateVertexNormals
|
|
|
|
Averages together the contributions of all faces that are
|
|
used by a vertex, creating drawVert->normal
|
|
=====================
|
|
*/
|
|
void R_CreateVertexNormals( srfTriangles_t *tri ) {
|
|
int i, j;
|
|
const idPlane *planes;
|
|
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
tri->verts[i].normal.Zero();
|
|
}
|
|
|
|
if ( !tri->facePlanes || !tri->facePlanesCalculated ) {
|
|
R_DeriveFacePlanes( tri );
|
|
}
|
|
if ( !tri->silIndexes ) {
|
|
R_CreateSilIndexes( tri );
|
|
}
|
|
planes = tri->facePlanes;
|
|
for ( i = 0 ; i < tri->numIndexes ; i += 3, planes++ ) {
|
|
for ( j = 0 ; j < 3 ; j++ ) {
|
|
int index = tri->silIndexes[i+j];
|
|
tri->verts[index].normal += planes->Normal();
|
|
}
|
|
}
|
|
|
|
// normalize and replicate from silIndexes to all indexes
|
|
for ( i = 0 ; i < tri->numIndexes ; i++ ) {
|
|
tri->verts[tri->indexes[i]].normal = tri->verts[tri->silIndexes[i]].normal;
|
|
tri->verts[tri->indexes[i]].normal.Normalize();
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_DefineEdge
|
|
===============
|
|
*/
|
|
static int c_duplicatedEdges, c_tripledEdges;
|
|
|
|
static void R_DefineEdge( int v1, int v2, int planeNum ) {
|
|
int i, hashKey;
|
|
|
|
// check for degenerate edge
|
|
if ( v1 == v2 ) {
|
|
return;
|
|
}
|
|
hashKey = silEdgeHash.GenerateKey( v1, v2 );
|
|
// search for a matching other side
|
|
for ( i = silEdgeHash.First( hashKey ); i >= 0 && i < MAX_SIL_EDGES; i = silEdgeHash.Next( i ) ) {
|
|
if ( silEdges[i].v1 == v1 && silEdges[i].v2 == v2 ) {
|
|
c_duplicatedEdges++;
|
|
// allow it to still create a new edge
|
|
continue;
|
|
}
|
|
if ( silEdges[i].v2 == v1 && silEdges[i].v1 == v2 ) {
|
|
if ( silEdges[i].p2 != numPlanes ) {
|
|
c_tripledEdges++;
|
|
// allow it to still create a new edge
|
|
continue;
|
|
}
|
|
// this is a matching back side
|
|
silEdges[i].p2 = planeNum;
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
// define the new edge
|
|
if ( numSilEdges == MAX_SIL_EDGES ) {
|
|
common->DWarning( "MAX_SIL_EDGES" );
|
|
return;
|
|
}
|
|
|
|
silEdgeHash.Add( hashKey, numSilEdges );
|
|
|
|
silEdges[numSilEdges].p1 = planeNum;
|
|
silEdges[numSilEdges].p2 = numPlanes;
|
|
silEdges[numSilEdges].v1 = v1;
|
|
silEdges[numSilEdges].v2 = v2;
|
|
|
|
numSilEdges++;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SilEdgeSort
|
|
=================
|
|
*/
|
|
static int SilEdgeSort( const void *a, const void *b ) {
|
|
if ( ((silEdge_t *)a)->p1 < ((silEdge_t *)b)->p1 ) {
|
|
return -1;
|
|
}
|
|
if ( ((silEdge_t *)a)->p1 > ((silEdge_t *)b)->p1 ) {
|
|
return 1;
|
|
}
|
|
if ( ((silEdge_t *)a)->p2 < ((silEdge_t *)b)->p2 ) {
|
|
return -1;
|
|
}
|
|
if ( ((silEdge_t *)a)->p2 > ((silEdge_t *)b)->p2 ) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_IdentifySilEdges
|
|
|
|
If the surface will not deform, coplanar edges (polygon interiors)
|
|
can never create silhouette plains, and can be omited
|
|
=================
|
|
*/
|
|
int c_coplanarSilEdges;
|
|
int c_totalSilEdges;
|
|
|
|
void R_IdentifySilEdges( srfTriangles_t *tri, bool omitCoplanarEdges ) {
|
|
int i;
|
|
int numTris;
|
|
int shared, single;
|
|
|
|
omitCoplanarEdges = false; // optimization doesn't work for some reason
|
|
|
|
numTris = tri->numIndexes / 3;
|
|
|
|
numSilEdges = 0;
|
|
silEdgeHash.Clear();
|
|
numPlanes = numTris;
|
|
|
|
c_duplicatedEdges = 0;
|
|
c_tripledEdges = 0;
|
|
|
|
for ( i = 0 ; i < numTris ; i++ ) {
|
|
int i1, i2, i3;
|
|
|
|
i1 = tri->silIndexes[ i*3 + 0 ];
|
|
i2 = tri->silIndexes[ i*3 + 1 ];
|
|
i3 = tri->silIndexes[ i*3 + 2 ];
|
|
|
|
// create the edges
|
|
R_DefineEdge( i1, i2, i );
|
|
R_DefineEdge( i2, i3, i );
|
|
R_DefineEdge( i3, i1, i );
|
|
}
|
|
|
|
if ( c_duplicatedEdges || c_tripledEdges ) {
|
|
common->DWarning( "%i duplicated edge directions, %i tripled edges", c_duplicatedEdges, c_tripledEdges );
|
|
}
|
|
|
|
// if we know that the vertexes aren't going
|
|
// to deform, we can remove interior triangulation edges
|
|
// on otherwise planar polygons.
|
|
// I earlier believed that I could also remove concave
|
|
// edges, because they are never silhouettes in the conventional sense,
|
|
// but they are still needed to balance out all the true sil edges
|
|
// for the shadow algorithm to function
|
|
int c_coplanarCulled;
|
|
|
|
c_coplanarCulled = 0;
|
|
if ( omitCoplanarEdges ) {
|
|
for ( i = 0 ; i < numSilEdges ; i++ ) {
|
|
int i1, i2, i3;
|
|
idPlane plane;
|
|
int base;
|
|
int j;
|
|
float d;
|
|
|
|
if ( silEdges[i].p2 == numPlanes ) { // the fake dangling edge
|
|
continue;
|
|
}
|
|
|
|
base = silEdges[i].p1 * 3;
|
|
i1 = tri->silIndexes[ base + 0 ];
|
|
i2 = tri->silIndexes[ base + 1 ];
|
|
i3 = tri->silIndexes[ base + 2 ];
|
|
|
|
plane.FromPoints( tri->verts[i1].xyz, tri->verts[i2].xyz, tri->verts[i3].xyz );
|
|
|
|
// check to see if points of second triangle are not coplanar
|
|
base = silEdges[i].p2 * 3;
|
|
for ( j = 0 ; j < 3 ; j++ ) {
|
|
i1 = tri->silIndexes[ base + j ];
|
|
d = plane.Distance( tri->verts[i1].xyz );
|
|
if ( d != 0 ) { // even a small epsilon causes problems
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( j == 3 ) {
|
|
// we can cull this sil edge
|
|
memmove( &silEdges[i], &silEdges[i+1], (numSilEdges-i-1) * sizeof( silEdges[i] ) );
|
|
c_coplanarCulled++;
|
|
numSilEdges--;
|
|
i--;
|
|
}
|
|
}
|
|
if ( c_coplanarCulled ) {
|
|
c_coplanarSilEdges += c_coplanarCulled;
|
|
// common->Printf( "%i of %i sil edges coplanar culled\n", c_coplanarCulled,
|
|
// c_coplanarCulled + numSilEdges );
|
|
}
|
|
}
|
|
c_totalSilEdges += numSilEdges;
|
|
|
|
// sort the sil edges based on plane number
|
|
qsort( silEdges, numSilEdges, sizeof( silEdges[0] ), SilEdgeSort );
|
|
|
|
// count up the distribution.
|
|
// a perfectly built model should only have shared
|
|
// edges, but most models will have some interpenetration
|
|
// and dangling edges
|
|
shared = 0;
|
|
single = 0;
|
|
for ( i = 0 ; i < numSilEdges ; i++ ) {
|
|
if ( silEdges[i].p2 == numPlanes ) {
|
|
single++;
|
|
} else {
|
|
shared++;
|
|
}
|
|
}
|
|
|
|
if ( !single ) {
|
|
tri->perfectHull = true;
|
|
} else {
|
|
tri->perfectHull = false;
|
|
}
|
|
|
|
tri->numSilEdges = numSilEdges;
|
|
tri->silEdges = triSilEdgeAllocator.Alloc( numSilEdges );
|
|
memcpy( tri->silEdges, silEdges, numSilEdges * sizeof( tri->silEdges[0] ) );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
R_FaceNegativePolarity
|
|
|
|
Returns true if the texture polarity of the face is negative, false if it is positive or zero
|
|
===============
|
|
*/
|
|
static bool R_FaceNegativePolarity( const srfTriangles_t *tri, int firstIndex ) {
|
|
idDrawVert *a, *b, *c;
|
|
float area;
|
|
float d0[5], d1[5];
|
|
|
|
a = tri->verts + tri->indexes[firstIndex + 0];
|
|
b = tri->verts + tri->indexes[firstIndex + 1];
|
|
c = tri->verts + tri->indexes[firstIndex + 2];
|
|
|
|
d0[3] = b->st[0] - a->st[0];
|
|
d0[4] = b->st[1] - a->st[1];
|
|
|
|
d1[3] = c->st[0] - a->st[0];
|
|
d1[4] = c->st[1] - a->st[1];
|
|
|
|
area = d0[3] * d1[4] - d0[4] * d1[3];
|
|
if ( area >= 0 ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
R_DeriveFaceTangents
|
|
==================
|
|
*/
|
|
typedef struct {
|
|
idVec3 tangents[2];
|
|
bool negativePolarity;
|
|
bool degenerate;
|
|
} faceTangents_t;
|
|
|
|
static void R_DeriveFaceTangents( const srfTriangles_t *tri, faceTangents_t *faceTangents ) {
|
|
int i;
|
|
int c_textureDegenerateFaces;
|
|
int c_positive, c_negative;
|
|
faceTangents_t *ft;
|
|
idDrawVert *a, *b, *c;
|
|
|
|
//
|
|
// calculate tangent vectors for each face in isolation
|
|
//
|
|
c_positive = 0;
|
|
c_negative = 0;
|
|
c_textureDegenerateFaces = 0;
|
|
for ( i = 0 ; i < tri->numIndexes ; i+=3 ) {
|
|
float area;
|
|
idVec3 temp;
|
|
float d0[5], d1[5];
|
|
|
|
ft = &faceTangents[i/3];
|
|
|
|
a = tri->verts + tri->indexes[i + 0];
|
|
b = tri->verts + tri->indexes[i + 1];
|
|
c = tri->verts + tri->indexes[i + 2];
|
|
|
|
d0[0] = b->xyz[0] - a->xyz[0];
|
|
d0[1] = b->xyz[1] - a->xyz[1];
|
|
d0[2] = b->xyz[2] - a->xyz[2];
|
|
d0[3] = b->st[0] - a->st[0];
|
|
d0[4] = b->st[1] - a->st[1];
|
|
|
|
d1[0] = c->xyz[0] - a->xyz[0];
|
|
d1[1] = c->xyz[1] - a->xyz[1];
|
|
d1[2] = c->xyz[2] - a->xyz[2];
|
|
d1[3] = c->st[0] - a->st[0];
|
|
d1[4] = c->st[1] - a->st[1];
|
|
|
|
area = d0[3] * d1[4] - d0[4] * d1[3];
|
|
if ( fabs( area ) < 1e-20f ) {
|
|
ft->negativePolarity = false;
|
|
ft->degenerate = true;
|
|
ft->tangents[0].Zero();
|
|
ft->tangents[1].Zero();
|
|
c_textureDegenerateFaces++;
|
|
continue;
|
|
}
|
|
if ( area > 0.0f ) {
|
|
ft->negativePolarity = false;
|
|
c_positive++;
|
|
} else {
|
|
ft->negativePolarity = true;
|
|
c_negative++;
|
|
}
|
|
ft->degenerate = false;
|
|
|
|
#ifdef USE_INVA
|
|
float inva = area < 0.0f ? -1 : 1; // was = 1.0f / area;
|
|
|
|
temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]) * inva;
|
|
temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]) * inva;
|
|
temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]) * inva;
|
|
temp.Normalize();
|
|
ft->tangents[0] = temp;
|
|
|
|
temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]) * inva;
|
|
temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]) * inva;
|
|
temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]) * inva;
|
|
temp.Normalize();
|
|
ft->tangents[1] = temp;
|
|
#else
|
|
temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]);
|
|
temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]);
|
|
temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]);
|
|
temp.Normalize();
|
|
ft->tangents[0] = temp;
|
|
|
|
temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]);
|
|
temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]);
|
|
temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]);
|
|
temp.Normalize();
|
|
ft->tangents[1] = temp;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===================
|
|
R_DuplicateMirroredVertexes
|
|
|
|
Modifies the surface to bust apart any verts that are shared by both positive and
|
|
negative texture polarities, so tangent space smoothing at the vertex doesn't
|
|
degenerate.
|
|
|
|
This will create some identical vertexes (which will eventually get different tangent
|
|
vectors), so never optimize the resulting mesh, or it will get the mirrored edges back.
|
|
|
|
Reallocates tri->verts and changes tri->indexes in place
|
|
Silindexes are unchanged by this.
|
|
|
|
sets mirroredVerts and mirroredVerts[]
|
|
|
|
===================
|
|
*/
|
|
typedef struct {
|
|
bool polarityUsed[2];
|
|
int negativeRemap;
|
|
} tangentVert_t;
|
|
|
|
static void R_DuplicateMirroredVertexes( srfTriangles_t *tri ) {
|
|
tangentVert_t *tverts, *vert;
|
|
int i, j;
|
|
int totalVerts;
|
|
int numMirror;
|
|
|
|
tverts = (tangentVert_t *)_alloca16( tri->numVerts * sizeof( *tverts ) );
|
|
memset( tverts, 0, tri->numVerts * sizeof( *tverts ) );
|
|
|
|
// determine texture polarity of each surface
|
|
|
|
// mark each vert with the polarities it uses
|
|
for ( i = 0 ; i < tri->numIndexes ; i+=3 ) {
|
|
int polarity;
|
|
|
|
polarity = R_FaceNegativePolarity( tri, i );
|
|
for ( j = 0 ; j < 3 ; j++ ) {
|
|
tverts[tri->indexes[i+j]].polarityUsed[ polarity ] = true;
|
|
}
|
|
}
|
|
|
|
// now create new verts as needed
|
|
totalVerts = tri->numVerts;
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
vert = &tverts[i];
|
|
if ( vert->polarityUsed[0] && vert->polarityUsed[1] ) {
|
|
vert->negativeRemap = totalVerts;
|
|
totalVerts++;
|
|
}
|
|
}
|
|
|
|
tri->numMirroredVerts = totalVerts - tri->numVerts;
|
|
|
|
// now create the new list
|
|
if ( totalVerts == tri->numVerts ) {
|
|
tri->mirroredVerts = NULL;
|
|
return;
|
|
}
|
|
|
|
tri->mirroredVerts = triMirroredVertAllocator.Alloc( tri->numMirroredVerts );
|
|
|
|
#ifdef USE_TRI_DATA_ALLOCATOR
|
|
tri->verts = triVertexAllocator.Resize( tri->verts, totalVerts );
|
|
#else
|
|
idDrawVert *oldVerts = tri->verts;
|
|
R_AllocStaticTriSurfVerts( tri, totalVerts );
|
|
memcpy( tri->verts, oldVerts, tri->numVerts * sizeof( tri->verts[0] ) );
|
|
triVertexAllocator.Free( oldVerts );
|
|
#endif
|
|
|
|
// create the duplicates
|
|
numMirror = 0;
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
j = tverts[i].negativeRemap;
|
|
if ( j ) {
|
|
tri->verts[j] = tri->verts[i];
|
|
tri->mirroredVerts[numMirror] = i;
|
|
numMirror++;
|
|
}
|
|
}
|
|
|
|
tri->numVerts = totalVerts;
|
|
// change the indexes
|
|
for ( i = 0 ; i < tri->numIndexes ; i++ ) {
|
|
if ( tverts[tri->indexes[i]].negativeRemap &&
|
|
R_FaceNegativePolarity( tri, 3*(i/3) ) ) {
|
|
tri->indexes[i] = tverts[tri->indexes[i]].negativeRemap;
|
|
}
|
|
}
|
|
|
|
tri->numVerts = totalVerts;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_DeriveTangentsWithoutNormals
|
|
|
|
Build texture space tangents for bump mapping
|
|
If a surface is deformed, this must be recalculated
|
|
|
|
This assumes that any mirrored vertexes have already been duplicated, so
|
|
any shared vertexes will have the tangent spaces smoothed across.
|
|
|
|
Texture wrapping slightly complicates this, but as long as the normals
|
|
are shared, and the tangent vectors are projected onto the normals, the
|
|
separate vertexes should wind up with identical tangent spaces.
|
|
|
|
mirroring a normalmap WILL cause a slightly visible seam unless the normals
|
|
are completely flat around the edge's full bilerp support.
|
|
|
|
Vertexes which are smooth shaded must have their tangent vectors
|
|
in the same plane, which will allow a seamless
|
|
rendering as long as the normal map is even on both sides of the
|
|
seam.
|
|
|
|
A smooth shaded surface may have multiple tangent vectors at a vertex
|
|
due to texture seams or mirroring, but it should only have a single
|
|
normal vector.
|
|
|
|
Each triangle has a pair of tangent vectors in it's plane
|
|
|
|
Should we consider having vertexes point at shared tangent spaces
|
|
to save space or speed transforms?
|
|
|
|
this version only handles bilateral symetry
|
|
=================
|
|
*/
|
|
void R_DeriveTangentsWithoutNormals( srfTriangles_t *tri ) {
|
|
int i, j;
|
|
faceTangents_t *faceTangents;
|
|
faceTangents_t *ft;
|
|
idDrawVert *vert;
|
|
|
|
faceTangents = (faceTangents_t *)_alloca16( sizeof(faceTangents[0]) * tri->numIndexes/3 );
|
|
R_DeriveFaceTangents( tri, faceTangents );
|
|
|
|
// clear the tangents
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
tri->verts[i].tangents[0].Zero();
|
|
tri->verts[i].tangents[1].Zero();
|
|
}
|
|
|
|
// sum up the neighbors
|
|
for ( i = 0 ; i < tri->numIndexes ; i+=3 ) {
|
|
ft = &faceTangents[i/3];
|
|
|
|
// for each vertex on this face
|
|
for ( j = 0 ; j < 3 ; j++ ) {
|
|
vert = &tri->verts[tri->indexes[i+j]];
|
|
|
|
vert->tangents[0] += ft->tangents[0];
|
|
vert->tangents[1] += ft->tangents[1];
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// sum up both sides of the mirrored verts
|
|
// so the S vectors exactly mirror, and the T vectors are equal
|
|
for ( i = 0 ; i < tri->numMirroredVerts ; i++ ) {
|
|
idDrawVert *v1, *v2;
|
|
|
|
v1 = &tri->verts[ tri->numVerts - tri->numMirroredVerts + i ];
|
|
v2 = &tri->verts[ tri->mirroredVerts[i] ];
|
|
|
|
v1->tangents[0] -= v2->tangents[0];
|
|
v1->tangents[1] += v2->tangents[1];
|
|
|
|
v2->tangents[0] = vec3_origin - v1->tangents[0];
|
|
v2->tangents[1] = v1->tangents[1];
|
|
}
|
|
#endif
|
|
|
|
|
|
// project the summed vectors onto the normal plane
|
|
// and normalize. The tangent vectors will not necessarily
|
|
// be orthogonal to each other, but they will be orthogonal
|
|
// to the surface normal.
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
vert = &tri->verts[i];
|
|
for ( j = 0 ; j < 2 ; j++ ) {
|
|
float d;
|
|
|
|
d = vert->tangents[j] * vert->normal;
|
|
vert->tangents[j] = vert->tangents[j] - d * vert->normal;
|
|
vert->tangents[j].Normalize();
|
|
}
|
|
}
|
|
|
|
tri->tangentsCalculated = true;
|
|
}
|
|
|
|
static ID_INLINE void VectorNormalizeFast2( const idVec3 &v, idVec3 &out) {
|
|
float ilength;
|
|
|
|
ilength = idMath::RSqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] );
|
|
out[0] = v[0] * ilength;
|
|
out[1] = v[1] * ilength;
|
|
out[2] = v[2] * ilength;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_BuildDominantTris
|
|
|
|
Find the largest triangle that uses each vertex
|
|
===================
|
|
*/
|
|
typedef struct {
|
|
int vertexNum;
|
|
int faceNum;
|
|
} indexSort_t;
|
|
|
|
static int IndexSort( const void *a, const void *b ) {
|
|
if ( ((indexSort_t *)a)->vertexNum < ((indexSort_t *)b)->vertexNum ) {
|
|
return -1;
|
|
}
|
|
if ( ((indexSort_t *)a)->vertexNum > ((indexSort_t *)b)->vertexNum ) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void R_BuildDominantTris( srfTriangles_t *tri ) {
|
|
int i, j;
|
|
dominantTri_t *dt;
|
|
indexSort_t *ind = (indexSort_t *)R_StaticAlloc( tri->numIndexes * sizeof( *ind ) );
|
|
|
|
for ( i = 0; i < tri->numIndexes; i++ ) {
|
|
ind[i].vertexNum = tri->indexes[i];
|
|
ind[i].faceNum = i / 3;
|
|
}
|
|
qsort( ind, tri->numIndexes, sizeof( *ind ), IndexSort );
|
|
|
|
tri->dominantTris = dt = triDominantTrisAllocator.Alloc( tri->numVerts );
|
|
memset( dt, 0, tri->numVerts * sizeof( dt[0] ) );
|
|
|
|
for ( i = 0; i < tri->numIndexes; i += j ) {
|
|
float maxArea = 0;
|
|
int vertNum = ind[i].vertexNum;
|
|
for ( j = 0; i + j < tri->numIndexes && ind[i+j].vertexNum == vertNum; j++ ) {
|
|
float d0[5], d1[5];
|
|
idDrawVert *a, *b, *c;
|
|
idVec3 normal, tangent, bitangent;
|
|
|
|
int i1 = tri->indexes[ind[i+j].faceNum * 3 + 0];
|
|
int i2 = tri->indexes[ind[i+j].faceNum * 3 + 1];
|
|
int i3 = tri->indexes[ind[i+j].faceNum * 3 + 2];
|
|
|
|
a = tri->verts + i1;
|
|
b = tri->verts + i2;
|
|
c = tri->verts + i3;
|
|
|
|
d0[0] = b->xyz[0] - a->xyz[0];
|
|
d0[1] = b->xyz[1] - a->xyz[1];
|
|
d0[2] = b->xyz[2] - a->xyz[2];
|
|
d0[3] = b->st[0] - a->st[0];
|
|
d0[4] = b->st[1] - a->st[1];
|
|
|
|
d1[0] = c->xyz[0] - a->xyz[0];
|
|
d1[1] = c->xyz[1] - a->xyz[1];
|
|
d1[2] = c->xyz[2] - a->xyz[2];
|
|
d1[3] = c->st[0] - a->st[0];
|
|
d1[4] = c->st[1] - a->st[1];
|
|
|
|
normal[0] = ( d1[1] * d0[2] - d1[2] * d0[1] );
|
|
normal[1] = ( d1[2] * d0[0] - d1[0] * d0[2] );
|
|
normal[2] = ( d1[0] * d0[1] - d1[1] * d0[0] );
|
|
|
|
float area = normal.Length();
|
|
|
|
// if this is smaller than what we already have, skip it
|
|
if ( area < maxArea ) {
|
|
continue;
|
|
}
|
|
maxArea = area;
|
|
|
|
if ( i1 == vertNum ) {
|
|
dt[vertNum].v2 = i2;
|
|
dt[vertNum].v3 = i3;
|
|
} else if ( i2 == vertNum ) {
|
|
dt[vertNum].v2 = i3;
|
|
dt[vertNum].v3 = i1;
|
|
} else {
|
|
dt[vertNum].v2 = i1;
|
|
dt[vertNum].v3 = i2;
|
|
}
|
|
|
|
float len = area;
|
|
if ( len < 0.001f ) {
|
|
len = 0.001f;
|
|
}
|
|
dt[vertNum].normalizationScale[2] = 1.0f / len; // normal
|
|
|
|
// texture area
|
|
area = d0[3] * d1[4] - d0[4] * d1[3];
|
|
|
|
tangent[0] = ( d0[0] * d1[4] - d0[4] * d1[0] );
|
|
tangent[1] = ( d0[1] * d1[4] - d0[4] * d1[1] );
|
|
tangent[2] = ( d0[2] * d1[4] - d0[4] * d1[2] );
|
|
len = tangent.Length();
|
|
if ( len < 0.001f ) {
|
|
len = 0.001f;
|
|
}
|
|
dt[vertNum].normalizationScale[0] = ( area > 0 ? 1 : -1 ) / len; // tangents[0]
|
|
|
|
bitangent[0] = ( d0[3] * d1[0] - d0[0] * d1[3] );
|
|
bitangent[1] = ( d0[3] * d1[1] - d0[1] * d1[3] );
|
|
bitangent[2] = ( d0[3] * d1[2] - d0[2] * d1[3] );
|
|
len = bitangent.Length();
|
|
if ( len < 0.001f ) {
|
|
len = 0.001f;
|
|
}
|
|
#ifdef DERIVE_UNSMOOTHED_BITANGENT
|
|
dt[vertNum].normalizationScale[1] = ( area > 0 ? 1 : -1 );
|
|
#else
|
|
dt[vertNum].normalizationScale[1] = ( area > 0 ? 1 : -1 ) / len; // tangents[1]
|
|
#endif
|
|
}
|
|
}
|
|
|
|
R_StaticFree( ind );
|
|
}
|
|
|
|
/*
|
|
====================
|
|
R_DeriveUnsmoothedTangents
|
|
|
|
Uses the single largest area triangle for each vertex, instead of smoothing over all
|
|
====================
|
|
*/
|
|
void R_DeriveUnsmoothedTangents( srfTriangles_t *tri ) {
|
|
if ( tri->tangentsCalculated ) {
|
|
return;
|
|
}
|
|
|
|
#if 1
|
|
|
|
SIMDProcessor->DeriveUnsmoothedTangents( tri->verts, tri->dominantTris, tri->numVerts );
|
|
|
|
#else
|
|
|
|
for ( int i = 0 ; i < tri->numVerts ; i++ ) {
|
|
idVec3 temp;
|
|
float d0[5], d1[5];
|
|
idDrawVert *a, *b, *c;
|
|
dominantTri_t *dt = &tri->dominantTris[i];
|
|
|
|
a = tri->verts + i;
|
|
b = tri->verts + dt->v2;
|
|
c = tri->verts + dt->v3;
|
|
|
|
d0[0] = b->xyz[0] - a->xyz[0];
|
|
d0[1] = b->xyz[1] - a->xyz[1];
|
|
d0[2] = b->xyz[2] - a->xyz[2];
|
|
d0[3] = b->st[0] - a->st[0];
|
|
d0[4] = b->st[1] - a->st[1];
|
|
|
|
d1[0] = c->xyz[0] - a->xyz[0];
|
|
d1[1] = c->xyz[1] - a->xyz[1];
|
|
d1[2] = c->xyz[2] - a->xyz[2];
|
|
d1[3] = c->st[0] - a->st[0];
|
|
d1[4] = c->st[1] - a->st[1];
|
|
|
|
a->normal[0] = dt->normalizationScale[2] * ( d1[1] * d0[2] - d1[2] * d0[1] );
|
|
a->normal[1] = dt->normalizationScale[2] * ( d1[2] * d0[0] - d1[0] * d0[2] );
|
|
a->normal[2] = dt->normalizationScale[2] * ( d1[0] * d0[1] - d1[1] * d0[0] );
|
|
|
|
a->tangents[0][0] = dt->normalizationScale[0] * ( d0[0] * d1[4] - d0[4] * d1[0] );
|
|
a->tangents[0][1] = dt->normalizationScale[0] * ( d0[1] * d1[4] - d0[4] * d1[1] );
|
|
a->tangents[0][2] = dt->normalizationScale[0] * ( d0[2] * d1[4] - d0[4] * d1[2] );
|
|
|
|
#ifdef DERIVE_UNSMOOTHED_BITANGENT
|
|
// derive the bitangent for a completely orthogonal axis,
|
|
// instead of using the texture T vector
|
|
a->tangents[1][0] = dt->normalizationScale[1] * ( a->normal[2] * a->tangents[0][1] - a->normal[1] * a->tangents[0][2] );
|
|
a->tangents[1][1] = dt->normalizationScale[1] * ( a->normal[0] * a->tangents[0][2] - a->normal[2] * a->tangents[0][0] );
|
|
a->tangents[1][2] = dt->normalizationScale[1] * ( a->normal[1] * a->tangents[0][0] - a->normal[0] * a->tangents[0][1] );
|
|
#else
|
|
// calculate the bitangent from the texture T vector
|
|
a->tangents[1][0] = dt->normalizationScale[1] * ( d0[3] * d1[0] - d0[0] * d1[3] );
|
|
a->tangents[1][1] = dt->normalizationScale[1] * ( d0[3] * d1[1] - d0[1] * d1[3] );
|
|
a->tangents[1][2] = dt->normalizationScale[1] * ( d0[3] * d1[2] - d0[2] * d1[3] );
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
tri->tangentsCalculated = true;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
R_DeriveTangents
|
|
|
|
This is called once for static surfaces, and every frame for deforming surfaces
|
|
|
|
Builds tangents, normals, and face planes
|
|
==================
|
|
*/
|
|
void R_DeriveTangents( srfTriangles_t *tri, bool allocFacePlanes ) {
|
|
int i;
|
|
idPlane *planes;
|
|
|
|
if ( tri->dominantTris != NULL ) {
|
|
R_DeriveUnsmoothedTangents( tri );
|
|
return;
|
|
}
|
|
|
|
if ( tri->tangentsCalculated ) {
|
|
return;
|
|
}
|
|
|
|
tr.pc.c_tangentIndexes += tri->numIndexes;
|
|
|
|
if ( !tri->facePlanes && allocFacePlanes ) {
|
|
R_AllocStaticTriSurfPlanes( tri, tri->numIndexes );
|
|
}
|
|
planes = tri->facePlanes;
|
|
|
|
#if 1
|
|
|
|
if ( !planes ) {
|
|
planes = (idPlane *)_alloca16( ( tri->numIndexes / 3 ) * sizeof( planes[0] ) );
|
|
}
|
|
|
|
SIMDProcessor->DeriveTangents( planes, tri->verts, tri->numVerts, tri->indexes, tri->numIndexes );
|
|
|
|
#else
|
|
|
|
for ( i = 0; i < tri->numVerts; i++ ) {
|
|
tri->verts[i].normal.Zero();
|
|
tri->verts[i].tangents[0].Zero();
|
|
tri->verts[i].tangents[1].Zero();
|
|
}
|
|
|
|
for ( i = 0; i < tri->numIndexes; i += 3 ) {
|
|
// make face tangents
|
|
float d0[5], d1[5];
|
|
idDrawVert *a, *b, *c;
|
|
idVec3 temp, normal, tangents[2];
|
|
|
|
a = tri->verts + tri->indexes[i + 0];
|
|
b = tri->verts + tri->indexes[i + 1];
|
|
c = tri->verts + tri->indexes[i + 2];
|
|
|
|
d0[0] = b->xyz[0] - a->xyz[0];
|
|
d0[1] = b->xyz[1] - a->xyz[1];
|
|
d0[2] = b->xyz[2] - a->xyz[2];
|
|
d0[3] = b->st[0] - a->st[0];
|
|
d0[4] = b->st[1] - a->st[1];
|
|
|
|
d1[0] = c->xyz[0] - a->xyz[0];
|
|
d1[1] = c->xyz[1] - a->xyz[1];
|
|
d1[2] = c->xyz[2] - a->xyz[2];
|
|
d1[3] = c->st[0] - a->st[0];
|
|
d1[4] = c->st[1] - a->st[1];
|
|
|
|
// normal
|
|
temp[0] = d1[1] * d0[2] - d1[2] * d0[1];
|
|
temp[1] = d1[2] * d0[0] - d1[0] * d0[2];
|
|
temp[2] = d1[0] * d0[1] - d1[1] * d0[0];
|
|
VectorNormalizeFast2( temp, normal );
|
|
|
|
#ifdef USE_INVA
|
|
float area = d0[3] * d1[4] - d0[4] * d1[3];
|
|
float inva = area < 0.0f ? -1 : 1; // was = 1.0f / area;
|
|
|
|
temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]) * inva;
|
|
temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]) * inva;
|
|
temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]) * inva;
|
|
VectorNormalizeFast2( temp, tangents[0] );
|
|
|
|
temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]) * inva;
|
|
temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]) * inva;
|
|
temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]) * inva;
|
|
VectorNormalizeFast2( temp, tangents[1] );
|
|
#else
|
|
temp[0] = (d0[0] * d1[4] - d0[4] * d1[0]);
|
|
temp[1] = (d0[1] * d1[4] - d0[4] * d1[1]);
|
|
temp[2] = (d0[2] * d1[4] - d0[4] * d1[2]);
|
|
VectorNormalizeFast2( temp, tangents[0] );
|
|
|
|
temp[0] = (d0[3] * d1[0] - d0[0] * d1[3]);
|
|
temp[1] = (d0[3] * d1[1] - d0[1] * d1[3]);
|
|
temp[2] = (d0[3] * d1[2] - d0[2] * d1[3]);
|
|
VectorNormalizeFast2( temp, tangents[1] );
|
|
#endif
|
|
|
|
// sum up the tangents and normals for each vertex on this face
|
|
for ( int j = 0 ; j < 3 ; j++ ) {
|
|
vert = &tri->verts[tri->indexes[i+j]];
|
|
vert->normal += normal;
|
|
vert->tangents[0] += tangents[0];
|
|
vert->tangents[1] += tangents[1];
|
|
}
|
|
|
|
if ( planes ) {
|
|
planes->Normal() = normal;
|
|
planes->FitThroughPoint( a->xyz );
|
|
planes++;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if 0
|
|
|
|
if ( tri->silIndexes != NULL ) {
|
|
for ( i = 0; i < tri->numVerts; i++ ) {
|
|
tri->verts[i].normal.Zero();
|
|
}
|
|
for ( i = 0; i < tri->numIndexes; i++ ) {
|
|
tri->verts[tri->silIndexes[i]].normal += planes[i/3].Normal();
|
|
}
|
|
for ( i = 0 ; i < tri->numIndexes ; i++ ) {
|
|
tri->verts[tri->indexes[i]].normal = tri->verts[tri->silIndexes[i]].normal;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
int *dupVerts = tri->dupVerts;
|
|
idDrawVert *verts = tri->verts;
|
|
|
|
// add the normal of a duplicated vertex to the normal of the first vertex with the same XYZ
|
|
for ( i = 0; i < tri->numDupVerts; i++ ) {
|
|
verts[dupVerts[i*2+0]].normal += verts[dupVerts[i*2+1]].normal;
|
|
}
|
|
|
|
// copy vertex normals to duplicated vertices
|
|
for ( i = 0; i < tri->numDupVerts; i++ ) {
|
|
verts[dupVerts[i*2+1]].normal = verts[dupVerts[i*2+0]].normal;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if 0
|
|
// sum up both sides of the mirrored verts
|
|
// so the S vectors exactly mirror, and the T vectors are equal
|
|
for ( i = 0 ; i < tri->numMirroredVerts ; i++ ) {
|
|
idDrawVert *v1, *v2;
|
|
|
|
v1 = &tri->verts[ tri->numVerts - tri->numMirroredVerts + i ];
|
|
v2 = &tri->verts[ tri->mirroredVerts[i] ];
|
|
|
|
v1->tangents[0] -= v2->tangents[0];
|
|
v1->tangents[1] += v2->tangents[1];
|
|
|
|
v2->tangents[0] = vec3_origin - v1->tangents[0];
|
|
v2->tangents[1] = v1->tangents[1];
|
|
}
|
|
#endif
|
|
|
|
// project the summed vectors onto the normal plane
|
|
// and normalize. The tangent vectors will not necessarily
|
|
// be orthogonal to each other, but they will be orthogonal
|
|
// to the surface normal.
|
|
#if 1
|
|
|
|
SIMDProcessor->NormalizeTangents( tri->verts, tri->numVerts );
|
|
|
|
#else
|
|
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
idDrawVert *vert = &tri->verts[i];
|
|
|
|
VectorNormalizeFast2( vert->normal, vert->normal );
|
|
|
|
// project the tangent vectors
|
|
for ( int j = 0 ; j < 2 ; j++ ) {
|
|
float d;
|
|
|
|
d = vert->tangents[j] * vert->normal;
|
|
vert->tangents[j] = vert->tangents[j] - d * vert->normal;
|
|
VectorNormalizeFast2( vert->tangents[j], vert->tangents[j] );
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
tri->tangentsCalculated = true;
|
|
tri->facePlanesCalculated = true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_RemoveDuplicatedTriangles
|
|
|
|
silIndexes must have already been calculated
|
|
|
|
silIndexes are used instead of indexes, because duplicated
|
|
triangles could have different texture coordinates.
|
|
=================
|
|
*/
|
|
void R_RemoveDuplicatedTriangles( srfTriangles_t *tri ) {
|
|
int c_removed;
|
|
int i, j, r;
|
|
int a, b, c;
|
|
|
|
c_removed = 0;
|
|
|
|
// check for completely duplicated triangles
|
|
// any rotation of the triangle is still the same, but a mirroring
|
|
// is considered different
|
|
for ( i = 0 ; i < tri->numIndexes ; i+=3 ) {
|
|
for ( r = 0 ; r < 3 ; r++ ) {
|
|
a = tri->silIndexes[i+r];
|
|
b = tri->silIndexes[i+(r+1)%3];
|
|
c = tri->silIndexes[i+(r+2)%3];
|
|
for ( j = i + 3 ; j < tri->numIndexes ; j+=3 ) {
|
|
if ( tri->silIndexes[j] == a && tri->silIndexes[j+1] == b && tri->silIndexes[j+2] == c ) {
|
|
c_removed++;
|
|
memmove( tri->indexes + j, tri->indexes + j + 3, ( tri->numIndexes - j - 3 ) * sizeof( tri->indexes[0] ) );
|
|
memmove( tri->silIndexes + j, tri->silIndexes + j + 3, ( tri->numIndexes - j - 3 ) * sizeof( tri->silIndexes[0] ) );
|
|
tri->numIndexes -= 3;
|
|
j -= 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( c_removed ) {
|
|
common->Printf( "removed %i duplicated triangles\n", c_removed );
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_RemoveDegenerateTriangles
|
|
|
|
silIndexes must have already been calculated
|
|
=================
|
|
*/
|
|
void R_RemoveDegenerateTriangles( srfTriangles_t *tri ) {
|
|
int c_removed;
|
|
int i;
|
|
int a, b, c;
|
|
|
|
// check for completely degenerate triangles
|
|
c_removed = 0;
|
|
for ( i = 0; i < tri->numIndexes; i += 3 ) {
|
|
a = tri->silIndexes[i];
|
|
b = tri->silIndexes[i+1];
|
|
c = tri->silIndexes[i+2];
|
|
if ( a == b || a == c || b == c ) {
|
|
c_removed++;
|
|
memmove( tri->indexes + i, tri->indexes + i + 3, ( tri->numIndexes - i - 3 ) * sizeof( tri->indexes[0] ) );
|
|
if ( tri->silIndexes ) {
|
|
memmove( tri->silIndexes + i, tri->silIndexes + i + 3, ( tri->numIndexes - i - 3 ) * sizeof( tri->silIndexes[0] ) );
|
|
}
|
|
tri->numIndexes -= 3;
|
|
i -= 3;
|
|
}
|
|
}
|
|
|
|
// this doesn't free the memory used by the unused verts
|
|
|
|
if ( c_removed ) {
|
|
common->Printf( "removed %i degenerate triangles\n", c_removed );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_TestDegenerateTextureSpace
|
|
=================
|
|
*/
|
|
void R_TestDegenerateTextureSpace( srfTriangles_t *tri ) {
|
|
int c_degenerate;
|
|
int i;
|
|
|
|
// check for triangles with a degenerate texture space
|
|
c_degenerate = 0;
|
|
for ( i = 0; i < tri->numIndexes; i += 3 ) {
|
|
const idDrawVert &a = tri->verts[tri->indexes[i+0]];
|
|
const idDrawVert &b = tri->verts[tri->indexes[i+1]];
|
|
const idDrawVert &c = tri->verts[tri->indexes[i+2]];
|
|
|
|
if ( a.st == b.st || b.st == c.st || c.st == a.st ) {
|
|
c_degenerate++;
|
|
}
|
|
}
|
|
|
|
if ( c_degenerate ) {
|
|
// common->Printf( "%d triangles with a degenerate texture space\n", c_degenerate );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_RemoveUnusedVerts
|
|
=================
|
|
*/
|
|
void R_RemoveUnusedVerts( srfTriangles_t *tri ) {
|
|
int i;
|
|
int *mark;
|
|
int index;
|
|
int used;
|
|
|
|
mark = (int *)R_ClearedStaticAlloc( tri->numVerts * sizeof( *mark ) );
|
|
|
|
for ( i = 0 ; i < tri->numIndexes ; i++ ) {
|
|
index = tri->indexes[i];
|
|
if ( index < 0 || index >= tri->numVerts ) {
|
|
common->Error( "R_RemoveUnusedVerts: bad index" );
|
|
}
|
|
mark[ index ] = 1;
|
|
|
|
if ( tri->silIndexes ) {
|
|
index = tri->silIndexes[i];
|
|
if ( index < 0 || index >= tri->numVerts ) {
|
|
common->Error( "R_RemoveUnusedVerts: bad index" );
|
|
}
|
|
mark[ index ] = 1;
|
|
}
|
|
}
|
|
|
|
used = 0;
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
if ( !mark[i] ) {
|
|
continue;
|
|
}
|
|
mark[i] = used + 1;
|
|
used++;
|
|
}
|
|
|
|
if ( used != tri->numVerts ) {
|
|
for ( i = 0 ; i < tri->numIndexes ; i++ ) {
|
|
tri->indexes[i] = mark[ tri->indexes[i] ] - 1;
|
|
if ( tri->silIndexes ) {
|
|
tri->silIndexes[i] = mark[ tri->silIndexes[i] ] - 1;
|
|
}
|
|
}
|
|
tri->numVerts = used;
|
|
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
index = mark[ i ];
|
|
if ( !index ) {
|
|
continue;
|
|
}
|
|
tri->verts[ index - 1 ] = tri->verts[i];
|
|
}
|
|
|
|
// this doesn't realloc the arrays to save the memory used by the unused verts
|
|
}
|
|
|
|
R_StaticFree( mark );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_MergeSurfaceList
|
|
|
|
Only deals with vertexes and indexes, not silhouettes, planes, etc.
|
|
Does NOT perform a cleanup triangles, so there may be duplicated verts in the result.
|
|
=================
|
|
*/
|
|
srfTriangles_t *R_MergeSurfaceList( const srfTriangles_t **surfaces, int numSurfaces ) {
|
|
srfTriangles_t *newTri;
|
|
const srfTriangles_t *tri;
|
|
int i, j;
|
|
int totalVerts;
|
|
int totalIndexes;
|
|
|
|
totalVerts = 0;
|
|
totalIndexes = 0;
|
|
for ( i = 0 ; i < numSurfaces ; i++ ) {
|
|
totalVerts += surfaces[i]->numVerts;
|
|
totalIndexes += surfaces[i]->numIndexes;
|
|
}
|
|
|
|
newTri = R_AllocStaticTriSurf();
|
|
newTri->numVerts = totalVerts;
|
|
newTri->numIndexes = totalIndexes;
|
|
R_AllocStaticTriSurfVerts( newTri, newTri->numVerts );
|
|
R_AllocStaticTriSurfIndexes( newTri, newTri->numIndexes );
|
|
|
|
totalVerts = 0;
|
|
totalIndexes = 0;
|
|
for ( i = 0 ; i < numSurfaces ; i++ ) {
|
|
tri = surfaces[i];
|
|
memcpy( newTri->verts + totalVerts, tri->verts, tri->numVerts * sizeof( *tri->verts ) );
|
|
for ( j = 0 ; j < tri->numIndexes ; j++ ) {
|
|
newTri->indexes[ totalIndexes + j ] = totalVerts + tri->indexes[j];
|
|
}
|
|
totalVerts += tri->numVerts;
|
|
totalIndexes += tri->numIndexes;
|
|
}
|
|
|
|
return newTri;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_MergeTriangles
|
|
|
|
Only deals with vertexes and indexes, not silhouettes, planes, etc.
|
|
Does NOT perform a cleanup triangles, so there may be duplicated verts in the result.
|
|
=================
|
|
*/
|
|
srfTriangles_t *R_MergeTriangles( const srfTriangles_t *tri1, const srfTriangles_t *tri2 ) {
|
|
const srfTriangles_t *tris[2];
|
|
|
|
tris[0] = tri1;
|
|
tris[1] = tri2;
|
|
|
|
return R_MergeSurfaceList( tris, 2 );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_ReverseTriangles
|
|
|
|
Lit two sided surfaces need to have the triangles actually duplicated,
|
|
they can't just turn on two sided lighting, because the normal and tangents
|
|
are wrong on the other sides.
|
|
|
|
This should be called before R_CleanupTriangles
|
|
=================
|
|
*/
|
|
void R_ReverseTriangles( srfTriangles_t *tri ) {
|
|
int i;
|
|
|
|
// flip the normal on each vertex
|
|
// If the surface is going to have generated normals, this won't matter,
|
|
// but if it has explicit normals, this will keep it on the correct side
|
|
for ( i = 0 ; i < tri->numVerts ; i++ ) {
|
|
tri->verts[i].normal = vec3_origin - tri->verts[i].normal;
|
|
}
|
|
|
|
// flip the index order to make them back sided
|
|
for ( i = 0 ; i < tri->numIndexes ; i+= 3 ) {
|
|
glIndex_t temp;
|
|
|
|
temp = tri->indexes[ i + 0 ];
|
|
tri->indexes[ i + 0 ] = tri->indexes[ i + 1 ];
|
|
tri->indexes[ i + 1 ] = temp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
R_CleanupTriangles
|
|
|
|
FIXME: allow createFlat and createSmooth normals, as well as explicit
|
|
=================
|
|
*/
|
|
void R_CleanupTriangles( srfTriangles_t *tri, bool createNormals, bool identifySilEdges, bool useUnsmoothedTangents ) {
|
|
R_RangeCheckIndexes( tri );
|
|
|
|
R_CreateSilIndexes( tri );
|
|
|
|
// R_RemoveDuplicatedTriangles( tri ); // this may remove valid overlapped transparent triangles
|
|
|
|
R_RemoveDegenerateTriangles( tri );
|
|
|
|
R_TestDegenerateTextureSpace( tri );
|
|
|
|
// R_RemoveUnusedVerts( tri );
|
|
|
|
if ( identifySilEdges ) {
|
|
R_IdentifySilEdges( tri, true ); // assume it is non-deformable, and omit coplanar edges
|
|
}
|
|
|
|
// bust vertexes that share a mirrored edge into separate vertexes
|
|
R_DuplicateMirroredVertexes( tri );
|
|
|
|
// optimize the index order (not working?)
|
|
// R_OrderIndexes( tri->numIndexes, tri->indexes );
|
|
|
|
R_CreateDupVerts( tri );
|
|
|
|
R_BoundTriSurf( tri );
|
|
|
|
if ( useUnsmoothedTangents ) {
|
|
R_BuildDominantTris( tri );
|
|
R_DeriveUnsmoothedTangents( tri );
|
|
} else if ( !createNormals ) {
|
|
R_DeriveFacePlanes( tri );
|
|
R_DeriveTangentsWithoutNormals( tri );
|
|
} else {
|
|
R_DeriveTangents( tri );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
DEFORMED SURFACES
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
/*
|
|
===================
|
|
R_BuildDeformInfo
|
|
===================
|
|
*/
|
|
deformInfo_t *R_BuildDeformInfo( int numVerts, const idDrawVert *verts, int numIndexes, const int *indexes, bool useUnsmoothedTangents ) {
|
|
deformInfo_t *deform;
|
|
srfTriangles_t tri;
|
|
int i;
|
|
|
|
memset( &tri, 0, sizeof( tri ) );
|
|
|
|
tri.numVerts = numVerts;
|
|
R_AllocStaticTriSurfVerts( &tri, tri.numVerts );
|
|
SIMDProcessor->Memcpy( tri.verts, verts, tri.numVerts * sizeof( tri.verts[0] ) );
|
|
|
|
tri.numIndexes = numIndexes;
|
|
R_AllocStaticTriSurfIndexes( &tri, tri.numIndexes );
|
|
|
|
// don't memcpy, so we can change the index type from int to short without changing the interface
|
|
for ( i = 0 ; i < tri.numIndexes ; i++ ) {
|
|
tri.indexes[i] = indexes[i];
|
|
}
|
|
|
|
R_RangeCheckIndexes( &tri );
|
|
R_CreateSilIndexes( &tri );
|
|
|
|
// should we order the indexes here?
|
|
|
|
// R_RemoveDuplicatedTriangles( &tri );
|
|
// R_RemoveDegenerateTriangles( &tri );
|
|
// R_RemoveUnusedVerts( &tri );
|
|
R_IdentifySilEdges( &tri, false ); // we cannot remove coplanar edges, because
|
|
// they can deform to silhouettes
|
|
|
|
R_DuplicateMirroredVertexes( &tri ); // split mirror points into multiple points
|
|
|
|
R_CreateDupVerts( &tri );
|
|
|
|
if ( useUnsmoothedTangents ) {
|
|
R_BuildDominantTris( &tri );
|
|
}
|
|
|
|
deform = (deformInfo_t *)R_ClearedStaticAlloc( sizeof( *deform ) );
|
|
|
|
deform->numSourceVerts = numVerts;
|
|
deform->numOutputVerts = tri.numVerts;
|
|
|
|
deform->numIndexes = numIndexes;
|
|
deform->indexes = tri.indexes;
|
|
|
|
deform->silIndexes = tri.silIndexes;
|
|
|
|
deform->numSilEdges = tri.numSilEdges;
|
|
deform->silEdges = tri.silEdges;
|
|
|
|
deform->dominantTris = tri.dominantTris;
|
|
|
|
deform->numMirroredVerts = tri.numMirroredVerts;
|
|
deform->mirroredVerts = tri.mirroredVerts;
|
|
|
|
deform->numDupVerts = tri.numDupVerts;
|
|
deform->dupVerts = tri.dupVerts;
|
|
|
|
if ( tri.verts ) {
|
|
triVertexAllocator.Free( tri.verts );
|
|
}
|
|
|
|
if ( tri.facePlanes ) {
|
|
triPlaneAllocator.Free( tri.facePlanes );
|
|
}
|
|
|
|
return deform;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_FreeDeformInfo
|
|
===================
|
|
*/
|
|
void R_FreeDeformInfo( deformInfo_t *deformInfo ) {
|
|
if ( deformInfo->indexes != NULL ) {
|
|
triIndexAllocator.Free( deformInfo->indexes );
|
|
}
|
|
if ( deformInfo->silIndexes != NULL ) {
|
|
triSilIndexAllocator.Free( deformInfo->silIndexes );
|
|
}
|
|
if ( deformInfo->silEdges != NULL ) {
|
|
triSilEdgeAllocator.Free( deformInfo->silEdges );
|
|
}
|
|
if ( deformInfo->dominantTris != NULL ) {
|
|
triDominantTrisAllocator.Free( deformInfo->dominantTris );
|
|
}
|
|
if ( deformInfo->mirroredVerts != NULL ) {
|
|
triMirroredVertAllocator.Free( deformInfo->mirroredVerts );
|
|
}
|
|
if ( deformInfo->dupVerts != NULL ) {
|
|
triDupVertAllocator.Free( deformInfo->dupVerts );
|
|
}
|
|
R_StaticFree( deformInfo );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
R_DeformInfoMemoryUsed
|
|
===================
|
|
*/
|
|
int R_DeformInfoMemoryUsed( deformInfo_t *deformInfo ) {
|
|
int total = 0;
|
|
|
|
if ( deformInfo->indexes != NULL ) {
|
|
total += deformInfo->numIndexes * sizeof( deformInfo->indexes[0] );
|
|
}
|
|
if ( deformInfo->silIndexes != NULL ) {
|
|
total += deformInfo->numIndexes * sizeof( deformInfo->silIndexes[0] );
|
|
}
|
|
if ( deformInfo->silEdges != NULL ) {
|
|
total += deformInfo->numSilEdges * sizeof( deformInfo->silEdges[0] );
|
|
}
|
|
if ( deformInfo->dominantTris != NULL ) {
|
|
total += deformInfo->numSourceVerts * sizeof( deformInfo->dominantTris[0] );
|
|
}
|
|
if ( deformInfo->mirroredVerts != NULL ) {
|
|
total += deformInfo->numMirroredVerts * sizeof( deformInfo->mirroredVerts[0] );
|
|
}
|
|
if ( deformInfo->dupVerts != NULL ) {
|
|
total += deformInfo->numDupVerts * sizeof( deformInfo->dupVerts[0] );
|
|
}
|
|
|
|
total += sizeof( *deformInfo );
|
|
return total;
|
|
}
|