mirror of
https://github.com/Q3Rally-Team/rallyunlimited-engine.git
synced 2024-11-22 04:12:11 +00:00
1426 lines
33 KiB
C
1426 lines
33 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
// tr_shade.c
|
|
|
|
#include "tr_local.h"
|
|
|
|
/*
|
|
|
|
THIS ENTIRE FILE IS BACK END
|
|
|
|
This file deals with applying shaders to surface data in the tess struct.
|
|
*/
|
|
|
|
|
|
/*
|
|
==================
|
|
R_DrawElements
|
|
==================
|
|
*/
|
|
#ifndef USE_VULKAN
|
|
void R_DrawElements( int numIndexes, const glIndex_t *indexes ) {
|
|
qglDrawElements( GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, indexes );
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
=============================================================
|
|
|
|
SURFACE SHADERS
|
|
|
|
=============================================================
|
|
*/
|
|
|
|
shaderCommands_t tess;
|
|
#ifndef USE_VULKAN
|
|
static qboolean setArraysOnce;
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
R_BindAnimatedImage
|
|
=================
|
|
*/
|
|
static void R_BindAnimatedImage( const textureBundle_t *bundle ) {
|
|
int64_t index;
|
|
double v;
|
|
|
|
if ( bundle->isVideoMap ) {
|
|
ri.CIN_RunCinematic(bundle->videoMapHandle);
|
|
ri.CIN_UploadCinematic(bundle->videoMapHandle);
|
|
return;
|
|
}
|
|
|
|
if ( bundle->isScreenMap /*&& backEnd.viewParms.frameSceneNum == 1*/ ) {
|
|
if ( !backEnd.screenMapDone )
|
|
GL_Bind( tr.blackImage );
|
|
else
|
|
vk_update_descriptor( glState.currenttmu + 2, vk.screenMap.color_descriptor );
|
|
return;
|
|
}
|
|
|
|
if ( bundle->numImageAnimations <= 1 ) {
|
|
GL_Bind( bundle->image[0] );
|
|
return;
|
|
}
|
|
|
|
// it is necessary to do this messy calc to make sure animations line up
|
|
// exactly with waveforms of the same frequency
|
|
//v = tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE;
|
|
//index = v;
|
|
//index >>= FUNCTABLE_SIZE2;
|
|
|
|
v = tess.shaderTime * bundle->imageAnimationSpeed; // fix for frameloss bug -EC-
|
|
index = v;
|
|
|
|
if ( index < 0 ) {
|
|
index = 0; // may happen with shader time offsets
|
|
}
|
|
index %= bundle->numImageAnimations;
|
|
|
|
GL_Bind( bundle->image[ index ] );
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
DrawTris
|
|
|
|
Draws triangle outlines for debugging
|
|
================
|
|
*/
|
|
static void DrawTris( const shaderCommands_t *input ) {
|
|
#ifdef USE_VULKAN
|
|
uint32_t pipeline;
|
|
|
|
if ( r_showtris->integer == 1 && backEnd.drawConsole )
|
|
return;
|
|
|
|
if ( tess.numIndexes == 0 )
|
|
return;
|
|
|
|
if ( r_fastsky->integer && input->shader->isSky )
|
|
return;
|
|
|
|
#ifdef USE_VBO
|
|
if ( tess.vboIndex ) {
|
|
#ifdef USE_PMLIGHT
|
|
if ( tess.dlightPass )
|
|
pipeline = backEnd.viewParms.portalView == PV_MIRROR ? vk.tris_mirror_debug_red_pipeline : vk.tris_debug_red_pipeline;
|
|
else
|
|
#endif
|
|
pipeline = backEnd.viewParms.portalView == PV_MIRROR ? vk.tris_mirror_debug_green_pipeline : vk.tris_debug_green_pipeline;
|
|
} else
|
|
#endif
|
|
{
|
|
#ifdef USE_PMLIGHT
|
|
if ( tess.dlightPass )
|
|
pipeline = backEnd.viewParms.portalView == PV_MIRROR ? vk.tris_mirror_debug_red_pipeline : vk.tris_debug_red_pipeline;
|
|
else
|
|
#endif
|
|
pipeline = backEnd.viewParms.portalView == PV_MIRROR ? vk.tris_mirror_debug_pipeline : vk.tris_debug_pipeline;
|
|
}
|
|
|
|
vk_bind_pipeline( pipeline );
|
|
vk_draw_geometry( DEPTH_RANGE_ZERO, qtrue );
|
|
|
|
#else
|
|
if ( r_showtris->integer == 1 && backEnd.drawConsole )
|
|
return;
|
|
|
|
GL_ClientState( 0, CLS_NONE );
|
|
qglDisable( GL_TEXTURE_2D );
|
|
|
|
qglColor4f( 1, 1, 1, 1 );
|
|
|
|
GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE );
|
|
qglDepthRange( 0, 0 );
|
|
|
|
qglVertexPointer( 3, GL_FLOAT, sizeof( input->xyz[0] ), input->xyz );
|
|
|
|
if ( qglLockArraysEXT ) {
|
|
qglLockArraysEXT( 0, input->numVertexes );
|
|
}
|
|
|
|
R_DrawElements( input->numIndexes, input->indexes );
|
|
|
|
if ( qglUnlockArraysEXT ) {
|
|
qglUnlockArraysEXT();
|
|
}
|
|
|
|
qglEnable( GL_TEXTURE_2D );
|
|
|
|
qglDepthRange( 0, 1 );
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
DrawNormals
|
|
|
|
Draws vertex normals for debugging
|
|
================
|
|
*/
|
|
static void DrawNormals( const shaderCommands_t *input ) {
|
|
int i;
|
|
#ifdef USE_VULKAN
|
|
#ifdef USE_VBO
|
|
if ( tess.vboIndex )
|
|
return; // must be handled specially
|
|
#endif
|
|
|
|
GL_Bind( tr.whiteImage );
|
|
|
|
tess.numIndexes = 0;
|
|
for ( i = 0; i < tess.numVertexes; i++ ) {
|
|
VectorMA( tess.xyz[i], 2.0, tess.normal[i], tess.xyz[i + tess.numVertexes] );
|
|
tess.indexes[ tess.numIndexes + 0 ] = i;
|
|
tess.indexes[ tess.numIndexes + 1 ] = i + tess.numVertexes;
|
|
tess.numIndexes += 2;
|
|
}
|
|
tess.numVertexes *= 2;
|
|
Com_Memset( tess.svars.colors[0][0].rgba, tr.identityLightByte, tess.numVertexes * sizeof( color4ub_t ) );
|
|
|
|
vk_bind_pipeline( vk.normals_debug_pipeline );
|
|
vk_bind_index();
|
|
vk_bind_geometry( TESS_XYZ | TESS_RGBA0 );
|
|
vk_draw_geometry( DEPTH_RANGE_ZERO, qtrue );
|
|
#else
|
|
GL_ClientState( 0, CLS_NONE );
|
|
|
|
qglDisable( GL_TEXTURE_2D );
|
|
qglColor4f( 1, 1, 1, 1 );
|
|
|
|
qglDepthRange( 0, 0 ); // never occluded
|
|
|
|
GL_State( GLS_DEPTHMASK_TRUE );
|
|
|
|
for ( i = tess.numVertexes-1; i >= 0; i-- ) {
|
|
VectorMA( tess.xyz[i], 2.0, tess.normal[i], tess.xyz[i*2 + 1] );
|
|
VectorCopy( tess.xyz[i], tess.xyz[i*2] );
|
|
}
|
|
|
|
qglVertexPointer( 3, GL_FLOAT, sizeof( tess.xyz[0] ), tess.xyz );
|
|
|
|
if ( qglLockArraysEXT ) {
|
|
qglLockArraysEXT( 0, tess.numVertexes * 2 );
|
|
}
|
|
|
|
qglDrawArrays( GL_LINES, 0, tess.numVertexes * 2 );
|
|
|
|
if ( qglUnlockArraysEXT ) {
|
|
qglUnlockArraysEXT();
|
|
}
|
|
|
|
qglEnable( GL_TEXTURE_2D );
|
|
|
|
qglDepthRange( 0, 1 );
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
RB_BeginSurface
|
|
|
|
We must set some things up before beginning any tesselation,
|
|
because a surface may be forced to perform a RB_End due
|
|
to overflow.
|
|
==============
|
|
*/
|
|
void RB_BeginSurface( shader_t *shader, int fogNum ) {
|
|
|
|
shader_t *state;
|
|
|
|
#ifdef USE_VBO
|
|
if ( shader->isStaticShader && !shader->remappedShader ) {
|
|
tess.allowVBO = qtrue;
|
|
} else {
|
|
tess.allowVBO = qfalse;
|
|
}
|
|
#endif
|
|
|
|
if ( shader->remappedShader ) {
|
|
state = shader->remappedShader;
|
|
} else {
|
|
state = shader;
|
|
}
|
|
|
|
#ifdef USE_PMLIGHT
|
|
if ( tess.fogNum != fogNum ) {
|
|
tess.dlightUpdateParams = qtrue;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_TESS_NEEDS_NORMAL
|
|
#ifdef USE_PMLIGHT
|
|
tess.needsNormal = state->needsNormal || tess.dlightPass || r_shownormals->integer;
|
|
#else
|
|
tess.needsNormal = state->needsNormal || r_shownormals->integer;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef USE_TESS_NEEDS_ST2
|
|
tess.needsST2 = state->needsST2;
|
|
#endif
|
|
|
|
tess.numIndexes = 0;
|
|
tess.numVertexes = 0;
|
|
tess.shader = state;
|
|
tess.fogNum = fogNum;
|
|
|
|
#ifdef USE_LEGACY_DLIGHTS
|
|
tess.dlightBits = 0; // will be OR'd in by surface functions
|
|
#endif
|
|
tess.xstages = state->stages;
|
|
tess.numPasses = state->numUnfoggedPasses;
|
|
|
|
tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset;
|
|
if ( tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime ) {
|
|
tess.shaderTime = tess.shader->clampTime;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
DrawMultitextured
|
|
|
|
output = t0 * t1 or t0 + t1
|
|
|
|
t0 = most upstream according to spec
|
|
t1 = most downstream according to spec
|
|
===================
|
|
*/
|
|
#ifndef USE_VULKAN
|
|
static void DrawMultitextured( const shaderCommands_t *input, int stage ) {
|
|
const shaderStage_t *pStage;
|
|
|
|
pStage = tess.xstages[ stage ];
|
|
|
|
GL_State( pStage->stateBits );
|
|
|
|
if ( !setArraysOnce ) {
|
|
R_ComputeColors( 0, tess.svars.colors[0], pStage );
|
|
R_ComputeTexCoords( 0, &pStage->bundle[0] );
|
|
R_ComputeTexCoords( 1, &pStage->bundle[1] );
|
|
GL_ClientState( 0, CLS_TEXCOORD_ARRAY | CLS_COLOR_ARRAY );
|
|
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoordPtr[0] );
|
|
qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, input->svars.colors[0].rgba );
|
|
|
|
GL_ClientState( 1, CLS_TEXCOORD_ARRAY );
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoordPtr[1] );
|
|
}
|
|
|
|
//
|
|
// base
|
|
//
|
|
GL_SelectTexture( 0 );
|
|
R_BindAnimatedImage( &pStage->bundle[0] );
|
|
|
|
//
|
|
// lightmap/secondary pass
|
|
//
|
|
GL_SelectTexture( 1 );
|
|
qglEnable( GL_TEXTURE_2D );
|
|
R_BindAnimatedImage( &pStage->bundle[1] );
|
|
|
|
if ( r_lightmap->integer ) {
|
|
GL_TexEnv( GL_REPLACE );
|
|
} else {
|
|
GL_TexEnv( pStage->mtEnv );
|
|
}
|
|
|
|
R_DrawElements( input->numIndexes, input->indexes );
|
|
|
|
//
|
|
// disable texturing on TEXTURE1, then select TEXTURE0
|
|
//
|
|
//GL_ClientState( 1, CLS_NONE );
|
|
|
|
qglDisable( GL_TEXTURE_2D );
|
|
GL_SelectTexture( 0 );
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef USE_LEGACY_DLIGHTS
|
|
/*
|
|
===================
|
|
ProjectDlightTexture
|
|
|
|
Perform dynamic lighting with another rendering pass
|
|
===================
|
|
*/
|
|
static void ProjectDlightTexture_scalar( void ) {
|
|
int i, l;
|
|
vec3_t origin;
|
|
float *texCoords;
|
|
byte *colors;
|
|
byte clipBits[SHADER_MAX_VERTEXES];
|
|
#ifdef USE_VULKAN
|
|
uint32_t pipeline;
|
|
#else
|
|
float texCoordsArray[SHADER_MAX_VERTEXES][2];
|
|
byte colorArray[SHADER_MAX_VERTEXES][4];
|
|
#endif
|
|
glIndex_t hitIndexes[SHADER_MAX_INDEXES];
|
|
int numIndexes;
|
|
float scale;
|
|
float radius;
|
|
float modulate = 0.0f;
|
|
const dlight_t *dl;
|
|
|
|
if ( !backEnd.refdef.num_dlights ) {
|
|
return;
|
|
}
|
|
|
|
for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) {
|
|
|
|
if ( !( tess.dlightBits & ( 1 << l ) ) ) {
|
|
continue; // this surface definitely doesn't have any of this light
|
|
}
|
|
|
|
#ifdef USE_VULKAN
|
|
texCoords = (float*)&tess.svars.texcoords[0][0];
|
|
tess.svars.texcoordPtr[0] = tess.svars.texcoords[0];
|
|
colors = tess.svars.colors[0][0].rgba;
|
|
#else
|
|
texCoords = texCoordsArray[0];
|
|
colors = colorArray[0];
|
|
#endif
|
|
|
|
dl = &backEnd.refdef.dlights[l];
|
|
VectorCopy( dl->transformed, origin );
|
|
radius = dl->radius;
|
|
scale = 1.0f / radius;
|
|
|
|
for ( i = 0 ; i < tess.numVertexes ; i++, texCoords += 2, colors += 4 ) {
|
|
int clip = 0;
|
|
vec3_t dist;
|
|
|
|
VectorSubtract( origin, tess.xyz[i], dist );
|
|
|
|
backEnd.pc.c_dlightVertexes++;
|
|
|
|
texCoords[0] = 0.5f + dist[0] * scale;
|
|
texCoords[1] = 0.5f + dist[1] * scale;
|
|
|
|
if ( !r_dlightBacks->integer &&
|
|
// dist . tess.normal[i]
|
|
( dist[0] * tess.normal[i][0] +
|
|
dist[1] * tess.normal[i][1] +
|
|
dist[2] * tess.normal[i][2] ) < 0.0f ) {
|
|
clip = 63;
|
|
} else {
|
|
if ( texCoords[0] < 0.0f ) {
|
|
clip |= 1;
|
|
} else if ( texCoords[0] > 1.0f ) {
|
|
clip |= 2;
|
|
}
|
|
if ( texCoords[1] < 0.0f ) {
|
|
clip |= 4;
|
|
} else if ( texCoords[1] > 1.0f ) {
|
|
clip |= 8;
|
|
}
|
|
|
|
// modulate the strength based on the height and color
|
|
if ( dist[2] > radius ) {
|
|
clip |= 16;
|
|
modulate = 0.0f;
|
|
} else if ( dist[2] < -radius ) {
|
|
clip |= 32;
|
|
modulate = 0.0f;
|
|
} else {
|
|
//*((int*)&dist[2]) &= 0x7FFFFFFF;
|
|
dist[2] = fabsf( dist[2] );
|
|
if ( dist[2] < radius * 0.5f ) {
|
|
modulate = 1.0 * 255.0;
|
|
} else {
|
|
modulate = 2.0f * (radius - dist[2]) * scale * 255.0;
|
|
}
|
|
}
|
|
}
|
|
clipBits[i] = clip;
|
|
colors[0] = dl->color[0] * modulate;
|
|
colors[1] = dl->color[1] * modulate;
|
|
colors[2] = dl->color[2] * modulate;
|
|
colors[3] = 255;
|
|
}
|
|
|
|
// build a list of triangles that need light
|
|
numIndexes = 0;
|
|
for ( i = 0 ; i < tess.numIndexes ; i += 3 ) {
|
|
int a, b, c;
|
|
|
|
a = tess.indexes[i];
|
|
b = tess.indexes[i+1];
|
|
c = tess.indexes[i+2];
|
|
if ( clipBits[a] & clipBits[b] & clipBits[c] ) {
|
|
continue; // not lighted
|
|
}
|
|
hitIndexes[numIndexes] = a;
|
|
hitIndexes[numIndexes+1] = b;
|
|
hitIndexes[numIndexes+2] = c;
|
|
numIndexes += 3;
|
|
}
|
|
|
|
if ( !numIndexes ) {
|
|
continue;
|
|
}
|
|
|
|
#ifndef USE_VULKAN
|
|
GL_ClientState( 1, CLS_NONE );
|
|
GL_ClientState( 0, CLS_TEXCOORD_ARRAY | CLS_COLOR_ARRAY );
|
|
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, texCoordsArray[0] );
|
|
qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray );
|
|
#endif
|
|
|
|
GL_Bind( tr.dlightImage );
|
|
|
|
#ifdef USE_VULKAN
|
|
pipeline = vk.dlight_pipelines[dl->additive > 0 ? 1 : 0][tess.shader->cullType][tess.shader->polygonOffset];
|
|
vk_bind_pipeline( pipeline );
|
|
vk_bind_index_ext( numIndexes, hitIndexes );
|
|
vk_bind_geometry( TESS_RGBA0 | TESS_ST0 );
|
|
vk_draw_geometry( DEPTH_RANGE_NORMAL, qtrue );
|
|
#else
|
|
// include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light
|
|
// where they aren't rendered
|
|
|
|
if ( dl->additive ) {
|
|
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL );
|
|
} else {
|
|
GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL );
|
|
}
|
|
|
|
R_DrawElements( numIndexes, hitIndexes );
|
|
#endif
|
|
backEnd.pc.c_totalIndexes += numIndexes;
|
|
backEnd.pc.c_dlightIndexes += numIndexes;
|
|
}
|
|
}
|
|
|
|
|
|
static void ProjectDlightTexture( void ) {
|
|
ProjectDlightTexture_scalar();
|
|
}
|
|
#endif // USE_LEGACY_DLIGHTS
|
|
|
|
uint32_t VK_PushUniform( const vkUniform_t *uniform );
|
|
void VK_SetFogParams( vkUniform_t *uniform, int *fogStage );
|
|
static vkUniform_t uniform;
|
|
|
|
/*
|
|
===================
|
|
RB_FogPass
|
|
|
|
Blends a fog texture on top of everything else
|
|
===================
|
|
*/
|
|
static void RB_FogPass( void ) {
|
|
#ifdef USE_VULKAN
|
|
uint32_t pipeline = vk.fog_pipelines[tess.shader->fogPass - 1][tess.shader->cullType][tess.shader->polygonOffset];
|
|
#ifdef USE_FOG_ONLY
|
|
int fog_stage;
|
|
|
|
// fog parameters
|
|
vk_bind_pipeline( pipeline );
|
|
VK_SetFogParams( &uniform, &fog_stage );
|
|
VK_PushUniform( &uniform );
|
|
vk_update_descriptor( 3, tr.fogImage->descriptor );
|
|
vk_draw_geometry( DEPTH_RANGE_NORMAL, qtrue );
|
|
#else
|
|
const fog_t *fog;
|
|
int i;
|
|
|
|
fog = tr.world->fogs + tess.fogNum;
|
|
|
|
for ( i = 0; i < tess.numVertexes; i++ ) {
|
|
tess.svars.colors[0][i].u32 = fog->colorInt.u32;
|
|
}
|
|
|
|
RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[0] );
|
|
tess.svars.texcoordPtr[ 0 ] = tess.svars.texcoords[ 0 ];
|
|
GL_Bind( tr.fogImage );
|
|
|
|
vk_bind_pipeline( pipeline );
|
|
vk_bind_geometry( TESS_ST0 | TESS_RGBA0 );
|
|
vk_draw_geometry( DEPTH_RANGE_NORMAL, qtrue );
|
|
#endif
|
|
#else
|
|
const fog_t *fog;
|
|
int i;
|
|
|
|
RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[0] );
|
|
|
|
GL_ClientState( 1, CLS_NONE );
|
|
GL_ClientState( 0, CLS_TEXCOORD_ARRAY | CLS_COLOR_ARRAY );
|
|
|
|
qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors[0].rgba );
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] );
|
|
|
|
GL_SelectTexture( 0 );
|
|
GL_Bind( tr.fogImage );
|
|
|
|
if ( tess.shader->fogPass == FP_EQUAL ) {
|
|
GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL );
|
|
} else {
|
|
GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
|
|
}
|
|
|
|
R_DrawElements( tess.numIndexes, tess.indexes );
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_ComputeColors
|
|
===============
|
|
*/
|
|
void R_ComputeColors( const int b, color4ub_t *dest, const shaderStage_t *pStage )
|
|
{
|
|
int i;
|
|
|
|
if ( !tess.numVertexes )
|
|
return;
|
|
|
|
//
|
|
// rgbGen
|
|
//
|
|
switch ( pStage->bundle[b].rgbGen )
|
|
{
|
|
case CGEN_IDENTITY:
|
|
Com_Memset( dest, 0xff, tess.numVertexes * 4 );
|
|
break;
|
|
default:
|
|
case CGEN_IDENTITY_LIGHTING:
|
|
Com_Memset( dest, tr.identityLightByte, tess.numVertexes * 4 );
|
|
break;
|
|
case CGEN_LIGHTING_DIFFUSE:
|
|
RB_CalcDiffuseColor( ( unsigned char * ) dest );
|
|
break;
|
|
case CGEN_EXACT_VERTEX:
|
|
Com_Memcpy( dest, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) );
|
|
break;
|
|
case CGEN_CONST:
|
|
for ( i = 0; i < tess.numVertexes; i++ ) {
|
|
dest[i].u32 = pStage->bundle[b].constantColor.u32;
|
|
}
|
|
break;
|
|
case CGEN_VERTEX:
|
|
if ( tr.identityLight == 1 )
|
|
{
|
|
Com_Memcpy( dest, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) );
|
|
}
|
|
else
|
|
{
|
|
for ( i = 0; i < tess.numVertexes; i++ )
|
|
{
|
|
dest[i].rgba[0] = tess.vertexColors[i].rgba[0] * tr.identityLight;
|
|
dest[i].rgba[1] = tess.vertexColors[i].rgba[1] * tr.identityLight;
|
|
dest[i].rgba[2] = tess.vertexColors[i].rgba[2] * tr.identityLight;
|
|
dest[i].rgba[3] = tess.vertexColors[i].rgba[3];
|
|
}
|
|
}
|
|
break;
|
|
case CGEN_ONE_MINUS_VERTEX:
|
|
if ( tr.identityLight == 1 )
|
|
{
|
|
for ( i = 0; i < tess.numVertexes; i++ )
|
|
{
|
|
dest[i].rgba[0] = 255 - tess.vertexColors[i].rgba[0];
|
|
dest[i].rgba[1] = 255 - tess.vertexColors[i].rgba[1];
|
|
dest[i].rgba[2] = 255 - tess.vertexColors[i].rgba[2];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( i = 0; i < tess.numVertexes; i++ )
|
|
{
|
|
dest[i].rgba[0] = ( 255 - tess.vertexColors[i].rgba[0] ) * tr.identityLight;
|
|
dest[i].rgba[1] = ( 255 - tess.vertexColors[i].rgba[1] ) * tr.identityLight;
|
|
dest[i].rgba[2] = ( 255 - tess.vertexColors[i].rgba[2] ) * tr.identityLight;
|
|
}
|
|
}
|
|
break;
|
|
case CGEN_FOG:
|
|
{
|
|
const fog_t *fog;
|
|
|
|
fog = tr.world->fogs + tess.fogNum;
|
|
|
|
for ( i = 0; i < tess.numVertexes; i++ ) {
|
|
dest[i].u32 = fog->colorInt.u32;
|
|
}
|
|
}
|
|
break;
|
|
case CGEN_WAVEFORM:
|
|
RB_CalcWaveColor( &pStage->bundle[b].rgbWave, dest->rgba );
|
|
break;
|
|
case CGEN_ENTITY:
|
|
RB_CalcColorFromEntity( dest->rgba );
|
|
break;
|
|
case CGEN_ONE_MINUS_ENTITY:
|
|
RB_CalcColorFromOneMinusEntity( dest->rgba );
|
|
break;
|
|
}
|
|
|
|
//
|
|
// alphaGen
|
|
//
|
|
switch ( pStage->bundle[b].alphaGen )
|
|
{
|
|
case AGEN_SKIP:
|
|
break;
|
|
case AGEN_IDENTITY:
|
|
if ( ( pStage->bundle[b].rgbGen == CGEN_VERTEX && tr.identityLight != 1 ) ||
|
|
pStage->bundle[b].rgbGen != CGEN_VERTEX ) {
|
|
for ( i = 0; i < tess.numVertexes; i++ ) {
|
|
dest[i].rgba[3] = 255;
|
|
}
|
|
}
|
|
break;
|
|
case AGEN_CONST:
|
|
for ( i = 0; i < tess.numVertexes; i++ ) {
|
|
dest[i].rgba[3] = pStage->bundle[b].constantColor.rgba[3];
|
|
}
|
|
break;
|
|
case AGEN_WAVEFORM:
|
|
RB_CalcWaveAlpha( &pStage->bundle[b].alphaWave, dest->rgba );
|
|
break;
|
|
case AGEN_LIGHTING_SPECULAR:
|
|
RB_CalcSpecularAlpha( dest->rgba );
|
|
break;
|
|
case AGEN_ENTITY:
|
|
RB_CalcAlphaFromEntity( dest->rgba );
|
|
break;
|
|
case AGEN_ONE_MINUS_ENTITY:
|
|
RB_CalcAlphaFromOneMinusEntity( dest->rgba );
|
|
break;
|
|
case AGEN_VERTEX:
|
|
for ( i = 0; i < tess.numVertexes; i++ ) {
|
|
dest[i].rgba[3] = tess.vertexColors[i].rgba[3];
|
|
}
|
|
break;
|
|
case AGEN_ONE_MINUS_VERTEX:
|
|
for ( i = 0; i < tess.numVertexes; i++ )
|
|
{
|
|
dest[i].rgba[3] = 255 - tess.vertexColors[i].rgba[3];
|
|
}
|
|
break;
|
|
case AGEN_PORTAL:
|
|
{
|
|
for ( i = 0; i < tess.numVertexes; i++ )
|
|
{
|
|
unsigned char alpha;
|
|
float len;
|
|
vec3_t v;
|
|
|
|
VectorSubtract( tess.xyz[i], backEnd.viewParms.or.origin, v );
|
|
len = VectorLength( v ) * tess.shader->portalRangeR;
|
|
|
|
if ( len > 1 )
|
|
{
|
|
alpha = 0xff;
|
|
}
|
|
else
|
|
{
|
|
alpha = len * 0xff;
|
|
}
|
|
|
|
dest[i].rgba[3] = alpha;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// fog adjustment for colors to fade out as fog increases
|
|
//
|
|
if ( tess.fogNum )
|
|
{
|
|
switch ( pStage->bundle[b].adjustColorsForFog )
|
|
{
|
|
case ACFF_MODULATE_RGB:
|
|
RB_CalcModulateColorsByFog( dest->rgba );
|
|
break;
|
|
case ACFF_MODULATE_ALPHA:
|
|
RB_CalcModulateAlphasByFog( dest->rgba );
|
|
break;
|
|
case ACFF_MODULATE_RGBA:
|
|
RB_CalcModulateRGBAsByFog( dest->rgba );
|
|
break;
|
|
case ACFF_NONE:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
R_ComputeTexCoords
|
|
===============
|
|
*/
|
|
void R_ComputeTexCoords( const int b, const textureBundle_t *bundle ) {
|
|
int i;
|
|
int tm;
|
|
vec2_t *src, *dst;
|
|
|
|
if ( !tess.numVertexes )
|
|
return;
|
|
|
|
src = dst = tess.svars.texcoords[b];
|
|
|
|
//
|
|
// generate the texture coordinates
|
|
//
|
|
switch ( bundle->tcGen )
|
|
{
|
|
case TCGEN_IDENTITY:
|
|
src = tess.texCoords00;
|
|
break;
|
|
case TCGEN_TEXTURE:
|
|
src = tess.texCoords[0];
|
|
break;
|
|
case TCGEN_LIGHTMAP:
|
|
src = tess.texCoords[1];
|
|
break;
|
|
case TCGEN_VECTOR:
|
|
for ( i = 0 ; i < tess.numVertexes ; i++ ) {
|
|
dst[i][0] = DotProduct( tess.xyz[i], bundle->tcGenVectors[0] );
|
|
dst[i][1] = DotProduct( tess.xyz[i], bundle->tcGenVectors[1] );
|
|
}
|
|
break;
|
|
case TCGEN_FOG:
|
|
RB_CalcFogTexCoords( ( float * ) dst );
|
|
break;
|
|
case TCGEN_ENVIRONMENT_MAPPED:
|
|
RB_CalcEnvironmentTexCoords( ( float * ) dst );
|
|
break;
|
|
case TCGEN_ENVIRONMENT_MAPPED_FP:
|
|
RB_CalcEnvironmentTexCoordsFP( ( float * ) dst, bundle->isScreenMap );
|
|
break;
|
|
case TCGEN_BAD:
|
|
return;
|
|
}
|
|
|
|
//
|
|
// alter texture coordinates
|
|
//
|
|
for ( tm = 0; tm < bundle->numTexMods ; tm++ ) {
|
|
switch ( bundle->texMods[tm].type )
|
|
{
|
|
case TMOD_NONE:
|
|
tm = TR_MAX_TEXMODS; // break out of for loop
|
|
break;
|
|
|
|
case TMOD_TURBULENT:
|
|
RB_CalcTurbulentTexCoords( &bundle->texMods[tm].wave, (float *)src, (float *) dst );
|
|
src = dst;
|
|
break;
|
|
|
|
case TMOD_ENTITY_TRANSLATE:
|
|
RB_CalcScrollTexCoords( backEnd.currentEntity->e.shaderTexCoord, (float *)src, (float *) dst );
|
|
src = dst;
|
|
break;
|
|
|
|
case TMOD_SCROLL:
|
|
RB_CalcScrollTexCoords( bundle->texMods[tm].scroll, (float *)src, (float *) dst );
|
|
src = dst;
|
|
break;
|
|
|
|
case TMOD_SCALE:
|
|
RB_CalcScaleTexCoords( bundle->texMods[tm].scale, (float *) src, (float *) dst );
|
|
src = dst;
|
|
break;
|
|
|
|
case TMOD_OFFSET:
|
|
for ( i = 0; i < tess.numVertexes; i++ ) {
|
|
dst[i][0] = src[i][0] + bundle->texMods[tm].offset[0];
|
|
dst[i][1] = src[i][1] + bundle->texMods[tm].offset[1];
|
|
}
|
|
src = dst;
|
|
break;
|
|
|
|
case TMOD_SCALE_OFFSET:
|
|
for ( i = 0; i < tess.numVertexes; i++ ) {
|
|
dst[i][0] = (src[i][0] * bundle->texMods[tm].scale[0] ) + bundle->texMods[tm].offset[0];
|
|
dst[i][1] = (src[i][1] * bundle->texMods[tm].scale[1] ) + bundle->texMods[tm].offset[1];
|
|
}
|
|
src = dst;
|
|
break;
|
|
|
|
case TMOD_STRETCH:
|
|
RB_CalcStretchTexCoords( &bundle->texMods[tm].wave, (float *)src, (float *) dst );
|
|
src = dst;
|
|
break;
|
|
|
|
case TMOD_TRANSFORM:
|
|
RB_CalcTransformTexCoords( &bundle->texMods[tm], (float *)src, (float *) dst );
|
|
src = dst;
|
|
break;
|
|
|
|
case TMOD_ROTATE:
|
|
RB_CalcRotateTexCoords( bundle->texMods[tm].rotateSpeed, (float *) src, (float *) dst );
|
|
src = dst;
|
|
break;
|
|
|
|
default:
|
|
ri.Error( ERR_DROP, "ERROR: unknown texmod '%d' in shader '%s'", bundle->texMods[tm].type, tess.shader->name );
|
|
break;
|
|
}
|
|
}
|
|
|
|
tess.svars.texcoordPtr[ b ] = src;
|
|
}
|
|
|
|
|
|
/*
|
|
** RB_IterateStagesGeneric
|
|
*/
|
|
#ifdef USE_VULKAN
|
|
static void RB_IterateStagesGeneric( const shaderCommands_t *input, qboolean fogCollapse )
|
|
#else
|
|
static void RB_IterateStagesGeneric( const shaderCommands_t *input )
|
|
#endif
|
|
{
|
|
const shaderStage_t *pStage;
|
|
int tess_flags;
|
|
int stage, i;
|
|
|
|
#ifdef USE_VULKAN
|
|
uint32_t pipeline;
|
|
int fog_stage;
|
|
|
|
vk_bind_index();
|
|
|
|
tess_flags = input->shader->tessFlags;
|
|
|
|
#ifdef USE_FOG_COLLAPSE
|
|
if ( fogCollapse ) {
|
|
VK_SetFogParams( &uniform, &fog_stage );
|
|
VectorCopy( backEnd.or.viewOrigin, uniform.eyePos );
|
|
VK_PushUniform( &uniform );
|
|
vk_update_descriptor( 5, tr.fogImage->descriptor );
|
|
} else
|
|
#endif
|
|
{
|
|
fog_stage = 0;
|
|
if ( tess_flags & TESS_VPOS ) {
|
|
VectorCopy( backEnd.or.viewOrigin, uniform.eyePos );
|
|
VK_PushUniform( &uniform );
|
|
tess_flags &= ~TESS_VPOS;
|
|
}
|
|
}
|
|
#endif // USE_VULKAN
|
|
|
|
for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ )
|
|
{
|
|
pStage = tess.xstages[ stage ];
|
|
if ( !pStage )
|
|
break;
|
|
|
|
#ifdef USE_VBO
|
|
tess.vboStage = stage;
|
|
#endif
|
|
|
|
#ifdef USE_VULKAN
|
|
tess_flags |= pStage->tessFlags;
|
|
|
|
for ( i = 0; i < pStage->numTexBundles; i++ ) {
|
|
if ( pStage->bundle[i].image[0] != NULL ) {
|
|
GL_SelectTexture( i );
|
|
R_BindAnimatedImage( &pStage->bundle[i] );
|
|
if ( tess_flags & ( TESS_ST0 << i ) ) {
|
|
R_ComputeTexCoords( i, &pStage->bundle[i] );
|
|
}
|
|
if ( tess_flags & ( TESS_RGBA0 << i ) ) {
|
|
R_ComputeColors( i, tess.svars.colors[i], pStage );
|
|
}
|
|
}
|
|
}
|
|
|
|
GL_SelectTexture( 0 );
|
|
|
|
if ( backEnd.viewParms.portalView == PV_MIRROR )
|
|
pipeline = pStage->vk_mirror_pipeline[ fog_stage ];
|
|
else
|
|
pipeline = pStage->vk_pipeline[ fog_stage ];
|
|
|
|
if ( r_lightmap->integer && pStage->bundle[1].lightmap != LIGHTMAP_INDEX_NONE ) {
|
|
//GL_SelectTexture( 0 );
|
|
GL_Bind( tr.whiteImage ); // replace diffuse texture with a white one thus effectively render only lightmap
|
|
}
|
|
|
|
vk_bind_pipeline( pipeline );
|
|
vk_bind_geometry( tess_flags );
|
|
vk_draw_geometry( tess.depthRange, qtrue );
|
|
|
|
if ( pStage->depthFragment ) {
|
|
if ( backEnd.viewParms.portalView == PV_MIRROR )
|
|
pipeline = pStage->vk_mirror_pipeline_df;
|
|
else
|
|
pipeline = pStage->vk_pipeline_df;
|
|
vk_bind_pipeline( pipeline );
|
|
vk_draw_geometry( tess.depthRange, qtrue );
|
|
}
|
|
#else
|
|
R_ComputeColors( 0, tess.svars.colors[0].rgba, pStage );
|
|
|
|
R_ComputeTexCoords( 0, &pStage->bundle[0] );
|
|
|
|
//
|
|
// do multitexture
|
|
//
|
|
if ( pStage->bundle[1].image[0] != NULL )
|
|
{
|
|
DrawMultitextured( input, stage );
|
|
}
|
|
else
|
|
{
|
|
if ( !setArraysOnce )
|
|
{
|
|
R_ComputeTexCoords( 0, &pStage->bundle[0] );
|
|
R_ComputeColors( 0, tess.svars.colors[0], pStage );
|
|
|
|
GL_ClientState( 1, CLS_NONE );
|
|
GL_ClientState( 0, CLS_TEXCOORD_ARRAY | CLS_COLOR_ARRAY );
|
|
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoordPtr[0] );
|
|
qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, input->svars.colors[0].rgba );
|
|
}
|
|
|
|
//
|
|
// set state
|
|
//
|
|
R_BindAnimatedImage( &pStage->bundle[0] );
|
|
|
|
GL_State( pStage->stateBits );
|
|
|
|
//
|
|
// draw
|
|
//
|
|
R_DrawElements( input->numIndexes, input->indexes );
|
|
}
|
|
#endif
|
|
|
|
// allow skipping out to show just lightmaps during development
|
|
if ( r_lightmap->integer && ( pStage->bundle[0].lightmap != LIGHTMAP_INDEX_NONE || pStage->bundle[1].lightmap != LIGHTMAP_INDEX_NONE ) )
|
|
break;
|
|
|
|
tess_flags = 0;
|
|
}
|
|
|
|
#ifdef USE_VULKAN
|
|
if ( tess_flags ) // fog-only shaders?
|
|
vk_bind_geometry( tess_flags );
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef USE_VULKAN
|
|
|
|
void VK_SetFogParams( vkUniform_t *uniform, int *fogStage )
|
|
{
|
|
if ( tess.fogNum && tess.shader->fogPass ) {
|
|
const fogProgramParms_t *fp = RB_CalcFogProgramParms();
|
|
// vertex data
|
|
Vector4Copy( fp->fogDistanceVector, uniform->fogDistanceVector );
|
|
Vector4Copy( fp->fogDepthVector, uniform->fogDepthVector );
|
|
uniform->fogEyeT[0] = fp->eyeT;
|
|
if ( fp->eyeOutside ) {
|
|
uniform->fogEyeT[1] = 0.0; // fog eye out
|
|
} else {
|
|
uniform->fogEyeT[1] = 1.0; // fog eye in
|
|
}
|
|
// fragment data
|
|
Vector4Copy( fp->fogColor, uniform->fogColor );
|
|
*fogStage = 1;
|
|
} else {
|
|
*fogStage = 0;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef USE_PMLIGHT
|
|
static void VK_SetLightParams( vkUniform_t *uniform, const dlight_t *dl ) {
|
|
float radius;
|
|
|
|
#ifdef USE_VULKAN
|
|
if ( !glConfig.deviceSupportsGamma && !vk.fboActive )
|
|
#else
|
|
if ( !glConfig.deviceSupportsGamma )
|
|
#endif
|
|
VectorScale( dl->color, 2 * powf( r_intensity->value, r_gamma->value ), uniform->lightColor);
|
|
else
|
|
VectorCopy( dl->color, uniform->lightColor );
|
|
|
|
radius = dl->radius;
|
|
|
|
// vertex data
|
|
VectorCopy( backEnd.or.viewOrigin, uniform->eyePos ); uniform->eyePos[3] = 0.0f;
|
|
VectorCopy( dl->transformed, uniform->lightPos ); uniform->lightPos[3] = 0.0f;
|
|
|
|
// fragment data
|
|
uniform->lightColor[3] = 1.0f / Square( radius );
|
|
|
|
if ( dl->linear )
|
|
{
|
|
vec4_t ab;
|
|
VectorSubtract( dl->transformed2, dl->transformed, ab );
|
|
ab[3] = 1.0f / DotProduct( ab, ab );
|
|
Vector4Copy( ab, uniform->lightVector );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
uint32_t VK_PushUniform( const vkUniform_t *uniform ) {
|
|
const uint32_t offset = vk.cmd->uniform_read_offset = PAD( vk.cmd->vertex_buffer_offset, vk.uniform_alignment );
|
|
|
|
if ( offset + vk.uniform_item_size > vk.geometry_buffer_size )
|
|
return ~0U;
|
|
|
|
// push uniform
|
|
Com_Memcpy( vk.cmd->vertex_buffer_ptr + offset, uniform, sizeof( *uniform ) );
|
|
vk.cmd->vertex_buffer_offset = offset + vk.uniform_item_size;
|
|
|
|
vk_reset_descriptor( 1 );
|
|
vk_update_descriptor( 1, vk.cmd->uniform_descriptor );
|
|
vk_update_descriptor_offset( 1, vk.cmd->uniform_read_offset );
|
|
|
|
return offset;
|
|
}
|
|
|
|
|
|
#ifdef USE_PMLIGHT
|
|
void VK_LightingPass( void )
|
|
{
|
|
static uint32_t uniform_offset;
|
|
static int fog_stage;
|
|
uint32_t pipeline;
|
|
const shaderStage_t *pStage;
|
|
cullType_t cull;
|
|
int abs_light;
|
|
|
|
if ( tess.shader->lightingStage < 0 )
|
|
return;
|
|
|
|
pStage = tess.xstages[ tess.shader->lightingStage ];
|
|
|
|
// we may need to update programs for fog transitions
|
|
if ( tess.dlightUpdateParams ) {
|
|
|
|
// fog parameters
|
|
VK_SetFogParams( &uniform, &fog_stage );
|
|
// light parameters
|
|
VK_SetLightParams( &uniform, tess.light );
|
|
|
|
uniform_offset = VK_PushUniform( &uniform );
|
|
|
|
tess.dlightUpdateParams = qfalse;
|
|
}
|
|
|
|
if ( uniform_offset == ~0 )
|
|
return; // no space left...
|
|
|
|
cull = tess.shader->cullType;
|
|
if ( backEnd.viewParms.portalView == PV_MIRROR ) {
|
|
switch ( cull ) {
|
|
case CT_FRONT_SIDED: cull = CT_BACK_SIDED; break;
|
|
case CT_BACK_SIDED: cull = CT_FRONT_SIDED; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
abs_light = /* (pStage->stateBits & GLS_ATEST_BITS) && */ (cull == CT_TWO_SIDED) ? 1 : 0;
|
|
|
|
if ( fog_stage )
|
|
vk_update_descriptor( 3, tr.fogImage->descriptor );
|
|
|
|
if ( tess.light->linear )
|
|
pipeline = vk.dlight1_pipelines_x[cull][tess.shader->polygonOffset][fog_stage][abs_light];
|
|
else
|
|
pipeline = vk.dlight_pipelines_x[cull][tess.shader->polygonOffset][fog_stage][abs_light];
|
|
|
|
GL_SelectTexture( 0 );
|
|
R_BindAnimatedImage( &pStage->bundle[ tess.shader->lightingBundle ] );
|
|
|
|
#ifdef USE_VBO
|
|
if ( tess.vboIndex == 0 )
|
|
#endif
|
|
{
|
|
R_ComputeTexCoords( tess.shader->lightingBundle, &pStage->bundle[ tess.shader->lightingBundle ] );
|
|
}
|
|
|
|
vk_bind_pipeline( pipeline );
|
|
vk_bind_index();
|
|
vk_bind_lighting( tess.shader->lightingStage, tess.shader->lightingBundle );
|
|
vk_draw_geometry( tess.depthRange, qtrue );
|
|
}
|
|
#endif // USE_PMLIGHT
|
|
|
|
|
|
void RB_StageIteratorGeneric( void )
|
|
{
|
|
qboolean fogCollapse = qfalse;
|
|
|
|
#ifdef USE_VBO
|
|
if ( tess.vboIndex != 0 ) {
|
|
VBO_PrepareQueues();
|
|
tess.vboStage = 0;
|
|
} else
|
|
#endif
|
|
RB_DeformTessGeometry();
|
|
|
|
#ifdef USE_PMLIGHT
|
|
if ( tess.dlightPass ) {
|
|
VK_LightingPass();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_FOG_COLLAPSE
|
|
fogCollapse = tess.fogNum && tess.shader->fogPass && tess.shader->fogCollapse;
|
|
#endif
|
|
|
|
// call shader function
|
|
RB_IterateStagesGeneric( &tess, fogCollapse );
|
|
|
|
// now do any dynamic lighting needed
|
|
#ifdef USE_LEGACY_DLIGHTS
|
|
#ifdef USE_PMLIGHT
|
|
if ( r_dlightMode->integer == 0 )
|
|
#endif
|
|
if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) {
|
|
if ( !fogCollapse ) {
|
|
ProjectDlightTexture();
|
|
}
|
|
}
|
|
#endif // USE_LEGACY_DLIGHTS
|
|
|
|
// now do fog
|
|
if ( tess.fogNum && tess.shader->fogPass && !fogCollapse ) {
|
|
RB_FogPass();
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
** RB_StageIteratorGeneric
|
|
*/
|
|
void RB_StageIteratorGeneric( void )
|
|
{
|
|
const shaderCommands_t *input;
|
|
shader_t *shader;
|
|
|
|
RB_DeformTessGeometry();
|
|
|
|
input = &tess;
|
|
shader = input->shader;
|
|
|
|
//
|
|
// set face culling appropriately
|
|
//
|
|
GL_Cull( shader->cullType );
|
|
|
|
// set polygon offset if necessary
|
|
if ( shader->polygonOffset )
|
|
{
|
|
qglEnable( GL_POLYGON_OFFSET_FILL );
|
|
qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value );
|
|
}
|
|
|
|
//
|
|
// if there is only a single pass then we can enable color
|
|
// and texture arrays before we compile, otherwise we need
|
|
// to avoid compiling those arrays since they will change
|
|
// during multipass rendering
|
|
//
|
|
if ( tess.numPasses > 1 )
|
|
{
|
|
setArraysOnce = qfalse;
|
|
|
|
GL_ClientState( 1, CLS_NONE );
|
|
GL_ClientState( 0, CLS_NONE );
|
|
}
|
|
else
|
|
{
|
|
// FIXME: we can't do that if going to lighting/fog later?
|
|
setArraysOnce = qtrue;
|
|
|
|
GL_ClientState( 0, CLS_COLOR_ARRAY | CLS_TEXCOORD_ARRAY );
|
|
|
|
if ( tess.xstages[0] )
|
|
{
|
|
R_ComputeColors( 0, tess.svars.colors, tess.xstages[0] );
|
|
qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors[0].rgba );
|
|
R_ComputeTexCoords( 0, &tess.xstages[0]->bundle[0] );
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoordPtr[0] );
|
|
if ( shader->multitextureEnv )
|
|
{
|
|
GL_ClientState( 1, CLS_TEXCOORD_ARRAY );
|
|
R_ComputeTexCoords( 1, &tess.xstages[0]->bundle[1] );
|
|
qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoordPtr[1] );
|
|
}
|
|
else
|
|
{
|
|
GL_ClientState( 1, CLS_NONE );
|
|
}
|
|
}
|
|
}
|
|
|
|
qglVertexPointer( 3, GL_FLOAT, sizeof( input->xyz[0] ), input->xyz ); // padded for SIMD
|
|
|
|
//
|
|
// lock XYZ
|
|
//
|
|
if ( qglLockArraysEXT )
|
|
{
|
|
qglLockArraysEXT( 0, input->numVertexes );
|
|
}
|
|
|
|
//
|
|
// call shader function
|
|
//
|
|
RB_IterateStagesGeneric( input );
|
|
|
|
//
|
|
// now do any dynamic lighting needed
|
|
//
|
|
if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) )
|
|
{
|
|
ProjectDlightTexture();
|
|
}
|
|
|
|
//
|
|
// now do fog
|
|
//
|
|
if ( tess.fogNum && tess.shader->fogPass )
|
|
{
|
|
RB_FogPass();
|
|
}
|
|
|
|
//
|
|
// unlock arrays
|
|
//
|
|
if ( qglUnlockArraysEXT )
|
|
{
|
|
qglUnlockArraysEXT();
|
|
}
|
|
|
|
GL_ClientState( 1, CLS_NONE );
|
|
|
|
//
|
|
// reset polygon offset
|
|
//
|
|
if ( shader->polygonOffset )
|
|
{
|
|
qglDisable( GL_POLYGON_OFFSET_FILL );
|
|
}
|
|
}
|
|
#endif // !USE_VULKAN
|
|
|
|
|
|
/*
|
|
** RB_EndSurface
|
|
*/
|
|
void RB_EndSurface( void ) {
|
|
const shaderCommands_t *input;
|
|
|
|
input = &tess;
|
|
|
|
if ( input->numIndexes == 0 ) {
|
|
//VBO_UnBind();
|
|
return;
|
|
}
|
|
|
|
if ( input->numIndexes > SHADER_MAX_INDEXES ) {
|
|
ri.Error( ERR_DROP, "RB_EndSurface() - SHADER_MAX_INDEXES hit" );
|
|
}
|
|
|
|
if ( input->numVertexes > SHADER_MAX_VERTEXES ) {
|
|
ri.Error( ERR_DROP, "RB_EndSurface() - SHADER_MAX_VERTEXES hit" );
|
|
}
|
|
|
|
if ( tess.shader == tr.shadowShader ) {
|
|
RB_ShadowTessEnd();
|
|
return;
|
|
}
|
|
|
|
// for debugging of sort order issues, stop rendering after a given sort value
|
|
if ( r_debugSort->integer && r_debugSort->integer < tess.shader->sort && !backEnd.doneSurfaces ) {
|
|
#ifdef USE_VBO
|
|
tess.vboIndex = 0; //VBO_UnBind();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
//
|
|
// update performance counters
|
|
//
|
|
#ifdef USE_PMLIGHT
|
|
if ( tess.dlightPass ) {
|
|
backEnd.pc.c_lit_batches++;
|
|
backEnd.pc.c_lit_vertices += tess.numVertexes;
|
|
backEnd.pc.c_lit_indices += tess.numIndexes;
|
|
} else
|
|
#endif
|
|
{
|
|
backEnd.pc.c_shaders++;
|
|
backEnd.pc.c_vertexes += tess.numVertexes;
|
|
backEnd.pc.c_indexes += tess.numIndexes;
|
|
}
|
|
backEnd.pc.c_totalIndexes += tess.numIndexes * tess.numPasses;
|
|
|
|
//
|
|
// call off to shader specific tess end function
|
|
//
|
|
tess.shader->optimalStageIteratorFunc();
|
|
|
|
//
|
|
// draw debugging stuff
|
|
//
|
|
if ( r_showtris->integer ) {
|
|
DrawTris( input );
|
|
}
|
|
if ( r_shownormals->integer ) {
|
|
DrawNormals( input );
|
|
}
|
|
|
|
// clear shader so we can tell we don't have any unclosed surfaces
|
|
tess.numIndexes = 0;
|
|
tess.numVertexes = 0;
|
|
|
|
#ifdef USE_VBO
|
|
tess.vboIndex = 0;
|
|
//VBO_ClearQueue();
|
|
#endif
|
|
}
|