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 qhandle_t	gun_model;
30 
31 //=============
32 
33 cvar_t		*cl_testparticles;
34 cvar_t		*cl_testentities;
35 cvar_t		*cl_testlights;
36 cvar_t		*cl_testblend;
37 
38 cvar_t		*cl_stats;
39 
40 
41 int			r_numdlights;
42 dlight_t	r_dlights[MAX_DLIGHTS];
43 
44 int			r_numentities;
45 entity_t	r_entities[MAX_ENTITIES];
46 
47 int			r_numparticles;
48 particle_t	r_particles[MAX_PARTICLES];
49 
50 lightstyle_t	r_lightstyles[MAX_LIGHTSTYLES];
51 
52 /*
53 ====================
54 V_ClearScene
55 
56 Specifies the model that will be used as the world
57 ====================
58 */
V_ClearScene(void)59 void V_ClearScene (void)
60 {
61 	r_numdlights = 0;
62 	r_numentities = 0;
63 	r_numparticles = 0;
64 }
65 
66 
67 /*
68 =====================
69 V_AddEntity
70 
71 =====================
72 */
V_AddEntity(entity_t * ent)73 void V_AddEntity (entity_t *ent)
74 {
75 	if (r_numentities >= MAX_ENTITIES)
76 		return;
77 
78 	r_entities[r_numentities++] = *ent;
79 }
80 
81 
82 /*
83 =====================
84 V_AddParticle
85 
86 =====================
87 */
V_AddParticle(particle_t * p)88 void V_AddParticle( particle_t *p )
89 {
90 	if (r_numparticles >= MAX_PARTICLES)
91 		return;
92 	r_particles[r_numparticles++] = *p;
93 
94 }
95 
96 /*
97 =====================
98 V_AddLight
99 
100 =====================
101 */
V_AddLight(vec3_t org,float intensity,float r,float g,float b)102 void V_AddLight (vec3_t org, float intensity, float r, float g, float b)
103 {
104 	dlight_t	*dl;
105 
106 	if (r_numdlights >= MAX_DLIGHTS)
107 		return;
108 	dl = &r_dlights[r_numdlights++];
109 	VectorCopy (org, dl->origin);
110 	dl->intensity = intensity;
111 	dl->color[0] = r;
112 	dl->color[1] = g;
113 	dl->color[2] = b;
114 }
115 
116 
117 /*
118 =====================
119 V_AddLightStyle
120 
121 =====================
122 */
V_AddLightStyle(int style,vec4_t value)123 void V_AddLightStyle (int style, vec4_t value)
124 {
125 	lightstyle_t	*ls;
126 
127 	if (style < 0 || style > MAX_LIGHTSTYLES)
128 		Com_Error (ERR_DROP, "Bad light style %i", style);
129 	ls = &r_lightstyles[style];
130 
131 	//ls->white = r+g+b;
132 	ls->rgb[0] = value[0];
133 	ls->rgb[1] = value[1];
134 	ls->rgb[2] = value[2];
135 	ls->white = value[3];
136 }
137 
138 /*
139 ================
140 V_TestParticles
141 
142 If cl_testparticles is set, create 4096 particles in the view
143 ================
144 */
V_TestParticles(void)145 void V_TestParticles (void)
146 {
147 	particle_t	*p;
148 	int			i, j;
149 	float		d, r, u;
150 
151 	r_numparticles = MAX_PARTICLES;
152 	for (i=0 ; i<r_numparticles ; i++)
153 	{
154 		d = i*0.25;
155 		r = 4*((i&7)-3.5);
156 		u = 4*(((i>>3)&7)-3.5);
157 		p = &r_particles[i];
158 
159 		for (j=0 ; j<3 ; j++)
160 			p->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*d +
161 			cl.v_right[j]*r + cl.v_up[j]*u;
162 
163 		p->color = 8;
164 		p->alpha = cl_testparticles->value;
165 	}
166 }
167 
168 /*
169 ================
170 V_TestEntities
171 
172 If cl_testentities is set, create 32 player models
173 ================
174 */
V_TestEntities(void)175 void V_TestEntities (void)
176 {
177 	int			i, j;
178 	float		f, r;
179 	entity_t	*ent;
180 
181 	r_numentities = 32;
182 	memset (r_entities, 0, sizeof(r_entities));
183 
184 	for (i=0 ; i<r_numentities ; i++)
185 	{
186 		ent = &r_entities[i];
187 
188 		r = 64 * ( (i%4) - 1.5 );
189 		f = 64 * (i/4) + 128;
190 
191 		for (j=0 ; j<3 ; j++)
192 			ent->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
193 			cl.v_right[j]*r;
194 
195 		ent->model = cl.baseclientinfo.model;
196 		ent->skin = cl.baseclientinfo.skin;
197 	}
198 }
199 
200 /*
201 ================
202 V_TestLights
203 
204 If cl_testlights is set, create 32 lights models
205 ================
206 */
V_TestLights(void)207 void V_TestLights (void)
208 {
209 	int			i, j;
210 	float		f, r;
211 	dlight_t	*dl;
212 
213     if( cl_testlights->integer == 2 ) {
214         dl = &r_dlights[0];
215         r_numdlights = 1;
216 
217         VectorMA( cl.refdef.vieworg, 256, cl.v_forward, dl->origin );
218         VectorSet( dl->color, 1, 1, 1  );
219         dl->intensity = 256;
220         return;
221     }
222 
223 	r_numdlights = 32;
224 	memset (r_dlights, 0, sizeof(r_dlights));
225 
226 	for (i=0 ; i<r_numdlights ; i++)
227 	{
228 		dl = &r_dlights[i];
229 
230 		r = 64 * ( (i%4) - 1.5 );
231 		f = 64 * (i/4) + 128;
232 
233 		for (j=0 ; j<3 ; j++)
234 			dl->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j]*f +
235 			cl.v_right[j]*r;
236 		dl->color[0] = ((i%6)+1) & 1;
237 		dl->color[1] = (((i%6)+1) & 2)>>1;
238 		dl->color[2] = (((i%6)+1) & 4)>>2;
239 		dl->intensity = 200;
240 	}
241 }
242 
243 //===================================================================
244 
245 /*
246 =================
247 CL_PrepRefresh
248 
249 Call before entering a new level, or after changing dlls
250 =================
251 */
CL_PrepRefresh(void)252 void CL_PrepRefresh (void)
253 {
254 	int			i;
255 	char		*name;
256 	float		rotate;
257 	vec3_t		axis;
258 	int			time;
259 
260 	if (!cl.mapname[0])
261 		return;		// no map loaded
262 
263 	time = Sys_Milliseconds();
264 
265 	Con_Close();
266 	UI_OpenMenu( UIMENU_NONE );
267 
268 	// register models, pics, and skins
269 	SCR_LoadingString( cl.configstrings[CS_MODELS+1] );
270 	ref.BeginRegistration( cl.mapname );
271 
272 	// precache status bar pics
273 	SCR_LoadingString( "pics" );
274 	SCR_TouchPics ();
275 
276 	SCR_LoadingString( "models" );
277 
278 	CL_RegisterTEntModels ();
279 
280 	cl.numWeaponModels = 1;
281 	strcpy(cl.weaponModels[0], "weapon.md2");
282 
283 	for (i=1 ; i<MAX_MODELS ; i++)
284 	{
285 		name = cl.configstrings[CS_MODELS+i];
286         if( !name[0] ) {
287             break;
288         }
289 		if (name[0] == '#')
290 		{
291 			// special player weapon model
292 			if (cl.numWeaponModels < MAX_CLIENTWEAPONMODELS)
293 			{
294 				Q_strncpyz( cl.weaponModels[cl.numWeaponModels], name + 1,
295 			        sizeof( cl.weaponModels[0] ) );
296 				cl.numWeaponModels++;
297 			}
298 		}
299 		else
300 		{
301 			cl.model_draw[i] = ref.RegisterModel( name );
302 			if( name[0] == '*' )
303 				cl.model_clip[i] = CM_InlineModel( &cl.cm, name );
304 			else
305 				cl.model_clip[i] = NULL;
306 		}
307 	}
308 
309 	SCR_LoadingString( "images" );
310 	for (i=1 ; i<MAX_IMAGES && cl.configstrings[CS_IMAGES+i][0] ; i++)
311 	{
312 		cl.image_precache[i] = ref.RegisterPic (cl.configstrings[CS_IMAGES+i]);
313 	}
314 
315 	SCR_LoadingString( "clients" );
316 
317 	for (i=0 ; i<MAX_CLIENTS ; i++)
318 	{
319 		if (!cl.configstrings[CS_PLAYERSKINS+i][0])
320 			continue;
321 
322 		CL_ParseClientinfo (i);
323 	}
324 
325 	CL_LoadClientinfo (&cl.baseclientinfo, "unnamed\\male/grunt", cl.clientNum);
326 
327 	SCR_LoadingString( "sky" );
328 
329 	// set sky textures and speed
330 	rotate = atof (cl.configstrings[CS_SKYROTATE]);
331 	sscanf (cl.configstrings[CS_SKYAXIS], "%f %f %f",
332 		&axis[0], &axis[1], &axis[2]);
333 	ref.SetSky (cl.configstrings[CS_SKY], rotate, axis);
334 
335 	SCR_LoadingString( "" );
336 
337 	// the renderer can now free unneeded stuff
338 	ref.EndRegistration ();
339 
340 	FS_FlushCache();
341 
342 	// clear any lines of console text
343 	Con_ClearNotify_f ();
344 
345 	SCR_UpdateScreen ();
346 
347 	// start the cd track
348 	CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), qtrue);
349 
350 	Com_Printf( "Map loaded in %d msec\n", Sys_Milliseconds() - time );
351 }
352 
353 
354 //============================================================================
355 
356 // gun frame debugging functions
V_Gun_Next_f(void)357 void V_Gun_Next_f (void)
358 {
359 	gun_frame++;
360 	Com_Printf ("frame %i\n", gun_frame);
361 }
362 
V_Gun_Prev_f(void)363 void V_Gun_Prev_f (void)
364 {
365 	gun_frame--;
366 	if (gun_frame < 0)
367 		gun_frame = 0;
368 	Com_Printf ("frame %i\n", gun_frame);
369 }
370 
V_Gun_Model_f(void)371 void V_Gun_Model_f (void)
372 {
373 	char	name[MAX_QPATH];
374 
375 	if (Cmd_Argc() != 2)
376 	{
377 		gun_model = 0;
378 		return;
379 	}
380 	Com_sprintf (name, sizeof(name), "models/%s/tris.md2", Cmd_Argv(1));
381 	gun_model = ref.RegisterModel (name);
382 }
383 
384 //============================================================================
385 
entitycmpfnc(const entity_t * a,const entity_t * b)386 static int entitycmpfnc( const entity_t *a, const entity_t *b )
387 {
388 	/*
389 	** all other models are sorted by model then skin
390 	*/
391 	if ( a->model == b->model )
392 	{
393 		return ( ( int ) a->skin - ( int ) b->skin );
394 	}
395 	else
396 	{
397 		return ( ( int ) a->model - ( int ) b->model );
398 	}
399 }
400 
V_SetLightLevel(void)401 void V_SetLightLevel( void ) {
402 	vec3_t shadelight;
403 
404 	// save off light value for server to look at (BIG HACK!)
405 	ref.LightPoint( cl.refdef.vieworg, shadelight );
406 
407 	// pick the greatest component, which should be the same
408 	// as the mono value returned by software
409 	if( shadelight[0] > shadelight[1] ) {
410 		if( shadelight[0] > shadelight[2] ) {
411 			cl.lightlevel = 150.0f*shadelight[0];
412 		} else {
413 			cl.lightlevel = 150.0f*shadelight[2];
414 		}
415 	} else {
416 		if( shadelight[1] > shadelight[2] ) {
417 			cl.lightlevel = 150.0f*shadelight[1];
418 		} else {
419 			cl.lightlevel = 150.0f*shadelight[2];
420 		}
421 	}
422 }
423 
424 /*
425 ==================
426 V_RenderView
427 
428 ==================
429 */
V_RenderView(float stereo_separation)430 void V_RenderView( float stereo_separation ) {
431 	// an invalid frame will just use the exact previous refdef
432 	// we can't use the old frame if the video mode has changed, though...
433 	if( cl.frame.valid ) {
434 		V_ClearScene ();
435 
436 		// build a refresh entity list and calc cl.sim*
437 		// this also calls CL_CalcViewValues which loads
438 		// v_forward, etc.
439 		CL_AddEntities ();
440 
441 		if (cl_testparticles->integer)
442 			V_TestParticles ();
443 		if (cl_testentities->integer)
444 			V_TestEntities ();
445 		if (cl_testlights->integer)
446 			V_TestLights ();
447 		if (cl_testblend->integer)
448 		{
449 			cl.refdef.blend[0] = 1;
450 			cl.refdef.blend[1] = 0.5;
451 			cl.refdef.blend[2] = 0.25;
452 			cl.refdef.blend[3] = 0.5;
453 		}
454 
455 		// offset vieworg appropriately if we're doing stereo separation
456 		if ( stereo_separation != 0 )
457 		{
458 			vec3_t tmp;
459 
460 			VectorScale( cl.v_right, stereo_separation, tmp );
461 			VectorAdd( cl.refdef.vieworg, tmp, cl.refdef.vieworg );
462 		}
463 
464 		// never let it sit exactly on a node line, because a water plane can
465 		// dissapear when viewed with the eye exactly on it.
466 		// the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis
467 		cl.refdef.vieworg[0] += 1.0/16;
468 		cl.refdef.vieworg[1] += 1.0/16;
469 		cl.refdef.vieworg[2] += 1.0/16;
470 
471 		cl.refdef.x = scr_vrect.x;
472 		cl.refdef.y = scr_vrect.y;
473 		cl.refdef.width = scr_vrect.width;
474 		cl.refdef.height = scr_vrect.height;
475 
476 		cl.refdef.fov_y = Com_CalcFov (cl.refdef.fov_x, cl.refdef.width, cl.refdef.height);
477 		cl.refdef.time = cl.time*0.001;
478 
479 		cl.refdef.areabits = cl.frame.areabits;
480 
481 		if (!cl_add_entities->integer)
482 			r_numentities = 0;
483 		if (!cl_add_particles->integer)
484 			r_numparticles = 0;
485 		if (!cl_add_lights->integer)
486 			r_numdlights = 0;
487 		//if (!cl_add_blend->value)
488 		//{
489 		//	VectorClear (cl.refdef.blend);
490 		//}
491 
492 		cl.refdef.num_entities = r_numentities;
493 		cl.refdef.entities = r_entities;
494 		cl.refdef.num_particles = r_numparticles;
495 		cl.refdef.particles = r_particles;
496 		cl.refdef.num_dlights = r_numdlights;
497 		cl.refdef.dlights = r_dlights;
498 		cl.refdef.lightstyles = r_lightstyles;
499 
500 		cl.refdef.rdflags = cl.frame.ps.ps.rdflags;
501 
502 		// sort entities for better cache locality
503         qsort( cl.refdef.entities, cl.refdef.num_entities, sizeof( cl.refdef.entities[0] ), (int (*)(const void *, const void *))entitycmpfnc );
504 	}
505 
506 	ref.RenderFrame (&cl.refdef);
507 	if (cl_stats->integer)
508 		Com_Printf ("ent:%i  lt:%i  part:%i\n", r_numentities, r_numdlights, r_numparticles);
509 
510 	V_SetLightLevel();
511 
512 }
513 
514 
515 /*
516 =============
517 V_Viewpos_f
518 =============
519 */
V_Viewpos_f(void)520 void V_Viewpos_f (void)
521 {
522 	Com_Printf ("(%i %i %i) : %i\n", (int)cl.refdef.vieworg[0],
523 		(int)cl.refdef.vieworg[1], (int)cl.refdef.vieworg[2],
524 		(int)cl.refdef.viewangles[YAW]);
525 }
526 
527 /*
528 =============
529 V_Init
530 =============
531 */
V_Init(void)532 void V_Init (void)
533 {
534 	Cmd_AddCommand ("gun_next", V_Gun_Next_f);
535 	Cmd_AddCommand ("gun_prev", V_Gun_Prev_f);
536 	Cmd_AddCommand ("gun_model", V_Gun_Model_f);
537 
538 	Cmd_AddCommand ("viewpos", V_Viewpos_f);
539 
540 	crosshair = Cvar_Get ("crosshair", "0", CVAR_ARCHIVE);
541 
542 	cl_testblend = Cvar_Get ("cl_testblend", "0", 0);
543 	cl_testparticles = Cvar_Get ("cl_testparticles", "0", 0);
544 	cl_testentities = Cvar_Get ("cl_testentities", "0", 0);
545 	cl_testlights = Cvar_Get ("cl_testlights", "0", CVAR_CHEAT);
546 
547 	cl_stats = Cvar_Get ("cl_stats", "0", 0);
548 }
549 
550 
551