/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_glstate.c - OpenGL state manager from Q2E #include "r_local.h" /* ================= GL_Enable ================= */ void GL_Enable (GLenum cap) { switch (cap) { case GL_CULL_FACE: if (glState.cullFace) return; glState.cullFace = true; break; case GL_POLYGON_OFFSET_FILL: if (glState.polygonOffsetFill) return; glState.polygonOffsetFill = true; break; case GL_TEXTURE_SHADER_NV: if (!glConfig.NV_texshaders || glState.TexShaderNV) return; glState.TexShaderNV = true; break; case GL_VERTEX_PROGRAM_ARB: if (!glConfig.arb_vertex_program || glState.vertexProgram) return; glState.vertexProgram = true; break; case GL_FRAGMENT_PROGRAM_ARB: if (!glConfig.arb_fragment_program || glState.fragmentProgram) return; glState.fragmentProgram = true; break; case GL_ALPHA_TEST: if (glState.alphaTest) return; glState.alphaTest = true; break; case GL_BLEND: if (glState.blend) return; glState.blend = true; break; case GL_DEPTH_TEST: if (glState.depthTest) return; glState.depthTest = true; break; case GL_STENCIL_TEST: if (glState.stencilTest) return; glState.stencilTest = true; break; case GL_SCISSOR_TEST: if (glState.scissorTest) return; glState.scissorTest = true; } qglEnable(cap); } /* ================= GL_Disable ================= */ void GL_Disable (GLenum cap) { switch (cap) { case GL_CULL_FACE: if (!glState.cullFace) return; glState.cullFace = false; break; case GL_POLYGON_OFFSET_FILL: if (!glState.polygonOffsetFill) return; glState.polygonOffsetFill = false; break; case GL_TEXTURE_SHADER_NV: if (!glConfig.NV_texshaders || !glState.TexShaderNV) return; glState.TexShaderNV = false; break; case GL_VERTEX_PROGRAM_ARB: if (!glConfig.arb_vertex_program || !glState.vertexProgram) return; glState.vertexProgram = false; break; case GL_FRAGMENT_PROGRAM_ARB: if (!glConfig.arb_fragment_program || !glState.fragmentProgram) return; glState.fragmentProgram = false; break; case GL_ALPHA_TEST: if (!glState.alphaTest) return; glState.alphaTest = false; break; case GL_BLEND: if (!glState.blend) return; glState.blend = false; break; case GL_DEPTH_TEST: if (!glState.depthTest) return; glState.depthTest = false; break; case GL_STENCIL_TEST: if (!glState.stencilTest) return; glState.stencilTest = false; break; case GL_SCISSOR_TEST: if (!glState.scissorTest) return; glState.scissorTest = false; } qglDisable(cap); } /* ================= GL_Stencil setting stencil buffer stenciling for shadows & color shells ================= */ void GL_Stencil (qboolean enable, qboolean shell) { if (!glConfig.have_stencil || !r_stencil->value) return; if (enable) { if (shell || r_shadows->value == 3) { qglPushAttrib(GL_STENCIL_BUFFER_BIT); if ( r_shadows->value == 3) qglClearStencil(1); qglClear(GL_STENCIL_BUFFER_BIT); } GL_Enable(GL_STENCIL_TEST); qglStencilFunc(GL_EQUAL, 1, 2); qglStencilOp(GL_KEEP,GL_KEEP,GL_INCR); } else { GL_Disable(GL_STENCIL_TEST); if (shell || r_shadows->value == 3) qglPopAttrib(); } } qboolean GL_HasStencil (void) { return (glConfig.have_stencil && r_stencil->value); } /* ================= R_ParticleStencil uses stencil buffer to redraw particles only over trans surfaces ================= */ extern cvar_t *r_particle_overdraw; void R_ParticleStencil (int passnum) { if (!glConfig.have_stencil || !r_particle_overdraw->value) return; if (passnum == 1) // write area of trans surfaces to stencil buffer { qglPushAttrib(GL_STENCIL_BUFFER_BIT); // save stencil buffer qglClearStencil(1); qglClear(GL_STENCIL_BUFFER_BIT); GL_Enable(GL_STENCIL_TEST); qglStencilFunc( GL_ALWAYS, 1, 0xFF); qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR); } else if (passnum == 2) // turn off writing { GL_Disable(GL_STENCIL_TEST); } else if (passnum == 3) // enable drawing only to affected area { GL_Enable(GL_STENCIL_TEST); qglStencilFunc( GL_NOTEQUAL, 1, 0xFF); qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); } else if (passnum == 4) // turn off and restore { GL_Disable(GL_STENCIL_TEST); qglPopAttrib(); // restore stencil buffer } } /* ================= GL_Envmap setting up envmap ================= */ #define GLSTATE_DISABLE_TEXGEN if (glState.texgen) { qglDisable(GL_TEXTURE_GEN_S); qglDisable(GL_TEXTURE_GEN_T); qglDisable(GL_TEXTURE_GEN_R); glState.texgen=false; } #define GLSTATE_ENABLE_TEXGEN if (!glState.texgen) { qglEnable(GL_TEXTURE_GEN_S); qglEnable(GL_TEXTURE_GEN_T); qglEnable(GL_TEXTURE_GEN_R); glState.texgen=true; } void GL_Envmap (qboolean enable) { if (enable) { qglTexGenf(GL_S, GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); qglTexGenf(GL_T, GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); GLSTATE_ENABLE_TEXGEN } else { GLSTATE_DISABLE_TEXGEN } } /* ================= GL_ShadeModel ================= */ void GL_ShadeModel (GLenum mode) { if (glState.shadeModelMode == mode) return; glState.shadeModelMode = mode; qglShadeModel(mode); } /* ================= GL_TexEnv ================= */ void GL_TexEnv (GLenum mode) { static int lastmodes[2] = { -1, -1 }; if ( mode != lastmodes[glState.currenttmu] ) { qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode ); lastmodes[glState.currenttmu] = mode; } } /* ================= GL_CullFace ================= */ void GL_CullFace (GLenum mode) { if (glState.cullMode == mode) return; glState.cullMode = mode; qglCullFace(mode); } /* ================= GL_PolygonOffset ================= */ void GL_PolygonOffset (GLfloat factor, GLfloat units) { if (glState.offsetFactor == factor && glState.offsetUnits == units) return; glState.offsetFactor = factor; glState.offsetUnits = units; qglPolygonOffset(factor, units); } /* ================= GL_AlphaFunc ================= */ void GL_AlphaFunc (GLenum func, GLclampf ref) { if (glState.alphaFunc == func && glState.alphaRef == ref) return; glState.alphaFunc = func; glState.alphaRef = ref; qglAlphaFunc(func, ref); } /* ================= GL_BlendFunc ================= */ void GL_BlendFunc (GLenum src, GLenum dst) { if (glState.blendSrc == src && glState.blendDst == dst) return; glState.blendSrc = src; glState.blendDst = dst; qglBlendFunc(src, dst); } /* ================= GL_DepthFunc ================= */ void GL_DepthFunc (GLenum func) { if (glState.depthFunc == func) return; glState.depthFunc = func; qglDepthFunc(func); } /* ================= GL_DepthMask ================= */ void GL_DepthMask (GLboolean mask) { if (glState.depthMask == mask) return; glState.depthMask = mask; qglDepthMask(mask); } /* ================= GL_DepthRange ================= */ void GL_DepthRange (GLfloat rMin, GLfloat rMax) { if (glState.depthMin == rMin && glState.depthMax == rMax) return; glState.depthMin = rMin; glState.depthMax = rMax; qglDepthRange (rMin, rMax); } /* ============= GL_LockArrays ============= */ void GL_LockArrays (int numVerts) { if (!glConfig.extCompiledVertArray) return; if (glState.arraysLocked) return; qglLockArraysEXT (0, numVerts); glState.arraysLocked = true; } /* ============= GL_UnlockArrays ============= */ void GL_UnlockArrays (void) { if (!glConfig.extCompiledVertArray) return; if (!glState.arraysLocked) return; qglUnlockArraysEXT (); glState.arraysLocked = false; } /* ================= GL_EnableTexture ================= */ void GL_EnableTexture (unsigned tmu) { if (tmu > 0 && !glConfig.multitexture) return; if (tmu >= MAX_TEXTURE_UNITS || tmu >= glConfig.max_texunits) return; GL_SelectTexture(tmu); qglEnable(GL_TEXTURE_2D); qglEnableClientState(GL_TEXTURE_COORD_ARRAY); qglTexCoordPointer(2, GL_FLOAT, sizeof(texCoordArray[tmu][0]), texCoordArray[tmu][0]); glState.activetmu[tmu] = true; } /* ================= GL_DisableTexture ================= */ void GL_DisableTexture (unsigned tmu) { if (tmu > 0 && !glConfig.multitexture) return; if (tmu >= MAX_TEXTURE_UNITS || tmu >= glConfig.max_texunits) return; GL_SelectTexture(tmu); qglDisable(GL_TEXTURE_2D); qglDisableClientState(GL_TEXTURE_COORD_ARRAY); glState.activetmu[tmu] = false; } /* ================= GL_EnableMultitexture Only used for world drawing ================= */ void GL_EnableMultitexture (qboolean enable) { if (!glConfig.multitexture) return; if (enable) { GL_EnableTexture(1); GL_TexEnv(GL_REPLACE); } else { GL_DisableTexture(1); GL_TexEnv(GL_REPLACE); } GL_SelectTexture(0); GL_TexEnv(GL_REPLACE); } /* ================= GL_SelectTexture ================= */ void GL_SelectTexture (unsigned tmu) { if (!glConfig.multitexture) return; if (tmu >= MAX_TEXTURE_UNITS || tmu >= glConfig.max_texunits) return; if (tmu == glState.currenttmu) return; glState.currenttmu = tmu; qglActiveTextureARB(GL_TEXTURE0_ARB+tmu); qglClientActiveTextureARB(GL_TEXTURE0_ARB+tmu); } /* ================= GL_Bind ================= */ void GL_Bind (int texnum) { extern image_t *draw_chars; if (r_nobind->value && draw_chars) // performance evaluation option texnum = draw_chars->texnum; if (glState.currenttextures[glState.currenttmu] == texnum) return; glState.currenttextures[glState.currenttmu] = texnum; qglBindTexture (GL_TEXTURE_2D, texnum); } /* ================= GL_MBind ================= */ void GL_MBind (unsigned tmu, int texnum) { if (tmu >= MAX_TEXTURE_UNITS || tmu >= glConfig.max_texunits) return; GL_SelectTexture(tmu); if (glState.currenttextures[tmu] == texnum) return; GL_Bind(texnum); } /* ================= GL_SetDefaultState ================= */ void GL_SetDefaultState (void) { int i; // Reset the state manager glState.texgen = false; glState.cullFace = false; glState.polygonOffsetFill = false; glState.TexShaderNV = false; glState.vertexProgram = false; glState.fragmentProgram = false; glState.alphaTest = false; glState.blend = false; glState.stencilTest = false; glState.depthTest = false; glState.scissorTest = false; glState.arraysLocked = false; glState.cullMode = GL_FRONT; glState.shadeModelMode = GL_FLAT; glState.depthMin = gldepthmin; glState.depthMax = gldepthmax; glState.offsetFactor = -1; glState.offsetUnits = -2; glState.alphaFunc = GL_GREATER; glState.alphaRef = 0.666; glState.blendSrc = GL_SRC_ALPHA; glState.blendDst = GL_ONE_MINUS_SRC_ALPHA; glState.depthFunc = GL_LEQUAL; glState.depthMask = GL_TRUE; // Set default state qglDisable(GL_TEXTURE_GEN_S); qglDisable(GL_TEXTURE_GEN_T); qglDisable(GL_TEXTURE_GEN_R); qglEnable(GL_TEXTURE_2D); qglDisable(GL_CULL_FACE); qglDisable(GL_POLYGON_OFFSET_FILL); qglDisable(GL_ALPHA_TEST); qglDisable(GL_BLEND); qglDisable(GL_STENCIL_TEST); qglDisable(GL_DEPTH_TEST); qglDisable(GL_SCISSOR_TEST); qglCullFace(GL_FRONT); qglShadeModel(GL_FLAT); qglPolygonOffset(-1, -2); qglAlphaFunc(GL_GREATER, 0.666); qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglDepthFunc(GL_LEQUAL); qglDepthMask(GL_TRUE); qglClearColor (1,0, 0.5, 0.5); qglClearDepth(1.0); qglClearStencil(128); qglColor4f (1,1,1,1); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); GL_TextureMode( r_texturemode->string ); GL_TextureAlphaMode( r_texturealphamode->string ); GL_TextureSolidMode( r_texturesolidmode->string ); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Vertex arrays qglEnableClientState (GL_TEXTURE_COORD_ARRAY); qglEnableClientState (GL_VERTEX_ARRAY); qglEnableClientState (GL_COLOR_ARRAY); qglTexCoordPointer (2, GL_FLOAT, sizeof(texCoordArray[0][0]), texCoordArray[0][0]); qglVertexPointer (3, GL_FLOAT, sizeof(vertexArray[0]), vertexArray[0]); qglColorPointer (4, GL_FLOAT, sizeof(colorArray[0]), colorArray[0]); // end vertex arrays glState.activetmu[0] = true; for (i=1; i