cl_view.c (12877B)
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 }