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