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