Paranoia2/cl_dll/render/gl_world_new.cpp

4026 lines
109 KiB
C++

/*
gl_world.cpp - world and bmodel rendering
Copyright (C) 2016 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "hud.h"
#include "cl_util.h"
#include "gl_local.h"
#include "pm_defs.h"
#include "event_api.h"
#include <stringlib.h>
#include "gl_studio.h"
#include "gl_shader.h"
#include "gl_world.h"
#include "gl_grass.h"
#include "gl_occlusion.h"
#include "vertex_fmt.h"
static gl_world_t worlddata;
gl_world_t *world = &worlddata;
static Vector env_dir[] =
{
Vector( 1.0f, 0.0f, 0.0f ),
Vector( -1.0f, 0.0f, 0.0f ),
Vector( 0.0f, 1.0f, 0.0f ),
Vector( 0.0f, -1.0f, 0.0f ),
Vector( 0.0f, 0.0f, 1.0f ),
Vector( 0.0f, 0.0f, -1.0f )
};
/*
==================
Mod_SampleSizeForFace
return the current lightmap resolution per face
==================
*/
int Mod_SampleSizeForFace( msurface_t *surf )
{
if( !surf || !surf->texinfo )
return LM_SAMPLE_SIZE;
// world luxels has more priority
if( FBitSet( surf->texinfo->flags, TEX_WORLD_LUXELS ))
return 1;
if( FBitSet( surf->texinfo->flags, TEX_EXTRA_LIGHTMAP ))
return LM_SAMPLE_EXTRASIZE;
if( surf->texinfo->faceinfo )
return surf->texinfo->faceinfo->texture_step;
return LM_SAMPLE_SIZE;
}
gl_texbuffer_t *Surf_GetSubview( mextrasurf_t *es )
{
ASSERT( glState.stack_position >= 0 && glState.stack_position < MAX_REF_STACK );
int handle = es->subtexture[glState.stack_position];
if( handle > 0 && handle <= MAX_SUBVIEW_TEXTURES )
return &tr.subviewTextures[handle-1];
return NULL;
}
bool Surf_CheckSubview( mextrasurf_t *es, bool puddle )
{
ASSERT( glState.stack_position >= 0 && glState.stack_position < MAX_REF_STACK );
int handle = es->subtexture[glState.stack_position];
if( !handle ) return false;
ASSERT( handle > 0 && handle <= MAX_SUBVIEW_TEXTURES );
// we can't directly compare here
if(( tr.realframecount - tr.subviewTextures[handle-1].texframe ) <= 1 )
{
if( puddle && FBitSet( es->surf->flags, SURF_REFLECT_PUDDLE ))
return true;
if( !puddle && FBitSet( es->surf->flags, SURF_REFLECT ))
return true;
}
return false;
}
/*
=============================================================
CUBEMAP HANDLING & PROCESSING
=============================================================
*/
/*
==================
Mod_FreeCubemap
unload a given cubemap
==================
*/
static void Mod_FreeCubemap( mcubemap_t *m )
{
if( m->valid && m->texture && m->texture != tr.whiteCubeTexture )
FREE_TEXTURE( m->texture );
memset( m, 0, sizeof( *m ));
}
/*
==================
Mod_FreeCubemaps
purge all the cubemaps
from current level
==================
*/
static void Mod_FreeCubemaps( void )
{
for( int i = 0; i < world->num_cubemaps; i++ )
Mod_FreeCubemap( &world->cubemaps[i] );
Mod_FreeCubemap( &world->defaultCubemap );
world->rebuilding_cubemaps = CMREBUILD_INACTIVE;
world->build_default_cubemap = false;
world->loading_cubemaps = false;
world->cubemap_build_number = 0;
world->num_cubemaps = 0;
}
/*
==================
Mod_CheckCubemap
check cubemap sides for valid
==================
*/
static bool Mod_CheckCubemap( const char *name )
{
const char *suf[6] = { "px", "nx", "py", "ny", "pz", "nz" };
int valid_sides = 0;
char sidename[64];
int iCompare;
// FIXME: potentially unsafe checking: looking for DDS_CUBEMAP bit?
if( FILE_EXISTS( va( "maps/env/%s/%s.dds", world->name, name )))
return true;
for( int i = 0; i < 6; i++ )
{
Q_snprintf( sidename, sizeof( sidename ), "maps/env/%s/%s%s.tga", world->name, name, suf[i] );
if( COMPARE_FILE_TIME( worldmodel->name, sidename, &iCompare ) && iCompare <= 0 )
valid_sides++;
}
return (valid_sides == 6) ? true : false;
}
/*
==================
Mod_DeleteCubemap
remove cubemap images from HDD
==================
*/
static void Mod_DeleteCubemap( const char *name )
{
const char *suf[6] = { "px", "nx", "py", "ny", "pz", "nz" };
char sidename[64];
for( int i = 0; i < 6; i++ )
{
Q_snprintf( sidename, sizeof( sidename ), "maps/env/%s/%s%s.tga", world->name, name, suf[i] );
if( FILE_EXISTS( sidename ))
Sys_RemoveFile( sidename );
}
}
/*
==================
Mod_LoadCubemap
load cubemap into
video memory
==================
*/
static bool Mod_LoadCubemap( mcubemap_t *m )
{
int flags = 0;
if( m->valid && !m->texture )
{
if( GL_Support( R_SEAMLESS_CUBEMAP ))
SetBits( flags, TF_BORDER ); // seamless cubemaps have support for border
else SetBits( flags, TF_CLAMP ); // default method
m->texture = LOAD_TEXTURE( m->name, NULL, 0, flags );
// make sure what is really cubemap
if( RENDER_GET_PARM( PARM_TEX_TARGET, m->texture ) == GL_TEXTURE_CUBE_MAP_ARB )
m->valid = true;
else m->valid = false;
// NOTE: old DDS cubemaps has no mip-levels
m->numMips = RENDER_GET_PARM( PARM_TEX_MIPCOUNT, m->texture );
}
return m->valid;
}
/*
==================
GL_LoadAndRebuildCubemaps
rebuild cubemaps that older than bspfile
loading actual cubemaps into videomemory
==================
*/
void GL_LoadAndRebuildCubemaps( int refParams )
{
if( !world->loading_cubemaps && world->rebuilding_cubemaps == CMREBUILD_INACTIVE )
return; // job is done
// we are in cubemap-rendering mode
if( FBitSet( refParams, RP_ENVVIEW|RP_SKYVIEW ))
return;
if( world->rebuilding_cubemaps != CMREBUILD_INACTIVE )
{
if( world->build_default_cubemap )
{
mcubemap_t *cm = &world->defaultCubemap;
if( world->rebuilding_cubemaps == CMREBUILD_WAITING )
{
cm->valid = Mod_CheckCubemap( "default" ); // need for rebuild?
world->rebuilding_cubemaps = CMREBUILD_CHECKING;
if( !cm->valid )
ALERT( at_error, "GL_RebuildCubemaps: can't build default cubemap\n" );
world->build_default_cubemap = false; // done
}
if( world->build_default_cubemap )
{
Mod_DeleteCubemap( "default" );
// NOTE: engine function EnvShot not makes shots immediately
// but create a queue. So we will wait for a next frame
ENV_SHOT( cm->origin, va( "%s.tga", cm->name ), false, cm->size );
world->rebuilding_cubemaps = CMREBUILD_WAITING;
return;
}
}
for( ; world->cubemap_build_number < world->num_cubemaps; world->cubemap_build_number++ )
{
mcubemap_t *cm = &world->cubemaps[world->cubemap_build_number];
if( world->rebuilding_cubemaps == CMREBUILD_WAITING )
{
cm->valid = Mod_CheckCubemap( va( "cube#%i", world->cubemap_build_number )); // need for rebuild?
world->rebuilding_cubemaps = CMREBUILD_CHECKING;
if( !cm->valid )
{
ALERT( at_error, "GL_RebuildCubemaps: can't build cube#%i\n", world->cubemap_build_number );
continue; // to avoid infinity cycle
}
}
if( cm->valid ) continue; // it's valid cubemap
Mod_DeleteCubemap( va( "cube#%i", world->cubemap_build_number ));
// NOTE: engine function EnvShot not makes shots immediately
// but create a queue. So we will wait for a next frame
ENV_SHOT( cm->origin, va( "%s.tga", cm->name ), false, cm->size );
world->rebuilding_cubemaps = CMREBUILD_WAITING;
return;
}
// we reached the end of list
// next frame will be restored gamma
SetBits( cv_brightness->flags, FCVAR_CHANGED );
SetBits( cv_gamma->flags, FCVAR_CHANGED );
world->rebuilding_cubemaps = CMREBUILD_INACTIVE;
world->cubemap_build_number = 0;
tr.params_changed = true;
tr.glsl_valid_sequence++;
tr.fClearScreen = false;
}
// now all the cubemaps are recreated, so we can starts to upload them
if( world->loading_cubemaps )
{
int i;
Mod_LoadCubemap( &world->defaultCubemap );
for( i = 0; i < world->num_cubemaps; i++ )
{
mcubemap_t *cm = &world->cubemaps[i];
Vector vecStart, vecEnd;
pmtrace_t pmtrace;
Mod_LoadCubemap( cm );
// compute viewbox size
for( int j = 0; j < 6; j++ )
{
vecStart = cm->origin;
vecEnd = vecStart + env_dir[j] * 4096.0f;
gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
gEngfuncs.pEventAPI->EV_PlayerTrace( vecStart, vecStart + vecEnd, PM_WORLD_ONLY, -1, &pmtrace );
AddPointToBounds( pmtrace.endpos, cm->mins, cm->maxs );
}
}
// bind cubemaps onto world surfaces
// so we don't need to search them again
for( int i = 0; i < worldmodel->numsurfaces; i++ )
{
msurface_t *surf = &worldmodel->surfaces[i];
mextrasurf_t *es = surf->info;
CL_FindTwoNearestCubeMapForSurface( es->origin, surf, &es->cubemap[0], &es->cubemap[1] );
// compute lerp factor
float dist0 = ( es->cubemap[0]->origin - es->origin ).Length();
float dist1 = ( es->cubemap[1]->origin - es->origin ).Length();
es->lerpFactor = dist0 / (dist0 + dist1);
}
world->loading_cubemaps = false;
}
}
/*
=============================================================
WORLD LOADING
=============================================================
*/
/*
=================
Mod_LoadCubemaps
=================
*/
static void Mod_LoadCubemaps( const byte *base, const dlump_t *l )
{
dcubemap_t *in;
mcubemap_t *out;
int i, count;
in = (dcubemap_t *)(base + l->fileofs);
if( l->filelen % sizeof( *in ))
HOST_ERROR( "Mod_LoadCubemaps: funny lump size\n" );
count = l->filelen / sizeof( *in );
if( count >= MAX_MAP_CUBEMAPS )
{
ALERT( at_error, "Mod_LoadCubemaps: map contain too many cubemaps. Will handle only first %i items\n", MAX_MAP_CUBEMAPS );
count = MAX_MAP_CUBEMAPS;
}
out = world->cubemaps;
world->num_cubemaps = count;
world->loading_cubemaps = (count > 0) ? true : false;
// makes an default cubemap from skybox
if( FBitSet( world->features, WORLD_HAS_SKYBOX ) && (count > 0))
{
mcubemap_t *cm = &world->defaultCubemap;
Q_snprintf( cm->name, sizeof( cm->name ), "maps/env/%s/default", world->name );
cm->origin = (worldmodel->mins + worldmodel->maxs) * 0.5f;
cm->size = 256; // default cubemap larger than others
cm->valid = Mod_CheckCubemap( "default" ); // need for rebuild?
world->loading_cubemaps = true;
if( !cm->valid )
{
world->rebuilding_cubemaps = CMREBUILD_CHECKING;
world->build_default_cubemap = true;
}
}
else
{
// using stub as default cubemap
mcubemap_t *cm = &world->defaultCubemap;
Q_snprintf( cm->name, sizeof( cm->name ), "*whiteCube" );
cm->origin = (worldmodel->mins + worldmodel->maxs) * 0.5f;
cm->texture = tr.whiteCubeTexture;
cm->valid = true;
cm->size = 4;
}
for( int i = 0; i < count; i++, in++, out++ )
{
// build a cubemap name like enum
Q_snprintf( out->name, sizeof( out->name ), "maps/env/%s/cube#%i", world->name, i );
out->valid = Mod_CheckCubemap( va( "cube#%i", i )); // need for rebuild?
if( !out->valid ) world->rebuilding_cubemaps = CMREBUILD_CHECKING;
VectorCopy( in->origin, out->origin );
ClearBounds( out->mins, out->maxs );
out->size = in->size;
if( out->size <= 0 )
out->size = DEFAULT_CUBEMAP_SIZE;
out->size = NearestPOW( out->size, false );
out->size = bound( 1, out->size, 512 );
}
// user request for disable autorebuild
if( gEngfuncs.CheckParm( "-noautorebuildcubemaps", NULL ))
{
world->rebuilding_cubemaps = CMREBUILD_INACTIVE;
world->build_default_cubemap = false;
}
}
/*
========================
Mod_CopyMaterialDesc
copy params from description
to real material struct
========================
*/
static void Mod_CopyMaterialDesc( material_t *mat, matdesc_t *desc )
{
mat->smoothness = desc->smoothness;
mat->detailScale[0] = desc->detailScale[0];
mat->detailScale[1] = desc->detailScale[1];
mat->reflectScale = desc->reflectScale;
mat->refractScale = desc->refractScale;
mat->aberrationScale = desc->aberrationScale;
mat->reliefScale = desc->reliefScale;
mat->effects = desc->effects;
}
/*
========================
Mod_LoadWorldMaterials
build a material for each world texture
========================
*/
static void Mod_LoadWorldMaterials( void )
{
char diffuse[128], bumpmap[128];
char glossmap[128], glowmap[128];
char heightmap[128];
world->materials = (material_t *)Mem_Alloc( sizeof( material_t ) * worldmodel->numtextures );
for( int i = 0; i < worldmodel->numtextures; i++ )
{
texture_t *tx = worldmodel->textures[i];
material_t *mat = &world->materials[i];
// bad texture?
if( !tx || !tx->name[0] ) continue;
// make cross-links for consistency
tx->material = mat;
mat->pSource = tx;
// build material names
Q_snprintf( diffuse, sizeof( diffuse ), "textures/%s", tx->name );
Q_snprintf( bumpmap, sizeof( bumpmap ), "textures/%s_norm", tx->name );
Q_snprintf( glossmap, sizeof( glossmap ), "textures/%s_gloss", tx->name );
Q_snprintf( glowmap, sizeof( glowmap ), "textures/%s_luma", tx->name );
Q_snprintf( heightmap, sizeof( heightmap ), "textures/%s_hmap", tx->name );
if( IMAGE_EXISTS( diffuse ))
{
mat->gl_diffuse_id = LOAD_TEXTURE( diffuse, NULL, 0, 0 );
if( tx->gl_texturenum != tr.defaultTexture )
FREE_TEXTURE( tx->gl_texturenum ); // release wad-texture
// so engine can be draw HQ image for gl_renderer 0
// FIXME: what about detail texture scales ?
tx->gl_texturenum = mat->gl_diffuse_id;
if( RENDER_GET_PARM( PARM_TEX_FLAGS, tx->gl_texturenum ) & TF_HAS_ALPHA )
mat->flags |= BRUSH_HAS_ALPHA;
}
else
{
// use texture from wad
mat->gl_diffuse_id = tx->gl_texturenum;
}
if( IMAGE_EXISTS( bumpmap ))
{
mat->gl_normalmap_id = LOAD_TEXTURE( bumpmap, NULL, 0, TF_NORMALMAP );
}
else
{
// try alternate suffix
Q_snprintf( bumpmap, sizeof( bumpmap ), "textures/%s_local", tx->name );
if( IMAGE_EXISTS( bumpmap ))
mat->gl_normalmap_id = LOAD_TEXTURE( bumpmap, NULL, 0, TF_NORMALMAP );
else mat->gl_normalmap_id = tr.normalmapTexture; // blank bumpy
}
if( IMAGE_EXISTS( glossmap ))
{
mat->gl_specular_id = LOAD_TEXTURE( glossmap, NULL, 0, 0 );
}
else
{
// try alternate suffix
Q_snprintf( glossmap, sizeof( glossmap ), "textures/%s_spec", tx->name );
if( IMAGE_EXISTS( glossmap ))
mat->gl_specular_id = LOAD_TEXTURE( glossmap, NULL, 0, 0 );
else mat->gl_specular_id = tr.blackTexture;
}
if( IMAGE_EXISTS( heightmap ))
{
mat->gl_heightmap_id = LOAD_TEXTURE( heightmap, NULL, 0, 0 );
}
else
{
// try alternate suffix
Q_snprintf( heightmap, sizeof( heightmap ), "textures/%s_bump", tx->name );
if( IMAGE_EXISTS( heightmap ))
mat->gl_heightmap_id = LOAD_TEXTURE( heightmap, NULL, 0, 0 );
else mat->gl_heightmap_id = tr.blackTexture;
}
if( IMAGE_EXISTS( glowmap ))
mat->gl_glowmap_id = LOAD_TEXTURE( glowmap, NULL, 0, 0 );
else mat->gl_glowmap_id = tr.blackTexture;
// setup material flags
if( mat->gl_normalmap_id > 0 && mat->gl_normalmap_id != tr.normalmapTexture )
SetBits( mat->flags, BRUSH_HAS_BUMP );
if( mat->gl_specular_id > 0 && mat->gl_specular_id != tr.blackTexture )
SetBits( mat->flags, BRUSH_HAS_SPECULAR );
if( mat->gl_glowmap_id > 0 && mat->gl_glowmap_id != tr.blackTexture )
SetBits( mat->flags, BRUSH_HAS_LUMA );
if( mat->gl_heightmap_id > 0 && mat->gl_heightmap_id != tr.blackTexture )
SetBits( mat->flags, BRUSH_HAS_HEIGHTMAP );
if( tx->name[0] == '{' )
SetBits( mat->flags, BRUSH_TRANSPARENT );
if( !Q_strnicmp( tx->name, "scroll", 6 ))
SetBits( mat->flags, BRUSH_CONVEYOR );
if( !Q_strnicmp( tx->name, "{scroll", 7 ))
SetBits( mat->flags, BRUSH_CONVEYOR|BRUSH_TRANSPARENT );
if( !Q_strncmp( tx->name, "mirror", 6 ) || !Q_strncmp( tx->name, "reflect", 7 ))
SetBits( mat->flags, BRUSH_REFLECT );
if( !Q_strncmp( tx->name, "movie", 5 ))
SetBits( mat->flags, BRUSH_FULLBRIGHT );
if( tx->name[0] == '!' || !Q_strncmp( tx->name, "water", 5 ))
{
SetBits( mat->flags, BRUSH_REFLECT|BRUSH_LIQUID );
if( tr.waterTextures[0] )
SetBits( mat->flags, BRUSH_HAS_BUMP );
}
if( !Q_strncmp( tx->name, "sky", 3 ))
SetBits( world->features, WORLD_HAS_SKYBOX );
// setup material constants
matdesc_t *desc = CL_FindMaterial( tx->name );
Mod_CopyMaterialDesc( mat, desc );
mat->gl_detailmap_id = desc->dt_texturenum;
if( mat->smoothness <= 0.0f ) // don't waste time
ClearBits( mat->flags, BRUSH_HAS_SPECULAR );
if( mat->gl_detailmap_id > 0 && mat->gl_detailmap_id != tr.grayTexture )
SetBits( mat->flags, BRUSH_HAS_DETAIL );
tx->effects = mat->effects;
}
}
static void Mod_SetupLeafExtradata( const dlump_t *l, const dlump_t *vis, const byte *buf )
{
dleaf_t *in = (dleaf_t *)(buf + l->fileofs);
mextraleaf_t *out;
world->numleafs = worldmodel->numleafs + 1; // world leafs + outside common leaf
world->leafs = out = (mextraleaf_t *)Mem_Alloc( sizeof( mextraleaf_t ) * world->numleafs );
world->totalleafs = l->filelen / sizeof( *in ); // keep the total leaf counting
for( int i = 0; i < world->numleafs; i++, in++, out++ )
{
VectorCopy( in->mins, out->mins );
VectorCopy( in->maxs, out->maxs );
}
}
/*
=================
Mod_SetupLeafLights
=================
*/
static void Mod_SetupLeafLights( void )
{
mextraleaf_t *out;
int i, j, k;
mleaf_t *leaf;
mworldlight_t *wl;
mlightprobe_t *lp;
out = (mextraleaf_t *)world->leafs;
wl = world->worldlights;
lp = world->leaflights;
j = k = 0;
for( i = 0; i < world->numleafs; i++, out++ )
{
leaf = INFO_LEAF( out, worldmodel );
// NOTE: lights already sorted by leafs
if( world->numworldlights > 0 && wl->leaf == leaf )
{
out->direct_lights = wl; // pointer to first light in the array that belong to this leaf
for( ;( j < world->numworldlights ) && (wl->leaf == leaf); j++, wl++ )
out->num_directlights++;
}
if( world->numleaflights > 0 && lp->leaf == leaf )
{
out->ambient_light = lp; // pointer to first light in the array that belong to this leaf
for( ;( k < world->numleaflights ) && (lp->leaf == leaf); k++, lp++ )
out->num_lightprobes++;
}
}
}
/*
=================
Mod_LoadVertNormals
=================
*/
static void Mod_LoadVertNormals( const byte *base, const dlump_t *l )
{
dnormallump_t *nhdr;
byte *data;
if( !l->filelen ) return;
data = (byte *)(base + l->fileofs);
nhdr = (dnormallump_t *)data;
// indexed normals
if( nhdr->ident == NORMIDENT )
{
int table_size = worldmodel->numsurfedges * sizeof( dvertnorm_t );
int data_size = nhdr->numnormals * sizeof( dnormal_t );
int total_size = sizeof( dnormallump_t ) + table_size + data_size;
if( l->filelen != total_size )
HOST_ERROR( "Mod_LoadVertNormals: funny lump size\n" );
data += sizeof( dnormallump_t );
// alloc remap table
world->surfnormals = (dvertnorm_t *)Mem_Alloc( table_size );
memcpy( world->surfnormals, data, table_size );
data += table_size;
// copy normals data
world->normals = (dnormal_t *)Mem_Alloc( data_size );
memcpy( world->normals, data, data_size );
world->numnormals = nhdr->numnormals;
}
else
{
// old method...
int count;
dnormal_t *in;
in = (dnormal_t *)(base + l->fileofs);
if( l->filelen % sizeof( *in ))
HOST_ERROR( "Mod_LoadVertNormals: funny lump size\n" );
count = l->filelen / sizeof( *in );
// all the other counts are invalid
if( count == worldmodel->numvertexes )
{
world->normals = (dnormal_t *)Mem_Alloc( count * sizeof( dnormal_t ));
memcpy( world->normals, in, count * sizeof( dnormal_t ));
}
}
}
/*
=============
BuildVisForDLight
create visibility cache for dlight
=============
*/
static int Mod_BuildVisForDLight( mworldlight_t *wl )
{
int leafnum;
if( wl->emittype == emit_skylight )
{
// all leafs that contain skyface should be added to sun visibility
for( leafnum = 0; leafnum < worldmodel->numleafs; leafnum++ )
{
msurface_t **mark = worldmodel->leafs[leafnum + 1].firstmarksurface;
for( int markface = 0; markface < worldmodel->leafs[leafnum + 1].nummarksurfaces; markface++, mark++ )
{
msurface_t *surf = *mark;
if( FBitSet( surf->flags, SURF_DRAWSKY ))
{
MergeDLightVis( wl, leafnum + 1 );
break; // no reason to check all faces, go to next leaf
}
}
}
// technically light_environment is outside of world
return -1;
}
else
{
leafnum = Mod_PointInLeaf( wl->origin, worldmodel->nodes ) - worldmodel->leafs;
SetDLightVis( wl, leafnum );
return leafnum;
}
}
/*
=================
Mod_LoadWorldLights
=================
*/
static void Mod_LoadWorldLights( const byte *base, const dlump_t *l )
{
dworldlight_t *in;
mworldlight_t *out, *out2;
int i, count, dup = 0;
int total = 0;
if( !l->filelen ) return;
in = (dworldlight_t *)(base + l->fileofs);
if( l->filelen % sizeof( *in ))
{
ALERT( at_error, "Mod_LoadWorldLights: funny lump size in %s\n", world->name );
return;
}
count = l->filelen / sizeof( *in );
world->worldlights = out = (mworldlight_t *)Mem_Alloc( count * sizeof( *out ));
world->numworldlights = count;
for( i = 0; i < count; i++, in++, out++ )
{
out->emittype = (emittype_t)in->emittype;
out->style = in->style;
VectorCopy( in->origin, out->origin );
VectorCopy( in->intensity, out->intensity );
VectorCopy( in->normal, out->normal );
out->stopdot = in->stopdot;
out->stopdot2 = in->stopdot2;
out->fade = in->fade;
out->leaf = &worldmodel->leafs[in->leafnum];
out->radius = in->radius;
out->falloff = in->falloff;
if( out->emittype == emit_surface )
out->surface = worldmodel->surfaces + in->facenum;
out->modelnum = in->modelnumber;
out->lightnum = i; // !!!
out->shadow_x = 0xFFFF;
out->shadow_y = 0xFFFF;
if( out->emittype == emit_skylight )
out->stopdot2 = -1.0f;
}
out = world->worldlights;
for( i = dup = 0; i < count; i++, out++ )
{
if( out->intensity == g_vecZero )
out->emittype = emit_ignored;
if( out->emittype != emit_surface )
continue;
VectorMA( out->origin, 1.0f, out->normal, out->origin );
SetBits( out->surface->flags, SURF_FULLBRIGHT ); // emit faces is always fullbright
SetBits( out->surface->texinfo->texture->material->flags, BRUSH_FULLBRIGHT );
out2 = world->worldlights;
for( int j = 0; j < count; j++, out2++ )
{
if( out == out2 )
continue; // himself
if( out2->emittype != emit_surface )
continue;
if( out->surface == out2->surface )
{
out2->emittype = emit_ignored;
dup++;
}
}
}
out = world->worldlights;
for( i = 0; i < count; i++, out++ )
{
if( out->emittype == emit_ignored )
continue;
Mod_BuildVisForDLight( out );
total++;
}
// alloc shadow occlusion buffers
world->shadowzbuffers = (lightzbuffer_t *)Mem_Alloc( count * sizeof( lightzbuffer_t ));
ALERT( at_console, "%d world lights\n", total );
}
static void Mod_LoadVertexLighting( const byte *base, const dlump_t *l )
{
dvlightlump_t *vl;
if( !l->filelen ) return;
vl = (dvlightlump_t *)(base + l->fileofs);
if( vl->ident != VLIGHTIDENT )
return; // probably it's LUMP_LEAF_LIGHTING
if( vl->version != VLIGHT_VERSION )
return; // old version?
if( vl->nummodels <= 0 ) return;
world->vertex_lighting = (dvlightlump_t *)Mem_Alloc( l->filelen );
memcpy( world->vertex_lighting, vl, l->filelen );
}
static void Mod_LoadSurfaceLighting( const byte *base, const dlump_t *l )
{
dvlightlump_t *vl;
if( !l->filelen ) return;
vl = (dvlightlump_t *)(base + l->fileofs);
if( vl->ident != FLIGHTIDENT )
return; // probably it's LUMP_LEAF_LIGHTING
if( vl->version != FLIGHT_VERSION )
return; // old version?
if( vl->nummodels <= 0 ) return;
world->surface_lighting = (dvlightlump_t *)Mem_Alloc( l->filelen );
memcpy( world->surface_lighting, vl, l->filelen );
}
/*
=================
Mod_LoadVisLightData
worldlights visibility per face
=================
*/
static void Mod_LoadVisLightData( const byte *base, const dlump_t *l )
{
if( !l->filelen ) return;
world->vislightdata = (byte *)Mem_Alloc( l->filelen );
memcpy( world->vislightdata, (byte *)(base + l->fileofs), l->filelen );
}
/*
=================
Mod_LoadLeafAmbientLighting
and link into leafs
=================
*/
static void Mod_LoadLeafAmbientLighting( const byte *base, const dlump_t *l )
{
dleafsample_t *in;
dvlightlump_t *vl;
mlightprobe_t *out;
int i, count;
short curleaf = -1;
mextraleaf_t *leaf = NULL;
if( !l->filelen ) return;
vl = (dvlightlump_t *)(base + l->fileofs);
if( vl->ident == VLIGHTIDENT )
{
// probably it's LUMP_VERTEX_LIGHTING
Mod_LoadVertexLighting( base, l );
return;
}
in = (dleafsample_t *)(base + l->fileofs);
if( l->filelen % sizeof( *in ))
{
ALERT( at_warning, "Mod_LoadLeafAmbientLighting: funny lump size in %s\n", world->name );
return;
}
count = l->filelen / sizeof( *in );
world->leaflights = out = (mlightprobe_t *)Mem_Alloc( count * sizeof( *out ));
world->numleaflights = count;
for( i = 0; i < count; i++, in++, out++ )
{
memcpy( &out->cube, &in->ambient, sizeof( dlightcube_t ));
out->leaf = &worldmodel->leafs[in->leafnum];
VectorCopy( in->origin, out->origin );
}
}
/*
=================
Mod_SurfaceCompareBuild
sort faces before lightmap building
=================
*/
static int Mod_SurfaceCompareBuild( const unsigned short *a, const unsigned short *b )
{
msurface_t *surf1, *surf2;
surf1 = &worldmodel->surfaces[*a];
surf2 = &worldmodel->surfaces[*b];
if( FBitSet( surf1->flags, SURF_DRAWSKY ) && !FBitSet( surf2->flags, SURF_DRAWSKY ))
return -1;
if( !FBitSet( surf1->flags, SURF_DRAWSKY ) && FBitSet( surf2->flags, SURF_DRAWSKY ))
return 1;
if( FBitSet( surf1->flags, SURF_DRAWTURB ) && !FBitSet( surf2->flags, SURF_DRAWTURB ))
return -1;
if( !FBitSet( surf1->flags, SURF_DRAWTURB ) && FBitSet( surf2->flags, SURF_DRAWTURB ))
return 1;
// there faces owned with model in local space, so it *always* have non-identity transform matrix.
// move them to end of the list
if( FBitSet( surf1->flags, SURF_LOCAL_SPACE ) && !FBitSet( surf2->flags, SURF_LOCAL_SPACE ))
return 1;
if( !FBitSet( surf1->flags, SURF_LOCAL_SPACE ) && FBitSet( surf2->flags, SURF_LOCAL_SPACE ))
return -1;
return 0;
}
/*
=================
Mod_SurfaceCompareInGame
sort faces to reduce shader switches
=================
*/
static int Mod_SurfaceCompareInGame( const unsigned short *a, const unsigned short *b )
{
msurface_t *surf1, *surf2;
mextrasurf_t *esrf1, *esrf2;
surf1 = &worldmodel->surfaces[*a];
surf2 = &worldmodel->surfaces[*b];
esrf1 = surf1->info;
esrf2 = surf2->info;
if( esrf1->forwardScene[0].GetHandle() > esrf2->forwardScene[0].GetHandle() )
return 1;
if( esrf1->forwardScene[0].GetHandle() < esrf2->forwardScene[0].GetHandle() )
return -1;
if( surf1->texinfo->texture->gl_texturenum > surf2->texinfo->texture->gl_texturenum )
return 1;
if( surf1->texinfo->texture->gl_texturenum < surf2->texinfo->texture->gl_texturenum )
return -1;
if( esrf1->lightmaptexturenum > esrf2->lightmaptexturenum )
return 1;
if( esrf1->lightmaptexturenum < esrf2->lightmaptexturenum )
return -1;
if( !esrf1->parent || !esrf2->parent )
return 0;
if( esrf1->parent->hCachedMatrix > esrf2->parent->hCachedMatrix )
return 1;
if( esrf1->parent->hCachedMatrix < esrf2->parent->hCachedMatrix )
return -1;
return 0;
}
/*
=================
Mod_FinalizeWorld
build representation table
of surfaces sorted by texture
then alloc lightmaps
=================
*/
static void Mod_FinalizeWorld( void )
{
int i;
world->sortedfaces = (unsigned short *)Mem_Alloc( worldmodel->numsurfaces * sizeof( unsigned short ));
world->numsortedfaces = worldmodel->numsurfaces;
// initial filling
for( i = 0; i < worldmodel->numsurfaces; i++ )
world->sortedfaces[i] = i;
qsort( world->sortedfaces, worldmodel->numsurfaces, sizeof( unsigned short ), (cmpfunc)Mod_SurfaceCompareBuild );
// alloc surface lightmaps and compute lm coords (for sorted list)
for( i = 0; i < worldmodel->numsurfaces; i++ )
{
msurface_t *surf = &worldmodel->surfaces[world->sortedfaces[i]];
// allocate the lightmap coords, create lightmap textures (empty at this moment)
GL_AllocLightmapForFace( surf );
}
}
/*
=================
Mod_ShaderSceneForward
compute albedo with static lighting
=================
*/
static word Mod_ShaderSceneForward( msurface_t *s )
{
char glname[64];
char options[MAX_OPTIONS_LENGTH];
mextrasurf_t *es = s->info;
cl_entity_t *e = es->parent ? es->parent : GET_ENTITY( 0 );
// don't cache shader for skyfaces!
if( FBitSet( s->flags, SURF_DRAWSKY ))
return 0;
// mirror is actual only if we has actual screen texture!
bool mirror = Surf_CheckSubview( s->info );
if( es->forwardScene[mirror].IsValid() && es->lastRenderMode == e->curstate.rendermode )
return es->forwardScene[mirror].GetHandle(); // valid
Q_strncpy( glname, "forward/scene_bmodel", sizeof( glname ));
memset( options, 0, sizeof( options ));
mfaceinfo_t *landscape = landscape = s->texinfo->faceinfo;
material_t *mat = R_TextureAnimation( s )->material;
bool shader_translucent = false;
bool shader_additive = false;
bool using_cubemaps = false;
bool fullbright = false;
if(( FBitSet( mat->flags, BRUSH_FULLBRIGHT ) || R_FullBright( ) || mirror ) && !FBitSet( mat->flags, BRUSH_LIQUID ))
fullbright = true;
if( e->curstate.rendermode == kRenderTransAdd )
{
shader_additive = true;
fullbright = true;
}
if( fullbright )
{
GL_AddShaderDirective( options, "LIGHTING_FULLBRIGHT" );
}
else
{
// process lightstyles
for( int i = 0; i < MAXLIGHTMAPS && s->styles[i] != LS_NONE; i++ )
{
if( tr.sun_light_enabled && s->styles[i] == LS_SKY )
continue; // skip the sunlight due realtime sun is enabled
GL_AddShaderDirective( options, va( "APPLY_STYLE%i", i ));
}
if( CVAR_TO_BOOL( cv_brdf ))
GL_AddShaderDirective( options, "APPLY_PBS" );
// NOTE: deluxemap and normalmap are separate because some modes may using
// normalmap directly e.g. for mirror distorsion
if( es->normals )
{
GL_AddShaderDirective( options, "HAS_DELUXEMAP" );
GL_AddShaderDirective( options, "COMPUTE_TBN" );
}
if( r_lightmap->value > 0.0f && r_lightmap->value <= 2.0f )
{
if( r_lightmap->value == 1.0f && worldmodel && worldmodel->lightdata )
GL_AddShaderDirective( options, "LIGHTMAP_DEBUG" );
else if( r_lightmap->value == 2.0f && FBitSet( world->features, WORLD_HAS_DELUXEMAP ))
GL_AddShaderDirective( options, "LIGHTVEC_DEBUG" );
}
if( !RP_CUBEPASS() && ( CVAR_TO_BOOL( cv_specular ) && FBitSet( mat->flags, BRUSH_HAS_SPECULAR )))
GL_AddShaderDirective( options, "HAS_GLOSSMAP" );
if( FBitSet( mat->flags, BRUSH_HAS_LUMA ))
GL_AddShaderDirective( options, "HAS_LUMA" );
}
if( FBitSet( mat->flags, BRUSH_MULTI_LAYERS ) && landscape && landscape->terrain )
{
GL_AddShaderDirective( options, va( "TERRAIN_NUM_LAYERS %i", landscape->terrain->numLayers ));
GL_AddShaderDirective( options, "APPLY_TERRAIN" );
if( landscape->terrain->indexmap.gl_diffuse_id != 0 && CVAR_TO_BOOL( r_detailtextures ))
GL_AddShaderDirective( options, "HAS_DETAIL" );
}
else
{
if( FBitSet( mat->flags, BRUSH_HAS_DETAIL ) && CVAR_TO_BOOL( r_detailtextures ))
GL_AddShaderDirective( options, "HAS_DETAIL" );
}
if( !RP_CUBEPASS() && ( FBitSet( mat->flags, BRUSH_HAS_BUMP ) && (CVAR_TO_BOOL( cv_bump ) || FBitSet( mat->flags, BRUSH_LIQUID ))))
{
// FIXME: all the waternormals should be encoded as first frame
if( FBitSet( mat->flags, BRUSH_LIQUID ))
GL_EncodeNormal( options, tr.waterTextures[0] );
else GL_EncodeNormal( options, mat->gl_normalmap_id );
GL_AddShaderDirective( options, "HAS_NORMALMAP" );
}
// and finally select the render-mode
if( FBitSet( mat->flags, BRUSH_LIQUID ))
{
GL_AddShaderDirective( options, "LIQUID_SURFACE" );
if( tr.waterlevel >= 3 )
GL_AddShaderDirective( options, "LIQUID_UNDERWATER" );
// world watery with compiler feature
if( !FBitSet( s->flags, SURF_OF_SUBMODEL ) && FBitSet( world->features, WORLD_WATERALPHA ))
shader_translucent = true;
else if( e->curstate.rendermode == kRenderTransColor || e->curstate.rendermode == kRenderTransTexture )
shader_translucent = true;
}
else if(( world->num_cubemaps > 0 ) && CVAR_TO_BOOL( cv_cubemaps ) && (mat->reflectScale > 0.0f) && !RP_CUBEPASS( ))
{
if( !FBitSet( mat->flags, BRUSH_REFLECT ))
{
GL_AddShaderDirective( options, "REFLECTION_CUBEMAP" );
using_cubemaps = true;
}
}
if( e->curstate.rendermode == kRenderTransColor || e->curstate.rendermode == kRenderTransTexture )
shader_translucent = true;
if( shader_translucent )
GL_AddShaderDirective( options, "TRANSLUCENT" );
if( shader_additive )
GL_AddShaderDirective( options, "ADDITIVE" );
if( mirror )
GL_AddShaderDirective( options, "PLANAR_REFLECTION" );
if( shader_translucent && FBitSet( mat->flags, BRUSH_HAS_ALPHA ))
GL_AddShaderDirective( options, "ALPHA_GLASS" );
if( mat->refractScale > 0.0f && Q_stristr( options, "HAS_NORMALMAP" ))
GL_AddShaderDirective( options, "APPLY_REFRACTION" );
if( mat->aberrationScale > 0.0f && Q_stristr( options, "HAS_NORMALMAP" ))
GL_AddShaderDirective( options, "APPLY_ABERRATION" );
if( tr.fogEnabled )
GL_AddShaderDirective( options, "APPLY_FOG_EXP" );
word shaderNum = GL_FindUberShader( glname, options );
if( !shaderNum )
{
tr.fClearScreen = true; // to avoid ugly blur
SetBits( s->flags, SURF_NODRAW );
return 0; // something bad happens
}
if( shader_translucent )
GL_AddShaderFeature( shaderNum, SHADER_TRANSLUCENT|SHADER_USE_SCREENCOPY );
if( shader_additive )
GL_AddShaderFeature( shaderNum, SHADER_ADDITIVE );
if( using_cubemaps )
GL_AddShaderFeature( shaderNum, SHADER_USE_CUBEMAPS );
es->lastRenderMode = e->curstate.rendermode;
ClearBits( s->flags, SURF_NODRAW );
es->forwardScene[mirror].SetShader( shaderNum );
return shaderNum;
}
/*
=================
Mod_ShaderLightForward
compute dynamic lighting
=================
*/
static word Mod_ShaderLightForward( CDynLight *dl, msurface_t *s )
{
char glname[64];
char options[MAX_OPTIONS_LENGTH];
mfaceinfo_t *landscape = NULL;
mextrasurf_t *es = s->info;
switch( dl->type )
{
case LIGHT_SPOT:
if( es->forwardLightSpot.IsValid( ))
return es->forwardLightSpot.GetHandle(); // valid
break;
case LIGHT_OMNI:
if( es->forwardLightOmni.IsValid( ))
return es->forwardLightOmni.GetHandle(); // valid
break;
case LIGHT_DIRECTIONAL:
if( es->forwardLightProj.IsValid( ))
return es->forwardLightProj.GetHandle(); // valid
break;
}
Q_strncpy( glname, "forward/light_bmodel", sizeof( glname ));
memset( options, 0, sizeof( options ));
switch( dl->type )
{
case LIGHT_SPOT:
GL_AddShaderDirective( options, "LIGHT_SPOT" );
break;
case LIGHT_OMNI:
GL_AddShaderDirective( options, "LIGHT_OMNI" );
break;
case LIGHT_DIRECTIONAL:
GL_AddShaderDirective( options, "LIGHT_PROJ" );
break;
}
// mirror is actual only if we has actual screen texture!
bool mirror = Surf_CheckSubview( s->info );
material_t *mat = R_TextureAnimation( s )->material;
landscape = s->texinfo->faceinfo;
if( CVAR_TO_BOOL( cv_brdf ))
GL_AddShaderDirective( options, "APPLY_PBS" );
if( CVAR_TO_BOOL( cv_bump ) || FBitSet( mat->flags, BRUSH_LIQUID ))
{
if( FBitSet( mat->flags, BRUSH_HAS_BUMP ) && !FBitSet( dl->flags, DLF_NOBUMP ))
{
// FIXME: all the waternormals should be encoded as first frame
if( FBitSet( mat->flags, BRUSH_LIQUID ))
GL_EncodeNormal( options, tr.waterTextures[0] );
else GL_EncodeNormal( options, mat->gl_normalmap_id );
GL_AddShaderDirective( options, "HAS_NORMALMAP" );
GL_AddShaderDirective( options, "COMPUTE_TBN" );
}
}
if( CVAR_TO_BOOL( cv_specular ) && FBitSet( mat->flags, BRUSH_HAS_SPECULAR ))
GL_AddShaderDirective( options, "HAS_GLOSSMAP" );
if( FBitSet( mat->flags, BRUSH_MULTI_LAYERS ) && landscape && landscape->terrain )
{
GL_AddShaderDirective( options, va( "TERRAIN_NUM_LAYERS %i", landscape->terrain->numLayers ));
GL_AddShaderDirective( options, "APPLY_TERRAIN" );
if( landscape->terrain->indexmap.gl_diffuse_id != 0 && CVAR_TO_BOOL( r_detailtextures ) && glConfig.max_varying_floats > 48 )
GL_AddShaderDirective( options, "HAS_DETAIL" );
}
else
{
if( FBitSet( mat->flags, BRUSH_HAS_DETAIL ) && CVAR_TO_BOOL( r_detailtextures ) && glConfig.max_varying_floats > 48 )
GL_AddShaderDirective( options, "HAS_DETAIL" );
}
if( mirror && glConfig.max_varying_floats > 48 )
GL_AddShaderDirective( options, "PLANAR_REFLECTION" );
// and finally select the render-mode
if( FBitSet( mat->flags, BRUSH_LIQUID ))
{
GL_AddShaderDirective( options, "LIQUID_SURFACE" );
if( tr.waterlevel >= 3 )
GL_AddShaderDirective( options, "LIQUID_UNDERWATER" );
}
if( CVAR_TO_BOOL( r_shadows ) && !FBitSet( dl->flags, DLF_NOSHADOWS ))
{
// shadow cubemaps only support if GL_EXT_gpu_shader4 is support
if( dl->type == LIGHT_DIRECTIONAL && CVAR_TO_BOOL( r_sunshadows ))
{
GL_AddShaderDirective( options, "APPLY_SHADOW" );
}
else if( dl->type == LIGHT_SPOT || GL_Support( R_EXT_GPU_SHADER4 ))
{
GL_AddShaderDirective( options, "APPLY_SHADOW" );
if( r_shadows->value == 2.0f )
GL_AddShaderDirective( options, "SHADOW_PCF2X2" );
else if( r_shadows->value >= 3.0f )
GL_AddShaderDirective( options, "SHADOW_PCF3X3" );
}
}
word shaderNum = GL_FindUberShader( glname, options );
if( !shaderNum )
{
if( dl->type == LIGHT_DIRECTIONAL )
SetBits( s->flags, SURF_NOSUNLIGHT );
else SetBits( s->flags, SURF_NODLIGHT );
return 0; // something bad happens
}
// done
switch( dl->type )
{
case LIGHT_SPOT:
es->forwardLightSpot.SetShader( shaderNum );
ClearBits( s->flags, SURF_NODLIGHT );
break;
case LIGHT_OMNI:
es->forwardLightOmni.SetShader( shaderNum );
ClearBits( s->flags, SURF_NODLIGHT );
break;
case LIGHT_DIRECTIONAL:
es->forwardLightProj.SetShader( shaderNum );
ClearBits( s->flags, SURF_NOSUNLIGHT );
break;
}
return shaderNum;
}
/*
=================
Mod_ShaderSceneDeferred
compute deferred albedo
=================
*/
static word Mod_ShaderSceneDeferred( msurface_t *s )
{
char glname[64];
char options[MAX_OPTIONS_LENGTH];
bool using_cubemaps = false;
mextrasurf_t *es = s->info;
if( es->deferredScene.IsValid( ))
return es->deferredScene.GetHandle(); // valid
Q_strncpy( glname, "deferred/scene_bmodel", sizeof( glname ));
memset( options, 0, sizeof( options ));
material_t *mat = s->texinfo->texture->material;
mfaceinfo_t *landscape = s->texinfo->faceinfo;
if( FBitSet( mat->flags, BRUSH_MULTI_LAYERS ) && landscape && landscape->terrain )
{
GL_AddShaderDirective( options, va( "TERRAIN_NUM_LAYERS %i", landscape->terrain->numLayers ));
GL_AddShaderDirective( options, "APPLY_TERRAIN" );
if( landscape->terrain->indexmap.gl_diffuse_id != 0 && CVAR_TO_BOOL( r_detailtextures ))
GL_AddShaderDirective( options, "HAS_DETAIL" );
}
else
{
if( FBitSet( mat->flags, BRUSH_HAS_DETAIL ) && CVAR_TO_BOOL( r_detailtextures ))
GL_AddShaderDirective( options, "HAS_DETAIL" );
}
if( FBitSet( mat->flags, BRUSH_FULLBRIGHT ) || R_FullBright( ))
GL_AddShaderDirective( options, "LIGHTING_FULLBRIGHT" );
if( FBitSet( mat->flags, BRUSH_HAS_LUMA ))
GL_AddShaderDirective( options, "HAS_LUMA" );
if( !RP_CUBEPASS() && ( FBitSet( mat->flags, BRUSH_HAS_BUMP ) && CVAR_TO_BOOL( cv_bump )))
{
GL_AddShaderDirective( options, "HAS_NORMALMAP" );
GL_EncodeNormal( options, mat->gl_normalmap_id );
GL_AddShaderDirective( options, "COMPUTE_TBN" );
}
if( !RP_CUBEPASS() && ( CVAR_TO_BOOL( cv_specular ) && FBitSet( mat->flags, BRUSH_HAS_SPECULAR )))
{
GL_AddShaderDirective( options, "HAS_GLOSSMAP" );
if(( world->num_cubemaps > 0 ) && CVAR_TO_BOOL( cv_cubemaps ) && !RP_CUBEPASS( ))
{
GL_AddShaderDirective( options, "REFLECTION_CUBEMAP" );
using_cubemaps = true;
}
}
word shaderNum = GL_FindUberShader( glname, options );
if( !shaderNum )
{
tr.fClearScreen = true; // to avoid ugly blur
SetBits( s->flags, SURF_NODRAW );
return 0; // something bad happens
}
if( using_cubemaps )
GL_AddShaderFeature( shaderNum, SHADER_USE_CUBEMAPS );
// done
es->deferredScene.SetShader( shaderNum );
ClearBits( s->flags, SURF_NODRAW );
return shaderNum;
}
/*
=================
Mod_ShaderLightDeferred
compute deferred lighting
=================
*/
static word Mod_ShaderLightDeferred( msurface_t *s )
{
char glname[64];
char options[MAX_OPTIONS_LENGTH];
mextrasurf_t *es = s->info;
if( es->deferredLight.IsValid( ))
return es->deferredLight.GetHandle(); // valid
Q_strncpy( glname, "deferred/light_bmodel", sizeof( glname ));
memset( options, 0, sizeof( options ));
material_t *mat = s->texinfo->texture->material;
if( FBitSet( mat->flags, BRUSH_HAS_LUMA ) || FBitSet( mat->flags, BRUSH_FULLBRIGHT ) || R_FullBright( ))
GL_AddShaderDirective( options, "LIGHTING_FULLBRIGHT" );
word shaderNum = GL_FindUberShader( glname, options );
if( !shaderNum )
{
tr.fClearScreen = true; // to avoid ugly blur
SetBits( s->flags, SURF_NODRAW );
return 0; // something bad happens
}
// done
es->deferredLight.SetShader( shaderNum );
ClearBits( s->flags, SURF_NODRAW );
return shaderNum;
}
/*
=================
Mod_ShaderSceneDepth
return bmodel depth-shader
=================
*/
static word Mod_ShaderSceneDepth( msurface_t *s )
{
mextrasurf_t *es = s->info;
if( es->forwardDepth.IsValid( ))
return es->forwardDepth.GetHandle();
word shaderNum = GL_FindUberShader( "forward/depth_bmodel" );
es->forwardDepth.SetShader( shaderNum );
return shaderNum;
}
/*
=================
Mod_PrecacheShaders
precache shaders to reduce freezes in-game
=================
*/
static void Mod_PrecacheShaders( void )
{
msurface_t *surf;
int i;
// preload shaders for all the world faces (but ignore watery faces)
for( i = 0; i < worldmodel->submodels[0].numfaces; i++ )
{
surf = &worldmodel->surfaces[world->sortedfaces[i]];
if( !FBitSet( surf->flags, SURF_DRAWTURB|SURF_DRAWSKY ))
{
Mod_ShaderSceneForward( surf );
// also precache a default light shaders
Mod_ShaderLightForward( &tr.defaultlightSpot, surf );
Mod_ShaderLightForward( &tr.defaultlightOmni, surf );
Mod_ShaderLightForward( &tr.defaultlightProj, surf );
}
}
tr.params_changed = true;
#if 0
Msg( "sorted faces:\n" );
for( i = 0; i < worldmodel->numsurfaces; i++ )
{
surf = &worldmodel->surfaces[world->sortedfaces[i]];
mextrasurf_t *esrf = SURF_INFO( surf, worldmodel );
Msg( "face %i (local %s), style[1] %i\n", i, FBitSet( surf->flags, SURF_LOCAL_SPACE ) ? "Yes" : "No", surf->styles[1] );
}
#endif
}
/*
=================
Mod_ResortFaces
if shaders was changed we need to resort them
=================
*/
void Mod_ResortFaces( void )
{
int i;
if( !tr.params_changed ) return;
// rebuild shaders
for( i = 0; i < worldmodel->submodels[0].numfaces; i++ )
Mod_ShaderSceneForward( &worldmodel->surfaces[i] );
// resort faces
qsort( world->sortedfaces, worldmodel->numsurfaces, sizeof( unsigned short ), (cmpfunc)Mod_SurfaceCompareInGame );
#if 0
Msg( "resorted faces:\n" );
for( i = 0; i < worldmodel->numsurfaces; i++ )
{
msurface_t *surf = &worldmodel->surfaces[world->sortedfaces[i]];
mextrasurf_t *esrf = SURF_INFO( surf, worldmodel );
Msg( "face %i (submodel %s) (local %s), shader %i, lightmap %i, style[1] %i\n",
i, FBitSet( surf->flags, SURF_OF_SUBMODEL ) ? "Yes" : "No", FBitSet( surf->flags, SURF_LOCAL_SPACE ) ? "Yes" : "No",
esrf->shaderNum, esrf->lightmaptexturenum, surf->styles[1] );
}
#endif
}
/*
=================
Mod_ComputeFaceTBN
compute smooth TBN with baked normals
=================
*/
static void Mod_ComputeFaceTBN( msurface_t *surf, mextrasurf_t *esrf )
{
Vector texdirections[2];
Vector directionnormals[2];
Vector faceNormal, vertNormal;
int side;
for( int i = 0; i < esrf->numverts; i++ )
{
bvert_t *v = &world->vertexes[esrf->firstvertex + i];
int l = worldmodel->surfedges[surf->firstedge + i];
int vert = worldmodel->edges[abs(l)].v[(l > 0) ? 0 : 1];
if( world->surfnormals != NULL && world->normals != NULL )
{
l = world->surfnormals[surf->firstedge + i];
if( l >= 0 || l < world->numnormals )
vertNormal = Vector( world->normals[l].normal );
else ALERT( at_error, "normal index %d out of range (max %d)\n", l, world->numnormals );
}
else if( world->normals != NULL )
vertNormal = Vector( world->normals[vert].normal );
// calc unsmoothed tangent space
if( FBitSet( surf->flags, SURF_PLANEBACK ))
faceNormal = -surf->plane->normal;
else faceNormal = surf->plane->normal;
// fallback
if( vertNormal == g_vecZero )
vertNormal = faceNormal;
vertNormal = vertNormal.Normalize();
for( side = 0; side < 2; side++ )
{
texdirections[side] = CrossProduct( faceNormal, surf->info->lmvecs[!side] ).Normalize();
if( DotProduct( texdirections[side], surf->info->lmvecs[side] ) < 0.0f )
texdirections[side] = -texdirections[side];
}
for( side = 0; side < 2; side++ )
{
float dot = DotProduct( texdirections[side], vertNormal );
VectorMA( texdirections[side], -dot, vertNormal, directionnormals[side] );
directionnormals[side] = directionnormals[side].Normalize();
}
v->tangent = directionnormals[0];
v->binormal = -directionnormals[1];
v->normal = vertNormal;
}
}
/*
==================
GetLayerIndexForPoint
this function came from q3map2
==================
*/
static byte Mod_GetLayerIndexForPoint( indexMap_t *im, const Vector &mins, const Vector &maxs, const Vector &point )
{
Vector size;
if( !im->pixels ) return 0;
for( int i = 0; i < 3; i++ )
size[i] = ( maxs[i] - mins[i] );
float s = ( point[0] - mins[0] ) / size[0];
float t = ( maxs[1] - point[1] ) / size[1];
int x = s * im->width;
int y = t * im->height;
x = bound( 0, x, ( im->width - 1 ));
y = bound( 0, y, ( im->height - 1 ));
return im->pixels[y * im->width + x];
}
/*
=================
Mod_LayerNameForPixel
return layer name per pixel
=================
*/
bool Mod_CheckLayerNameForPixel( mfaceinfo_t *land, const Vector &point, const char *checkName )
{
terrain_t *terra;
layerMap_t *lm;
indexMap_t *im;
if( !land ) return true; // no landscape specified
terra = land->terrain;
if( !terra ) return true;
im = &terra->indexmap;
lm = &terra->layermap;
if( !Q_stricmp( checkName, lm->names[Mod_GetLayerIndexForPoint( im, land->mins, land->maxs, point )] ))
return true;
return false;
}
/*
=================
Mod_CheckLayerNameForSurf
return layer name per face
=================
*/
bool Mod_CheckLayerNameForSurf( msurface_t *surf, const char *checkName )
{
mtexinfo_t *tx = surf->texinfo;
mfaceinfo_t *land = tx->faceinfo;
terrain_t *terra;
layerMap_t *lm;
if( land != NULL && land->terrain != NULL )
{
terra = land->terrain;
lm = &terra->layermap;
for( int i = 0; i < terra->numLayers; i++ )
{
if( !Q_stricmp( checkName, lm->names[i] ))
return true;
}
}
else
{
const char *texname = surf->texinfo->texture->name;
if( !Q_stricmp( checkName, texname ))
return true;
}
return false;
}
/*
=================
Mod_ProcessLandscapes
handle all the landscapes per level
=================
*/
static void Mod_ProcessLandscapes( msurface_t *surf, mextrasurf_t *esrf )
{
mtexinfo_t *tx = surf->texinfo;
mfaceinfo_t *land = tx->faceinfo;
if( !land || land->groupid == 0 || !land->landname[0] )
return; // no landscape specified, just lightmap resolution
if( !land->terrain )
{
land->terrain = R_FindTerrain( land->landname );
if( !land->terrain )
{
// land name was specified in bsp but not declared in script file
ALERT( at_error, "Mod_ProcessLandscapes: %s missing description\n", land->landname );
land->landname[0] = '\0'; // clear name to avoid trying to find invalid terrain
return;
}
// prepare new landscape params
ClearBounds( land->mins, land->maxs );
// setup shared pointers
for( int i = 0; i < land->terrain->numLayers; i++ )
land->effects[i] = land->terrain->layermap.material[i]->effects;
land->heightmap = land->terrain->indexmap.pixels;
land->heightmap_width = land->terrain->indexmap.width;
land->heightmap_height = land->terrain->indexmap.height;
}
// update terrain bounds
AddPointToBounds( esrf->mins, land->mins, land->maxs );
AddPointToBounds( esrf->maxs, land->mins, land->maxs );
for( int j = 0; j < esrf->numverts; j++ )
{
bvert_t *v = &world->vertexes[esrf->firstvertex + j];
AddPointToBounds( v->vertex, land->mins, land->maxs );
}
}
/*
=================
Mod_MappingLandscapes
now landscape AABB is actual
mappping the surfaces
=================
*/
static void Mod_MappingLandscapes( msurface_t *surf, mextrasurf_t *esrf )
{
mtexinfo_t *tx = surf->texinfo;
mfaceinfo_t *land = tx->faceinfo;
float mappingScale;
terrain_t *terra;
bvert_t *v;
if( !land ) return; // no landscape specified
terra = land->terrain;
if( !terra ) return; // ooops! something bad happens!
// now we have landscape info!
SetBits( surf->flags, SURF_LANDSCAPE );
mappingScale = terra->texScale;
// setup layers here
if( surf->texinfo && surf->texinfo->texture && surf->texinfo->texture->material )
{
material_t *mat = surf->texinfo->texture->material;
ASSERT( mat != NULL );
mat->gl_diffuse_id = terra->layermap.gl_diffuse_id;
mat->gl_normalmap_id = terra->layermap.gl_normalmap_id;
mat->gl_specular_id = terra->layermap.gl_specular_id;
mat->gl_glowmap_id = tr.blackTexture;
mat->flags |= BRUSH_MULTI_LAYERS;
if( mat->gl_normalmap_id > 0 && mat->gl_normalmap_id != tr.normalmapTexture )
mat->flags |= BRUSH_HAS_BUMP;
if( mat->gl_specular_id > 0 && mat->gl_specular_id != tr.blackTexture )
mat->flags |= BRUSH_HAS_SPECULAR;
if( mat->gl_glowmap_id > 0 && mat->gl_glowmap_id != tr.blackTexture )
mat->flags |= BRUSH_HAS_LUMA;
if( RENDER_GET_PARM( PARM_TEX_FLAGS, mat->gl_specular_id ) & TF_HAS_ALPHA )
mat->flags |= BRUSH_GLOSSPOWER;
// refresh material constants
matdesc_t *desc = CL_FindMaterial( terra->indexmap.diffuse );
Mod_CopyMaterialDesc( mat, desc );
}
// mapping global diffuse texture
for( int i = 0; i < esrf->numverts; i++ )
{
v = &world->vertexes[esrf->firstvertex + i];
v->stcoord0[0] *= mappingScale;
v->stcoord0[1] *= mappingScale;
R_GlobalCoords( surf, v->vertex, v->stcoord0 );
}
}
/*
=================
Mod_FindStaticLights
find a mark lights that affected to this face
=================
*/
void Mod_FindStaticLights( byte *vislight, byte lights[MAXDYNLIGHTS], const Vector &origin )
{
mworldlight_t *wl = world->worldlights;
int indexes[32];
int i, count;
memset( lights, 255, sizeof( byte ) * MAXDYNLIGHTS );
count = 0;
// failed to vislightdata...
if( !vislight ) return;
// mark all the lights that can lit this face
for( i = 0; i < world->numworldlights; i++, wl++ )
{
if( wl->emittype == emit_ignored )
continue; // bad light?
// this face is invisible for this light
if( !CHECKVISBIT( vislight, i ))
continue;
if( count >= ARRAYSIZE( indexes ))
{
ALERT( at_aiconsole, "too many lights on a face\n" );
break;
}
indexes[count++] = i;// member this light
}
get_next_light:
float maxPhotons = 0.0;
int ignored = -1;
int light = 255;
for( i = 0; i < count; i++ )
{
if( indexes[i] == -1 )
continue;
wl = world->worldlights + indexes[i];
Vector delta = (wl->origin - origin);
float dist = Q_max( delta.Length(), 1.0 );
delta = delta.Normalize();
float ratio = 1.0 / (dist * dist);
Vector add = wl->intensity * ratio;
float photons = VectorMax( add );
// skylight has a maximum priority
if( photons > maxPhotons )
{
maxPhotons = photons;
light = indexes[i];
ignored = i;
}
}
if( ignored == -1 )
{
if( count > (int)cv_deferred_maxlights->value )
ALERT( at_aiconsole, "total %i lights affected to face\n", count );
return;
}
for( i = 0; i < (int)cv_deferred_maxlights->value && lights[i] != 255; i++ );
if( i < (int)cv_deferred_maxlights->value )
lights[i] = light; // nearest light for surf
indexes[ignored] = -1; // this light is handled
// if( count > (int)cv_deferred_maxlights->value && i == (int)cv_deferred_maxlights->value )
// Msg( "skipped light %i intensity %g, type %d\n", light, maxPhotons, world->worldlights[light].emittype );
goto get_next_light;
}
/*
=================
Mod_InitLightTexture
=================
*/
static void Mod_InitLightTexture( void )
{
mworldlight_t *wl = world->worldlights;
int height = ((world->numworldlights / 256) + 1) * 3;
int lightnum = 0;
int width = 256;
if( !world->numworldlights )
return;
Vector4D *worldlights = (Vector4D *)Mem_Alloc( sizeof( Vector4D ) * width * height );
// Msg( "light: %d %d\n", width, height );
for( int y = 0; y < height; y += 3 )
{
for( int x = 0; x < width; x++, lightnum++ )
{
if( lightnum == world->numworldlights )
break;
wl = &world->worldlights[lightnum];
worldlights[((y+0)*width)+x] = Vector4D( NormalToFloat( wl->normal ), wl->stopdot, wl->stopdot2, wl->emittype );
worldlights[((y+1)*width)+x] = Vector4D( wl->origin[0], wl->origin[1], wl->origin[2], wl->falloff );
worldlights[((y+2)*width)+x] = Vector4D( wl->intensity[0], wl->intensity[1], wl->intensity[2], wl->style );
}
}
tr.packed_lights_texture = CREATE_TEXTURE( "*worldlights", width, height, (byte *)worldlights, TF_STORAGE );
Mem_Free( worldlights );
}
/*
=================
Mod_InitBSPTreeTexture
=================
*/
static void Mod_InitBSPTreeTexture( void )
{
int planenum = 0;
int height = 256;
int width = 256;
int nodenum = 0;
mnode_t *cn, *child;
int x, y;
dclipnode_t out;
mplane_t *pl;
// planes are 4-th components float texture 256x56 (max 65536 planes)
Vector4D *worldplanes = (Vector4D *)Mem_Alloc( sizeof( Vector4D ) * width * height );
for( y = 0; y < height; y++ )
{
for( x = 0; x < width; x++, planenum++ )
{
if( planenum == worldmodel->numplanes )
break;
pl = &worldmodel->planes[planenum];
// we not enough free space to store type or signbits but these optimizations doesn't matter on GPU anyway
worldplanes[(y*width)+x] = Vector4D( pl->normal.x, pl->normal.y, pl->normal.z, pl->dist );
}
}
tr.packed_planes_texture = CREATE_TEXTURE( "*worldplanes", width, height, (byte *)worldplanes, TF_STORAGE );
Mem_Free( worldplanes );
// nodes are 4-th components float texture 256x256 (max 65536 nodes)
Vector4D *worldnodes = (Vector4D *)Mem_Alloc( sizeof( Vector4D ) * width * height );
for( y = 0; y < height; y++ )
{
for( x = 0; x < width; x++, nodenum++ )
{
if( nodenum == worldmodel->numnodes )
break; // all nodes are stored
cn = &worldmodel->nodes[nodenum];
out.planenum = cn->plane - worldmodel->planes;
// convert nodes to clipnodes
for( int j = 0; j < 2; j++ )
{
child = cn->children[j];
if( child->contents < 0 )
out.children[j] = child->contents;
else out.children[j] = child - worldmodel->nodes;
}
// FIXME: store into 4-th component something useful :)
worldnodes[(y*width)+x] = Vector4D( out.children[0], out.children[1], out.planenum, 0.0f ); // unused
}
}
tr.packed_nodes_texture = CREATE_TEXTURE( "*worldnodes", width, height, (byte *)worldnodes, TF_STORAGE );
Mem_Free( worldnodes );
// Msg( "bsp structure placed into textures (%i nodes, %i planes)\n", worldmodel->numnodes, worldmodel->numplanes );
}
/*
=================
Mod_InitBSPTreeTexture
=================
*/
void Mod_InitBSPModelsTexture( void )
{
cl_entity_t *visible_ents[MAX_VISIBLE_ENTS];
Vector absmin, absmax;
static double lastupdate;
gl_state_t *glm;
if( !CVAR_TO_BOOL( cv_deferred_tracebmodels ))
return;
if( !FBitSet( RI->params, RP_DEFERREDSCENE|RP_DEFERREDLIGHT ))
return;
if( lastupdate > Sys_DoubleTime( ))
return;
// don't upload models too often
lastupdate = Sys_DoubleTime() + 0.01;
world->num_visible_models = 0;
for( int i = 0; i < tr.num_draw_entities; i++ )
{
cl_entity_t *e = tr.draw_entities[i];
if( !e || !e->model || e->model->type != mod_brush )
continue;
if( e->curstate.rendermode != kRenderNormal )
continue;
glm = GL_GetCache( e->hCachedMatrix );
if( glm->m_bSkyEntity ) continue;
visible_ents[world->num_visible_models] = e;
world->num_visible_models++;
}
int height = worldmodel->numsubmodels;
int width = 8, flags = 0;
// data representation [width = 26][height = nummodels]
// first 6 floats - bounding box, next 16 floats - actual model matrix,
// next 2 float - rootnode, totalnodes, last 2 floats - first face,
Vector4D *worldmodels = (Vector4D *)stackalloc( sizeof( Vector4D ) * width * height );
for( int y = 0; y < world->num_visible_models; y++ )
{
cl_entity_t *e = visible_ents[y];
// grab the transformed vieworg
glm = GL_GetCache( e->hCachedMatrix );
matrix4x4 im = glm->transform.Invert();
model_t *m = e->model;
if( e->angles != g_vecZero )
{
TransformAABB( glm->transform, e->model->mins, e->model->maxs, absmin, absmax );
}
else
{
absmin = e->origin + e->model->mins;
absmax = e->origin + e->model->maxs;
}
// first 2 pixels - transformed bounding box, identity flag, rootnode
worldmodels[(y*width)+0] = Vector4D( absmin.x, absmin.y, absmin.z, e->hCachedMatrix > 0 ? 1.0f : 0.0f );
worldmodels[(y*width)+1] = Vector4D( absmax.x, absmax.y, absmax.z, m->hulls[0].firstclipnode );
// next 4 pixels - actual inverted model matrix
worldmodels[(y*width)+2] = Vector4D( im[0][0], im[1][0], im[2][0], im[3][0] );
worldmodels[(y*width)+3] = Vector4D( im[0][1], im[1][1], im[2][1], im[3][1] );
worldmodels[(y*width)+4] = Vector4D( im[0][2], im[1][2], im[2][2], im[3][2] );
worldmodels[(y*width)+5] = Vector4D( im[0][3], im[1][3], im[2][3], im[3][3] );
}
// if texture already present - update it
if( tr.packed_models_texture != 0 )
SetBits( flags, TF_UPDATE );
// it will automatically called glSubImage on next calls
tr.packed_models_texture = CREATE_TEXTURE( "*worldmodels", width, height, (byte *)worldmodels, TF_STORAGE|flags );
}
static void CreateBufferBaseGL21( bvert_t *arrayxvert )
{
static bvert_v0_gl21_t arraybvert[MAX_MAP_VERTS*4];
// convert to GLSL-compacted array
for( int i = 0; i < world->numvertexes; i++ )
{
arraybvert[i].vertex[0] = arrayxvert[i].vertex[0];
arraybvert[i].vertex[1] = arrayxvert[i].vertex[1];
arraybvert[i].vertex[2] = arrayxvert[i].vertex[2];
arraybvert[i].tangent[0] = arrayxvert[i].tangent[0];
arraybvert[i].tangent[1] = arrayxvert[i].tangent[1];
arraybvert[i].tangent[2] = arrayxvert[i].tangent[2];
arraybvert[i].binormal[0] = arrayxvert[i].binormal[0];
arraybvert[i].binormal[1] = arrayxvert[i].binormal[1];
arraybvert[i].binormal[2] = arrayxvert[i].binormal[2];
arraybvert[i].normal[0] = arrayxvert[i].normal[0];
arraybvert[i].normal[1] = arrayxvert[i].normal[1];
arraybvert[i].normal[2] = arrayxvert[i].normal[2];
arraybvert[i].stcoord0[0] = arrayxvert[i].stcoord0[0];
arraybvert[i].stcoord0[1] = arrayxvert[i].stcoord0[1];
arraybvert[i].stcoord0[2] = arrayxvert[i].stcoord0[2];
arraybvert[i].stcoord0[3] = arrayxvert[i].stcoord0[3];
arraybvert[i].lmcoord0[0] = arrayxvert[i].lmcoord0[0];
arraybvert[i].lmcoord0[1] = arrayxvert[i].lmcoord0[1];
arraybvert[i].lmcoord0[2] = arrayxvert[i].lmcoord0[2];
arraybvert[i].lmcoord0[3] = arrayxvert[i].lmcoord0[3];
arraybvert[i].lmcoord1[0] = arrayxvert[i].lmcoord1[0];
arraybvert[i].lmcoord1[1] = arrayxvert[i].lmcoord1[1];
arraybvert[i].lmcoord1[2] = arrayxvert[i].lmcoord1[2];
arraybvert[i].lmcoord1[3] = arrayxvert[i].lmcoord1[3];
memcpy( arraybvert[i].styles, arrayxvert[i].styles, MAXLIGHTMAPS );
}
world->cacheSize = world->numvertexes * sizeof( bvert_v0_gl21_t );
// create world vertex buffer
pglGenBuffersARB( 1, &world->vertex_buffer_object );
pglBindBufferARB( GL_ARRAY_BUFFER_ARB, world->vertex_buffer_object );
pglBufferDataARB( GL_ARRAY_BUFFER_ARB, world->cacheSize, &arraybvert[0], GL_STATIC_DRAW_ARB );
pglBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
}
static void BindBufferBaseGL21( void )
{
pglBindBufferARB( GL_ARRAY_BUFFER_ARB, world->vertex_buffer_object );
pglVertexAttribPointerARB( ATTR_INDEX_POSITION, 3, GL_FLOAT, 0, sizeof( bvert_v0_gl21_t ), (void *)offsetof( bvert_v0_gl21_t, vertex ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_POSITION );
pglVertexAttribPointerARB( ATTR_INDEX_TANGENT, 3, GL_FLOAT, 0, sizeof( bvert_v0_gl21_t ), (void *)offsetof( bvert_v0_gl21_t, tangent ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_TANGENT );
pglVertexAttribPointerARB( ATTR_INDEX_BINORMAL, 3, GL_FLOAT, 0, sizeof( bvert_v0_gl21_t ), (void *)offsetof( bvert_v0_gl21_t, binormal ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_BINORMAL );
pglVertexAttribPointerARB( ATTR_INDEX_NORMAL, 3, GL_FLOAT, 0, sizeof( bvert_v0_gl21_t ), (void *)offsetof( bvert_v0_gl21_t, normal ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_NORMAL );
pglVertexAttribPointerARB( ATTR_INDEX_TEXCOORD0, 4, GL_FLOAT, 0, sizeof( bvert_v0_gl21_t ), (void *)offsetof( bvert_v0_gl21_t, stcoord0 ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD0 );
pglVertexAttribPointerARB( ATTR_INDEX_TEXCOORD1, 4, GL_FLOAT, 0, sizeof( bvert_v0_gl21_t ), (void *)offsetof( bvert_v0_gl21_t, lmcoord0 ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD1 );
pglVertexAttribPointerARB( ATTR_INDEX_TEXCOORD2, 4, GL_FLOAT, 0, sizeof( bvert_v0_gl21_t ), (void *)offsetof( bvert_v0_gl21_t, lmcoord1 ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD2 );
pglVertexAttribPointerARB( ATTR_INDEX_LIGHT_STYLES, 4, GL_UNSIGNED_BYTE, 0, sizeof( bvert_v0_gl21_t ), (void *)offsetof( bvert_v0_gl21_t, styles ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_LIGHT_STYLES );
}
static void CreateBufferBaseGL30( bvert_t *arrayxvert )
{
static bvert_v0_gl30_t arraybvert[MAX_MAP_VERTS*4];
// convert to GLSL-compacted array
for( int i = 0; i < world->numvertexes; i++ )
{
arraybvert[i].vertex[0] = arrayxvert[i].vertex[0];
arraybvert[i].vertex[1] = arrayxvert[i].vertex[1];
arraybvert[i].vertex[2] = arrayxvert[i].vertex[2];
CompressNormalizedVector( arraybvert[i].normal, arrayxvert[i].normal );
CompressNormalizedVector( arraybvert[i].tangent, arrayxvert[i].tangent );
CompressNormalizedVector( arraybvert[i].binormal, arrayxvert[i].binormal );
arraybvert[i].stcoord0[0] = arrayxvert[i].stcoord0[0];
arraybvert[i].stcoord0[1] = arrayxvert[i].stcoord0[1];
arraybvert[i].stcoord0[2] = arrayxvert[i].stcoord0[2];
arraybvert[i].stcoord0[3] = arrayxvert[i].stcoord0[3];
arraybvert[i].lmcoord0[0] = arrayxvert[i].lmcoord0[0];
arraybvert[i].lmcoord0[1] = arrayxvert[i].lmcoord0[1];
arraybvert[i].lmcoord0[2] = arrayxvert[i].lmcoord0[2];
arraybvert[i].lmcoord0[3] = arrayxvert[i].lmcoord0[3];
arraybvert[i].lmcoord1[0] = arrayxvert[i].lmcoord1[0];
arraybvert[i].lmcoord1[1] = arrayxvert[i].lmcoord1[1];
arraybvert[i].lmcoord1[2] = arrayxvert[i].lmcoord1[2];
arraybvert[i].lmcoord1[3] = arrayxvert[i].lmcoord1[3];
memcpy( arraybvert[i].styles, arrayxvert[i].styles, MAXLIGHTMAPS );
memcpy( arraybvert[i].lights0, arrayxvert[i].lights0, MAXLIGHTMAPS );
memcpy( arraybvert[i].lights1, arrayxvert[i].lights1, MAXLIGHTMAPS );
}
world->cacheSize = world->numvertexes * sizeof( bvert_v0_gl30_t );
// create world vertex buffer
pglGenBuffersARB( 1, &world->vertex_buffer_object );
pglBindBufferARB( GL_ARRAY_BUFFER_ARB, world->vertex_buffer_object );
pglBufferDataARB( GL_ARRAY_BUFFER_ARB, world->cacheSize, &arraybvert[0], GL_STATIC_DRAW_ARB );
pglBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
}
static void BindBufferBaseGL30( void )
{
pglBindBufferARB( GL_ARRAY_BUFFER_ARB, world->vertex_buffer_object );
pglVertexAttribPointerARB( ATTR_INDEX_POSITION, 3, GL_FLOAT, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, vertex ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_POSITION );
pglVertexAttribPointerARB( ATTR_INDEX_TANGENT, 3, GL_BYTE, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, tangent ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_TANGENT );
pglVertexAttribPointerARB( ATTR_INDEX_BINORMAL, 3, GL_BYTE, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, binormal ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_BINORMAL );
pglVertexAttribPointerARB( ATTR_INDEX_NORMAL, 3, GL_BYTE, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, normal ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_NORMAL );
pglVertexAttribPointerARB( ATTR_INDEX_TEXCOORD0, 4, GL_FLOAT, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, stcoord0 ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD0 );
pglVertexAttribPointerARB( ATTR_INDEX_TEXCOORD1, 4, GL_FLOAT, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, lmcoord0 ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD1 );
pglVertexAttribPointerARB( ATTR_INDEX_TEXCOORD2, 4, GL_FLOAT, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, lmcoord1 ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD2 );
pglVertexAttribPointerARB( ATTR_INDEX_LIGHT_STYLES, 4, GL_UNSIGNED_BYTE, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, styles ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_LIGHT_STYLES );
pglVertexAttribPointerARB( ATTR_INDEX_LIGHT_NUMS0, 4, GL_UNSIGNED_BYTE, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, lights0 ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_LIGHT_NUMS0 );
pglVertexAttribPointerARB( ATTR_INDEX_LIGHT_NUMS1, 4, GL_UNSIGNED_BYTE, 0, sizeof( bvert_v0_gl30_t ), (void *)offsetof( bvert_v0_gl30_t, lights1 ));
pglEnableVertexAttribArrayARB( ATTR_INDEX_LIGHT_NUMS1 );
}
/*
=================
Mod_CreateBufferObject
=================
*/
static void Mod_CreateBufferObject( void )
{
if( world->vertex_buffer_object )
return; // already created
// calculate number of used faces and vertexes
msurface_t *surf = worldmodel->surfaces;
int i, j, curVert = 0;
byte *vislight = NULL;
mvertex_t *dv;
bvert_t *mv;
world->numvertexes = 0;
// compute totalvertex count for VBO but ignore sky polys
for( i = 0; i < worldmodel->numsurfaces; i++, surf++ )
{
if( FBitSet( surf->flags, SURF_DRAWSKY ))
continue;
world->numvertexes += surf->numedges;
}
// temporary array will be removed at end of this function
// g-cont. i'm leave local copy of vertexes for some debug purpoces
world->vertexes = (bvert_t *)Mem_Alloc( sizeof( bvert_t ) * world->numvertexes );
surf = worldmodel->surfaces;
// create VBO-optimized vertex array (single for world and all brush-models)
for( i = 0; i < worldmodel->numsurfaces; i++, surf++ )
{
Vector t, b, n;
if( FBitSet( surf->flags, SURF_DRAWSKY ))
continue; // ignore sky polys it was never be drawed
mv = &world->vertexes[curVert];
// request vislightdata for this surface
if( world->vislightdata ) vislight = world->vislightdata + i * ((world->numworldlights + 7) / 8);
Mod_FindStaticLights( vislight, surf->info->lights, surf->info->origin );
// NOTE: all polygons stored as source (no tesselation anyway)
for( j = 0; j < surf->numedges; j++, mv++ )
{
int l = worldmodel->surfedges[surf->firstedge + j];
int vert = worldmodel->edges[abs(l)].v[(l > 0) ? 0 : 1];
memcpy( mv->styles, surf->styles, sizeof( surf->styles ));
memcpy( mv->lights0, surf->info->lights, sizeof( surf->info->lights ));
dv = &worldmodel->vertexes[vert];
mv->vertex = dv->position;
R_TextureCoords( surf, mv->vertex, mv->stcoord0 );
R_LightmapCoords( surf, mv->vertex, mv->lmcoord0, 0 ); // styles 0-1
R_LightmapCoords( surf, mv->vertex, mv->lmcoord1, 2 ); // styles 2-3
}
// NOTE: now firstvertex are handled in world->vertexes[] array, not in world->tbn_vectors[] !!!
surf->info->firstvertex = curVert;
surf->info->numverts = surf->numedges;
curVert += surf->numedges;
Mod_ComputeFaceTBN( surf, surf->info );
}
// compute water global coords
for( i = 1; i < worldmodel->numsubmodels; i++ )
{
Vector absmin, absmax;
ClearBounds( absmin, absmax );
// first iteration - compute water bbox
for( j = 0; j < worldmodel->submodels[i].numfaces; j++ )
{
surf = &worldmodel->surfaces[worldmodel->submodels[i].firstface + j];
if( !FBitSet( surf->flags, SURF_DRAWTURB ))
continue;
AddPointToBounds( surf->info->mins, absmin, absmax );
AddPointToBounds( surf->info->maxs, absmin, absmax );
}
if( BoundsIsCleared( absmin, absmax ))
continue; // not a water
float scale = sqrt( (absmax - absmin).Average()) * 0.3f; // FIXME: tune this constant?
// second iteration - mapping global coords
for( j = 0; j < worldmodel->submodels[i].numfaces; j++ )
{
surf = &worldmodel->surfaces[worldmodel->submodels[i].firstface + j];
if( !FBitSet( surf->flags, SURF_DRAWTURB ))
continue;
for( int k = 0; k < surf->info->numverts; k++ )
{
mv = &world->vertexes[surf->info->firstvertex + k];
R_GlobalCoords( surf, mv->vertex, absmin, absmax, scale, mv->stcoord0 );
}
}
}
// time to prepare landscapes
for( i = 0; i < worldmodel->numsurfaces; i++ )
{
msurface_t *surf = &worldmodel->surfaces[i];
Mod_ProcessLandscapes( surf, surf->info );
}
for( i = 0; i < worldmodel->numsurfaces; i++ )
{
msurface_t *surf = &worldmodel->surfaces[i];
Mod_MappingLandscapes( surf, surf->info );
}
// now normals are merged into single array world->vertexes[]
if( world->normals != NULL )
Mem_Free( world->normals );
world->normals = NULL;
GL_CheckVertexArrayBinding();
// create world vertex buffer
if( glConfig.version < ACTUAL_GL_VERSION )
CreateBufferBaseGL21( world->vertexes );
else CreateBufferBaseGL30( world->vertexes );
// create vertex array object
pglGenVertexArrays( 1, &world->vertex_array_object );
pglBindVertexArray( world->vertex_array_object );
if( glConfig.version < ACTUAL_GL_VERSION )
BindBufferBaseGL21();
else BindBufferBaseGL30();
pglBindVertexArray( GL_FALSE );
// update stats
tr.total_vbo_memory += world->cacheSize;
}
/*
=================
Mod_DeleteBufferObject
=================
*/
static void Mod_DeleteBufferObject( void )
{
if( world->vertex_array_object ) pglDeleteVertexArrays( 1, &world->vertex_array_object );
if( world->vertex_buffer_object ) pglDeleteBuffersARB( 1, &world->vertex_buffer_object );
tr.total_vbo_memory -= world->cacheSize;
world->vertex_array_object = world->vertex_buffer_object = 0;
world->cacheSize = 0;
}
/*
=================
Mod_PrepareModelInstances
throw all the instances before
loading the new map
=================
*/
void Mod_PrepareModelInstances( void )
{
// invalidate model handles
for( int i = 1; i < RENDER_GET_PARM( PARM_MAX_ENTITIES, 0 ); i++ )
{
cl_entity_t *e = GET_ENTITY( i );
if( !e ) break;
e->modelhandle = INVALID_HANDLE;
}
if( gHUD.m_pHeadShieldEnt != NULL )
{
gHUD.m_pHeadShieldEnt->modelhandle = INVALID_HANDLE;
gHUD.m_pHeadShieldEnt->curstate.movetype = MOVETYPE_WALK;
}
GET_VIEWMODEL()->modelhandle = INVALID_HANDLE;
memset( tr.draw_entities, 0, sizeof( tr.draw_entities ));
}
/*
=================
Mod_ThrowModelInstances
throw all the instances before
loading the new map
=================
*/
void Mod_ThrowModelInstances( void )
{
// engine already released entity array so we can't release
// model instance for each entity personally
g_StudioRenderer.DestroyAllModelInstances();
// may caused by Host_Error, so clear gl state
if( g_fRenderInitialized )
GL_SetDefaultState();
}
/*
=================
Mod_LoadWorld
=================
*/
static void Mod_LoadWorld( model_t *mod, const byte *buf )
{
dheader_t *header;
dextrahdr_t *extrahdr;
int i;
header = (dheader_t *)buf;
extrahdr = (dextrahdr_t *)((byte *)buf + sizeof( dheader_t ));
if( RENDER_GET_PARM( PARM_FEATURES, 0 ) & ENGINE_LARGE_LIGHTMAPS )
glConfig.block_size = BLOCK_SIZE_MAX;
else glConfig.block_size = BLOCK_SIZE_DEFAULT;
if( RENDER_GET_PARM( PARM_MAP_HAS_DELUXE, 0 ))
SetBits( world->features, WORLD_HAS_DELUXEMAP );
if( RENDER_GET_PARM( PARM_WATER_ALPHA, 0 ))
SetBits( world->features, WORLD_WATERALPHA );
COM_FileBase( worldmodel->name, world->name );
ALERT( at_aiconsole, "Mod_LoadWorld: %s\n", world->name );
R_CheckChanges(); // catch all the cvar changes
tr.glsl_valid_sequence = 1;
tr.params_changed = false;
// precache and upload cinematics
R_InitCinematics();
// prepare visibility and setup leaf extradata
Mod_SetupLeafExtradata( &header->lumps[LUMP_LEAFS], &header->lumps[LUMP_VISIBILITY], buf );
// all the old lightmaps are freed
GL_BeginBuildingLightmaps();
// process landscapes first
R_LoadLandscapes( world->name );
// load material textures
Mod_LoadWorldMaterials();
// mark submodel faces
for( i = worldmodel->submodels[0].numfaces; i < worldmodel->numsurfaces; i++ )
SetBits( worldmodel->surfaces[i].flags, SURF_OF_SUBMODEL );
// detect surfs in local space
for( i = 0; i < worldmodel->numsubmodels; i++ )
{
dmodel_t *bm = &worldmodel->submodels[i];
if( bm->origin == g_vecZero )
continue; // abs space
// mark surfs in local space
msurface_t *surf = worldmodel->surfaces + bm->firstface;
for( int j = 0; j < bm->numfaces; j++, surf++ )
SetBits( surf->flags, SURF_LOCAL_SPACE );
}
if( extrahdr->id == IDEXTRAHEADER )
{
if( extrahdr->version == EXTRA_VERSION )
{
// new Xash3D extended format
if( GL_Support( R_TEXTURECUBEMAP_EXT )) // loading cubemaps only when it's support
Mod_LoadCubemaps( buf, &extrahdr->lumps[LUMP_CUBEMAPS] );
Mod_LoadVertNormals( buf, &extrahdr->lumps[LUMP_VERTNORMALS] );
Mod_LoadWorldLights( buf, &extrahdr->lumps[LUMP_WORLDLIGHTS] );
Mod_LoadLeafAmbientLighting( buf, &extrahdr->lumps[LUMP_LEAF_LIGHTING] );
Mod_LoadVertexLighting( buf, &extrahdr->lumps[LUMP_VERTEX_LIGHT] );
Mod_LoadSurfaceLighting( buf, &extrahdr->lumps[LUMP_SURFACE_LIGHT] );
Mod_LoadVisLightData( buf, &extrahdr->lumps[LUMP_VISLIGHTDATA] );
}
else if( extrahdr->version == EXTRA_VERSION_OLD )
{
// P2: Savior regular format
if( GL_Support( R_TEXTURECUBEMAP_EXT )) // loading cubemaps only when it's support
Mod_LoadCubemaps( buf, &extrahdr->lumps[LUMP_CUBEMAPS] );
Mod_LoadWorldLights( buf, &extrahdr->lumps[LUMP_WORLDLIGHTS] );
}
Mod_SetupLeafLights();
}
// mark surfaces for world features
for( i = 0; i < worldmodel->numsurfaces; i++ )
{
msurface_t *surf = &worldmodel->surfaces[i];
if( FBitSet( surf->flags, SURF_DRAWSKY ))
SetBits( world->features, WORLD_HAS_SKYBOX );
if( FBitSet( surf->flags, SURF_DRAWTURB ))
{
SetBits( surf->flags, SURF_REFLECT );
}
if( !Q_strncmp( surf->texinfo->texture->name, "movie", 5 ))
{
SetBits( world->features, WORLD_HAS_MOVIES );
SetBits( surf->flags, SURF_MOVIE );
}
if( !Q_strncmp( surf->texinfo->texture->name, "mirror", 6 ) || !Q_strncmp( surf->texinfo->texture->name, "reflect", 7 ))
{
SetBits( surf->flags, SURF_REFLECT );
}
if( !Q_strncmp( surf->texinfo->texture->name, "water", 5 ))
{
SetBits( surf->flags, SURF_REFLECT );
}
if( FBitSet( surf->flags, SURF_REFLECT ))
GL_AllocOcclusionQuery( surf );
}
world->deluxedata = (color24 *)RENDER_GET_PARM( PARM_DELUXEDATA, 0 );
world->shadowdata = (byte *)RENDER_GET_PARM( PARM_SHADOWDATA, 0 );
Mod_FinalizeWorld();
Mod_CreateBufferObject();
// helper to precache shaders
R_InitDefaultLights();
// time to place grass
for( i = 0; i < worldmodel->numsurfaces; i++ )
{
// place to initialize our grass
R_GrassInitForSurface( &worldmodel->surfaces[i] );
}
// precache world shaders
Mod_PrecacheShaders();
// creating float texture from worldlights
Mod_InitLightTexture();
// put bsp-tree into two texture (planes and nodes)
Mod_InitBSPTreeTexture();
}
static void Mod_FreeWorld( model_t *mod )
{
Mod_FreeCubemaps();
// destroy VBO & VAO
Mod_DeleteBufferObject();
if( world->leafs )
Mem_Free( world->leafs );
world->leafs = NULL;
if( world->vertexes )
Mem_Free( world->vertexes );
world->vertexes = NULL;
if( world->vertex_lighting )
Mem_Free( world->vertex_lighting );
world->vertex_lighting = NULL;
if( world->surface_lighting )
Mem_Free( world->surface_lighting );
world->surface_lighting = NULL;
// free old cinematics
R_FreeCinematics();
// free landscapes
R_FreeLandscapes();
if( tr.packed_lights_texture )
{
FREE_TEXTURE( tr.packed_lights_texture );
tr.packed_lights_texture = 0;
}
if( tr.packed_planes_texture )
{
FREE_TEXTURE( tr.packed_planes_texture );
tr.packed_planes_texture = 0;
}
if( tr.packed_nodes_texture )
{
FREE_TEXTURE( tr.packed_nodes_texture );
tr.packed_nodes_texture = 0;
}
if( tr.packed_models_texture )
{
FREE_TEXTURE( tr.packed_models_texture );
tr.packed_models_texture = 0;
}
if( world->materials )
{
for( int i = 0; i < worldmodel->numtextures; i++ )
{
texture_t *tx = worldmodel->textures[i];
material_t *mat = &world->materials[i];
if( !mat->pSource ) continue; // not initialized?
if( !FBitSet( mat->flags, BRUSH_MULTI_LAYERS ))
{
if( tx->gl_texturenum != mat->gl_diffuse_id )
FREE_TEXTURE( mat->gl_diffuse_id );
if( mat->gl_normalmap_id != tr.normalmapTexture )
FREE_TEXTURE( mat->gl_normalmap_id );
if( mat->gl_specular_id != tr.blackTexture )
FREE_TEXTURE( mat->gl_specular_id );
}
if( mat->gl_glowmap_id != tr.blackTexture )
FREE_TEXTURE( mat->gl_glowmap_id );
}
Mem_Free( world->materials );
world->materials = NULL;
}
// delete queries
for( int i = 0; i < worldmodel->numsurfaces; i++ )
GL_DeleteOcclusionQuery( &worldmodel->surfaces[i] );
if( FBitSet( world->features, WORLD_HAS_GRASS ))
{
// throw grass vbo's
for( int i = 0; i < worldmodel->numsurfaces; i++ )
R_RemoveGrassForSurface( worldmodel->surfaces[i].info );
}
memset( world, 0, sizeof( gl_world_t ));
}
/*
==================
Mod_SetOrthoBounds
setup ortho min\maxs for draw overview
==================
*/
void Mod_SetOrthoBounds( const float *mins, const float *maxs )
{
if( !g_fRenderInitialized ) return;
world->orthocenter.x = ((maxs[0] + mins[0]) * 0.5f);
world->orthocenter.y = ((maxs[1] + mins[1]) * 0.5f);
world->orthohalf.x = maxs[0] - world->orthocenter.x;
world->orthohalf.y = maxs[1] - world->orthocenter.y;
}
/*
==================
R_ProcessWorldData
resource management
==================
*/
void R_ProcessWorldData( model_t *mod, qboolean create, const byte *buffer )
{
worldmodel = mod;
if( create )
{
double start = Sys_DoubleTime();
Mod_LoadWorld( mod, buffer );
double end = Sys_DoubleTime();
r_buildstats.create_buffer_object += (end - start);
r_buildstats.total_buildtime += (end - start);
}
else Mod_FreeWorld( mod );
}
/*
=============================================================================
WORLD RENDERING
=============================================================================
*/
static unsigned int tempElems[MAX_MAP_ELEMS];
static unsigned int numTempElems;
/*
=================
R_MarkSubmodelVisibleFaces
do all the visibility checks, update surface
params, and mark them as visible
=================
*/
void R_MarkSubmodelVisibleFaces( void )
{
cl_entity_t *e = RI->currententity;
model_t *model = RI->currentmodel;
Vector absmin, absmax;
CFrustum *frustum;
mplane_t plane;
msurface_t *surf;
mextrasurf_t *esrf;
gl_state_t *glm;
int i;
// grab the transformed vieworg
glm = GL_GetCache( e->hCachedMatrix );
if( e->angles != g_vecZero )
{
TransformAABB( glm->transform, model->mins, model->maxs, absmin, absmax );
}
else
{
absmin = e->origin + model->mins;
absmax = e->origin + model->maxs;
}
if( !Mod_CheckBoxVisible( absmin, absmax ))
{
r_stats.c_culled_entities++;
return;
}
// frustum checking
if( R_CullBrushModel( e ))
{
r_stats.c_culled_entities++;
return;
}
// mark all the visible faces (will added later)
for( i = 0; model != NULL && i < model->nummodelsurfaces; i++ )
{
surf = model->surfaces + model->firstmodelsurface + i;
esrf = surf->info;
esrf->parent = RI->currententity; // setup dynamic upcast
if( FBitSet( surf->flags, SURF_DRAWTURB ))
{
if( FBitSet( surf->flags, SURF_PLANEBACK ))
SetPlane( &plane, -surf->plane->normal, -surf->plane->dist );
else SetPlane( &plane, surf->plane->normal, surf->plane->dist );
glm->transform.TransformPositivePlane( plane, plane );
if( surf->plane->type != PLANE_Z && !FBitSet( e->curstate.effects, EF_WATERSIDES ))
continue;
if( absmin[2] + 1.0 >= plane.dist )
continue;
}
// non-moved bmodels can be culled by frustum
if( R_StaticEntity( e ))
frustum = &RI->view.frustum;
else frustum = NULL;
int cull_type = R_CullSurface( surf, tr.modelorg, frustum );
if( cull_type >= CULL_FRUSTUM )
continue;
if( cull_type == CULL_BACKSIDE )
{
if( !FBitSet( surf->flags, SURF_DRAWTURB ))
continue;
}
SETVISBIT( RI->view.visfaces, model->firstmodelsurface + i );
// surface has passed all visibility checks
// and can be update some data (lightmaps, mirror matrix, etc)
if( RP_NORMALPASS( )) R_UpdateSurfaceParams( surf );
}
if( e->origin != e->baseline.vuser4 )
{
R_FindWorldLights( e->origin, e->model->mins, e->model->maxs, e->lights, true );
#if 0
Msg( "%s gather lights: %d %d %d %d %d %d %d %d\n", e->model->name,
e->lights[0], e->lights[1], e->lights[2], e->lights[3], e->lights[4], e->lights[4], e->lights[6], e->lights[7] );
#endif
e->baseline.vuser4 = e->origin;
}
}
/*
=================
R_UpdateSubmodelParams
run cinematic, evaluate conveyor etc
=================
*/
void R_UpdateSubmodelParams( void )
{
cl_entity_t *e = RI->currententity;
model_t *model = RI->currentmodel;
msurface_t *surf;
mextrasurf_t *esrf;
// mark all the visible faces (will added later)
for( int i = 0; model != NULL && i < model->nummodelsurfaces; i++ )
{
surf = model->surfaces + model->firstmodelsurface + i;
esrf = surf->info;
esrf->parent = RI->currententity; // setup dynamic upcast
// and can be update some data (lightmaps, mirror matrix, etc)
R_UpdateSurfaceParams( surf );
}
}
_forceinline void R_DrawSurface( mextrasurf_t *es )
{
// accumulate the indices
for( int j = 0; j < es->numverts - 2; j++ )
{
ASSERT( numTempElems < ( MAX_MAP_ELEMS - 3 ));
tempElems[numTempElems++] = es->firstvertex;
tempElems[numTempElems++] = es->firstvertex + j + 1;
tempElems[numTempElems++] = es->firstvertex + j + 2;
}
}
void R_MarkVisibleLights( byte lights[MAXDYNLIGHTS] )
{
// mark lights that visible for this frame
for( int i = 0; i < (int)cv_deferred_maxlights->value && lights[i] != 255; i++ )
SETVISBIT( RI->view.vislight, lights[i] );
}
/*
=================
R_AddSurfaceToDrawList
add specified face into sorted drawlist
=================
*/
bool R_AddSurfaceToDrawList( msurface_t *surf, drawlist_t drawlist_type )
{
cl_entity_t *e = RI->currententity;
mextrasurf_t *es = surf->info;
word hProgram = 0;
CSolidEntry entry_s;
CTransEntry entry_t;
switch( drawlist_type )
{
case DRAWLIST_SOLID:
if( FBitSet( surf->flags, SURF_NODRAW ))
return false;
if( FBitSet( RI->params, RP_DEFERREDSCENE|RP_DEFERREDLIGHT ))
{
// precache shaders
Mod_ShaderSceneDeferred( surf );
Mod_ShaderLightDeferred( surf );
}
else hProgram = Mod_ShaderSceneForward( surf );
R_MarkVisibleLights( es->lights );
entry_s.SetRenderSurface( surf, hProgram );
RI->frame.solid_faces.AddToTail( entry_s );
break;
case DRAWLIST_TRANS:
if( FBitSet( surf->flags, SURF_NODRAW ))
return false;
hProgram = Mod_ShaderSceneForward( surf );
entry_t.SetRenderSurface( surf, hProgram );
if( ScreenCopyRequired( &glsl_programs[hProgram] ))
{
Vector mins, maxs;
gl_state_t *glm = GL_GetCache( e->hCachedMatrix );
TransformAABB( glm->transform, es->mins, es->maxs, mins, maxs );
// create sentinel border for refractions
ExpandBounds( mins, maxs, 2.0f );
entry_t.ComputeScissor( mins, maxs );
}
RI->frame.trans_list.AddToTail( entry_t );
break;
case DRAWLIST_SHADOW:
if( FBitSet( surf->flags, SURF_NODRAW ))
return false;
hProgram = Mod_ShaderSceneDepth( surf );
entry_s.SetRenderSurface( surf, hProgram );
RI->frame.solid_faces.AddToTail( entry_s );
break;
case DRAWLIST_LIGHT:
if( RI->currentlight->type == LIGHT_DIRECTIONAL )
{
if( FBitSet( surf->flags, SURF_NOSUNLIGHT ))
return false;
}
else
{
if( FBitSet( surf->flags, SURF_NODLIGHT ))
return false;
}
hProgram = Mod_ShaderLightForward( RI->currentlight, surf );
entry_s.SetRenderSurface( surf, hProgram );
RI->frame.light_faces.AddToTail( entry_s );
break;
case DRAWLIST_SUBVIEW:
if( RI->frame.num_subview_faces >= MAX_SUBVIEW_FACES )
return false;
if( !FBitSet( surf->flags, SURF_REFLECT ))
{
if( !FBitSet( surf->flags, SURF_REFLECT_PUDDLE ) || !CVAR_TO_BOOL( cv_realtime_puddles ))
return false; // if realtime puddles not allowed
}
RI->frame.subview_faces[RI->frame.num_subview_faces] = surf;
RI->frame.num_subview_faces++;
GL_SurfaceOccluded( surf );// fetch queries result
break;
default:
HOST_ERROR( "R_AddSurfaceToDrawList: unknown drawlist( %i )\n", drawlist_type );
break;
}
return true;
}
/*
================
R_SetSurfaceUniforms
================
*/
void R_SetSurfaceUniforms( word hProgram, msurface_t *surface, bool force )
{
mextrasurf_t *es = surface->info;
Vector4D lightstyles, lightdir;
cl_entity_t *e = es->parent;
msurface_t *s = surface;
float r, g, b, a, *v;
int map;
// begin draw the sorted list
if( force || ( RI->currentshader != &glsl_programs[hProgram] ))
{
// force to bind new shader
GL_BindShader( &glsl_programs[hProgram] );
}
material_t *mat = R_TextureAnimation( s )->material;
glsl_program_t *shader = RI->currentshader;
CDynLight *pl = RI->currentlight; // may be NULL
gl_state_t *glm = GL_GetCache( e->hCachedMatrix );
mtexinfo_t *tx = s->texinfo;
mfaceinfo_t *land = tx->faceinfo;
GLfloat viewMatrix[16];
if( !FBitSet( RI->params, RP_SHADOWVIEW ))
{
// setup some GL-states
if( e->curstate.renderfx == SKYBOX_ENTITY )
{
GL_DepthRange( 0.8f, 0.9f );
GL_ClipPlane( false );
}
else
{
GL_DepthRange( gldepthmin, gldepthmax );
GL_ClipPlane( true );
}
if( tr.waterlevel >= 3 && RP_NORMALPASS() && FBitSet( s->flags, SURF_DRAWTURB ))
GL_Cull( GL_BACK );
else GL_Cull( GL_FRONT );
}
if( !FBitSet( RI->params, RP_DEFERREDLIGHT ))
{
if( FBitSet( mat->flags, BRUSH_TRANSPARENT|BRUSH_HAS_ALPHA ))
GL_AlphaTest( GL_TRUE );
else GL_AlphaTest( GL_FALSE );
}
tr.modelorg = glm->GetModelOrigin();
// setup specified uniforms (and texture bindings)
for( int i = 0; i < shader->numUniforms; i++ )
{
uniform_t *u = &shader->uniforms[i];
switch( u->type )
{
case UT_COLORMAP:
if( Surf_CheckSubview( es ))
u->SetValue( Surf_GetSubview( es )->texturenum );
else if( FBitSet( s->flags, SURF_MOVIE ) && RI->currententity->curstate.body )
u->SetValue( tr.cinTextures[es->cintexturenum-1] );
else u->SetValue( mat->gl_diffuse_id );
break;
case UT_NORMALMAP:
if( FBitSet( mat->flags, BRUSH_LIQUID ) && tr.waterTextures[0] > 0 )
u->SetValue( tr.waterTextures[(int)( tr.time * WATER_ANIMTIME ) % WATER_TEXTURES] );
else u->SetValue( mat->gl_normalmap_id );
break;
case UT_GLOSSMAP:
u->SetValue( mat->gl_specular_id );
break;
case UT_DETAILMAP:
if( land && land->terrain && land->terrain->indexmap.gl_diffuse_id != 0 )
u->SetValue( land->terrain->indexmap.gl_diffuse_id );
else u->SetValue( mat->gl_detailmap_id );
break;
case UT_PROJECTMAP:
if( pl && pl->type == LIGHT_SPOT )
u->SetValue( pl->spotlightTexture );
else u->SetValue( tr.whiteTexture );
break;
case UT_SHADOWMAP:
case UT_SHADOWMAP0:
if( pl ) u->SetValue( pl->shadowTexture[0] );
else u->SetValue( tr.depthTexture );
break;
case UT_SHADOWMAP1:
if( pl ) u->SetValue( pl->shadowTexture[1] );
else u->SetValue( tr.depthTexture );
break;
case UT_SHADOWMAP2:
if( pl ) u->SetValue( pl->shadowTexture[2] );
else u->SetValue( tr.depthTexture );
break;
case UT_SHADOWMAP3:
if( pl ) u->SetValue( pl->shadowTexture[3] );
else u->SetValue( tr.depthTexture );
break;
case UT_LIGHTMAP:
if( R_FullBright( )) u->SetValue( tr.grayTexture );
else u->SetValue( tr.lightmaps[es->lightmaptexturenum].lightmap );
break;
case UT_DELUXEMAP:
if( R_FullBright( )) u->SetValue( tr.deluxemapTexture );
else u->SetValue( tr.lightmaps[es->lightmaptexturenum].deluxmap );
break;
case UT_DECALMAP:
// unacceptable for brushmodels
u->SetValue( tr.whiteTexture );
break;
case UT_SCREENMAP:
u->SetValue( tr.screen_color );
break;
case UT_DEPTHMAP:
u->SetValue( tr.screen_depth );
break;
case UT_ENVMAP0:
case UT_ENVMAP:
if( es->cubemap[0] != NULL )
u->SetValue( es->cubemap[0]->texture );
else u->SetValue( tr.whiteCubeTexture );
break;
case UT_ENVMAP1:
if( es->cubemap[1] != NULL )
u->SetValue( es->cubemap[1]->texture );
else u->SetValue( tr.whiteCubeTexture );
break;
case UT_GLOWMAP:
u->SetValue( mat->gl_glowmap_id );
break;
case UT_LAYERMAP:
if( FBitSet( mat->flags, BRUSH_MULTI_LAYERS ) && land && land->terrain )
u->SetValue( land->terrain->indexmap.gl_heightmap_id );
else u->SetValue( tr.whiteTexture );
break;
case UT_HEIGHTMAP:
u->SetValue( mat->gl_heightmap_id );
break;
case UT_BSPPLANESMAP:
u->SetValue( tr.packed_planes_texture );
break;
case UT_BSPNODESMAP:
u->SetValue( tr.packed_nodes_texture );
break;
case UT_BSPLIGHTSMAP:
u->SetValue( tr.packed_lights_texture );
break;
case UT_FITNORMALMAP:
u->SetValue( tr.normalsFitting );
break;
case UT_MODELMATRIX:
u->SetValue( &glm->modelMatrix[0] );
break;
case UT_REFLECTMATRIX:
if( Surf_CheckSubview( es ))
Surf_GetSubview( es )->matrix.CopyToArray( viewMatrix );
else memcpy( viewMatrix, glState.identityMatrix, sizeof( float ) * 16 );
u->SetValue( &viewMatrix[0] );
break;
case UT_SCREENSIZEINV:
u->SetValue( 1.0f / (float)glState.width, 1.0f / (float)glState.height );
break;
case UT_ZFAR:
u->SetValue( -tr.farclip * 1.74f );
break;
case UT_LIGHTSTYLES:
for( map = 0; map < MAXLIGHTMAPS; map++ )
{
if( s->styles[map] != 255 )
lightstyles[map] = tr.lightstyle[s->styles[map]];
else lightstyles[map] = 0.0f;
}
u->SetValue( lightstyles.x, lightstyles.y, lightstyles.z, lightstyles.w );
break;
case UT_LIGHTSTYLEVALUES:
u->SetValue( &tr.lightstyle[0], MAX_LIGHTSTYLES );
break;
case UT_REALTIME:
u->SetValue( (float)tr.time );
break;
case UT_DETAILSCALE:
u->SetValue( mat->detailScale[0], mat->detailScale[1] );
break;
case UT_FOGPARAMS:
if( e->curstate.renderfx == SKYBOX_ENTITY )
u->SetValue( tr.fogColor[0], tr.fogColor[1], tr.fogColor[2], tr.fogSkyDensity );
else u->SetValue( tr.fogColor[0], tr.fogColor[1], tr.fogColor[2], tr.fogDensity );
break;
case UT_SHADOWPARMS:
if( pl != NULL )
{
float shadowWidth = 1.0f / (float)RENDER_GET_PARM( PARM_TEX_WIDTH, pl->shadowTexture[0] );
float shadowHeight = 1.0f / (float)RENDER_GET_PARM( PARM_TEX_HEIGHT, pl->shadowTexture[0] );
// depth scale and bias and shadowmap resolution
u->SetValue( shadowWidth, shadowHeight, -pl->projectionMatrix[2][2], pl->projectionMatrix[3][2] );
}
else u->SetValue( 0.0f, 0.0f, 0.0f, 0.0f );
break;
case UT_TEXOFFSET:
u->SetValue( es->texofs[0], es->texofs[1] );
break;
case UT_VIEWORIGIN:
if( pl ) u->SetValue( GetVieworg().x, GetVieworg().y, GetVieworg().z );
else u->SetValue( tr.modelorg.x, tr.modelorg.y, tr.modelorg.z, e->hCachedMatrix ? 1.0f : 0.0f );
break;
case UT_VIEWRIGHT:
u->SetValue( GetVRight().x, GetVRight().y, GetVRight().z );
break;
case UT_RENDERCOLOR:
if( e->curstate.rendermode == kRenderNormal )
{
r = g = b = a = 1.0f;
}
else
{
int sum = (e->curstate.rendercolor.r + e->curstate.rendercolor.g + e->curstate.rendercolor.b);
if(( sum > 0 ) && !FBitSet( s->flags, SURF_CONVEYOR ))
{
r = e->curstate.rendercolor.r / 255.0f;
g = e->curstate.rendercolor.g / 255.0f;
b = e->curstate.rendercolor.b / 255.0f;
}
else
{
r = g = b = 1.0f;
}
if( e->curstate.rendermode != kRenderTransAlpha )
a = Q_max( e->curstate.renderamt / 255.0f, 0.25f );
else a = 1.0f;
}
u->SetValue( r, g, b, a );
break;
case UT_SMOOTHNESS:
if( FBitSet( mat->flags, BRUSH_MULTI_LAYERS ) && land && land->terrain )
{
terrain_t *terra = land->terrain;
u->SetValue( &terra->layermap.smoothness[0], terra->numLayers );
}
else u->SetValue( mat->smoothness );
break;
case UT_SHADOWMATRIX:
if( pl ) u->SetValue( &pl->gl_shadowMatrix[0][0], MAX_SHADOWMAPS );
break;
case UT_SHADOWSPLITDIST:
v = RI->view.parallelSplitDistances;
u->SetValue( v[0], v[1], v[2], v[3] );
break;
case UT_TEXELSIZE:
u->SetValue( 1.0f / (float)sunSize[0], 1.0f / (float)sunSize[1], 1.0f / (float)sunSize[2], 1.0f / (float)sunSize[3] );
break;
case UT_GAMMATABLE:
u->SetValue( &tr.gamma_table[0][0], 64 );
break;
case UT_LIGHTDIR:
if( pl )
{
if( pl->type == LIGHT_DIRECTIONAL ) lightdir = -tr.sky_normal;
else lightdir = pl->frustum.GetPlane( FRUSTUM_FAR )->normal;
u->SetValue( lightdir.x, lightdir.y, lightdir.z, pl->fov );
}
break;
case UT_LIGHTDIFFUSE:
if( pl ) u->SetValue( pl->color.x, pl->color.y, pl->color.z );
break;
case UT_LIGHTORIGIN:
if( pl ) u->SetValue( pl->origin.x, pl->origin.y, pl->origin.z, ( 1.0f / pl->radius ));
break;
case UT_LIGHTVIEWPROJMATRIX:
if( pl )
{
GLfloat gl_lightViewProjMatrix[16];
pl->lightviewProjMatrix.CopyToArray( gl_lightViewProjMatrix );
u->SetValue( &gl_lightViewProjMatrix[0] );
}
break;
case UT_DIFFUSEFACTOR:
u->SetValue( tr.diffuseFactor );
break;
case UT_AMBIENTFACTOR:
if( pl && pl->type == LIGHT_DIRECTIONAL )
u->SetValue( tr.sun_ambient );
else u->SetValue( tr.ambientFactor );
break;
case UT_SUNREFRACT:
u->SetValue( tr.sun_refract );
break;
case UT_LERPFACTOR:
u->SetValue( es->lerpFactor );
break;
case UT_REFRACTSCALE:
u->SetValue( bound( 0.0f, mat->refractScale, 1.0f ));
break;
case UT_REFLECTSCALE:
u->SetValue( bound( 0.0f, mat->reflectScale, 1.0f ));
break;
case UT_ABERRATIONSCALE:
u->SetValue( bound( 0.0f, mat->aberrationScale, 1.0f ));
break;
case UT_BOXMINS:
if( world->num_cubemaps > 0 )
{
Vector mins[2];
mins[0] = es->cubemap[0]->mins;
mins[1] = es->cubemap[1]->mins;
u->SetValue( &mins[0][0], 2 );
}
break;
case UT_BOXMAXS:
if( world->num_cubemaps > 0 )
{
Vector maxs[2];
maxs[0] = es->cubemap[0]->maxs;
maxs[1] = es->cubemap[1]->maxs;
u->SetValue( &maxs[0][0], 2 );
}
break;
case UT_CUBEORIGIN:
if( world->num_cubemaps > 0 )
{
Vector origin[2];
origin[0] = es->cubemap[0]->origin;
origin[1] = es->cubemap[1]->origin;
u->SetValue( &origin[0][0], 2 );
}
break;
case UT_CUBEMIPCOUNT:
if( world->num_cubemaps > 0 )
{
r = Q_max( 1, es->cubemap[0]->numMips - cv_cube_lod_bias->value );
g = Q_max( 1, es->cubemap[1]->numMips - cv_cube_lod_bias->value );
u->SetValue( r, g );
}
break;
case UT_LIGHTNUMS0:
u->SetValue( (float)e->lights[0], (float)e->lights[1], (float)e->lights[2], (float)e->lights[3] );
break;
case UT_LIGHTNUMS1:
u->SetValue( (float)e->lights[4], (float)e->lights[5], (float)e->lights[6], (float)e->lights[7] );
break;
default:
ALERT( at_error, "%s: unhandled uniform %s\n", RI->currentshader->name, u->name );
break;
}
}
}
/*
================
R_RenderDynLightList
================
*/
void R_BuildFaceListForLight( CDynLight *pl, bool solid )
{
int i;
RI->currententity = GET_ENTITY( 0 );
RI->currentmodel = RI->currententity->model;
RI->frame.light_faces.Purge();
RI->frame.light_grass.Purge();
tr.modelorg = pl->origin;
if( solid )
{
// only visible polys passed through the light list
for( i = 0; i < RI->frame.solid_faces.Count(); i++ )
{
CSolidEntry *entry = &RI->frame.solid_faces[i];
if( entry->m_bDrawType != DRAWTYPE_SURFACE )
continue;
mextrasurf_t *es = entry->m_pSurf->info;
gl_state_t *glm = GL_GetCache( es->parent->hCachedMatrix );
RI->currententity = es->parent;
RI->currentmodel = RI->currententity->model;
if( RI->currententity->curstate.renderfx == SKYBOX_ENTITY )
continue; // fast reject
if( FBitSet( RI->currententity->curstate.effects, EF_FULLBRIGHT ))
continue;
bool worldpos = R_StaticEntity( es->parent ) ? true : false;
CFrustum *frustum = (worldpos) ? &pl->frustum : NULL;
tr.modelorg = glm->GetModelOrigin();
R_AddGrassToDrawList( entry->m_pSurf, DRAWLIST_LIGHT );
if( R_CullSurface( entry->m_pSurf, tr.modelorg, frustum ))
continue;
// move from main list into light list
R_AddSurfaceToDrawList( entry->m_pSurf, DRAWLIST_LIGHT );
}
}
else
{
// only visible polys passed through the light list
for( i = 0; i < RI->frame.trans_list.Count(); i++ )
{
CTransEntry *entry = &RI->frame.trans_list[i];
if( entry->m_bDrawType != DRAWTYPE_SURFACE )
continue;
mextrasurf_t *es = entry->m_pSurf->info;
gl_state_t *glm = GL_GetCache( es->parent->hCachedMatrix );
RI->currententity = es->parent;
RI->currentmodel = RI->currententity->model;
if( RI->currententity->curstate.renderfx == SKYBOX_ENTITY )
continue; // fast reject
if( FBitSet( RI->currententity->curstate.effects, EF_FULLBRIGHT ))
continue;
bool worldpos = R_StaticEntity( es->parent ) ? true : false;
CFrustum *frustum = (worldpos) ? &pl->frustum : NULL;
tr.modelorg = glm->GetModelOrigin();
R_AddGrassToDrawList( entry->m_pSurf, DRAWLIST_LIGHT );
if( R_CullSurface( entry->m_pSurf, tr.modelorg, frustum ))
continue;
// move from main list into light list
R_AddSurfaceToDrawList( entry->m_pSurf, DRAWLIST_LIGHT );
}
}
}
/*
================
R_DrawLightForSurfList
setup light projection for each
================
*/
void R_DrawLightForSurfList( CDynLight *pl )
{
material_t *cached_material = NULL;
cl_entity_t *cached_entity = NULL;
bool flush_buffer = false;
int startv, endv;
pglBindVertexArray( world->vertex_array_object );
pglBlendFunc( GL_ONE, GL_ONE );
float y2 = (float)RI->view.port[3] - pl->h - pl->y;
pglScissor( pl->x, y2, pl->w, pl->h );
numTempElems = 0;
for( int i = 0; i < RI->frame.light_faces.Count(); i++ )
{
CSolidEntry *entry = &RI->frame.light_faces[i];
if( entry->m_bDrawType != DRAWTYPE_SURFACE )
continue;
mextrasurf_t *es = entry->m_pSurf->info;
cl_entity_t *e = RI->currententity = es->parent;
RI->currentmodel = e->model;
msurface_t *s = entry->m_pSurf;
if( !entry->m_hProgram ) continue;
material_t *mat = R_TextureAnimation( s )->material;
if(( i == 0 ) || ( RI->currentshader != &glsl_programs[entry->m_hProgram] ))
flush_buffer = true;
if( cached_entity != RI->currententity )
flush_buffer = true;
if( cached_material != mat )
flush_buffer = true;
if( flush_buffer )
{
if( numTempElems )
{
pglDrawRangeElementsEXT( GL_TRIANGLES, startv, endv - 1, numTempElems, GL_UNSIGNED_INT, tempElems );
r_stats.c_total_tris += (numTempElems / 3);
r_stats.num_flushes++;
numTempElems = 0;
}
flush_buffer = false;
startv = MAX_MAP_ELEMS;
endv = 0;
}
// now cache values
cached_entity = RI->currententity;
cached_material = mat;
if( numTempElems == 0 ) // new chain has started, apply uniforms
R_SetSurfaceUniforms( entry->m_hProgram, entry->m_pSurf, ( i == 0 ));
startv = Q_min( startv, es->firstvertex );
endv = Q_max( es->firstvertex + es->numverts, endv );
R_DrawSurface( es );
}
if( numTempElems )
{
pglDrawRangeElementsEXT( GL_TRIANGLES, startv, endv - 1, numTempElems, GL_UNSIGNED_INT, tempElems );
r_stats.c_total_tris += (numTempElems / 3);
r_stats.num_flushes++;
startv = MAX_MAP_ELEMS;
numTempElems = 0;
endv = 0;
}
R_DrawLightForGrass( pl );
}
/*
================
R_RenderDynLightList
draw dynamic lights for world and bmodels
================
*/
void R_RenderDynLightList( bool solid )
{
if( FBitSet( RI->params, RP_ENVVIEW|RP_SKYVIEW ))
return;
if( !FBitSet( RI->view.flags, RF_HASDYNLIGHTS ))
return;
if( R_FullBright( )) return;
GL_Blend( GL_TRUE );
GL_AlphaTest( GL_FALSE );
GL_DepthMask( GL_FALSE );
CDynLight *pl = tr.dlights;
for( int i = 0; i < MAX_DLIGHTS; i++, pl++ )
{
if( pl->Expired( )) continue;
if( pl->type == LIGHT_SPOT || pl->type == LIGHT_OMNI )
{
if( !pl->Active( )) continue;
if( !Mod_CheckBoxVisible( pl->absmin, pl->absmax ))
continue;
if( R_CullFrustum( &pl->frustum ))
continue;
pglEnable( GL_SCISSOR_TEST );
}
else
{
// couldn't use scissor for sunlight
pglDisable( GL_SCISSOR_TEST );
}
RI->currentlight = pl;
// draw world from light position
R_BuildFaceListForLight( pl, solid );
if( !RI->frame.light_faces.Count() && !RI->frame.light_grass.Count() )
continue; // no interaction with this light?
R_DrawLightForSurfList( pl );
}
GL_CleanupDrawState();
pglDisable( GL_SCISSOR_TEST );
RI->currentlight = NULL;
}
/*
================
R_RenderDeferredBrushList
================
*/
void R_RenderDeferredBrushList( void )
{
cl_entity_t *cached_entity = NULL;
material_t *cached_material = NULL;
mcubemap_t *cached_cubemap[2];
bool flush_buffer = false;
int startv, endv;
if( !RI->frame.solid_faces.Count() )
return;
GL_Blend( GL_FALSE );
GL_AlphaTest( GL_FALSE );
GL_DepthMask( GL_TRUE );
pglAlphaFunc( GL_GREATER, 0.25f );
numTempElems = 0;
if( GL_Support( R_SEAMLESS_CUBEMAP ))
pglEnable( GL_TEXTURE_CUBE_MAP_SEAMLESS );
pglBindVertexArray( world->vertex_array_object );
cached_cubemap[0] = &world->defaultCubemap;
cached_cubemap[1] = &world->defaultCubemap;
for( int i = 0; i < RI->frame.solid_faces.Count(); i++ )
{
CSolidEntry *entry = &RI->frame.solid_faces[i];
if( entry->m_bDrawType != DRAWTYPE_SURFACE )
continue;
mextrasurf_t *es = entry->m_pSurf->info;
cl_entity_t *e = RI->currententity = es->parent;
RI->currentmodel = e->model;
msurface_t *s = entry->m_pSurf;
if( FBitSet( RI->params, RP_DEFERREDSCENE ))
entry->m_hProgram = es->deferredScene.GetHandle();
else if( FBitSet( RI->params, RP_DEFERREDLIGHT ))
entry->m_hProgram = es->deferredLight.GetHandle();
else entry->m_hProgram = 0;
if( !entry->m_hProgram ) continue;
material_t *mat = R_TextureAnimation( s )->material;
if(( i == 0 ) || ( RI->currentshader != &glsl_programs[entry->m_hProgram] ))
flush_buffer = true;
if( cached_entity != RI->currententity )
flush_buffer = true;
if( cached_material != mat )
flush_buffer = true;
if( IsReflectShader( RI->currentshader ) && ( cached_cubemap[0] != es->cubemap[0] || cached_cubemap[1] != es->cubemap[1] ))
flush_buffer = true;
if( flush_buffer )
{
if( numTempElems )
{
pglDrawRangeElementsEXT( GL_TRIANGLES, startv, endv - 1, numTempElems, GL_UNSIGNED_INT, tempElems );
r_stats.c_total_tris += (numTempElems / 3);
r_stats.num_flushes++;
numTempElems = 0;
}
flush_buffer = false;
startv = MAX_MAP_ELEMS;
endv = 0;
}
// now cache values
cached_entity = RI->currententity = es->parent;
RI->currentmodel = es->parent->model;
cached_cubemap[0] = es->cubemap[0];
cached_cubemap[1] = es->cubemap[1];
cached_material = mat;
if( numTempElems == 0 ) // new chain has started, apply uniforms
R_SetSurfaceUniforms( entry->m_hProgram, entry->m_pSurf, ( i == 0 ));
startv = Q_min( startv, es->firstvertex );
endv = Q_max( es->firstvertex + es->numverts, endv );
R_DrawSurface( es );
}
if( numTempElems )
{
pglDrawRangeElementsEXT( GL_TRIANGLES, startv, endv - 1, numTempElems, GL_UNSIGNED_INT, tempElems );
r_stats.c_total_tris += (numTempElems / 3);
r_stats.num_flushes++;
startv = MAX_MAP_ELEMS;
numTempElems = 0;
endv = 0;
}
if( GL_Support( R_SEAMLESS_CUBEMAP ))
pglDisable( GL_TEXTURE_CUBE_MAP_SEAMLESS );
GL_DepthRange( gldepthmin, gldepthmax );
GL_SelectTexture( glConfig.max_texture_units - 1 ); // force to cleanup all the units
GL_CleanUpTextureUnits( 0 );
GL_AlphaTest( GL_FALSE );
GL_ClipPlane( true );
if( FBitSet( RI->params, RP_DEFERREDSCENE ))
{
// render all decals with alpha-channel
R_RenderDecalsSolidList( DRAWLIST_SOLID );
// render all decals with gray base
R_RenderDecalsSolidList( DRAWLIST_TRANS );
}
// draw grass on visible surfaces
R_RenderGrassOnList();
GL_CleanupDrawState();
}
/*
================
R_RenderSolidBrushList
================
*/
void R_RenderSolidBrushList( void )
{
cl_entity_t *cached_entity = NULL;
material_t *cached_material = NULL;
int cached_mirror = -1;
int cached_lightmap = -1;
qboolean flush_buffer = false;
mcubemap_t *cached_cubemap[2];
int startv, endv;
if( !RI->frame.solid_faces.Count() )
return;
GL_Blend( GL_FALSE );
GL_AlphaTest( GL_FALSE );
GL_DepthMask( GL_TRUE );
pglAlphaFunc( GL_GREATER, 0.25f );
numTempElems = 0;
if( GL_Support( R_SEAMLESS_CUBEMAP ))
pglEnable( GL_TEXTURE_CUBE_MAP_SEAMLESS );
pglBindVertexArray( world->vertex_array_object );
cached_cubemap[0] = &world->defaultCubemap;
cached_cubemap[1] = &world->defaultCubemap;
for( int i = 0; i < RI->frame.solid_faces.Count(); i++ )
{
CSolidEntry *entry = &RI->frame.solid_faces[i];
if( entry->m_bDrawType != DRAWTYPE_SURFACE )
continue;
mextrasurf_t *es = entry->m_pSurf->info;
cl_entity_t *e = RI->currententity = es->parent;
RI->currentmodel = e->model;
msurface_t *s = entry->m_pSurf;
if( !entry->m_hProgram ) continue;
material_t *mat = R_TextureAnimation( s )->material;
if(( i == 0 ) || ( RI->currentshader != &glsl_programs[entry->m_hProgram] ))
flush_buffer = true;
if( cached_entity != RI->currententity )
flush_buffer = true;
if( cached_material != mat )
flush_buffer = true;
if( cached_lightmap != es->lightmaptexturenum )
flush_buffer = true;
if( cached_mirror != es->subtexture[glState.stack_position] )
flush_buffer = true;
if( IsReflectShader( RI->currentshader ) && ( cached_cubemap[0] != es->cubemap[0] || cached_cubemap[1] != es->cubemap[1] ))
flush_buffer = true;
if( flush_buffer )
{
if( numTempElems )
{
pglDrawRangeElementsEXT( GL_TRIANGLES, startv, endv - 1, numTempElems, GL_UNSIGNED_INT, tempElems );
r_stats.c_total_tris += (numTempElems / 3);
r_stats.num_flushes++;
numTempElems = 0;
}
flush_buffer = false;
startv = MAX_MAP_ELEMS;
endv = 0;
}
// now cache values
cached_entity = RI->currententity = es->parent;
cached_lightmap = es->lightmaptexturenum;
RI->currentmodel = es->parent->model;
cached_mirror = es->subtexture[glState.stack_position];
cached_cubemap[0] = es->cubemap[0];
cached_cubemap[1] = es->cubemap[1];
cached_material = mat;
if( numTempElems == 0 ) // new chain has started, apply uniforms
R_SetSurfaceUniforms( entry->m_hProgram, entry->m_pSurf, ( i == 0 ));
startv = Q_min( startv, es->firstvertex );
endv = Q_max( es->firstvertex + es->numverts, endv );
R_DrawSurface( es );
}
if( numTempElems )
{
pglDrawRangeElementsEXT( GL_TRIANGLES, startv, endv - 1, numTempElems, GL_UNSIGNED_INT, tempElems );
r_stats.c_total_tris += (numTempElems / 3);
r_stats.num_flushes++;
startv = MAX_MAP_ELEMS;
numTempElems = 0;
endv = 0;
}
if( GL_Support( R_SEAMLESS_CUBEMAP ))
pglDisable( GL_TEXTURE_CUBE_MAP_SEAMLESS );
GL_DepthRange( gldepthmin, gldepthmax );
GL_CleanupDrawState();
GL_AlphaTest( GL_FALSE );
GL_ClipPlane( true );
GL_Cull( GL_FRONT );
// render all decals with alpha-channel
R_RenderDecalsSolidList( DRAWLIST_SOLID );
// draw grass on visible surfaces
R_RenderGrassOnList();
// draw dynamic lighting for world and bmodels
R_RenderDynLightList( true );
GL_CleanupDrawState();
// render all decals with gray base
R_RenderDecalsSolidList( DRAWLIST_TRANS );
}
void R_RenderTransSurface( CTransEntry *entry )
{
if( entry->m_bDrawType != DRAWTYPE_SURFACE )
return;
if( !entry->m_hProgram ) return;
mextrasurf_t *es = entry->m_pSurf->info;
cl_entity_t *e = RI->currententity = es->parent;
msurface_t *s = entry->m_pSurf;
RI->currentmodel = e->model;
int startv = MAX_MAP_ELEMS;
numTempElems = 0;
int endv = 0;
if( ScreenCopyRequired( &glsl_programs[entry->m_hProgram] ))
{
if( !FBitSet( s->flags, SURF_OCCLUDED ))
{
entry->RequestScreenColor();
// underwater fog
if( R_WaterEntity( RI->currentmodel ))
entry->RequestScreenDepth();
r_stats.c_screen_copy++;
}
}
pglBindVertexArray( world->vertex_array_object );
R_SetSurfaceUniforms( entry->m_hProgram, entry->m_pSurf, true );
startv = Q_min( startv, es->firstvertex );
endv = Q_max( es->firstvertex + es->numverts, endv );
if( FBitSet( glsl_programs[entry->m_hProgram].status, SHADER_ADDITIVE ))
{
GL_DepthMask( GL_FALSE );
GL_Blend( GL_TRUE );
pglBlendFunc( GL_SRC_ALPHA, GL_ONE );
}
else
{
GL_DepthMask( GL_TRUE );
GL_Blend( GL_FALSE );
}
R_DrawSurface( es );
if( numTempElems )
{
pglDrawRangeElementsEXT( GL_TRIANGLES, startv, endv - 1, numTempElems, GL_UNSIGNED_INT, tempElems );
r_stats.c_total_tris += (numTempElems / 3);
r_stats.num_flushes++;
numTempElems = 0;
}
GL_DepthMask( GL_TRUE );
GL_Blend( GL_FALSE );
GL_ClipPlane( true );
GL_Cull( GL_FRONT );
}
/*
================
R_RenderShadowBrushList
================
*/
void R_RenderShadowBrushList( void )
{
int cached_matrix = -1;
material_t *cached_material = NULL;
qboolean flush_buffer = false;
int startv, endv;
if( !RI->frame.solid_faces.Count() )
return;
pglBindVertexArray( world->vertex_array_object );
pglAlphaFunc( GL_GREATER, 0.25f );
numTempElems = 0;
for( int i = 0; i < RI->frame.solid_faces.Count(); i++ )
{
CSolidEntry *entry = &RI->frame.solid_faces[i];
if( entry->m_bDrawType != DRAWTYPE_SURFACE )
continue;
mextrasurf_t *es = entry->m_pSurf->info;
cl_entity_t *e = RI->currententity = es->parent;
RI->currentmodel = e->model;
msurface_t *s = entry->m_pSurf;
if( !entry->m_hProgram ) continue;
material_t *mat = s->texinfo->texture->material;
if(( i == 0 ) || ( RI->currentshader != &glsl_programs[entry->m_hProgram] ))
flush_buffer = true;
if( cached_matrix != es->parent->hCachedMatrix )
flush_buffer = true;
if( cached_material != mat )
flush_buffer = true;
if( flush_buffer )
{
if( numTempElems )
{
pglDrawRangeElementsEXT( GL_TRIANGLES, startv, endv - 1, numTempElems, GL_UNSIGNED_INT, tempElems );
r_stats.c_total_tris += (numTempElems / 3);
r_stats.num_flushes++;
numTempElems = 0;
}
flush_buffer = false;
startv = MAX_MAP_ELEMS;
endv = 0;
}
// now cache values
cached_matrix = es->parent->hCachedMatrix;
cached_material = mat;
if( numTempElems == 0 ) // new chain has started, apply uniforms
R_SetSurfaceUniforms( entry->m_hProgram, entry->m_pSurf, ( i == 0 ));
startv = Q_min( startv, es->firstvertex );
endv = Q_max( es->firstvertex + es->numverts, endv );
R_DrawSurface( es );
}
if( numTempElems )
{
pglDrawRangeElementsEXT( GL_TRIANGLES, startv, endv - 1, numTempElems, GL_UNSIGNED_INT, tempElems );
r_stats.c_total_tris += (numTempElems / 3);
r_stats.num_flushes++;
startv = MAX_MAP_ELEMS;
numTempElems = 0;
endv = 0;
}
R_RenderShadowGrassOnList();
GL_CleanupDrawState();
}