/* 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]; /* -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]; } /* ============= 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; } } #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; } } /* ============= 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); } } /* ============= R_MarkEntities Adds entities that have a lightsource attched to the shadow light list. (used for static ents) We just check if they have a torch/flame model or not. ============= */ void R_MarkEntities (void) { int i; entity_t *current; for (i=0 ; imodel->name, "progs/flame2.mdl") || !strcmp (current->model->name, "progs/flame.mdl") ) { R_ShadowFromEntity(current); } } } 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; } } } qboolean R_MarkShadowSurf(msurface_t *surf, shadowlight_t *light) { mplane_t *plane; float dist; glpoly_t *poly; //we don't cast shadows with water if (( surf->flags & SURF_DRAWTURB ) || ( surf->flags & SURF_DRAWSKY )) { return false; } 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; } poly->lightTimestamp = r_lightTimestamp; return true; } #define MAX_LEAF_LIST 32 mleaf_t *leafList[MAX_LEAF_LIST]; int numLeafList; extern vec3_t r_emins, r_emaxs; // added "extern". /* ============= InShadowEntity Some efrag based sceme may cut even more ents! ============= */ qboolean InShadowEntity(entity_t *ent) { int i, leafindex; model_t *entmodel = ent->model; mleaf_t *leaf; vec3_t dst; float radius, d; radius = entmodel->radius; VectorSubtract (currentshadowlight->origin, ent->origin, dst); d = Length (dst); if (d < (currentshadowlight->radius + radius)) { 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->entvis[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_VisibleEntity Adds shadow casting ents to the list ============= */ void MarkShadowEntities() { 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 (InShadowEntity(currententity)) { currententity->lightTimestamp = r_lightTimestamp; cl_lightvisedicts[cl_numlightvisedicts] = currententity; cl_numlightvisedicts++; } else cut_ent++; } } } /* ============= 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=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; 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; } /************************************************************** World model shadow volumes ***************************************************************/ /* ============= 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; } /* ============= R_MarkShadowCasting 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_MarkShadowCasting (shadowlight_t *light, mnode_t *node) { mplane_t *plane; float dist; msurface_t **surf; mleaf_t *leaf; int c,leafindex; mesh_t *mesh; 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 (R_MarkShadowSurf ((*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 (mesh->lightTimestamp == r_lightTimestamp) continue; mesh->shadowchain = meshshadowchain; meshshadowchain = mesh; mesh->lightTimestamp = r_lightTimestamp; } 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_MarkShadowCasting (light, node->children[0]); R_MarkShadowCasting (light, node->children[1]); } float SphereInFrustum( vec3_t o, float radius ) { int p; float d; for( p = 0; p < 6; p++ ) { d = frustumPlanes[p][0] * o[0] + frustumPlanes[p][1] * o[1] + frustumPlanes[p][2] * o[2] + frustumPlanes[p][3]; if( d <= -radius ) return 0; } return d + radius; } /* ============= R_ContributeFrame Returns true if the light should be rendered. ============= */ qboolean R_ContributeFrame (shadowlight_t *light) { mleaf_t *lightleaf; float dist; 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) 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; } //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) { R_ProjectSphere (light, 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->vis[0]; } else { lightleaf = Mod_PointInLeaf (light->origin, cl.worldmodel); lightvis = Mod_LeafPVS (lightleaf, cl.worldmodel); Q_memcpy(&light->vis,lightvis,MAX_MAP_LEAFS/8); Q_memcpy(&light->entvis, 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)))) { return false; } 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 { return false; } } /* ============= R_FillShadowChain Returns true if the light should be rendered. ============= */ qboolean R_FillShadowChain (shadowlight_t *light) { r_lightTimestamp++; shadowchain = NULL; meshshadowchain = NULL; lightvis = &light->vis[0]; //numUsedShadowLights++; //mark shadow casting ents MarkShadowEntities(); //mark shadow casting polygons if (!light->isStatic) { R_MarkShadowCasting ( light, cl.worldmodel->nodes); } else { return true; } return (shadowchain || meshshadowchain) ? true : false; } 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) { 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); 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++) { R_MarkShadowSurf(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); } } /* ============= 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; if (sh_norevis.value) return; for (i=0 ; inumleafs ; i++)//overloop alle leafs { leaf = &cl.worldmodel->leafs[i]; if (vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) { //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) { //set vis bit on false vis[i>>3] &= ~(1<<(i&7)); removed++; } */ } } //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_MarkShadowCasting (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_CalcSvBsp Called for every static ent during spawning of the client ================ */ void R_CalcSvBsp(entity_t *ent) { int i; msurface_t *surf; msurface_t *s; mapshader_t *t; //Con_Printf("Shadow volumes start\n"); if (ent->model == NULL) { Con_Printf("null model"); return; } if (!strcmp (ent->model->name, "progs/flame2.mdl") || !strcmp (ent->model->name, "progs/flame.mdl") || !strcmp (ent->model->name, "progs/s_light.spr") || !strcmp (ent->model->name, "progs/b_light.spr") || !strcmp (ent->model->name, "progs/w_light.spr")) { 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_CalcSvBsp: More than MAXSHADOWLIGHTS lights"); return; } currentshadowlight = &shadowlights[numShadowLights-1]; //Hack: support quake light_* entities if (!strcmp (ent->model->name, "progs/flame2.mdl")) { currentshadowlight->baseColor[0] = 1; currentshadowlight->baseColor[1] = 0.9; currentshadowlight->baseColor[2] = 0.75; } else if (!strcmp (ent->model->name, "progs/flame.mdl")) { currentshadowlight->baseColor[0] = 1; currentshadowlight->baseColor[1] = 0.9; currentshadowlight->baseColor[2] = 0.75; } else if (!strcmp (ent->model->name, "progs/s_light.spr")) { currentshadowlight->baseColor[0] = 1; currentshadowlight->baseColor[1] = 0.9; currentshadowlight->baseColor[2] = 0.75; } currentshadowlight->isStatic = true; //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->entvis[0], lightvis, MAX_MAP_LEAFS/8); CutLeafs(currentshadowlight->vis); //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; //Con_Printf("light done\n"); } else { //Con_Printf("thrown away"); } } #define surfaceLightRadius 350.0 /* Not used places lights in front of things that look like lights in a map */ void LightFromSurface(msurface_t *surf) { vec3_t center, orig, normal; glpoly_t *poly; float *v, invnum; int i; shadowlight_t *light; qboolean tooClose; entity_t fakeEnt; poly = surf->polys; invnum = 1.0/poly->numverts; //Calculate origin for the light we are possibly going to spawn //v = poly->verts[0]; v = (float *)(&globalVertexTable[poly->firstvertex]); center[0] = center[1] = center[2] = 0; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { VectorAdd(center,v,center); } VectorScale(center,invnum,center); if (surf->flags & SURF_PLANEBACK) { VectorScale(surf->plane->normal,-1,normal); } else { VectorCopy(surf->plane->normal,normal); } VectorMA(center,16,normal,orig); //not to close to other lights then add it tooClose = false; for (i=0; iorigin,dist); length = Length(dist); if (length < (surfaceLightRadius*sh_radiusscale.value + light->radius*sh_radiusscale.value)) { tooClose = true; } } if (!tooClose) { Q_memset(&fakeEnt,0,sizeof(entity_t)); fakeEnt.light_lev = surfaceLightRadius; VectorCopy(orig,fakeEnt.origin); fakeEnt.model = Mod_ForName("progs/w_light.spr",true); R_CalcSvBsp(&fakeEnt); Con_Printf("Added surface light"); } } /** Hacked entitiy loading code, this parses the entities client side to find lights in it **/ void LightFromFile(vec3_t orig, vec3_t color, float light_lev) { int i; shadowlight_t *light; qboolean tooClose; entity_t fakeEnt; //not to close to other lights then add it tooClose = false; for (i=0; iorigin,dist); length = Length(dist); if (length < (light_lev*sh_radiusscale.value + light->radius*sh_radiusscale.value)) { tooClose = true; } } if (!tooClose) { Q_memset(&fakeEnt,0,sizeof(entity_t)); fakeEnt.light_lev = light_lev; VectorCopy(orig,fakeEnt.origin); VectorCopy(color,fakeEnt.color); fakeEnt.model = Mod_ForName("progs/w_light.spr",true); R_CalcSvBsp(&fakeEnt); Con_Printf("Added file light"); } } 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, vec3_t origin, vec3_t color, float *light) { 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, "light")) { *isLight = true; } else { *isLight = false; } } else if (!strcmp(keyname, "_color")) { ParseVector(com_token, color); } else if (!strcmp(keyname, "origin")) { ParseVector(com_token, origin); } else if (!strcmp(keyname, "light")) { *light = atof(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")) { ParseVector(com_token, origin); Cvar_SetValue("fog_r",origin[0]); Cvar_SetValue("fog_g",origin[1]); Cvar_SetValue("fog_b",origin[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; vec3_t origin, color; float light; Cvar_SetValue ("fog_start",0.0); Cvar_SetValue ("fog_end",0.0); color[0] = 1; color[1] = 1; color[2] = 1; light = 300; // 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; data = ParseEnt (data, &isLight, origin, color, &light); if (isLight) { LightFromFile(origin, color, light); //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); }