doom3quest/Projects/Android/jni/d3es-multithread-master/neo/renderer/draw_gles2.cpp
Simon b2b8f43c9d Initial Commit
Builds, runs, no stereo or much else is working, menus work ok though
2020-09-08 23:10:45 +01:00

2452 lines
No EOL
78 KiB
C++

/*
* This file is part of the D3wasm project (http://www.continuation-labs.com/projects/d3wasm)
* Copyright (c) 2019 Gabriel Cuvillier.
*
* 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, version 3.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sys/platform.h"
#include "renderer/VertexCache.h"
#include "tr_local.h"
#include "glsl/glsl_shaders.h"
shaderProgram_t interactionShader;
shaderProgram_t interactionPhongShader;
shaderProgram_t fogShader;
shaderProgram_t blendLightShader;
shaderProgram_t zfillShader;
shaderProgram_t zfillClipShader;
shaderProgram_t diffuseMapShader;
shaderProgram_t diffuseCubeShader;
shaderProgram_t skyboxCubeShader;
shaderProgram_t reflectionCubeShader;
shaderProgram_t stencilShadowShader;
#define ATTR_VERTEX 0 // Don't change this, as WebGL require the vertex attrib 0 to be always bound
#define ATTR_COLOR 1
#define ATTR_TEXCOORD 2
#define ATTR_NORMAL 3
#define ATTR_TANGENT 4
#define ATTR_BITANGENT 5
/*
====================
GL_UseProgram
====================
*/
void GL_UseProgram(shaderProgram_t* program) {
if ( backEnd.glState.currentProgram == program ) {
return;
}
qglUseProgram(program ? program->program : 0);
backEnd.glState.currentProgram = program;
}
/*
====================
GL_Uniform1fv
====================
*/
static void GL_Uniform1fv(GLint location, const GLfloat* value) {
qglUniform1fv(*( GLint * )((char*) backEnd.glState.currentProgram + location), 1, value);
}
/*
====================
GL_Uniform1iv
====================
*/
static void GL_Uniform1iv(GLint location, const GLint* value) {
qglUniform1iv(*( GLint * )((char*) backEnd.glState.currentProgram + location), 1, value);
}
/*
====================
GL_Uniform4fv
====================
*/
static void GL_Uniform4fv(GLint location, const GLfloat* value) {
qglUniform4fv(*( GLint * )((char*) backEnd.glState.currentProgram + location), 1, value);
}
/*
====================
GL_UniformMatrix4fv
====================
*/
static void GL_UniformMatrix4fv(GLint location, const GLfloat* value) {
qglUniformMatrix4fv(*( GLint * )((char*) backEnd.glState.currentProgram + location), 1, GL_FALSE, value);
}
/*
====================
GL_EnableVertexAttribArray
====================
*/
void GL_EnableVertexAttribArray(GLuint index) {
qglEnableVertexAttribArray(index);
}
/*
====================
GL_DisableVertexAttribArray
====================
*/
void GL_DisableVertexAttribArray(GLuint index) {
qglDisableVertexAttribArray(index);
}
/*
====================
GL_VertexAttribPointer
====================
*/
static void GL_VertexAttribPointer(GLuint index, GLint size, GLenum type,
GLboolean normalized, GLsizei stride,
const GLvoid* pointer) {
qglVertexAttribPointer(*( GLint * )((char*) backEnd.glState.currentProgram + index),
size, type, normalized, stride, pointer);
}
/*
=================
R_LoadGLSLShader
loads GLSL vertex or fragment shaders
=================
*/
static void R_LoadGLSLShader(const char* buffer, shaderProgram_t* shaderProgram, GLenum type) {
if ( !glConfig.isInitialized ) {
return;
}
switch ( type ) {
case GL_VERTEX_SHADER:
// create vertex shader
shaderProgram->vertexShader = qglCreateShader(GL_VERTEX_SHADER);
qglShaderSource(shaderProgram->vertexShader, 1, (const GLchar**) &buffer, 0);
qglCompileShader(shaderProgram->vertexShader);
break;
case GL_FRAGMENT_SHADER:
// create fragment shader
shaderProgram->fragmentShader = qglCreateShader(GL_FRAGMENT_SHADER);
qglShaderSource(shaderProgram->fragmentShader, 1, (const GLchar**) &buffer, 0);
qglCompileShader(shaderProgram->fragmentShader);
break;
default:
common->Printf("R_LoadGLSLShader: no type\n");
return;
}
}
/*
=================
R_LinkGLSLShader
links the GLSL vertex and fragment shaders together to form a GLSL program
=================
*/
static bool R_LinkGLSLShader(shaderProgram_t* shaderProgram, const char* name) {
char buf[BUFSIZ];
int len;
GLint linked;
shaderProgram->program = qglCreateProgram();
qglAttachShader(shaderProgram->program, shaderProgram->vertexShader);
qglAttachShader(shaderProgram->program, shaderProgram->fragmentShader);
// Bind attributes locations
qglBindAttribLocation(shaderProgram->program, ATTR_VERTEX, "attr_Vertex");
qglBindAttribLocation(shaderProgram->program, ATTR_COLOR, "attr_Color");
qglBindAttribLocation(shaderProgram->program, ATTR_TEXCOORD, "attr_TexCoord");
qglBindAttribLocation(shaderProgram->program, ATTR_NORMAL, "attr_Normal");
qglBindAttribLocation(shaderProgram->program, ATTR_TANGENT, "attr_Tangent");
qglBindAttribLocation(shaderProgram->program, ATTR_BITANGENT, "attr_Bitangent");
qglLinkProgram(shaderProgram->program);
qglGetProgramiv(shaderProgram->program, GL_LINK_STATUS, &linked);
if ( com_developer.GetBool()) {
qglGetShaderInfoLog(shaderProgram->vertexShader, sizeof(buf), &len, buf);
common->Printf("VS:\n%.*s\n", len, buf);
qglGetShaderInfoLog(shaderProgram->fragmentShader, sizeof(buf), &len, buf);
common->Printf("FS:\n%.*s\n", len, buf);
}
if ( !linked ) {
common->Error("R_LinkGLSLShader: program failed to link: %s\n", name);
return false;
}
return true;
}
/*
=================
R_ValidateGLSLProgram
makes sure GLSL program is valid
=================
*/
static bool R_ValidateGLSLProgram(shaderProgram_t* shaderProgram) {
GLint validProgram;
qglValidateProgram(shaderProgram->program);
qglGetProgramiv(shaderProgram->program, GL_VALIDATE_STATUS, &validProgram);
if ( !validProgram ) {
common->Printf("R_ValidateGLSLProgram: program invalid\n");
return false;
}
return true;
}
/*
=================
RB_GLSL_GetUniformLocations
=================
*/
static void RB_GLSL_GetUniformLocations(shaderProgram_t* shader) {
int i;
char buffer[32];
GL_UseProgram(shader);
shader->localLightOrigin = qglGetUniformLocation(shader->program, "u_lightOrigin");
shader->localViewOrigin = qglGetUniformLocation(shader->program, "u_viewOrigin");
shader->lightProjection = qglGetUniformLocation(shader->program, "u_lightProjection");
shader->bumpMatrixS = qglGetUniformLocation(shader->program, "u_bumpMatrixS");
shader->bumpMatrixT = qglGetUniformLocation(shader->program, "u_bumpMatrixT");
shader->diffuseMatrixS = qglGetUniformLocation(shader->program, "u_diffuseMatrixS");
shader->diffuseMatrixT = qglGetUniformLocation(shader->program, "u_diffuseMatrixT");
shader->specularMatrixS = qglGetUniformLocation(shader->program, "u_specularMatrixS");
shader->specularMatrixT = qglGetUniformLocation(shader->program, "u_specularMatrixT");
shader->colorModulate = qglGetUniformLocation(shader->program, "u_colorModulate");
shader->colorAdd = qglGetUniformLocation(shader->program, "u_colorAdd");
shader->fogColor = qglGetUniformLocation(shader->program, "u_fogColor");
shader->diffuseColor = qglGetUniformLocation(shader->program, "u_diffuseColor");
shader->specularColor = qglGetUniformLocation(shader->program, "u_specularColor");
shader->glColor = qglGetUniformLocation(shader->program, "u_glColor");
shader->alphaTest = qglGetUniformLocation(shader->program, "u_alphaTest");
shader->specularExponent = qglGetUniformLocation(shader->program, "u_specularExponent");
shader->modelViewProjectionMatrix = qglGetUniformLocation(shader->program, "u_modelViewProjectionMatrix");
shader->modelViewMatrix = qglGetUniformLocation(shader->program, "u_modelViewMatrix");
shader->textureMatrix = qglGetUniformLocation(shader->program, "u_textureMatrix");
shader->clipPlane = qglGetUniformLocation(shader->program, "u_clipPlane");
shader->fogMatrix = qglGetUniformLocation(shader->program, "u_fogMatrix");
shader->attr_TexCoord = qglGetAttribLocation(shader->program, "attr_TexCoord");
shader->attr_Tangent = qglGetAttribLocation(shader->program, "attr_Tangent");
shader->attr_Bitangent = qglGetAttribLocation(shader->program, "attr_Bitangent");
shader->attr_Normal = qglGetAttribLocation(shader->program, "attr_Normal");
shader->attr_Vertex = qglGetAttribLocation(shader->program, "attr_Vertex");
shader->attr_Color = qglGetAttribLocation(shader->program, "attr_Color");
// Init default values
for ( i = 0; i < MAX_FRAGMENT_IMAGES; i++ ) {
idStr::snPrintf(buffer, sizeof(buffer), "u_fragmentMap%d", i);
shader->u_fragmentMap[i] = qglGetUniformLocation(shader->program, buffer);
qglUniform1i(shader->u_fragmentMap[i], i);
}
for ( i = 0; i < MAX_FRAGMENT_IMAGES; i++ ) {
idStr::snPrintf(buffer, sizeof(buffer), "u_fragmentCubeMap%d", i);
shader->u_fragmentCubeMap[i] = qglGetUniformLocation(shader->program, buffer);
qglUniform1i(shader->u_fragmentCubeMap[i], i);
}
if (shader->textureMatrix >= 0) {
// Load identity matrix for Texture marix
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), mat4_identity.ToFloatPtr());
}
static const GLfloat one[1] = { 1.0f };
if (shader->alphaTest >= 0) {
// Alpha test always pass by default
GL_Uniform1fv(offsetof(shaderProgram_t, alphaTest), one);
}
if (shader->colorModulate >= 0) {
static const GLfloat zero[1] = { 0.0f };
GL_Uniform1fv(offsetof(shaderProgram_t, colorModulate), zero);
}
if (shader->colorAdd >= 0) {
GL_Uniform1fv(offsetof(shaderProgram_t, colorAdd), one);
}
GL_CheckErrors();
GL_UseProgram(NULL);
}
/*
=================
RB_GLSL_InitShaders
=================
*/
static bool RB_GLSL_InitShaders(void) {
// main Interaction shader
common->Printf("Loading main interaction shader\n");
memset(&interactionShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(interactionShaderVP, &interactionShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(interactionShaderFP, &interactionShader, GL_FRAGMENT_SHADER);
if ( !R_LinkGLSLShader(&interactionShader, "interaction") && !R_ValidateGLSLProgram(&interactionShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&interactionShader);
}
// main Interaction shader, Phong version
common->Printf("Loading main interaction shader (Phong) \n");
memset(&interactionPhongShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(interactionPhongShaderVP, &interactionPhongShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(interactionPhongShaderFP, &interactionPhongShader, GL_FRAGMENT_SHADER);
if ( !R_LinkGLSLShader(&interactionPhongShader, "interactionPhong") &&
!R_ValidateGLSLProgram(&interactionPhongShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&interactionPhongShader);
}
// default diffuse shader
common->Printf("Loading default diffuse shader\n");
memset(&diffuseMapShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(diffuseMapShaderVP, &diffuseMapShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(diffuseMapShaderFP, &diffuseMapShader, GL_FRAGMENT_SHADER);
if ( !R_LinkGLSLShader(&diffuseMapShader, "diffuseMap") && !R_ValidateGLSLProgram(&diffuseMapShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&diffuseMapShader);
}
// Skybox cubemap shader
common->Printf("Loading skybox cubemap shader\n");
memset(&skyboxCubeShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(skyboxCubeShaderVP, &skyboxCubeShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(cubeMapShaderFP, &skyboxCubeShader, GL_FRAGMENT_SHADER); // Use the common "cubeMapShaderFP"
if ( !R_LinkGLSLShader(&skyboxCubeShader, "skyboxCube") && !R_ValidateGLSLProgram(&skyboxCubeShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&skyboxCubeShader);
}
// Reflection cubemap shader
common->Printf("Loading reflection cubemap shader\n");
memset(&reflectionCubeShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(reflectionCubeShaderVP, &reflectionCubeShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(cubeMapShaderFP, &reflectionCubeShader, GL_FRAGMENT_SHADER); // Use the common "cubeMapShaderFP"
if ( !R_LinkGLSLShader(&reflectionCubeShader, "reflectionCube") &&
!R_ValidateGLSLProgram(&reflectionCubeShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&reflectionCubeShader);
}
// Diffuse cubemap shader
common->Printf("Loading diffuse cubemap shader\n");
memset(&diffuseCubeShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(diffuseCubeShaderVP, &diffuseCubeShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(cubeMapShaderFP, &diffuseCubeShader, GL_FRAGMENT_SHADER); // Use the common "cubeMapShaderFP"
if ( !R_LinkGLSLShader(&diffuseCubeShader, "diffuseCube") && !R_ValidateGLSLProgram(&diffuseCubeShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&diffuseCubeShader);
}
// Z Fill shader
common->Printf("Loading Zfill shader\n");
memset(&zfillShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(zfillShaderVP, &zfillShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(zfillShaderFP, &zfillShader, GL_FRAGMENT_SHADER);
if ( !R_LinkGLSLShader(&zfillShader, "zfill") && !R_ValidateGLSLProgram(&zfillShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&zfillShader);
}
// Z Fill shader, Clip planes version
common->Printf("Loading Zfill shader (Clip plane version)\n");
memset(&zfillClipShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(zfillClipShaderVP, &zfillClipShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(zfillClipShaderFP, &zfillClipShader, GL_FRAGMENT_SHADER);
if ( !R_LinkGLSLShader(&zfillClipShader, "zfillClip") && !R_ValidateGLSLProgram(&zfillClipShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&zfillClipShader);
}
// Fog shader
common->Printf("Loading Fog shader\n");
memset(&fogShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(fogShaderVP, &fogShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(fogShaderFP, &fogShader, GL_FRAGMENT_SHADER);
if ( !R_LinkGLSLShader(&fogShader, "fog") && !R_ValidateGLSLProgram(&fogShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&fogShader);
}
// BlendLight shader
common->Printf("Loading BlendLight shader\n");
memset(&blendLightShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(blendLightShaderVP, &blendLightShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(fogShaderFP, &blendLightShader, GL_FRAGMENT_SHADER); // Reuse the common "FogShaderFP"
if ( !R_LinkGLSLShader(&blendLightShader, "blendLight") && !R_ValidateGLSLProgram(&blendLightShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&blendLightShader);
}
// Stencil shadow shader
common->Printf("Loading Stencil shadow shader\n");
memset(&stencilShadowShader, 0, sizeof(shaderProgram_t));
R_LoadGLSLShader(stencilShadowShaderVP, &stencilShadowShader, GL_VERTEX_SHADER);
R_LoadGLSLShader(stencilShadowShaderFP, &stencilShadowShader, GL_FRAGMENT_SHADER);
if ( !R_LinkGLSLShader(&stencilShadowShader, "stencilShadow") && !R_ValidateGLSLProgram(&stencilShadowShader)) {
return false;
} else {
RB_GLSL_GetUniformLocations(&stencilShadowShader);
}
return true;
}
/*
==================
R_ReloadGLSLPrograms_f
==================
*/
void R_ReloadGLSLPrograms_f(const idCmdArgs& args) {
common->Printf("----- R_ReloadGLSLPrograms -----\n");
if ( !RB_GLSL_InitShaders()) {
common->Printf("GLSL shaders failed to init.\n");
}
common->Printf("-------------------------------\n");
}
/*
=================
RB_ComputeMVP
Compute the model view matrix, with eventually required projection matrix depth hacks
=================
*/
void RB_ComputeMVP( const drawSurf_t * const surf, float mvp[16] ) {
// Get the projection matrix
float localProjectionMatrix[16];
memcpy(localProjectionMatrix, backEnd.viewDef->projectionMatrix, sizeof(localProjectionMatrix));
// Quick and dirty hacks on the projection matrix
if ( surf->space->weaponDepthHack ) {
localProjectionMatrix[14] = backEnd.viewDef->projectionMatrix[14] * 0.25;
}
if ( surf->space->modelDepthHack != 0.0 ) {
localProjectionMatrix[14] = backEnd.viewDef->projectionMatrix[14] - surf->space->modelDepthHack;
}
/* if (backEnd.viewDef->viewEntitys != NULL) {
// transform by the camera placement
idVec3 origin;
origin.Zero();
//
if (stereoSide == 0) // left eye
{
origin -= backEnd.viewDef->renderView.viewaxis[0] * 0.065f * 25.0f;
} else // right eye
{
origin += backEnd.viewDef->renderView.viewaxis[0] * 0.065f * 25.0f;
}
float viewerMatrix[16];
memset(viewerMatrix, 0, sizeof(float) * 16);
viewerMatrix[0] = 1;
viewerMatrix[5] = 1;
viewerMatrix[10] = 1;
viewerMatrix[12] = origin[0];
viewerMatrix[13] = origin[1];
viewerMatrix[14] = origin[2];
viewerMatrix[15] = 1;
float temp[16];
myGlMultMatrix(viewerMatrix, surf->space->modelViewMatrix, temp);
// precompute the MVP
myGlMultMatrix(temp, localProjectionMatrix, mvp);
}
else
*/ {
myGlMultMatrix(surf->space->modelViewMatrix, localProjectionMatrix, mvp);
}
}
/*
==================
RB_GLSL_DrawInteraction
==================
*/
static void RB_GLSL_DrawInteraction(const drawInteraction_t* din) {
static const GLfloat zero[1] = { 0 };
static const GLfloat one[1] = { 1 };
static const GLfloat oneScaled[1] = { 1 / 255.0f };
static const GLfloat negOneScaled[1] = { -1.0f / 255.0f };
// load all the vertex program parameters
GL_Uniform4fv(offsetof(shaderProgram_t, localLightOrigin), din->localLightOrigin.ToFloatPtr());
GL_Uniform4fv(offsetof(shaderProgram_t, localViewOrigin), din->localViewOrigin.ToFloatPtr());
GL_UniformMatrix4fv(offsetof(shaderProgram_t, lightProjection), din->lightProjection.ToFloatPtr());
GL_Uniform4fv(offsetof(shaderProgram_t, bumpMatrixS), din->bumpMatrix[0].ToFloatPtr());
GL_Uniform4fv(offsetof(shaderProgram_t, bumpMatrixT), din->bumpMatrix[1].ToFloatPtr());
GL_Uniform4fv(offsetof(shaderProgram_t, diffuseMatrixS), din->diffuseMatrix[0].ToFloatPtr());
GL_Uniform4fv(offsetof(shaderProgram_t, diffuseMatrixT), din->diffuseMatrix[1].ToFloatPtr());
GL_Uniform4fv(offsetof(shaderProgram_t, specularMatrixS), din->specularMatrix[0].ToFloatPtr());
GL_Uniform4fv(offsetof(shaderProgram_t, specularMatrixT), din->specularMatrix[1].ToFloatPtr());
switch ( din->vertexColor ) {
case SVC_MODULATE: {
GL_Uniform1fv(offsetof(shaderProgram_t, colorModulate), oneScaled);
GL_Uniform1fv(offsetof(shaderProgram_t, colorAdd), zero);
break;
}
case SVC_INVERSE_MODULATE: {
GL_Uniform1fv(offsetof(shaderProgram_t, colorModulate), negOneScaled);
GL_Uniform1fv(offsetof(shaderProgram_t, colorAdd), one);
break;
}
default:
case SVC_IGNORE:
// This is already the default values (zero, one)
break;
}
// set the constant colors
GL_Uniform4fv(offsetof(shaderProgram_t, diffuseColor), din->diffuseColor.ToFloatPtr());
GL_Uniform4fv(offsetof(shaderProgram_t, specularColor), din->specularColor.ToFloatPtr());
// set the textures
// texture 0 will be the per-surface bump map
// NB: Texture 0 is expected to be active at this point
din->bumpImage->Bind();
// texture 1 will be the light falloff texture
GL_SelectTexture(1);
din->lightFalloffImage->Bind();
// texture 2 will be the light projection texture
GL_SelectTexture(2);
din->lightImage->Bind();
// texture 3 is the per-surface diffuse map
GL_SelectTexture(3);
din->diffuseImage->Bind();
// texture 4 is the per-surface specular map
GL_SelectTexture(4);
din->specularImage->Bind();
// Be sure to activate Texture 0 for next interaction, or next pass
GL_SelectTexture(0);
// draw it
RB_DrawElementsWithCounters(din->surf);
// Restore color modulation state to default values
if ( din->vertexColor != SVC_IGNORE ) {
GL_Uniform1fv(offsetof(shaderProgram_t, colorModulate), zero);
GL_Uniform1fv(offsetof(shaderProgram_t, colorAdd), one);
}
}
/*
=============
RB_CreateSingleDrawInteractions
This can be used by different draw_* backends to decompose a complex light / surface
interaction into primitive interactions
=============
*/
static void
RB_GLSL_CreateSingleDrawInteractions(const drawSurf_t* surf, void (* DrawInteraction)(const drawInteraction_t*), const viewLight_t* vLight) {
const idMaterial* surfaceShader = surf->material;
const float* surfaceRegs = surf->shaderRegisters;
const idMaterial* lightShader = vLight->lightShader;
const float* lightRegs = vLight->shaderRegisters;
drawInteraction_t inter;
if ( r_skipInteractions.GetBool() || !surf->geoFrontEnd || !surf->ambientCache ) {
return;
}
// change the scissor if needed
if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals(surf->scissorRect)) {
backEnd.currentScissor = surf->scissorRect;
if (( backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1 ) < 0.0f ||
( backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 ) < 0.0f ) {
backEnd.currentScissor = backEnd.viewDef->scissor;
}
qglScissor(backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1);
}
inter.surf = surf;
inter.lightFalloffImage = vLight->falloffImage;
R_GlobalPointToLocal(surf->space->modelMatrix, vLight->globalLightOrigin, inter.localLightOrigin.ToVec3());
R_GlobalPointToLocal(surf->space->modelMatrix, backEnd.viewDef->renderView.vieworg, inter.localViewOrigin.ToVec3());
inter.localLightOrigin[3] = 0;
inter.localViewOrigin[3] = 1;
inter.ambientLight = lightShader->IsAmbientLight();
// the base projections may be modified by texture matrix on light stages
idPlane lightProject[4];
for ( int i = 0; i < 4; i++ ) {
R_GlobalPlaneToLocal(surf->space->modelMatrix, vLight->lightProject[i], lightProject[i]);
}
for ( int lightStageNum = 0; lightStageNum < lightShader->GetNumStages(); lightStageNum++ ) {
const shaderStage_t* lightStage = lightShader->GetStage(lightStageNum);
// ignore stages that fail the condition
if ( !lightRegs[lightStage->conditionRegister] ) {
continue;
}
inter.lightImage = lightStage->texture.image;
inter.lightProjection[0] = lightProject[0].ToVec4(); // S
inter.lightProjection[1] = lightProject[1].ToVec4(); // T
inter.lightProjection[2] = lightProject[3].ToVec4(); // CAUTION! this is the 4th vector. R = Falloff
inter.lightProjection[3] = lightProject[2].ToVec4(); // CAUTION! this is the 3rd vector. Q
// now multiply the texgen by the light texture matrix
if ( lightStage->texture.hasMatrix ) {
float lightTextureMatrix[16];
RB_GetShaderTextureMatrix(lightRegs, &lightStage->texture, lightTextureMatrix);
RB_BakeTextureMatrixIntoTexgen(inter.lightProjection, lightTextureMatrix);
}
inter.bumpImage = NULL;
inter.specularImage = NULL;
inter.diffuseImage = NULL;
inter.diffuseColor[0] = inter.diffuseColor[1] = inter.diffuseColor[2] = inter.diffuseColor[3] = 0;
inter.specularColor[0] = inter.specularColor[1] = inter.specularColor[2] = inter.specularColor[3] = 0;
float lightColor[4];
const float lightscale = r_lightScale.GetFloat();
lightColor[0] = lightscale * lightRegs[lightStage->color.registers[0]];
lightColor[1] = lightscale * lightRegs[lightStage->color.registers[1]];
lightColor[2] = lightscale * lightRegs[lightStage->color.registers[2]];
lightColor[3] = lightRegs[lightStage->color.registers[3]];
// go through the individual stages
for ( int surfaceStageNum = 0; surfaceStageNum < surfaceShader->GetNumStages(); surfaceStageNum++ ) {
const shaderStage_t* surfaceStage = surfaceShader->GetStage(surfaceStageNum);
switch ( surfaceStage->lighting ) {
case SL_AMBIENT: {
// ignore ambient stages while drawing interactions
break;
}
case SL_BUMP: {
// ignore stage that fails the condition
if ( !surfaceRegs[surfaceStage->conditionRegister] ) {
break;
}
// draw any previous interaction
RB_SubmittInteraction(&inter, DrawInteraction);
inter.diffuseImage = NULL;
inter.specularImage = NULL;
RB_SetDrawInteraction(surfaceStage, surfaceRegs, &inter.bumpImage, inter.bumpMatrix, NULL);
break;
}
case SL_DIFFUSE: {
// ignore stage that fails the condition
if ( !surfaceRegs[surfaceStage->conditionRegister] ) {
break;
}
if ( inter.diffuseImage ) {
RB_SubmittInteraction(&inter, DrawInteraction);
}
RB_SetDrawInteraction(surfaceStage, surfaceRegs, &inter.diffuseImage,
inter.diffuseMatrix, inter.diffuseColor.ToFloatPtr());
inter.diffuseColor[0] *= lightColor[0];
inter.diffuseColor[1] *= lightColor[1];
inter.diffuseColor[2] *= lightColor[2];
inter.diffuseColor[3] *= lightColor[3];
inter.vertexColor = surfaceStage->vertexColor;
break;
}
case SL_SPECULAR: {
// ignore stage that fails the condition
if ( !surfaceRegs[surfaceStage->conditionRegister] ) {
break;
}
if ( inter.specularImage ) {
RB_SubmittInteraction(&inter, DrawInteraction);
}
RB_SetDrawInteraction(surfaceStage, surfaceRegs, &inter.specularImage,
inter.specularMatrix, inter.specularColor.ToFloatPtr());
inter.specularColor[0] *= lightColor[0];
inter.specularColor[1] *= lightColor[1];
inter.specularColor[2] *= lightColor[2];
inter.specularColor[3] *= lightColor[3];
inter.vertexColor = surfaceStage->vertexColor;
break;
}
}
}
// draw the final interaction
RB_SubmittInteraction(&inter, DrawInteraction);
}
}
/*
=============
RB_GLSL_CreateDrawInteractions
=============
*/
static void RB_GLSL_CreateDrawInteractions(const drawSurf_t* surf, const viewLight_t* vLight, const int depthFunc = GLS_DEPTHFUNC_EQUAL) {
if ( !surf ) {
return;
}
// perform setup here that will be constant for all interactions
GL_State(GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | depthFunc);
// bind the vertex and fragment shader
if ( r_usePhong.GetBool()) {
GL_UseProgram(&interactionPhongShader);
// Set the specular exponent now (NB: it could be cached instead)
const float f = r_specularExponent.GetFloat();
GL_Uniform1fv(offsetof(shaderProgram_t, specularExponent), &f);
} else {
GL_UseProgram(&interactionShader);
}
// Setup attributes arrays
// Vertex attribute is always enabled
// Color attribute is always enabled
// TexCoord attribute is always enabled
// Enable the rest
GL_EnableVertexAttribArray(ATTR_TANGENT);
GL_EnableVertexAttribArray(ATTR_BITANGENT);
GL_EnableVertexAttribArray(ATTR_NORMAL);
backEnd.currentSpace = NULL;
for ( ; surf; surf = surf->nextOnLight ) {
// perform setup here that will not change over multiple interaction passes
if ( surf->space != backEnd.currentSpace ) {
float mvp[16];
RB_ComputeMVP(surf, mvp);
GL_UniformMatrix4fv(offsetof(shaderProgram_t, modelViewProjectionMatrix), mvp);
}
// Hack Depth Range if necessary
bool bNeedRestoreDepthRange = false;
if (surf->space->weaponDepthHack && surf->space->modelDepthHack == 0.0f) {
qglDepthRangef(0.0f, 0.5f);
bNeedRestoreDepthRange = true;
}
// set the vertex pointers
idDrawVert* ac = (idDrawVert*) vertexCache.Position(surf->ambientCache);
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Normal), 3, GL_FLOAT, false, sizeof(idDrawVert),
ac->normal.ToFloatPtr());
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Bitangent), 3, GL_FLOAT, false, sizeof(idDrawVert),
ac->tangents[1].ToFloatPtr());
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Tangent), 3, GL_FLOAT, false, sizeof(idDrawVert),
ac->tangents[0].ToFloatPtr());
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_TexCoord), 2, GL_FLOAT, false, sizeof(idDrawVert),
ac->st.ToFloatPtr());
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Vertex), 3, GL_FLOAT, false, sizeof(idDrawVert),
ac->xyz.ToFloatPtr());
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Color), 4, GL_UNSIGNED_BYTE, false, sizeof(idDrawVert),
(void*) &ac->color);
// this may cause RB_GLSL_DrawInteraction to be exacuted multiple
// times with different colors and images if the surface or light have multiple layers
RB_GLSL_CreateSingleDrawInteractions(surf, RB_GLSL_DrawInteraction, vLight);
// Restore the Depth Range in case it have been hacked
if ( bNeedRestoreDepthRange ) {
qglDepthRangef( 0.0f, 1.0f );
}
backEnd.currentSpace = surf->space;
}
backEnd.currentSpace = NULL;
// Restore attributes arrays
// Vertex attribute is always enabled
// Color attribute is always enabled
// TexCoord attribute is always enabled
GL_DisableVertexAttribArray(ATTR_TANGENT);
GL_DisableVertexAttribArray(ATTR_BITANGENT);
GL_DisableVertexAttribArray(ATTR_NORMAL);
}
/*
=====================
RB_T_GLSL_Shadow
the shadow volumes face INSIDE
=====================
*/
static void RB_T_GLSL_Shadow(const drawSurf_t* surf, const viewLight_t* vLight) {
//const srfTriangles_t* tri = surf->geo;
if ( !surf->shadowCache ) {
return;
}
// set the light position for the vertex program to project the rear surfaces
if ( surf->space != backEnd.currentSpace ) {
idVec4 localLight;
R_GlobalPointToLocal(surf->space->modelMatrix, vLight->globalLightOrigin, localLight.ToVec3());
localLight.w = 0.0f;
GL_Uniform4fv(offsetof(shaderProgram_t, localLightOrigin), localLight.ToFloatPtr());
}
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Vertex), 4, GL_FLOAT, false, sizeof(shadowCache_t),
vertexCache.Position(surf->shadowCache));
// we always draw the sil planes, but we may not need to draw the front or rear caps
int numIndexes;
bool external = false;
if ( !r_useExternalShadows.GetInteger()) {
numIndexes = surf->numIndexes;
} else if ( r_useExternalShadows.GetInteger() == 2 ) { // force to no caps for testing
numIndexes = surf->numShadowIndexesNoCaps;
} else if ( !( surf->dsFlags & DSF_VIEW_INSIDE_SHADOW )) {
// if we aren't inside the shadow projection, no caps are ever needed needed
numIndexes = surf->numShadowIndexesNoCaps;
external = true;
} else if ( !vLight->viewInsideLight && !( surf->shadowCapPlaneBits & SHADOW_CAP_INFINITE )) {
// if we are inside the shadow projection, but outside the light, and drawing
// a non-infinite shadow, we can skip some caps
if ( vLight->viewSeesShadowPlaneBits & surf->shadowCapPlaneBits ) {
// we can see through a rear cap, so we need to draw it, but we can skip the
// caps on the actual surface
numIndexes = surf->numShadowIndexesNoFrontCaps;
} else {
// we don't need to draw any caps
numIndexes = surf->numShadowIndexesNoCaps;
}
external = true;
} else {
// must draw everything
numIndexes = surf->numIndexes;
}
// depth-fail stencil shadows
if ( !external ) {
qglStencilOpSeparate(backEnd.viewDef->isMirror ? GL_FRONT : GL_BACK, GL_KEEP, GL_DECR, GL_KEEP);
qglStencilOpSeparate(backEnd.viewDef->isMirror ? GL_BACK : GL_FRONT, GL_KEEP, GL_INCR, GL_KEEP);
} else {
// traditional depth-pass stencil shadows
qglStencilOpSeparate(backEnd.viewDef->isMirror ? GL_FRONT : GL_BACK, GL_KEEP, GL_KEEP, GL_INCR);
qglStencilOpSeparate(backEnd.viewDef->isMirror ? GL_BACK : GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR);
}
RB_DrawShadowElementsWithCounters(surf, numIndexes);
// patent-free work around
/*
if (!external) {
// "preload" the stencil buffer with the number of volumes
// that get clipped by the near or far clip plane
qglStencilOp(GL_KEEP, GL_DECR, GL_DECR);
GL_Cull(CT_FRONT_SIDED);
RB_DrawShadowElementsWithCounters(tri, numIndexes);
qglStencilOp(GL_KEEP, GL_INCR, GL_INCR);
GL_Cull(CT_BACK_SIDED);
RB_DrawShadowElementsWithCounters(tri, numIndexes);
}
// traditional depth-pass stencil shadows
qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
GL_Cull(CT_FRONT_SIDED);
RB_DrawShadowElementsWithCounters(tri, numIndexes);
qglStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
GL_Cull(CT_BACK_SIDED);
RB_DrawShadowElementsWithCounters(tri, numIndexes);
*/
}
/*
======================
RB_RenderDrawSurfChainWithFunction
======================
*/
void RB_GLSL_RenderDrawSurfChainWithFunction(const drawSurf_t* drawSurfs,
void (* triFunc_)(const drawSurf_t*, const viewLight_t*), const viewLight_t* vLight) {
const drawSurf_t* drawSurf;
backEnd.currentSpace = NULL;
for ( drawSurf = drawSurfs; drawSurf; drawSurf = drawSurf->nextOnLight ) {
// Change the MVP matrix if needed
if ( drawSurf->space != backEnd.currentSpace ) {
float mvp[16];
RB_ComputeMVP(drawSurf, mvp);
// We can set the uniform now, as the shader is already bound
GL_UniformMatrix4fv(offsetof(shaderProgram_t, modelViewProjectionMatrix), mvp);
}
// Hack Depth Range if necessary
bool bNeedRestoreDepthRange = false;
if (drawSurf->space->weaponDepthHack && drawSurf->space->modelDepthHack == 0.0f) {
qglDepthRangef(0.0f, 0.5f);
bNeedRestoreDepthRange = true;
}
// change the scissor if needed
if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals(drawSurf->scissorRect)) {
backEnd.currentScissor = drawSurf->scissorRect;
if (( backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1 ) < 0.0f ||
( backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 ) < 0.0f ) {
backEnd.currentScissor = backEnd.viewDef->scissor;
}
qglScissor(backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1);
}
// render it
triFunc_(drawSurf, vLight);
// Restore the Depth Range in case it have been hacked
if ( bNeedRestoreDepthRange ) {
qglDepthRangef( 0.0f, 1.0f );
}
backEnd.currentSpace = drawSurf->space;
}
backEnd.currentSpace = NULL;
}
/*
=====================
RB_GLSL_StencilShadowPass
Stencil test should already be enabled, and the stencil buffer should have
been set to 128 on any surfaces that might receive shadows
=====================
*/
void RB_GLSL_StencilShadowPass(const drawSurf_t* drawSurfs, const viewLight_t* vLight) {
//////////////
// Skip cases
//////////////
if ( !r_shadows.GetBool()) {
return;
}
if ( !drawSurfs ) {
return;
}
//////////////////
// Setup GL state
//////////////////
// Expected client GL state:
// Vertex attribute enabled
// Color attribute enabled
// Use the stencil shadow shader
GL_UseProgram(&stencilShadowShader);
// Setup attributes arrays
// Vertex attribute is always enabled
// Disable Color attribute (as it is enabled by default)
// Disable TexCoord attribute (as it is enabled by default)
GL_DisableVertexAttribArray(ATTR_COLOR);
GL_DisableVertexAttribArray(ATTR_TEXCOORD);
// don't write to the color buffer, just the stencil buffer
GL_State(GLS_DEPTHMASK | GLS_COLORMASK | GLS_ALPHAMASK | GLS_DEPTHFUNC_LESS);
if ( r_shadowPolygonFactor.GetFloat() || r_shadowPolygonOffset.GetFloat()) {
qglPolygonOffset(r_shadowPolygonFactor.GetFloat(), -r_shadowPolygonOffset.GetFloat());
qglEnable(GL_POLYGON_OFFSET_FILL);
}
qglStencilFunc(GL_ALWAYS, 1, 255);
// Culling will be done two side for shadows
GL_Cull(CT_TWO_SIDED);
RB_GLSL_RenderDrawSurfChainWithFunction(drawSurfs, RB_T_GLSL_Shadow, vLight);
// Restore culling
GL_Cull(CT_FRONT_SIDED);
if ( r_shadowPolygonFactor.GetFloat() || r_shadowPolygonOffset.GetFloat()) {
qglDisable(GL_POLYGON_OFFSET_FILL);
}
qglStencilFunc(GL_GEQUAL, 128, 255);
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// Restore attributes arrays
// Vertex attribute is always enabled
// Re-enable Color attribute (as it is enabled by default)
// Re-enable TexCoord attribute (as it is enabled by default)
GL_EnableVertexAttribArray(ATTR_COLOR);
GL_EnableVertexAttribArray(ATTR_TEXCOORD);
}
/*
==================
RB_GLSL_DrawInteractions
==================
*/
void RB_GLSL_DrawInteractions(void) {
///////////////////////
// For each light loop
///////////////////////
const viewLight_t* vLight;
//
// for each light, perform adding and shadowing
//
for ( vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next ) {
//////////////
// Skip Cases
//////////////
// do fogging later
if ( vLight->lightShader->IsFogLight()) {
continue;
}
if ( vLight->lightShader->IsBlendLight()) {
continue;
}
if ( !vLight->localInteractions && !vLight->globalInteractions
&& !vLight->translucentInteractions ) {
continue;
}
//////////////////
// Setup GL state
//////////////////
// clear the stencil buffer if needed
if ( vLight->globalShadows || vLight->localShadows ) {
if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals(vLight->scissorRect)) {
backEnd.currentScissor = vLight->scissorRect;
if (( backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1 ) < 0.0f ||
( backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 ) < 0.0f ) {
backEnd.currentScissor = backEnd.viewDef->scissor;
}
qglScissor(backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1);
}
qglClear(GL_STENCIL_BUFFER_BIT);
} else {
// no shadows, so no need to read or write the stencil buffer
// we might in theory want to use GL_ALWAYS instead of disabling
// completely, to satisfy the invarience rules
qglStencilFunc(GL_ALWAYS, 128, 255);
}
RB_GLSL_StencilShadowPass(vLight->globalShadows, vLight);
RB_GLSL_CreateDrawInteractions(vLight->localInteractions, vLight);
RB_GLSL_StencilShadowPass(vLight->localShadows, vLight);
RB_GLSL_CreateDrawInteractions(vLight->globalInteractions, vLight);
// translucent surfaces never get stencil shadowed
if ( r_skipTranslucent.GetBool()) {
continue;
}
qglStencilFunc(GL_ALWAYS, 128, 255);
RB_GLSL_CreateDrawInteractions(vLight->translucentInteractions, vLight, GLS_DEPTHFUNC_LESS);
}
// disable stencil shadow test
qglStencilFunc(GL_ALWAYS, 128, 255);
}
// NB: oh, a nice global variable. Argh....
static idPlane fogPlanes[4];
/*
=====================
RB_T_BasicFog
=====================
*/
static void RB_T_GLSL_BasicFog(const drawSurf_t* surf, const viewLight_t* vLight) {
if ( backEnd.currentSpace != surf->space ) {
idPlane transfoFogPlane[4];
//S
R_GlobalPlaneToLocal(surf->space->modelMatrix, fogPlanes[0], transfoFogPlane[0]);
transfoFogPlane[0][3] += 0.5;
//T
transfoFogPlane[1][0] = transfoFogPlane[1][1] = transfoFogPlane[1][2] = 0;
transfoFogPlane[1][3] = 0.5;
//T
R_GlobalPlaneToLocal(surf->space->modelMatrix, fogPlanes[2], transfoFogPlane[2]);
transfoFogPlane[2][3] += FOG_ENTER;
//S
R_GlobalPlaneToLocal(surf->space->modelMatrix, fogPlanes[3], transfoFogPlane[3]);
idMat4 fogMatrix;
fogMatrix[0] = transfoFogPlane[0].ToVec4();
fogMatrix[1] = transfoFogPlane[1].ToVec4();
fogMatrix[2] = transfoFogPlane[2].ToVec4();
fogMatrix[3] = transfoFogPlane[3].ToVec4();
GL_UniformMatrix4fv(offsetof(shaderProgram_t, fogMatrix), fogMatrix.ToFloatPtr());
}
idDrawVert* ac = (idDrawVert*) vertexCache.Position(surf->ambientCache);
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Vertex), 3, GL_FLOAT, false, sizeof(idDrawVert),
ac->xyz.ToFloatPtr());
RB_DrawElementsWithCounters(surf);
}
/*
==================
RB_FogPass
==================
*/
void RB_GLSL_FogPass(const drawSurf_t* drawSurfs, const drawSurf_t* drawSurfs2, const viewLight_t* vLight) {
drawSurf_t ds;
const idMaterial* lightShader;
const shaderStage_t* stage;
const float* regs;
// create a surface for the light frustom triangles, which are oriented drawn side out
const srfTriangles_t* frustumTris = vLight->frustumTris;
// if we ran out of vertex cache memory, skip it
if ( !frustumTris->ambientCache ) {
return;
}
// Initial expected GL state:
// Texture 0 is active, and bound to NULL
// Vertex attribute array is enabled
// All other attributes array are disabled
// No shaders active
GL_UseProgram(&fogShader);
// Setup attributes arrays
// Vertex attribute is always enabled
// Disable Color attribute (as it is enabled by default)
// Disable TexCoord attribute (as it is enabled by default)
GL_DisableVertexAttribArray(ATTR_COLOR);
GL_DisableVertexAttribArray(ATTR_TEXCOORD);
memset(&ds, 0, sizeof(ds));
ds.space = &backEnd.viewDef->worldSpace;
//ds.geo = frustumTris;
ds.ambientCache = frustumTris->ambientCache;
ds.indexCache = frustumTris->indexCache;
ds.shadowCache = frustumTris->shadowCache;
ds.numIndexes = frustumTris->numIndexes;
ds.scissorRect = backEnd.viewDef->scissor;
// find the current color and density of the fog
lightShader = vLight->lightShader;
regs = vLight->shaderRegisters;
// assume fog shaders have only a single stage
stage = lightShader->GetStage(0);
float lightColor[4];
lightColor[0] = regs[stage->color.registers[0]];
lightColor[1] = regs[stage->color.registers[1]];
lightColor[2] = regs[stage->color.registers[2]];
lightColor[3] = regs[stage->color.registers[3]];
// FogColor
GL_Uniform4fv(offsetof(shaderProgram_t, fogColor), lightColor);
// calculate the falloff planes
const float a = ( lightColor[3] <= 1.0 ) ? -0.5f / DEFAULT_FOG_DISTANCE : -0.5f / lightColor[3];
// texture 0 is the falloff image
// It is expected to be already active
globalImages->fogImage->Bind();
fogPlanes[0][0] = a * backEnd.viewDef->worldSpace.modelViewMatrix[2];
fogPlanes[0][1] = a * backEnd.viewDef->worldSpace.modelViewMatrix[6];
fogPlanes[0][2] = a * backEnd.viewDef->worldSpace.modelViewMatrix[10];
fogPlanes[0][3] = a * backEnd.viewDef->worldSpace.modelViewMatrix[14];
fogPlanes[1][0] = a * backEnd.viewDef->worldSpace.modelViewMatrix[0];
fogPlanes[1][1] = a * backEnd.viewDef->worldSpace.modelViewMatrix[4];
fogPlanes[1][2] = a * backEnd.viewDef->worldSpace.modelViewMatrix[8];
fogPlanes[1][3] = a * backEnd.viewDef->worldSpace.modelViewMatrix[12];
// texture 1 is the entering plane fade correction
GL_SelectTexture(1);
globalImages->fogEnterImage->Bind();
// reactive texture 0 for next passes
GL_SelectTexture(0);
// T will get a texgen for the fade plane, which is always the "top" plane on unrotated lights
fogPlanes[2][0] = 0.001f * vLight->fogPlane[0];
fogPlanes[2][1] = 0.001f * vLight->fogPlane[1];
fogPlanes[2][2] = 0.001f * vLight->fogPlane[2];
fogPlanes[2][3] = 0.001f * vLight->fogPlane[3];
// S is based on the view origin
const float s = backEnd.viewDef->renderView.vieworg * fogPlanes[2].Normal() + fogPlanes[2][3];
fogPlanes[3][0] = 0;
fogPlanes[3][1] = 0;
fogPlanes[3][2] = 0;
fogPlanes[3][3] = FOG_ENTER + s;
// draw it
GL_State(GLS_DEPTHMASK | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL);
RB_GLSL_RenderDrawSurfChainWithFunction(drawSurfs, RB_T_GLSL_BasicFog, vLight);
RB_GLSL_RenderDrawSurfChainWithFunction(drawSurfs2, RB_T_GLSL_BasicFog, vLight);
// the light frustum bounding planes aren't in the depth buffer, so use depthfunc_less instead
// of depthfunc_equal
GL_State(GLS_DEPTHMASK | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_LESS);
GL_Cull(CT_BACK_SIDED);
RB_GLSL_RenderDrawSurfChainWithFunction(&ds, RB_T_GLSL_BasicFog, vLight);
// Restore culling
GL_Cull(CT_FRONT_SIDED);
GL_State(GLS_DEPTHMASK | GLS_DEPTHFUNC_EQUAL); // Restore DepthFunc
// Restore attributes arrays
// Vertex attribute is always enabled
// Re-enable Color attribute (as it is enabled by default)
// Re-enable TexCoord attribute (as it is enabled by default)
GL_EnableVertexAttribArray(ATTR_COLOR);
GL_EnableVertexAttribArray(ATTR_TEXCOORD);
}
/*
==================
RB_T_FillDepthBuffer
==================
*/
void RB_T_GLSL_FillDepthBuffer(const drawSurf_t* surf) {
const idMaterial* const shader = surf->material;
//////////////
// Skip cases
//////////////
if ( !shader->IsDrawn()) {
return;
}
//const srfTriangles_t* const tri = surf->geo;
// some deforms may disable themselves by setting numIndexes = 0
if ( !surf->numIndexes ) {
return;
}
// translucent surfaces don't put anything in the depth buffer and don't
// test against it, which makes them fail the mirror clip plane operation
if ( shader->Coverage() == MC_TRANSLUCENT ) {
return;
}
if ( !surf->ambientCache ) {
return;
}
// get the expressions for conditionals / color / texcoords
const float* const regs = surf->shaderRegisters;
// if all stages of a material have been conditioned off, don't do anything
int stage;
for ( stage = 0; stage < shader->GetNumStages(); stage++ ) {
const shaderStage_t* pStage = shader->GetStage(stage);
// check the stage enable condition
if ( regs[pStage->conditionRegister] != 0 ) {
break;
}
}
if ( stage == shader->GetNumStages()) {
return;
}
///////////////////////////////////////////
// GL Shader setup for the current surface
///////////////////////////////////////////
// update the clip plane if needed
if ( backEnd.viewDef->numClipPlanes && surf->space != backEnd.currentSpace ) {
idPlane plane;
R_GlobalPlaneToLocal(surf->space->modelMatrix, backEnd.viewDef->clipPlanes[0], plane);
plane[3] += 0.5; // the notch is in the middle
GL_Uniform4fv(offsetof(shaderProgram_t, clipPlane), plane.ToFloatPtr());
}
// set polygon offset if necessary
// NB: will be restored at the end of the process
if ( shader->TestMaterialFlag(MF_POLYGONOFFSET)) {
qglEnable(GL_POLYGON_OFFSET_FILL);
qglPolygonOffset(r_offsetFactor.GetFloat(), r_offsetUnits.GetFloat() * shader->GetPolygonOffset());
}
// Color
// black by default
float color[4] = { 0, 0, 0, 1 };
// subviews will just down-modulate the color buffer by overbright
// NB: will be restored at end of the process
if ( shader->GetSort() == SS_SUBVIEW ) {
GL_State(GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO | GLS_DEPTHFUNC_LESS);
color[0] = color[1] = color[2] = 1.0f; // NB: was 1.0 / backEnd.overBright );
}
GL_Uniform4fv(offsetof(shaderProgram_t, glColor), color);
// Get vertex data
idDrawVert* ac = (idDrawVert*) vertexCache.Position(surf->ambientCache);
// Setup attribute pointers
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Vertex), 3, GL_FLOAT, false, sizeof(idDrawVert),
ac->xyz.ToFloatPtr());
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_TexCoord), 2, GL_FLOAT, false, sizeof(idDrawVert),
ac->st.ToFloatPtr());
bool drawSolid = false;
if ( shader->Coverage() == MC_OPAQUE ) {
drawSolid = true;
}
////////////////////////////////
// Perforated surfaces handling
////////////////////////////////
// we may have multiple alpha tested stages
if ( shader->Coverage() == MC_PERFORATED ) {
// if the only alpha tested stages are condition register omitted,
// draw a normal opaque surface
bool didDraw = false;
///////////////////////
// For each stage loop
///////////////////////
// perforated surfaces may have multiple alpha tested stages
for ( stage = 0; stage < shader->GetNumStages(); stage++ ) {
const shaderStage_t* pStage = shader->GetStage(stage);
//////////////
// Skip cases
//////////////
if ( !pStage->hasAlphaTest ) {
continue;
}
// check the stage enable condition
if ( regs[pStage->conditionRegister] == 0 ) {
continue;
}
// if we at least tried to draw an alpha tested stage,
// we won't draw the opaque surface
didDraw = true;
// set the alpha modulate
color[3] = regs[pStage->color.registers[3]];
// skip the entire stage if alpha would be black
if ( color[3] <= 0 ) {
continue;
}
//////////////////////////
// GL Setup for the stage
//////////////////////////
// Color
// alpha testing
GL_Uniform4fv(offsetof(shaderProgram_t, glColor), color);
GL_Uniform1fv(offsetof(shaderProgram_t, alphaTest), &regs[pStage->alphaTestRegister]);
// bind the texture
pStage->texture.image->Bind();
// Setup the texture matrix if needed
// NB: will be restored to identity
if ( pStage->texture.hasMatrix ) {
float matrix[16];
RB_GetShaderTextureMatrix(surf->shaderRegisters, &pStage->texture, matrix);
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), matrix);
}
///////////
// Draw it
///////////
RB_DrawElementsWithCounters(surf);
////////////////////////////////////////////////////////////
// Restore everything to an acceptable state for next stage
////////////////////////////////////////////////////////////
// Restore identity matrix
if ( pStage->texture.hasMatrix ) {
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), mat4_identity.ToFloatPtr());
}
}
if ( !didDraw ) {
drawSolid = true;
}
///////////////////////////////////////////////////////////
// Restore everything to an acceptable state for next step
///////////////////////////////////////////////////////////
// Restore color alpha to opaque
color[3] = 1;
GL_Uniform4fv(offsetof(shaderProgram_t, glColor), color);
// Restore alphatest always passing
static const float one[1] = { 1 };
GL_Uniform1fv(offsetof(shaderProgram_t, alphaTest), one);
// Restore white image binding to Tex0
globalImages->whiteImage->Bind();
}
////////////////////////////////////////
// Normal surfaces case (non perforated)
////////////////////////////////////////
// draw the entire surface solid
if ( drawSolid ) {
///////////
// Draw it
///////////
RB_DrawElementsWithCounters(surf);
}
/////////////////////////////////////////////
// Restore everything to an acceptable state
/////////////////////////////////////////////
// reset polygon offset
if ( shader->TestMaterialFlag(MF_POLYGONOFFSET)) {
qglDisable(GL_POLYGON_OFFSET_FILL);
}
// Restore blending
if ( shader->GetSort() == SS_SUBVIEW ) {
GL_State(GLS_DEPTHFUNC_LESS);
}
}
/*
=====================
RB_GLSL_FillDepthBuffer
If we are rendering a subview with a near clip plane, use a second texture
to force the alpha test to fail when behind that clip plane
=====================
*/
void RB_GLSL_FillDepthBuffer(drawSurf_t** drawSurfs, int numDrawSurfs) {
//////////////
// Skip cases
//////////////
// if we are just doing 2D rendering, no need to fill the depth buffer
if ( !backEnd.viewDef->viewEntitys ) {
return;
}
////////////////////////////////////////
// GL Shader setup for the current pass
// (ie. common to each surface)
////////////////////////////////////////
// Expected client GL State at this point
// Tex0 active
// Vertex attribute enabled
// Color attribute enabled
// Shader AlphaTest is one
// Shader Texture Matrix is Identity
// If clip planes are enabled in the view, use he "Clip" version of zfill shader
// and enable the second texture for mirror plane clipping if needed
if ( backEnd.viewDef->numClipPlanes ) {
// Use he zfillClip shader
GL_UseProgram(&zfillClipShader);
// Bind the Texture 1 to alphaNotchImage
GL_SelectTexture(1);
globalImages->alphaNotchImage->Bind();
// Be sure to reactivate Texture 0, as it will be bound right after
GL_SelectTexture(0);
}
// If no clip planes, just use the regular zfill shader
else {
GL_UseProgram(&zfillShader);
}
// Setup attributes arrays
// Vertex attribute is always enabled
// TexCoord attribute is always enabled
// Disable Color attribute (as it is enabled by default)
GL_DisableVertexAttribArray(ATTR_COLOR);
// Texture 0 will be used for alpha tested surfaces. It should be already active.
// Bind it to white image by default
globalImages->whiteImage->Bind();
// Decal surfaces may enable polygon offset
// GAB Note: Looks like it is not needed, because in case of offsetted surface we will use the offset value of the surface
// qglPolygonOffset(r_offsetFactor.GetFloat(), r_offsetUnits.GetFloat());
// Depth func to LESS
GL_State(GLS_DEPTHFUNC_LESS);
// Enable stencil test if we are going to be using it for shadows.
// If we didn't do this, it would be legal behavior to get z fighting
// from the ambient pass and the light passes.
qglEnable(GL_STENCIL_TEST);
qglStencilFunc(GL_ALWAYS, 1, 255);
//////////////////////////
// For each surfaces loop
//////////////////////////
// Optimization to only change MVP matrix when needed
backEnd.currentSpace = NULL;
for ( int i = 0; i < numDrawSurfs; i++ ) {
const drawSurf_t* const drawSurf = drawSurfs[i];
///////////////////////////////////////////
// GL shader setup for the current surface
///////////////////////////////////////////
// Change the MVP matrix if needed
if ( drawSurf->space != backEnd.currentSpace ) {
float mvp[16];
RB_ComputeMVP(drawSurf, mvp);
// We can set the uniform now as it shader is already bound
GL_UniformMatrix4fv(offsetof(shaderProgram_t, modelViewProjectionMatrix), mvp);
}
// Hack Depth Range if necessary
bool bNeedRestoreDepthRange = false;
if (drawSurf->space->weaponDepthHack && drawSurf->space->modelDepthHack == 0.0f) {
qglDepthRangef(0, 0.5);
bNeedRestoreDepthRange = true;
}
// change the scissor if needed
if ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals(drawSurf->scissorRect)) {
backEnd.currentScissor = drawSurf->scissorRect;
if (( backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1 ) < 0.0f ||
( backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 ) < 0.0f ) {
backEnd.currentScissor = backEnd.viewDef->scissor;
}
qglScissor(backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1);
}
////////////////////
// Do the real work
////////////////////
RB_T_GLSL_FillDepthBuffer(drawSurf);
/////////////////////////////////////////////
// Restore everything to an acceptable state
/////////////////////////////////////////////
if (bNeedRestoreDepthRange) {
qglDepthRangef(0.0f, 1.0f);
}
// Let's change space for next iteration
backEnd.currentSpace = drawSurf->space;
}
/////////////////////////////////////////////
// Restore everything to an acceptable state
/////////////////////////////////////////////
// Restore current space to NULL
backEnd.currentSpace = NULL;
// Restore attributes arrays
// Vertex attribute is always enabled
// TexCoord attribute is always enabled
// Re-enable Color attribute (as it is enabled by default)
GL_EnableVertexAttribArray(ATTR_COLOR);
}
/*
==================
RB_GLSL_T_RenderShaderPasses
This is also called for the generated 2D rendering
==================
*/
void RB_GLSL_T_RenderShaderPasses(const drawSurf_t* surf, const float mvp[16]) {
// global constants
static const GLfloat zero[1] = { 0 };
static const GLfloat one[1] = { 1 };
static const GLfloat oneScaled[1] = { 1 / 255.0f };
static const GLfloat negOneScaled[1] = { -1.0f / 255.0f };
// usefull pointers
const idMaterial* const shader = surf->material;
// const srfTriangles_t* const tri = surf->geo;
//////////////
// Skip cases
//////////////
#ifdef NO_LIGHT
if ( !r_noLight.GetBool() )
#endif
if ( !shader->HasAmbient()) {
return;
}
if ( shader->IsPortalSky()) {
return;
}
// some deforms may disable themselves by setting numIndexes = 0
if ( !surf->numIndexes ) {
return;
}
if ( !surf->ambientCache ) {
return;
}
///////////////////////////////////
// GL shader setup for the surface
// (ie. common to each Stage)
///////////////////////////////////
// change the scissor if needed
if ( r_useScissor.GetBool() &&
!backEnd.currentScissor.Equals(surf->scissorRect)) {
backEnd.currentScissor = surf->scissorRect;
if (( backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1 ) < 0.0f ||
( backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 ) < 0.0f ) {
backEnd.currentScissor = backEnd.viewDef->scissor;
}
qglScissor(backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1);
}
// set polygon offset if necessary
// NB: must be restored at end of process
if ( shader->TestMaterialFlag(MF_POLYGONOFFSET)) {
qglEnable(GL_POLYGON_OFFSET_FILL);
qglPolygonOffset(r_offsetFactor.GetFloat(), r_offsetUnits.GetFloat() * shader->GetPolygonOffset());
}
// set face culling appropriately
GL_Cull(shader->GetCullType());
// Location of vertex attributes data
const idDrawVert* const ac = (const idDrawVert* const) vertexCache.Position(surf->ambientCache);
// get the expressions for conditionals / color / texcoords
const float* const regs = surf->shaderRegisters;
// Caches to set per surface shader GL state only when necessary
bool bMVPSet[TG_GLASSWARP-TG_EXPLICIT];
memset(bMVPSet, 0, (TG_GLASSWARP-TG_EXPLICIT)*sizeof(bool));
bool bVASet [TG_GLASSWARP-TG_EXPLICIT];
memset(bVASet, 0, (TG_GLASSWARP-TG_EXPLICIT)*sizeof(bool));
// precompute the local view origin (might be needed for some texgens)
idVec4 localViewOrigin;
R_GlobalPointToLocal(surf->space->modelMatrix, backEnd.viewDef->renderView.vieworg, localViewOrigin.ToVec3());
localViewOrigin.w = 1.0f;
///////////////////////
// For each stage loop
///////////////////////
for ( int stage = 0; stage < shader->GetNumStages(); stage++ ) {
const shaderStage_t* const pStage = shader->GetStage(stage);
///////////////
// Skip cases
///////////////
// check the enable condition
if ( regs[pStage->conditionRegister] == 0 ) {
continue;
}
// skip the stages involved in lighting
if ( pStage->lighting != SL_AMBIENT ) {
continue;
}
// skip if the stage is ( GL_ZERO, GL_ONE ), which is used for some alpha masks
if (( pStage->drawStateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS )) ==
( GLS_SRCBLEND_ZERO | GLS_DSTBLEND_ONE )) {
continue;
}
// see if we are a new-style stage
const newShaderStage_t* const newStage = pStage->newStage;
if ( newStage ) {
// new style stages: Not implemented in GLSL yet!
continue;
} else {
// old style stages
/////////////////////////
// Additional skip cases
/////////////////////////
// precompute the color
const float color[4] = {
regs[pStage->color.registers[0]],
regs[pStage->color.registers[1]],
regs[pStage->color.registers[2]],
regs[pStage->color.registers[3]]
};
// skip the entire stage if an add would be black
if (
( pStage->drawStateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS )) == ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE )
&& color[0] <= 0 && color[1] <= 0 && color[2] <= 0 ) {
continue;
}
// skip the entire stage if a blend would be completely transparent
if (( pStage->drawStateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS )) ==
( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA )
&& color[3] <= 0 ) {
continue;
}
/////////////////////////////////
// GL shader setup for the stage
/////////////////////////////////
// The very first thing we need to do before going down into GL is to choose he correct GLSL shader depending on
// the associated TexGen. Then, setup its specific uniforms/attribs, and then only we can setup the common uniforms/attribs
if ( pStage->texture.texgen == TG_DIFFUSE_CUBE ) {
// Not used in game, but implemented because trivial
// This is diffuse cube mapping
GL_UseProgram(&diffuseCubeShader);
// Possible that normals should be transformed by a normal matrix in the shader ? I am not sure...
// Setup texcoord array to use the normals
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_TexCoord), 3, GL_FLOAT, false, sizeof(idDrawVert),
ac->normal.ToFloatPtr());
// Setup the texture matrix
if ( pStage->texture.hasMatrix ) {
float matrix[16];
RB_GetShaderTextureMatrix(surf->shaderRegisters, &pStage->texture, matrix);
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), matrix);
}
} else if ( pStage->texture.texgen == TG_SKYBOX_CUBE ) {
// This is skybox cube mapping
GL_UseProgram(&skyboxCubeShader);
// Disable TexCoord attribute
GL_DisableVertexAttribArray(ATTR_TEXCOORD);
// Setup the local view origin uniform
GL_Uniform4fv(offsetof(shaderProgram_t, localViewOrigin), localViewOrigin.ToFloatPtr());
// Setup the texture matrix
if ( pStage->texture.hasMatrix ) {
float matrix[16];
RB_GetShaderTextureMatrix(surf->shaderRegisters, &pStage->texture, matrix);
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), matrix);
}
} else if ( pStage->texture.texgen == TG_WOBBLESKY_CUBE ) {
// This is skybox cube mapping, with special texture matrix
GL_UseProgram(&skyboxCubeShader);
// Disable TexCoord attribute
GL_DisableVertexAttribArray(ATTR_TEXCOORD);
// Setup the local view origin uniform
GL_Uniform4fv(offsetof(shaderProgram_t, localViewOrigin), localViewOrigin.ToFloatPtr());
// Setup the texture matrix
// Note: here, we combine the shader texturematrix and precomputed wobblesky matrix
if ( pStage->texture.hasMatrix ) {
float texturematrix[16];
RB_GetShaderTextureMatrix(surf->shaderRegisters, &pStage->texture, texturematrix);
float finalmatrix[16];
myGlMultMatrix(texturematrix, surf->wobbleTransform, finalmatrix);
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), finalmatrix);
} else {
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), surf->wobbleTransform);
}
} else if ( pStage->texture.texgen == TG_SCREEN ) {
// Not used in game, so not implemented
continue;
} else if ( pStage->texture.texgen == TG_SCREEN2 ) {
// Not used in game, so not implemented
continue;
} else if ( pStage->texture.texgen == TG_GLASSWARP ) {
// Not used in game, so not implemented. The shader code is even not present in original D3 data
continue;
} else if ( pStage->texture.texgen == TG_REFLECT_CUBE ) {
// This is reflection cubemapping
GL_UseProgram(&reflectionCubeShader);
// NB: in original D3, if the surface had a bump map it would lead to the "Bumpy reflection cubemaping" shader being used.
// This is not implemented for now, we only do standard reflection cubemaping. Visual difference is really minor.
// Setup texcoord array to use the normals
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_TexCoord), 3, GL_FLOAT, false, sizeof(idDrawVert),
ac->normal.ToFloatPtr());
// Setup the modelViewMatrix, we will need it to compute the reflection
GL_UniformMatrix4fv(offsetof(shaderProgram_t, modelViewMatrix), surf->space->modelViewMatrix);
// Setup the texture matrix like original D3 code does: using the transpose modelViewMatrix of the view
// NB: this is curious, not sure why this is done like this....
float mat[16];
R_TransposeGLMatrix(backEnd.viewDef->worldSpace.modelViewMatrix, mat);
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), mat);
} else { // TG_EXPLICIT
// Otherwise, this is just regular surface shader with explicit texcoords
GL_UseProgram(&diffuseMapShader);
// Setup the TexCoord pointer
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_TexCoord), 2, GL_FLOAT, false, sizeof(idDrawVert),
ac->st.ToFloatPtr());
// Setup the texture matrix
if ( pStage->texture.hasMatrix ) {
float matrix[16];
RB_GetShaderTextureMatrix(surf->shaderRegisters, &pStage->texture, matrix);
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), matrix);
}
}
// Now we have a shader, we can setup the uniforms and attribute pointers common to all kind of shaders
// The specifics have already been done in the shader selection code (see above)
// Non-stage dependent state (per drawsurf, may be done once per GL shader)
{
// Vertex Attributes
if ( !bVASet[pStage->texture.texgen] ) {
// Setup the Vertex Attrib pointer
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Vertex), 3, GL_FLOAT, false, sizeof(idDrawVert),
ac->xyz.ToFloatPtr());
// Setup the Color pointer
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Color), 4, GL_UNSIGNED_BYTE, false, sizeof(idDrawVert),
(void*) &ac->color);
bVASet[pStage->texture.texgen] = true;
}
// MVP
if ( !bMVPSet[pStage->texture.texgen] ) {
// Setup the MVP uniform
GL_UniformMatrix4fv(offsetof(shaderProgram_t, modelViewProjectionMatrix), mvp);
bMVPSet[pStage->texture.texgen] = true;
}
}
// Stage dependent state
// Setup the Color uniform
GL_Uniform4fv(offsetof(shaderProgram_t, glColor), color);
// Setup the Color modulation
switch ( pStage->vertexColor ) {
case SVC_MODULATE: {
GL_Uniform1fv(offsetof(shaderProgram_t, colorModulate), oneScaled);
GL_Uniform1fv(offsetof(shaderProgram_t, colorAdd), zero);
break;
}
case SVC_INVERSE_MODULATE: {
GL_Uniform1fv(offsetof(shaderProgram_t, colorModulate), negOneScaled);
GL_Uniform1fv(offsetof(shaderProgram_t, colorAdd), one);
break;
}
default:
case SVC_IGNORE:
// This is already the default values (zero, one)
break;
}
// bind the texture (this will be either a dynamic texture, or a static one)
RB_BindVariableStageImage(&pStage->texture, regs);
// set the state
GL_State(pStage->drawStateBits);
// set privatePolygonOffset if necessary
if ( pStage->privatePolygonOffset ) {
qglEnable(GL_POLYGON_OFFSET_FILL);
qglPolygonOffset(r_offsetFactor.GetFloat(), r_offsetUnits.GetFloat() * pStage->privatePolygonOffset);
}
#ifdef NO_LIGHT
if (r_noLight.GetBool() )
{
if (pStage->drawStateBits!=9000)
GL_State(pStage->drawStateBits);
else
{
if (shader->TestMaterialFlag(MF_POLYGONOFFSET))
GL_State(GLS_SRCBLEND_ONE|GLS_DSTBLEND_ONE|GLS_DEPTHFUNC_LESS);
else
GL_State(GLS_SRCBLEND_ONE|GLS_DSTBLEND_ONE|GLS_DEPTHFUNC_LESS);
}
}
#endif
/////////////////////
// Draw the surface!
/////////////////////
RB_DrawElementsWithCounters(surf);
/////////////////////////////////////////////
// Restore everything to an acceptable state
/////////////////////////////////////////////
// Disable the other attributes array
if ( pStage->texture.texgen == TG_DIFFUSE_CUBE ) {
// Restore identity to the texture matrix
if ( pStage->texture.hasMatrix) {
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), mat4_identity.ToFloatPtr());
}
} else if ( pStage->texture.texgen == TG_SKYBOX_CUBE ) {
// Reenable TexCoord attribute
GL_EnableVertexAttribArray(ATTR_TEXCOORD);
// Restore identity to the texture matrix
if ( pStage->texture.hasMatrix) {
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), mat4_identity.ToFloatPtr());
}
} else if ( pStage->texture.texgen == TG_WOBBLESKY_CUBE ) {
// Reenable TexCoord attribute
GL_EnableVertexAttribArray(ATTR_TEXCOORD);
// Restore identity to the texture matrix (shall be done each time, as there is the wobblesky transform combined inside)
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), mat4_identity.ToFloatPtr());
} else if ( pStage->texture.texgen == TG_SCREEN ) {
} else if ( pStage->texture.texgen == TG_SCREEN2 ) {
} else if ( pStage->texture.texgen == TG_GLASSWARP ) {
} else if ( pStage->texture.texgen == TG_REFLECT_CUBE ) {
// Restore identity to the texture matrix (shall be done each time)
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), mat4_identity.ToFloatPtr());
} else {
// Restore identity to the texture matrix
if ( pStage->texture.hasMatrix) {
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), mat4_identity.ToFloatPtr());
}
}
// unset privatePolygonOffset if necessary
if ( pStage->privatePolygonOffset && !surf->material->TestMaterialFlag(MF_POLYGONOFFSET)) {
qglDisable(GL_POLYGON_OFFSET_FILL);
} else if ( pStage->privatePolygonOffset && surf->material->TestMaterialFlag(MF_POLYGONOFFSET)) {
qglPolygonOffset(r_offsetFactor.GetFloat(), r_offsetUnits.GetFloat() * shader->GetPolygonOffset());
}
// Restore color modulation state to default values
if ( pStage->vertexColor != SVC_IGNORE ) {
GL_Uniform1fv(offsetof(shaderProgram_t, colorModulate), zero);
GL_Uniform1fv(offsetof(shaderProgram_t, colorAdd), one);
}
// Don't touch the rest, as this will either reset by the next stage, or handled by end of this method
}
}
/////////////////////////////////////////////
// Restore everything to an acceptable state
/////////////////////////////////////////////
// reset polygon offset
if ( shader->TestMaterialFlag(MF_POLYGONOFFSET)) {
qglDisable(GL_POLYGON_OFFSET_FILL);
}
}
/*
=====================
RB_GLSL_DrawShaderPasses
Draw non-light dependent passes
=====================
*/
int RB_GLSL_DrawShaderPasses(drawSurf_t** drawSurfs, int numDrawSurfs) {
//////////////
// Skip cases
//////////////
// only obey skipAmbient if we are rendering a view
if ( backEnd.viewDef->viewEntitys && r_skipAmbient.GetBool()) {
return numDrawSurfs;
}
// if we are about to draw the first surface that needs
// the rendering in a texture, copy it over
if ( drawSurfs[0]->material->GetSort() >= SS_POST_PROCESS ) {
if ( r_skipPostProcess.GetBool()) {
return 0;
}
// only dump if in a 3d view
if ( backEnd.viewDef->viewEntitys ) {
//globalImages->currentRenderImage->CopyFramebuffer(backEnd.viewDef->viewport.x1,
// backEnd.viewDef->viewport.y1,
// backEnd.viewDef->viewport.x2 - backEnd.viewDef->viewport.x1 + 1,
// backEnd.viewDef->viewport.y2 - backEnd.viewDef->viewport.y1 + 1,
// true);
}
backEnd.currentRenderCopied = true;
}
////////////////////////////////////////
// GL shader setup for the current pass
// (ie. common to each surface)
////////////////////////////////////////
// Texture 0 is expected to be active
// Setup attributes arrays
// Vertex attribute is always enabled
// Color attribute is always enabled
// Texcoord attribute is always enabled
/////////////////////////
// For each surface loop
/////////////////////////
float mvp[16];
backEnd.currentSpace = NULL;
int i;
for ( i = 0; i < numDrawSurfs; i++ ) {
//////////////
// Skip cases
//////////////
if ( drawSurfs[i]->material->SuppressInSubview()) {
continue;
}
if ( backEnd.viewDef->isXraySubview && drawSurfs[i]->space->entityDef ) {
if ( drawSurfs[i]->space->entityDef->parms.xrayIndex != 2 ) {
continue;
}
}
// we need to draw the post process shaders after we have drawn the fog lights
if ( drawSurfs[i]->material->GetSort() >= SS_POST_PROCESS
&& !backEnd.currentRenderCopied ) {
break;
}
// Change the MVP matrix if needed
if ( drawSurfs[i]->space != backEnd.currentSpace ) {
RB_ComputeMVP(drawSurfs[i], mvp);
// We can't set the uniform now, as we still don't know which shader to use
}
// Hack Depth Range if necessary
bool bNeedRestoreDepthRange = false;
if (drawSurfs[i]->space->weaponDepthHack && drawSurfs[i]->space->modelDepthHack == 0.0f) {
qglDepthRangef(0.0f, 0.5f);
bNeedRestoreDepthRange = true;
}
////////////////////
// Do the real work
////////////////////
RB_GLSL_T_RenderShaderPasses(drawSurfs[i], mvp);
if (bNeedRestoreDepthRange) {
qglDepthRangef(0.0f, 1.0f);
}
backEnd.currentSpace = drawSurfs[i]->space;
}
/////////////////////////////////////////////
// Restore everything to an acceptable state
/////////////////////////////////////////////
backEnd.currentSpace = NULL;
// Restore culling
GL_Cull(CT_FRONT_SIDED);
// Restore attributes arrays
// Vertex attribute is always enabled
// Color attribute is always enabled
// Texcoord attribute is always enabled
// Trashed state:
// Current Program
// Tex0 binding
// Return the counter of drawn surfaces
return i;
}
/*
=====================
RB_T_BlendLight
=====================
*/
static void RB_T_GLSL_BlendLight(const drawSurf_t *surf, const viewLight_t* vLight) {
// const srfTriangles_t *tri = surf->geo;
////////////
// GL setup
////////////
// Shader uniforms
// Setup the fogMatrix as being the local Light Projection
// Only do this once per space
if (backEnd.currentSpace != surf->space) {
idPlane lightProject[4];
int i;
for (i = 0; i < 4; i++) {
R_GlobalPlaneToLocal(surf->space->modelMatrix, vLight->lightProject[i], lightProject[i]);
}
idMat4 fogMatrix;
fogMatrix[0] = lightProject[0].ToVec4();
fogMatrix[1] = lightProject[1].ToVec4();
fogMatrix[2] = lightProject[2].ToVec4();
fogMatrix[3] = lightProject[3].ToVec4();
GL_UniformMatrix4fv(offsetof(shaderProgram_t, fogMatrix), fogMatrix.ToFloatPtr());
}
// Attributes pointers
// This gets used for both blend lights and shadow draws
if (surf->ambientCache) {
idDrawVert *ac = (idDrawVert *) vertexCache.Position(surf->ambientCache);
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Vertex), 3, GL_FLOAT, false, sizeof(idDrawVert), ac->xyz.ToFloatPtr());
} else if (surf->shadowCache) {
shadowCache_t *sc = (shadowCache_t *) vertexCache.Position(surf->shadowCache);
GL_VertexAttribPointer(offsetof(shaderProgram_t, attr_Vertex), 3, GL_FLOAT, false, sizeof(idDrawVert), sc->xyz.ToFloatPtr());
}
////////////////////
// Draw the surface
////////////////////
RB_DrawElementsWithCounters(surf);
}
/*
=====================
RB_GLSL BlendLight
Dual texture together the falloff and projection texture with a blend
mode to the framebuffer, instead of interacting with the surface texture
=====================
*/
void RB_GLSL_BlendLight(const drawSurf_t *drawSurfs, const drawSurf_t *drawSurfs2, const viewLight_t* vLight) {
const idMaterial * const lightShader = vLight->lightShader;
const float * const regs = vLight->shaderRegisters;
//////////////
// Skip Cases
//////////////
if (!drawSurfs) {
return;
}
if (r_skipBlendLights.GetBool()) {
return;
}
////////////////////////////////////
// GL setup for the current pass
// (ie. common to all Light Stages)
////////////////////////////////////
// Use blendLight shader
GL_UseProgram(&blendLightShader);
// Texture 1 will get the falloff texture
GL_SelectTexture(1);
vLight->falloffImage->Bind();
// Texture 0 will get the projected texture
GL_SelectTexture(0);
////////////////////////
// For each Light Stage
////////////////////////
int i;
for (i = 0; i < lightShader->GetNumStages(); i++) {
const shaderStage_t *stage = lightShader->GetStage(i);
//////////////
// Skip Cases
//////////////
if (!regs[stage->conditionRegister]) {
continue;
}
////////////////////////////////////////
// GL setup for the current Light Stage
// (ie. common to all surfaces)
////////////////////////////////////////
// Global GL state
// Setup the drawState
GL_State(GLS_DEPTHMASK | stage->drawStateBits | GLS_DEPTHFUNC_EQUAL);
// Bind the projected texture
stage->texture.image->Bind();
// Shader Uniforms
// Setup the texture matrix
if ( stage->texture.hasMatrix ) {
float matrix[16];
RB_GetShaderTextureMatrix(regs, &stage->texture, matrix);
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), matrix);
}
// Setup the Fog Color
float lightColor[4];
lightColor[0] = regs[stage->color.registers[0]];
lightColor[1] = regs[stage->color.registers[1]];
lightColor[2] = regs[stage->color.registers[2]];
lightColor[3] = regs[stage->color.registers[3]];
GL_Uniform4fv(offsetof(shaderProgram_t, fogColor), lightColor);
////////////////////
// Do the Real Work
////////////////////
RB_GLSL_RenderDrawSurfChainWithFunction(drawSurfs, RB_T_GLSL_BlendLight, vLight);
RB_GLSL_RenderDrawSurfChainWithFunction(drawSurfs2, RB_T_GLSL_BlendLight, vLight);
////////////////////
// GL state restore
////////////////////
// Restore texture matrix to identity
if (stage->texture.hasMatrix) {
GL_UniformMatrix4fv(offsetof(shaderProgram_t, textureMatrix), mat4_identity.ToFloatPtr());
}
}
}
/*
==================
RB_FogAllLights
==================
*/
void RB_GLSL_FogAllLights(void) {
//////////////
// Skip Cases
//////////////
if (r_skipFogLights.GetBool() || backEnd.viewDef->isXraySubview /* dont fog in xray mode*/ ) {
return;
}
/////////////////////////////////////////////
// GL setup for the current pass
// (ie. common to both fog and blend lights)
/////////////////////////////////////////////
// Disable Stencil Test
qglDisable(GL_STENCIL_TEST);
// Disable TexCoord array
// Disable Color array
GL_DisableVertexAttribArray(ATTR_TEXCOORD);
GL_DisableVertexAttribArray(ATTR_COLOR);
//////////////////
// For each Light
//////////////////
const viewLight_t *vLight;
for (vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next) {
//////////////
// Skip Cases
//////////////
// We are only interested in Fog and Blend lights
if (!vLight->lightShader->IsFogLight() && !vLight->lightShader->IsBlendLight()) {
continue;
}
///////////////////////
// Do the Light passes
///////////////////////
if (vLight->lightShader->IsFogLight()) {
RB_GLSL_FogPass(vLight->globalInteractions, vLight->localInteractions, vLight);
} else if (vLight->lightShader->IsBlendLight()) {
RB_GLSL_BlendLight(vLight->globalInteractions, vLight->localInteractions, vLight);
}
}
////////////////////
// GL state restore
////////////////////
// Re-enable TexCoord array
// Re-enable Color array
GL_EnableVertexAttribArray(ATTR_TEXCOORD);
GL_EnableVertexAttribArray(ATTR_COLOR);
// Re-enable Stencil Test
qglEnable(GL_STENCIL_TEST);
}
/*
=================
RB_BeginGLSLShaderPasses
=================
*/
void RB_GLSL_PrepareShaders(void) {
// No shaders set by default
GL_UseProgram(NULL);
// Always enable the vertex, color and texcoord attributes arrays
GL_EnableVertexAttribArray(ATTR_VERTEX);
GL_EnableVertexAttribArray(ATTR_COLOR);
GL_EnableVertexAttribArray(ATTR_TEXCOORD);
// Disable the other arrays
GL_DisableVertexAttribArray(ATTR_NORMAL);
GL_DisableVertexAttribArray(ATTR_TANGENT);
GL_DisableVertexAttribArray(ATTR_BITANGENT);
}