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