2021-02-01 20:05:04 +01:00
# include "vk_brush.h"
2021-01-24 04:38:00 +01:00
2021-01-23 19:54:44 +01:00
# include "vk_core.h"
2021-02-01 20:05:04 +01:00
# include "vk_const.h"
2021-01-23 19:54:44 +01:00
# include "vk_buffer.h"
2021-01-24 04:38:00 +01:00
# include "vk_pipeline.h"
# include "vk_framectl.h"
# include "vk_math.h"
2023-10-20 17:18:55 +02:00
# include "r_textures.h"
2021-01-31 00:31:23 +01:00
# include "vk_lightmap.h"
2021-02-01 20:05:04 +01:00
# include "vk_scene.h"
2021-02-08 19:57:27 +01:00
# include "vk_render.h"
2022-07-09 21:31:46 +02:00
# include "vk_geometry.h"
2021-04-12 20:07:41 +02:00
# include "vk_light.h"
2021-11-27 08:59:48 +01:00
# include "vk_mapents.h"
2023-03-25 18:45:37 +01:00
# include "r_speeds.h"
2023-09-07 17:16:47 +02:00
# include "vk_staging.h"
2023-08-29 18:31:57 +02:00
# include "vk_logs.h"
2023-09-08 20:12:35 +02:00
# include "profiler.h"
2021-01-24 04:38:00 +01:00
# include "ref_params.h"
# include "eiface.h"
# include <math.h>
# include <memory.h>
2021-01-23 19:54:44 +01:00
2023-06-13 18:39:50 +02:00
# define MODULE_NAME "brush"
2023-08-29 18:31:57 +02:00
# define LOG_MODULE LogModule_Brush
2023-06-13 18:39:50 +02:00
2022-02-03 06:32:10 +01:00
typedef struct vk_brush_model_s {
2023-05-26 19:45:44 +02:00
model_t * engine_model ;
r_geometry_range_t geometry ;
2022-02-03 06:32:10 +01:00
vk_render_model_t render_model ;
2023-03-03 08:23:31 +01:00
int * surface_to_geometry_index ;
2023-04-28 21:35:48 +02:00
int * animated_indexes ;
int animated_indexes_count ;
2023-05-31 18:39:27 +02:00
matrix4x4 prev_transform ;
2023-08-28 19:12:02 +02:00
float prev_time ;
2023-06-05 21:53:25 +02:00
struct {
int surfaces_count ;
const int * surfaces_indices ;
r_geometry_range_t geometry ;
vk_render_model_t render_model ;
} water ;
2022-02-03 06:32:10 +01:00
} vk_brush_model_t ;
2023-06-05 21:53:25 +02:00
typedef struct {
int num_surfaces , num_vertices , num_indices ;
int max_texture_id ;
int water_surfaces ;
int animated_count ;
int water_vertices ;
int water_indices ;
} model_sizes_t ;
2023-09-04 19:48:58 +02:00
typedef struct conn_edge_s {
int first_surface ;
int count ;
} conn_edge_t ;
typedef struct linked_value_s {
int value , link ;
} linked_value_t ;
# define MAX_VERTEX_SURFACES 16
typedef struct conn_vertex_s {
int count ;
linked_value_t surfs [ MAX_VERTEX_SURFACES ] ;
} conn_vertex_t ;
2021-01-23 19:54:44 +01:00
static struct {
2021-02-10 19:33:44 +01:00
struct {
2023-05-26 19:45:44 +02:00
int total_vertices , total_indices ;
2023-03-25 18:45:37 +01:00
int models_drawn ;
int water_surfaces_drawn ;
int water_polys_drawn ;
2021-02-10 19:33:44 +01:00
} stat ;
2021-02-10 01:42:08 +01:00
int rtable [ MOD_FRAMES ] [ MOD_FRAMES ] ;
2023-05-26 19:45:44 +02:00
// Unfortunately the engine only tracks the toplevel worldmodel. *xx submodels, while having their own entities and models, are not lifetime-tracked.
// I.e. the engine doesn't call Mod_ProcessRenderData() on them, so we don't directly know when to create or destroy them.
// Therefore, we need to track them manually and destroy them based on some other external event, e.g. Mod_ProcessRenderData(worldmodel)
vk_brush_model_t * models [ MAX_MODELS ] ;
int models_count ;
2023-08-31 18:11:17 +02:00
# define MAX_ANIMATED_TEXTURES 256
int updated_textures [ MAX_ANIMATED_TEXTURES ] ;
2023-09-04 19:48:58 +02:00
// Smoothed normals comptutation
// Connectome for edges and vertices
struct {
int edges_capacity ;
conn_edge_t * edges ;
int vertices_capacity ;
conn_vertex_t * vertices ;
} conn ;
2021-02-06 20:38:08 +01:00
} g_brush ;
2021-01-23 19:54:44 +01:00
2021-02-10 01:42:08 +01:00
void VK_InitRandomTable ( void )
{
int tu , tv ;
// make random predictable
gEngine . COM_SetRandomSeed ( 255 ) ;
for ( tu = 0 ; tu < MOD_FRAMES ; tu + + )
{
for ( tv = 0 ; tv < MOD_FRAMES ; tv + + )
{
g_brush . rtable [ tu ] [ tv ] = gEngine . COM_RandomLong ( 0 , 0x7FFF ) ;
}
}
gEngine . COM_SetRandomSeed ( 0 ) ;
}
2021-02-01 20:05:04 +01:00
qboolean VK_BrushInit ( void )
2021-01-23 19:54:44 +01:00
{
2021-02-10 01:42:08 +01:00
VK_InitRandomTable ( ) ;
2023-06-14 20:23:09 +02:00
R_SPEEDS_COUNTER ( g_brush . stat . models_drawn , " drawn " , kSpeedsMetricCount ) ;
R_SPEEDS_COUNTER ( g_brush . stat . water_surfaces_drawn , " water.surfaces " , kSpeedsMetricCount ) ;
R_SPEEDS_COUNTER ( g_brush . stat . water_polys_drawn , " water.polys " , kSpeedsMetricCount ) ;
2023-03-25 18:45:37 +01:00
2021-01-23 19:54:44 +01:00
return true ;
}
2023-09-04 19:48:58 +02:00
void VK_BrushShutdown ( void ) {
if ( g_brush . conn . edges )
Mem_Free ( g_brush . conn . edges ) ;
2021-01-23 19:54:44 +01:00
}
2021-06-14 18:19:21 +02:00
// speed up sin calculations
static const float r_turbsin [ ] =
{
# include "warpsin.h"
} ;
# define SUBDIVIDE_SIZE 64
# define TURBSCALE ( 256.0f / ( M_PI2 ))
2023-06-05 21:53:25 +02:00
static void addWarpVertIndCounts ( const msurface_t * warp , int * num_vertices , int * num_indices ) {
for ( glpoly_t * p = warp - > polys ; p ; p = p - > next ) {
const int triangles = p - > numverts - 2 ;
* num_vertices + = p - > numverts ;
* num_indices + = triangles * 3 ;
}
}
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
typedef struct {
2023-08-28 19:12:02 +02:00
float prev_time ;
2023-06-05 21:53:25 +02:00
float scale ;
const msurface_t * warp ;
qboolean reverse ;
vk_vertex_t * dst_vertices ;
uint16_t * dst_indices ;
vk_render_geometry_t * dst_geometry ;
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
int * out_vertex_count , * out_index_count ;
} compute_water_polys_t ;
2023-03-25 18:45:37 +01:00
2023-06-05 21:53:25 +02:00
static void brushComputeWaterPolys ( compute_water_polys_t args ) {
const float time = gpGlobals - > time ;
2023-01-22 00:45:29 +01:00
2021-06-14 20:22:07 +02:00
# define MAX_WATER_VERTICES 16
vk_vertex_t poly_vertices [ MAX_WATER_VERTICES ] ;
2023-06-05 21:53:25 +02:00
// FIXME unused? const qboolean useQuads = FBitSet( warp->flags, SURF_DRAWTURB_QUADS );
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
ASSERT ( args . warp - > polys ) ;
2021-06-14 18:19:21 +02:00
// set the current waveheight
// FIXME VK if( warp->polys->verts[0][2] >= RI.vieworg[2] )
// waveHeight = -ent->curstate.scale;
// else
2023-06-05 21:53:25 +02:00
// waveHeight = ent->curstate.scale;
const float scale = args . scale ;
2021-06-14 18:19:21 +02:00
// reset fog color for nonlightmapped water
// FIXME VK GL_ResetFogColor();
2023-06-05 21:53:25 +02:00
int vertices = 0 ;
int indices = 0 ;
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
for ( glpoly_t * p = args . warp - > polys ; p ; p = p - > next ) {
2021-06-14 20:22:07 +02:00
ASSERT ( p - > numverts < = MAX_WATER_VERTICES ) ;
2023-06-05 21:53:25 +02:00
float * v ;
if ( args . reverse )
2021-06-14 18:19:21 +02:00
v = p - > verts [ 0 ] + ( p - > numverts - 1 ) * VERTEXSIZE ;
else v = p - > verts [ 0 ] ;
2023-06-05 21:53:25 +02:00
for ( int i = 0 ; i < p - > numverts ; i + + )
2021-06-14 18:19:21 +02:00
{
2023-06-05 21:53:25 +02:00
float nv , prev_nv ;
if ( scale )
2021-06-14 18:19:21 +02:00
{
2021-06-14 20:22:07 +02:00
nv = r_turbsin [ ( int ) ( time * 160.0f + v [ 1 ] + v [ 0 ] ) & 255 ] + 8.0f ;
nv = ( r_turbsin [ ( int ) ( v [ 0 ] * 5.0f + time * 171.0f - v [ 1 ] ) & 255 ] + 8.0f ) * 0.8f + nv ;
2023-06-05 21:53:25 +02:00
nv = nv * scale + v [ 2 ] ;
2023-01-22 00:45:29 +01:00
2023-08-28 19:12:02 +02:00
prev_nv = r_turbsin [ ( int ) ( args . prev_time * 160.0f + v [ 1 ] + v [ 0 ] ) & 255 ] + 8.0f ;
prev_nv = ( r_turbsin [ ( int ) ( v [ 0 ] * 5.0f + args . prev_time * 171.0f - v [ 1 ] ) & 255 ] + 8.0f ) * 0.8f + prev_nv ;
2023-06-05 21:53:25 +02:00
prev_nv = prev_nv * scale + v [ 2 ] ;
2021-06-14 18:19:21 +02:00
}
2023-02-01 21:35:15 +01:00
else prev_nv = nv = v [ 2 ] ;
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
const float os = v [ 3 ] ;
const float ot = v [ 4 ] ;
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
float s = os + r_turbsin [ ( int ) ( ( ot * 0.125f + gpGlobals - > time ) * TURBSCALE ) & 255 ] ;
2021-06-14 18:19:21 +02:00
s * = ( 1.0f / SUBDIVIDE_SIZE ) ;
2023-06-05 21:53:25 +02:00
float t = ot + r_turbsin [ ( int ) ( ( os * 0.125f + gpGlobals - > time ) * TURBSCALE ) & 255 ] ;
2021-06-14 18:19:21 +02:00
t * = ( 1.0f / SUBDIVIDE_SIZE ) ;
2021-06-14 20:22:07 +02:00
poly_vertices [ i ] . pos [ 0 ] = v [ 0 ] ;
poly_vertices [ i ] . pos [ 1 ] = v [ 1 ] ;
poly_vertices [ i ] . pos [ 2 ] = nv ;
2021-06-14 18:19:21 +02:00
2023-01-22 00:45:29 +01:00
poly_vertices [ i ] . prev_pos [ 0 ] = v [ 0 ] ;
poly_vertices [ i ] . prev_pos [ 1 ] = v [ 1 ] ;
poly_vertices [ i ] . prev_pos [ 2 ] = prev_nv ;
2021-06-14 20:22:07 +02:00
poly_vertices [ i ] . gl_tc [ 0 ] = s ;
poly_vertices [ i ] . gl_tc [ 1 ] = t ;
2021-06-14 18:19:21 +02:00
2021-06-14 20:22:07 +02:00
poly_vertices [ i ] . lm_tc [ 0 ] = 0 ;
poly_vertices [ i ] . lm_tc [ 1 ] = 0 ;
2021-06-14 18:19:21 +02:00
2023-02-26 03:38:22 +01:00
Vector4Set ( poly_vertices [ i ] . color , 255 , 255 , 255 , 255 ) ;
2021-06-14 20:22:07 +02:00
poly_vertices [ i ] . normal [ 0 ] = 0 ;
poly_vertices [ i ] . normal [ 1 ] = 0 ;
poly_vertices [ i ] . normal [ 2 ] = 0 ;
2021-06-14 18:19:21 +02:00
if ( i > 1 ) {
2021-06-14 20:22:07 +02:00
vec3_t e0 , e1 , normal ;
VectorSubtract ( poly_vertices [ i - 1 ] . pos , poly_vertices [ 0 ] . pos , e0 ) ;
VectorSubtract ( poly_vertices [ i ] . pos , poly_vertices [ 0 ] . pos , e1 ) ;
CrossProduct ( e1 , e0 , normal ) ;
VectorAdd ( normal , poly_vertices [ 0 ] . normal , poly_vertices [ 0 ] . normal ) ;
VectorAdd ( normal , poly_vertices [ i ] . normal , poly_vertices [ i ] . normal ) ;
VectorAdd ( normal , poly_vertices [ i - 1 ] . normal , poly_vertices [ i - 1 ] . normal ) ;
2023-06-05 21:53:25 +02:00
args . dst_indices [ indices + + ] = ( uint16_t ) ( vertices ) ;
args . dst_indices [ indices + + ] = ( uint16_t ) ( vertices + i - 1 ) ;
args . dst_indices [ indices + + ] = ( uint16_t ) ( vertices + i ) ;
2021-06-14 18:19:21 +02:00
}
2023-06-05 21:53:25 +02:00
if ( args . reverse )
2021-06-14 18:19:21 +02:00
v - = VERTEXSIZE ;
2021-08-11 20:43:33 +02:00
else
v + = VERTEXSIZE ;
2021-06-14 18:19:21 +02:00
}
2023-06-05 21:53:25 +02:00
for ( int i = 0 ; i < p - > numverts ; i + + )
2021-06-14 20:22:07 +02:00
VectorNormalize ( poly_vertices [ i ] . normal ) ;
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
memcpy ( args . dst_vertices + vertices , poly_vertices , sizeof ( vk_vertex_t ) * p - > numverts ) ;
vertices + = p - > numverts ;
2021-06-14 18:19:21 +02:00
}
2023-06-05 21:53:25 +02:00
// FIXME VK GL_SetupFogColorForSurfaces();
2021-06-14 18:19:21 +02:00
// Render
2023-06-05 21:53:25 +02:00
const int tex_id = args . warp - > texinfo - > texture - > gl_texturenum ;
2023-09-22 15:43:27 +02:00
const r_vk_material_t material = R_VkMaterialGetForTexture ( tex_id ) ;
2023-06-05 21:53:25 +02:00
* args . dst_geometry = ( vk_render_geometry_t ) {
2023-09-22 15:43:27 +02:00
. material = material ,
2023-09-19 19:20:04 +02:00
. ye_olde_texture = tex_id ,
2023-06-05 21:53:25 +02:00
. surf_deprecate = args . warp ,
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
. max_vertex = vertices ,
. element_count = indices ,
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
. emissive = { 0 , 0 , 0 } ,
} ;
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
RT_GetEmissiveForTexture ( args . dst_geometry - > emissive , tex_id ) ;
* args . out_vertex_count = vertices ;
* args . out_index_count = indices ;
2021-06-14 18:19:21 +02:00
2023-06-05 21:53:25 +02:00
g_brush . stat . water_surfaces_drawn + + ;
g_brush . stat . water_polys_drawn + = indices / 3 ;
2021-06-14 18:19:21 +02:00
}
2023-02-25 21:09:20 +01:00
static vk_render_type_e brushRenderModeToRenderType ( int render_mode ) {
switch ( render_mode ) {
case kRenderNormal : return kVkRenderTypeSolid ;
case kRenderTransColor : return kVkRenderType_A_1mA_RW ;
case kRenderTransTexture : return kVkRenderType_A_1mA_R ;
case kRenderGlow : return kVkRenderType_A_1mA_R ;
case kRenderTransAlpha : return kVkRenderType_AT ;
case kRenderTransAdd : return kVkRenderType_A_1_R ;
default : ASSERT ( ! " Unxpected render_mode " ) ;
}
return kVkRenderTypeSolid ;
}
2023-06-05 21:53:25 +02:00
#if 0 // TOO OLD
static void brushDrawWaterSurfaces ( const cl_entity_t * ent , const vec4_t color , const matrix4x4 transform ) {
2021-06-14 18:19:21 +02:00
const model_t * model = ent - > model ;
2023-06-05 21:53:25 +02:00
vec3_t mins , maxs ;
2021-06-14 20:22:07 +02:00
if ( ! VectorIsNull ( ent - > angles ) )
{
for ( int i = 0 ; i < 3 ; i + + )
{
mins [ i ] = ent - > origin [ i ] - model - > radius ;
maxs [ i ] = ent - > origin [ i ] + model - > radius ;
}
//rotated = true;
}
else
{
VectorAdd ( ent - > origin , model - > mins , mins ) ;
VectorAdd ( ent - > origin , model - > maxs , maxs ) ;
//rotated = false;
}
// if( R_CullBox( mins, maxs ))
// return;
2023-05-04 19:30:13 +02:00
VK_RenderModelDynamicBegin ( brushRenderModeToRenderType ( ent - > curstate . rendermode ) , color , transform , " %s water " , model - > name ) ;
2021-06-14 18:19:21 +02:00
// Iterate through all surfaces, find *TURB*
for ( int i = 0 ; i < model - > nummodelsurfaces ; i + + )
{
const msurface_t * surf = model - > surfaces + model - > firstmodelsurface + i ;
if ( ! FBitSet ( surf - > flags , SURF_DRAWTURB ) & & ! FBitSet ( surf - > flags , SURF_DRAWTURB_QUADS ) )
continue ;
2021-06-14 18:44:35 +02:00
if ( surf - > plane - > type ! = PLANE_Z & & ! FBitSet ( ent - > curstate . effects , EF_WATERSIDES ) )
continue ;
2021-06-14 20:22:07 +02:00
if ( mins [ 2 ] + 1.0f > = surf - > plane - > dist )
continue ;
2021-06-14 18:44:35 +02:00
2021-06-14 18:19:21 +02:00
EmitWaterPolys ( ent , surf , false ) ;
}
// submit as dynamic model
VK_RenderModelDynamicCommit ( ) ;
// TODO:
// - upload water geometry only once, animate in compute/vertex shader
}
2023-06-05 21:53:25 +02:00
# endif
2023-06-09 19:03:00 +02:00
static void fillWaterSurfaces ( const cl_entity_t * ent , vk_brush_model_t * bmodel , vk_render_geometry_t * geometries ) {
2023-06-05 21:53:25 +02:00
ASSERT ( bmodel - > water . surfaces_count > 0 ) ;
const r_geometry_range_lock_t geom_lock = R_GeometryRangeLock ( & bmodel - > water . geometry ) ;
const float scale = ent ? ent - > curstate . scale : 1.f ;
int vertices_offset = 0 ;
int indices_offset = 0 ;
for ( int i = 0 ; i < bmodel - > water . surfaces_count ; + + i ) {
const int surf_index = bmodel - > water . surfaces_indices [ i ] ;
int vertices = 0 , indices = 0 ;
brushComputeWaterPolys ( ( compute_water_polys_t ) {
2023-08-28 19:12:02 +02:00
. prev_time = bmodel - > prev_time ,
2023-06-05 21:53:25 +02:00
. scale = scale ,
. reverse = false , // ??? is it ever true?
. warp = bmodel - > engine_model - > surfaces + surf_index ,
. dst_vertices = geom_lock . vertices + vertices_offset ,
. dst_indices = geom_lock . indices + indices_offset ,
. dst_geometry = geometries + i ,
. out_vertex_count = & vertices ,
. out_index_count = & indices ,
} ) ;
geometries [ i ] . vertex_offset = bmodel - > water . geometry . vertices . unit_offset + vertices_offset ;
geometries [ i ] . index_offset = bmodel - > water . geometry . indices . unit_offset + indices_offset ;
vertices_offset + = vertices ;
indices_offset + = indices ;
ASSERT ( vertices_offset < = bmodel - > water . geometry . vertices . count ) ;
ASSERT ( indices_offset < = bmodel - > water . geometry . indices . count ) ;
}
R_GeometryRangeUnlock ( & geom_lock ) ;
}
2023-08-31 18:49:25 +02:00
static rt_light_add_polygon_t loadPolyLight ( const model_t * mod , const int surface_index , const msurface_t * surf , const vec3_t emissive ) ;
2023-11-06 17:42:07 +01:00
static qboolean doesTextureChainChange ( const texture_t * const base ) {
const texture_t * cur = base ;
if ( ! cur )
return false ;
2023-06-05 21:53:25 +02:00
2023-11-06 17:42:07 +01:00
cur = cur - > anim_next ;
while ( cur & & cur ! = base ) {
if ( cur - > gl_texturenum ! = base - > gl_texturenum )
return true ;
cur = cur - > anim_next ;
2023-06-05 21:53:25 +02:00
}
2023-11-06 17:42:07 +01:00
return false ;
}
2023-06-05 21:53:25 +02:00
2023-11-06 17:42:07 +01:00
static qboolean isSurfaceAnimated ( const msurface_t * s ) {
const texture_t * const base = s - > texinfo - > texture ;
if ( ! base - > anim_total & & ! base - > alternate_anims )
2023-06-05 21:53:25 +02:00
return false ;
2023-11-06 17:42:07 +01:00
/* TODO why did we do this? It doesn't seem to rule out animation really.
2023-06-05 21:53:25 +02:00
if ( base - > name [ 0 ] = = ' - ' )
return false ;
2023-11-06 17:42:07 +01:00
*/
2023-06-05 21:53:25 +02:00
2023-11-06 17:42:07 +01:00
if ( base - > alternate_anims & & base - > gl_texturenum ! = base - > alternate_anims - > gl_texturenum )
return true ;
2023-09-08 20:21:00 +02:00
2023-11-06 17:42:07 +01:00
return doesTextureChainChange ( base ) | | doesTextureChainChange ( base - > alternate_anims ) ;
2023-06-05 21:53:25 +02:00
}
typedef enum {
BrushSurface_Hidden = 0 ,
BrushSurface_Regular ,
BrushSurface_Animated ,
BrushSurface_Water ,
BrushSurface_Sky ,
} brush_surface_type_e ;
static brush_surface_type_e getSurfaceType ( const msurface_t * surf , int i ) {
// if ( i >= 0 && (surf->flags & ~(SURF_PLANEBACK | SURF_UNDERWATER | SURF_TRANSPARENT)) != 0)
// {
2023-08-29 18:31:57 +02:00
// DEBUG("\t%d flags: ", i);
2023-06-05 21:53:25 +02:00
/ / # define PRINTFLAGS ( X ) \
/ / X ( SURF_PLANEBACK ) \
/ / X ( SURF_DRAWSKY ) \
/ / X ( SURF_DRAWTURB_QUADS ) \
/ / X ( SURF_DRAWTURB ) \
/ / X ( SURF_DRAWTILED ) \
/ / X ( SURF_CONVEYOR ) \
/ / X ( SURF_UNDERWATER ) \
// X(SURF_TRANSPARENT)
2023-08-29 18:31:57 +02:00
// #define PRINTFLAG(f) if (FBitSet(surf->flags, f)) DEBUG(" %s", #f);
2023-06-05 21:53:25 +02:00
// PRINTFLAGS(PRINTFLAG)
2023-08-29 18:31:57 +02:00
// DEBUG("\n");
2023-06-05 21:53:25 +02:00
// }
const xvk_patch_surface_t * patch_surface = R_VkPatchGetSurface ( i ) ;
if ( patch_surface & & patch_surface - > flags & Patch_Surface_Delete )
return BrushSurface_Hidden ;
if ( surf - > flags & ( SURF_DRAWTURB | SURF_DRAWTURB_QUADS ) ) {
return ( ! surf - > polys ) ? BrushSurface_Hidden : BrushSurface_Water ;
}
// Explicitly enable SURF_SKY, otherwise they will be skipped by SURF_DRAWTILED
if ( FBitSet ( surf - > flags , SURF_DRAWSKY ) )
return BrushSurface_Sky ;
//if( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS ) ) {
if ( surf - > flags & ( SURF_DRAWTURB | SURF_DRAWTURB_QUADS ) ) {
//if( surf->flags & ( SURF_DRAWSKY | SURF_CONVEYOR ) ) {
// FIXME don't print this on second sort-by-texture pass
2023-08-29 18:31:57 +02:00
//DEBUG("Skipping surface %d because of flags %08x", i, surf->flags);
2023-06-05 21:53:25 +02:00
return BrushSurface_Hidden ;
}
if ( FBitSet ( surf - > flags , SURF_DRAWTILED ) ) {
2023-08-29 18:31:57 +02:00
//DEBUG("Skipping surface %d because of tiled flag", i);
2023-06-05 21:53:25 +02:00
return BrushSurface_Hidden ;
}
2023-10-30 17:43:26 +01:00
const qboolean patched_material = patch_surface & & ! ! ( patch_surface - > flags & Patch_Surface_Material ) ;
if ( ! patched_material & & isSurfaceAnimated ( surf ) ) {
2023-06-05 21:53:25 +02:00
return BrushSurface_Animated ;
}
return BrushSurface_Regular ;
}
static qboolean brushCreateWaterModel ( const model_t * mod , vk_brush_model_t * bmodel , const model_sizes_t sizes ) {
bmodel - > water . surfaces_count = sizes . water_surfaces ;
const r_geometry_range_t geometry = R_GeometryRangeAlloc ( sizes . water_vertices , sizes . water_indices ) ;
if ( ! geometry . block_handle . size ) {
2023-08-29 18:31:57 +02:00
ERR ( " Cannot allocate geometry (v=%d, i=%d) for water model %s " ,
2023-06-05 21:53:25 +02:00
sizes . water_vertices , sizes . water_indices , mod - > name ) ;
return false ;
}
vk_render_geometry_t * const geometries = Mem_Malloc ( vk_core . pool , sizeof ( vk_render_geometry_t ) * sizes . water_surfaces ) ;
int * const surfaces_indices = Mem_Malloc ( vk_core . pool , sizes . water_surfaces * sizeof ( int ) ) ;
int index_index = 0 ;
for ( int i = 0 ; i < mod - > nummodelsurfaces ; + + i ) {
const int surface_index = mod - > firstmodelsurface + i ;
const msurface_t * surf = mod - > surfaces + surface_index ;
if ( getSurfaceType ( surf , surface_index ) ! = BrushSurface_Water )
continue ;
surfaces_indices [ index_index + + ] = surface_index ;
}
ASSERT ( index_index = = sizes . water_surfaces ) ;
bmodel - > water . surfaces_indices = surfaces_indices ;
bmodel - > water . geometry = geometry ;
fillWaterSurfaces ( NULL , bmodel , geometries ) ;
2023-06-09 19:03:00 +02:00
if ( ! R_RenderModelCreate ( & bmodel - > water . render_model , ( vk_render_model_init_t ) {
2023-06-05 21:53:25 +02:00
. name = mod - > name ,
. geometries = geometries ,
. geometries_count = sizes . water_surfaces ,
2023-06-09 19:03:00 +02:00
. dynamic = true ,
2023-06-05 21:53:25 +02:00
} ) ) {
2023-08-29 18:31:57 +02:00
ERR ( " Could not create water render model for brush model %s " , mod - > name ) ;
2023-06-05 21:53:25 +02:00
return false ;
}
bmodel - > water . surfaces_indices = surfaces_indices ;
return true ;
}
2023-10-31 16:15:15 +01:00
static material_mode_e brushMaterialModeForRenderType ( vk_render_type_e render_type ) {
2023-09-26 19:14:28 +02:00
switch ( render_type ) {
case kVkRenderTypeSolid :
return kMaterialMode_Opaque ;
break ;
case kVkRenderType_A_1mA_RW : // blend: scr*a + dst*(1-a), depth: RW
case kVkRenderType_A_1mA_R : // blend: scr*a + dst*(1-a), depth test
return kMaterialMode_Translucent ;
break ;
case kVkRenderType_A_1 : // blend: scr*a + dst, no depth test or write; sprite:kRenderGlow only
return kMaterialMode_BlendGlow ;
break ;
case kVkRenderType_A_1_R : // blend: scr*a + dst, depth test
case kVkRenderType_1_1_R : // blend: scr + dst, depth test
return kMaterialMode_BlendAdd ;
break ;
case kVkRenderType_AT : // no blend, depth RW, alpha test
return kMaterialMode_AlphaTest ;
break ;
default :
gEngine . Host_Error ( " Unexpected render type %d \n " , render_type ) ;
}
return kMaterialMode_Opaque ;
}
2023-06-05 21:53:25 +02:00
static void brushDrawWater ( vk_brush_model_t * bmodel , const cl_entity_t * ent , int render_type , const vec4_t color , const matrix4x4 transform ) {
2023-09-08 20:12:35 +02:00
APROF_SCOPE_DECLARE_BEGIN ( brush_draw_water , __FUNCTION__ ) ;
2023-06-05 21:53:25 +02:00
ASSERT ( bmodel - > water . surfaces_count > 0 ) ;
2023-06-09 19:03:00 +02:00
fillWaterSurfaces ( NULL , bmodel , bmodel - > water . render_model . geometries ) ;
if ( ! R_RenderModelUpdate ( & bmodel - > water . render_model ) ) {
2023-08-29 18:31:57 +02:00
ERR ( " Failed to update brush model \" %s \" water " , bmodel - > render_model . debug_name ) ;
2023-06-09 19:03:00 +02:00
}
2023-06-05 21:53:25 +02:00
2023-09-26 19:14:28 +02:00
const material_mode_e material_mode = brushMaterialModeForRenderType ( render_type ) ;
2023-06-05 21:53:25 +02:00
R_RenderModelDraw ( & bmodel - > water . render_model , ( r_model_draw_t ) {
. render_type = render_type ,
2023-09-26 19:14:28 +02:00
. material_mode = material_mode ,
2023-06-05 21:53:25 +02:00
. color = ( const vec4_t * ) color ,
. transform = ( const matrix4x4 * ) transform ,
. prev_transform = & bmodel - > prev_transform ,
} ) ;
2023-09-08 20:12:35 +02:00
APROF_SCOPE_END ( brush_draw_water ) ;
2023-06-05 21:53:25 +02:00
}
2021-06-14 18:19:21 +02:00
2023-10-24 17:32:42 +02:00
#if 0
// TODO use this
2023-06-06 19:40:10 +02:00
static void computeConveyorSpeed ( const color24 rendercolor , int tex_index , vec2_t speed ) {
float sy , cy ;
float flConveyorSpeed = 0.0f ;
float flRate , flAngle ;
2023-10-19 17:15:45 +02:00
vk_texture_t * texture = R_TextureGetByIndex ( tex_index ) ;
2023-06-06 19:40:10 +02:00
//gl_texture_t *texture;
// FIXME
/* if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ) && RI.currententity == gEngfuncs.GetEntityByIndex( 0 ) ) */
/* { */
/* // same as doom speed */
/* flConveyorSpeed = -35.0f; */
/* } */
/* else */
{
flConveyorSpeed = ( rendercolor . g < < 8 | rendercolor . b ) / 16.0f ;
if ( rendercolor . r ) flConveyorSpeed = - flConveyorSpeed ;
}
//texture = R_GetTexture( glState.currentTextures[glState.activeTMU] );
flRate = fabs ( flConveyorSpeed ) / ( float ) texture - > width ;
flAngle = ( flConveyorSpeed > = 0 ) ? 180 : 0 ;
SinCos ( flAngle * ( M_PI_F / 180.0f ) , & sy , & cy ) ;
speed [ 0 ] = cy * flRate ;
speed [ 1 ] = sy * flRate ;
}
2023-10-24 17:32:42 +02:00
# endif
2023-06-06 19:40:10 +02:00
2021-02-10 01:42:08 +01:00
/*
= = = = = = = = = = = = = = =
R_TextureAnimation
Returns the proper texture for a given time and surface
= = = = = = = = = = = = = = =
*/
2023-10-30 17:43:26 +01:00
const texture_t * R_TextureAnimation ( const cl_entity_t * ent , const msurface_t * s )
2021-02-10 01:42:08 +01:00
{
2023-10-30 17:43:26 +01:00
const texture_t * base = s - > texinfo - > texture ;
2021-02-10 01:42:08 +01:00
int count , reletive ;
if ( ent & & ent - > curstate . frame )
{
if ( base - > alternate_anims )
base = base - > alternate_anims ;
}
if ( ! base - > anim_total )
return base ;
if ( base - > name [ 0 ] = = ' - ' )
{
int tx = ( int ) ( ( s - > texturemins [ 0 ] + ( base - > width < < 16 ) ) / base - > width ) % MOD_FRAMES ;
int ty = ( int ) ( ( s - > texturemins [ 1 ] + ( base - > height < < 16 ) ) / base - > height ) % MOD_FRAMES ;
reletive = g_brush . rtable [ tx ] [ ty ] % base - > anim_total ;
}
else
{
int speed ;
// Quake1 textures uses 10 frames per second
2023-10-24 17:32:42 +02:00
/* TODO
2023-10-19 17:15:45 +02:00
if ( FBitSet ( R_TextureGetByIndex ( base - > gl_texturenum ) - > flags , TF_QUAKEPAL ) )
2021-02-10 01:42:08 +01:00
speed = 10 ;
2023-10-24 17:32:42 +02:00
else */ speed = 20 ;
2021-02-10 01:42:08 +01:00
reletive = ( int ) ( gpGlobals - > time * speed ) % base - > anim_total ;
}
count = 0 ;
while ( base - > anim_min > reletive | | base - > anim_max < = reletive )
{
base = base - > anim_next ;
if ( ! base | | + + count > MOD_FRAMES )
return s - > texinfo - > texture ;
}
return base ;
}
2023-05-31 18:39:27 +02:00
void VK_BrushModelDraw ( const cl_entity_t * ent , int render_mode , float blend , const matrix4x4 in_transform ) {
2021-01-27 21:00:11 +01:00
// Expect all buffers to be bound
2021-02-10 01:42:08 +01:00
const model_t * mod = ent - > model ;
2021-04-07 21:11:20 +02:00
vk_brush_model_t * bmodel = mod - > cache . data ;
2021-01-31 00:31:23 +01:00
2021-02-01 01:50:27 +01:00
if ( ! bmodel ) {
2023-08-29 18:31:57 +02:00
ERR ( " Model %s wasn't loaded " , mod - > name ) ;
2021-01-27 21:00:11 +01:00
return ;
2021-02-01 01:50:27 +01:00
}
2023-05-31 18:39:27 +02:00
matrix4x4 transform ;
if ( in_transform )
Matrix4x4_Copy ( transform , in_transform ) ;
2023-05-04 19:30:13 +02:00
else
2023-05-31 18:39:27 +02:00
Matrix4x4_LoadIdentity ( transform ) ;
2023-05-04 19:30:13 +02:00
2023-05-31 18:39:27 +02:00
vec4_t color = { 1 , 1 , 1 , 1 } ;
2023-05-01 19:27:30 +02:00
vk_render_type_e render_type = kVkRenderTypeSolid ;
switch ( render_mode ) {
case kRenderNormal :
2023-05-31 18:39:27 +02:00
Vector4Set ( color , 1.f , 1.f , 1.f , 1.f ) ;
2023-05-01 19:27:30 +02:00
render_type = kVkRenderTypeSolid ;
break ;
case kRenderTransColor :
render_type = kVkRenderType_A_1mA_RW ;
2023-05-31 18:39:27 +02:00
Vector4Set ( color ,
2023-05-01 19:27:30 +02:00
ent - > curstate . rendercolor . r / 255.f ,
ent - > curstate . rendercolor . g / 255.f ,
ent - > curstate . rendercolor . b / 255.f ,
blend ) ;
break ;
case kRenderTransAdd :
2023-05-31 18:39:27 +02:00
Vector4Set ( color , blend , blend , blend , 1.f ) ;
2023-05-01 19:27:30 +02:00
render_type = kVkRenderType_A_1_R ;
break ;
case kRenderTransAlpha :
if ( gEngine . EngineGetParm ( PARM_QUAKE_COMPATIBLE , 0 ) )
{
render_type = kVkRenderType_A_1mA_RW ;
2023-05-31 18:39:27 +02:00
Vector4Set ( color , 1.f , 1.f , 1.f , blend ) ;
2023-05-01 19:27:30 +02:00
}
else
{
2023-05-31 18:39:27 +02:00
Vector4Set ( color , 1.f , 1.f , 1.f , 1.f ) ;
2023-05-01 19:27:30 +02:00
render_type = kVkRenderType_AT ;
}
break ;
case kRenderTransTexture :
case kRenderGlow :
render_type = kVkRenderType_A_1mA_R ;
2023-05-31 18:39:27 +02:00
Vector4Set ( color , 1.f , 1.f , 1.f , blend ) ;
2023-05-01 19:27:30 +02:00
break ;
2023-02-25 07:48:26 +01:00
}
2023-02-25 08:54:41 +01:00
// Only Normal and TransAlpha have lightmaps
// TODO: on big maps more than a single lightmap texture is possible
bmodel - > render_model . lightmap = ( render_mode = = kRenderNormal | | render_mode = = kRenderTransAlpha ) ? 1 : 0 ;
2023-06-05 21:53:25 +02:00
if ( bmodel - > water . surfaces_count )
brushDrawWater ( bmodel , ent , render_type , color , transform ) ;
+ + g_brush . stat . models_drawn ;
2021-06-14 18:19:21 +02:00
2021-04-13 19:13:17 +02:00
if ( bmodel - > render_model . num_geometries = = 0 )
return ;
2023-10-02 17:39:00 +02:00
// Animate textures
{
2023-09-08 20:12:35 +02:00
APROF_SCOPE_DECLARE_BEGIN ( brush_update_textures , " brush: update animated textures " ) ;
2023-04-28 21:35:48 +02:00
// Update animated textures
2023-08-31 18:11:17 +02:00
int updated_textures_count = 0 ;
2023-04-28 21:35:48 +02:00
for ( int i = 0 ; i < bmodel - > animated_indexes_count ; + + i ) {
2023-08-31 18:49:25 +02:00
const int geom_index = bmodel - > animated_indexes [ i ] ;
vk_render_geometry_t * geom = bmodel - > render_model . geometries + geom_index ;
2023-04-28 21:35:48 +02:00
const int surface_index = geom - > surf_deprecate - mod - > surfaces ;
2021-12-22 20:55:35 +01:00
// Optionally patch by texture_s pointer and run animations
2023-10-30 17:43:26 +01:00
const texture_t * t = R_TextureAnimation ( ent , geom - > surf_deprecate ) ;
2023-09-19 19:20:04 +02:00
const int new_tex_id = t - > gl_texturenum ;
2023-08-31 18:11:17 +02:00
2023-09-19 19:20:04 +02:00
if ( new_tex_id > = 0 & & new_tex_id ! = geom - > ye_olde_texture ) {
geom - > ye_olde_texture = new_tex_id ;
geom - > material = R_VkMaterialGetForTexture ( new_tex_id ) ;
2023-08-31 18:11:17 +02:00
if ( updated_textures_count < MAX_ANIMATED_TEXTURES ) {
g_brush . updated_textures [ updated_textures_count + + ] = bmodel - > animated_indexes [ i ] ;
}
}
2023-08-31 18:49:25 +02:00
// Animated textures can be emissive
// Add them as dynamic lights for now. It would probably be better if they were static lights (for worldmodel),
// but there's no easy way to do it for now.
vec3_t * emissive = & bmodel - > render_model . geometries [ geom_index ] . emissive ;
2023-09-19 19:20:04 +02:00
if ( RT_GetEmissiveForTexture ( * emissive , new_tex_id ) ) {
2023-08-31 18:49:25 +02:00
rt_light_add_polygon_t polylight = loadPolyLight ( mod , surface_index , geom - > surf_deprecate , * emissive ) ;
polylight . dynamic = true ;
polylight . transform_row = ( const matrix3x4 * ) & transform ;
RT_LightAddPolygon ( & polylight ) ;
}
2023-08-31 18:11:17 +02:00
}
if ( updated_textures_count > 0 ) {
R_RenderModelUpdateMaterials ( & bmodel - > render_model , g_brush . updated_textures , updated_textures_count ) ;
2021-12-22 20:55:35 +01:00
}
2023-09-08 20:12:35 +02:00
APROF_SCOPE_END ( brush_update_textures ) ;
2021-04-07 21:11:20 +02:00
}
2021-01-31 00:31:23 +01:00
2023-09-26 19:14:28 +02:00
const material_mode_e material_mode = brushMaterialModeForRenderType ( render_type ) ;
2023-05-31 18:39:27 +02:00
R_RenderModelDraw ( & bmodel - > render_model , ( r_model_draw_t ) {
. render_type = render_type ,
2023-09-26 19:14:28 +02:00
. material_mode = material_mode ,
2023-05-31 18:39:27 +02:00
. color = & color ,
. transform = & transform ,
. prev_transform = & bmodel - > prev_transform ,
} ) ;
Matrix4x4_Copy ( bmodel - > prev_transform , transform ) ;
2023-08-28 19:12:02 +02:00
bmodel - > prev_time = gpGlobals - > time ;
2021-04-07 21:11:20 +02:00
}
2021-01-31 00:31:23 +01:00
2021-04-07 21:11:20 +02:00
static model_sizes_t computeSizes ( const model_t * mod ) {
model_sizes_t sizes = { 0 } ;
for ( int i = 0 ; i < mod - > nummodelsurfaces ; + + i )
{
2021-11-29 18:51:33 +01:00
const int surface_index = mod - > firstmodelsurface + i ;
const msurface_t * surf = mod - > surfaces + surface_index ;
2022-02-03 06:32:10 +01:00
const int tex_id = surf - > texinfo - > texture - > gl_texturenum ;
2021-04-07 21:11:20 +02:00
2023-04-28 21:35:48 +02:00
if ( tex_id > sizes . max_texture_id )
sizes . max_texture_id = tex_id ;
2021-06-14 18:19:21 +02:00
2023-04-28 21:35:48 +02:00
switch ( getSurfaceType ( surf , surface_index ) ) {
case BrushSurface_Water :
sizes . water_surfaces + + ;
2023-06-05 21:53:25 +02:00
addWarpVertIndCounts ( surf , & sizes . water_vertices , & sizes . water_indices ) ;
2023-04-28 21:35:48 +02:00
case BrushSurface_Hidden :
2021-04-07 21:11:20 +02:00
continue ;
2023-04-28 21:35:48 +02:00
case BrushSurface_Animated :
sizes . animated_count + + ;
case BrushSurface_Regular :
2023-09-22 16:15:34 +02:00
case BrushSurface_Sky :
2023-04-28 21:35:48 +02:00
break ;
}
2021-04-07 21:11:20 +02:00
+ + sizes . num_surfaces ;
sizes . num_vertices + = surf - > numedges ;
sizes . num_indices + = 3 * ( surf - > numedges - 1 ) ;
2021-02-14 02:19:59 +01:00
}
2021-02-01 01:50:27 +01:00
2023-08-31 18:11:17 +02:00
DEBUG ( " Computed sizes for brush model \" %s \" : num_surfaces=%d num_vertices=%d num_indices=%d max_texture_id=%d water_surfaces=%d animated_count=%d water_vertices=%d water_indices=%d " , mod - > name , sizes . num_surfaces , sizes . num_vertices , sizes . num_indices , sizes . max_texture_id , sizes . water_surfaces , sizes . animated_count , sizes . water_vertices , sizes . water_indices ) ;
2021-04-07 21:11:20 +02:00
return sizes ;
2021-01-27 21:00:11 +01:00
}
2023-05-30 21:14:44 +02:00
typedef struct {
const model_t * mod ;
const vk_brush_model_t * bmodel ;
model_sizes_t sizes ;
uint32_t base_vertex_offset ;
uint32_t base_index_offset ;
vk_render_geometry_t * out_geometries ;
vk_vertex_t * out_vertices ;
uint16_t * out_indices ;
} fill_geometries_args_t ;
2023-09-04 19:48:58 +02:00
static void getSurfaceNormal ( const msurface_t * surf , vec3_t out_normal ) {
if ( FBitSet ( surf - > flags , SURF_PLANEBACK ) )
VectorNegate ( surf - > plane - > normal , out_normal ) ;
else
VectorCopy ( surf - > plane - > normal , out_normal ) ;
// TODO scale normal by area -- bigger surfaces should have bigger impact
2023-10-30 17:43:26 +01:00
// NOTE scaling normal by area might be totally incorrect in many circumstances
// The more corect logic there is way more difficult
2023-09-04 19:48:58 +02:00
//VectorScale(normal, surf->plane.
}
2023-10-02 17:18:22 +02:00
static qboolean shouldSmoothLinkSurfaces ( const model_t * mod , qboolean smooth_entire_model , int surf1 , int surf2 ) {
2023-09-05 18:35:28 +02:00
// Filter explicit exclusion
2023-10-31 17:43:16 +01:00
for ( int i = 0 ; i < g_map_entities . smoothing . excluded_pairs_count ; i + = 2 ) {
const int cand1 = g_map_entities . smoothing . excluded_pairs [ i ] ;
const int cand2 = g_map_entities . smoothing . excluded_pairs [ i + 1 ] ;
2023-09-05 18:35:28 +02:00
if ( ( cand1 = = surf1 & & cand2 = = surf2 )
| | ( cand1 = = surf2 & & cand2 = = surf1 ) )
return false ;
}
2023-10-31 17:43:16 +01:00
qboolean excluded = false ;
for ( int i = 0 ; i < g_map_entities . smoothing . excluded_count ; + + i ) {
const int cand = g_map_entities . smoothing . excluded [ i ] ;
if ( cand = = surf1 | | cand = = surf2 ) {
excluded = true ;
break ;
}
}
if ( smooth_entire_model & & ! excluded )
2023-10-02 17:18:22 +02:00
return true ;
2023-10-31 17:43:16 +01:00
// Smoothing groups have priority over individual exclusion.
// That way we can exclude a surface from smoothing with most of its neighbours,
// but still smooth it with some.
2023-09-05 18:54:57 +02:00
for ( int i = 0 ; i < g_map_entities . smoothing . groups_count ; + + i ) {
const xvk_smoothing_group_t * g = g_map_entities . smoothing . groups + i ;
uint32_t bits = 0 ;
for ( int j = 0 ; j < g - > count ; + + j ) {
if ( g - > surfaces [ j ] = = surf1 ) {
bits | = 1 ;
if ( bits = = 3 )
return true ;
}
else if ( g - > surfaces [ j ] = = surf2 ) {
bits | = 2 ;
if ( bits = = 3 )
return true ;
}
}
}
2023-10-31 17:43:16 +01:00
if ( excluded )
return false ;
2023-09-05 18:54:57 +02:00
// Do not join surfaces with different textures. Assume they belong to different objects.
2023-10-30 17:43:26 +01:00
{
// Should we also check texture/material patches too to filter out pairs which originally had
// same textures, but with patches do not?
if ( mod - > surfaces [ surf1 ] . texinfo - > texture - > gl_texturenum
! = mod - > surfaces [ surf2 ] . texinfo - > texture - > gl_texturenum )
return false ;
}
2023-09-05 18:54:57 +02:00
2023-09-04 19:48:58 +02:00
vec3_t n1 , n2 ;
getSurfaceNormal ( mod - > surfaces + surf1 , n1 ) ;
getSurfaceNormal ( mod - > surfaces + surf2 , n2 ) ;
2023-09-04 20:35:19 +02:00
const float dot = DotProduct ( n1 , n2 ) ;
2023-09-05 18:06:59 +02:00
DEBUG ( " Smoothing: dot(%d, %d) = %f (t=%f) " , surf1 , surf2 , dot , g_map_entities . smoothing . threshold ) ;
2023-09-04 20:35:19 +02:00
2023-09-05 18:06:59 +02:00
return dot > = g_map_entities . smoothing . threshold ;
2023-09-04 19:48:58 +02:00
}
static int lvFindValue ( const linked_value_t * li , int count , int needle ) {
for ( int i = 0 ; i < count ; + + i )
if ( li [ i ] . value = = needle )
return i ;
return - 1 ;
}
static int lvFindOrAddValue ( linked_value_t * li , int * count , int capacity , int needle ) {
const int found = lvFindValue ( li , * count , needle ) ;
if ( found > = 0 )
return found ;
if ( * count = = capacity )
return - 1 ;
li [ * count ] . value = needle ;
li [ * count ] . link = * count ;
return ( * count ) + + ;
}
static int lvFindBaseIndex ( const linked_value_t * li , int index ) {
while ( li [ index ] . link ! = index )
index = li [ index ] . link ;
return index ;
}
static void lvFlatten ( linked_value_t * li , int count ) {
for ( int i = 0 ; i < count ; + + i ) {
for ( int j = i ; j < count ; + + j ) {
if ( lvFindBaseIndex ( li , j ) = = i ) {
li [ j ] . link = i ;
}
}
}
}
static void linkSmoothSurfaces ( const model_t * mod , int surf1 , int surf2 , int vertex_index ) {
conn_vertex_t * v = g_brush . conn . vertices + vertex_index ;
int i1 = lvFindOrAddValue ( v - > surfs , & v - > count , COUNTOF ( v - > surfs ) , surf1 ) ;
int i2 = lvFindOrAddValue ( v - > surfs , & v - > count , COUNTOF ( v - > surfs ) , surf2 ) ;
DEBUG ( " Link %d(%d)<->%d(%d) v=%d " , surf1 , i1 , surf2 , i2 , vertex_index ) ;
if ( i1 < 0 | | i2 < 0 ) {
ERR ( " Model %s cannot smooth link surf %d<->%d for vertex %d " , mod - > name , surf1 , surf2 , vertex_index ) ;
return ;
}
i1 = lvFindBaseIndex ( v - > surfs , i1 ) ;
i2 = lvFindBaseIndex ( v - > surfs , i2 ) ;
// Link them
v - > surfs [ Q_max ( i1 , i2 ) ] . link = Q_min ( i1 , i2 ) ;
}
2023-10-02 17:18:22 +02:00
static void connectVertices ( const model_t * mod , qboolean smooth_entire_model ) {
2023-09-04 19:48:58 +02:00
if ( mod - > numedges > g_brush . conn . edges_capacity ) {
if ( g_brush . conn . edges )
Mem_Free ( g_brush . conn . edges ) ;
g_brush . conn . edges_capacity = mod - > numedges ;
g_brush . conn . edges = Mem_Calloc ( vk_core . pool , sizeof ( * g_brush . conn . edges ) * g_brush . conn . edges_capacity ) ;
}
if ( mod - > numvertexes > g_brush . conn . vertices_capacity ) {
if ( g_brush . conn . vertices )
Mem_Free ( g_brush . conn . vertices ) ;
g_brush . conn . vertices_capacity = mod - > numvertexes ;
g_brush . conn . vertices = Mem_Calloc ( vk_core . pool , sizeof ( * g_brush . conn . vertices ) * g_brush . conn . vertices_capacity ) ;
}
// Find connection edges
for ( int i = 0 ; i < mod - > nummodelsurfaces ; + + i ) {
const int surface_index = mod - > firstmodelsurface + i ;
const msurface_t * surf = mod - > surfaces + surface_index ;
for ( int k = 0 ; k < surf - > numedges ; k + + ) {
const int iedge_dir = mod - > surfedges [ surf - > firstedge + k ] ;
const int iedge = iedge_dir > = 0 ? iedge_dir : - iedge_dir ;
ASSERT ( iedge > = 0 ) ;
ASSERT ( iedge < mod - > numedges ) ;
conn_edge_t * cedge = g_brush . conn . edges + iedge ;
if ( cedge - > count = = 0 ) {
cedge - > first_surface = surface_index ;
} else {
const medge_t * edge = mod - > edges + iedge ;
2023-10-02 17:18:22 +02:00
if ( shouldSmoothLinkSurfaces ( mod , smooth_entire_model , cedge - > first_surface , surface_index ) ) {
2023-09-04 19:48:58 +02:00
linkSmoothSurfaces ( mod , cedge - > first_surface , surface_index , edge - > v [ 0 ] ) ;
linkSmoothSurfaces ( mod , cedge - > first_surface , surface_index , edge - > v [ 1 ] ) ;
}
if ( cedge - > count > 1 ) {
WARN ( " Model %s edge %d has %d surfaces " , mod - > name , i , cedge - > count ) ;
}
}
cedge - > count + + ;
} // for surf->numedges
} // for mod->nummodelsurfaces
int hist [ 17 ] = { 0 } ;
for ( int i = 0 ; i < mod - > numvertexes ; + + i ) {
conn_vertex_t * vtx = g_brush . conn . vertices + i ;
if ( vtx - > count < 16 ) {
hist [ vtx - > count ] + + ;
} else {
hist [ 16 ] + + ;
}
lvFlatten ( vtx - > surfs , vtx - > count ) ;
// Too verbose
#if 0
if ( vtx - > count ) {
DEBUG ( " Vertex %d linked count %d " , i , vtx - > count ) ;
for ( int j = 0 ; j < vtx - > count ; + + j ) {
DEBUG ( " %d: l=%d v=%d " , j , vtx - > surfs [ j ] . link , vtx - > surfs [ j ] . value ) ;
}
}
# endif
}
for ( int i = 0 ; i < COUNTOF ( hist ) ; + + i ) {
DEBUG ( " VTX hist[%d] = %d " , i , hist [ i ] ) ;
}
}
static qboolean getSmoothedNormalFor ( const model_t * mod , int vertex_index , int surface_index , vec3_t out_normal ) {
const conn_vertex_t * v = g_brush . conn . vertices + vertex_index ;
const int index = lvFindValue ( v - > surfs , v - > count , surface_index ) ;
if ( index < 0 )
return false ;
const int base = lvFindBaseIndex ( v - > surfs , index ) ;
vec3_t normal = { 0 } ;
for ( int i = 0 ; i < v - > count ; + + i ) {
if ( v - > surfs [ i ] . link = = base ) {
const int surface = v - > surfs [ i ] . value ;
vec3_t surf_normal = { 0 } ;
getSurfaceNormal ( mod - > surfaces + surface , surf_normal ) ;
VectorAdd ( normal , surf_normal , normal ) ;
}
}
VectorNormalize ( normal ) ;
VectorCopy ( normal , out_normal ) ;
return true ;
}
2023-09-29 18:48:32 +02:00
static const xvk_mapent_func_any_t * getModelFuncAnyPatch ( const model_t * const mod ) {
for ( int i = 0 ; i < g_map_entities . func_any_count ; + + i ) {
const xvk_mapent_func_any_t * const fw = g_map_entities . func_any + i ;
if ( Q_strcmp ( mod - > name , fw - > model ) = = 0 ) {
return fw ;
}
}
return NULL ;
}
2023-05-30 21:14:44 +02:00
static qboolean fillBrushSurfaces ( fill_geometries_args_t args ) {
int vertex_offset = 0 ;
2021-06-05 21:48:16 +02:00
int num_geometries = 0 ;
2023-04-28 21:35:48 +02:00
int animated_count = 0 ;
2021-01-27 21:00:11 +01:00
2023-05-30 21:14:44 +02:00
vk_vertex_t * p_vert = args . out_vertices ;
uint16_t * p_ind = args . out_indices ;
int index_offset = args . base_index_offset ;
2021-02-23 03:54:13 +01:00
2023-09-29 18:48:32 +02:00
const xvk_mapent_func_any_t * const entity_patch = getModelFuncAnyPatch ( args . mod ) ;
2023-10-02 17:18:22 +02:00
connectVertices ( args . mod , entity_patch ? entity_patch - > smooth_entire_model : false ) ;
2023-09-29 18:48:32 +02:00
2021-01-31 00:31:23 +01:00
// Load sorted by gl_texturenum
2022-01-14 07:36:21 +01:00
// TODO this does not make that much sense in vulkan (can sort later)
2023-05-30 21:14:44 +02:00
for ( int t = 0 ; t < = args . sizes . max_texture_id ; + + t ) {
for ( int i = 0 ; i < args . mod - > nummodelsurfaces ; + + i ) {
const int surface_index = args . mod - > firstmodelsurface + i ;
msurface_t * surf = args . mod - > surfaces + surface_index ;
2023-09-04 19:48:58 +02:00
const mextrasurf_t * info = surf - > info ;
2023-05-30 21:14:44 +02:00
vk_render_geometry_t * model_geometry = args . out_geometries + num_geometries ;
2021-01-31 00:31:23 +01:00
const float sample_size = gEngine . Mod_SampleSizeForFace ( surf ) ;
2021-04-07 21:11:20 +02:00
int index_count = 0 ;
2021-11-17 18:43:10 +01:00
vec3_t tangent ;
2023-09-19 19:20:04 +02:00
const int orig_tex_id = surf - > texinfo - > texture - > gl_texturenum ;
2023-09-29 18:48:32 +02:00
if ( t ! = orig_tex_id )
2021-01-31 00:31:23 +01:00
continue ;
2021-01-27 21:00:11 +01:00
2023-09-29 18:48:32 +02:00
int tex_id = orig_tex_id ;
2023-10-03 18:48:26 +02:00
// TODO this patching should probably override entity patching below
2023-09-29 18:48:32 +02:00
const xvk_patch_surface_t * const psurf = R_VkPatchGetSurface ( surface_index ) ;
2023-04-28 21:35:48 +02:00
const brush_surface_type_e type = getSurfaceType ( surf , surface_index ) ;
switch ( type ) {
case BrushSurface_Water :
case BrushSurface_Hidden :
2021-01-31 00:31:23 +01:00
continue ;
2023-04-28 21:35:48 +02:00
case BrushSurface_Animated :
2023-05-30 21:14:44 +02:00
args . bmodel - > animated_indexes [ animated_count + + ] = num_geometries ;
2023-04-28 21:35:48 +02:00
case BrushSurface_Regular :
2023-09-22 16:15:34 +02:00
case BrushSurface_Sky :
2023-04-28 21:35:48 +02:00
break ;
}
2021-01-30 21:10:54 +01:00
2023-05-30 21:14:44 +02:00
args . bmodel - > surface_to_geometry_index [ i ] = num_geometries ;
2023-03-03 08:23:31 +01:00
2021-06-05 21:48:16 +02:00
+ + num_geometries ;
2021-01-31 00:31:23 +01:00
2023-08-29 18:31:57 +02:00
//DEBUG( "surface %d: numverts=%d numedges=%d", i, surf->polys ? surf->polys->numverts : -1, surf->numedges );
2021-01-31 00:31:23 +01:00
2023-05-30 21:14:44 +02:00
if ( vertex_offset + surf - > numedges > = UINT16_MAX ) {
// We might be able to handle it by adjusting base_vertex_offset, etc
2023-08-29 18:31:57 +02:00
ERR ( " Model %s indices don't fit into 16 bits " , args . mod - > name ) ;
2021-04-07 21:11:20 +02:00
return false ;
2021-01-31 00:31:23 +01:00
}
2023-10-02 17:39:00 +02:00
model_geometry - > ye_olde_texture = orig_tex_id ;
2023-09-29 19:58:02 +02:00
qboolean material_assigned = false ;
2023-10-30 17:43:26 +01:00
if ( psurf & & ( psurf - > flags & Patch_Surface_Material ) ) {
model_geometry - > material = R_VkMaterialGetForRef ( psurf - > material_ref ) ;
material_assigned = true ;
}
if ( ! material_assigned & & entity_patch ) {
2023-09-29 18:48:32 +02:00
for ( int i = 0 ; i < entity_patch - > matmap_count ; + + i ) {
2023-10-03 18:48:26 +02:00
if ( entity_patch - > matmap [ i ] . from_tex = = orig_tex_id ) {
2023-09-29 19:58:02 +02:00
model_geometry - > material = R_VkMaterialGetForTexture ( entity_patch - > matmap [ i ] . to_mat . index ) ;
material_assigned = true ;
2023-09-29 18:48:32 +02:00
break ;
}
}
2023-09-29 19:58:02 +02:00
if ( ! material_assigned & & entity_patch - > rendermode > 0 ) {
2023-10-02 17:39:00 +02:00
material_assigned = R_VkMaterialGetEx ( tex_id , entity_patch - > rendermode , & model_geometry - > material ) ;
if ( ! material_assigned & & entity_patch - > rendermode = = kRenderTransColor ) {
// TransColor means ignore textures and draw just color
model_geometry - > material = R_VkMaterialGetForTexture ( tglob . whiteTexture ) ;
model_geometry - > ye_olde_texture = tglob . whiteTexture ;
material_assigned = true ;
}
2023-09-29 19:58:02 +02:00
}
}
if ( ! material_assigned ) {
model_geometry - > material = R_VkMaterialGetForTexture ( tex_id ) ;
material_assigned = true ;
2023-09-29 18:48:32 +02:00
}
2023-03-03 08:23:31 +01:00
VectorClear ( model_geometry - > emissive ) ;
2023-04-21 20:07:58 +02:00
model_geometry - > surf_deprecate = surf ;
2021-07-04 21:48:32 +02:00
2023-05-30 21:14:44 +02:00
model_geometry - > vertex_offset = args . base_vertex_offset ;
2021-08-18 18:17:48 +02:00
model_geometry - > max_vertex = vertex_offset + surf - > numedges ;
2021-07-04 21:48:32 +02:00
model_geometry - > index_offset = index_offset ;
2021-01-31 00:31:23 +01:00
2023-09-22 16:15:34 +02:00
if ( type = = BrushSurface_Sky ) {
2023-11-07 19:30:46 +01:00
# define TEX_BASE_SKYBOX 0x0f000000u // FIXME ray_interop.h
2023-09-22 16:15:34 +02:00
model_geometry - > material . tex_base_color = TEX_BASE_SKYBOX ;
} else {
2023-04-28 21:35:48 +02:00
ASSERT ( ! FBitSet ( surf - > flags , SURF_DRAWTILED ) ) ;
2023-05-30 21:14:44 +02:00
VK_CreateSurfaceLightmap ( surf , args . mod ) ;
2021-06-19 21:52:13 +02:00
}
2021-01-31 00:31:23 +01:00
2021-10-27 19:21:06 +02:00
if ( FBitSet ( surf - > flags , SURF_CONVEYOR ) ) {
2023-04-21 20:47:07 +02:00
// FIXME make an explicit list of dynamic-uv geometries
2021-10-27 19:21:06 +02:00
}
2023-09-04 19:48:58 +02:00
vec3_t surf_normal ;
getSurfaceNormal ( surf , surf_normal ) ;
vk_vertex_t * const pvert_begin = p_vert ;
2021-01-31 00:31:23 +01:00
for ( int k = 0 ; k < surf - > numedges ; k + + )
{
2023-09-04 19:48:58 +02:00
const int iedge_dir = args . mod - > surfedges [ surf - > firstedge + k ] ;
const int iedge = iedge_dir > = 0 ? iedge_dir : - iedge_dir ;
const medge_t * edge = args . mod - > edges + iedge ;
const int vertex_index = iedge_dir > = 0 ? edge - > v [ 0 ] : edge - > v [ 1 ] ;
const mvertex_t * in_vertex = args . mod - > vertexes + vertex_index ;
2021-02-17 22:26:09 +01:00
vk_vertex_t vertex = {
2021-01-31 00:31:23 +01:00
{ in_vertex - > position [ 0 ] , in_vertex - > position [ 1 ] , in_vertex - > position [ 2 ] } ,
} ;
2023-01-22 00:45:29 +01:00
vertex . prev_pos [ 0 ] = in_vertex - > position [ 0 ] ;
vertex . prev_pos [ 1 ] = in_vertex - > position [ 1 ] ;
vertex . prev_pos [ 2 ] = in_vertex - > position [ 2 ] ;
2023-11-07 18:03:40 +01:00
// Compute texture coordinates, process tangent
2023-04-12 20:29:24 +02:00
{
vec4_t svec , tvec ;
2023-11-07 18:03:40 +01:00
if ( psurf & & ( psurf - > flags & Patch_Surface_TexMatrix ) ) {
svec [ 0 ] = surf - > texinfo - > vecs [ 0 ] [ 0 ] * psurf - > texmat_s [ 0 ] + surf - > texinfo - > vecs [ 1 ] [ 0 ] * psurf - > texmat_s [ 1 ] ;
svec [ 1 ] = surf - > texinfo - > vecs [ 0 ] [ 1 ] * psurf - > texmat_s [ 0 ] + surf - > texinfo - > vecs [ 1 ] [ 1 ] * psurf - > texmat_s [ 1 ] ;
svec [ 2 ] = surf - > texinfo - > vecs [ 0 ] [ 2 ] * psurf - > texmat_s [ 0 ] + surf - > texinfo - > vecs [ 1 ] [ 2 ] * psurf - > texmat_s [ 1 ] ;
svec [ 3 ] = surf - > texinfo - > vecs [ 0 ] [ 3 ] + psurf - > texmat_s [ 2 ] ;
tvec [ 0 ] = surf - > texinfo - > vecs [ 0 ] [ 0 ] * psurf - > texmat_t [ 0 ] + surf - > texinfo - > vecs [ 1 ] [ 0 ] * psurf - > texmat_t [ 1 ] ;
tvec [ 1 ] = surf - > texinfo - > vecs [ 0 ] [ 1 ] * psurf - > texmat_t [ 0 ] + surf - > texinfo - > vecs [ 1 ] [ 1 ] * psurf - > texmat_t [ 1 ] ;
tvec [ 2 ] = surf - > texinfo - > vecs [ 0 ] [ 2 ] * psurf - > texmat_t [ 0 ] + surf - > texinfo - > vecs [ 1 ] [ 2 ] * psurf - > texmat_t [ 1 ] ;
tvec [ 3 ] = surf - > texinfo - > vecs [ 1 ] [ 3 ] + psurf - > texmat_t [ 2 ] ;
2023-04-12 20:29:24 +02:00
} else {
Vector4Copy ( surf - > texinfo - > vecs [ 0 ] , svec ) ;
Vector4Copy ( surf - > texinfo - > vecs [ 1 ] , tvec ) ;
}
2023-11-07 18:03:40 +01:00
const float s = DotProduct ( in_vertex - > position , svec ) + svec [ 3 ] ;
const float t = DotProduct ( in_vertex - > position , tvec ) + tvec [ 3 ] ;
2023-04-12 20:29:24 +02:00
vertex . gl_tc [ 0 ] = s / surf - > texinfo - > texture - > width ;
vertex . gl_tc [ 1 ] = t / surf - > texinfo - > texture - > height ;
2023-11-02 18:47:31 +01:00
VectorCopy ( svec , tangent ) ;
VectorNormalize ( tangent ) ;
// "Inverted" texture mapping should not lead to inverted tangent/normal map
// Make sure that orientation is preserved.
{
vec4_t stnorm ;
CrossProduct ( tvec , svec , stnorm ) ;
if ( DotProduct ( stnorm , surf_normal ) < 0. )
VectorNegate ( tangent , tangent ) ;
}
2023-04-12 20:29:24 +02:00
}
2021-01-31 00:31:23 +01:00
// lightmap texture coordinates
2023-04-12 20:29:24 +02:00
{
float s = DotProduct ( in_vertex - > position , info - > lmvecs [ 0 ] ) + info - > lmvecs [ 0 ] [ 3 ] ;
s - = info - > lightmapmins [ 0 ] ;
s + = surf - > light_s * sample_size ;
s + = sample_size * 0.5f ;
s / = BLOCK_SIZE * sample_size ; //fa->texinfo->texture->width;
float t = DotProduct ( in_vertex - > position , info - > lmvecs [ 1 ] ) + info - > lmvecs [ 1 ] [ 3 ] ;
t - = info - > lightmapmins [ 1 ] ;
t + = surf - > light_t * sample_size ;
t + = sample_size * 0.5f ;
t / = BLOCK_SIZE * sample_size ; //fa->texinfo->texture->height;
vertex . lm_tc [ 0 ] = s ;
vertex . lm_tc [ 1 ] = t ;
}
2021-01-31 00:31:23 +01:00
2023-09-04 19:48:58 +02:00
// Compute smoothed normal if needed
if ( ! getSmoothedNormalFor ( args . mod , vertex_index , surface_index , vertex . normal ) ) {
VectorCopy ( surf_normal , vertex . normal ) ;
}
2021-03-07 01:40:35 +01:00
2021-11-19 18:52:10 +01:00
VectorCopy ( tangent , vertex . tangent ) ;
2023-02-26 03:38:22 +01:00
Vector4Set ( vertex . color , 255 , 255 , 255 , 255 ) ;
2023-05-30 21:14:44 +02:00
* ( p_vert + + ) = vertex ;
2021-01-31 00:31:23 +01:00
2021-06-12 21:54:53 +02:00
// Ray tracing apparently expects triangle list only (although spec is not very clear about this kekw)
2021-01-31 00:31:23 +01:00
if ( k > 1 ) {
2023-05-30 21:14:44 +02:00
* ( p_ind + + ) = ( uint16_t ) ( vertex_offset + 0 ) ;
* ( p_ind + + ) = ( uint16_t ) ( vertex_offset + k - 1 ) ;
* ( p_ind + + ) = ( uint16_t ) ( vertex_offset + k ) ;
2021-04-07 21:11:20 +02:00
index_count + = 3 ;
2021-02-10 19:33:44 +01:00
index_offset + = 3 ;
2021-01-31 00:31:23 +01:00
}
2023-09-04 19:48:58 +02:00
} // for surf->numedges
2021-01-27 21:00:11 +01:00
2021-04-07 21:11:20 +02:00
model_geometry - > element_count = index_count ;
2021-01-31 00:31:23 +01:00
vertex_offset + = surf - > numedges ;
2023-09-04 19:48:58 +02:00
} // for mod->nummodelsurfaces
2021-01-27 21:00:11 +01:00
}
2023-05-30 21:14:44 +02:00
ASSERT ( args . sizes . num_surfaces = = num_geometries ) ;
ASSERT ( args . sizes . animated_count = = animated_count ) ;
2021-04-13 19:13:17 +02:00
return true ;
2021-01-30 21:10:54 +01:00
}
2023-05-30 21:14:44 +02:00
static qboolean createRenderModel ( const model_t * mod , vk_brush_model_t * bmodel , const model_sizes_t sizes ) {
bmodel - > geometry = R_GeometryRangeAlloc ( sizes . num_vertices , sizes . num_indices ) ;
if ( ! bmodel - > geometry . block_handle . size ) {
2023-08-29 18:31:57 +02:00
ERR ( " Cannot allocate geometry for %s " , mod - > name ) ;
2023-05-30 21:14:44 +02:00
return false ;
}
vk_render_geometry_t * const geometries = Mem_Malloc ( vk_core . pool , sizeof ( vk_render_geometry_t ) * sizes . num_surfaces ) ;
bmodel - > surface_to_geometry_index = Mem_Malloc ( vk_core . pool , sizeof ( int ) * mod - > nummodelsurfaces ) ;
bmodel - > animated_indexes = Mem_Malloc ( vk_core . pool , sizeof ( int ) * sizes . animated_count ) ;
bmodel - > animated_indexes_count = sizes . animated_count ;
2023-08-31 18:11:17 +02:00
if ( sizes . animated_count > MAX_ANIMATED_TEXTURES ) {
WARN ( " Too many animated textures %d for model \" %s \" some surfaces can be static " , sizes . animated_count , mod - > name ) ;
}
2023-05-30 21:14:44 +02:00
const r_geometry_range_lock_t geom_lock = R_GeometryRangeLock ( & bmodel - > geometry ) ;
const qboolean fill_result = fillBrushSurfaces ( ( fill_geometries_args_t ) {
. mod = mod ,
. bmodel = bmodel ,
. sizes = sizes ,
. base_vertex_offset = bmodel - > geometry . vertices . unit_offset ,
. base_index_offset = bmodel - > geometry . indices . unit_offset ,
. out_geometries = geometries ,
. out_vertices = geom_lock . vertices ,
. out_indices = geom_lock . indices ,
} ) ;
R_GeometryRangeUnlock ( & geom_lock ) ;
if ( ! fill_result ) {
// TODO unlock and free buffers if failed? Currently we can't free geometry range, as it is being implicitly referenced by staging queue. Flush staging and free?
// This shouldn't really happen btw, kind of unrecoverable for now tbh.
// Also, we might just handle it, as the only reason it can fail is 16 bit index overflow.
// I. Split into smaller geometries sets.
// II. Make indices 32 bit
return false ;
}
2023-06-09 19:03:00 +02:00
if ( ! R_RenderModelCreate ( & bmodel - > render_model , ( vk_render_model_init_t ) {
2023-05-30 21:14:44 +02:00
. name = mod - > name ,
. geometries = geometries ,
. geometries_count = sizes . num_surfaces ,
2023-06-09 19:03:00 +02:00
. dynamic = false ,
2023-05-30 21:14:44 +02:00
} ) ) {
2023-08-29 18:31:57 +02:00
ERR ( " Could not create render model for brush model %s " , mod - > name ) ;
2023-05-30 21:14:44 +02:00
return false ;
}
return true ;
}
2023-03-03 07:32:08 +01:00
qboolean VK_BrushModelLoad ( model_t * mod ) {
if ( mod - > cache . data ) {
2023-08-29 18:31:57 +02:00
WARN ( " Model %s was already loaded " , mod - > name ) ;
2021-01-30 21:10:54 +01:00
return true ;
}
2023-08-29 18:31:57 +02:00
DEBUG ( " %s: %s flags=%08x " , __FUNCTION__ , mod - > name , mod - > flags ) ;
2021-01-30 21:10:54 +01:00
2023-05-30 21:14:44 +02:00
vk_brush_model_t * bmodel = Mem_Calloc ( vk_core . pool , sizeof ( * bmodel ) ) ;
ASSERT ( g_brush . models_count < COUNTOF ( g_brush . models ) ) ;
g_brush . models [ g_brush . models_count + + ] = bmodel ;
2021-06-14 18:19:21 +02:00
2023-05-30 21:14:44 +02:00
bmodel - > engine_model = mod ;
mod - > cache . data = bmodel ;
2023-04-28 21:35:48 +02:00
2023-08-28 19:12:02 +02:00
Matrix4x4_LoadIdentity ( bmodel - > prev_transform ) ;
bmodel - > prev_time = gpGlobals - > time ;
2023-05-30 21:14:44 +02:00
const model_sizes_t sizes = computeSizes ( mod ) ;
2021-04-13 19:13:17 +02:00
2023-05-30 21:14:44 +02:00
if ( sizes . num_surfaces ! = 0 ) {
if ( ! createRenderModel ( mod , bmodel , sizes ) ) {
2023-08-29 18:31:57 +02:00
ERR ( " Could not load brush model %s " , mod - > name ) ;
2023-06-05 21:53:25 +02:00
// FIXME Cannot deallocate bmodel as we might still have staging references to its memory
return false ;
}
}
if ( sizes . water_surfaces ) {
if ( ! brushCreateWaterModel ( mod , bmodel , sizes ) ) {
2023-08-29 18:31:57 +02:00
ERR ( " Could not load brush water model %s " , mod - > name ) ;
2023-06-05 21:53:25 +02:00
// FIXME Cannot deallocate bmodel as we might still have staging references to its memory
2023-05-30 21:14:44 +02:00
return false ;
2021-04-07 21:11:20 +02:00
}
2023-05-30 21:14:44 +02:00
}
2023-05-26 19:45:44 +02:00
2023-06-05 21:53:25 +02:00
g_brush . stat . total_vertices + = sizes . num_indices + sizes . water_vertices ;
g_brush . stat . total_indices + = sizes . num_vertices + sizes . water_indices ;
2021-04-07 21:11:20 +02:00
2023-08-29 18:31:57 +02:00
DEBUG ( " Model %s loaded surfaces: %d (of %d); total vertices: %u, total indices: %u " ,
2023-05-30 21:14:44 +02:00
mod - > name , bmodel - > render_model . num_geometries , mod - > nummodelsurfaces , g_brush . stat . total_vertices , g_brush . stat . total_indices ) ;
2021-01-30 21:10:54 +01:00
2021-01-27 21:00:11 +01:00
return true ;
}
2023-05-26 19:45:44 +02:00
static void VK_BrushModelDestroy ( vk_brush_model_t * bmodel ) {
ASSERT ( bmodel - > engine_model ) ;
2023-08-29 18:31:57 +02:00
DEBUG ( " %s: %s " , __FUNCTION__ , bmodel - > engine_model - > name ) ;
2023-05-26 19:45:44 +02:00
ASSERT ( bmodel - > engine_model - > cache . data = = bmodel ) ;
ASSERT ( bmodel - > engine_model - > type = = mod_brush ) ;
2021-04-07 21:11:20 +02:00
2023-06-05 21:53:25 +02:00
if ( bmodel - > water . surfaces_count ) {
2023-06-09 19:03:00 +02:00
R_RenderModelDestroy ( & bmodel - > water . render_model ) ;
2023-06-05 21:53:25 +02:00
Mem_Free ( ( int * ) bmodel - > water . surfaces_indices ) ;
Mem_Free ( bmodel - > water . render_model . geometries ) ;
R_GeometryRangeFree ( & bmodel - > water . geometry ) ;
}
2023-06-09 19:03:00 +02:00
R_RenderModelDestroy ( & bmodel - > render_model ) ;
2023-05-26 19:45:44 +02:00
2023-04-28 22:29:02 +02:00
if ( bmodel - > animated_indexes )
Mem_Free ( bmodel - > animated_indexes ) ;
2023-05-26 19:45:44 +02:00
2023-04-28 22:29:02 +02:00
if ( bmodel - > surface_to_geometry_index )
Mem_Free ( bmodel - > surface_to_geometry_index ) ;
2023-05-26 19:45:44 +02:00
if ( bmodel - > render_model . geometries ) {
2023-04-28 22:29:02 +02:00
Mem_Free ( bmodel - > render_model . geometries ) ;
2023-05-26 19:45:44 +02:00
R_GeometryRangeFree ( & bmodel - > geometry ) ;
}
bmodel - > engine_model - > cache . data = NULL ;
2021-04-07 21:11:20 +02:00
Mem_Free ( bmodel ) ;
}
2023-05-26 19:45:44 +02:00
void VK_BrushModelDestroyAll ( void ) {
2023-08-29 18:31:57 +02:00
DEBUG ( " Destroying %d brush models " , g_brush . models_count ) ;
2023-05-26 19:45:44 +02:00
for ( int i = 0 ; i < g_brush . models_count ; i + + )
VK_BrushModelDestroy ( g_brush . models [ i ] ) ;
g_brush . stat . total_vertices = 0 ;
g_brush . stat . total_indices = 0 ;
g_brush . models_count = 0 ;
2023-09-04 19:48:58 +02:00
memset ( g_brush . conn . edges , 0 , sizeof ( * g_brush . conn . edges ) * g_brush . conn . edges_capacity ) ;
memset ( g_brush . conn . vertices , 0 , sizeof ( * g_brush . conn . vertices ) * g_brush . conn . vertices_capacity ) ;
2021-02-01 01:53:24 +01:00
}
2023-03-03 07:32:08 +01:00
static rt_light_add_polygon_t loadPolyLight ( const model_t * mod , const int surface_index , const msurface_t * surf , const vec3_t emissive ) {
rt_light_add_polygon_t lpoly = { 0 } ;
lpoly . num_vertices = Q_min ( 7 , surf - > numedges ) ;
// TODO split, don't clip
if ( surf - > numedges > 7 )
2023-09-07 16:43:21 +02:00
WARN_THROTTLED ( 10 , " emissive surface %d has %d vertices; clipping to 7 " , surface_index , surf - > numedges ) ;
2023-03-03 07:32:08 +01:00
VectorCopy ( emissive , lpoly . emissive ) ;
for ( int i = 0 ; i < lpoly . num_vertices ; + + i ) {
const int iedge = mod - > surfedges [ surf - > firstedge + i ] ;
const medge_t * edge = mod - > edges + ( iedge > = 0 ? iedge : - iedge ) ;
const mvertex_t * vertex = mod - > vertexes + ( iedge > = 0 ? edge - > v [ 0 ] : edge - > v [ 1 ] ) ;
VectorCopy ( vertex - > position , lpoly . vertices [ i ] ) ;
}
lpoly . surface = surf ;
return lpoly ;
}
void R_VkBrushModelCollectEmissiveSurfaces ( const struct model_s * mod , qboolean is_worldmodel ) {
2023-04-18 20:32:34 +02:00
vk_brush_model_t * const bmodel = mod - > cache . data ;
ASSERT ( bmodel ) ;
2023-09-29 18:48:32 +02:00
const xvk_mapent_func_any_t * func_any = getModelFuncAnyPatch ( mod ) ;
const qboolean is_static = is_worldmodel | | ( func_any & & func_any - > origin_patched ) ;
2023-04-18 20:32:34 +02:00
2023-03-03 07:32:08 +01:00
typedef struct {
int model_surface_index ;
int surface_index ;
const msurface_t * surf ;
vec3_t emissive ;
} emissive_surface_t ;
emissive_surface_t emissive_surfaces [ MAX_SURFACE_LIGHTS ] ;
2023-08-31 17:45:51 +02:00
int geom_indices [ MAX_SURFACE_LIGHTS ] ;
2023-03-03 07:32:08 +01:00
int emissive_surfaces_count = 0 ;
2023-03-04 01:41:11 +01:00
// Load list of all emissive surfaces
2023-03-03 07:32:08 +01:00
for ( int i = 0 ; i < mod - > nummodelsurfaces ; + + i ) {
const int surface_index = mod - > firstmodelsurface + i ;
const msurface_t * surf = mod - > surfaces + surface_index ;
2023-04-28 21:35:48 +02:00
switch ( getSurfaceType ( surf , surface_index ) ) {
case BrushSurface_Regular :
case BrushSurface_Animated :
break ;
default :
2023-03-03 07:32:08 +01:00
continue ;
2023-04-28 21:35:48 +02:00
}
2023-03-03 07:32:08 +01:00
const int tex_id = surf - > texinfo - > texture - > gl_texturenum ; // TODO animation?
vec3_t emissive ;
2023-03-03 08:23:31 +01:00
const xvk_patch_surface_t * const psurf = R_VkPatchGetSurface ( surface_index ) ;
if ( psurf & & ( psurf - > flags & Patch_Surface_Emissive ) ) {
VectorCopy ( psurf - > emissive , emissive ) ;
} else if ( RT_GetEmissiveForTexture ( emissive , tex_id ) ) {
// emissive
} else {
// not emissive, continue to the next
continue ;
}
2023-03-03 07:32:08 +01:00
2023-08-29 19:39:36 +02:00
DEBUG ( " %d: i=%d surf_index=%d tex_id=%d patch=%d(%#x) => emissive=(%f,%f,%f) " , emissive_surfaces_count , i , surface_index , tex_id , ! ! psurf , psurf ? psurf - > flags : 0 , emissive [ 0 ] , emissive [ 1 ] , emissive [ 2 ] ) ;
2023-03-03 08:23:31 +01:00
if ( emissive_surfaces_count = = MAX_SURFACE_LIGHTS ) {
2023-08-29 18:31:57 +02:00
ERR ( " Too many emissive surfaces for model %s: max=%d " , mod - > name , MAX_SURFACE_LIGHTS ) ;
2023-03-03 08:23:31 +01:00
break ;
2023-03-03 07:32:08 +01:00
}
2023-03-03 08:23:31 +01:00
emissive_surface_t * const surface = & emissive_surfaces [ emissive_surfaces_count + + ] ;
surface - > model_surface_index = i ;
surface - > surface_index = surface_index ;
surface - > surf = surf ;
VectorCopy ( emissive , surface - > emissive ) ;
2023-03-03 07:32:08 +01:00
}
2023-03-04 01:41:11 +01:00
// Clear old per-geometry emissive values. The new emissive values will be assigned by the loop below only to the relevant geoms
for ( int i = 0 ; i < bmodel - > render_model . num_geometries ; + + i ) {
vk_render_geometry_t * const geom = bmodel - > render_model . geometries + i ;
VectorClear ( geom - > emissive ) ;
}
2023-04-18 20:32:34 +02:00
// Non-static brush models may move around and so must have their emissive surfaces treated as dynamic
if ( ! is_static ) {
2023-03-03 07:32:08 +01:00
if ( bmodel - > render_model . dynamic_polylights )
Mem_Free ( bmodel - > render_model . dynamic_polylights ) ;
bmodel - > render_model . dynamic_polylights_count = emissive_surfaces_count ;
bmodel - > render_model . dynamic_polylights = Mem_Malloc ( vk_core . pool , sizeof ( bmodel - > render_model . dynamic_polylights [ 0 ] ) * emissive_surfaces_count ) ;
}
2023-03-04 01:41:11 +01:00
// Apply all emissive surfaces found
2023-03-03 07:32:08 +01:00
for ( int i = 0 ; i < emissive_surfaces_count ; + + i ) {
const emissive_surface_t * const s = emissive_surfaces + i ;
2023-03-04 01:41:11 +01:00
2023-04-18 20:32:34 +02:00
rt_light_add_polygon_t polylight = loadPolyLight ( mod , s - > surface_index , s - > surf , s - > emissive ) ;
2023-09-29 18:48:32 +02:00
// func_any surfaces do not really belong to BSP+PVS system, so they can't be used
2023-04-18 20:32:34 +02:00
// for lights visibility calculation directly.
2023-09-29 18:48:32 +02:00
if ( func_any & & func_any - > origin_patched ) {
2023-04-18 20:32:34 +02:00
// TODO this is not really dynamic, but this flag signals using MovingSurface visibility calc
polylight . dynamic = true ;
matrix3x4 m ;
Matrix3x4_LoadIdentity ( m ) ;
2023-09-29 18:48:32 +02:00
Matrix3x4_SetOrigin ( m , func_any - > origin [ 0 ] , func_any - > origin [ 1 ] , func_any - > origin [ 2 ] ) ;
2023-04-18 20:32:34 +02:00
polylight . transform_row = & m ;
}
// Static emissive surfaces are added immediately, as they are drawn all the time.
// Non-static ones will be applied later when the model is actually rendered
if ( is_static ) {
2023-03-03 07:32:08 +01:00
RT_LightAddPolygon ( & polylight ) ;
2023-03-04 01:41:11 +01:00
} else {
bmodel - > render_model . dynamic_polylights [ i ] = polylight ;
2023-03-03 07:32:08 +01:00
}
2023-03-03 08:23:31 +01:00
2023-03-04 01:41:11 +01:00
// Assign the emissive value to the right geometry
2023-08-31 17:45:51 +02:00
const int geom_index = bmodel - > surface_to_geometry_index [ s - > model_surface_index ] ;
geom_indices [ i ] = geom_index ;
VectorCopy ( polylight . emissive , bmodel - > render_model . geometries [ geom_index ] . emissive ) ;
2023-03-03 07:32:08 +01:00
}
2023-08-31 17:45:51 +02:00
if ( emissive_surfaces_count > 0 ) {
// Update emissive values in kusochki. This is required because initial VK_BrushModelLoad happens before we've read
// RAD data in vk_light.c, so the emissive values are empty. This is the place and time where we actually get to
// know them, so let's fixup things.
// TODO minor optimization: sort geom_indices to have a better chance for them to be sequential
2023-09-07 17:16:47 +02:00
{
// Make sure that staging has been flushed.
// Updating materials leads to staging an upload to the same memory that we've just staged an upload to.
// This doesn't please the validator.
// Ensure that these uploads are not mixed into the same unsynchronized stream.
// TODO this might be not great for performance (extra waiting for GPU), so a better solution should be considered. E.g. tracking and barrier-syncing regions to-be-reuploaded.
R_VkStagingFlushSync ( ) ;
}
2023-08-31 17:45:51 +02:00
R_RenderModelUpdateMaterials ( & bmodel - > render_model , geom_indices , emissive_surfaces_count ) ;
2023-08-29 18:31:57 +02:00
INFO ( " Loaded %d polylights for %s model %s " , emissive_surfaces_count , is_static ? " static " : " movable " , mod - > name ) ;
2023-08-31 17:45:51 +02:00
}
2023-03-03 07:32:08 +01:00
}
2023-10-17 17:43:38 +02:00
void VK_BrushUnloadTextures ( model_t * mod )
{
int i ;
for ( i = 0 ; i < mod - > numtextures ; i + + )
{
texture_t * tx = mod - > textures [ i ] ;
if ( ! tx | | tx - > gl_texturenum = = tglob . defaultTexture )
continue ; // free slot
2023-10-24 17:32:42 +02:00
R_TextureFree ( tx - > gl_texturenum ) ; // main texture
R_TextureFree ( tx - > fb_texturenum ) ; // luma texture
2023-10-17 17:43:38 +02:00
}
}