/* 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. */ // // rb_shadow.c // #include "rb_local.h" /* ============================================================================= VOLUMETRIC SHADOWS ============================================================================= */ #ifdef SHADOW_VOLUMES #define MAX_SHADOWVOLUME_INDEXES RB_MAX_INDEXES*4 static qBool rb_triFacingLight[RB_MAX_TRIANGLES]; static index_t rb_shadowVolIndexes[MAX_SHADOWVOLUME_INDEXES]; static int rb_numShadowVolTris; /* ============= RB_BuildShadowVolumeTriangles ============= */ static int RB_BuildShadowVolumeTriangles (void) { int i, j, tris; int *neighbors = rb.inNeighbors; index_t *indexes = rb.inIndices; index_t *out = rb_shadowVolIndexes; // check each frontface for bordering backfaces, // and cast shadow polygons from those edges, // also create front and back caps for shadow volume for (i=0, j=0, tris=0 ; i 0) ? qTrue : qFalse; } } /* ============= RB_MakeTriangleShadowFlags ============= */ static void RB_MakeTriangleShadowFlags (vec3_t lightDist, float lightRadius) { int i, j; float f; float *v0; float *trnormal = rb.inTrNormals[0]; index_t *indexes = rb.inIndices; for (i=0, j=0 ; i 0) { rb_triFacingLight[j] = qTrue; } else { rb_triFacingLight[j] = qFalse; } } } /* ============= RB_ShadowProjectVertices ============= */ static void RB_ShadowProjectVertices (vec3_t lightDist, float projectDistance) { vec3_t diff; float *in, *out; int i; in = (float *)(rb.inVertices[0]); out = (float *)(rb.inVertices[rb.numVerts]); for (i=0 ; iorigin, lightDist2); if (!RB_CheckLightBoundaries (mins, maxs, lightDist2, intensity2)) return; projectDistance = radius - (float)Vec3Length (lightDist2); if (projectDistance > 0) return; // Light is inside the bbox projectDistance += intensity; if (projectDistance <= 0.1) return; // Too far away // Rotate if (!Matrix3_Compare (ent->axis, axisIdentity)) Matrix3_TransformVector (ent->axis, lightDist2, lightDist); else Vec3Copy (lightDist2, lightDist); RB_BuildShadowVolume (lightDist, projectDistance); RB_LockArrays (rb.numVerts * 2); if (gl_shadows->intVal == SHADOW_VOLUMES) { if (ri.useStencil) { if (ri.config.extStencilTwoSide) { qglEnable (GL_STENCIL_TEST_TWO_SIDE_EXT); qglActiveStencilFaceEXT (GL_BACK); qglStencilOp (GL_KEEP, GL_INCR, GL_KEEP); qglActiveStencilFaceEXT (GL_FRONT); qglStencilOp (GL_KEEP, GL_DECR, GL_KEEP); RB_DrawShadowVolume (); qglDisable (GL_STENCIL_TEST_TWO_SIDE_EXT); } else { qglCullFace (GL_BACK); // Quake is backwards, this culls front faces qglStencilOp (GL_KEEP, GL_INCR, GL_KEEP); RB_DrawShadowVolume (); // Decrement stencil if frontface is behind depthbuffer qglCullFace (GL_FRONT); // Quake is backwards, this culls back faces qglStencilOp (GL_KEEP, GL_DECR, GL_KEEP); RB_DrawShadowVolume (); } } else { qglCullFace (GL_BACK); // Quake is backwards, this culls front faces RB_DrawShadowVolume (); // Decrement stencil if frontface is behind depthbuffer qglCullFace (GL_FRONT); // Quake is backwards, this culls back faces RB_DrawShadowVolume (); } } else { RB_DrawShadowVolume (); } RB_UnlockArrays (); } /* ============= RB_SetShadowState ============= */ void RB_SetShadowState (qBool start) { if (start) { // Clear state RB_FinishRendering (); // Set the mode RB_SelectTexture (0); RB_TextureTarget (0); switch (gl_shadows->intVal) { case 1: RB_StateForBits (SB1_CULL_FRONT|SB1_BLEND_ON|SB1_DEFAULT); qglColor4f (0, 0, 0, SHADOW_ALPHA); qglDepthFunc (GL_LEQUAL); if (!ri.useStencil) break; qglEnable (GL_STENCIL_TEST); qglStencilFunc (GL_EQUAL, 128, 0xFF); qglStencilOp (GL_KEEP, GL_KEEP, GL_INCR); qglStencilMask (255); break; #ifdef SHADOW_VOLUMES case SHADOW_VOLUMES: if (ri.config.extStencilTwoSide) RB_StateForBits (SB1_BLEND_ON|SB1_DEFAULT); else RB_StateForBits (SB1_CULL_FRONT|SB1_BLEND_ON|SB1_DEFAULT); qglColor4f (1, 1, 1, 1); qglColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); qglDepthFunc (GL_LESS); if (!ri.useStencil) break; qglEnable (GL_STENCIL_TEST); qglStencilFunc (GL_ALWAYS, 128, 255); qglStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); qglStencilMask (255); break; case 3: RB_StateForBits (SB1_DEPTHTEST_ON|SB1_BLEND_ON|SB1_BLENDSRC_ONE|SB1_BLENDDST_ONE); qglColor3f (1.0f, 0.1f, 0.1f); qglDepthFunc (GL_LEQUAL); if (!ri.useStencil) break; qglDisable (GL_STENCIL_TEST); break; #endif } return; } // Reset switch (gl_shadows->intVal) { case 1: if (!ri.useStencil) break; qglDisable (GL_STENCIL_TEST); break; #ifdef SHADOW_VOLUMES case SHADOW_VOLUMES: qglColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); qglDepthFunc (GL_LEQUAL); if (!ri.useStencil) break; qglStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); qglDisable (GL_STENCIL_TEST); break; default: break; #endif } RB_TextureTarget (GL_TEXTURE_2D); RB_StateForBits (SB1_DEPTHMASK_ON|SB1_DEFAULT); } /* ============= RB_DrawShadowVolumes ============= */ void RB_DrawShadowVolumes (mesh_t *mesh, refEntity_t *ent, vec3_t mins, vec3_t maxs, float radius) { refDLight_t *dLight; vec3_t hack; uint32 i; if (ri.def.rdFlags & RDF_NOWORLDMODEL) { RB_ResetPointers (); return; } // FIXME: HACK Vec3Copy (ent->origin, hack); hack[2] += 128; hack[1] += 128; RB_CastShadowVolume (ent, mins, maxs, radius, hack, 500); // Dynamic light shadows dLight = ri.scn.dLightList; for (i=0 ; iorigin, dLight->intensity); RB_ResetPointers (); } /* ============= RB_ShadowBlend ============= */ void RB_ShadowBlend (void) { if (gl_shadows->intVal != SHADOW_VOLUMES || !ri.useStencil) return; qglMatrixMode (GL_PROJECTION); qglLoadIdentity (); qglOrtho (0, 1, 1, 0, -99999, 99999); qglMatrixMode (GL_MODELVIEW); qglLoadIdentity (); RB_StateForBits (SB1_BLEND_ON|SB1_BLENDSRC_SRC_ALPHA|SB1_BLENDDST_ONE_MINUS_SRC_ALPHA); RB_TextureTarget (0); qglColor4f (0, 0, 0, SHADOW_ALPHA); qglEnable (GL_STENCIL_TEST); qglStencilFunc (GL_NOTEQUAL, 128, 255); qglStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); qglBegin (GL_TRIANGLES); qglVertex2f (-5, -5); qglVertex2f (10, -5); qglVertex2f (-5, 10); qglEnd (); RB_StateForBits (SB1_CULL_FRONT|SB1_DEFAULT); qglDisable (GL_STENCIL_TEST); RB_TextureTarget (GL_TEXTURE_2D); qglColor4f (1, 1, 1, 1); } #endif /* ============================================================================= SIMPLE SHADOWS The mesh is simply pushed flat. ============================================================================= */ /* ============= RB_SimpleShadow ============= */ void RB_SimpleShadow (refEntity_t *ent, vec3_t shadowSpot) { float height; int i; // Set the height height = (ent->origin[2] - shadowSpot[2]) * -1; for (i=0 ; i