/* =========================================================================== Copyright (C) 1997-2001 Id Software, Inc. This file is part of Quake 2 source code. Quake 2 source code 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. Quake 2 source code 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 Quake 2 source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // r_alias.c: alias triangle model functions #include "r_local.h" #include "vlights.h" #include "r_normals.h" /* ============================================================= ALIAS MODELS ============================================================= */ vec3_t tempVertexArray[MD3_MAX_MESHES][MD3_MAX_VERTS]; vec3_t aliasLightDir = {0, 0, 0}; float aliasShadowAlpha; /* ================= R_LightAliasVertex ================= */ void R_LightAliasVertex (vec3_t baselight, vec3_t normal, vec3_t lightOut, byte normalindex, qboolean shaded) { int i; float l; if (r_fullbright->integer != 0) { VectorSet (lightOut, 1.0f, 1.0f, 1.0f); return; } if (r_model_shading->integer) { if (shaded) { if (r_model_shading->integer == 3) l = 2.0 * shadedots[normalindex] - 1; else if (r_model_shading->integer == 2) l = 1.5 * shadedots[normalindex] - 0.5; else l = shadedots[normalindex]; VectorScale(baselight, l, lightOut); } else VectorCopy(baselight, lightOut); if (model_dlights_num) for (i=0; iangles[PITCH], currententity->angles[YAW], true); VectorMA(lightOut, l, model_dlights[i].color, lightOut); } } else { l = 2.0 * VLight_GetLightValue (normal, aliasLightDir, currententity->angles[PITCH], currententity->angles[YAW], false); VectorScale(baselight, l, lightOut); } for (i=0; i<3; i++) lightOut[i] = max(min(lightOut[i], 1.0f), 0.0f); } /* ================= R_LightAliasVertexCel Adds dlights only for cel shading ================= */ void R_LightAliasVertexCel (vec3_t baselight, vec3_t normal, vec3_t lightOut, byte normalindex) { int i; float l; if (r_fullbright->integer != 0) { VectorSet (lightOut, 1.0f, 1.0f, 1.0f); return; } VectorCopy(baselight, lightOut); if (model_dlights_num) for (i=0; iangles[PITCH], currententity->angles[YAW], true); VectorMA(lightOut, l, model_dlights[i].color, lightOut); } for (i=0; i<3; i++) lightOut[i] = max(min(lightOut[i], 1.0f), 0.0f); } /* ================= R_CelTexCoord ================= */ #define CEL_OUTLINEDROPOFF 1024.0f // distance for cel shading outline to disappear #define CEL_TEX_MIN (0.5f/32.0f) #define CEL_TEX_MAX (31.5f/32.0f) float R_CelTexCoord (vec3_t meshlight, vec3_t normal, byte lightnormalindex) { float shadeCoord; int i, highest = 0; vec3_t lightColor; R_LightAliasVertex (meshlight, normal, lightColor, lightnormalindex, true); for (i=0; i<3; i++) { if (lightColor[i] > lightColor[highest]) highest = i; } for (i=0; i<3; i++) { lightColor[i] = min(max(lightColor[i], 0.0f), 1.0f); } shadeCoord = lightColor[highest]; shadeCoord = min(max(shadeCoord, CEL_TEX_MIN), CEL_TEX_MAX); return shadeCoord; } /* ================= R_AliasMeshesAreBatchable ================= */ qboolean R_AliasMeshesAreBatchable (maliasmodel_t *paliashdr, unsigned meshnum1, unsigned meshnum2, unsigned skinnum) { maliasmesh_t *mesh1, *mesh2; renderparms_t *skinParms1, *skinParms2; int skinnum1, skinnum2; if (!paliashdr) return false; mesh1 = &paliashdr->meshes[meshnum1]; mesh2 = &paliashdr->meshes[meshnum2]; skinnum1 = (skinnumnum_skins)?skinnum:0; skinnum2 = (skinnumnum_skins)?skinnum:0; skinParms1 = &mesh1->skins[skinnum1].renderparms; skinParms2 = &mesh2->skins[skinnum2].renderparms; if (!mesh1 || !mesh2 || !skinParms1 || !skinParms2) return false; if (currentmodel->skins[meshnum1][skinnum1] != currentmodel->skins[meshnum2][skinnum2]) return false; if (mesh1->skins[skinnum1].glowimage != mesh2->skins[skinnum2].glowimage) return false; if (skinParms1->alphatest != skinParms2->alphatest) return false; if (skinParms1->basealpha != skinParms2->basealpha) return false; if (skinParms1->blend != skinParms2->blend) return false; if (skinParms1->blendfunc_src != skinParms2->blendfunc_src) return false; if (skinParms1->blendfunc_dst != skinParms2->blendfunc_dst) return false; if (skinParms1->envmap != skinParms2->envmap) return false; if ( (skinParms1->glow.type != skinParms2->glow.type) || (skinParms1->glow.params[0] != skinParms2->glow.params[0]) || (skinParms1->glow.params[1] != skinParms2->glow.params[1]) || (skinParms1->glow.params[2] != skinParms2->glow.params[2]) || (skinParms1->glow.params[3] != skinParms2->glow.params[3]) ) return false; if (skinParms1->nodraw != skinParms2->nodraw) return false; if (skinParms1->twosided != skinParms2->twosided) return false; return true; } /* ================= RB_RenderAliasMesh Backend for R_DrawAliasMeshes ================= */ void RB_RenderAliasMesh (maliasmodel_t *paliashdr, unsigned meshnum, unsigned skinnum, image_t *skin, qboolean reverseCull) { entity_t *e = currententity; maliasmesh_t *mesh; renderparms_t *skinParms; int i; float thisalpha = colorArray[0][3]; qboolean shellModel = e->flags & RF_MASK_SHELL; if (!paliashdr) return; mesh = &paliashdr->meshes[meshnum]; if (!shellModel) GL_Bind(skin->texnum); // md3 skin scripting skinParms = &mesh->skins[skinnum].renderparms; if (skinParms->twosided) GL_Disable (GL_CULL_FACE); else GL_Enable (GL_CULL_FACE); if (skinParms->alphatest && !shellModel) GL_Enable (GL_ALPHA_TEST); else GL_Disable (GL_ALPHA_TEST); if (thisalpha < 1.0f || skinParms->blend) GL_Enable (GL_BLEND); else GL_Disable (GL_BLEND); if (skinParms->blend && !shellModel) GL_BlendFunc (skinParms->blendfunc_src, skinParms->blendfunc_dst); else if (shellModel) GL_BlendFunc (GL_ONE, GL_ONE); else GL_BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // md3 skin scripting // draw RB_DrawArrays (); // glow pass if (mesh->skins[skinnum].glowimage && !shellModel) { float glowcolor; if (skinParms->glow.type > -1) glowcolor = RB_CalcGlowColor (skinParms); else glowcolor = 1.0; qglDisableClientState (GL_COLOR_ARRAY); qglColor4f(glowcolor, glowcolor, glowcolor, 1.0); GL_Enable (GL_BLEND); GL_BlendFunc (GL_ONE, GL_ONE); GL_Bind(mesh->skins[skinnum].glowimage->texnum); RB_DrawArrays (); qglColor4f(1.0, 1.0, 1.0, 1.0); qglEnableClientState (GL_COLOR_ARRAY); } // envmap pass if (skinParms->envmap > 0.0f && !shellModel) { GL_Enable (GL_BLEND); GL_BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); qglTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); qglTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // apply alpha to array for (i=0; ienvmap; GL_Bind(glMedia.envmappic->texnum); qglEnable(GL_TEXTURE_GEN_S); qglEnable(GL_TEXTURE_GEN_T); RB_DrawArrays (); qglDisable(GL_TEXTURE_GEN_S); qglDisable(GL_TEXTURE_GEN_T); } // cel shading if ( r_celshading->integer && !(thisalpha < 1.0f || skinParms->blend || skinParms->alphatest) ) { float strength, len; vec3_t offset; // blend cel shade texture qglDepthMask (false); GL_Enable (GL_BLEND); GL_BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_Bind (glMedia.celshadetexture->texnum); qglTexCoordPointer (2, GL_FLOAT, sizeof(celTexCoordArray[0]), celTexCoordArray[0]); qglDisableClientState (GL_COLOR_ARRAY); qglColor4f(1.0f, 1.0f, 1.0f, 1.0f); RB_DrawArrays (); qglTexCoordPointer (2, GL_FLOAT, sizeof(texCoordArray[0][0]), texCoordArray[0][0]); // qglEnableClientState (GL_COLOR_ARRAY); GL_Disable (GL_BLEND); qglDepthMask (true); // draw outlines VectorSubtract (r_newrefdef.vieworg, currententity->origin, offset); len = VectorNormalize(offset); strength = (CEL_OUTLINEDROPOFF - len) / CEL_OUTLINEDROPOFF; strength = min(max(strength, 0.0f), 1.0f); qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); if (reverseCull) GL_CullFace(GL_FRONT); else GL_CullFace(GL_BACK); qglColor4f(0.0f, 0.0f, 0.0f, 1.0f); qglLineWidth(r_celshading_width->value * strength); RB_DrawArrays (); qglLineWidth(1.0f); qglColor4f(1.0f, 1.0f, 1.0f, 1.0f); if (reverseCull) GL_CullFace(GL_BACK); else GL_CullFace(GL_FRONT); qglEnableClientState (GL_COLOR_ARRAY); qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } RB_DrawMeshTris (); rb_vertex = rb_index = 0; // restore state GL_Enable (GL_CULL_FACE); GL_Disable (GL_ALPHA_TEST); GL_Disable (GL_BLEND); GL_BlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } /* ================= R_DrawAliasMeshes ================= */ //void R_DrawAliasMeshes (maliasmodel_t *paliashdr, entity_t *e, qboolean lerpOnly, qboolean mirrored, qboolean viewFlipped) void R_DrawAliasMeshes (maliasmodel_t *paliashdr, entity_t *e, qboolean mirrored, qboolean viewFlipped, qboolean preLerped, qboolean lerpOnly) { int i, k, meshnum, skinnum, baseindex; // numCalls maliasframe_t *frame, *oldframe; maliasmesh_t mesh; maliasvertex_t *v, *ov; vec3_t move, delta, vectors[3]; vec3_t curScale, oldScale, curNormal, oldNormal; vec3_t tempNormalsArray[MD3_MAX_VERTS]; vec2_t tempSkinCoord; vec3_t meshlight, lightcolor; float alpha, meshalpha, thisalpha, shellscale, frontlerp, backlerp = e->backlerp, mirrormult; image_t *skin; renderparms_t skinParms; qboolean shellModel = e->flags & RF_MASK_SHELL; qboolean meshCelShaded; // added for cel shading if (lerpOnly) preLerped = false; frontlerp = 1.0 - backlerp; if (shellModel && FlowingShell()) alpha = 0.7; else if (e->flags & RF_TRANSLUCENT) alpha = e->alpha; else alpha = 1.0; frame = paliashdr->frames + e->frame; oldframe = paliashdr->frames + e->oldframe; if (!preLerped) { VectorScale(frame->scale, frontlerp, curScale); VectorScale(oldframe->scale, backlerp, oldScale); mirrormult = (mirrored) ? -1.0f : 1.0f; // move should be the delta back to the previous frame * backlerp VectorSubtract (e->oldorigin, e->origin, delta); AngleVectors (e->angles, vectors[0], vectors[1], vectors[2]); move[0] = DotProduct (delta, vectors[0]); // forward move[1] = -DotProduct (delta, vectors[1]); // left move[2] = DotProduct (delta, vectors[2]); // up VectorAdd (move, oldframe->translate, move); for (i=0 ; i<3 ; i++) move[i] = backlerp*move[i] + frontlerp*frame->translate[i]; } GL_ShadeModel (GL_SMOOTH); GL_TexEnv (GL_MODULATE); R_SetVertexRGBScale(true); R_SetShellBlend (true); rb_vertex = rb_index = 0; // numCalls = 0; // new outer loop for whole model for (k=0, meshnum=0; k < paliashdr->num_meshes; k++, meshnum++) { mesh = paliashdr->meshes[k]; // select skin if (e->skin) { // custom player skin skinnum = 0; skin = e->skin; } else { skinnum = (e->skinnumskinnum:0; // catch bad skinnums skin = currentmodel->skins[k][skinnum]; if (!skin) { skinnum = 0; skin = currentmodel->skins[k][0]; } } if (!skin) { skinnum = 0; skin = glMedia.notexture; } // md3 skin scripting skinParms = mesh.skins[skinnum].renderparms; if (skinParms.nodraw) continue; // skip this mesh for this skin if (skinParms.fullbright) VectorSet(meshlight, 1.0f, 1.0f, 1.0f); else VectorCopy(shadelight, meshlight); meshalpha = alpha * skinParms.basealpha; // md3 skin scripting // is this mesh cel shaded? meshCelShaded = (r_celshading->integer && !(meshalpha < 1.0f || skinParms.blend || skinParms.alphatest)); v = mesh.vertexes + e->frame * mesh.num_verts; ov = mesh.vertexes + e->oldframe * mesh.num_verts; baseindex = rb_vertex; // set indices for each triangle for (i=0; inormal[0]] * r_cosTable[v->normal[1]]; curNormal[1] = r_sinTable[v->normal[0]] * r_sinTable[v->normal[1]]; curNormal[2] = r_cosTable[v->normal[0]]; oldNormal[0] = r_sinTable[ov->normal[0]] * r_cosTable[ov->normal[1]]; oldNormal[1] = r_sinTable[ov->normal[0]] * r_sinTable[ov->normal[1]]; oldNormal[2] = r_cosTable[ov->normal[0]]; VectorSet ( tempNormalsArray[i], curNormal[0] + (oldNormal[0] - curNormal[0])*backlerp, curNormal[1] + (oldNormal[1] - curNormal[1])*backlerp, curNormal[2] + (oldNormal[2] - curNormal[2])*backlerp ); if (shellModel) shellscale = (e->flags & RF_WEAPONMODEL) ? WEAPON_SHELL_SCALE: POWERSUIT_SCALE; else shellscale = 0.0; VectorSet ( tempVertexArray[meshnum][i], move[0] + ov->xyz[0]*oldScale[0] + v->xyz[0]*curScale[0] + tempNormalsArray[i][0]*shellscale, mirrormult * (move[1] + ov->xyz[1]*oldScale[1] + v->xyz[1]*curScale[1] + tempNormalsArray[i][1]*shellscale), move[2] + ov->xyz[2]*oldScale[2] + v->xyz[2]*curScale[2] + tempNormalsArray[i][2]*shellscale ); tempNormalsArray[i][1] *= mirrormult; } // skip drawing if we're only lerping the verts for a shadow-only rendering pass if (lerpOnly) continue; // calc lighting and alpha if (shellModel) VectorCopy(meshlight, lightcolor); else if (meshCelShaded) R_LightAliasVertexCel (meshlight, tempNormalsArray[i], lightcolor, v->lightnormalindex); // added for cel shading else R_LightAliasVertex (meshlight, tempNormalsArray[i], lightcolor, v->lightnormalindex, !skinParms.nodiffuse); //thisalpha = R_CalcEntAlpha(meshalpha, tempVertexArray[meshnum][i]); thisalpha = meshalpha; // get tex coords if (shellModel && FlowingShell()) { tempSkinCoord[0] = (tempVertexArray[meshnum][i][0] + tempVertexArray[meshnum][i][1]) * DIV40 + shellFlowH; tempSkinCoord[1] = tempVertexArray[meshnum][i][2] * DIV40 + shellFlowV; // was / 40 } else { tempSkinCoord[0] = mesh.stcoords[i].st[0]; tempSkinCoord[1] = mesh.stcoords[i].st[1]; } // add to arrays VA_SetElem2(texCoordArray[0][rb_vertex], tempSkinCoord[0], tempSkinCoord[1]); VA_SetElem3(vertexArray[rb_vertex], tempVertexArray[meshnum][i][0], tempVertexArray[meshnum][i][1], tempVertexArray[meshnum][i][2]); VA_SetElem4(colorArray[rb_vertex], lightcolor[0], lightcolor[1], lightcolor[2], thisalpha); if (meshCelShaded) { VA_SetElem2(celTexCoordArray[rb_vertex], R_CelTexCoord(meshlight, tempNormalsArray[i], v->lightnormalindex), 0); // added for cel shading } rb_vertex++; } if (!shellModel) RB_ModifyTextureCoords (&texCoordArray[0][baseindex][0], &vertexArray[baseindex][0], mesh.num_verts, &skinParms); // compare renderparms for next mesh and check for overflow if ( k < (paliashdr->num_meshes-1) ) { if ( ( shellModel || R_AliasMeshesAreBatchable (paliashdr, k, k+1, e->skinnum) ) && !RB_CheckArrayOverflow (paliashdr->meshes[k+1].num_verts, paliashdr->meshes[k+1].num_tris*3) ) continue; } RB_RenderAliasMesh (paliashdr, meshnum, skinnum, skin, (mirrored || viewFlipped)); // numCalls++; } // end new outer loop // if (paliashdr->num_meshes > numCalls) // VID_Printf (PRINT_DEVELOPER, "%s: rendered %i meshes in %i pass(es)\n", currentmodel->name, paliashdr->num_meshes, numCalls); R_SetShellBlend (false); R_SetVertexRGBScale(false); GL_TexEnv (GL_REPLACE); GL_ShadeModel (GL_FLAT); } unsigned shadow_va, shadow_index; /* ============= R_BuildShadowVolume based on code from BeefQuake R6 ============= */ void R_BuildShadowVolume (maliasmodel_t *hdr, int meshnum, vec3_t light, float projectdistance, qboolean nocap) { int i, j; BOOL triangleFacingLight[MD3_MAX_TRIANGLES]; vec3_t v0, v1, v2, v3; float thisAlpha; maliasmesh_t mesh; maliasvertex_t *verts; mesh = hdr->meshes[meshnum]; verts = mesh.vertexes; thisAlpha = aliasShadowAlpha; // was r_shadowalpha->value for (i=0; i 0; } shadow_va = shadow_index = 0; for (i=0; iorigin, currententity->origin)) continue; VectorSubtract(dl->origin, currententity->origin, temp); dist = dl->intensity - VectorLength(temp); if (dist <= 0) continue; lnum++; // Factor in the intensity of a dlight VectorScale (temp, dist*0.25, temp); VectorAdd (vecAdd, temp, vecAdd); } VectorNormalize(vecAdd); VectorScale(vecAdd, 1024, vecAdd); // get projection distance from lightspot height highest = lowest = bbox[0][2]; for (i=0; i<8; i++) { if (bbox[i][2] > highest) highest = bbox[i][2]; if (bbox[i][2] < lowest) lowest = bbox[i][2]; } projected_distance = fabs((highest - lightspot[2]) + (highest-lowest)) / fabs(vecAdd[2]); // projected_distance = 1.5f * (fabs(highest - lightspot[2])) / fabs(vecAdd[2]); VectorCopy(vecAdd, lightVec); // reverse-rotate light vector based on angles angle = -currententity->angles[PITCH] / 180 * M_PI; cosp = cos(angle), sinp = sin(angle); angle = -currententity->angles[YAW] / 180 * M_PI; cosy = cos(angle), siny = sin(angle); angle = -currententity->angles[ROLL] / 180 * M_PI * R_RollMult(); // roll is backwards cosr = cos(angle), sinr = sin(angle); // rotate for yaw (z axis) ix = lightVec[0], iy = lightVec[1]; lightVec[0] = cosy * ix - siny * iy + 0; lightVec[1] = siny * ix + cosy * iy + 0; // rotate for pitch (y axis) ix = lightVec[0], iz = lightVec[2]; lightVec[0] = cosp * ix + 0 + sinp * iz; lightVec[2] = -sinp * ix + 0 + cosp * iz; // rotate for roll (x axis) iy = lightVec[1], iz = lightVec[2]; lightVec[1] = 0 + cosr * iy - sinr * iz; lightVec[2] = 0 + sinr * iy + cosr * iz; // for (i=0; i<3; i++) // shadowVec[i] = -lightVec[i] * projected_distance; // for (i=0; i<8; i++) // VectorAdd (bbox[i], shadowVec, endBBox[i]); return projected_distance; } /* ============= R_DrawAliasVolumeShadow based on code from BeefQuake R6 ============= */ void R_DrawAliasVolumeShadow (maliasmodel_t *paliashdr, vec3_t bbox[8]) { vec3_t light, vecAdd; // temp vec3_t shadowVec, endBBox[8], volumeMins, volumeMaxs; float projected_distance; int i, j, skinnum; // lnum qboolean zFail = (r_shadow_zfail->integer != 0); qboolean inVolume = false; // GLenum incr, decr; // float dist, highest, lowest; // float angle, cosp, sinp, cosy, siny, cosr, sinr, ix, iy, iz; // dlight_t *dl; #if 0 dl = r_newrefdef.dlights; VectorSet(vecAdd, 680,0,1024); // set base vector, was 576,0,1024 // compute average light vector from dlights for (i=0, lnum=0; iorigin, currententity->origin)) continue; VectorSubtract(dl->origin, currententity->origin, temp); dist = dl->intensity - VectorLength(temp); if (dist <= 0) continue; lnum++; // Factor in the intensity of a dlight VectorScale (temp, dist*0.25, temp); VectorAdd (vecAdd, temp, vecAdd); } VectorNormalize(vecAdd); VectorScale(vecAdd, 1024, vecAdd); // get projection distance from lightspot height highest = lowest = bbox[0][2]; for (i=0; i<8; i++) { if (bbox[i][2] > highest) highest = bbox[i][2]; if (bbox[i][2] < lowest) lowest = bbox[i][2]; } projected_distance = (fabs(highest - lightspot[2]) + (highest-lowest)) / vecAdd[2]; VectorCopy(vecAdd, light); // reverse-rotate light vector based on angles angle = -currententity->angles[PITCH] / 180 * M_PI; cosp = cos(angle), sinp = sin(angle); angle = -currententity->angles[YAW] / 180 * M_PI; cosy = cos(angle), siny = sin(angle); angle = -currententity->angles[ROLL] / 180 * M_PI * R_RollMult(); // roll is backwards cosr = cos(angle), sinr = sin(angle); // rotate for yaw (z axis) ix = light[0], iy = light[1]; light[0] = cosy * ix - siny * iy + 0; light[1] = siny * ix + cosy * iy + 0; // rotate for pitch (y axis) ix = light[0], iz = light[2]; light[0] = cosp * ix + 0 + sinp * iz; light[2] = -sinp * ix + 0 + cosp * iz; // rotate for roll (x axis) iy = light[1], iz = light[2]; light[1] = 0 + cosr * iy - sinr * iz; light[2] = 0 + sinr * iy + cosr * iz; #endif projected_distance = R_CalcAliasVolumeShadowLightVector (bbox, light); // For Z-Pass method, calc bbox for shadow volume to see if vieworg is likely to be inside it if (!zFail) { // calc bbox for end of shadow volume for (i=0; i<3; i++) shadowVec[i] = -vecAdd[i] * projected_distance; for (i=0; i<8; i++) VectorAdd (bbox[i], shadowVec, endBBox[i]); // get bbox for entire shadow volume VectorCopy (currententity->origin, volumeMaxs); VectorCopy (currententity->origin, volumeMins); for (i=0; i<8; i++) { for (j=0; j<3; j++) { if (bbox[i][j] < volumeMins[j]) volumeMins[j] = bbox[i][j]; if (endBBox[i][j] < volumeMins[j]) volumeMins[j] = endBBox[i][j]; if (bbox[i][j] > volumeMaxs[j]) volumeMaxs[j] = bbox[i][j]; if (endBBox[i][j] > volumeMaxs[j]) volumeMaxs[j] = endBBox[i][j]; } } // if the vieworg is inside the volume bbox, assume it's inside the volume if ( (r_newrefdef.vieworg[0] >= volumeMins[0] && r_newrefdef.vieworg[1] >= volumeMins[1] && r_newrefdef.vieworg[2] >= volumeMins[2]) && (r_newrefdef.vieworg[0] <= volumeMaxs[0] && r_newrefdef.vieworg[1] <= volumeMaxs[1] && r_newrefdef.vieworg[2] <= volumeMaxs[2]) ) inVolume = true; } // set up stenciling if (!r_shadowvolumes->integer) { /*if (glConfig.extStencilWrap) { incr = GL_INCR_WRAP_EXT; decr = GL_DECR_WRAP_EXT; } else { incr = GL_INCR; decr = GL_DECR; }*/ qglPushAttrib(GL_STENCIL_BUFFER_BIT); // save stencil buffer qglClear(GL_STENCIL_BUFFER_BIT); qglColorMask(0,0,0,0); GL_DepthMask(0); GL_DepthFunc(GL_LESS); GL_Enable(GL_STENCIL_TEST); qglStencilFunc(GL_ALWAYS, 0, 255); // qglStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); // qglStencilMask (255); } // build shadow volumes and render each to stencil buffer for (i=0; inum_meshes; i++) { skinnum = (currententity->skinnummeshes[i].num_skins)?currententity->skinnum:0; if (paliashdr->meshes[i].skins[skinnum].renderparms.noshadow) continue; R_BuildShadowVolume (paliashdr, i, light, projected_distance, r_shadowvolumes->integer); GL_LockArrays (shadow_va); if (!r_shadowvolumes->integer) { if (zFail &&glConfig.atiSeparateStencil && glConfig.extStencilWrap && r_stencilTwoSide->integer) // Barnes ATI stenciling { GL_Disable(GL_CULL_FACE); qglStencilOpSeparateATI (GL_BACK, GL_KEEP, GL_INCR_WRAP_EXT, GL_KEEP); qglStencilOpSeparateATI (GL_FRONT, GL_KEEP, GL_DECR_WRAP_EXT, GL_KEEP); R_DrawShadowVolume (); GL_Enable(GL_CULL_FACE); } else if (zFail && glConfig.extStencilTwoSide && glConfig.extStencilWrap && r_stencilTwoSide->integer) // Echon's two-sided stenciling { GL_Disable(GL_CULL_FACE); qglEnable (GL_STENCIL_TEST_TWO_SIDE_EXT); qglActiveStencilFaceEXT (GL_BACK); qglStencilOp (GL_KEEP, GL_INCR_WRAP_EXT, GL_KEEP); qglActiveStencilFaceEXT (GL_FRONT); qglStencilOp (GL_KEEP, GL_DECR_WRAP_EXT, GL_KEEP); R_DrawShadowVolume (); qglDisable (GL_STENCIL_TEST_TWO_SIDE_EXT); GL_Enable(GL_CULL_FACE); } else if (zFail) { // increment stencil if backface is behind depthbuffer GL_CullFace(GL_BACK); // quake is backwards, this culls front faces qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP); R_DrawShadowVolume (); // decrement stencil if frontface is behind depthbuffer GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP); R_DrawShadowVolume (); } else // Z-Pass { // Fix for z-Pass shadows if viewpoint is inside volume // Same as Carmack's patent-free method for Doom3 GPL source // This pre-loads the stencil buffer with # of volumes // that get clipped by the near or far clip plane. if (inVolume) { GL_CullFace(GL_BACK); // quake is backwards, this culls front faces qglStencilOp(GL_KEEP, GL_INCR, GL_INCR); R_DrawShadowVolume (); GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces qglStencilOp(GL_KEEP, GL_DECR, GL_DECR); R_DrawShadowVolume (); } // increment stencil if frontface is behind depthbuffer GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces qglStencilOp(GL_KEEP, GL_KEEP, GL_INCR); R_DrawShadowVolume (); // decrement stencil if backface is behind depthbuffer GL_CullFace(GL_BACK); // quake is backwards, this culls front faces qglStencilOp(GL_KEEP, GL_KEEP, GL_DECR); R_DrawShadowVolume (); } } else R_DrawShadowVolume (); GL_UnlockArrays (); } // end stenciling and draw stenciled volume if (!r_shadowvolumes->integer) { GL_CullFace(GL_FRONT); GL_Disable(GL_STENCIL_TEST); GL_DepthFunc(GL_LEQUAL); GL_DepthMask(1); qglColorMask(1,1,1,1); // draw shadows for this model now R_ShadowBlend (aliasShadowAlpha * currententity->alpha); // was r_shadowalpha->value qglPopAttrib(); // restore stencil buffer } } /* ================= R_DrawAliasPlanarShadow ================= */ void R_DrawAliasPlanarShadow (maliasmodel_t *paliashdr) { maliasmesh_t mesh; float height, lheight, thisAlpha; vec3_t point, shadevector; int i, j, skinnum; R_ShadowLight (currententity->origin, shadevector); lheight = currententity->origin[2] - lightspot[2]; height = -lheight + 0.1f; if (currententity->flags & RF_TRANSLUCENT) thisAlpha = aliasShadowAlpha * currententity->alpha; // was r_shadowalpha->value else thisAlpha = aliasShadowAlpha; // was r_shadowalpha->value // don't draw shadows above view origin, thnx to MrG if (r_newrefdef.vieworg[2] < (currententity->origin[2] + height)) return; GL_Stencil (true, false); GL_BlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA); rb_vertex = rb_index = 0; for (i=0; inum_meshes; i++) { mesh = paliashdr->meshes[i]; skinnum = (currententity->skinnumskinnum:0; if (mesh.skins[skinnum].renderparms.noshadow) continue; for (j=0; j < mesh.num_tris; j++) { indexArray[rb_index++] = rb_vertex + mesh.indexes[3*j+0]; indexArray[rb_index++] = rb_vertex + mesh.indexes[3*j+1]; indexArray[rb_index++] = rb_vertex + mesh.indexes[3*j+2]; } for (j=0; j < mesh.num_verts; j++) { VectorCopy(tempVertexArray[i][j], point); point[0] -= shadevector[0]*(point[2]+lheight); point[1] -= shadevector[1]*(point[2]+lheight); point[2] = height; VA_SetElem3(vertexArray[rb_vertex], point[0], point[1], point[2]); VA_SetElem4(colorArray[rb_vertex], 0, 0, 0, thisAlpha); rb_vertex++; } } RB_DrawArrays (); rb_vertex = rb_index = 0; GL_Stencil (false, false); } /* ================= R_CullAliasModel ================= */ //static qboolean R_CullAliasModel ( vec3_t bbox[8], entity_t *e ) static qboolean R_CullAliasModel (vec3_t bbox[8], vec3_t shadowBBox[8], entity_t *e, qboolean volumeShadow) { int i, j, mask, aggregatemask = ~0; float dp, volProjDist; vec3_t mins, maxs, tmp, vectors[3]; //angles; vec3_t lightVec, shadowVec, tmp_bbox[8], end_bbox[8], volumeMins, volumeMaxs; maliasmodel_t *paliashdr; maliasframe_t *pframe, *poldframe; paliashdr = (maliasmodel_t *)currentmodel->extradata; if ( ( e->frame >= paliashdr->num_frames ) || ( e->frame < 0 ) ) { VID_Printf (PRINT_ALL, "R_CullAliasModel %s: no such frame %d\n", currentmodel->name, e->frame); e->frame = 0; } if ( ( e->oldframe >= paliashdr->num_frames ) || ( e->oldframe < 0 ) ) { VID_Printf (PRINT_ALL, "R_CullAliasModel %s: no such oldframe %d\n", currentmodel->name, e->oldframe); e->oldframe = 0; } pframe = paliashdr->frames + e->frame; poldframe = paliashdr->frames + e->oldframe; // compute axially aligned mins and maxs if ( pframe == poldframe ) { VectorCopy(pframe->mins, mins); VectorCopy(pframe->maxs, maxs); } else { for ( i = 0; i < 3; i++ ) { if (pframe->mins[i] < poldframe->mins[i]) mins[i] = pframe->mins[i]; else mins[i] = poldframe->mins[i]; if (pframe->maxs[i] > poldframe->maxs[i]) maxs[i] = pframe->maxs[i]; else maxs[i] = poldframe->maxs[i]; } } // jitspoe's bbox rotation fix // compute and rotate bonding box e->angles[ROLL] = -e->angles[ROLL]; // roll is backwards AngleVectors(e->angles, vectors[0], vectors[1], vectors[2]); e->angles[ROLL] = -e->angles[ROLL]; // roll is backwards VectorSubtract(vec3_origin, vectors[1], vectors[1]); // AngleVectors returns "right" instead of "left" for (i = 0; i < 8; i++) { tmp[0] = ((i & 1) ? mins[0] : maxs[0]); tmp[1] = ((i & 2) ? mins[1] : maxs[1]); tmp[2] = ((i & 4) ? mins[2] : maxs[2]); VectorCopy(tmp, tmp_bbox[i]); // save off un-rotated bbox VectorAdd(tmp, e->origin, end_bbox[i]); // version with e->origin added for light vector calc bbox[i][0] = vectors[0][0] * tmp[0] + vectors[1][0] * tmp[1] + vectors[2][0] * tmp[2] + e->origin[0]; bbox[i][1] = vectors[0][1] * tmp[0] + vectors[1][1] * tmp[1] + vectors[2][1] * tmp[2] + e->origin[1]; bbox[i][2] = vectors[0][2] * tmp[0] + vectors[1][2] * tmp[1] + vectors[2][2] * tmp[2] + e->origin[2]; } // calc shadow volume bbox and rotate if (volumeShadow) { R_LightPoint (e->origin, shadelight, false); volProjDist = R_CalcAliasVolumeShadowLightVector (end_bbox, lightVec); for (i=0; i<3; i++) shadowVec[i] = -lightVec[i] * volProjDist; VectorCopy (vec3_origin, volumeMaxs); VectorCopy (vec3_origin, volumeMins); for (i = 0; i < 8; i++) { VectorAdd(tmp_bbox[i], shadowVec, end_bbox[i]); // VectorCopy(end_bbox[i], tmp); for (j=0; j<3; j++) { if (tmp_bbox[i][j] < volumeMins[j]) volumeMins[j] = tmp_bbox[i][j]; if (end_bbox[i][j] < volumeMins[j]) volumeMins[j] = end_bbox[i][j]; if (tmp_bbox[i][j] > volumeMaxs[j]) volumeMaxs[j] = tmp_bbox[i][j]; if (end_bbox[i][j] > volumeMaxs[j]) volumeMaxs[j] = end_bbox[i][j]; } tmp[0] = ((i & 1) ? volumeMins[0] : volumeMaxs[0]); tmp[1] = ((i & 2) ? volumeMins[1] : volumeMaxs[1]); tmp[2] = ((i & 4) ? volumeMins[2] : volumeMaxs[2]); shadowBBox[i][0] = vectors[0][0] * tmp[0] + vectors[1][0] * tmp[1] + vectors[2][0] * tmp[2] + e->origin[0]; shadowBBox[i][1] = vectors[0][1] * tmp[0] + vectors[1][1] * tmp[1] + vectors[2][1] * tmp[2] + e->origin[1]; shadowBBox[i][2] = vectors[0][2] * tmp[0] + vectors[1][2] * tmp[1] + vectors[2][2] * tmp[2] + e->origin[2]; } } // cull for (i=0; i<8; i++) { mask = 0; for (j=0; j<4; j++) { dp = DotProduct(frustum[j].normal, bbox[i]); if ( ( dp - frustum[j].dist ) < 0 ) mask |= (1<integer >= 1) && !(r_newrefdef.rdflags & RDF_NOWORLDMODEL) && (r_worldmodel != NULL) && (r_worldmodel->lightdata != 0) && !(e->flags & (RF_WEAPONMODEL | RF_NOSHADOW)) && !( (e->flags & RF_MASK_SHELL) && (e->flags & RF_TRANSLUCENT) ) ) // no shadows from shells { aliasShadowAlpha = R_CalcShadowAlpha(e); if ( (r_shadows->integer == 3) && (aliasShadowAlpha >= DIV255) ) volumeShadow = true; else if (aliasShadowAlpha >= DIV255) planarShadow = true; } // also skip this for viewermodels and cameramodels if ( !(e->flags & RF_WEAPONMODEL || e->flags & RF_VIEWERMODEL || e->renderfx & RF2_CAMERAMODEL) ) { // if (R_CullAliasModel(bbox, e)) // return; qboolean culled = R_CullAliasModel(bbox, shadowBBox, e, volumeShadow); if (volumeShadow) { if (culled) { if ( R_CullAliasShadow(shadowBBox, e) ) return; else volumeShadowOnly = true; } } else if (culled) return; } // mirroring support if (e->flags & RF_WEAPONMODEL) { if (r_lefthand->integer == 2) return; else if (r_lefthand->integer == 1) mirrorview = true; } else if (e->renderfx & RF2_CAMERAMODEL) { if (r_lefthand->integer == 1) mirrormodel = true; } else if (e->flags & RF_MIRRORMODEL) mirrormodel = true; // end mirroring support // clamp r_celshading_width to >= 1.0 if (!r_celshading_width) r_celshading_width = Cvar_Get("r_celshading_width", "4", 0); if (r_celshading_width->value < 1.0f) Cvar_SetValue( "r_celshading_width", 1.0f); paliashdr = (maliasmodel_t *)currentmodel->extradata; R_SetShadeLight (); if (e->flags & RF_DEPTHHACK) // hack the depth range to prevent view model from poking into walls { if (r_newrefdef.rdflags & RDF_NOWORLDMODEL) GL_DepthRange (gldepthmin, gldepthmin + 0.01*(gldepthmax-gldepthmin)); else GL_DepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); } // mirroring support // if (mirrormodel) // R_FlipModel (true); if (mirrorview || mirrormodel) R_FlipModel (true, mirrormodel); for (i=0; i < paliashdr->num_meshes; i++) c_alias_polys += paliashdr->meshes[i].num_tris; qglPushMatrix (); e->angles[ROLL] = e->angles[ROLL] * R_RollMult(); // roll is backwards R_RotateForEntity (e, true); e->angles[ROLL] = e->angles[ROLL] * R_RollMult(); // roll is backwards if ( (e->frame >= paliashdr->num_frames) || (e->frame < 0) ) { VID_Printf (PRINT_ALL, "R_DrawAliasModel %s: no such frame %d\n", currentmodel->name, e->frame); e->frame = 0; e->oldframe = 0; } if ( (e->oldframe >= paliashdr->num_frames) || (e->oldframe < 0)) { VID_Printf (PRINT_ALL, "R_DrawAliasModel %s: no such oldframe %d\n", currentmodel->name, e->oldframe); e->frame = 0; e->oldframe = 0; } if (!r_lerpmodels->integer) e->backlerp = 0; // draw shadow only here if ( volumeShadowOnly || (volumeShadow && r_shadow_self->integer == 0) ) { preLerped = true; R_DrawAliasMeshes (paliashdr, e, mirrormodel, mirrorview, false, true); GL_DisableTexture(0); GL_Enable (GL_BLEND); R_DrawAliasVolumeShadow (paliashdr, bbox); GL_Disable (GL_BLEND); GL_EnableTexture(0); // the following is not called if drawing shadow volume before model if ( volumeShadowOnly ) { qglPopMatrix (); if (mirrorview || mirrormodel) R_FlipModel (false, mirrormodel); if (e->flags & RF_DEPTHHACK) GL_DepthRange (gldepthmin, gldepthmax); if (r_showbbox->integer) { GL_Disable (GL_DEPTH_TEST); R_DrawAliasModelBBox (shadowBBox, e, 0.0f, 0.0f, 1.0f, 1.0f); GL_Enable (GL_DEPTH_TEST); } return; } } // R_DrawAliasMeshes (paliashdr, e, false, mirrormodel, mirrorview); R_DrawAliasMeshes (paliashdr, e, mirrormodel, mirrorview, preLerped, false); qglPopMatrix (); // mirroring support // if (mirrormodel) // R_FlipModel (false); if (mirrorview || mirrormodel) R_FlipModel (false, mirrormodel); // show model bounding box // R_DrawAliasModelBBox (bbox, e, 1.0f, 1.0f, 1.0f, 1.0f); if (r_showbbox->integer) { R_DrawAliasModelBBox (bbox, e, 1.0f, 1.0f, 1.0f, 1.0f); if (volumeShadow) { GL_Disable (GL_DEPTH_TEST); R_DrawAliasModelBBox (shadowBBox, e, 0.0f, 0.0f, 1.0f, 1.0f); GL_Enable (GL_DEPTH_TEST); } } if (e->flags & RF_DEPTHHACK) GL_DepthRange (gldepthmin, gldepthmax); // aliasShadowAlpha = R_CalcShadowAlpha(e); /* if ( !(e->flags & (RF_WEAPONMODEL | RF_NOSHADOW)) // no shadows from shells && !( (e->flags & RF_MASK_SHELL) && (e->flags & RF_TRANSLUCENT) ) && r_shadows->integer >= 1 && aliasShadowAlpha >= DIV255) */ if ( volumeShadow || planarShadow) { qglPushMatrix (); GL_DisableTexture(0); GL_Enable (GL_BLEND); // if (r_shadows->integer == 3) { if (volumeShadow && r_shadow_self->integer != 0) { e->angles[ROLL] = e->angles[ROLL] * R_RollMult(); // roll is backwards R_RotateForEntity (e, true); e->angles[ROLL] = e->angles[ROLL] * R_RollMult(); // roll is backwards R_DrawAliasVolumeShadow (paliashdr, bbox); } // else { else if (planarShadow) { R_RotateForEntity (e, false); R_DrawAliasPlanarShadow (paliashdr); } GL_Disable (GL_BLEND); GL_EnableTexture(0); qglPopMatrix (); } } #if 0 /* ================= R_DrawAliasModelShadow Just draws the shadow for a model ================= */ void R_DrawAliasModelShadow (entity_t *e) { maliasmodel_t *paliashdr; vec3_t bbox[8]; qboolean mirrormodel = false; if (!r_shadows->integer) return; if (e->flags & (RF_WEAPONMODEL | RF_NOSHADOW)) return; // no shadows from shells if ( (e->flags & RF_MASK_SHELL) && (e->flags & RF_TRANSLUCENT) ) return; // also skip this for viewermodels and cameramodels if ( !(e->flags & RF_WEAPONMODEL || e->flags & RF_VIEWERMODEL || e->renderfx & RF2_CAMERAMODEL) ) { if (R_CullAliasModel(bbox, e)) return; } aliasShadowAlpha = R_CalcShadowAlpha(e); if (aliasShadowAlpha < DIV255) // out of range return; if (e->renderfx & RF2_CAMERAMODEL) { if (r_lefthand->integer == 1) mirrormodel = true; } else if (e->flags & RF_MIRRORMODEL) mirrormodel = true; paliashdr = (maliasmodel_t *)currentmodel->extradata; if ( (e->frame >= paliashdr->num_frames) || (e->frame < 0) ) { e->frame = 0; e->oldframe = 0; } if ( (e->oldframe >= paliashdr->num_frames) || (e->oldframe < 0)) { e->frame = 0; e->oldframe = 0; } // if ( !r_lerpmodels->integer ) // e->backlerp = 0; // R_DrawAliasMeshes (paliashdr, e, true, mirrormodel); R_DrawAliasMeshes (paliashdr, e, mirrormodel, false, false, true); qglPushMatrix (); GL_DisableTexture(0); GL_Enable (GL_BLEND); if (r_shadows->integer == 3) { e->angles[ROLL] = e->angles[ROLL] * R_RollMult(); // roll is backwards R_RotateForEntity (e, true); e->angles[ROLL] = e->angles[ROLL] * R_RollMult(); // roll is backwards R_DrawAliasVolumeShadow (paliashdr, bbox); } else { R_RotateForEntity (e, false); R_DrawAliasPlanarShadow (paliashdr); } GL_Disable (GL_BLEND); GL_EnableTexture(0); qglPopMatrix (); } #endif