Skip to content

Commit d33d5ca

Browse files
committed
Add GeometryOptimiser
Move some surface merging stuff there from `tr_bsp`, add some definitions for material system use.
1 parent fa67b54 commit d33d5ca

File tree

5 files changed

+362
-112
lines changed

5 files changed

+362
-112
lines changed

src.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ set(RENDERERLIST
9494
${ENGINE_DIR}/renderer/tr_font.cpp
9595
${ENGINE_DIR}/renderer/GeometryCache.cpp
9696
${ENGINE_DIR}/renderer/GeometryCache.h
97+
${ENGINE_DIR}/renderer/GeometryOptimiser.cpp
98+
${ENGINE_DIR}/renderer/GeometryOptimiser.h
9799
${ENGINE_DIR}/renderer/InternalImage.cpp
98100
${ENGINE_DIR}/renderer/InternalImage.h
99101
${ENGINE_DIR}/renderer/Material.cpp
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
/*
2+
===========================================================================
3+
Copyright (C) 1999-2005 Id Software, Inc.
4+
Copyright (C) 2006-2011 Robert Beckebans <[email protected]>
5+
Copyright (C) 2009 Peter McNeill <[email protected]>
6+
7+
This file is part of Daemon source code.
8+
9+
Daemon source code is free software; you can redistribute it
10+
and/or modify it under the terms of the GNU General Public License as
11+
published by the Free Software Foundation; either version 2 of the License,
12+
or (at your option) any later version.
13+
14+
Daemon source code is distributed in the hope that it will be
15+
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
GNU General Public License for more details.
18+
19+
You should have received a copy of the GNU General Public License
20+
along with Daemon source code; if not, write to the Free Software
21+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22+
===========================================================================
23+
*/
24+
// GeometryOptimiser.cpp
25+
26+
#include "common/Common.h"
27+
28+
#include "GeometryOptimiser.h"
29+
30+
static int LeafSurfaceCompare( const void* a, const void* b ) {
31+
bspSurface_t* aa, * bb;
32+
33+
aa = *( bspSurface_t** ) a;
34+
bb = *( bspSurface_t** ) b;
35+
36+
// shader first
37+
if ( aa->shader < bb->shader ) {
38+
return -1;
39+
}
40+
41+
else if ( aa->shader > bb->shader ) {
42+
return 1;
43+
}
44+
45+
// by lightmap
46+
if ( aa->lightmapNum < bb->lightmapNum ) {
47+
return -1;
48+
}
49+
50+
else if ( aa->lightmapNum > bb->lightmapNum ) {
51+
return 1;
52+
}
53+
54+
if ( aa->fogIndex < bb->fogIndex ) {
55+
return -1;
56+
} else if ( aa->fogIndex > bb->fogIndex ) {
57+
return 1;
58+
}
59+
60+
// sort by leaf
61+
if ( aa->interactionBits < bb->interactionBits ) {
62+
return -1;
63+
} else if ( aa->interactionBits > bb->interactionBits ) {
64+
return 1;
65+
}
66+
67+
// sort by leaf marksurfaces index to increase the likelihood of multidraw merging in the backend
68+
if ( aa->lightCount < bb->lightCount ) {
69+
return -1;
70+
} else if ( aa->lightCount > bb->lightCount ) {
71+
return 1;
72+
}
73+
return 0;
74+
}
75+
76+
bspSurface_t** OptimiseMapGeometryCore( world_t* world, int &numSurfaces ) {
77+
// mark matching surfaces
78+
for ( int i = 0; i < world->numnodes - world->numDecisionNodes; i++ ) {
79+
bspNode_t *leaf = world->nodes + world->numDecisionNodes + i;
80+
81+
for ( int j = 0; j < leaf->numMarkSurfaces; j++ ) {
82+
bspSurface_t* surf1 = world->markSurfaces[leaf->firstMarkSurface + j];
83+
84+
if ( surf1->viewCount != -1 ) {
85+
continue;
86+
}
87+
88+
if ( *surf1->data != surfaceType_t::SF_GRID && *surf1->data != surfaceType_t::SF_TRIANGLES
89+
&& *surf1->data != surfaceType_t::SF_FACE ) {
90+
continue;
91+
}
92+
93+
shader_t* shader1 = surf1->shader;
94+
95+
if ( shader1->isPortal || shader1->autoSpriteMode != 0 ) {
96+
continue;
97+
}
98+
99+
int fogIndex1 = surf1->fogIndex;
100+
int lightMapNum1 = surf1->lightmapNum;
101+
surf1->viewCount = surf1 - world->surfaces;
102+
surf1->lightCount = j;
103+
surf1->interactionBits = i;
104+
105+
bool merged = false;
106+
for ( int k = j + 1; k < leaf->numMarkSurfaces; k++ ) {
107+
bspSurface_t* surf2 = world->markSurfaces[leaf->firstMarkSurface + k];
108+
109+
if ( surf2->viewCount != -1 ) {
110+
continue;
111+
}
112+
113+
if ( *surf2->data != surfaceType_t::SF_GRID && *surf2->data != surfaceType_t::SF_TRIANGLES
114+
&& *surf2->data != surfaceType_t::SF_FACE ) {
115+
continue;
116+
}
117+
118+
shader_t* shader2 = surf2->shader;
119+
int fogIndex2 = surf2->fogIndex;
120+
int lightMapNum2 = surf2->lightmapNum;
121+
if ( shader1 != shader2 || fogIndex1 != fogIndex2 || lightMapNum1 != lightMapNum2 ) {
122+
continue;
123+
}
124+
125+
surf2->viewCount = surf1->viewCount;
126+
surf2->lightCount = k;
127+
surf2->interactionBits = i;
128+
merged = true;
129+
}
130+
131+
if ( !merged ) {
132+
surf1->viewCount = -1;
133+
surf1->lightCount = -1;
134+
// don't clear the leaf number so
135+
// surfaces that arn't merged are placed
136+
// closer to other leafs in the vbo
137+
}
138+
}
139+
}
140+
141+
bspSurface_t** coreSurfaces = ( bspSurface_t** ) ri.Hunk_AllocateTempMemory( sizeof( bspSurface_t* ) * numSurfaces );
142+
143+
numSurfaces = 0;
144+
for ( int k = 0; k < world->numSurfaces; k++ ) {
145+
bspSurface_t* surface = &world->surfaces[k];
146+
147+
if ( surface->shader->isPortal ) {
148+
// HACK: don't use VBO because when adding a portal we have to read back the verts CPU-side
149+
continue;
150+
}
151+
152+
if ( surface->shader->autoSpriteMode != 0 ) {
153+
// don't use VBO because verts are rewritten each time based on view origin
154+
continue;
155+
}
156+
157+
if ( *surface->data == surfaceType_t::SF_FACE || *surface->data == surfaceType_t::SF_GRID
158+
|| *surface->data == surfaceType_t::SF_TRIANGLES ) {
159+
coreSurfaces[numSurfaces++] = surface;
160+
}
161+
}
162+
163+
qsort( coreSurfaces, numSurfaces, sizeof( bspSurface_t* ), LeafSurfaceCompare );
164+
165+
return coreSurfaces;
166+
}
167+
168+
/*
169+
===========================================================================
170+
171+
Daemon BSD Source Code
172+
Copyright (c) 2025 Daemon Developers
173+
All rights reserved.
174+
175+
This file is part of the Daemon BSD Source Code (Daemon Source Code).
176+
177+
Redistribution and use in source and binary forms, with or without
178+
modification, are permitted provided that the following conditions are met:
179+
* Redistributions of source code must retain the above copyright
180+
notice, this list of conditions and the following disclaimer.
181+
* Redistributions in binary form must reproduce the above copyright
182+
notice, this list of conditions and the following disclaimer in the
183+
documentation and/or other materials provided with the distribution.
184+
* Neither the name of the Daemon developers nor the
185+
names of its contributors may be used to endorse or promote products
186+
derived from this software without specific prior written permission.
187+
188+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
189+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
190+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
191+
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY
192+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
193+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
194+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
195+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
196+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
197+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
198+
199+
===========================================================================
200+
*/
201+
202+
static void ProcessMaterialSurface( MaterialSurface& surface, std::vector<MaterialSurface>& materialSurfaces,
203+
std::vector<MaterialSurface>& processedMaterialSurfaces,
204+
srfVert_t* verts, glIndex_t* indices ) {
205+
if ( surface.count == MAX_MATERIAL_SURFACE_TRIS ) {
206+
materialSurfaces.emplace_back( surface );
207+
}
208+
209+
while ( surface.count > MAX_MATERIAL_SURFACE_TRIS ) {
210+
MaterialSurface srf = surface;
211+
212+
srf.count = MAX_MATERIAL_SURFACE_TRIS;
213+
surface.count -= MAX_MATERIAL_SURFACE_TRIS;
214+
surface.firstIndex += MAX_MATERIAL_SURFACE_TRIS;
215+
216+
processedMaterialSurfaces.push_back( srf );
217+
}
218+
}
219+
220+
std::vector<MaterialSurface> OptimiseMapGeometryMaterial( world_t* world, int numSurfaces ) {
221+
std::vector<MaterialSurface> materialSurfaces;
222+
materialSurfaces.reserve( numSurfaces );
223+
224+
std::vector<MaterialSurface> processedMaterialSurfaces;
225+
processedMaterialSurfaces.reserve( numSurfaces );
226+
227+
int surfaceIndex = 0;
228+
for ( int k = 0; k < world->numSurfaces; k++ ) {
229+
bspSurface_t* surface = &world->surfaces[k];
230+
231+
if ( surface->shader->isPortal ) {
232+
continue;
233+
}
234+
235+
if ( surface->shader->autoSpriteMode ) {
236+
continue;
237+
}
238+
239+
if ( *surface->data == surfaceType_t::SF_FACE || *surface->data == surfaceType_t::SF_GRID
240+
|| *surface->data == surfaceType_t::SF_TRIANGLES ) {
241+
MaterialSurface srf {};
242+
243+
srf.shader = surface->shader;
244+
srf.bspSurface = true;
245+
srf.fog = surface->fogIndex;
246+
247+
switch ( *surface->data ) {
248+
case surfaceType_t::SF_FACE:
249+
srf.firstIndex = ( ( srfSurfaceFace_t* ) surface->data )->firstIndex;
250+
srf.count = ( ( srfSurfaceFace_t* ) surface->data )->numTriangles;
251+
srf.verts = ( ( srfSurfaceFace_t* ) surface->data )->verts;
252+
srf.tris = ( ( srfSurfaceFace_t* ) surface->data )->triangles;
253+
break;
254+
case surfaceType_t::SF_GRID:
255+
srf.firstIndex = ( ( srfGridMesh_t* ) surface->data )->firstIndex;
256+
srf.count = ( ( srfGridMesh_t* ) surface->data )->numTriangles;
257+
srf.verts = ( ( srfGridMesh_t* ) surface->data )->verts;
258+
srf.tris = ( ( srfGridMesh_t* ) surface->data )->triangles;
259+
break;
260+
case surfaceType_t::SF_TRIANGLES:
261+
srf.firstIndex = ( ( srfTriangles_t* ) surface->data )->firstIndex;
262+
srf.count = ( ( srfTriangles_t* ) surface->data )->numTriangles;
263+
srf.verts = ( ( srfTriangles_t* ) surface->data )->verts;
264+
srf.tris = ( ( srfTriangles_t* ) surface->data )->triangles;
265+
break;
266+
default:
267+
break;
268+
}
269+
270+
materialSurfaces.emplace_back( srf );
271+
surfaceIndex++;
272+
}
273+
}
274+
275+
while ( materialSurfaces.size() ) {
276+
ProcessMaterialSurface( materialSurfaces.front(), materialSurfaces, processedMaterialSurfaces, verts, indices );
277+
}
278+
279+
// qsort( materialSurfaces, numSurfaces, sizeof( bspSurface_t* ), LeafSurfaceCompare );
280+
281+
return materialSurfaces;
282+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
===========================================================================
3+
4+
Daemon BSD Source Code
5+
Copyright (c) 2025 Daemon Developers
6+
All rights reserved.
7+
8+
This file is part of the Daemon BSD Source Code (Daemon Source Code).
9+
10+
Redistribution and use in source and binary forms, with or without
11+
modification, are permitted provided that the following conditions are met:
12+
* Redistributions of source code must retain the above copyright
13+
notice, this list of conditions and the following disclaimer.
14+
* Redistributions in binary form must reproduce the above copyright
15+
notice, this list of conditions and the following disclaimer in the
16+
documentation and/or other materials provided with the distribution.
17+
* Neither the name of the Daemon developers nor the
18+
names of its contributors may be used to endorse or promote products
19+
derived from this software without specific prior written permission.
20+
21+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24+
DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY
25+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
32+
===========================================================================
33+
*/
34+
// GeometryOptimiser.h
35+
36+
#include "tr_local.h"
37+
#include "Material.h"
38+
39+
#ifndef GEOMETRY_OPTIMISER_H
40+
#define GEOMETRY_OPTIMISER_H
41+
42+
#define MAX_MATERIAL_SURFACE_TRIS 64
43+
#define MAX_MATERIAL_SURFACE_DISTANCE 256
44+
45+
bspSurface_t** OptimiseMapGeometryCore( world_t* world, int &numSurfaces );
46+
std::vector<MaterialSurface> OptimiseMapGeometryMaterial( world_t* world, int numSurfaces );
47+
48+
#endif // GEOMETRY_OPTIMISER_H

src/engine/renderer/Material.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,27 @@ struct DrawCommand {
7676
}
7777
};
7878

79+
struct MaterialSurface {
80+
shader_t* shader;
81+
bool bspSurface;
82+
int fog;
83+
int portalNum = -1;
84+
85+
GLuint firstIndex;
86+
GLuint count;
87+
88+
srfVert_t* verts;
89+
srfTriangle_t* tris;
90+
91+
uint32_t materialPackIDs[MAX_SHADER_STAGES];
92+
uint32_t materialIDs[MAX_SHADER_STAGES];
93+
94+
uint32_t drawCommandIDs[MAX_SHADER_STAGES];
95+
uint32_t texDataIDs[MAX_SHADER_STAGES];
96+
bool texDataDynamic[MAX_SHADER_STAGES];
97+
uint32_t shaderVariant[MAX_SHADER_STAGES];
98+
};
99+
79100
struct Material {
80101
uint32_t materialsSSBOOffset = 0;
81102

0 commit comments

Comments
 (0)