mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
add4c7cc46
Added new SCR_DrawPic() variants in cl_screen.c. Added new graphics for text fields and sliders in menus. Improved mouse interaction for menu sliders. Added resettargets developer command to default Lazarus and missionpack DLLs. Added hint_test developer command to missionpack DLL. Fixed freeze developer command in default Lazarus and missionpack DLLs so it can be used more than once. More tweaks to Tactician Gunner prox mine safety checks in misssionpack DLL.
1596 lines
45 KiB
C
1596 lines
45 KiB
C
/*
|
|
===========================================================================
|
|
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; i<model_dlights_num; i++)
|
|
{
|
|
l = 2.0 * VLight_GetLightValue (normal, model_dlights[i].direction,
|
|
currententity->angles[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; i<model_dlights_num; i++)
|
|
{
|
|
l = 2.0 * VLight_GetLightValue (normal, model_dlights[i].direction,
|
|
currententity->angles[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 = (skinnum<mesh1->num_skins)?skinnum:0;
|
|
skinnum2 = (skinnum<mesh2->num_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 was originally here
|
|
|
|
// 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; i<rb_vertex; i++)
|
|
colorArray[i][3] = thisalpha*skinParms->envmap;
|
|
|
|
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);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
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->skinnum<mesh.num_skins)?e->skinnum: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; i<mesh.num_tris; i++)
|
|
{
|
|
indexArray[rb_index++] = rb_vertex + mesh.indexes[3*i+0];
|
|
indexArray[rb_index++] = rb_vertex + mesh.indexes[3*i+1];
|
|
indexArray[rb_index++] = rb_vertex + mesh.indexes[3*i+2];
|
|
}
|
|
|
|
for (i=0; i<mesh.num_verts; i++, v++, ov++)
|
|
{
|
|
// lerp verts
|
|
if (!preLerped)
|
|
{
|
|
curNormal[0] = r_sinTable[v->normal[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.tcmod);
|
|
|
|
// 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<mesh.num_tris; i++)
|
|
{
|
|
VectorCopy(tempVertexArray[meshnum][mesh.indexes[3*i+0]], v0);
|
|
VectorCopy(tempVertexArray[meshnum][mesh.indexes[3*i+1]], v1);
|
|
VectorCopy(tempVertexArray[meshnum][mesh.indexes[3*i+2]], v2);
|
|
|
|
triangleFacingLight[i] =
|
|
(light[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1]))
|
|
+ (light[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2]))
|
|
+ (light[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0;
|
|
}
|
|
|
|
shadow_va = shadow_index = 0;
|
|
for (i=0; i<mesh.num_tris; i++)
|
|
{
|
|
if (!triangleFacingLight[i])
|
|
continue;
|
|
|
|
if (mesh.trneighbors[i*3+0] < 0 || !triangleFacingLight[mesh.trneighbors[i*3+0]])
|
|
{
|
|
for (j=0; j<3; j++)
|
|
{
|
|
v0[j]=tempVertexArray[meshnum][mesh.indexes[3*i+1]][j];
|
|
v1[j]=tempVertexArray[meshnum][mesh.indexes[3*i+0]][j];
|
|
v2[j]=v1[j]+((v1[j]-light[j]) * projectdistance);
|
|
v3[j]=v0[j]+((v0[j]-light[j]) * projectdistance);
|
|
}
|
|
indexArray[shadow_index++] = shadow_va+0;
|
|
indexArray[shadow_index++] = shadow_va+1;
|
|
indexArray[shadow_index++] = shadow_va+2;
|
|
indexArray[shadow_index++] = shadow_va+0;
|
|
indexArray[shadow_index++] = shadow_va+2;
|
|
indexArray[shadow_index++] = shadow_va+3;
|
|
|
|
VA_SetElem3(vertexArray[shadow_va], v0[0], v0[1], v0[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v1[0], v1[1], v1[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v2[0], v2[1], v2[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v3[0], v3[1], v3[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
}
|
|
|
|
if (mesh.trneighbors[i*3+1] < 0 || !triangleFacingLight[mesh.trneighbors[i*3+1]])
|
|
{
|
|
for (j=0; j<3; j++)
|
|
{
|
|
v0[j]=tempVertexArray[meshnum][mesh.indexes[3*i+2]][j];
|
|
v1[j]=tempVertexArray[meshnum][mesh.indexes[3*i+1]][j];
|
|
v2[j]=v1[j]+((v1[j]-light[j]) * projectdistance);
|
|
v3[j]=v0[j]+((v0[j]-light[j]) * projectdistance);
|
|
}
|
|
indexArray[shadow_index++] = shadow_va+0;
|
|
indexArray[shadow_index++] = shadow_va+1;
|
|
indexArray[shadow_index++] = shadow_va+2;
|
|
indexArray[shadow_index++] = shadow_va+0;
|
|
indexArray[shadow_index++] = shadow_va+2;
|
|
indexArray[shadow_index++] = shadow_va+3;
|
|
|
|
VA_SetElem3(vertexArray[shadow_va], v0[0], v0[1], v0[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v1[0], v1[1], v1[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v2[0], v2[1], v2[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v3[0], v3[1], v3[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
}
|
|
|
|
if (mesh.trneighbors[i*3+2] < 0 || !triangleFacingLight[mesh.trneighbors[i*3+2]])
|
|
{
|
|
for (j=0; j<3; j++)
|
|
{
|
|
v0[j]=tempVertexArray[meshnum][mesh.indexes[3*i+0]][j];
|
|
v1[j]=tempVertexArray[meshnum][mesh.indexes[3*i+2]][j];
|
|
v2[j]=v1[j]+((v1[j]-light[j]) * projectdistance);
|
|
v3[j]=v0[j]+((v0[j]-light[j]) * projectdistance);
|
|
}
|
|
indexArray[shadow_index++] = shadow_va+0;
|
|
indexArray[shadow_index++] = shadow_va+1;
|
|
indexArray[shadow_index++] = shadow_va+2;
|
|
indexArray[shadow_index++] = shadow_va+0;
|
|
indexArray[shadow_index++] = shadow_va+2;
|
|
indexArray[shadow_index++] = shadow_va+3;
|
|
|
|
VA_SetElem3(vertexArray[shadow_va], v0[0], v0[1], v0[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v1[0], v1[1], v1[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v2[0], v2[1], v2[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v3[0], v3[1], v3[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
shadow_va++;
|
|
}
|
|
}
|
|
|
|
if (nocap) return;
|
|
|
|
// cap the volume
|
|
for (i=0; i<mesh.num_tris; i++)
|
|
{
|
|
if (!triangleFacingLight[i]) // changed to draw only front facing polys- thanx to Kirk Barnes
|
|
continue;
|
|
|
|
VectorCopy(tempVertexArray[meshnum][mesh.indexes[3*i+0]], v0);
|
|
VectorCopy(tempVertexArray[meshnum][mesh.indexes[3*i+1]], v1);
|
|
VectorCopy(tempVertexArray[meshnum][mesh.indexes[3*i+2]], v2);
|
|
|
|
VA_SetElem3(vertexArray[shadow_va], v0[0], v0[1], v0[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
indexArray[shadow_index++] = shadow_va;
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v1[0], v1[1], v1[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
indexArray[shadow_index++] = shadow_va;
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v2[0], v2[1], v2[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
indexArray[shadow_index++] = shadow_va;
|
|
shadow_va++;
|
|
|
|
// rear with reverse order
|
|
for (j=0; j<3; j++)
|
|
{
|
|
v0[j]=tempVertexArray[meshnum][mesh.indexes[3*i+0]][j];
|
|
v1[j]=tempVertexArray[meshnum][mesh.indexes[3*i+1]][j];
|
|
v2[j]=tempVertexArray[meshnum][mesh.indexes[3*i+2]][j];
|
|
|
|
v0[j]=v0[j]+((v0[j]-light[j]) * projectdistance);
|
|
v1[j]=v1[j]+((v1[j]-light[j]) * projectdistance);
|
|
v2[j]=v2[j]+((v2[j]-light[j]) * projectdistance);
|
|
}
|
|
VA_SetElem3(vertexArray[shadow_va], v2[0], v2[1], v2[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
indexArray[shadow_index++] = shadow_va;
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v1[0], v1[1], v1[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
indexArray[shadow_index++] = shadow_va;
|
|
shadow_va++;
|
|
VA_SetElem3(vertexArray[shadow_va], v0[0], v0[1], v0[2]);
|
|
VA_SetElem4(colorArray[shadow_va], 0, 0, 0, thisAlpha);
|
|
indexArray[shadow_index++] = shadow_va;
|
|
shadow_va++;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
R_DrawShadowVolume
|
|
=============
|
|
*/
|
|
void R_DrawShadowVolume (void)
|
|
{
|
|
if (glConfig.drawRangeElements)
|
|
qglDrawRangeElementsEXT(GL_TRIANGLES, 0, shadow_va, shadow_index, GL_UNSIGNED_INT, indexArray);
|
|
else
|
|
qglDrawElements(GL_TRIANGLES, shadow_index, GL_UNSIGNED_INT, indexArray);
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
R_CalcAliasVolumeShadowLightVector
|
|
=============
|
|
*/
|
|
float R_CalcAliasVolumeShadowLightVector (vec3_t bbox[8], vec3_t lightVec)
|
|
{
|
|
vec3_t temp, vecAdd;
|
|
int i, lnum;
|
|
float dist, highest, lowest, projected_distance;
|
|
float angle, cosp, sinp, cosy, siny, cosr, sinr, ix, iy, iz;
|
|
dlight_t *dl;
|
|
|
|
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; i<r_newrefdef.num_dlights; i++, dl++)
|
|
{
|
|
if (VectorCompare(dl->origin, 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; i<r_newrefdef.num_dlights; i++, dl++)
|
|
{
|
|
if (VectorCompare(dl->origin, 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; i<paliashdr->num_meshes; i++)
|
|
{
|
|
skinnum = (currententity->skinnum<paliashdr->meshes[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; i<paliashdr->num_meshes; i++)
|
|
{
|
|
mesh = paliashdr->meshes[i];
|
|
|
|
skinnum = (currententity->skinnum<mesh.num_skins)?currententity->skinnum: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<<j);
|
|
}
|
|
|
|
aggregatemask &= mask;
|
|
}
|
|
|
|
if ( aggregatemask )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
R_CullAliasShadow
|
|
=================
|
|
*/
|
|
static qboolean R_CullAliasShadow (vec3_t bbox[8], entity_t *e)
|
|
{
|
|
int i, j, mask, aggregatemask = ~0;
|
|
float dp;
|
|
|
|
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<<j);
|
|
}
|
|
aggregatemask &= mask;
|
|
}
|
|
|
|
if ( aggregatemask )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
R_DrawAliasModel
|
|
=================
|
|
*/
|
|
void R_DrawAliasModel (entity_t *e)
|
|
{
|
|
maliasmodel_t *paliashdr;
|
|
vec3_t bbox[8], shadowBBox[8];
|
|
qboolean mirrorview = false, mirrormodel = false;
|
|
qboolean planarShadow = false, volumeShadow = false, volumeShadowOnly = false;
|
|
qboolean preLerped = false;
|
|
int i;
|
|
|
|
// determine if this model will have a volume shadow
|
|
if ( (r_shadows->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
|