quakespasm/Quake/r_alias.c

1541 lines
50 KiB
C

/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2010-2014 QuakeSpasm developers
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.
*/
//r_alias.c -- alias model rendering
#include "quakedef.h"
extern cvar_t r_drawflat, gl_overbright_models, gl_fullbrights, r_lerpmodels, r_lerpmove; //johnfitz
extern cvar_t scr_fov, cl_gun_fovscale;
#define NUMVERTEXNORMALS 162
float r_avertexnormals[NUMVERTEXNORMALS][3] =
{
#include "anorms.h"
};
extern vec3_t lightcolor; //johnfitz -- replaces "float shadelight" for lit support
// precalculated dot products for quantized angles
#define SHADEDOT_QUANT 16
float r_avertexnormal_dots[SHADEDOT_QUANT][256] =
{
#include "anorm_dots.h"
};
extern vec3_t lightspot;
float *shadedots = r_avertexnormal_dots[0];
vec3_t shadevector;
float entalpha; //johnfitz
qboolean overbright; //johnfitz
qboolean shading = true; //johnfitz -- if false, disable vertex shading for various reasons (fullbright, r_lightmap, showtris, etc)
//johnfitz -- struct for passing lerp information to drawing functions
typedef struct {
short pose1;
short pose2;
float blend;
vec3_t origin;
vec3_t angles;
bonepose_t *bonestate;
} lerpdata_t;
//johnfitz
enum
{
ALIAS_GLSL_BASIC,
ALIAS_GLSL_SKELETAL,
ALIAS_GLSL_MODES
};
typedef struct
{
int maxbones;
GLuint program;
// uniforms used in vert shader
GLuint bonesLoc;
GLuint blendLoc;
GLuint shadevectorLoc;
GLuint lightColorLoc;
// uniforms used in frag shader
GLuint texLoc;
GLuint lowerTexLoc;
GLuint upperTexLoc;
GLuint fullbrightTexLoc;
GLuint useFullbrightTexLoc;
GLuint useOverbrightLoc;
GLuint useAlphaTestLoc;
GLuint colorTintLoc;
} aliasglsl_t;
static aliasglsl_t r_alias_glsl[ALIAS_GLSL_MODES];
#define pose1VertexAttrIndex 0
#define pose1NormalAttrIndex 1
#define pose2VertexAttrIndex 2
#define pose2NormalAttrIndex 3
#define texCoordsAttrIndex 4
#define vertColoursAttrIndex 5
#define boneWeightAttrIndex pose2VertexAttrIndex
#define boneIndexAttrIndex pose2NormalAttrIndex
/*
=============
GLARB_GetXYZOffset
Returns the offset of the first vertex's meshxyz_t.xyz in the vbo for the given
model and pose.
=============
*/
static void *GLARB_GetXYZOffset_MDL (aliashdr_t *hdr, int pose)
{
const size_t xyzoffs = offsetof (meshxyz_mdl_t, xyz);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_mdl_t)) + xyzoffs);
}
static void *GLARB_GetXYZOffset_MDLQF (aliashdr_t *hdr, int pose)
{
const size_t xyzoffs = offsetof (meshxyz_mdl16_t, xyz);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_mdl16_t)) + xyzoffs);
}
static void *GLARB_GetXYZOffset_MD3 (aliashdr_t *hdr, int pose)
{
const size_t xyzoffs = offsetof (meshxyz_md3_t, xyz);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_md3_t)) + xyzoffs);
}
/*
=============
GLARB_GetNormalOffset
Returns the offset of the first vertex's meshxyz_t.normal in the vbo for the
given model and pose.
=============
*/
static void *GLARB_GetNormalOffset_MDL (aliashdr_t *hdr, int pose)
{
const size_t normaloffs = offsetof (meshxyz_mdl_t, normal);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_mdl_t)) + normaloffs);
}
static void *GLARB_GetNormalOffset_MDLQF (aliashdr_t *hdr, int pose)
{
const size_t normaloffs = offsetof (meshxyz_mdl16_t, normal);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_mdl16_t)) + normaloffs);
}
static void *GLARB_GetNormalOffset_MD3 (aliashdr_t *hdr, int pose)
{
const size_t normaloffs = offsetof (meshxyz_md3_t, normal);
return currententity->model->meshvboptr+(hdr->vbovertofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_md3_t)) + normaloffs);
}
/*
=============
GLAlias_CreateShaders
=============
*/
void GLAlias_CreateShaders (void)
{
int i;
aliasglsl_t *glsl;
char processedVertSource[8192], *defines;
const glsl_attrib_binding_t bindings[] = {
{ "TexCoords", texCoordsAttrIndex },
{ "Pose1Vert", pose1VertexAttrIndex },
{ "Pose1Normal", pose1NormalAttrIndex },
{ "Pose2Vert", pose2VertexAttrIndex },
{ "Pose2Normal", pose2NormalAttrIndex },
{ "VertColours", vertColoursAttrIndex }
};
const GLchar *vertSource = \
"#version 110\n"
"%s"
"\n"
"uniform vec3 ShadeVector;\n"
"uniform vec4 LightColor;\n"
"attribute vec4 TexCoords; // only xy are used \n"
"attribute vec4 Pose1Vert;\n"
"attribute vec3 Pose1Normal;\n"
"#ifdef SKELETAL\n"
"#define BoneWeight Pose2Vert\n"
"#define BoneIndex Pose2Normal\n"
"attribute vec4 BoneWeight;\n"
"attribute vec4 BoneIndex;\n"
"attribute vec4 VertColours;\n"
"uniform vec4 BoneTable[MAXBONES*3];\n" //fixme: should probably try to use a UBO or SSBO.
"#else\n"
"uniform float Blend;\n"
"attribute vec4 Pose2Vert;\n"
"attribute vec3 Pose2Normal;\n"
"#endif\n"
"\n"
"varying float FogFragCoord;\n"
"\n"
"float r_avertexnormal_dot(vec3 vertexnormal) // from MH \n"
"{\n"
" float dot = dot(vertexnormal, ShadeVector);\n"
" // wtf - this reproduces anorm_dots within as reasonable a degree of tolerance as the >= 0 case\n"
" if (dot < 0.0)\n"
" return 1.0 + dot * (13.0 / 44.0);\n"
" else\n"
" return 1.0 + dot;\n"
"}\n"
"void main()\n"
"{\n"
" gl_TexCoord[0] = TexCoords;\n"
"#ifdef SKELETAL\n"
" mat4 wmat;"
" wmat[0] = BoneTable[0+3*int(BoneIndex.x)] * BoneWeight.x;"
" wmat[0] += BoneTable[0+3*int(BoneIndex.y)] * BoneWeight.y;"
" wmat[0] += BoneTable[0+3*int(BoneIndex.z)] * BoneWeight.z;"
" wmat[0] += BoneTable[0+3*int(BoneIndex.w)] * BoneWeight.w;"
" wmat[1] = BoneTable[1+3*int(BoneIndex.x)] * BoneWeight.x;"
" wmat[1] += BoneTable[1+3*int(BoneIndex.y)] * BoneWeight.y;"
" wmat[1] += BoneTable[1+3*int(BoneIndex.z)] * BoneWeight.z;"
" wmat[1] += BoneTable[1+3*int(BoneIndex.w)] * BoneWeight.w;"
" wmat[2] = BoneTable[2+3*int(BoneIndex.x)] * BoneWeight.x;"
" wmat[2] += BoneTable[2+3*int(BoneIndex.y)] * BoneWeight.y;"
" wmat[2] += BoneTable[2+3*int(BoneIndex.z)] * BoneWeight.z;"
" wmat[2] += BoneTable[2+3*int(BoneIndex.w)] * BoneWeight.w;"
" wmat[3] = vec4(0.0,0.0,0.0,1.0);\n"
" vec4 lerpedVert = (vec4(Pose1Vert.xyz, 1.0) * wmat);\n"
" float dot1 = r_avertexnormal_dot(normalize((vec4(Pose1Normal.xyz, 0.0) * wmat).xyz));\n"
"#else\n"
" vec4 lerpedVert = mix(vec4(Pose1Vert.xyz, 1.0), vec4(Pose2Vert.xyz, 1.0), Blend);\n"
" float dot1 = mix(r_avertexnormal_dot(Pose1Normal), r_avertexnormal_dot(Pose2Normal), Blend);\n"
"#endif\n"
" gl_Position = gl_ModelViewProjectionMatrix * lerpedVert;\n"
" FogFragCoord = gl_Position.w;\n"
" gl_FrontColor = LightColor * vec4(vec3(dot1), 1.0);\n"
"#ifdef SKELETAL\n"
" gl_FrontColor *= VertColours;\n" //this is basically only useful for vertex alphas.
"#endif\n"
"}\n";
const GLchar *fragSource = \
"#version 110\n"
"\n"
"uniform sampler2D Tex;\n"
"uniform sampler2D LowerTex;\n" //team colour
"uniform sampler2D UpperTex;\n" //personal colour
"uniform sampler2D FullbrightTex;\n"
"uniform bool UseFullbrightTex;\n"
"uniform bool UseOverbright;\n"
"uniform bool UseAlphaTest;\n"
"uniform vec4 ColourTint[3];\n" //base+bot+top+fb
"\n"
"varying float FogFragCoord;\n"
"\n"
"void main()\n"
"{\n"
" vec4 result = texture2D(Tex, gl_TexCoord[0].xy);\n" //base
" if (ColourTint[0].a != 0.0) result.rgb += texture2D(LowerTex, gl_TexCoord[0].xy).rgb * ColourTint[0].rgb;\n" //team/lower/trousers
" if (ColourTint[1].a != 0.0) result.rgb += texture2D(UpperTex, gl_TexCoord[0].xy).rgb * ColourTint[1].rgb;\n" //personal/upper/torso
" if (UseAlphaTest && (result.a < 0.666))\n"
" discard;\n"
" result *= gl_Color;\n" //vertex lighting results (and colormod).
" if (UseOverbright)\n"
" result.rgb *= 2.0;\n"
" if (UseFullbrightTex)\n"
" result += texture2D(FullbrightTex, gl_TexCoord[0].xy) * ColourTint[2];\n" //fullbrights (with glowmod)
" result = clamp(result, 0.0, 1.0);\n"
" float fog = exp(-gl_Fog.density * gl_Fog.density * FogFragCoord * FogFragCoord);\n"
" fog = clamp(fog, 0.0, 1.0) * gl_Fog.color.a;\n"
" result.rgb = mix(gl_Fog.color.rgb, result.rgb, fog);\n"
" result.a *= gl_Color.a;\n" // FIXME: This will make almost transparent things cut holes though heavy fog
" gl_FragColor = result;\n"
"}\n";
if (!gl_glsl_alias_able)
return;
for (i = 0; i < ALIAS_GLSL_MODES; i++)
{
glsl = &r_alias_glsl[i];
if (i == ALIAS_GLSL_SKELETAL)
{
defines = "#define SKELETAL\n#define MAXBONES 64\n";
glsl->maxbones = 64;
}
else
{
defines = "";
glsl->maxbones = 0;
}
q_snprintf(processedVertSource, sizeof(processedVertSource), vertSource, defines);
glsl->program = GL_CreateProgram (processedVertSource, fragSource, sizeof(bindings)/sizeof(bindings[0]), bindings);
if (glsl->program != 0)
{
// get uniform locations
if (i == ALIAS_GLSL_SKELETAL)
{
glsl->bonesLoc = GL_GetUniformLocation (&glsl->program, "BoneTable");
glsl->blendLoc = -1;
}
else
{
glsl->bonesLoc = -1;
glsl->blendLoc = GL_GetUniformLocation (&glsl->program, "Blend");
}
glsl->shadevectorLoc = GL_GetUniformLocation (&glsl->program, "ShadeVector");
glsl->lightColorLoc = GL_GetUniformLocation (&glsl->program, "LightColor");
glsl->texLoc = GL_GetUniformLocation (&glsl->program, "Tex");
glsl->lowerTexLoc = GL_GetUniformLocation (&glsl->program, "LowerTex");
glsl->upperTexLoc = GL_GetUniformLocation (&glsl->program, "UpperTex");
glsl->fullbrightTexLoc = GL_GetUniformLocation (&glsl->program, "FullbrightTex");
glsl->useFullbrightTexLoc = GL_GetUniformLocation (&glsl->program, "UseFullbrightTex");
glsl->useOverbrightLoc = GL_GetUniformLocation (&glsl->program, "UseOverbright");
glsl->useAlphaTestLoc = GL_GetUniformLocation (&glsl->program, "UseAlphaTest");
glsl->colorTintLoc = GL_GetUniformLocation (&glsl->program, "ColourTint");
//we can do this here, its not going to change.
GL_UseProgramFunc (glsl->program);
GL_Uniform1iFunc (glsl->texLoc, 0);
GL_Uniform1iFunc (glsl->fullbrightTexLoc, 1);
GL_Uniform1iFunc (glsl->lowerTexLoc, 2);
GL_Uniform1iFunc (glsl->upperTexLoc, 3);
GL_UseProgramFunc (0);
}
}
}
/*
=============
GL_DrawAliasFrame_GLSL -- ericw
Optimized alias model drawing codepath.
Compared to the original GL_DrawAliasFrame, this makes 1 draw call,
no vertex data is uploaded (it's already in the r_meshvbo and r_meshindexesvbo
static VBOs), and lerping and lighting is done in the vertex shader.
Supports optional overbright, optional fullbright pixels.
Based on code by MH from RMQEngine
=============
*/
static void GL_DrawAliasFrame_GLSL (aliasglsl_t *glsl, aliashdr_t *paliashdr, lerpdata_t lerpdata, struct skintextures_s tex, entity_t *e)
{
GLfloat tints[3][4];
float blend;
if (lerpdata.pose1 != lerpdata.pose2)
{
blend = lerpdata.blend;
}
else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled
{
blend = 0;
}
GL_UseProgramFunc (glsl->program);
GL_BindBuffer (GL_ARRAY_BUFFER, currententity->model->meshvbo);
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, currententity->model->meshindexesvbo);
GL_EnableVertexAttribArrayFunc (texCoordsAttrIndex);
GL_EnableVertexAttribArrayFunc (pose1VertexAttrIndex);
GL_EnableVertexAttribArrayFunc (pose2VertexAttrIndex);
GL_EnableVertexAttribArrayFunc (pose1NormalAttrIndex);
GL_EnableVertexAttribArrayFunc (pose2NormalAttrIndex);
switch(paliashdr->poseverttype)
{
case PV_QUAKE1:
GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, 0, currententity->model->meshvboptr+paliashdr->vbostofs);
GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_mdl_t), GLARB_GetXYZOffset_MDL (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_mdl_t), GLARB_GetXYZOffset_MDL (paliashdr, lerpdata.pose2));
// GL_TRUE to normalize the signed bytes to [-1 .. 1]
GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_mdl_t), GLARB_GetNormalOffset_MDL (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_mdl_t), GLARB_GetNormalOffset_MDL (paliashdr, lerpdata.pose2));
break;
case PV_QUAKEFORGE:
GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, 0, currententity->model->meshvboptr+paliashdr->vbostofs);
GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_UNSIGNED_SHORT, GL_FALSE, sizeof (meshxyz_mdl16_t), GLARB_GetXYZOffset_MDLQF (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_UNSIGNED_SHORT, GL_FALSE, sizeof (meshxyz_mdl16_t), GLARB_GetXYZOffset_MDLQF (paliashdr, lerpdata.pose2));
// GL_TRUE to normalize the signed bytes to [-1 .. 1]
GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_mdl16_t), GLARB_GetNormalOffset_MDLQF (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_mdl16_t), GLARB_GetNormalOffset_MDLQF (paliashdr, lerpdata.pose2));
break;
case PV_QUAKE3:
GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, 0, currententity->model->meshvboptr+paliashdr->vbostofs);
GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_SHORT, GL_FALSE, sizeof (meshxyz_md3_t), GLARB_GetXYZOffset_MD3 (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_SHORT, GL_FALSE, sizeof (meshxyz_md3_t), GLARB_GetXYZOffset_MD3 (paliashdr, lerpdata.pose2));
// GL_TRUE to normalize the signed bytes to [-1 .. 1]
GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_md3_t), GLARB_GetNormalOffset_MD3 (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_md3_t), GLARB_GetNormalOffset_MD3 (paliashdr, lerpdata.pose2));
break;
case PV_IQM:
{
const iqmvert_t *pose = (const iqmvert_t*)(currententity->model->meshvboptr+paliashdr->vbovertofs + (paliashdr->numverts_vbo * 0 * sizeof (iqmvert_t)));
GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 3, GL_FLOAT, GL_FALSE, sizeof (iqmvert_t), pose->xyz);
GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 3, GL_FLOAT, GL_FALSE, sizeof (iqmvert_t), pose->norm);
GL_VertexAttribPointerFunc (boneWeightAttrIndex, 4, GL_FLOAT, GL_FALSE, sizeof (iqmvert_t), pose->weight);
GL_VertexAttribPointerFunc (boneIndexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (iqmvert_t), pose->idx);
GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, sizeof (iqmvert_t), pose->st);
GL_EnableVertexAttribArrayFunc (vertColoursAttrIndex);
GL_VertexAttribPointerFunc (vertColoursAttrIndex, 4, GL_FLOAT, GL_FALSE, sizeof (iqmvert_t), pose->rgba);
}
break;
}
#define MyVectorScale(a,s,b) do{(b)[0]=(s)*(a)[0];(b)[1]=(s)*(a)[1];(b)[2]=(s)*(a)[2];}while(0)
if (e->netstate.colormap > 0 && e->netstate.colormap <= cl.maxclients)
{
scoreboard_t *sb = &cl.scores[e->netstate.colormap-1];
byte *pal;
pal = CL_PLColours_ToRGB(&sb->pants);
MyVectorScale(pal, 1.0/255, tints[0]);
pal = CL_PLColours_ToRGB(&sb->shirt);
MyVectorScale(pal, 1.0/255, tints[1]);
}
else
{
MyVectorScale((byte*)&d_8to24table[BOTTOM_RANGE+15], 1.0/255, tints[0]);
MyVectorScale((byte*)&d_8to24table[TOP_RANGE+15], 1.0/255, tints[1]);
}
tints[0][3] = 1;
tints[1][3] = 1;
MyVectorScale(e->netstate.glowmod, 1.0/32, tints[2]); tints[2][3] = 1;
// set textures
GL_SelectTexture (GL_TEXTURE0);
GL_Bind (tex.base);
if (tex.luma)
{
GL_SelectTexture (GL_TEXTURE1);
GL_Bind (tex.luma);
}
else
tints[2][0] = tints[2][1] = tints[2][2] = tints[2][3] = 0;
if (tex.lower)
{
GL_SelectTexture (GL_TEXTURE2);
GL_Bind (tex.lower);
}
else
tints[0][0] = tints[0][1] = tints[0][2] = tints[0][3] = 0; //ask the glsl to not read it
if (tex.upper)
{
GL_SelectTexture (GL_TEXTURE3);
GL_Bind (tex.upper);
}
else
tints[1][0] = tints[1][1] = tints[1][2] = tints[1][3] = 0;
// set uniforms
if (glsl->blendLoc != -1)
GL_Uniform1fFunc (glsl->blendLoc, blend);
if (glsl->bonesLoc != -1)
GL_Uniform4fvFunc (glsl->bonesLoc, paliashdr->numbones*3, lerpdata.bonestate->mat);
GL_Uniform3fFunc (glsl->shadevectorLoc, shadevector[0], shadevector[1], shadevector[2]);
GL_Uniform4fFunc (glsl->lightColorLoc, lightcolor[0], lightcolor[1], lightcolor[2], entalpha); //this includes colormod
GL_Uniform1iFunc (glsl->useFullbrightTexLoc, (tex.luma != NULL) ? 1 : 0);
GL_Uniform1fFunc (glsl->useOverbrightLoc, overbright ? 1 : 0);
GL_Uniform1iFunc (glsl->useAlphaTestLoc, (currententity->model->flags & MF_HOLEY) ? 1 : 0);
GL_Uniform4fvFunc(glsl->colorTintLoc, countof(tints), tints[0]); //colourmapping and glowmod.
// draw
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, currententity->model->meshindexesvboptr+paliashdr->eboofs);
// clean up
GL_DisableVertexAttribArrayFunc (texCoordsAttrIndex);
GL_DisableVertexAttribArrayFunc (pose1VertexAttrIndex);
GL_DisableVertexAttribArrayFunc (pose2VertexAttrIndex);
GL_DisableVertexAttribArrayFunc (pose1NormalAttrIndex);
GL_DisableVertexAttribArrayFunc (pose2NormalAttrIndex);
GL_DisableVertexAttribArrayFunc (vertColoursAttrIndex);
GL_UseProgramFunc (0);
GL_SelectTexture (GL_TEXTURE0);
rs_aliaspasses += paliashdr->numtris;
}
/*
=============
GL_DrawAliasFrame
-- johnfitz -- rewritten to support colored light, lerping, entalpha, multitexture, and r_drawflat
-- spike -- rewritten to use vertex arrays, which should be slightly faster thanks to less branches+gl calls (note that this requires gl1.1, which we depend on anyway for texture objects, and is pretty much universal.
=============
*/
void GL_DrawAliasFrame (aliashdr_t *paliashdr, lerpdata_t lerpdata)
{
static vec3_t vpos[65536];
static vec4_t vc[65536];
int i;
float blend, iblend;
const float *texcoords = (const float *)(currententity->model->meshvboptr+paliashdr->vbostofs);
int texcoordstride = 0;
if (lerpdata.pose1 != lerpdata.pose2)
{
blend = lerpdata.blend;
iblend = 1.0-blend;
}
else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled
{
blend = 1;
iblend = 0;
}
//pose1*iblend + pose2*blend
if (shading && r_drawflat_cheatsafe)
{
shading = false;
glColor4f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0, entalpha);
}
glEnableClientState(GL_VERTEX_ARRAY);
switch(paliashdr->poseverttype)
{
case PV_QUAKE1:
case PV_QUAKEFORGE: //just going to ignore the extra data here.
{
trivertx_t *verts1 = (trivertx_t*)((byte *)paliashdr + paliashdr->vertexes) + lerpdata.pose1 * paliashdr->numverts_vbo*(paliashdr->poseverttype==PV_QUAKEFORGE?2:1);
trivertx_t *verts2 = (trivertx_t*)((byte *)paliashdr + paliashdr->vertexes) + lerpdata.pose2 * paliashdr->numverts_vbo*(paliashdr->poseverttype==PV_QUAKEFORGE?2:1);
if (iblend)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vpos[i][0] = verts1[i].v[0] * iblend + blend * verts2[i].v[0];
vpos[i][1] = verts1[i].v[1] * iblend + blend * verts2[i].v[1];
vpos[i][2] = verts1[i].v[2] * iblend + blend * verts2[i].v[2];
}
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glVertexPointer(3, GL_FLOAT, sizeof (vpos[0]), vpos);
if (shading)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vc[i][0] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[0];
vc[i][1] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[1];
vc[i][2] = (shadedots[verts1->lightnormalindex]*iblend + shadedots[verts2->lightnormalindex]*blend) * lightcolor[2];
vc[i][3] = entalpha;
}
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, sizeof(*vc), vc);
}
}
else
{
if (shading)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vc[i][0] = shadedots[verts2->lightnormalindex] * lightcolor[0];
vc[i][1] = shadedots[verts2->lightnormalindex] * lightcolor[1];
vc[i][2] = shadedots[verts2->lightnormalindex] * lightcolor[2];
vc[i][3] = entalpha;
}
glEnableClientState(GL_COLOR_ARRAY);
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glColorPointer(4, GL_FLOAT, 0, vc);
}
//glVertexPointer may not take GL_UNSIGNED_BYTE, which means we can't use our vbos. attribute 0 MAY be vertex coords, but I don't want to depend on that.
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vpos[i][0] = verts2[i].v[0];
vpos[i][1] = verts2[i].v[1];
vpos[i][2] = verts2[i].v[2];
}
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glVertexPointer(3, GL_FLOAT, sizeof (vpos[0]), vpos);
}
}
break;
case PV_QUAKE3:
{
md3XyzNormal_t *verts1 = (md3XyzNormal_t*)((byte *)paliashdr + paliashdr->vertexes) + lerpdata.pose1 * paliashdr->numverts_vbo;
md3XyzNormal_t *verts2 = (md3XyzNormal_t*)((byte *)paliashdr + paliashdr->vertexes) + lerpdata.pose2 * paliashdr->numverts_vbo;
if (iblend)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vpos[i][0] = verts1[i].xyz[0] * iblend + blend * verts2[i].xyz[0];
vpos[i][1] = verts1[i].xyz[1] * iblend + blend * verts2[i].xyz[1];
vpos[i][2] = verts1[i].xyz[2] * iblend + blend * verts2[i].xyz[2];
}
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glVertexPointer(3, GL_FLOAT, sizeof (vpos[0]), vpos);
if (shading)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vec3_t n;
float dot;
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
//this should be a table.
float lat = (float)verts2[i].latlong[0] * (2 * M_PI)*(1.0 / 255.0);
float lng = (float)verts2[i].latlong[1] * (2 * M_PI)*(1.0 / 255.0);
n[0] = blend * cos ( lng ) * sin ( lat );
n[1] = blend * sin ( lng ) * sin ( lat );
n[2] = blend * cos ( lat );
lat = (float)verts1[i].latlong[0] * (2 * M_PI)*(1.0 / 255.0);
lng = (float)verts1[i].latlong[1] * (2 * M_PI)*(1.0 / 255.0);
n[0] += iblend * cos ( lng ) * sin ( lat );
n[1] += iblend * sin ( lng ) * sin ( lat );
n[2] += iblend * cos ( lat );
dot = DotProduct(n, shadevector);
if (dot < 0.0) //bizzare maths guessed by mh
dot = 1.0 + dot * (13.0 / 44.0);
else
dot = 1.0 + dot;
vc[i][0] = dot * lightcolor[0];
vc[i][1] = dot * lightcolor[1];
vc[i][2] = dot * lightcolor[2];
vc[i][3] = entalpha;
}
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, 0, vc);
}
}
else
{
if (shading)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
vec3_t n;
float dot;
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
//this should be a table.
float lat = (float)verts2[i].latlong[0] * (2 * M_PI)*(1.0 / 255.0);
float lng = (float)verts2[i].latlong[1] * (2 * M_PI)*(1.0 / 255.0);
n[0] = cos ( lng ) * sin ( lat );
n[1] = sin ( lng ) * sin ( lat );
n[2] = cos ( lat );
dot = DotProduct(n, shadevector);
if (dot < 0.0) //bizzare maths guessed by mh
dot = 1.0 + dot * (13.0 / 44.0);
else
dot = 1.0 + dot;
vc[i][0] = dot * lightcolor[0];
vc[i][1] = dot * lightcolor[1];
vc[i][2] = dot * lightcolor[2];
vc[i][3] = entalpha;
}
glEnableClientState(GL_COLOR_ARRAY);
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glColorPointer(4, GL_FLOAT, 0, vc);
}
GL_BindBuffer (GL_ARRAY_BUFFER, currententity->model->meshvbo);
glVertexPointer(3, GL_SHORT, sizeof (meshxyz_md3_t), GLARB_GetXYZOffset_MD3 (paliashdr, lerpdata.pose2));
}
}
break;
case PV_IQM:
{ //iqm does its blending using bones instead of verts, so we only have to care about one pose here
int morphpose = 0;
const iqmvert_t *verts2 = (const iqmvert_t*)((byte *)paliashdr + paliashdr->vertexes) + morphpose * paliashdr->numverts_vbo;
const iqmvert_t *vboverts2 = (const iqmvert_t*)(currententity->model->meshvboptr+paliashdr->vbovertofs) + (paliashdr->numverts_vbo * morphpose);
if (shading)
{
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
float dot;
dot = DotProduct(verts2[i].norm, shadevector); //NOTE: ignores animated normals
if (dot < 0.0) //bizzare maths guessed by mh
dot = 1.0 + dot * (13.0 / 44.0);
else
dot = 1.0 + dot;
vc[i][0] = dot * lightcolor[0] * verts2[i].rgba[0];
vc[i][1] = dot * lightcolor[1] * verts2[i].rgba[1];
vc[i][2] = dot * lightcolor[2] * verts2[i].rgba[2];
vc[i][3] = entalpha * verts2[i].rgba[3];
}
glEnableClientState(GL_COLOR_ARRAY);
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glColorPointer(4, GL_FLOAT, 0, vc);
}
if (lerpdata.bonestate)
{ //oh dear. its animated. and we don't have any glsl to animate it for us.
bonepose_t pose;
const bonepose_t *in;
const float *xyz;
float w;
int j, k;
for (i = 0; i < paliashdr->numverts_vbo; i++)
{
//lerp the matrix... this is less of a nightmare in glsl...
in = lerpdata.bonestate + verts2[i].idx[0];
w = verts2[i].weight[0];
for (j = 0; j < 12; j++)
pose.mat[j] = in->mat[j] * w;
for (k = 1; k < 3; k++)
{
w = verts2[i].weight[k];
if (!w)
continue;
in = lerpdata.bonestate + verts2[i].idx[k];
for (j = 0; j < 12; j++)
pose.mat[j] += in->mat[j] * w;
}
xyz = verts2[i].xyz;
vpos[i][0] = xyz[0]*pose.mat[0] + xyz[1]*pose.mat[1] + xyz[2]*pose.mat[2] + pose.mat[3];
vpos[i][1] = xyz[0]*pose.mat[4] + xyz[1]*pose.mat[5] + xyz[2]*pose.mat[6] + pose.mat[7];
vpos[i][2] = xyz[0]*pose.mat[8] + xyz[1]*pose.mat[9] + xyz[2]*pose.mat[10] + pose.mat[11];
}
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
glVertexPointer(3, GL_FLOAT, 0, vpos);
}
else
{
GL_BindBuffer (GL_ARRAY_BUFFER, currententity->model->meshvbo);
glVertexPointer(3, GL_FLOAT, sizeof (iqmvert_t), vboverts2->xyz);
}
texcoordstride = sizeof(iqmvert_t);
texcoords = vboverts2->st;
}
break;
}
// set textures
GL_BindBuffer (GL_ARRAY_BUFFER, currententity->model->meshvbo);
if (mtexenabled)
{
GL_ClientActiveTextureFunc (GL_TEXTURE0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, texcoordstride, texcoords);
GL_ClientActiveTextureFunc (GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, texcoordstride, texcoords);
}
else
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, texcoordstride, texcoords);
}
// draw
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, currententity->model->meshindexesvbo);
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, currententity->model->meshindexesvboptr + paliashdr->eboofs);
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
GL_BindBuffer (GL_ARRAY_BUFFER, 0);
// clean up
if (mtexenabled)
{
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE0);
}
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
rs_aliaspasses += paliashdr->numtris;
}
/*
=================
R_SetupAliasFrame -- johnfitz -- rewritten to support lerping
=================
*/
void R_SetupAliasFrame (aliashdr_t *paliashdr, entity_t *e, lerpdata_t *lerpdata)
{
int frame = e->frame;
int posenum, numposes;
if ((frame >= paliashdr->numframes) || (frame < 0))
{
Con_DPrintf ("R_AliasSetupFrame: no such frame %d for '%s'\n", frame, e->model->name);
frame = 0;
}
if (e->lerpflags & LERP_EXPLICIT)
{
int frame2 = e->lerp.snap.frame2;
float frac = e->lerp.snap.lerpfrac;
float time = cl.time;
if ((frame2 >= paliashdr->numframes) || (frame2 < 0))
frame2 = 0;
if (paliashdr->frames[(frac > 0.5)?frame2:frame].numposes > 1)
{ //our stronger sequence is a framegroup, but we can only do two-way blends.
if (frac > 0.5)
{
frame = frame2;
time = e->lerp.snap.time[1];
}
else
time = e->lerp.snap.time[0];
if (time < 0)
time = 0; //just in case...
frac = (time / paliashdr->frames[frame].interval);
posenum = (int)frac;
lerpdata->blend = (frac - posenum);
posenum += paliashdr->frames[frame].firstpose;
numposes = paliashdr->frames[frame].numposes;
lerpdata->pose1 = (posenum)%numposes;
lerpdata->pose2 = (posenum+1)%numposes;
}
else
{
lerpdata->blend = frac;
lerpdata->pose1 = paliashdr->frames[frame].firstpose;
lerpdata->pose1 += (unsigned int)(e->lerp.snap.time[0]/paliashdr->frames[frame].interval) % paliashdr->frames[frame].numposes;
lerpdata->pose2 = paliashdr->frames[frame2].firstpose;
lerpdata->pose2 += (unsigned int)(e->lerp.snap.time[1]/paliashdr->frames[frame2].interval) % paliashdr->frames[frame].numposes;
}
}
else
{
posenum = paliashdr->frames[frame].firstpose;
numposes = paliashdr->frames[frame].numposes;
if (numposes > 1)
{
float time = cl.time + e->syncbase; //Spike: Readded syncbase
if (time < 0)
time = 0; //just in case...
e->lerp.state.lerptime = paliashdr->frames[frame].interval; //FIXME: no per-frame intervals
posenum += (int)(time / e->lerp.state.lerptime) % numposes;
}
else
e->lerp.state.lerptime = 0.1;
if (e->lerpflags & LERP_RESETANIM) //kill any lerp in progress
{
e->lerp.state.lerpstart = 0;
e->lerp.state.previouspose = posenum;
e->lerp.state.currentpose = posenum;
e->lerpflags -= LERP_RESETANIM;
}
else if (e->lerp.state.currentpose != posenum) // pose changed, start new lerp
{
if (e->lerpflags & LERP_RESETANIM2) //defer lerping one more time
{
e->lerp.state.lerpstart = 0;
e->lerp.state.previouspose = posenum;
e->lerp.state.currentpose = posenum;
e->lerpflags -= LERP_RESETANIM2;
}
else
{
e->lerp.state.lerpstart = cl.time;
e->lerp.state.previouspose = e->lerp.state.currentpose;
e->lerp.state.currentpose = posenum;
}
}
//set up values
if (r_lerpmodels.value && !(e->model->flags & MOD_NOLERP && r_lerpmodels.value != 2))
{
if (e->lerpflags & LERP_FINISH && numposes == 1)
lerpdata->blend = CLAMP (0.0f, (float)(cl.time - e->lerp.state.lerpstart) / (e->lerpfinish - e->lerp.state.lerpstart), 1.0f);
else
lerpdata->blend = CLAMP (0.0f, (float)(cl.time - e->lerp.state.lerpstart) / e->lerp.state.lerptime, 1.0f);
lerpdata->pose1 = e->lerp.state.previouspose;
lerpdata->pose2 = e->lerp.state.currentpose;
}
else //don't lerp
{
lerpdata->blend = 1;
lerpdata->pose1 = posenum;
lerpdata->pose2 = posenum;
}
}
if (paliashdr->numboneposes)
{
static bonepose_t inverted[256];
bonepose_t lerpbones[256], l;
int b, j;
const boneinfo_t *bi = (const boneinfo_t *)((byte*)paliashdr + paliashdr->boneinfo);
const bonepose_t *p1 = (const bonepose_t *)((byte*)paliashdr + paliashdr->boneposedata) + lerpdata->pose1*paliashdr->numbones;
const bonepose_t *p2 = (const bonepose_t *)((byte*)paliashdr + paliashdr->boneposedata) + lerpdata->pose2*paliashdr->numbones;
float w2 = lerpdata->blend;
float w1 = 1-w2;
for (b = 0; b < paliashdr->numbones; b++, p1++, p2++)
{
//interpolate it
for (j = 0; j < 12; j++)
l.mat[j] = p1->mat[j]*w1 + p2->mat[j]*w2;
//concat it onto the parent (relative->abs)
if (bi[b].parent < 0)
memcpy(lerpbones[b].mat, l.mat, sizeof(l.mat));
else
R_ConcatTransforms((void*)lerpbones[bi[b].parent].mat, (void*)l.mat, (void*)lerpbones[b].mat);
//and finally invert it
R_ConcatTransforms((void*)lerpbones[b].mat, (void*)bi[b].inverse.mat, (void*)inverted[b].mat);
}
lerpdata->bonestate = inverted; //and now we can use it.
}
else
lerpdata->bonestate = NULL;
}
/*
=================
R_SetupEntityTransform -- johnfitz -- set up transform part of lerpdata
=================
*/
void R_SetupEntityTransform (entity_t *e, lerpdata_t *lerpdata)
{
float blend;
vec3_t d;
int i;
// if LERP_RESETMOVE, kill any lerps in progress
if (e->lerpflags & LERP_RESETMOVE)
{
e->movelerpstart = 0;
VectorCopy (e->origin, e->previousorigin);
VectorCopy (e->origin, e->currentorigin);
VectorCopy (e->angles, e->previousangles);
VectorCopy (e->angles, e->currentangles);
e->lerpflags -= LERP_RESETMOVE;
}
else if (!VectorCompare (e->origin, e->currentorigin) || !VectorCompare (e->angles, e->currentangles)) // origin/angles changed, start new lerp
{
e->movelerpstart = cl.time;
VectorCopy (e->currentorigin, e->previousorigin);
VectorCopy (e->origin, e->currentorigin);
VectorCopy (e->currentangles, e->previousangles);
VectorCopy (e->angles, e->currentangles);
}
//set up values
if (r_lerpmove.value && e != &cl.viewent && e->lerpflags & LERP_MOVESTEP)
{
if (e->lerpflags & LERP_FINISH)
blend = CLAMP (0.0f, (float)(cl.time - e->movelerpstart) / (e->lerpfinish - e->movelerpstart), 1.0f);
else
blend = CLAMP (0.0f, (float)(cl.time - e->movelerpstart) / 0.1f, 1.0f);
//translation
VectorSubtract (e->currentorigin, e->previousorigin, d);
lerpdata->origin[0] = e->previousorigin[0] + d[0] * blend;
lerpdata->origin[1] = e->previousorigin[1] + d[1] * blend;
lerpdata->origin[2] = e->previousorigin[2] + d[2] * blend;
//rotation
VectorSubtract (e->currentangles, e->previousangles, d);
for (i = 0; i < 3; i++)
{
if (d[i] > 180) d[i] -= 360;
if (d[i] < -180) d[i] += 360;
}
lerpdata->angles[0] = e->previousangles[0] + d[0] * blend;
lerpdata->angles[1] = e->previousangles[1] + d[1] * blend;
lerpdata->angles[2] = e->previousangles[2] + d[2] * blend;
}
else //don't lerp
{
VectorCopy (e->origin, lerpdata->origin);
VectorCopy (e->angles, lerpdata->angles);
}
}
/*
=================
R_SetupAliasLighting -- johnfitz -- broken out from R_DrawAliasModel and rewritten
=================
*/
void R_SetupAliasLighting (entity_t *e)
{
vec3_t dist;
float add;
int i;
int quantizedangle;
float radiansangle;
float *origin;
if (!r_refdef.drawworld)
lightcolor[0] = lightcolor[1] = lightcolor[2] = 255;
else
{
if (e->eflags & EFLAGS_VIEWMODEL)
{
origin = r_refdef.vieworg;
R_LightPoint (origin);
}
else
{
origin = e->origin;
// if the initial trace is completely black, try again from above
// this helps with models whose origin is slightly below ground level
// (e.g. some of the candles in the DOTM start map)
if (!R_LightPoint (origin))
{
vec3_t lpos;
VectorCopy (origin, lpos);
lpos[2] += e->model->maxs[2] * 0.5f;
R_LightPoint (lpos);
}
}
//add dlights
for (i=0 ; i<MAX_DLIGHTS ; i++)
{
if (cl_dlights[i].die >= cl.time)
{
VectorSubtract (origin, cl_dlights[i].origin, dist);
add = cl_dlights[i].radius - VectorLength(dist);
if (add > 0)
VectorMA (lightcolor, add, cl_dlights[i].color, lightcolor);
}
}
// minimum light value on gun (24)
if (e->eflags & EFLAGS_VIEWMODEL)
{
add = 72.0f - (lightcolor[0] + lightcolor[1] + lightcolor[2]);
if (add > 0.0f)
{
lightcolor[0] += add / 3.0f;
lightcolor[1] += add / 3.0f;
lightcolor[2] += add / 3.0f;
}
}
// minimum light value on players (8)
if (e > cl.entities && e <= cl.entities + cl.maxclients)
{
add = 24.0f - (lightcolor[0] + lightcolor[1] + lightcolor[2]);
if (add > 0.0f)
{
lightcolor[0] += add / 3.0f;
lightcolor[1] += add / 3.0f;
lightcolor[2] += add / 3.0f;
}
}
}
// clamp lighting so it doesn't overbright as much (96)
if (overbright)
{
add = 288.0f / (lightcolor[0] + lightcolor[1] + lightcolor[2]);
if (add < 1.0f)
VectorScale(lightcolor, add, lightcolor);
}
//hack up the brightness when fullbrights but no overbrights (256)
if (gl_fullbrights.value && !gl_overbright_models.value)
if (e->model->flags & MOD_FBRIGHTHACK)
{
lightcolor[0] = 256.0f;
lightcolor[1] = 256.0f;
lightcolor[2] = 256.0f;
}
quantizedangle = ((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1);
//ericw -- shadevector is passed to the shader to compute shadedots inside the
//shader, see GLAlias_CreateShaders()
radiansangle = (quantizedangle / 16.0) * 2.0 * 3.14159;
shadevector[0] = cos(-radiansangle);
shadevector[1] = sin(-radiansangle);
shadevector[2] = 1;
VectorNormalize(shadevector);
//ericw --
shadedots = r_avertexnormal_dots[quantizedangle];
VectorScale (lightcolor, 1.0f / 200.0f, lightcolor);
lightcolor[0] *= e->netstate.colormod[0] / 32.0;
lightcolor[1] *= e->netstate.colormod[1] / 32.0;
lightcolor[2] *= e->netstate.colormod[2] / 32.0;
}
/*
=================
R_DrawAliasModel -- johnfitz -- almost completely rewritten
=================
*/
void R_DrawAliasModel (entity_t *e)
{
aliasglsl_t *glsl;
aliashdr_t *paliashdr;
int anim, skinnum;
struct skintextures_s tex;
lerpdata_t lerpdata;
qboolean alphatest = !!(e->model->flags & MF_HOLEY);
int surf;
float fovscale = 1.0f;
//
// setup pose/lerp data -- do it first so we don't miss updates due to culling
//
paliashdr = (aliashdr_t *)Mod_Extradata (e->model);
R_SetupAliasFrame (paliashdr, e, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
glsl = &r_alias_glsl[(paliashdr->poseverttype==PV_IQM&&lerpdata.bonestate)?ALIAS_GLSL_SKELETAL:ALIAS_GLSL_BASIC];
if (e->eflags & EFLAGS_VIEWMODEL)
{
if (skyroom_drawing)
return; //no viewmodels inside skyrooms!
//transform it relative to the view, by rebuilding the modelview matrix without the view position.
glPushMatrix ();
glLoadIdentity();
glRotatef (-90, 1, 0, 0); // put Z going up
glRotatef (90, 0, 0, 1); // put Z going up
glDepthRange (0, 0.3);
//FIXME: this needs to go. combine with depthrange and explicit viewmodel-only fov into a different projection matrix..
if (scr_fov.value > 90.f && cl_gun_fovscale.value)
fovscale = tan(scr_fov.value * (0.5f * M_PI / 180.f));
}
else
{
//
// cull it
//
if (R_CullModelForEntity(e))
return;
//
// transform it
//
glPushMatrix ();
}
R_RotateForEntity (lerpdata.origin, lerpdata.angles, e->netstate.scale);
glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1] * fovscale, paliashdr->scale_origin[2] * fovscale);
glScalef (paliashdr->scale[0], paliashdr->scale[1] * fovscale, paliashdr->scale[2] * fovscale);
//
// random stuff
//
if (gl_smoothmodels.value && !r_drawflat_cheatsafe)
glShadeModel (GL_SMOOTH);
if (gl_affinemodels.value)
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
overbright = gl_overbright_models.value;
shading = true;
//
// set up for alpha blending
//
if (r_drawflat_cheatsafe || r_lightmap_cheatsafe) //no alpha in drawflat or lightmap mode
entalpha = 1;
else
entalpha = ENTALPHA_DECODE(e->alpha);
if (entalpha == 0)
goto cleanup;
if (entalpha < 1)
{
if (!gl_texture_env_combine) overbright = false; //overbright can't be done in a single pass without combiners
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
}
else if (alphatest)
glEnable (GL_ALPHA_TEST);
if (e->effects & EF_ADDITIVE)
{
glBlendFunc (GL_SRC_ALPHA, GL_ONE);
glEnable(GL_BLEND);
}
//
// set up lighting
//
R_SetupAliasLighting (e);
for(surf=0;;surf++)
{
rs_aliaspolys += paliashdr->numtris;
//
// set up textures
//
GL_DisableMultitexture();
anim = (int)(cl.time*10) & 3;
skinnum = e->skinnum;
if ((skinnum >= paliashdr->numskins) || (skinnum < 0))
{
Con_DPrintf ("R_DrawAliasModel: no such skin # %d for '%s'\n", skinnum, e->model->name);
// ericw -- display skin 0 for winquake compatibility
skinnum = 0;
}
if (paliashdr->numskins <= 0)
{
tex.base = tex.luma = tex.lower = tex.upper = NULL; // NULL will give the checkerboard texture
}
else
tex = paliashdr->textures[skinnum][anim];
if (!gl_nocolors.value)
{
if (e->eflags & EFLAGS_COLOURMAPPED)
{ //support for dp's self.colormap = 4096 | top*16 | bottom; thing (solves corpses changing colours, can't handle rich colours though)
if (tex.base && tex.base->source_format == SRC_INDEXED && !tex.upper && !tex.lower)
{
struct gltexture_s *t = TexMgr_ColormapTexture(tex.base, CL_PLColours_FromLegacy(e->netstate.colormap&15), CL_PLColours_FromLegacy(e->netstate.colormap>>4));
if (t)
tex.base = t;
}
}
else if (e->netstate.colormap>=1&&e->netstate.colormap<=cl.maxclients)
{ //despite being able to handle _shirt+_pants textures in our glsl, we still prefer to generate per-player textures.
//1) works with non-glsl.
//2) preserves the weird non-linear ranges.
//3) ... and without breaking those ranges on models that are NOT colourmapped (the lower's remapped range is the worst of the non-linear ranges, so would make too many models ugly).
//so we only use the shirt+pans stuff when using external textures
//on the plus side, we do use a lookup so we don't break quakerally. csqc also benefits from not needing to worry about edict numbers.
if (tex.base && tex.base->source_format == SRC_INDEXED && !tex.upper && !tex.lower)
{
scoreboard_t *sb = &cl.scores[e->netstate.colormap-1];
struct gltexture_s *t = TexMgr_ColormapTexture(tex.base, sb->pants, sb->shirt);
if (t)
tex.base = t;
}
}
}
if (!gl_fullbrights.value)
tex.luma = NULL;
//
// draw it
//
if (r_drawflat_cheatsafe)
{
glDisable (GL_TEXTURE_2D);
GL_DrawAliasFrame (paliashdr, lerpdata);
glEnable (GL_TEXTURE_2D);
srand((int) (cl.time * 1000)); //restore randomness
}
else if (r_fullbright_cheatsafe)
{
GL_Bind (tex.base);
shading = false;
glColor4f(1,1,1,entalpha);
GL_DrawAliasFrame (paliashdr, lerpdata);
if (tex.luma)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_Bind(tex.luma);
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
glColor3f(entalpha,entalpha,entalpha);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
}
}
else if (r_lightmap_cheatsafe)
{
glDisable (GL_TEXTURE_2D);
shading = false;
glColor3f(1,1,1);
GL_DrawAliasFrame (paliashdr, lerpdata);
glEnable (GL_TEXTURE_2D);
}
// call fast path if possible. if the shader compliation failed for some reason,
// r_alias_program will be 0.
else if (glsl->program != 0 && (paliashdr->numbones <= glsl->maxbones||!lerpdata.bonestate))
{
GL_DrawAliasFrame_GLSL (glsl, paliashdr, lerpdata, tex, e);
}
else if (overbright)
{
if (gl_texture_env_combine && gl_mtexable && gl_texture_env_add && tex.luma) //case 1: everything in one pass
{
GL_Bind (tex.base);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f);
GL_EnableMultitexture(); // selects TEXTURE1
GL_Bind (tex.luma);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
// glEnable(GL_BLEND);
GL_DrawAliasFrame (paliashdr, lerpdata);
// glDisable(GL_BLEND);
GL_DisableMultitexture();
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
else if (gl_texture_env_combine) //case 2: overbright in one pass, then fullbright pass
{
// first pass
GL_Bind(tex.base);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f);
GL_DrawAliasFrame (paliashdr, lerpdata);
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// second pass
if (tex.luma)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_Bind(tex.luma);
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
shading = false;
glColor3f(entalpha,entalpha,entalpha);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
}
else //case 3: overbright in two passes, then fullbright pass
{
// first pass
GL_Bind(tex.base);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_DrawAliasFrame (paliashdr, lerpdata);
// second pass -- additive with black fog, to double the object colors but not the fog color
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
// third pass
if (tex.luma)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_Bind(tex.luma);
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
shading = false;
glColor3f(entalpha,entalpha,entalpha);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
}
}
else
{
if (gl_mtexable && gl_texture_env_add && tex.luma) //case 4: fullbright mask using multitexture
{
GL_DisableMultitexture(); // selects TEXTURE0
GL_Bind (tex.base);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_EnableMultitexture(); // selects TEXTURE1
GL_Bind (tex.luma);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
glEnable(GL_BLEND);
GL_DrawAliasFrame (paliashdr, lerpdata);
glDisable(GL_BLEND);
GL_DisableMultitexture();
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
else //case 5: fullbright mask without multitexture
{
// first pass
GL_Bind(tex.base);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
GL_DrawAliasFrame (paliashdr, lerpdata);
// second pass
if (tex.luma)
{
GL_Bind(tex.luma);
glEnable(GL_BLEND);
glBlendFunc (GL_ONE, GL_ONE);
glDepthMask(GL_FALSE);
shading = false;
glColor3f(entalpha,entalpha,entalpha);
Fog_StartAdditive ();
GL_DrawAliasFrame (paliashdr, lerpdata);
Fog_StopAdditive ();
glDepthMask(GL_TRUE);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
}
}
}
if (!paliashdr->nextsurface)
break;
paliashdr = (aliashdr_t*)((byte*)paliashdr + paliashdr->nextsurface);
}
cleanup:
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glShadeModel (GL_FLAT);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
if (alphatest)
glDisable (GL_ALPHA_TEST);
glColor3f(1,1,1);
if (e->eflags & EFLAGS_VIEWMODEL)
glDepthRange (0, 1);
if (e->effects & EF_ADDITIVE)
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPopMatrix ();
}
//johnfitz -- values for shadow matrix
#define SHADOW_SKEW_X -0.7 //skew along x axis. -0.7 to mimic glquake shadows
#define SHADOW_SKEW_Y 0 //skew along y axis. 0 to mimic glquake shadows
#define SHADOW_VSCALE 0 //0=completely flat
#define SHADOW_HEIGHT 0.1 //how far above the floor to render the shadow
//johnfitz
/*
=============
GL_DrawAliasShadow -- johnfitz -- rewritten
TODO: orient shadow onto "lightplane" (a global mplane_t*)
=============
*/
void GL_DrawAliasShadow (entity_t *e)
{
float shadowmatrix[16] = {1, 0, 0, 0,
0, 1, 0, 0,
SHADOW_SKEW_X, SHADOW_SKEW_Y, SHADOW_VSCALE, 0,
0, 0, SHADOW_HEIGHT, 1};
float lheight;
aliashdr_t *paliashdr;
lerpdata_t lerpdata;
if (R_CullModelForEntity(e))
return;
if (e == &cl.viewent || e->effects & EF_NOSHADOW || e->model->flags & MOD_NOSHADOW)
return;
entalpha = ENTALPHA_DECODE(e->alpha);
if (entalpha == 0) return;
paliashdr = (aliashdr_t *)Mod_Extradata (e->model);
R_SetupAliasFrame (paliashdr, e, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
R_LightPoint (e->origin);
lheight = currententity->origin[2] - lightspot[2];
// set up matrix
glPushMatrix ();
glTranslatef (lerpdata.origin[0], lerpdata.origin[1], lerpdata.origin[2]);
glTranslatef (0,0,-lheight);
glMultMatrixf (shadowmatrix);
glTranslatef (0,0,lheight);
glRotatef (lerpdata.angles[1], 0, 0, 1);
glRotatef (-lerpdata.angles[0], 0, 1, 0);
glRotatef (lerpdata.angles[2], 1, 0, 0);
glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);
// draw it
glDepthMask(GL_FALSE);
glEnable (GL_BLEND);
GL_DisableMultitexture ();
glDisable (GL_TEXTURE_2D);
shading = false;
glColor4f(0,0,0,entalpha * 0.5);
GL_DrawAliasFrame (paliashdr, lerpdata);
glEnable (GL_TEXTURE_2D);
glDisable (GL_BLEND);
glDepthMask(GL_TRUE);
//clean up
glPopMatrix ();
}
/*
=================
R_DrawAliasModel_ShowTris -- johnfitz
=================
*/
void R_DrawAliasModel_ShowTris (entity_t *e)
{
aliashdr_t *paliashdr;
lerpdata_t lerpdata;
if (R_CullModelForEntity(e))
return;
paliashdr = (aliashdr_t *)Mod_Extradata (e->model);
R_SetupAliasFrame (paliashdr, e, &lerpdata);
R_SetupEntityTransform (e, &lerpdata);
glPushMatrix ();
R_RotateForEntity (lerpdata.origin,lerpdata.angles, e->netstate.scale);
glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]);
glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]);
shading = false;
glColor3f(1,1,1);
GL_DrawAliasFrame (paliashdr, lerpdata);
glPopMatrix ();
}