/* Copyright (C) 1996-1997 Id Software, Inc. 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_main.c #include "quakedef.h" entity_t r_worldentity; qboolean r_cache_thrash; // compatability vec3_t modelorg, r_entorigin; entity_t *currententity; int r_visframecount; // bumped when going to a new PVS int r_framecount; // used for dlight push checking mplane_t frustum[4]; int c_brush_polys, c_alias_polys; qboolean envmap; // true during envmap command capture int currenttexture = -1; // to avoid unnecessary texture sets int cnttextures[2] = {-1, -1}; // cached int particletexture; // little dot for particles int playertextures; // up to 16 color translated skins int mirrortexturenum; // quake texturenum, not gltexturenum qboolean mirror; mplane_t *mirror_plane; // // view origin // vec3_t vup; vec3_t vpn; vec3_t vright; vec3_t r_origin; float r_world_matrix[16]; float r_base_world_matrix[16]; // // screen size info // refdef_t r_refdef; mleaf_t *r_viewleaf, *r_oldviewleaf; texture_t *r_notexture_mip; int d_lightstylevalue[256]; // 8.8 fraction of base light value void R_MarkLeaves (void); cvar_t r_norefresh = CVAR2("r_norefresh","0"); cvar_t r_drawentities = CVAR2("r_drawentities","1"); cvar_t r_drawviewmodel = CVAR2("r_drawviewmodel","1"); cvar_t r_speeds = CVAR2("r_speeds","0"); cvar_t r_fullbright = CVAR2("r_fullbright","0"); cvar_t r_lightmap = CVAR2("r_lightmap","0"); cvar_t r_shadows = CVAR2("r_shadows","0"); cvar_t r_mirroralpha = CVAR2("r_mirroralpha","1"); cvar_t r_wateralpha = CVAR2("r_wateralpha","1"); cvar_t r_dynamic = CVAR2("r_dynamic","1"); cvar_t r_novis = CVAR2("r_novis","0"); cvar_t gl_finish = CVAR2("gl_finish","0"); cvar_t gl_clear = CVAR2("gl_clear","0"); cvar_t gl_cull = CVAR2("gl_cull","1"); cvar_t gl_texsort = CVAR2("gl_texsort","1"); cvar_t gl_smoothmodels = CVAR2("gl_smoothmodels","1"); cvar_t gl_affinemodels = CVAR2("gl_affinemodels","1"); cvar_t gl_polyblend = CVAR2("gl_polyblend","1"); cvar_t gl_flashblend = CVAR2("gl_flashblend","1"); cvar_t gl_playermip = CVAR2("gl_playermip","0"); cvar_t gl_nocolors = CVAR2("gl_nocolors","0"); cvar_t gl_keeptjunctions = CVAR2("gl_keeptjunctions","1"); cvar_t gl_reporttjunctions = CVAR2("gl_reporttjunctions","0"); cvar_t gl_doubleeyes = CVAR2("gl_doubleeys", "1"); extern cvar_t gl_ztrick; /* ================= R_CullBox Returns true if the box is completely outside the frustom ================= */ qboolean R_CullBox (vec3_t mins, vec3_t maxs) { int i; for (i=0 ; i<4 ; i++) if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) return true; return false; } void R_RotateForEntity (entity_t *e) { glTranslatef (e->origin[0], e->origin[1], e->origin[2]); glRotatef (e->angles[1], 0, 0, 1); glRotatef (-e->angles[0], 0, 1, 0); glRotatef (e->angles[2], 1, 0, 0); } /* ============================================================= SPRITE MODELS ============================================================= */ /* ================ R_GetSpriteFrame ================ */ mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) { msprite_t *psprite; mspritegroup_t *pspritegroup; mspriteframe_t *pspriteframe; int i, numframes, frame; float *pintervals, fullinterval, targettime, time; psprite = (msprite_t*) currententity->model->cache.data; frame = currententity->frame; if ((frame >= psprite->numframes) || (frame < 0)) { Con_Printf ("R_DrawSprite: no such frame %d\n", frame); frame = 0; } if (psprite->frames[frame].type == SPR_SINGLE) { pspriteframe = psprite->frames[frame].frameptr; } else { pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; pintervals = pspritegroup->intervals; numframes = pspritegroup->numframes; fullinterval = pintervals[numframes-1]; time = cl.time + currententity->syncbase; // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values // are positive, so we don't have to worry about division by 0 targettime = time - ((int)(time / fullinterval)) * fullinterval; for (i=0 ; i<(numframes-1) ; i++) { if (pintervals[i] > targettime) break; } pspriteframe = pspritegroup->frames[i]; } return pspriteframe; } /* ================= R_DrawSpriteModel ================= */ void R_DrawSpriteModel (entity_t *e) { vec3_t point; mspriteframe_t *frame; float *up, *right; vec3_t v_forward, v_right, v_up; msprite_t *psprite; // don't even bother culling, because it's just a single // polygon without a surface cache frame = R_GetSpriteFrame (e); psprite = (msprite_t*) currententity->model->cache.data; if (psprite->type == SPR_ORIENTED) { // bullet marks on walls AngleVectors (currententity->angles, v_forward, v_right, v_up); up = v_up; right = v_right; } else { // normal sprite up = vup; right = vright; } glColor3f (1,1,1); GL_DisableMultitexture(); GL_Bind(frame->gl_texturenum); glEnable (GL_ALPHA_TEST); #ifdef USE_OPENGLES { float* pPoint = gVertexBuffer; float texCoords[] = { 0, 1, 0, 0, 1, 0, 1, 1 }; VectorMA (e->origin, frame->down, up, point); VectorMA (point, frame->left, right, pPoint); pPoint += 3; VectorMA (e->origin, frame->up, up, point); VectorMA (point, frame->left, right, pPoint); pPoint += 3; VectorMA (e->origin, frame->up, up, point); VectorMA (point, frame->right, right, pPoint); pPoint += 3; VectorMA (e->origin, frame->down, up, point); VectorMA (point, frame->right, right, pPoint); glVertexPointer(3, GL_FLOAT, 0, gVertexBuffer); glTexCoordPointer(2, GL_FLOAT, 0, texCoords); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } #else glBegin (GL_QUADS); glTexCoord2f (0, 1); VectorMA (e->origin, frame->down, up, point); VectorMA (point, frame->left, right, point); glVertex3fv (point); glTexCoord2f (0, 0); VectorMA (e->origin, frame->up, up, point); VectorMA (point, frame->left, right, point); glVertex3fv (point); glTexCoord2f (1, 0); VectorMA (e->origin, frame->up, up, point); VectorMA (point, frame->right, right, point); glVertex3fv (point); glTexCoord2f (1, 1); VectorMA (e->origin, frame->down, up, point); VectorMA (point, frame->right, right, point); glVertex3fv (point); glEnd (); #endif glDisable (GL_ALPHA_TEST); } /* ============================================================= ALIAS MODELS ============================================================= */ #define NUMVERTEXNORMALS 162 float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; vec3_t shadevector; float shadelight, ambientlight; // precalculated dot products for quantized angles #define SHADEDOT_QUANT 16 float r_avertexnormal_dots[SHADEDOT_QUANT][256] = #include "anorm_dots.h" ; float *shadedots = r_avertexnormal_dots[0]; int lastposenum; /* ============= GL_DrawAliasFrame ============= */ void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum) { float s, t; float l; int i, j; int index; trivertx_t *v, *verts; int list; int *order; vec3_t point; float *normal; int count; #ifdef USE_OPENGLES glEnableClientState(GL_COLOR_ARRAY); #endif lastposenum = posenum; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done #ifdef USE_OPENGLES { int primType; int c; float* pColor; float* pTexCoord; float* pPos; if (count < 0) { count = -count; primType = GL_TRIANGLE_FAN; } else primType = GL_TRIANGLE_STRIP; // texture coordinates come from the draw list glTexCoordPointer(2, GL_FLOAT, 0, gTexCoordBuffer); glVertexPointer(3, GL_FLOAT, 0, gVertexBuffer); glColorPointer(4, GL_FLOAT, 0, gColorBuffer); pColor = gColorBuffer; pPos = gVertexBuffer; pTexCoord = gTexCoordBuffer; c = count; do { // texture coordinates come from the draw list *pTexCoord++ = ((float *)order)[0]; *pTexCoord++ = ((float *)order)[1]; order += 2; // normals and vertexes come from the frame list l = shadedots[verts->lightnormalindex] * shadelight; *pColor++ = l; *pColor++ = l; *pColor++ = l; *pColor++ = 1.0f; *pPos++ = verts->v[0]; *pPos++ = verts->v[1]; *pPos++ = verts->v[2]; verts++; } while (--c); glDrawArrays(primType, 0, count); } #else if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else glBegin (GL_TRIANGLE_STRIP); do { // texture coordinates come from the draw list glTexCoord2f (((float *)order)[0], ((float *)order)[1]); order += 2; // normals and vertexes come from the frame list l = shadedots[verts->lightnormalindex] * shadelight; glColor3f (l, l, l); glVertex3f (verts->v[0], verts->v[1], verts->v[2]); verts++; } while (--count); glEnd (); #endif } #ifdef USE_OPENGLES glDisableClientState(GL_COLOR_ARRAY); #endif } /* ============= GL_DrawAliasShadow ============= */ extern vec3_t lightspot; void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) { float s, t, l; int i, j; int index; trivertx_t *v, *verts; int list; int *order; vec3_t point; float *normal; float height, lheight; int count; lheight = currententity->origin[2] - lightspot[2]; height = 0; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); height = -lheight + 1.0; while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done #ifdef USE_OPENGLES { int primType; int c; float* pVertex; if (count < 0) { count = -count; primType = GL_TRIANGLE_FAN; } else primType = GL_TRIANGLE_STRIP; pVertex = gVertexBuffer; for(c = 0; c < count; c++) { // texture coordinates come from the draw list // (skipped for shadows) glTexCoord2fv ((float *)order); order += 2; // normals and vertexes come from the frame list pVertex[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; pVertex[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; pVertex[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; pVertex[0] -= shadevector[0]*(pVertex[2]+lheight); pVertex[1] -= shadevector[1]*(pVertex[2]+lheight); pVertex[2] = height; // height -= 0.001; pVertex += 3; verts++; } glVertexPointer(3, GL_FLOAT, 0, gVertexBuffer); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDrawArrays(primType, 0, count); glEnableClientState(GL_TEXTURE_COORD_ARRAY); } #else if (count < 0) { count = -count; glBegin (GL_TRIANGLE_FAN); } else glBegin (GL_TRIANGLE_STRIP); do { // texture coordinates come from the draw list // (skipped for shadows) glTexCoord2fv ((float *)order); order += 2; // normals and vertexes come from the frame list point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; point[0] -= shadevector[0]*(point[2]+lheight); point[1] -= shadevector[1]*(point[2]+lheight); point[2] = height; // height -= 0.001; glVertex3fv (point); verts++; } while (--count); glEnd (); #endif } } /* ================= R_SetupAliasFrame ================= */ void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr) { int pose, numposes; float interval; if ((frame >= paliashdr->numframes) || (frame < 0)) { Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); frame = 0; } pose = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { interval = paliashdr->frames[frame].interval; pose += (int)(cl.time / interval) % numposes; } GL_DrawAliasFrame (paliashdr, pose); } /* ================= R_DrawAliasModel ================= */ void R_DrawAliasModel (entity_t *e) { int i, j; int lnum; vec3_t dist; float add; model_t *clmodel; vec3_t mins, maxs; aliashdr_t *paliashdr; trivertx_t *verts, *v; int index; float s, t, an; int anim; clmodel = currententity->model; VectorAdd (currententity->origin, clmodel->mins, mins); VectorAdd (currententity->origin, clmodel->maxs, maxs); if (R_CullBox (mins, maxs)) return; VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); // // get lighting information // ambientlight = shadelight = R_LightPoint (currententity->origin); // allways give the gun some light if (e == &cl.viewent && ambientlight < 24) ambientlight = shadelight = 24; for (lnum=0 ; lnum= cl.time) { VectorSubtract (currententity->origin, cl_dlights[lnum].origin, dist); add = cl_dlights[lnum].radius - Length(dist); if (add > 0) { ambientlight += add; //ZOID models should be affected by dlights as well shadelight += add; } } } // clamp lighting so it doesn't overbright as much if (ambientlight > 128) ambientlight = 128; if (ambientlight + shadelight > 192) shadelight = 192 - ambientlight; // ZOID: never allow players to go totally black i = currententity - cl_entities; if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */) if (ambientlight < 8) ambientlight = shadelight = 8; // HACK HACK HACK -- no fullbright colors, so make torches full light if (!strcmp (clmodel->name, "progs/flame2.mdl") || !strcmp (clmodel->name, "progs/flame.mdl") ) ambientlight = shadelight = 256; shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; shadelight = shadelight / 200.0; an = e->angles[1]/180*M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize (shadevector); // // locate the proper data // paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); c_alias_polys += paliashdr->numtris; // // draw all the triangles // GL_DisableMultitexture(); glPushMatrix (); R_RotateForEntity (e); if (!strcmp (clmodel->name, "progs/eyes.mdl") && gl_doubleeyes.value) { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8)); // double size of eyes, since they are really hard to see in gl glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2); } else { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); } anim = (int)(cl.time*10) & 3; GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]); // we can't dynamically colormap textures, so they are cached // seperately for the players. Heads are just uncolored. if (currententity->colormap != vid.colormap && !gl_nocolors.value) { i = currententity - cl_entities; if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */) GL_Bind(playertextures - 1 + i); } if (gl_smoothmodels.value) glShadeModel (GL_SMOOTH); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); R_SetupAliasFrame (currententity->frame, paliashdr); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glShadeModel (GL_FLAT); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glPopMatrix (); if (r_shadows.value) { glPushMatrix (); R_RotateForEntity (e); glDisable (GL_TEXTURE_2D); glEnable (GL_BLEND); glColor4f (0,0,0,0.5); GL_DrawAliasShadow (paliashdr, lastposenum); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); glColor4f (1,1,1,1); glPopMatrix (); } } //================================================================================== /* ============= R_DrawEntitiesOnList ============= */ void R_DrawEntitiesOnList (void) { int i; if (!r_drawentities.value) return; // draw sprites seperately, because of alpha blending for (i=0 ; imodel->type) { case mod_alias: R_DrawAliasModel (currententity); break; case mod_brush: R_DrawBrushModel (currententity); break; default: break; } } for (i=0 ; imodel->type) { case mod_sprite: R_DrawSpriteModel (currententity); break; default : break; } } } /* ============= R_DrawViewModel ============= */ void R_DrawViewModel (void) { float ambient[4], diffuse[4]; int j; int lnum; vec3_t dist; float add; dlight_t *dl; int ambientlight, shadelight; if (!r_drawviewmodel.value) return; if (chase_active.value) return; if (envmap) return; if (!r_drawentities.value) return; if (cl.items & IT_INVISIBILITY) return; if (cl.stats[STAT_HEALTH] <= 0) return; currententity = &cl.viewent; if (!currententity->model) return; j = R_LightPoint (currententity->origin); if (j < 24) j = 24; // allways give some light on gun ambientlight = j; shadelight = j; // add dynamic lights for (lnum=0 ; lnumradius) continue; if (!dl->radius) continue; if (dl->die < cl.time) continue; VectorSubtract (currententity->origin, dl->origin, dist); add = dl->radius - Length(dist); if (add > 0) ambientlight += (int) add; } ambient[0] = ambient[1] = ambient[2] = ambient[3] = (float)ambientlight / 128; diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = (float)shadelight / 128; // hack the depth range to prevent view model from poking into walls #ifdef USE_OPENGLES glDepthRangef(gldepthmin, gldepthmin + 0.3f*(gldepthmax-gldepthmin)); R_DrawAliasModel (currententity); glDepthRangef(gldepthmin, gldepthmax); #else glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin)); R_DrawAliasModel (currententity); glDepthRange (gldepthmin, gldepthmax); #endif } /* ============ R_PolyBlend ============ */ void R_PolyBlend (void) { if (!gl_polyblend.value) return; if (!v_blend[3]) return; GL_DisableMultitexture(); glDisable (GL_ALPHA_TEST); glEnable (GL_BLEND); glDisable (GL_DEPTH_TEST); glDisable (GL_TEXTURE_2D); glLoadIdentity (); glRotatef (-90, 1, 0, 0); // put Z going up glRotatef (90, 0, 0, 1); // put Z going up glColor4fv (v_blend); #ifdef USE_OPENGLES float vertex[3*4] = { 10, 100, 100, 10, -100, 100, 10, -100, -100, 10, 100, -100 }; glDisableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer( 3, GL_FLOAT, 0, vertex); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glEnableClientState(GL_TEXTURE_COORD_ARRAY); #else glBegin (GL_QUADS); glVertex3f (10, 100, 100); glVertex3f (10, -100, 100); glVertex3f (10, -100, -100); glVertex3f (10, 100, -100); glEnd (); #endif glDisable (GL_BLEND); glEnable (GL_TEXTURE_2D); glEnable (GL_ALPHA_TEST); } int SignbitsForPlane (mplane_t *out) { int bits, j; // for fast box on planeside test bits = 0; for (j=0 ; j<3 ; j++) { if (out->normal[j] < 0) bits |= 1< 1) Cvar_Set ("r_fullbright", "0"); R_AnimateLight (); r_framecount++; // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); V_SetContentsColor (r_viewleaf->contents); V_CalcBlend (); r_cache_thrash = false; c_brush_polys = 0; c_alias_polys = 0; } #ifdef USE_OPENGLES void MYgluPerspective( float fovy, float aspect, float zNear, float zFar ) { float xmin, xmax, ymin, ymax; ymax = zNear * tan( fovy * M_PI / 360.0f ); ymin = -ymax; xmin = ymin * aspect; xmax = ymax * aspect; glFrustumf( xmin, xmax, ymin, ymax, zNear, zFar ); } #else void MYgluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar ) { GLdouble xmin, xmax, ymin, ymax; ymax = zNear * tan( fovy * M_PI / 360.0 ); ymin = -ymax; xmin = ymin * aspect; xmax = ymax * aspect; glFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); } #endif #define DO_OWN_MATRIX_MATH #ifdef DO_OWN_MATRIX_MATH // We can't count on being able to read back the model view matrix, so calculate it ourselves. #define I(_i, _j) ((_j)+ 4*(_i)) void mulMM(float* r, const float* lhs, const float* rhs) { float const* const m = lhs; for (int i=0 ; i<4 ; i++) { register const float rhs_i0 = rhs[ I(i,0) ]; register float ri0 = m[ I(0,0) ] * rhs_i0; register float ri1 = m[ I(0,1) ] * rhs_i0; register float ri2 = m[ I(0,2) ] * rhs_i0; register float ri3 = m[ I(0,3) ] * rhs_i0; for (int j=1 ; j<4 ; j++) { register const float rhs_ij = rhs[ I(i,j) ]; ri0 += m[ I(j,0) ] * rhs_ij; ri1 += m[ I(j,1) ] * rhs_ij; ri2 += m[ I(j,2) ] * rhs_ij; ri3 += m[ I(j,3) ] * rhs_ij; } r[ I(i,0) ] = ri0; r[ I(i,1) ] = ri1; r[ I(i,2) ] = ri2; r[ I(i,3) ] = ri3; } } static void setIdentityM(float* sm, int smOffset) { for (int i=0 ; i<16 ; i++) { sm[smOffset + i] = 0; } for(int i = 0; i < 16; i += 5) { sm[smOffset + i] = 1.0f; } } static void translateM(float* m, int mOffset, float x, float y, float z) { for (int i=0 ; i<4 ; i++) { int mi = mOffset + i; m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z; } } static float length(float x, float y, float z) { return (float) sqrtf(x * x + y * y + z * z); } static void setRotateM(float* rm, int rmOffset, float a, float x, float y, float z) { rm[rmOffset + 3] = 0; rm[rmOffset + 7] = 0; rm[rmOffset + 11]= 0; rm[rmOffset + 12]= 0; rm[rmOffset + 13]= 0; rm[rmOffset + 14]= 0; rm[rmOffset + 15]= 1; a *= (float) (M_PI / 180.0f); float s = (float) sinf(a); float c = (float) cosf(a); if (1.0f == x && 0.0f == y && 0.0f == z) { rm[rmOffset + 5] = c; rm[rmOffset + 10]= c; rm[rmOffset + 6] = s; rm[rmOffset + 9] = -s; rm[rmOffset + 1] = 0; rm[rmOffset + 2] = 0; rm[rmOffset + 4] = 0; rm[rmOffset + 8] = 0; rm[rmOffset + 0] = 1; } else if (0.0f == x && 1.0f == y && 0.0f == z) { rm[rmOffset + 0] = c; rm[rmOffset + 10]= c; rm[rmOffset + 8] = s; rm[rmOffset + 2] = -s; rm[rmOffset + 1] = 0; rm[rmOffset + 4] = 0; rm[rmOffset + 6] = 0; rm[rmOffset + 9] = 0; rm[rmOffset + 5] = 1; } else if (0.0f == x && 0.0f == y && 1.0f == z) { rm[rmOffset + 0] = c; rm[rmOffset + 5] = c; rm[rmOffset + 1] = s; rm[rmOffset + 4] = -s; rm[rmOffset + 2] = 0; rm[rmOffset + 6] = 0; rm[rmOffset + 8] = 0; rm[rmOffset + 9] = 0; rm[rmOffset + 10]= 1; } else { float len = length(x, y, z); if (1.0f != len) { float recipLen = 1.0f / len; x *= recipLen; y *= recipLen; z *= recipLen; } float nc = 1.0f - c; float xy = x * y; float yz = y * z; float zx = z * x; float xs = x * s; float ys = y * s; float zs = z * s; rm[rmOffset + 0] = x*x*nc + c; rm[rmOffset + 4] = xy*nc - zs; rm[rmOffset + 8] = zx*nc + ys; rm[rmOffset + 1] = xy*nc + zs; rm[rmOffset + 5] = y*y*nc + c; rm[rmOffset + 9] = yz*nc - xs; rm[rmOffset + 2] = zx*nc - ys; rm[rmOffset + 6] = yz*nc + xs; rm[rmOffset + 10] = z*z*nc + c; } } static void rotateM(float* m, float a, float x, float y, float z) { float temp[16]; float temp2[16]; setRotateM(temp, 0, a, x, y, z); mulMM(temp2, m, temp); memcpy(m, temp2, 16 * sizeof(float)); } #undef I #endif // DO_OWN_MATRIX_MATH /* ============= R_SetupGL ============= */ void R_SetupGL (void) { float screenaspect; float yfov; int i; extern int glwidth, glheight; int x, x2, y2, y, w, h; // // set up viewpoint // glMatrixMode(GL_PROJECTION); glLoadIdentity (); x = r_refdef.vrect.x * glwidth/vid.width; x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth/vid.width; y = (vid.height-r_refdef.vrect.y) * glheight/vid.height; y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight/vid.height; // fudge around because of frac screen scale if (x > 0) x--; if (x2 < glwidth) x2++; if (y2 < 0) y2--; if (y < glheight) y++; w = x2 - x; h = y - y2; if (envmap) { x = y2 = 0; w = h = 256; } glViewport (glx + x, gly + y2, w, h); screenaspect = (float)r_refdef.vrect.width/r_refdef.vrect.height; // yfov = 2*atan((float)r_refdef.vrect.height/r_refdef.vrect.width)*180/M_PI; MYgluPerspective (r_refdef.fov_y, screenaspect, 4, 4096); if (mirror) { if (mirror_plane->normal[2]) glScalef (1, -1, 1); else glScalef (-1, 1, 1); glCullFace(GL_BACK); } else glCullFace(GL_FRONT); glMatrixMode(GL_MODELVIEW); #ifdef DO_OWN_MATRIX_MATH float mv[16]; setIdentityM(mv, 0); rotateM(mv, -90, 1, 0, 0); // put Z going up rotateM(mv, 90, 0, 0, 1); // put Z going up rotateM(mv, -r_refdef.viewangles[2], 1, 0, 0); rotateM(mv, -r_refdef.viewangles[0], 0, 1, 0); rotateM(mv, -r_refdef.viewangles[1], 0, 0, 1); translateM(mv, 0, -r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); glLoadMatrixf(mv); memcpy(r_world_matrix, mv, sizeof(r_world_matrix)); #else glLoadIdentity (); glRotatef (-90, 1, 0, 0); // put Z going up glRotatef (90, 0, 0, 1); // put Z going up glRotatef (-r_refdef.viewangles[2], 1, 0, 0); glRotatef (-r_refdef.viewangles[0], 0, 1, 0); glRotatef (-r_refdef.viewangles[1], 0, 0, 1); glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); #ifdef USE_OPENGLES static qboolean initialized; static qboolean haveGL_OES_matrix_get; static qboolean haveGL_OES_query_matrix; #if 0 if (! initialized) { const char* extensions = (const char*) glGetString(GL_EXTENSIONS); haveGL_OES_matrix_get = strstr(extensions, "GL_OES_matrix_get") != NULL; haveGL_OES_query_matrix = strstr(extensions, "GL_OES_query_matrix") != NULL; initialized = true; } if (haveGL_OES_query_matrix) { GLfixed mantissa[16]; GLint exponent[16]; glQueryMatrixxOES( mantissa, exponent ); for(int i = 0; i < 16; i++) { r_world_matrix[i] = scalbnf(mantissa[i], exponent[i]-16); } } else if (haveGL_OES_matrix_get) { glGetIntegerv (MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES, (GLint*) r_world_matrix); } else #endif { // No way to get the world matix, set to identity memset(r_world_matrix, 0, sizeof(r_world_matrix)); for(i = 0; i < 16; i += 5) { r_world_matrix[i] = 1.0f; } } #else glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix); #endif #endif // DO_OWN_MATRIX_MATH // // set drawing parms // if (gl_cull.value) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); glEnable(GL_DEPTH_TEST); } /* ================ R_RenderScene r_refdef must be set before the first call ================ */ void R_RenderScene (void) { R_SetupFrame (); R_SetFrustum (); R_SetupGL (); R_MarkLeaves (); // done here so we know if we're in water R_DrawWorld (); // adds static entities to the list S_ExtraUpdate (); // don't let sound get messed up if going slow R_DrawEntitiesOnList (); GL_DisableMultitexture(); R_RenderDlights (); R_DrawParticles (); #ifdef GLTEST Test_Draw (); #endif } /* ============= R_Clear ============= */ void R_Clear (void) { if (r_mirroralpha.value != 1.0) { if (gl_clear.value) glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); else glClear (GL_DEPTH_BUFFER_BIT); gldepthmin = 0; gldepthmax = 0.5; glDepthFunc (GL_LEQUAL); } else if (gl_ztrick.value) { static int trickframe; if (gl_clear.value) glClear (GL_COLOR_BUFFER_BIT); trickframe++; if (trickframe & 1) { gldepthmin = 0; gldepthmax = 0.49999; glDepthFunc (GL_LEQUAL); } else { gldepthmin = 1; gldepthmax = 0.5; glDepthFunc (GL_GEQUAL); } } else { if (gl_clear.value) glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); else glClear (GL_DEPTH_BUFFER_BIT); gldepthmin = 0; gldepthmax = 1; glDepthFunc (GL_LEQUAL); } #ifdef USE_OPENGLES glDepthRangef (gldepthmin, gldepthmax); #else glDepthRange (gldepthmin, gldepthmax); #endif } /* ============= R_Mirror ============= */ void R_Mirror (void) { float d; msurface_t *s; entity_t *ent; if (!mirror) return; memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); d = DotProduct (vpn, mirror_plane->normal); VectorMA (vpn, -2*d, mirror_plane->normal, vpn); r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; r_refdef.viewangles[2] = -r_refdef.viewangles[2]; ent = &cl_entities[cl.viewentity]; if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; } gldepthmin = 0.5; gldepthmax = 1; #ifdef USE_OPENGLES glDepthRangef (gldepthmin, gldepthmax); #else glDepthRange (gldepthmin, gldepthmax); #endif glDepthFunc (GL_LEQUAL); R_RenderScene (); R_DrawWaterSurfaces (); gldepthmin = 0; gldepthmax = 0.5; #ifdef USE_OPENGLES glDepthRangef (gldepthmin, gldepthmax); #else glDepthRange (gldepthmin, gldepthmax); #endif glDepthFunc (GL_LEQUAL); // blend on top glEnable (GL_BLEND); glMatrixMode(GL_PROJECTION); if (mirror_plane->normal[2]) glScalef (1,-1,1); else glScalef (-1,1,1); glCullFace(GL_FRONT); glMatrixMode(GL_MODELVIEW); glLoadMatrixf (r_base_world_matrix); glColor4f (1,1,1,r_mirroralpha.value); s = cl.worldmodel->textures[mirrortexturenum]->texturechain; for ( ; s ; s=s->texturechain) R_RenderBrushPoly (s); cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; glDisable (GL_BLEND); glColor4f (1,1,1,1); } /* ================ R_RenderView r_refdef must be set before the first call ================ */ void R_RenderView (void) { double time1 = 0.0; double time2; GLfloat colors[4] = {(GLfloat) 0.0, (GLfloat) 0.0, (GLfloat) 1, (GLfloat) 0.20}; if (r_norefresh.value) return; if (!r_worldentity.model || !cl.worldmodel) Sys_Error ("R_RenderView: NULL worldmodel"); if (r_speeds.value) { glFinish (); time1 = Sys_FloatTime (); c_brush_polys = 0; c_alias_polys = 0; } mirror = false; if (gl_finish.value) glFinish (); R_Clear (); // render normal view /***** Experimental silly looking fog ****** ****** Use r_fullbright if you enable ****** glFogi(GL_FOG_MODE, GL_LINEAR); glFogfv(GL_FOG_COLOR, colors); glFogf(GL_FOG_END, 512.0); glEnable(GL_FOG); ********************************************/ R_RenderScene (); R_DrawViewModel (); R_DrawWaterSurfaces (); // More fog right here :) // glDisable(GL_FOG); // End of all fog code... // render mirror view R_Mirror (); R_PolyBlend (); if (r_speeds.value) { // glFinish (); time2 = Sys_FloatTime (); Con_Printf ("%3i ms %4i wpoly %4i epoly\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys); } }