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