| /* |
| 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<MAX_DLIGHTS ; lnum++) |
| { |
| if (cl_dlights[lnum].die >= 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 ; i<cl_numvisedicts ; i++) |
| { |
| currententity = cl_visedicts[i]; |
| |
| switch (currententity->model->type) |
| { |
| case mod_alias: |
| R_DrawAliasModel (currententity); |
| break; |
| |
| case mod_brush: |
| R_DrawBrushModel (currententity); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| for (i=0 ; i<cl_numvisedicts ; i++) |
| { |
| currententity = cl_visedicts[i]; |
| |
| switch (currententity->model->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 ; lnum<MAX_DLIGHTS ; lnum++) |
| { |
| dl = &cl_dlights[lnum]; |
| if (!dl->radius) |
| 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<<j; |
| } |
| return bits; |
| } |
| |
| |
| void R_SetFrustum (void) |
| { |
| int i; |
| |
| if (r_refdef.fov_x == 90) |
| { |
| // front side is visible |
| |
| VectorAdd (vpn, vright, frustum[0].normal); |
| VectorSubtract (vpn, vright, frustum[1].normal); |
| |
| VectorAdd (vpn, vup, frustum[2].normal); |
| VectorSubtract (vpn, vup, frustum[3].normal); |
| } |
| else |
| { |
| // rotate VPN right by FOV_X/2 degrees |
| RotatePointAroundVector( frustum[0].normal, vup, vpn, -(90-r_refdef.fov_x / 2 ) ); |
| // rotate VPN left by FOV_X/2 degrees |
| RotatePointAroundVector( frustum[1].normal, vup, vpn, 90-r_refdef.fov_x / 2 ); |
| // rotate VPN up by FOV_X/2 degrees |
| RotatePointAroundVector( frustum[2].normal, vright, vpn, 90-r_refdef.fov_y / 2 ); |
| // rotate VPN down by FOV_X/2 degrees |
| RotatePointAroundVector( frustum[3].normal, vright, vpn, -( 90 - r_refdef.fov_y / 2 ) ); |
| } |
| |
| for (i=0 ; i<4 ; i++) |
| { |
| frustum[i].type = PLANE_ANYZ; |
| frustum[i].dist = DotProduct (r_origin, frustum[i].normal); |
| frustum[i].signbits = SignbitsForPlane (&frustum[i]); |
| } |
| } |
| |
| |
| |
| /* |
| =============== |
| R_SetupFrame |
| =============== |
| */ |
| void R_SetupFrame (void) |
| { |
| int edgecount; |
| vrect_t vrect; |
| float w, h; |
| |
| // don't allow cheats in multiplayer |
| if (cl.maxclients > 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); |
| } |
| } |