/* Copyright (C) 2001-2002 Charles Hollemeersch 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. PENTA: the whole file is freakin penta... */ // gl_shadow.c stencil shadow support for quake #include "quakedef.h" int numShadowLights; int numStaticShadowLights; int numUsedShadowLights; //number of shadow lights acutally drawn this frame shadowlight_t shadowlights[MAXSHADOWLIGHTS]; shadowlight_t *usedshadowlights[MAXUSEDSHADOWLIGHS]; shadowlight_t *currentshadowlight; int volumeCmdsBuff[MAX_VOLUME_COMMANDS+128]; //Hack protect against slight overflows float volumeVertsBuff[MAX_VOLUME_VERTS+128]; lightcmd_t lightCmdsBuff[MAX_LIGHT_COMMANDS+128]; lightcmd_t lightCmdsBuffMesh[MAX_LIGHT_COMMANDS+128]; int numVolumeCmds; int numLightCmds; int numLightCmdsMesh; int numVolumeVerts; msurface_t *shadowchain; //linked list of polygons that are shadowed mesh_t *meshshadowchain; byte *lightvis; byte worldvis[MAX_MAP_LEAFS/8]; extern aabox_t worldbox; /* -DC- isn't that volumeVertsBuff ? vec3_t volumevertices[MAX_VOLUME_VERTICES];//buffer for the vertices of the shadow volume int usedvolumevertices; */ void DrawVolumeFromCmds(int *volumeCmds, lightcmd_t *lightCmds, float *volumeVerts); void DrawAttentFromCmds(lightcmd_t *lightCmds); void DrawBumpFromCmds(lightcmd_t *lightCmds); void DrawSpecularBumpFromCmds(lightcmd_t *lightCmds); void PrecalcVolumesForLight(model_t *model); int getVertexIndexFromSurf(msurface_t *surf, int index, model_t *model); qboolean R_ContributeFrame(shadowlight_t *light); /* ============= AllocShadowLight ============= */ shadowlight_t* AllocShadowLight(void) { if (numShadowLights >= MAXSHADOWLIGHTS) { return NULL; } shadowlights[numShadowLights].owner = NULL; numShadowLights++; return &shadowlights[numShadowLights-1]; } void DrawLightVolumeInfo(void) { int i; aabox_t b; if (!sh_showlightvolume.value) return; glDisable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glColor3f(0.0,0.0,1.0); for (i=0; ishadowchainfilled) continue; b = intersectBoxes(&l->box, &worldbox); drawBoxWireframe(&b); } glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); } void BoxForRadius(shadowlight_t *l) { l->box.mins[0] = l->origin[0] - l->radius; l->box.mins[1] = l->origin[1] - l->radius; l->box.mins[2] = l->origin[2] - l->radius; l->box.maxs[0] = l->origin[0] + l->radius; l->box.maxs[1] = l->origin[1] + l->radius; l->box.maxs[2] = l->origin[2] + l->radius; l->radiusv[0] = l->radius; l->radiusv[1] = l->radius; l->radiusv[2] = l->radius; //l->filtercube = 0; } /* ============= R_ShadowFromDlight ============= */ void R_ShadowFromDlight(dlight_t *light) { shadowlight_t *l; l = AllocShadowLight(); if (!l) return; VectorCopy(light->origin,l->origin); l->radius = light->radius; l->color[0] = light->color[0]; l->color[1] = light->color[1]; l->color[2] = light->color[2]; l->baseColor[0] = light->color[0]; l->baseColor[1] = light->color[1]; l->baseColor[2] = light->color[2]; l->style = 0; l->brightness = 1; l->isStatic = false; l->numVisSurf = 0; l->visSurf = NULL; l->style = light->style; l->owner = light->owner; //VectorCopy(light->angles,l->angles); //We use some different angle convention l->angles[1] = light->angles[0]; l->angles[0] = light->angles[2]; l->angles[2] = light->angles[1]; l->filtercube = light->filtercube; l->rspeed = 0; l->cubescale = 1; l->castShadow = true; //Some people will be instulted by the mere existence of this flag. if (light->pflags & PFLAG_NOSHADOW) { l->castShadow = false; } else { l->castShadow = true; } if (light->pflags & PFLAG_HALO) { l->halo = true; } else { l->halo = false; } BoxForRadius(l); } /* ============= R_MarkDLights Adds dynamic lights to the shadow light list ============= */ void R_MarkDlights (void) { int i; dlight_t *l; l = cl_dlights; for (i=0 ; idie < cl.time || !l->radius) continue; R_ShadowFromDlight(l); } } #define NUM_CUBEMAPS 64 int cubemap_tex_obj [NUM_CUBEMAPS]; /* ============= R_CubeMapLookup ============= */ int R_CubeMapLookup(int i) { if (i > NUM_CUBEMAPS) { return 0; } else { if (!cubemap_tex_obj[i]) { cubemap_tex_obj[i] = GL_LoadCubeMap(i); } return cubemap_tex_obj[i]; } } /* ============= R_ShadowFromEntity ============= */ void R_ShadowFromEntity(entity_t *ent) { shadowlight_t *l; l = AllocShadowLight(); if (!l) return; VectorCopy(ent->origin,l->origin); l->radius = 350; l->color[0] = 1; l->color[1] = 1; l->color[2] = 1; l->style = 0; l->brightness = 1; l->isStatic = false; l->numVisSurf = 0; l->visSurf = NULL; l->style = 0; l->owner = ent; l->style = ent->style; if (ent->light_lev != 0) { l->radius = ent->light_lev; } else { l->radius = 350; } VectorCopy(ent->color,l->baseColor); if ((l->baseColor[0] == 0) && (l->baseColor[1] == 0) && (l->baseColor[2] == 0)) { l->baseColor[0] = 1; l->baseColor[1] = 1; l->baseColor[2] = 1; } if (ent->skinnum >= 16) { l->filtercube = R_CubeMapLookup(ent->skinnum); } else { l->filtercube = 0; } //We use some different angle convention l->angles[1] = ent->angles[0]; l->angles[0] = ent->angles[2]; l->angles[2] = ent->angles[1]; l->rspeed = ent->alpha*512; l->cubescale = 1; //Some people will be instulted by the mere existence of this flag. if (ent->pflags & PFLAG_NOSHADOW) { l->castShadow = false; } else { l->castShadow = true; } if (ent->pflags & PFLAG_HALO) { l->halo = true; } else { l->halo = false; } BoxForRadius(l); } int cut_ent; /* ============= R_InitShadowsForFrame Do per frame intitialization for the shadows ============= */ void R_InitShadowsForFrame(void) { byte *vis; int i; numShadowLights = numStaticShadowLights; R_MarkDlights (); //add dynamic lights to the list // R_ShadowFromPlayer();//give the player some sort of torch numUsedShadowLights = 0; //if (cut_ent) Con_Printf("cut ents: %i\n",cut_ent); cut_ent = 0; Q_memset (&worldvis, 0, MAX_MAP_LEAFS/8); //all invisible vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); Q_memcpy(&worldvis, vis, MAX_MAP_LEAFS/8); for (i=0; ivisible = true; if (numUsedShadowLights < MAXUSEDSHADOWLIGHS) { usedshadowlights[numUsedShadowLights] = currentshadowlight; numUsedShadowLights++; } else { Con_Printf("R_InitShadowsForFrame: More than MAXUSEDSHADOWLIGHS lights for frame\n"); } } else { currentshadowlight->visible = false; } } } #define MAX_LEAF_LIST 32 mleaf_t *leafList[MAX_LEAF_LIST]; int numLeafList; extern vec3_t r_emins, r_emaxs; // added "extern". /* ============= R_ProjectSphere Returns the rectangle the sphere will be in when it is drawn. FIXME: This is crappy code we draw a "sprite" and project those points it should be possible to analytically derive a eq. ============= */ /* void R_ProjectSphere (shadowlight_t *light, int *rect) { int i, j; float a; vec3_t v, vp2; float rad; double minx, maxx, miny, maxy; double px, py, pz; rad = light->radius; //rect[0] = 100; //rect[1] = 100; //rect[2] = 300; //rect[3] = 300; //return; VectorSubtract (light->origin, r_origin, v); //dave - slight fix VectorSubtract (light->origin, r_origin, vp2); VectorNormalize(vp2); minx = 1000000; miny = 1000000; maxx = -1000000; maxy = -1000000; for (i=32 ; i>=0 ; i--) { a = i/32.0 * M_PI*2; for (j=0 ; j<3 ; j++) v[j] = light->origin[j] + vright[j]*cos(a)*rad + vup[j]*sin(a)*rad; gluProject(v[0], v[1], v[2], r_Dworld_matrix, r_Dproject_matrix, (GLint *) r_Iviewport, &px, &py, &pz); // added cast. if (px > maxx) maxx = px; if (px < minx) minx = px; if (py > maxy) maxy = py; if (py < miny) miny = py; } rect[0] = (int)minx; rect[1] = (int)miny; rect[2] = (int)maxx; rect[3] = (int)maxy; } */ /* ============= R_ProjectBoundingBox Returns the screen rectangle this light's bounding box will be in when it is drawn. ============= */ void R_ProjectBoundingBox (shadowlight_t *light, int *rect) { vec3_t dest; double minx, maxx, miny, maxy; double px, py, pz; int i, j, k; static float verts[9*3]; matrix_4x4 modelviewproj, world, proj; matrix_1x4 point, res; aabox_t box; minx = 100000000; miny = 100000000; maxx = -100000000; maxy = -100000000; box = intersectBoxes(&light->box, &worldbox); i=0; VectorConstruct(box.maxs[0],box.maxs[1],box.maxs[2],&verts[i]); i+=3; VectorConstruct(box.mins[0],box.maxs[1],box.maxs[2],&verts[i]); i+=3; VectorConstruct(box.maxs[0],box.mins[1],box.maxs[2],&verts[i]); i+=3; VectorConstruct(box.mins[0],box.mins[1],box.maxs[2],&verts[i]); i+=3; VectorConstruct(box.mins[0],box.mins[1],box.mins[2],&verts[i]); i+=3; VectorConstruct(box.maxs[0],box.mins[1],box.mins[2],&verts[i]); i+=3; VectorConstruct(box.maxs[0],box.maxs[1],box.mins[2],&verts[i]); i+=3; VectorConstruct(box.mins[0],box.maxs[1],box.mins[2],&verts[i]); i+=3; glGetFloatv (GL_MODELVIEW_MATRIX, &world[0][0]); // glMatrixMode(GL_PROJECTION); // glPushMatrix(); //gluPerspective(90, r_Iviewport[0]/ (float)r_Iviewport[1], 0.1, 2000.0); glGetFloatv (GL_PROJECTION_MATRIX, &proj[0][0]); // glGetDoublev (GL_PROJECTION_MATRIX, &r_Dproject_matrix[0]); Mat_Mul_4x4_4x4(world, proj, modelviewproj); for (i=0; i<8; i++) { //float s; //float *v = &verts[i*3]; point[0] = verts[i*3+0]; point[1] = verts[i*3+1]; point[2] = verts[i*3+2]; point[3] = 1.0f; Mat_Mul_1x4_4x4(point, modelviewproj, res); //gluProject(v[0], v[1], v[2], r_Dworld_matrix, r_Dproject_matrix, // (GLint *) r_Iviewport, &px, &py, &pz); //if (res[3] < 0) { //Con_Printf("%f %f\n", (float)px, (float)py); //s = (pz < 0) ? -1 : 1; px = (res[0]*(1/res[3])+1.0) * 0.5; py = (res[1]*(1/res[3])+1.0) * 0.5; px = px * r_Iviewport[2] + r_Iviewport[0]; py = py * r_Iviewport[3] + r_Iviewport[1]; //Con_Printf("%f %f\n", (float)px, (float)py); //continue; //} /* if (px < r_Iviewport[0]) px = r_Iviewport[0]; if (py < r_Iviewport[1]) py = r_Iviewport[1]; if (px > r_Iviewport[0]+r_Iviewport[2]) px = r_Iviewport[0]+r_Iviewport[2]; if (py > r_Iviewport[1]+r_Iviewport[3]) py = r_Iviewport[1]+r_Iviewport[3]; */ if (px > maxx) maxx = px; if (px < minx) minx = px; if (py > maxy) maxy = py; if (py < miny) miny = py; } /* glPopMatrix(); glGetDoublev (GL_PROJECTION_MATRIX, &r_Dproject_matrix[0]); glMatrixMode(GL_MODELVIEW);*/ /* for (i=-1; i<=1; i+=2) { for (j=-1; j<=1; j+=2) { for (k=-1; k<=1; k+=2) { MakeVertex(i*light->radius, j*light->radius, k*light->radius, light->origin, dest); Con_Printf("%f %f %f\n",dest[0],dest[1],dest[2]); gluProject(dest[0], dest[1], dest[2], r_Dworld_matrix, r_Dproject_matrix, (GLint *) r_Iviewport, &px, &py, &pz); if (px > maxx) maxx = px; if (px < minx) minx = px; if (py > maxy) maxy = py; if (py < miny) miny = py; } } } */ /* if (minx < (double)r_Iviewport[0]) minx = (double)r_Iviewport[0]; if (miny < (double)r_Iviewport[1]) miny = (double)r_Iviewport[1]; if (maxx > (double)r_Iviewport[0]+r_Iviewport[2]) maxx = (double)r_Iviewport[0]+r_Iviewport[2]; if (maxy > (double)r_Iviewport[1]+r_Iviewport[3]) maxy = (double)r_Iviewport[1]+r_Iviewport[3]; */ rect[0] = (int)minx; rect[1] = (int)miny; rect[2] = (int)maxx; rect[3] = (int)maxy; } /* ============= HasSharedLeaves Returns true if both vis arrays have shared leafs visible. FIXME: compare bytes at a time (what does quake fill the unused bits with??) ============= */ qboolean HasSharedLeafs(byte *v1, byte *v2) { int i; for (i=0 ; inumclusters; i++) { if (v1[i>>3] & (1<<(i&7))) { if (v2[i>>3] & (1<<(i&7))) return true; } } return false; } /* ============= HasVisibleClusters Returns true if the light has leaves shared with the clusters in v2. ============= */ qboolean HasVisibleClusters(shadowlight_t *l, byte *v2) { int i; for (i=0 ; inumleafs; i++) { int cluster = cl.worldmodel->leafs[i].cluster; //leaf is visible from the light? if (l->leafvis[i>>3] & (1<<(i&7))) { //cluster is visible from the camera? if (v2[cluster>>3] & (1<<(cluster&7))) return true; } } return false; } /************************************************************** Code to determine what stuff is visible to the light. ***************************************************************/ /* ============= CheckSurfInLight Returns true if the surface is lit by the light. (Of course approximately) ============= */ qboolean CheckSurfInLight(msurface_t *surf, shadowlight_t *light) { mplane_t *plane; float dist; glpoly_t *poly; //Doesn't recieve per pixel light or cast shadows... if (!(surf->flags & SURF_PPLIGHT) && (surf->flags & SURF_NOSHADOW)) { return false; } plane = surf->plane; poly = surf->polys; if (poly->lightTimestamp == r_lightTimestamp) { return false; } switch (plane->type) { case PLANE_X: dist = light->origin[0] - plane->dist; break; case PLANE_Y: dist = light->origin[1] - plane->dist; break; case PLANE_Z: dist = light->origin[2] - plane->dist; break; default: dist = DotProduct (light->origin, plane->normal) - plane->dist; break; } //the normals are flipped when surf_planeback is 1 if (((surf->flags & SURF_PLANEBACK) && (dist > 0)) || (!(surf->flags & SURF_PLANEBACK) && (dist < 0))) { return false; } //the normals are flipped when surf_planeback is 1 if ( abs(dist) > light->radius) { return false; } //Doesn't intersect light volume if (light->isStatic) { vec3_t mins = {10e10,10e10,10e10}; vec3_t maxs = {-10e10,-10e10,-10e10}; int i; for (i=0; inumindecies; i++) { VectorMax(maxs,globalVertexTable[poly->indecies[i]].position,maxs); VectorMin(mins,globalVertexTable[poly->indecies[i]].position,mins); } if (!intersectsMinsMaxs(&light->box, mins, maxs)) { return false; } } poly->lightTimestamp = r_lightTimestamp; return true; } qboolean CheckMeshInLight(mesh_t *mesh, shadowlight_t *light) { //Doesn't recieve per pixel light or cast shadows... if (!(mesh->shader->shader->flags & SURF_PPLIGHT) && (mesh->shader->shader->flags & SURF_NOSHADOW)) { return false; } //Already flagged if (mesh->lightTimestamp == r_lightTimestamp) { return false; } //Doesn't intersect light volume if (!intersectsMinsMaxs(&light->box, mesh->mins, mesh->maxs)) { return false; } mesh->lightTimestamp = r_lightTimestamp; return true; } /* ============= R_MarkLightSurfaces Fills the shadow chain with polygons we should consider. Polygons that will be added are: 1. In the light volume. (sphere) 2. "Visible" to the light. Visible is: a. facing the light (dotprod > 0) b. in a leaf that is visible from the light's leaf. (based on vis data) This is crude for satic lights we use extra tricks (svbsp / revis) to reduce the number of polyons. ============= */ void R_MarkLightSurfaces (shadowlight_t *light, mnode_t *node) { mplane_t *plane; float dist; msurface_t **surf; mleaf_t *leaf; int c,leafindex; mesh_t *mesh; //Not in the current light's bouding box if (!intersectsMinsMaxs(&light->box,node->minmaxs,node->minmaxs+3)) return; if (node->contents & CONTENTS_LEAF) { //we are in a leaf leaf = (mleaf_t *)node; leafindex = leaf->cluster; //is this leaf visible from the light if (!(lightvis[leafindex>>3] & (1<<(leafindex&7)))) { return; } c = leaf->nummarksurfaces; surf = leaf->firstmarksurface; for (c=0; cnummarksurfaces; c++, surf++) { if (CheckSurfInLight ((*surf), light)) { (*surf)->shadowchain = shadowchain; shadowchain = (*surf); //svBsp_NumKeptPolys++; } } c = leaf->nummeshes; for (c=0; cnummeshes; c++) { mesh = &cl.worldmodel->meshes[cl.worldmodel->leafmeshes[leaf->firstmesh+c]]; if (CheckMeshInLight(mesh, light)) { mesh->shadowchain = meshshadowchain; meshshadowchain = mesh; } } return; } /* plane = node->plane; dist = DotProduct (light->origin, plane->normal) - plane->dist; if (dist > light->radius) { R_MarkShadowCasting (light, node->children[0]); return; } if (dist < -light->radius) { R_MarkShadowCasting (light, node->children[1]); return; } */ R_MarkLightSurfaces (light, node->children[0]); R_MarkLightSurfaces (light, node->children[1]); } /* ============= CheckEntityInLight Checks if Entity is in the light's volume and visible to the light (using vis) ============= */ qboolean CheckEntityInLight(entity_t *ent) { int i, leafindex; model_t *entmodel = ent->model; mleaf_t *leaf; vec3_t mins, maxs; VectorAdd(ent->origin, entmodel->mins, mins); VectorAdd(ent->origin, entmodel->maxs, maxs); if (intersectsMinsMaxs(¤tshadowlight->box,mins,maxs)) { if (sh_noefrags.value) return true; for (i=0; inumleafs;i++) { leafindex = ent->leafnums[i]; //leaf ent is in is visible from light leaf = cl.worldmodel->leafs+leafindex; if (currentshadowlight->entclustervis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) { return true; } } if (ent->numleafs == 0) { //Con_Printf("Ent with no leafs"); return true; } return false; } return false; } /* ============= R_MarkLightEntities Adds shadow casting ents to the list ============= */ void R_MarkLightEntities() { int i; vec3_t mins, maxs; if (!cg_showentities.value) return; cl_numlightvisedicts = 0; for (i=0 ; imodel->flags & EF_NOSHADOW) && (currententity->model->flags & EF_FULLBRIGHT)) { continue; } if (mirror) { VectorAdd (currententity->origin,currententity->model->mins, mins); VectorAdd (currententity->origin,currententity->model->maxs, maxs); if (mirror_clipside == BoxOnPlaneSide(mins, maxs, mirror_plane)) { continue; } if ( BoxOnPlaneSide(mins, maxs, &mirror_far_plane) == 1) { return; } } //Dont cast shadows with the ent this light is attached to because //when the light is partially in the model shadows will look weird. //FIXME: Model is not lit by its own light. if (currententity != currentshadowlight->owner) { if (CheckEntityInLight(currententity)) { currententity->lightTimestamp = r_lightTimestamp; cl_lightvisedicts[cl_numlightvisedicts] = currententity; cl_numlightvisedicts++; } else cut_ent++; } } } /* ============= R_ContributeFrame Returns true if the light should be rendered. We try to throw away as much ligts as possible here. Most of the lights are culled here but some are culled further down the pipeline (but rarely). ============= */ void boxScreenSpaceRect(aabox_t *b, int *rect); qboolean R_ContributeFrame (shadowlight_t *light) { mleaf_t *lightleaf; float dist; aabox_t lightbox; float b = d_lightstylevalue[light->style]/255.0; light->color[0] = light->baseColor[0] * b; light->color[1] = light->baseColor[1] * b; light->color[2] = light->baseColor[2] * b; //verry soft light, don't bother. if (b < 0.1) { //Con_Printf("Culled: Weak light\n"); return false; } //frustum scissor testing /* dist = SphereInFrustum(light->origin, light->radius); if (dist == 0) { //whole sphere is out ouf frustum so cut it. return false; } */ if (R_CullBox(light->box.mins, light->box.maxs)) { //Con_Printf("Culled: Box outside frustum\n"); return false; } lightbox.mins[0] = light->origin[0] - light->radius; lightbox.mins[1] = light->origin[1] - light->radius; lightbox.mins[2] = light->origin[2] - light->radius; lightbox.maxs[0] = light->origin[0] + light->radius; lightbox.maxs[1] = light->origin[1] + light->radius; lightbox.maxs[2] = light->origin[2] + light->radius; //fully/partially in frustum if (!sh_noscissor.value) { //vec3_t dst; //float d; //VectorSubtract (light->origin, r_refdef.vieworg, dst); //d = Length (dst); if (/*d > light->radius*/!intersectsBoxPoint(&light->box, r_refdef.vieworg)) { //R_ProjectBoundingBox (light, light->scizz.coords); boxScreenSpaceRect(&light->box, light->scizz.coords); //glScissor(light->scizz.coords[0], light->scizz.coords[1], // light->scizz.coords[2]-light->scizz.coords[0], light->scizz.coords[3]-light->scizz.coords[1]); } else { //viewport is ofs/width based light->scizz.coords[0] = r_Iviewport[0]; light->scizz.coords[1] = r_Iviewport[1]; light->scizz.coords[2] = r_Iviewport[0]+r_Iviewport[2]; light->scizz.coords[3] = r_Iviewport[1]+r_Iviewport[3]; //glScissor(r_Iviewport[0], r_Iviewport[1], r_Iviewport[2], r_Iviewport[3]); } } //r_lightTimestamp++; shadowchain = NULL; meshshadowchain = NULL; if (light->isStatic) { lightvis = &light->leafvis[0]; } else { lightleaf = Mod_PointInLeaf (light->origin, cl.worldmodel); lightvis = Mod_LeafPVS (lightleaf, cl.worldmodel); Q_memcpy(&light->leafvis,lightvis,MAX_MAP_LEAFS/8); Q_memcpy(&light->entclustervis, lightvis, MAX_MAP_LEAFS/8); light->area = lightleaf->area; } //not in a visible area => skip it if (!(r_refdef.areabits[light->area>>3] & (1<<(light->area&7)))) { //Con_Printf("Culled: Not in same area\n"); return false; } /* if (!intersectsBox(&worldbox, &lightbox)) { Con_Printf("Culled: Box outside the visible world\n"); return false; } */ if (light->isStatic) { qboolean b = HasVisibleClusters(light,&worldvis[0]); //if (!b) Con_Printf("Culled: No visible clusters\n"); return b; } else { qboolean b = HasSharedLeafs(lightvis,&worldvis[0]); //if (!b) Con_Printf("Culled: No shared leafs\n"); return b; } /* if (HasSharedLeafs(lightvis,&worldvis[0])) { //light->visible = true; //numUsedShadowLights++; //mark shadow casting ents //MarkShadowEntities(); //mark shadow casting polygons //if (!light->isStatic) { // R_MarkShadowCasting ( light, cl.worldmodel->nodes); //} else { // return true; //} //return (shadowchain) ? true : false; return true; } else { Con_Printf("No shared leafs\n"); return false; } */ } /* ============= R_FillLightChains Returns true if the light should be rendered. It's here lights are seldomly removed further down the pipeline. ============= */ qboolean R_FillLightChains (shadowlight_t *light) { r_lightTimestamp++; shadowchain = NULL; meshshadowchain = NULL; //lightvis = &light->vis[0]; //numUsedShadowLights++; //mark shadow casting ents R_MarkLightEntities(); //mark shadow casting polygons if (!light->isStatic) { R_MarkLightSurfaces ( light, cl.worldmodel->nodes); } else { return true; } return (shadowchain || meshshadowchain) ? true : false; } /************************************************************** Shadow volume calculations / drawing ***************************************************************/ void *VolumeVertsPointer; /* ============= R_ConstructShadowVolume Calculate the shadow volume commands for the light. (only if dynamic) ============= */ void R_ConstructShadowVolume(shadowlight_t *light) { if (!light->isStatic) { PrecalcVolumesForLight(cl.worldmodel); light->volumeCmds = &volumeCmdsBuff[0]; light->volumeVerts = &volumeVertsBuff[0]; light->lightCmds = &lightCmdsBuff[0]; light->lightCmdsMesh = &lightCmdsBuffMesh[0]; light->numlightcmdsmesh = numLightCmdsMesh; light->numlightcmds = numLightCmds; } VolumeVertsPointer = light->volumeVerts; if (gl_var) { if (light->numVolumeVerts < AGP_BUFFER_SIZE/4) { memcpy(AGP_Buffer,light->volumeVerts,light->numVolumeVerts*4); VolumeVertsPointer = AGP_Buffer; } } } /* ============= R_DrawShadowVolume Draws the shadow volume, for statics this is the precalc one for dynamics this is the R_ConstructShadowVolume one. ============= */ void R_DrawShadowVolume(shadowlight_t *light) { GL_SelectTexture(GL_TEXTURE0_ARB); glDisable(GL_TEXTURE_2D); GL_SelectTexture(GL_TEXTURE1_ARB); glDisable(GL_TEXTURE_2D); GL_SelectTexture(GL_TEXTURE2_ARB); glDisable(GL_TEXTURE_2D); GL_SelectTexture(GL_TEXTURE3_ARB); glDisable(GL_TEXTURE_2D); /* if (!light->isStatic) { glColor3f(0,1,0); DrawVolumeFromCmds (&volumeCmdsBuff[0], &lightCmdsBuff[0]); } else { */ glColor3f(1,0,0); DrawVolumeFromCmds (light->volumeCmds, light->lightCmds, VolumeVertsPointer); // } glColor3f(1,1,1); GL_SelectTexture(GL_TEXTURE0_ARB); glEnable(GL_TEXTURE_2D); } /************************************************************** Brush model shadow volume support ***************************************************************/ /* ============= R_MarkBrushModelSurfaces Set the light timestamps of the brush model. ============= */ void R_MarkBrushModelSurfaces(entity_t *e) { vec3_t mins, maxs; int i; msurface_t *psurf; model_t *clmodel; qboolean rotated; vec3_t oldlightorigin; //backup light origin since we will have to translate //light into model space VectorCopy (currentshadowlight->origin, oldlightorigin); currententity = e; currenttexture = -1; clmodel = e->model; if (e->angles[0] || e->angles[1] || e->angles[2]) { rotated = true; for (i=0 ; i<3 ; i++) { mins[i] = e->origin[i] - clmodel->radius; maxs[i] = e->origin[i] + clmodel->radius; } } else { rotated = false; VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); } VectorSubtract (r_refdef.vieworg, e->origin, modelorg); VectorSubtract (currentshadowlight->origin, e->origin, currentshadowlight->origin); if (rotated) { vec3_t temp; vec3_t forward, right, up; VectorCopy (modelorg, temp); AngleVectors (e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); VectorCopy (currentshadowlight->origin, temp); currentshadowlight->origin[0] = DotProduct (temp, forward); currentshadowlight->origin[1] = -DotProduct (temp, right); currentshadowlight->origin[2] = DotProduct (temp, up); } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; glPushMatrix (); e->angles[0] = -e->angles[0]; // stupid quake bug R_RotateForEntity (e); e->angles[0] = -e->angles[0]; // stupid quake bug for (i=0 ; inummodelsurfaces ; i++, psurf++) { CheckSurfInLight(psurf, currentshadowlight); } VectorCopy(oldlightorigin,currentshadowlight->origin); glPopMatrix (); } /* ============= R_DrawBrushModelVolumes Draw the shadow volumes of the brush model. They are dynamically calculated. ============= */ /* void R_DrawBrushModelVolumes(entity_t *e) { int j, k; vec3_t mins, maxs; int i, numsurfaces; msurface_t *surf; float dot; mplane_t *pplane; model_t *clmodel; qboolean rotated; glpoly_t *poly; vec3_t v1,*v2; float scale; qboolean shadow; vec3_t temp[32]; vec3_t oldlightorigin; //backup light origin since we will have to translate //light into model space VectorCopy (currentshadowlight->origin, oldlightorigin); currententity = e; currenttexture = -1; clmodel = e->model; if (e->angles[0] || e->angles[1] || e->angles[2]) { rotated = true; for (i=0 ; i<3 ; i++) { mins[i] = e->origin[i] - clmodel->radius; maxs[i] = e->origin[i] + clmodel->radius; } } else { rotated = false; VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); } VectorSubtract (r_refdef.vieworg, e->origin, modelorg); VectorSubtract (currentshadowlight->origin, e->origin, currentshadowlight->origin); if (rotated) { vec3_t temp; vec3_t forward, right, up; VectorCopy (modelorg, temp); AngleVectors (e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); VectorCopy (currentshadowlight->origin, temp); currentshadowlight->origin[0] = DotProduct (temp, forward); currentshadowlight->origin[1] = -DotProduct (temp, right); currentshadowlight->origin[2] = DotProduct (temp, up); } surf = &clmodel->surfaces[clmodel->firstmodelsurface]; glPushMatrix (); e->angles[0] = -e->angles[0]; // stupid quake bug R_RotateForEntity (e); e->angles[0] = -e->angles[0]; // stupid quake bug for (i=0 ; inummodelsurfaces ; i++, surf++) { if (surf->polys->lightTimestamp != r_lightTimestamp) continue; poly = surf->polys; for (j=0 ; jnumedges ; j++) { v2 = (vec3_t *)&poly->verts[j]; VectorSubtract ( (*v2) ,currentshadowlight->origin, v1); scale = Length (v1); if (sh_visiblevolumes.value) VectorScale (v1, (1/scale)*50, v1); else VectorScale (v1, (1/scale)*currentshadowlight->radius*2, v1); VectorAdd (v1, (*v2), temp[j]); } //check if neighbouring polygons are shadowed for (j=0 ; jnumedges ; j++) { shadow = false; if (poly->neighbours[j] != NULL) { if ( poly->neighbours[j]->lightTimestamp != poly->lightTimestamp) { shadow = true; } } else { shadow = true; } if (shadow) { //we extend the shadow volumes by projecting them on the //light's sphere. //This sometimes gives problems when the light is verry close to a big //polygon. But further extending the volume wastes fill rate. //So ill have to fix it. glBegin(GL_QUAD_STRIP); glVertex3fv(&poly->verts[j][0]); glVertex3fv(&temp[j][0]); glVertex3fv(&poly->verts[((j+1)% poly->numverts)][0]); glVertex3fv(&temp[((j+1)% poly->numverts)][0]); glEnd(); } } //Draw near light cap glBegin(GL_POLYGON); for (j=0; jnumedges ; j++) { glVertex3fv(&poly->verts[j][0]); } glEnd(); //Draw extruded cap glBegin(GL_POLYGON); for (j=surf->numedges-1; j>=0 ; j--) { glVertex3fv(&temp[j][0]); } glEnd(); } //PrecalcVolumesForLight(clmodel); VectorCopy(oldlightorigin,currentshadowlight->origin); glPopMatrix (); } */ /* ============= R_DrawBrushModelVolumes Draw the shadow volumes of the brush model. They are dynamically calculated. ============= */ void R_DrawBrushModelVolumes(entity_t *e) { model_t *model = e->model; msurface_t *surf; glpoly_t *poly; int i, j, count; brushlightinstant_t *ins = e->brushlightinstant; float *v; count = 0; glPushMatrix (); e->angles[0] = -e->angles[0]; // stupid quake bug R_RotateForEntity (e); e->angles[0] = -e->angles[0]; // stupid quake bug surf = &model->surfaces[model->firstmodelsurface]; for (i=0; inummodelsurfaces; i++, surf++) { if (!ins->polygonVis[i]) continue; if (surf->flags & SURF_NOSHADOW) continue; poly = surf->polys; //extrude edges for (j=0 ; jnumneighbours ; j++) { mneighbour_t *neigh = &poly->neighbours[j]; if (ins->neighbourVis[count+j]) { glBegin(GL_QUAD_STRIP); //Note: The neighbour p1,p2 are absolute vertex indecies, for the extruded ones //we want polygon relative indecies. //glVertex3fv(&poly->verts[j][0]); glVertex3fv((float *)(&globalVertexTable[neigh->p1])); glVertex3fv(&ins->extvertices[count+neigh->p1-poly->firstvertex][0]); //glVertex3fv(&poly->verts[((j+1)% poly->numverts)][0]); glVertex3fv((float *)(&globalVertexTable[neigh->p2])); glVertex3fv(&ins->extvertices[count+neigh->p2-poly->firstvertex][0]); glEnd(); } } //Draw near light cap /* glBegin(GL_TRIANGLE_FAN); for (j=0; jnumedges ; j++) { //glVertex3fv(&poly->verts[j][0]); glVertex3fv((float *)(&globalVertexTable[surf->polys->firstvertex+j])); } glEnd(); */ glBegin(GL_TRIANGLES); //v = surf->polys->verts[0]; for (j=0; jpolys->numindecies; j++) { v = (float *)(&globalVertexTable[surf->polys->indecies[j]]); glVertex3fv(&v[0]); } glEnd(); //Draw extruded cap /* glBegin(GL_TRIANGLE_FAN); for (j=surf->numedges-1; j>=0 ; j--) { glVertex3fv(&ins->extvertices[count+j][0]); } glEnd(); */ glBegin(GL_TRIANGLES); //v = surf->polys->verts[0]; for (j=surf->polys->numindecies-1; j>=0; j--) { v = (float *)(&ins->extvertices[count+surf->polys->indecies[j]-surf->polys->firstvertex]); glVertex3fv(&v[0]); } glEnd(); count+=surf->numedges; } glPopMatrix (); } /************************************************************** Shadow volume precalculation & storing ***************************************************************/ /* ============= getVertexIndexFromSurf Gets index of the i'th vertex of the surface in the models vertex array ============= */ int getVertexIndexFromSurf(msurface_t *surf, int index, model_t *model) { int lindex = model->surfedges[surf->firstedge + index]; medge_t *r_pedge; if (lindex > 0) { r_pedge = &model->edges[lindex]; //if (r_pedge->v[0] == 0) Con_Printf("moord en brand"); return r_pedge->v[0]; } else { r_pedge = &model->edges[-lindex]; //if (r_pedge->v[1] == 0) Con_Printf("moord en brand"); return r_pedge->v[1]; } } /* ============= PrecalcVolumesForLight This will create arrays with gl-commands that define the shadow volumes (something similar to the mesh arrays that are created.) They are stored for static lights and recalculated every frame for dynamic ones. Non calculated vertices are not saved in the list but the index in the vertex array of the model is saved. We store them in volumeCmdsBuff and lightCmdsBuff ============= */ void PrecalcVolumesForLight(model_t *model) { msurface_t *surf; int *volumeCmds = &volumeCmdsBuff[0]; lightcmd_t *lightCmds = &lightCmdsBuff[0]; lightcmd_t *lightCmdsMesh = &lightCmdsBuffMesh[0]; float *volumeVerts = &volumeVertsBuff[0]; int volumePos = 0; int lightPos = 0; int lightPosMesh = 0; int vertPos = 0; int startVerts, startNearVerts, numPos = 0, stripLen = 0, i; glpoly_t *poly; qboolean lastshadow; vec3_t v1, *v2, vert1; float scale; qboolean shadow; vec3_t *s, *t, nearPt, nearToVert; float dist, colorscale; mplane_t *splitplane; int j; float *v; mesh_t *mesh; surf = shadowchain; //1. Calculate shadow volumes while (surf) { poly = surf->polys; /* if (( surf->flags & SURF_DRAWTURB ) || ( surf->flags & SURF_DRAWSKY )) { surf = surf->shadowchain; Con_Printf ("Water/Sky in shadow chain!!"); continue; } */ if (surf->shader->shader->flags & SURF_NOSHADOW) goto noshadow; //a. far cap // volumeCmds[volumePos++] = GL_POLYGON; volumeCmds[volumePos++] = GL_TRIANGLES; volumeCmds[volumePos++] = poly->numindecies; //extrude verts startVerts = (int)vertPos/3; for (i=0 ; inumedges ; i++) { //v2 = (vec3_t *)&poly->verts[i]; v2 = (vec3_t *)(&globalVertexTable[surf->polys->firstvertex+i]); VectorSubtract ( (*v2), currentshadowlight->origin, v1); scale = Length (v1); if (sh_visiblevolumes.value) { //make them short so that we see them VectorScale (v1, (1/scale)*50, v1); } else { //we don't have to be afraid they will clip with the far plane //since we use the infinite matrix trick VectorScale (v1, (1/scale)* currentshadowlight->radius*10, v1); } VectorAdd (v1, (*v2) ,vert1); VectorCopy (vert1, ((vec3_t *)(volumeVerts+vertPos))[0]); vertPos+=3; } //create indexes for (i=poly->numindecies-1; i>=0; i--) { volumeCmds[volumePos++] = startVerts+poly->indecies[i]-poly->firstvertex; } //copy vertices startNearVerts = (int)vertPos/3; for (i=0 ; inumedges ; i++) { //v2 = (vec3_t *)&poly->verts[i]; v2 = (vec3_t *)(&globalVertexTable[surf->polys->firstvertex+i]); /*(float)*/volumeVerts[vertPos++] = (*v2)[0]; // lvalue cast. what da...? /*(float)*/volumeVerts[vertPos++] = (*v2)[1]; // a float is a float is a... /*(float)*/volumeVerts[vertPos++] = (*v2)[2]; } if (vertPos > MAX_VOLUME_VERTS) { Con_Printf ("More than MAX_VOLUME_VERTS vetices! %i\n", volumePos); break; } //b. borders of volume //we make quad strips if we have continuous borders lastshadow = false; for (i=0 ; inumneighbours ; i++) { mneighbour_t *neigh = &poly->neighbours[i]; shadow = false; if (neigh->n != NULL) { if ( neigh->n->lightTimestamp != poly->lightTimestamp) { shadow = true; } } else { shadow = true; } //if (shadow) { // if (!lastshadow) { //begin new strip volumeCmds[volumePos++] = GL_QUAD_STRIP; numPos = volumePos; volumeCmds[volumePos++] = 4; stripLen = 2; //copy vertices volumeCmds[volumePos++] = startNearVerts+neigh->p1-poly->firstvertex;//-getVertexIndexFromSurf(surf, i, model); volumeCmds[volumePos++] = startVerts+neigh->p1-poly->firstvertex; // } volumeCmds[volumePos++] = startNearVerts+neigh->p2-poly->firstvertex;//-getVertexIndexFromSurf(surf, (i+1)%poly->numverts, model); volumeCmds[volumePos++] = startVerts+neigh->p2-poly->firstvertex; stripLen+=2; // } else { // if (lastshadow) { //close list up volumeCmds[numPos] = stripLen; // } // } // lastshadow = shadow; } if (lastshadow) { //close list up volumeCmds[numPos] = stripLen; } if (volumePos > MAX_VOLUME_COMMANDS) { Con_Printf ("More than MAX_VOLUME_COMMANDS commands! %i\n", volumePos); break; } noshadow: lightCmds[lightPos++].asVoid = surf; //c. glow surfaces/texture coordinates //leftright vectors of plane /* s = (vec3_t *)&surf->tangent; t = (vec3_t *)&surf->binormal; //VectorNormalize(*s); //VectorNormalize(*t); splitplane = surf->plane; dist = DotProduct (currentshadowlight->origin, splitplane->normal) - splitplane->dist; dist = abs(dist); if (dist > currentshadowlight->radius) Con_Printf("Polygon to far\n"); ProjectPlane (currentshadowlight->origin, (*s), (*t), nearPt); scale = 1 /((2 * currentshadowlight->radius) - dist); colorscale = (1 - (dist / currentshadowlight->radius)); if (colorscale <0) colorscale = 0; lightCmds[lightPos++].asInt = GL_TRIANGLE_FAN; lightCmds[lightPos++].asVoid = surf; lightCmds[lightPos++].asFloat = currentshadowlight->color[0]*colorscale; lightCmds[lightPos++].asFloat = currentshadowlight->color[1]*colorscale; lightCmds[lightPos++].asFloat = currentshadowlight->color[2]*colorscale; lightCmds[lightPos++].asFloat = colorscale; //v = poly->verts[0]; v = (float *)(&globalVertexTable[surf->polys->firstvertex]); for (j=0 ; jnumverts ; j++, v+= VERTEXSIZE) { // Project the light image onto the face VectorSubtract (v, nearPt, nearToVert); // Get our texture coordinates, transform into tangent plane lightCmds[lightPos++].asVec = DotProduct (nearToVert, (*s)) * scale + 0.5; lightCmds[lightPos++].asVec = DotProduct (nearToVert, (*t)) * scale + 0.5; //calculate local light vector and put it into tangent space { vec3_t lightDir, tsLightDir; VectorSubtract( currentshadowlight->origin,v,lightDir); if (surf->flags & SURF_PLANEBACK) { tsLightDir[2] = -DotProduct(lightDir,surf->plane->normal); } else { tsLightDir[2] = DotProduct(lightDir,surf->plane->normal); } tsLightDir[1] = -DotProduct(lightDir,(*t)); tsLightDir[0] = DotProduct(lightDir,(*s)); lightCmds[lightPos++].asVec = tsLightDir[0]; lightCmds[lightPos++].asVec = tsLightDir[1]; lightCmds[lightPos++].asVec = tsLightDir[2]; } } */ if (lightPos > MAX_LIGHT_COMMANDS) { Con_Printf ("More than MAX_LIGHT_COMMANDS commands %i\n", lightPos); break; } surf = surf->shadowchain; } lightPosMesh = 0; mesh = meshshadowchain; while (mesh) { lightCmdsMesh[lightPosMesh++].asVoid = mesh; mesh = mesh->shadowchain; } //Con_Printf("used %i\n",volumePos); //finish them off with 0 lightCmds[lightPos++].asVoid = NULL; lightCmdsMesh[lightPosMesh++].asVoid = NULL; volumeCmds[volumePos++] = 0; numLightCmds = lightPos; numLightCmdsMesh = lightPosMesh; numVolumeCmds = volumePos; numVolumeVerts = vertPos; } /* ============= DrawVolumeFromCmds Draws the generated commands as shadow volumes ============= */ void DrawVolumeFromCmds(int *volumeCmds, lightcmd_t *lightCmds, float *volumeVerts) { int command, num, i; int volumePos = 0; int lightPos = 0; // int count = 0; // no longer required. msurface_t *surf; float *v; glVertexPointer(3, GL_FLOAT, 0, volumeVerts); glEnableClientState(GL_VERTEX_ARRAY); while (1) { command = volumeCmds[volumePos++]; if (command == 0) break; //end of list num = volumeCmds[volumePos++]; glDrawElements(command,num,GL_UNSIGNED_INT,&volumeCmds[volumePos]); volumePos+=num; /*glBegin(command); if ((command == GL_QUAD_STRIP) || (command == GL_QUADS)) { for (i=0; i no longer required. } glDisableClientState(GL_VERTEX_ARRAY); //Con_Printf("%i objects drawn\n",count); if (sh_visiblevolumes.value) return; while (1) { /* command = lightCmds[lightPos++].asInt; if (command == 0) break; //end of list surf = lightCmds[lightPos++].asVoid; lightPos+=4; //skip color num = surf->polys->numverts; */ surf = lightCmds[lightPos++].asVoid; if (surf == NULL) break; if (surf->shader->shader->flags & SURF_NOSHADOW) continue; glBegin(GL_TRIANGLES); //v = surf->polys->verts[0]; for (i=0; ipolys->numindecies; i++) { v = (float *)(&globalVertexTable[surf->polys->indecies[i]]); glVertex3fv(&v[0]); } glEnd(); /* glBegin(command); //v = surf->polys->verts[0]; v = (float *)(&globalVertexTable[surf->polys->firstvertex]); for (i=0; inumtriangles > MAXALIASTRIS*2) return; if (mesh->numvertices > MAXALIASVERTS) return; extrudeTimeStamp++; //calculate visibility for (i=0; inumtriangles; i++) { d = DotProduct(mesh->triplanes[i].normal, light->origin) - mesh->triplanes[i].dist; if (d > 0) triangleVis[i] = true; else triangleVis[i] = false; } //extude vertices for (i=0; inumtriangles; i++) { if (triangleVis[i]) {//backfacing extrude it! for (j=0; j<3; j++) { int index = mesh->indecies[i*3+j]; if (extrudedTimestamp[index] == extrudeTimeStamp) continue; extrudedTimestamp[index] = extrudeTimeStamp; v1 = &extrudedVerts[index]; VectorCopy(globalVertexTable[mesh->firstvertex+index].position,v2); VectorSubtract (v2, light->origin, (*v1)); scale = Length ((*v1)); if (sh_visiblevolumes.value) { //make them short so that we see them VectorScale ((*v1), (1/scale)* 70, (*v1)); } else { //we don't have to be afraid they will clip with the far plane //since we use the infinite matrix trick VectorScale ((*v1), (1/scale)* light->radius*10, (*v1)); } VectorAdd ((*v1), v2 ,(*v1)); } } } } void DrawMeshVolume(mesh_t *mesh) { int i, j; if (mesh->numtriangles > MAXALIASTRIS*2) return; if (mesh->numvertices > MAXALIASVERTS) return; for (i=0; inumtriangles; i++) { if (triangleVis[i]) { for (j=0; j<3; j++) { qboolean shadow = false; if (mesh->neighbours[i*3+j] == -1) { shadow = true; } else if (!triangleVis[mesh->neighbours[i*3+j]]) { shadow = true; } if (shadow) { int index = mesh->indecies[i*3+j]; glBegin(GL_QUAD_STRIP); glVertex3fv(&globalVertexTable[mesh->firstvertex+index].position[0]); glVertex3fv(&extrudedVerts[index][0]); index = mesh->indecies[i*3+(j+1)%3]; glVertex3fv(&globalVertexTable[mesh->firstvertex+index].position[0]); glVertex3fv(&extrudedVerts[index][0]); glEnd(); } } /* } } glBegin(GL_TRIANGLES); for (i=0; inumtriangles; i++) { if (triangleVis[i]) { */ glBegin(GL_TRIANGLES); for (j=0; j<3; j++) { int index = mesh->indecies[i*3+j]; glVertex3fv(&globalVertexTable[mesh->firstvertex+index].position[0]); } glEnd(); glBegin(GL_TRIANGLES); for (j=2; j>=0; j--) { int index = mesh->indecies[i*3+j]; glVertex3fv(&extrudedVerts[index][0]); } glEnd(); } } //glEnd(); } void StencilMeshVolume(mesh_t *mesh) { if (mesh->shader->shader->flags & SURF_NOSHADOW) return; SetupMeshToLightVisibility(currentshadowlight, mesh); // //Pass 1 increase // glCullFace(GL_BACK); glStencilOp(GL_KEEP, GL_INCR, GL_KEEP); //glCullFace(GL_FRONT); DrawMeshVolume(mesh); // // Second Pass. Decrease Stencil Value In The Shadow // glCullFace(GL_FRONT); glStencilOp(GL_KEEP, GL_DECR, GL_KEEP); //glCullFace(GL_BACK); DrawMeshVolume(mesh); glCullFace(GL_BACK); } void StencilMeshVolumes() { int i; for (i=0; inumlightcmdsmesh-1; i++) { StencilMeshVolume((mesh_t *)currentshadowlight->lightCmdsMesh[i].asVoid); } } /************************************************************** Stuff that should be elsewhere ***************************************************************/ /* ============= R_RenderGlow Render a halo around a light. The idea is similar to the UT2003 lensflares. ============= */ //speed at wich halo grows fainter when it gets occluded #define HALO_FALLOF 2 //the scale the alpa is multipied with (none is verry bright) #define HALO_ALPHA_SCALE 0.7 //the maximum radius (in pixels) of the halo #define HALO_SIZE 30 //the distance of the eye at wich the halo stops shrinking #define HALO_MIN_DIST 300 void R_RenderGlow (shadowlight_t *light) { vec3_t hit = {0, 0, 0}; double x,y,z; float fdist,realz; int ofsx, ofsy; qboolean hitWorld; if (!light->halo || gl_wireframe.value) return; //trace a from the eye to the light source TraceLine (r_refdef.vieworg, light->origin, hit); //if it's not visible anymore slowly fade it if (Length(hit) != 0) { light->haloalpha = light->haloalpha - (cl.time - cl.oldtime)*HALO_FALLOF; if (light->haloalpha < 0) return; hitWorld = true; } else hitWorld = false; /* VectorSubtract(r_refdef.vieworg, light->origin, dist); fdist = Length(dist); if (fdist < HALO_MIN_DIST) fdist = HALO_MIN_DIST; fdist = (1-1/fdist)*HALO_SIZE; */ fdist = HALO_SIZE; gluProject(light->origin[0], light->origin[1], light->origin[2], r_Dworld_matrix, r_Dproject_matrix, (GLint *) r_Iviewport, &x, &y, &z); // added cast. //Con_Printf("Viewp %i %i %i %i\n",r_Iviewport[0],r_Iviewport[1],r_Iviewport[2],r_Iviewport[3]); if (!hitWorld) { //we didn't hit any bsp try to read the z buffer (wich is totally utterly freakingly // über evil!) glReadPixels((int)x,(int)y,1,1,GL_DEPTH_COMPONENT,GL_FLOAT,&realz); if (realz < z) { light->haloalpha = light->haloalpha - (cl.time - cl.oldtime)*HALO_FALLOF; if (light->haloalpha < 0) return; } else { //nothing in the way make it fully bright light->haloalpha = 1.0; } } ofsx = r_Iviewport[0]; ofsy = r_Iviewport[1]; x = x; y = glheight-y; x = (x/(float)glwidth)*vid.width; y = (y/(float)glheight)*vid.height; //glDisable (GL_TEXTURE_2D) GL_Bind(halo_texture_object); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glShadeModel (GL_SMOOTH); glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE); glDisable(GL_ALPHA_TEST); glBegin (GL_QUADS); glColor4f(light->color[0],light->color[1],light->color[2],light->haloalpha*HALO_ALPHA_SCALE); //glColor4f(1,1,1,light->haloalpha); glTexCoord2f (0, 0); glVertex2f (x-fdist, y-fdist); glTexCoord2f (1, 0); glVertex2f (x+fdist, y-fdist); glTexCoord2f (1, 1); glVertex2f (x+fdist, y+fdist); glTexCoord2f (0, 1); glVertex2f (x-fdist, y+fdist); glEnd (); /* rad = 40;//light->radius * 0.35; VectorSubtract (light->origin, r_origin, v); //dave - slight fix VectorSubtract (light->origin, r_origin, vp2); VectorNormalize(vp2); glBegin (GL_TRIANGLE_FAN); glColor3f (light->color[0]*0.2,light->color[1]*0.2,light->color[2]*0.2); for (i=0 ; i<3 ; i++) v[i] = light->origin[i] - vp2[i]*rad; glVertex3fv (v); glColor3f (0,0,0); for (i=16 ; i>=0 ; i--) { a = i/16.0 * M_PI*2; for (j=0 ; j<3 ; j++) v[j] = light->origin[j] + vright[j]*cos(a)*rad + vup[j]*sin(a)*rad; glVertex3fv (v); } glEnd (); */ glEnable(GL_ALPHA_TEST); glColor3f (1,1,1); //glDisable (GL_BLEND); //glEnable (GL_TEXTURE_2D); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask (1); glColor3f (1,1,1); } /************************************************************** Shadow volume bsp's Some of this suff should be put in gl_svbsp.c ***************************************************************/ svnode_t *currentlightroot; vec3_t testvect = {10,10,10}; /* ================ CutLeafs Removes leaves that were cut by using the svbsp from the light's visibility list. This gains some speed in certain cases. ================ */ void CutLeafs(byte *vis) { int c, i; msurface_t **surf; mleaf_t *leaf; qboolean found; int removed = 0; aabox_t leafbox; aabox_t visbox = emptyBox(); if (sh_norevis.value) return; Q_memset(currentshadowlight->leafvis,0,MAX_MAP_LEAFS/8); for (i=0 ; inumleafs ; i++)//overloop alle leafs { leaf = &cl.worldmodel->leafs[i]; if (vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) { leafbox = constructBox(leaf->minmaxs,leaf->minmaxs+3); if (!intersectsBox(¤tshadowlight->box,&leafbox)) continue; visbox = addBoxes(&visbox,&leafbox); //this leaf is visible from the leaf the current light (in a brute force way) //now check if we entirely cut this leaf by means of the svbsp c = leaf->nummarksurfaces; surf = leaf->firstmarksurface; // if (leaf->index != i) Con_Printf("Weird leaf index %i, %i\n",i,leaf->index); found = false; for (c=0; cnummarksurfaces; c++, surf++) { if ((*surf)->polys->lightTimestamp == r_lightTimestamp) { found = true; break; } } if (found) { currentshadowlight->leafvis[i>>3] |= (1<<(i&7)); removed++; } /* if (!found) { //set vis bit on false vis[i>>3] &= ~(1<<(i&7)); removed++; } */ } } leafbox = intersectBoxes(¤tshadowlight->box,&visbox); currentshadowlight->box = leafbox; //Con_Printf(" Removed leafs: %i\n", removed); } /* ================ AddToShadowBsp Add a surface as potential shadow caster to the svbsp, it can be cut when it is occluded by other surfaces. ================ */ void AddToShadowBsp(msurface_t *surf) { vec3_t surfvects[32]; int numsurfvects; int i; float *v; /* PENTA: removed we checked this twice //we don't cast shadows with water if (( surf->flags & SURF_DRAWTURB ) || ( surf->flags & SURF_DRAWSKY )) { return; } plane = surf->plane; switch (plane->type) { case PLANE_X: dist = currentshadowlight->origin[0] - plane->dist; break; case PLANE_Y: dist = currentshadowlight->origin[1] - plane->dist; break; case PLANE_Z: dist = currentshadowlight->origin[2] - plane->dist; break; default: dist = DotProduct (currentshadowlight->origin, plane->normal) - plane->dist; break; } //the normals are flipped when surf_planeback is 1 if (((surf->flags & SURF_PLANEBACK) && (dist > 0)) || (!(surf->flags & SURF_PLANEBACK) && (dist < 0))) { return; } //the normals are flipped when surf_planeback is 1 if ( abs(dist) > currentshadowlight->radius) { return; } */ //FIXME: use constant instead of 32 if (surf->numedges > 32) { Con_Printf("Error: to many edges"); return; } surf->visframe = 0; //Make temp copy of suface polygon numsurfvects = surf->numedges; for (i=0, v=(float *)(&globalVertexTable[surf->polys->firstvertex]); ishadowchain = shadowchain; surf->polys->lightTimestamp = r_lightTimestamp; shadowchain = surf; svBsp_NumKeptPolys++; } } /* ================ R_RecursiveShadowAdd Add surfaces front to back to the shadow bsp. ================ */ void R_RecursiveShadowAdd(mnode_t *node) { int c, side; mplane_t *plane; msurface_t *surf; double dot; if (node->contents == CONTENTS_SOLID) { return; // solid } if (node->contents & CONTENTS_LEAF) { return; // leaf } //find which side of the node we are on plane = node->plane; switch (plane->type) { case PLANE_X: dot = currentshadowlight->origin[0] - plane->dist; break; case PLANE_Y: dot = currentshadowlight->origin[1] - plane->dist; break; case PLANE_Z: dot = currentshadowlight->origin[2] - plane->dist; break; default: dot = DotProduct (currentshadowlight->origin, plane->normal) - plane->dist; break; } if (dot >= 0) side = 0; else side = 1; //recurse down the children, front side first R_RecursiveShadowAdd (node->children[side]); //draw stuff c = node->numsurfaces; if (c) { surf = cl.worldmodel->surfaces + node->firstsurface; do { if (surf->polys) { if ((surf->polys->lightTimestamp == r_lightTimestamp)) { AddToShadowBsp (surf); } } surf++; } while (--c); } //recurse down the back side R_RecursiveShadowAdd (node->children[!side]); } /* =============== R_MarkLightLeaves Marks nodes from the light, this is used for gross culling during svbsp creation. =============== */ void R_MarkLightLeaves (void) { byte solid[4096]; mleaf_t *lightleaf; //we use the same timestamp as for rendering (may cause errors maybe) r_visframecount++; lightleaf = Mod_PointInLeaf (currentshadowlight->origin, cl.worldmodel); if (r_novis.value) { lightvis = solid; memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); } else lightvis = Mod_LeafPVS (lightleaf, cl.worldmodel); } /* ================ ShadowVolumeBsp Create the shadow volume bsp for currentshadowlight ================ */ void ShadowVolumeBsp() { msurface_t *n; currentlightroot = R_CreateEmptyTree(); R_MarkLightLeaves(); R_MarkLightSurfaces (currentshadowlight,cl.worldmodel->nodes); //PENTA: Q3 hack until svbsp's are fixed again svBsp_NumKeptPolys = 0; n = shadowchain; while (n) { svBsp_NumKeptPolys++; n = n->shadowchain; } //shadowchain = NULL; //svBsp_NumKeptPolys = 0; //R_RecursiveShadowAdd(cl.worldmodel->nodes); } int done = 0; /* ================ R_StaticLightFromEnt Spawn a static lights for the given entity. ================ */ void R_StaticLightFromEnt(entity_t *ent) { int i; msurface_t *surf; msurface_t *s; mapshader_t *t; //Con_Printf("Shadow volumes start\n"); if (!ent->model) { Con_Printf("Light with null model"); return; } shadowchain = NULL; meshshadowchain = NULL; done++; //Con_Printf("->Light %i\n",done); r_lightTimestamp++; r_framecount++; svBsp_NumCutPolys = 0; svBsp_NumKeptPolys = 0; svBsp_NumAddedPolys = 0; //Create a light and make it static R_ShadowFromEntity(ent); numStaticShadowLights++; if (numShadowLights >= MAXSHADOWLIGHTS) { Con_Printf("R_StaticLightFromEnt: More than MAXSHADOWLIGHTS lights"); return; } currentshadowlight = &shadowlights[numShadowLights-1]; currentshadowlight->isStatic = true; //Get origin / box from the model VectorAdd(ent->model->mins, ent->model-> maxs, currentshadowlight->origin); VectorScale(currentshadowlight->origin, 0.5f, currentshadowlight->origin); VectorCopy(ent->model->mins, currentshadowlight->box.mins); VectorCopy(ent->model->maxs, currentshadowlight->box.maxs); VectorSubtract(ent->model->maxs,ent->model->mins,currentshadowlight->radiusv); VectorScale(currentshadowlight->radiusv, 0.5f, currentshadowlight->radiusv); currentshadowlight->radius = max(Length(ent->model->mins),Length(ent->model->maxs)); //Shader to use on light == shader on the ent's model if ((ent->model->type == mod_brush) && (ent->model->nummodelbrushes)) { model_t *mod = ent->model; mbrush_t *b = &mod->brushes[mod->firstmodelbrush]; mbrushside_t *bs = &mod->brushsides[b->firstbrushside]; currentshadowlight->shader = bs->shader->shader; } else { currentshadowlight->shader = GL_ShaderForName("textures/lights/default"); } //Calculate visible polygons ShadowVolumeBsp(); //Print stats /* Con_Printf(" Thrown away: %i\n",svBsp_NumAddedPolys-svBsp_NumKeptPolys); Con_Printf(" Total in volume: %i\n",svBsp_NumKeptPolys); */ currentshadowlight->visSurf = Hunk_Alloc(4*svBsp_NumKeptPolys); currentshadowlight->numVisSurf = svBsp_NumKeptPolys; surf = shadowchain; //Clear texture chains for (i=0 ; inummapshaders; i++) { cl.worldmodel->mapshaders[i].texturechain = NULL; } //Remark polys since polygons may have been removed since the last time stamp r_lightTimestamp++; for (i=0; ishadowchain) { surf->polys->lightTimestamp = r_lightTimestamp; currentshadowlight->visSurf[i] = surf; //put it in the correct texture chain surf->texturechain = surf->shader->texturechain; surf->shader->texturechain = surf; } //Sort surfs in our list per texture shadowchain = NULL; //meshshadowchain = NULL; for (i=0 ; inummapshaders ; i++) { t = &cl.worldmodel->mapshaders[i]; if (!t) continue; s = t->texturechain; if (!s) continue; else { for ( ; s ; s=s->texturechain) { s->shadowchain = shadowchain; shadowchain = s; } } t->texturechain = NULL; } //Recalculate vis for this light currentshadowlight->leaf = Mod_PointInLeaf (currentshadowlight->origin, cl.worldmodel); lightvis = Mod_LeafPVS (currentshadowlight->leaf, cl.worldmodel); currentshadowlight->area = currentshadowlight->leaf->area; //Q_memcpy(¤tshadowlight->vis[0], lightvis, MAX_MAP_LEAFS/8); Q_memcpy(¤tshadowlight->entclustervis[0], lightvis, MAX_MAP_LEAFS/8); CutLeafs(lightvis); //Precalculate the shadow volume / glow-texcoords PrecalcVolumesForLight(cl.worldmodel); currentshadowlight->volumeCmds = Hunk_Alloc(4*numVolumeCmds); Q_memcpy(currentshadowlight->volumeCmds, &volumeCmdsBuff, 4*numVolumeCmds); currentshadowlight->volumeVerts = Hunk_Alloc(4*numVolumeVerts); currentshadowlight->numVolumeVerts = numVolumeVerts; Q_memcpy(currentshadowlight->volumeVerts, &volumeVertsBuff, 4*numVolumeVerts); currentshadowlight->lightCmds = Hunk_Alloc(4*numLightCmds); Q_memcpy(currentshadowlight->lightCmds, &lightCmdsBuff, 4*numLightCmds); currentshadowlight->numlightcmds = numLightCmds; currentshadowlight->lightCmdsMesh = Hunk_Alloc(4*numLightCmdsMesh); Q_memcpy(currentshadowlight->lightCmdsMesh, &lightCmdsBuffMesh, 4*numLightCmdsMesh); currentshadowlight->numlightcmdsmesh = numLightCmdsMesh; } /************************************************************** Client side light entity loading code ***************************************************************/ /** Hacked entitiy loading code, this parses the entities client side to find lights in it **/ void LightFromFile(entity_t *fakeEnt) { R_StaticLightFromEnt(fakeEnt); } void ParseVector (char *s, float *d) { int i; char string[128]; char *v, *w; strncpy (string, s,sizeof(string)); v = string; w = string; for (i=0 ; i<3 ; i++) { while (*v && *v != ' ') v++; *v = 0; d[i] = atof (w); w = v = v+1; } } char *ParseEnt (char *data, qboolean *isLight, entity_t *ent) { qboolean init; char keyname[256]; qboolean foundworld = false; init = false; // go through all the dictionary pairs while (1) { // parse key data = COM_Parse (data); if (com_token[0] == '}') break; if (!data) Sys_Error ("ParseEnt: EOF without closing brace"); strncpy (keyname, com_token,sizeof(keyname)); // parse value data = COM_Parse (data); if (!data) Sys_Error ("ED_ParseEntity: EOF without closing brace"); if (com_token[0] == '}') Sys_Error ("ED_ParseEntity: closing brace without data"); init = true; if (!strcmp(keyname, "classname")) { if (!strcmp(com_token, "rt_light")) { *isLight = true; } else { *isLight = false; } } else if (!strcmp(keyname, "_color")) { ParseVector(com_token, ent->color); } else if (!strcmp(keyname, "origin")) { ParseVector(com_token, ent->origin); } else if (!strcmp(keyname, "light")) { ent->light_lev = atof(com_token); } else if (!strcmp(keyname, "model")) { ent->model = Mod_ForName(com_token, true); } else if (!strcmp(keyname, "skin")) { ent->skinnum = atoi(com_token); } else if (!strcmp(keyname, "_noautolight")) { Con_Printf("Automatic light gen disabled\n");//XYW \n foundworld = true; } else if (!strcmp(keyname, "_skybox")) { strncpy(skybox_name,com_token,sizeof(skybox_name)); } else if (!strcmp(keyname, "_cloudspeed")) { skybox_cloudspeed = atof(com_token); } else if (!strcmp(keyname, "_lightmapbright")) { Cvar_Set("sh_lightmapbright",com_token); Con_Printf("Lightmap brightness set to %f\n",sh_lightmapbright.value); } else if (!strcmp(keyname, "_fog_color")) { vec3_t temp; ParseVector(com_token, temp); Cvar_SetValue("fog_r",temp[0]); Cvar_SetValue("fog_g",temp[1]); Cvar_SetValue("fog_b",temp[2]); } else if (!strcmp(keyname, "_fog_start")) { Cvar_Set("fog_start",com_token); } else if (!strcmp(keyname, "_fog_end")) { Cvar_Set("fog_end",com_token); } else { //just do nothing } } if (foundworld) return NULL; return data; } void LoadLightsFromFile (char *data) { qboolean isLight; entity_t fakeEnt; Cvar_SetValue ("fog_start",0.0); Cvar_SetValue ("fog_end",0.0); // parse ents while (1) { // parse the opening brace data = COM_Parse (data); if (!data) break; if (com_token[0] != '{') Sys_Error ("ED_LoadFromFile: found %s when expecting {",com_token); isLight = false; Q_memset(&fakeEnt,0,sizeof(entity_t)); data = ParseEnt (data, &isLight, &fakeEnt); if (isLight) { LightFromFile(&fakeEnt); //Con_Printf("found light in file"); } } if ((!fog_start.value) && (!fog_end.value)) { Cvar_SetValue ("gl_fog",0.0); } } void R_AutomaticLightPos() { /* for (i=0; inumsurfaces; i++) { surf = &m->surfaces[i]; if (strstr(surf->texinfo->texture->name,"light")) { LightFromSurface(surf); } }*/ LoadLightsFromFile(cl.worldmodel->entities); }