File: client\cl_view.c

    1 /*
    2 Copyright (C) 1997-2001 Id Software, Inc.
    3 
    4 This program is free software; you can redistribute it and/or
    5 modify it under the terms of the GNU General Public License
    6 as published by the Free Software Foundation; either version 2
    7 of the License, or (at your option) any later version.
    8 
    9 This program is distributed in the hope that it will be useful,
   10 but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   12 
   13 See the GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; if not, write to the Free Software
   17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   18 
   19 */
   20 // cl_view.c -- player rendering positioning
   21 
   22 #include "client.h"
   23 
   24 //=============
   25 //
   26 // development tools for weapons
   27 //
   28 int                     gun_frame;
   29 struct model_s  *gun_model;
   30 
   31 //=============
   32 
   33 cvar_t          *crosshair;
   34 cvar_t          *cl_testparticles;
   35 cvar_t          *cl_testentities;
   36 cvar_t          *cl_testlights;
   37 cvar_t          *cl_testblend;
   38 
   39 cvar_t          *cl_stats;
   40 
   41 
   42 int                     r_numdlights;
   43 dlight_t        r_dlights[MAX_DLIGHTS];
   44 
   45 int                     r_numentities;
   46 entity_t        r_entities[MAX_ENTITIES];
   47 
   48 int                     r_numparticles;
   49 particle_t      r_particles[MAX_PARTICLES];
   50 
   51 lightstyle_t    r_lightstyles[MAX_LIGHTSTYLES];
   52 
   53 char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
   54 int num_cl_weaponmodels;
   55 
   56 /*
   57 ====================
   58 V_ClearScene
   59 
   60 Specifies the model that will be used as the world
   61 ====================
   62 */
   63 void V_ClearScene (void)
   64 {
   65         r_numdlights = 0;
   66         r_numentities = 0;
   67         r_numparticles = 0;
   68 }
   69 
   70 
   71 /*
   72 =====================
   73 V_AddEntity
   74 
   75 =====================
   76 */
   77 void V_AddEntity (entity_t *ent)
   78 {
   79         if (r_numentities >= MAX_ENTITIES)
   80                 return;
   81         r_entities[r_numentities++] = *ent;
   82 }
   83 
   84 
   85 /*
   86 =====================
   87 V_AddParticle
   88 
   89 =====================
   90 */
   91 void V_AddParticle (vec3_t org, int color, float alpha)
   92 {
   93         particle_t      *p;
   94 
   95         if (r_numparticles >= MAX_PARTICLES)
   96                 return;
   97         p = &r_particles[r_numparticles++];
   98         VectorCopy (org, p->origin);
   99         p->color = color;
  100         p->alpha = alpha;
  101 }
  102 
  103 /*
  104 =====================
  105 V_AddLight
  106 
  107 =====================
  108 */
  109 void V_AddLight (vec3_t org, float intensity, float r, float g, float b)
  110 {
  111         dlight_t        *dl;
  112 
  113         if (r_numdlights >= MAX_DLIGHTS)
  114                 return;
  115         dl = &r_dlights[r_numdlights++];
  116         VectorCopy (org, dl->origin);
  117         dl->intensity = intensity;
  118         dl->color[0] = r;
  119         dl->color[1] = g;
  120         dl->color[2] = b;
  121 }
  122 
  123 
  124 /*
  125 =====================
  126 V_AddLightStyle
  127 
  128 =====================
  129 */
  130 void V_AddLightStyle (int style, float r, float g, float b)
  131 {
  132         lightstyle_t    *ls;
  133 
  134         if (style < 0 || style > MAX_LIGHTSTYLES)
  135                 Com_Error (ERR_DROP, "Bad light style %i", style);
  136         ls = &r_lightstyles[style];
  137 
  138         ls->white = r+g+b;
  139         ls->rgb[0] = r;
  140         ls->rgb[1] = g;
  141         ls->rgb[2] = b;
  142 }
  143 
  144 /*
  145 ================
  146 V_TestParticles
  147 
  148 If cl_testparticles is set, create 4096 particles in the view
  149 ================
  150 */
  151 void V_TestParticles (void)
  152 {
  153         particle_t      *p;
  154         int                     i, j;
  155         float           d, r, u;
  156 
  157         r_numparticles = MAX_PARTICLES;
  158         for (i=0 ; i<r_numparticles ; i++)
  159         {
  160                 d = i*0.25;
  161                 r = 4*((i&7)-3.5);
  162                 u = 4*(((i>>3)&7)-3.5);
  163                 p = &r_particles[i];
  164 
  165                 for (j=0 ; j<3 ; j++)
  166                         p->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*d +
  167                         cl.v_right[j]*r + cl.v_up[j]*u;
  168 
  169                 p->color = 8;
  170                 p->alpha = cl_testparticles->value;
  171         }
  172 }
  173 
  174 /*
  175 ================
  176 V_TestEntities
  177 
  178 If cl_testentities is set, create 32 player models
  179 ================
  180 */
  181 void V_TestEntities (void)
  182 {
  183         int                     i, j;
  184         float           f, r;
  185         entity_t        *ent;
  186 
  187         r_numentities = 32;
  188         memset (r_entities, 0, sizeof(r_entities));
  189 
  190         for (i=0 ; i<r_numentities ; i++)
  191         {
  192                 ent = &r_entities[i];
  193 
  194                 r = 64 * ( (i%4) - 1.5 );
  195                 f = 64 * (i/4) + 128;
  196 
  197                 for (j=0 ; j<3 ; j++)
  198                         ent->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
  199                         cl.v_right[j]*r;
  200 
  201                 ent->model = cl.baseclientinfo.model;
  202                 ent->skin = cl.baseclientinfo.skin;
  203         }
  204 }
  205 
  206 /*
  207 ================
  208 V_TestLights
  209 
  210 If cl_testlights is set, create 32 lights models
  211 ================
  212 */
  213 void V_TestLights (void)
  214 {
  215         int                     i, j;
  216         float           f, r;
  217         dlight_t        *dl;
  218 
  219         r_numdlights = 32;
  220         memset (r_dlights, 0, sizeof(r_dlights));
  221 
  222         for (i=0 ; i<r_numdlights ; i++)
  223         {
  224                 dl = &r_dlights[i];
  225 
  226                 r = 64 * ( (i%4) - 1.5 );
  227                 f = 64 * (i/4) + 128;
  228 
  229                 for (j=0 ; j<3 ; j++)
  230                         dl->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
  231                         cl.v_right[j]*r;
  232                 dl->color[0] = ((i%6)+1) & 1;
  233                 dl->color[1] = (((i%6)+1) & 2)>>1;
  234                 dl->color[2] = (((i%6)+1) & 4)>>2;
  235                 dl->intensity = 200;
  236         }
  237 }
  238 
  239 //===================================================================
  240 
  241 /*
  242 =================
  243 CL_PrepRefresh
  244 
  245 Call before entering a new level, or after changing dlls
  246 =================
  247 */
  248 void CL_PrepRefresh (void)
  249 {
  250         char            mapname[32];
  251         int                     i;
  252         char            name[MAX_QPATH];
  253         float           rotate;
  254         vec3_t          axis;
  255 
  256         if (!cl.configstrings[CS_MODELS+1][0])
  257                 return;         // no map loaded
  258 
  259         SCR_AddDirtyPoint (0, 0);
  260         SCR_AddDirtyPoint (viddef.width-1, viddef.height-1);
  261 
  262         // let the render dll load the map
  263         strcpy (mapname, cl.configstrings[CS_MODELS+1] + 5);    // skip "maps/"
  264         mapname[strlen(mapname)-4] = 0;         // cut off ".bsp"
  265 
  266         // register models, pics, and skins
  267         Com_Printf ("Map: %s\r", mapname); 
  268         SCR_UpdateScreen ();
  269         re.BeginRegistration (mapname);
  270         Com_Printf ("                                     \r");
  271 
  272         // precache status bar pics
  273         Com_Printf ("pics\r"); 
  274         SCR_UpdateScreen ();
  275         SCR_TouchPics ();
  276         Com_Printf ("                                     \r");
  277 
  278         CL_RegisterTEntModels ();
  279 
  280         num_cl_weaponmodels = 1;
  281         strcpy(cl_weaponmodels[0], "weapon.md2");
  282 
  283         for (i=1 ; i<MAX_MODELS && cl.configstrings[CS_MODELS+i][0] ; i++)
  284         {
  285                 strcpy (name, cl.configstrings[CS_MODELS+i]);
  286                 name[37] = 0;   // never go beyond one line
  287                 if (name[0] != '*')
  288                         Com_Printf ("%s\r", name); 
  289                 SCR_UpdateScreen ();
  290                 Sys_SendKeyEvents ();   // pump message loop
  291                 if (name[0] == '#')
  292                 {
  293                         // special player weapon model
  294                         if (num_cl_weaponmodels < MAX_CLIENTWEAPONMODELS)
  295                         {
  296                                 strncpy(cl_weaponmodels[num_cl_weaponmodels], cl.configstrings[CS_MODELS+i]+1,
  297                                         sizeof(cl_weaponmodels[num_cl_weaponmodels]) - 1);
  298                                 num_cl_weaponmodels++;
  299                         }
  300                 } 
  301                 else
  302                 {
  303                         cl.model_draw[i] = re.RegisterModel (cl.configstrings[CS_MODELS+i]);
  304                         if (name[0] == '*')
  305                                 cl.model_clip[i] = CM_InlineModel (cl.configstrings[CS_MODELS+i]);
  306                         else
  307                                 cl.model_clip[i] = NULL;
  308                 }
  309                 if (name[0] != '*')
  310                         Com_Printf ("                                     \r");
  311         }
  312 
  313         Com_Printf ("images\r", i); 
  314         SCR_UpdateScreen ();
  315         for (i=1 ; i<MAX_IMAGES && cl.configstrings[CS_IMAGES+i][0] ; i++)
  316         {
  317                 cl.image_precache[i] = re.RegisterPic (cl.configstrings[CS_IMAGES+i]);
  318                 Sys_SendKeyEvents ();   // pump message loop
  319         }
  320         
  321         Com_Printf ("                                     \r");
  322         for (i=0 ; i<MAX_CLIENTS ; i++)
  323         {
  324                 if (!cl.configstrings[CS_PLAYERSKINS+i][0])
  325                         continue;
  326                 Com_Printf ("client %i\r", i); 
  327                 SCR_UpdateScreen ();
  328                 Sys_SendKeyEvents ();   // pump message loop
  329                 CL_ParseClientinfo (i);
  330                 Com_Printf ("                                     \r");
  331         }
  332 
  333         CL_LoadClientinfo (&cl.baseclientinfo, "unnamed\\male/grunt");
  334 
  335         // set sky textures and speed
  336         Com_Printf ("sky\r", i); 
  337         SCR_UpdateScreen ();
  338         rotate = atof (cl.configstrings[CS_SKYROTATE]);
  339         sscanf (cl.configstrings[CS_SKYAXIS], "%f %f %f", 
  340                 &axis[0], &axis[1], &axis[2]);
  341         re.SetSky (cl.configstrings[CS_SKY], rotate, axis);
  342         Com_Printf ("                                     \r");
  343 
  344         // the renderer can now free unneeded stuff
  345         re.EndRegistration ();
  346 
  347         // clear any lines of console text
  348         Con_ClearNotify ();
  349 
  350         SCR_UpdateScreen ();
  351         cl.refresh_prepped = true;
  352         cl.force_refdef = true; // make sure we have a valid refdef
  353 
  354         // start the cd track
  355         CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true);
  356 }
  357 
  358 /*
  359 ====================
  360 CalcFov
  361 ====================
  362 */
  363 float CalcFov (float fov_x, float width, float height)
  364 {
  365         float   a;
  366         float   x;
  367 
  368         if (fov_x < 1 || fov_x > 179)
  369                 Com_Error (ERR_DROP, "Bad fov: %f", fov_x);
  370 
  371         x = width/tan(fov_x/360*M_PI);
  372 
  373         a = atan (height/x);
  374 
  375         a = a*360/M_PI;
  376 
  377         return a;
  378 }
  379 
  380 //============================================================================
  381 
  382 // gun frame debugging functions
  383 void V_Gun_Next_f (void)
  384 {
  385         gun_frame++;
  386         Com_Printf ("frame %i\n", gun_frame);
  387 }
  388 
  389 void V_Gun_Prev_f (void)
  390 {
  391         gun_frame--;
  392         if (gun_frame < 0)
  393                 gun_frame = 0;
  394         Com_Printf ("frame %i\n", gun_frame);
  395 }
  396 
  397 void V_Gun_Model_f (void)
  398 {
  399         char    name[MAX_QPATH];
  400 
  401         if (Cmd_Argc() != 2)
  402         {
  403                 gun_model = NULL;
  404                 return;
  405         }
  406         Com_sprintf (name, sizeof(name), "models/%s/tris.md2", Cmd_Argv(1));
  407         gun_model = re.RegisterModel (name);
  408 }
  409 
  410 //============================================================================
  411 
  412 
  413 /*
  414 =================
  415 SCR_DrawCrosshair
  416 =================
  417 */
  418 void SCR_DrawCrosshair (void)
  419 {
  420         if (!crosshair->value)
  421                 return;
  422 
  423         if (crosshair->modified)
  424         {
  425                 crosshair->modified = false;
  426                 SCR_TouchPics ();
  427         }
  428 
  429         if (!crosshair_pic[0])
  430                 return;
  431 
  432         re.DrawPic (scr_vrect.x + ((scr_vrect.width - crosshair_width)>>1)
  433         , scr_vrect.y + ((scr_vrect.height - crosshair_height)>>1), crosshair_pic);
  434 }
  435 
  436 /*
  437 ==================
  438 V_RenderView
  439 
  440 ==================
  441 */
  442 void V_RenderView( float stereo_separation )
  443 {
  444         extern int entitycmpfnc( const entity_t *, const entity_t * );
  445 
  446         if (cls.state != ca_active)
  447                 return;
  448 
  449         if (!cl.refresh_prepped)
  450                 return;                 // still loading
  451 
  452         if (cl_timedemo->value)
  453         {
  454                 if (!cl.timedemo_start)
  455                         cl.timedemo_start = Sys_Milliseconds ();
  456                 cl.timedemo_frames++;
  457         }
  458 
  459         // an invalid frame will just use the exact previous refdef
  460         // we can't use the old frame if the video mode has changed, though...
  461         if ( cl.frame.valid && (cl.force_refdef || !cl_paused->value) )
  462         {
  463                 cl.force_refdef = false;
  464 
  465                 V_ClearScene ();
  466 
  467                 // build a refresh entity list and calc cl.sim*
  468                 // this also calls CL_CalcViewValues which loads
  469                 // v_forward, etc.
  470                 CL_AddEntities ();
  471 
  472                 if (cl_testparticles->value)
  473                         V_TestParticles ();
  474                 if (cl_testentities->value)
  475                         V_TestEntities ();
  476                 if (cl_testlights->value)
  477                         V_TestLights ();
  478                 if (cl_testblend->value)
  479                 {
  480                         cl.refdef.blend[0] = 1;
  481                         cl.refdef.blend[1] = 0.5;
  482                         cl.refdef.blend[2] = 0.25;
  483                         cl.refdef.blend[3] = 0.5;
  484                 }
  485 
  486                 // offset vieworg appropriately if we're doing stereo separation
  487                 if ( stereo_separation != 0 )
  488                 {
  489                         vec3_t tmp;
  490 
  491                         VectorScale( cl.v_right, stereo_separation, tmp );
  492                         VectorAdd( cl.refdef.vieworg, tmp, cl.refdef.vieworg );
  493                 }
  494 
  495                 // never let it sit exactly on a node line, because a water plane can
  496                 // dissapear when viewed with the eye exactly on it.
  497                 // the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis
  498                 cl.refdef.vieworg[0] += 1.0/16;
  499                 cl.refdef.vieworg[1] += 1.0/16;
  500                 cl.refdef.vieworg[2] += 1.0/16;
  501 
  502                 cl.refdef.x = scr_vrect.x;
  503                 cl.refdef.y = scr_vrect.y;
  504                 cl.refdef.width = scr_vrect.width;
  505                 cl.refdef.height = scr_vrect.height;
  506                 cl.refdef.fov_y = CalcFov (cl.refdef.fov_x, cl.refdef.width, cl.refdef.height);
  507                 cl.refdef.time = cl.time*0.001;
  508 
  509                 cl.refdef.areabits = cl.frame.areabits;
  510 
  511                 if (!cl_add_entities->value)
  512                         r_numentities = 0;
  513                 if (!cl_add_particles->value)
  514                         r_numparticles = 0;
  515                 if (!cl_add_lights->value)
  516                         r_numdlights = 0;
  517                 if (!cl_add_blend->value)
  518                 {
  519                         VectorClear (cl.refdef.blend);
  520                 }
  521 
  522                 cl.refdef.num_entities = r_numentities;
  523                 cl.refdef.entities = r_entities;
  524                 cl.refdef.num_particles = r_numparticles;
  525                 cl.refdef.particles = r_particles;
  526                 cl.refdef.num_dlights = r_numdlights;
  527                 cl.refdef.dlights = r_dlights;
  528                 cl.refdef.lightstyles = r_lightstyles;
  529 
  530                 cl.refdef.rdflags = cl.frame.playerstate.rdflags;
  531 
  532                 // sort entities for better cache locality
  533         qsort( cl.refdef.entities, cl.refdef.num_entities, sizeof( cl.refdef.entities[0] ), (int (*)(const void *, const void *))entitycmpfnc );
  534         }
  535 
  536         re.RenderFrame (&cl.refdef);
  537         if (cl_stats->value)
  538                 Com_Printf ("ent:%i  lt:%i  part:%i\n", r_numentities, r_numdlights, r_numparticles);
  539         if ( log_stats->value && ( log_stats_file != 0 ) )
  540                 fprintf( log_stats_file, "%i,%i,%i,",r_numentities, r_numdlights, r_numparticles);
  541 
  542 
  543         SCR_AddDirtyPoint (scr_vrect.x, scr_vrect.y);
  544         SCR_AddDirtyPoint (scr_vrect.x+scr_vrect.width-1,
  545                 scr_vrect.y+scr_vrect.height-1);
  546 
  547         SCR_DrawCrosshair ();
  548 }
  549 
  550 
  551 /*
  552 =============
  553 V_Viewpos_f
  554 =============
  555 */
  556 void V_Viewpos_f (void)
  557 {
  558         Com_Printf ("(%i %i %i) : %i\n", (int)cl.refdef.vieworg[0],
  559                 (int)cl.refdef.vieworg[1], (int)cl.refdef.vieworg[2], 
  560                 (int)cl.refdef.viewangles[YAW]);
  561 }
  562 
  563 /*
  564 =============
  565 V_Init
  566 =============
  567 */
  568 void V_Init (void)
  569 {
  570         Cmd_AddCommand ("gun_next", V_Gun_Next_f);
  571         Cmd_AddCommand ("gun_prev", V_Gun_Prev_f);
  572         Cmd_AddCommand ("gun_model", V_Gun_Model_f);
  573 
  574         Cmd_AddCommand ("viewpos", V_Viewpos_f);
  575 
  576         crosshair = Cvar_Get ("crosshair", "0", CVAR_ARCHIVE);
  577 
  578         cl_testblend = Cvar_Get ("cl_testblend", "0", 0);
  579         cl_testparticles = Cvar_Get ("cl_testparticles", "0", 0);
  580         cl_testentities = Cvar_Get ("cl_testentities", "0", 0);
  581         cl_testlights = Cvar_Get ("cl_testlights", "0", 0);
  582 
  583         cl_stats = Cvar_Get ("cl_stats", "0", 0);
  584 }
  585