Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 261 additions & 25 deletions src/engine/renderer/GeometryOptimiser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#include "GeometryOptimiser.h"

#include "ShadeCommon.h"
#include "GeometryCache.h"

static int LeafSurfaceCompare( const void* a, const void* b ) {
bspSurface_t* aa, * bb;

Expand Down Expand Up @@ -420,35 +423,131 @@ void MergeDuplicateVertices( bspSurface_t** rendererSurfaces, int numSurfaces, s
Log::Notice( "Merged %i vertices into %i in %i ms", numVerticesIn, numVerticesOut, Sys::Milliseconds() - start );
}

/* static void ProcessMaterialSurface( MaterialSurface& surface, std::vector<MaterialSurface>& materialSurfaces,
std::vector<MaterialSurface>& processedMaterialSurfaces,
srfVert_t* verts, glIndex_t* indices ) {
if ( surface.count == MAX_MATERIAL_SURFACE_TRIS ) {
materialSurfaces.emplace_back( surface );
static void ProcessMaterialSurface( MaterialSurface* surface, SurfaceIndexes* surfaceIdxs,
std::vector<MaterialSurface>& processedSurfaces,
const srfVert_t* vertexes, glIndex_t* idxs, uint32_t* numIndices ) {
vec3_t mins;
vec3_t maxs;
ClearBounds( mins, maxs );

for ( uint32_t i = 0; i < surface->count; i++ ) {
AddPointToBounds( vertexes[surfaceIdxs->idxs[i]].xyz, mins, maxs );
}

while ( surface.count > MAX_MATERIAL_SURFACE_TRIS ) {
MaterialSurface srf = surface;
const uint32_t oldFirstIndex = surface->firstIndex;
surface->firstIndex = *numIndices;

memcpy( idxs + ( *numIndices ), surfaceIdxs->idxs, surface->count * sizeof( glIndex_t ) );

*numIndices += surface->count;

SphereFromBounds( mins, maxs, surface->origin, &surface->radius );

processedSurfaces.emplace_back( *surface );

surface->firstIndex = oldFirstIndex;
}

static void OptimiseMaterialSurfaces( std::vector<MaterialSurface>& materialSurfaces,
Comment thread
VReaperV marked this conversation as resolved.
const uint32_t gridSize[3], const uint32_t cellSize[3], const vec3_t worldMins,
std::function<bool( const MaterialSurface&, const MaterialSurface& )> materialSurfaceSort,
const srfVert_t* vertices, const glIndex_t* indices,
glIndex_t* idxs,
std::vector<MaterialSurface>& processedMaterialSurfaces, uint32_t& numIndices ) {

Grid<uint32_t> surfaceGrid( true );
surfaceGrid.SetSize( gridSize[0], gridSize[1], gridSize[2] );
surfaceGrid.Clear();

Grid<SurfaceIndexes> surfaceIdxsGrid( true );
surfaceIdxsGrid.SetSize( gridSize[0], gridSize[1], gridSize[2] );

MaterialSurface* surface = &materialSurfaces[0];
MaterialSurface* surface2;
bool addedSurfaces = false;

std::function<void()> processGrid = [&]() {
if ( !addedSurfaces ) {
surface = surface2;
return;
}

for ( Grid<uint32_t>::Iterator it = surfaceGrid.begin(); it != surfaceGrid.end(); it++ ) {
if ( *it ) {
SurfaceIndexes* srfIdxs = &surfaceIdxsGrid( it - surfaceGrid.begin() );
surface->count = *it;
ProcessMaterialSurface( surface, srfIdxs, processedMaterialSurfaces, vertices, idxs, &numIndices );
}
}

surfaceGrid.Clear();

surface = surface2;
addedSurfaces = false;
};

for ( uint32_t surfaceIndex = 0; surfaceIndex < materialSurfaces.size(); surfaceIndex++ ) {
surface2 = &materialSurfaces[surfaceIndex];
if ( surface2->skyBrush ) {
continue;
}

if ( materialSurfaceSort( *surface, *surface2 ) ) {
processGrid();
}

for ( uint32_t i = 0; i < surface2->count; i += 3 ) {
glIndex_t tri[3] = { indices[i + surface2->firstIndex], indices[i + 1 + surface2->firstIndex],
indices[i + 2 + surface2->firstIndex] };

srfVert_t triVerts[3] = { vertices[tri[0]], vertices[tri[1]], vertices[tri[2]] };

vec3_t origin;
VectorAdd( triVerts[0].xyz, triVerts[1].xyz, origin );
VectorAdd( origin, triVerts[2].xyz, origin );

VectorScale( origin, 1.0f / 3.0f, origin );
VectorSubtract( origin, worldMins, origin );

uint32_t gridOrigin[3]{ uint32_t( origin[0] / cellSize[0] ), uint32_t( origin[1] / cellSize[1] ),
uint32_t( origin[2] / cellSize[2] ) };

uint32_t* count = &surfaceGrid( gridOrigin[0], gridOrigin[1], gridOrigin[2] );
SurfaceIndexes* srfIdxs = &surfaceIdxsGrid( gridOrigin[0], gridOrigin[1], gridOrigin[2] );

memcpy( srfIdxs->idxs + ( *count ), tri, 3 * sizeof( glIndex_t ) );

*count += 3;

if ( *count + 3 >= MAX_MATERIAL_SURFACE_INDEXES ) {
const uint32_t oldCount = surface2->count;
surface2->count = *count;

srf.count = MAX_MATERIAL_SURFACE_TRIS;
surface.count -= MAX_MATERIAL_SURFACE_TRIS;
surface.firstIndex += MAX_MATERIAL_SURFACE_TRIS;
ProcessMaterialSurface( surface2, srfIdxs, processedMaterialSurfaces, vertices, idxs, &numIndices );

processedMaterialSurfaces.push_back( srf );
surface2->count = oldCount;
*count = 0;
} else {
addedSurfaces = true;
}
}
}
} */

std::vector<MaterialSurface> OptimiseMapGeometryMaterial(bspSurface_t** rendererSurfaces, int numSurfaces ) {
processGrid();
}

std::vector<MaterialSurface> OptimiseMapGeometryMaterial( world_t* world, bspSurface_t** rendererSurfaces, int numSurfaces,
const srfVert_t* vertices, const int numVerticesIn, const glIndex_t* indices, const int numIndicesIn ) {
std::vector<MaterialSurface> materialSurfaces;
std::vector<MaterialSurface> materialSurfacesExtended;
materialSurfaces.reserve( numSurfaces );
materialSurfacesExtended.reserve( numSurfaces );

std::vector<MaterialSurface> processedMaterialSurfaces;
processedMaterialSurfaces.reserve( numSurfaces );

// std::unordered_map<TriEdge, TriIndex> triEdges;

vec3_t worldBounds[2] = {};
materialSystem.buildOneShader = false;
vec3_t worldBounds[2] = {};
vec3_t worldBoundsExtended[2] = {};
ClearBounds( worldBounds[0], worldBounds[1] );
ClearBounds( worldBoundsExtended[0], worldBoundsExtended[1] );
for ( int i = 0; i < numSurfaces; i++ ) {
bspSurface_t* surface = rendererSurfaces[i];

Expand Down Expand Up @@ -476,20 +575,157 @@ std::vector<MaterialSurface> OptimiseMapGeometryMaterial(bspSurface_t** renderer
VectorCopy( ( ( srfGeneric_t* ) surface->data )->origin, srf.origin );
srf.radius = ( ( srfGeneric_t* ) surface->data )->radius;

BoundsAdd( worldBounds[0], worldBounds[1],
( ( srfGeneric_t* ) surface->data )->bounds[0], ( ( srfGeneric_t* ) surface->data )->bounds[1] );

materialSystem.GenerateMaterial( &srf );

materialSurfaces.emplace_back( srf );
static const float MAX_NORMAL_SURFACE_DISTANCE = 65536.0f * sqrtf( 2.0f );
if ( VectorLength( ( ( srfGeneric_t* ) surface->data )->bounds[0] ) > MAX_NORMAL_SURFACE_DISTANCE
|| VectorLength( ( ( srfGeneric_t* ) surface->data )->bounds[1] ) > MAX_NORMAL_SURFACE_DISTANCE ) {
BoundsAdd( worldBoundsExtended[0], worldBoundsExtended[1],
( ( srfGeneric_t* ) surface->data )->bounds[0], ( ( srfGeneric_t* ) surface->data )->bounds[1] );

materialSurfacesExtended.emplace_back( srf );
} else {
BoundsAdd( worldBounds[0], worldBounds[1],
( ( srfGeneric_t* ) surface->data )->bounds[0], ( ( srfGeneric_t* ) surface->data )->bounds[1] );

materialSurfaces.emplace_back( srf );
}
}

vec3_t fullWorldBounds[2];
VectorCopy( worldBounds[0], fullWorldBounds[0] );
VectorCopy( worldBounds[1], fullWorldBounds[1] );
BoundsAdd( fullWorldBounds[0], fullWorldBounds[1], worldBoundsExtended[0], worldBoundsExtended[1] );

materialSystem.SetWorldBounds( fullWorldBounds );

materialSystem.buildOneShader = true;

/* GenerateWorldMaterialsBuffer() must be called before the surface merging loop, because it will compare the UBO offsets,
which are set by this call */
materialSystem.GenerateWorldMaterialsBuffer();

std::vector<MaterialSurface> processedMaterialSurfaces;
processedMaterialSurfaces.reserve( numSurfaces );

std::function<bool( const MaterialSurface&, const MaterialSurface& )>
materialSurfaceSort = []( const MaterialSurface& lhs, const MaterialSurface& rhs ) {
if ( lhs.stages < rhs.stages ) {
return true;
} else if ( lhs.stages > rhs.stages ) {
return false;
}

for ( uint8_t stage = 0; stage < lhs.stages; stage++ ) {
if ( lhs.materialPackIDs[stage] < rhs.materialPackIDs[stage] ) {
return true;
} else if ( lhs.materialPackIDs[stage] > rhs.materialPackIDs[stage] ) {
return false;
}

if ( lhs.materialIDs[stage] < rhs.materialIDs[stage] ) {
return true;
} else if ( lhs.materialIDs[stage] > rhs.materialIDs[stage] ) {
return false;
}

shaderStage_t* pStage = lhs.shaderStages[stage];
pStage = pStage->materialRemappedStage ? pStage->materialRemappedStage : pStage;

const uint32_t surfaceMaterialID =
pStage->materialOffset + pStage->variantOffsets[lhs.shaderVariant[stage]];

pStage = rhs.shaderStages[stage];
pStage = pStage->materialRemappedStage ? pStage->materialRemappedStage : pStage;

const uint32_t surfaceMaterialID2 =
pStage->materialOffset + pStage->variantOffsets[rhs.shaderVariant[stage]];

if ( surfaceMaterialID < surfaceMaterialID2 ) {
return true;
} else if ( surfaceMaterialID > surfaceMaterialID2 ) {
return false;
}

uint32_t texData = lhs.texDataDynamic[stage]
? ( lhs.texDataIDs[stage] + materialSystem.GetTexDataSize() ) << TEX_BUNDLE_BITS
: lhs.texDataIDs[stage] << TEX_BUNDLE_BITS;
texData |= ( HasLightMap( &lhs ) ? GetLightMapNum( &lhs ) : 255 ) << LIGHTMAP_BITS;

uint32_t texData2 = rhs.texDataDynamic[stage]
? ( rhs.texDataIDs[stage] + materialSystem.GetTexDataSize() ) << TEX_BUNDLE_BITS
: rhs.texDataIDs[stage] << TEX_BUNDLE_BITS;
texData2 |= ( HasLightMap( &rhs ) ? GetLightMapNum( &rhs ) : 255 ) << LIGHTMAP_BITS;

if ( texData < texData2 ) {
return true;
} else if ( texData > texData2 ) {
return false;
}
}

return lhs.firstIndex < rhs.firstIndex;
};

glIndex_t* idxs = ( glIndex_t* ) ri.Hunk_AllocateTempMemory( numIndicesIn * sizeof( glIndex_t ) );
uint32_t numIndices = 0;

uint32_t gridSize[3] {};
uint32_t cellSize[3];
vec3_t worldSize;
VectorSubtract( worldBounds[1], worldBounds[0], worldSize );
for ( int i = 0; i < 3; i++ ) {
if ( worldSize[i] < 32768 ) {
cellSize[i] = 1024;
} else {
cellSize[i] = 2048;
}

gridSize[i] = ( worldSize[i] + cellSize[i] - 1 ) / cellSize[i];
}

Log::Debug( "BSP material surface grid cell size: %u %u %u", cellSize[0], cellSize[1], cellSize[2] );

std::sort( materialSurfaces.begin(), materialSurfaces.end(), materialSurfaceSort );

OptimiseMaterialSurfaces( materialSurfaces, gridSize, cellSize, world->nodes[0].mins,
materialSurfaceSort,
vertices, indices, idxs, processedMaterialSurfaces, numIndices );

/* On some maps surfaces go outside the 64k range, used for background and sky brushes.
We process them separately so we can use a larger cell size there, otherwise we'll run out of memory.
The regular surfaces can continue using samller grid sizes this way,
resulting in actually useable bounding spheres */
if( materialSurfacesExtended.size() ) {
VectorSubtract( worldBoundsExtended[1], worldBoundsExtended[0], worldSize );
for ( int i = 0; i < 3; i++ ) {
cellSize[i] = std::max( 2048.0f, worldSize[i] / 32.0f );

gridSize[i] = ( worldSize[i] + cellSize[i] - 1 ) / cellSize[i];
}

Log::Debug( "BSP material surface extended grid cell size: %u %u %u", cellSize[0], cellSize[1], cellSize[2] );

std::sort( materialSurfacesExtended.begin(), materialSurfacesExtended.end(), materialSurfaceSort );

OptimiseMaterialSurfaces( materialSurfacesExtended, gridSize, cellSize, world->nodes[0].mins,
materialSurfaceSort,
vertices, indices, idxs, processedMaterialSurfaces, numIndices );
}

materialSystem.GeneratePortalBoundingSpheres();
materialSystem.SetWorldBounds( worldBounds );
materialSystem.GenerateWorldCommandBuffer( materialSurfaces );
materialSystem.GenerateWorldCommandBuffer( processedMaterialSurfaces );

vertexAttributeSpec_t attrs[] {
{ ATTR_INDEX_POSITION, GL_FLOAT, GL_FLOAT, &vertices[0].xyz, 3, sizeof( *vertices ), 0 },
{ ATTR_INDEX_COLOR, GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE, &vertices[0].lightColor, 4, sizeof( *vertices ), ATTR_OPTION_NORMALIZE },
{ ATTR_INDEX_QTANGENT, GL_SHORT, GL_SHORT, &vertices[0].qtangent, 4, sizeof( *vertices ), ATTR_OPTION_NORMALIZE },
{ ATTR_INDEX_TEXCOORD, GL_FLOAT, GL_HALF_FLOAT, &vertices[0].st, 4, sizeof( *vertices ), 0 },
};

geometryCache.AddMapGeometry( numVerticesIn, numIndices, std::begin( attrs ), std::end( attrs ), idxs );
Comment thread
slipher marked this conversation as resolved.

ri.Hunk_FreeTempMemory( idxs );

return materialSurfaces;
}
31 changes: 6 additions & 25 deletions src/engine/renderer/GeometryOptimiser.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,30 +41,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

static const uint32_t MAX_MATERIAL_SURFACE_TRIS = 64;
static const uint32_t MAX_MATERIAL_SURFACE_INDEXES = 3 * MAX_MATERIAL_SURFACE_TRIS;
static const uint32_t MAX_MATERIAL_SURFACE_DISTANCE = 256;

struct TriEdge {
enum State : uint32_t {
MERGEABLE_TRUE = BIT( 27 ),
MERGEABLE_FALSE = BIT( 28 ),
MERGEABLE_NOT_PROCESSED = BIT( 29 ),
NONE = BIT( 30 )
};

uint32_t index1;
uint32_t index2;
};

struct TriEdgeHasher {
size_t operator()( const TriEdge& triEdge ) {
return std::hash<uint32_t>{} ( triEdge.index1 ) ^ ( std::hash<uint32_t>{} ( triEdge.index2 ) << 16 );
}
};

struct TriIndex {
int tri1 = -1;
int tri2 = -1;
};

struct MapVertHasher {
size_t operator()( const srfVert_t& vert ) const {
Expand Down Expand Up @@ -97,6 +73,10 @@ struct MapVertEqual {
}
};

struct SurfaceIndexes {
glIndex_t idxs[MAX_MATERIAL_SURFACE_INDEXES];
};

void MarkShaderBuildNONE( const shaderStage_t* );
void MarkShaderBuildNOP( const shaderStage_t* );
void MarkShaderBuildGeneric3D( const shaderStage_t* pStage );
Expand All @@ -120,6 +100,7 @@ void OptimiseMapGeometryCore( world_t* world, bspSurface_t** rendererSurfaces, i
void MergeLeafSurfacesCore( world_t* world, bspSurface_t** rendererSurfaces, int numSurfaces );
void MergeDuplicateVertices( bspSurface_t** rendererSurfaces, int numSurfaces, srfVert_t* vertices, int numVerticesIn,
glIndex_t* indices, int numIndicesIn, int& numVerticesOut, int& numIndicesOut );
std::vector<MaterialSurface> OptimiseMapGeometryMaterial( bspSurface_t** rendererSurfaces, int numSurfaces );
std::vector<MaterialSurface> OptimiseMapGeometryMaterial( world_t* world, bspSurface_t** rendererSurfaces, int numSurfaces,
const srfVert_t* vertices, const int numVerticesIn, const glIndex_t* indices, const int numIndicesIn );

#endif // GEOMETRY_OPTIMISER_H
Loading