1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// view.c -- player eye positioning
21
22#include "quakedef.h"
23#include "r_local.h"
24
25/*
26
27The view is allowed to move slightly from it's true position for bobbing,
28but if it exceeds 8 pixels linear distance (spherical, not box), the list of
29entities sent from the server may not include everything in the pvs, especially
30when crossing a water boudnary.
31
32*/
33
34cvar_t	lcd_x = CVAR2("lcd_x", "0");	// FIXME: make this work sometime...
35
36cvar_t	cl_rollspeed = CVAR2("cl_rollspeed", "200");
37cvar_t	cl_rollangle = CVAR2("cl_rollangle", "2.0");
38
39cvar_t	cl_bob = CVAR3("cl_bob","0.02", false);
40cvar_t	cl_bobcycle = CVAR3("cl_bobcycle","0.6", false);
41cvar_t	cl_bobup = CVAR3("cl_bobup","0.5", false);
42
43cvar_t	v_kicktime = CVAR3("v_kicktime", "0.5", false);
44cvar_t	v_kickroll = CVAR3("v_kickroll", "0.6", false);
45cvar_t	v_kickpitch = CVAR3("v_kickpitch", "0.6", false);
46
47cvar_t	v_iyaw_cycle = CVAR3("v_iyaw_cycle", "2", false);
48cvar_t	v_iroll_cycle = CVAR3("v_iroll_cycle", "0.5", false);
49cvar_t	v_ipitch_cycle = CVAR3("v_ipitch_cycle", "1", false);
50cvar_t	v_iyaw_level = CVAR3("v_iyaw_level", "0.3", false);
51cvar_t	v_iroll_level = CVAR3("v_iroll_level", "0.1", false);
52cvar_t	v_ipitch_level = CVAR3("v_ipitch_level", "0.3", false);
53
54cvar_t	v_idlescale = CVAR3("v_idlescale", "0", false);
55
56cvar_t	crosshair = CVAR3("crosshair", "0", true);
57cvar_t	crosshaircolor = CVAR3("crosshaircolor", "79", true);
58
59cvar_t  cl_crossx = CVAR3("cl_crossx", "0", true);
60cvar_t  cl_crossy = CVAR3("cl_crossy", "0", true);
61
62#ifdef GLQUAKE
63cvar_t	gl_cshiftpercent = CVAR3("gl_cshiftpercent", "100", false);
64#endif
65
66cvar_t  v_contentblend = CVAR3("v_contentblend", "1", false);
67
68float	v_dmg_time, v_dmg_roll, v_dmg_pitch;
69
70extern	int			in_forward, in_forward2, in_back;
71
72frame_t		*view_frame;
73player_state_t		*view_message;
74
75/*
76===============
77V_CalcRoll
78
79===============
80*/
81float V_CalcRoll (vec3_t angles, vec3_t velocity)
82{
83	vec3_t	forward, right, up;
84	float	sign;
85	float	side;
86	float	value;
87
88	AngleVectors (angles, forward, right, up);
89	side = DotProduct (velocity, right);
90	sign = side < 0 ? -1 : 1;
91	side = fabs(side);
92
93	value = cl_rollangle.value;
94
95	if (side < cl_rollspeed.value)
96		side = side * value / cl_rollspeed.value;
97	else
98		side = value;
99
100	return side*sign;
101
102}
103
104
105/*
106===============
107V_CalcBob
108
109===============
110*/
111float V_CalcBob (void)
112{
113	static	double	bobtime;
114	static float	bob;
115	float	cycle;
116
117	if (cl.spectator)
118		return 0;
119
120	if (onground == -1)
121		return bob;		// just use old value
122
123	bobtime += host_frametime;
124	cycle = bobtime - (int)(bobtime/cl_bobcycle.value)*cl_bobcycle.value;
125	cycle /= cl_bobcycle.value;
126	if (cycle < cl_bobup.value)
127		cycle = M_PI * cycle / cl_bobup.value;
128	else
129		cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value);
130
131// bob is proportional to simulated velocity in the xy plane
132// (don't count Z, or jumping messes it up)
133
134	bob = sqrt(cl.simvel[0]*cl.simvel[0] + cl.simvel[1]*cl.simvel[1]) * cl_bob.value;
135	bob = bob*0.3 + bob*0.7*sin(cycle);
136	if (bob > 4)
137		bob = 4;
138	else if (bob < -7)
139		bob = -7;
140	return bob;
141
142}
143
144
145//=============================================================================
146
147
148cvar_t	v_centermove = CVAR3("v_centermove", "0.15", false);
149cvar_t	v_centerspeed = CVAR2("v_centerspeed","500");
150
151
152void V_StartPitchDrift (void)
153{
154#if 1
155	if (cl.laststop == cl.time)
156	{
157		return;		// something else is keeping it from drifting
158	}
159#endif
160	if (cl.nodrift || !cl.pitchvel)
161	{
162		cl.pitchvel = v_centerspeed.value;
163		cl.nodrift = false;
164		cl.driftmove = 0;
165	}
166}
167
168void V_StopPitchDrift (void)
169{
170	cl.laststop = cl.time;
171	cl.nodrift = true;
172	cl.pitchvel = 0;
173}
174
175/*
176===============
177V_DriftPitch
178
179Moves the client pitch angle towards cl.idealpitch sent by the server.
180
181If the user is adjusting pitch manually, either with lookup/lookdown,
182mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped.
183
184Drifting is enabled when the center view key is hit, mlook is released and
185lookspring is non 0, or when
186===============
187*/
188void V_DriftPitch (void)
189{
190	float		delta, move;
191
192	if (view_message->onground == -1 || cls.demoplayback )
193	{
194		cl.driftmove = 0;
195		cl.pitchvel = 0;
196		return;
197	}
198
199// don't count small mouse motion
200	if (cl.nodrift)
201	{
202		if ( fabs(cl.frames[(cls.netchan.outgoing_sequence-1)&UPDATE_MASK].cmd.forwardmove) < 200)
203			cl.driftmove = 0;
204		else
205			cl.driftmove += host_frametime;
206
207		if ( cl.driftmove > v_centermove.value)
208		{
209			V_StartPitchDrift ();
210		}
211		return;
212	}
213
214	delta = 0 - cl.viewangles[PITCH];
215
216	if (!delta)
217	{
218		cl.pitchvel = 0;
219		return;
220	}
221
222	move = host_frametime * cl.pitchvel;
223	cl.pitchvel += host_frametime * v_centerspeed.value;
224
225//Con_Printf ("move: %f (%f)\n", move, host_frametime);
226
227	if (delta > 0)
228	{
229		if (move > delta)
230		{
231			cl.pitchvel = 0;
232			move = delta;
233		}
234		cl.viewangles[PITCH] += move;
235	}
236	else if (delta < 0)
237	{
238		if (move > -delta)
239		{
240			cl.pitchvel = 0;
241			move = -delta;
242		}
243		cl.viewangles[PITCH] -= move;
244	}
245}
246
247
248
249
250
251/*
252==============================================================================
253
254						PALETTE FLASHES
255
256==============================================================================
257*/
258
259
260cshift_t	cshift_empty = { {130,80,50}, 0 };
261cshift_t	cshift_water = { {130,80,50}, 128 };
262cshift_t	cshift_slime = { {0,25,5}, 150 };
263cshift_t	cshift_lava = { {255,80,0}, 150 };
264
265cvar_t		v_gamma = CVAR3("gamma", "1", true);
266
267byte		gammatable[256];	// palette is sent through this
268
269
270#ifdef	GLQUAKE
271byte		ramps[3][256];
272float		v_blend[4];		// rgba 0.0 - 1.0
273#endif	// GLQUAKE
274
275void BuildGammaTable (float g)
276{
277	int		i, inf;
278
279	if (g == 1.0)
280	{
281		for (i=0 ; i<256 ; i++)
282			gammatable[i] = i;
283		return;
284	}
285
286	for (i=0 ; i<256 ; i++)
287	{
288		inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5;
289		if (inf < 0)
290			inf = 0;
291		if (inf > 255)
292			inf = 255;
293		gammatable[i] = inf;
294	}
295}
296
297/*
298=================
299V_CheckGamma
300=================
301*/
302qboolean V_CheckGamma (void)
303{
304	static float oldgammavalue;
305
306	if (v_gamma.value == oldgammavalue)
307		return false;
308	oldgammavalue = v_gamma.value;
309
310	BuildGammaTable (v_gamma.value);
311	vid.recalc_refdef = 1;				// force a surface cache flush
312
313	return true;
314}
315
316
317
318/*
319===============
320V_ParseDamage
321===============
322*/
323void V_ParseDamage (void)
324{
325	int		armor, blood;
326	vec3_t	from;
327	int		i;
328	vec3_t	forward, right, up;
329	float	side;
330	float	count;
331
332	armor = MSG_ReadByte ();
333	blood = MSG_ReadByte ();
334	for (i=0 ; i<3 ; i++)
335		from[i] = MSG_ReadCoord ();
336
337	count = blood*0.5 + armor*0.5;
338	if (count < 10)
339		count = 10;
340
341	cl.faceanimtime = cl.time + 0.2;		// but sbar face into pain frame
342
343	cl.cshifts[CSHIFT_DAMAGE].percent += 3*count;
344	if (cl.cshifts[CSHIFT_DAMAGE].percent < 0)
345		cl.cshifts[CSHIFT_DAMAGE].percent = 0;
346	if (cl.cshifts[CSHIFT_DAMAGE].percent > 150)
347		cl.cshifts[CSHIFT_DAMAGE].percent = 150;
348
349	if (armor > blood)
350	{
351		cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200;
352		cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100;
353		cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100;
354	}
355	else if (armor)
356	{
357		cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220;
358		cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50;
359		cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50;
360	}
361	else
362	{
363		cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255;
364		cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0;
365		cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0;
366	}
367
368//
369// calculate view angle kicks
370//
371	VectorSubtract (from, cl.simorg, from);
372	VectorNormalize (from);
373
374	AngleVectors (cl.simangles, forward, right, up);
375
376	side = DotProduct (from, right);
377	v_dmg_roll = count*side*v_kickroll.value;
378
379	side = DotProduct (from, forward);
380	v_dmg_pitch = count*side*v_kickpitch.value;
381
382	v_dmg_time = v_kicktime.value;
383}
384
385
386/*
387==================
388V_cshift_f
389==================
390*/
391void V_cshift_f (void)
392{
393	cshift_empty.destcolor[0] = atoi(Cmd_Argv(1));
394	cshift_empty.destcolor[1] = atoi(Cmd_Argv(2));
395	cshift_empty.destcolor[2] = atoi(Cmd_Argv(3));
396	cshift_empty.percent = atoi(Cmd_Argv(4));
397}
398
399
400/*
401==================
402V_BonusFlash_f
403
404When you run over an item, the server sends this command
405==================
406*/
407void V_BonusFlash_f (void)
408{
409	cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215;
410	cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186;
411	cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69;
412	cl.cshifts[CSHIFT_BONUS].percent = 50;
413}
414
415/*
416=============
417V_SetContentsColor
418
419Underwater, lava, etc each has a color shift
420=============
421*/
422void V_SetContentsColor (int contents)
423{
424	if (!v_contentblend.value) {
425		cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
426		return;
427	}
428
429	switch (contents)
430	{
431	case CONTENTS_EMPTY:
432		cl.cshifts[CSHIFT_CONTENTS] = cshift_empty;
433		break;
434	case CONTENTS_LAVA:
435		cl.cshifts[CSHIFT_CONTENTS] = cshift_lava;
436		break;
437	case CONTENTS_SOLID:
438	case CONTENTS_SLIME:
439		cl.cshifts[CSHIFT_CONTENTS] = cshift_slime;
440		break;
441	default:
442		cl.cshifts[CSHIFT_CONTENTS] = cshift_water;
443	}
444}
445
446/*
447=============
448V_CalcPowerupCshift
449=============
450*/
451void V_CalcPowerupCshift (void)
452{
453	if (cl.stats[STAT_ITEMS] & IT_QUAD)
454	{
455		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
456		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0;
457		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255;
458		cl.cshifts[CSHIFT_POWERUP].percent = 30;
459	}
460	else if (cl.stats[STAT_ITEMS] & IT_SUIT)
461	{
462		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0;
463		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
464		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
465		cl.cshifts[CSHIFT_POWERUP].percent = 20;
466	}
467	else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY)
468	{
469		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100;
470		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100;
471		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100;
472		cl.cshifts[CSHIFT_POWERUP].percent = 100;
473	}
474	else if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY)
475	{
476		cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255;
477		cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255;
478		cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0;
479		cl.cshifts[CSHIFT_POWERUP].percent = 30;
480	}
481	else
482		cl.cshifts[CSHIFT_POWERUP].percent = 0;
483}
484
485
486/*
487=============
488V_CalcBlend
489=============
490*/
491#ifdef	GLQUAKE
492void V_CalcBlend (void)
493{
494	float	r, g, b, a, a2;
495	int		j;
496
497	r = 0;
498	g = 0;
499	b = 0;
500	a = 0;
501
502	for (j=0 ; j<NUM_CSHIFTS ; j++)
503	{
504		if (!gl_cshiftpercent.value)
505			continue;
506
507		a2 = ((cl.cshifts[j].percent * gl_cshiftpercent.value) / 100.0) / 255.0;
508
509//		a2 = (cl.cshifts[j].percent/2)/255.0;
510		if (!a2)
511			continue;
512		a = a + a2*(1-a);
513//Con_Printf ("j:%i a:%f\n", j, a);
514		a2 = a2/a;
515		r = r*(1-a2) + cl.cshifts[j].destcolor[0]*a2;
516		g = g*(1-a2) + cl.cshifts[j].destcolor[1]*a2;
517		b = b*(1-a2) + cl.cshifts[j].destcolor[2]*a2;
518	}
519
520	v_blend[0] = r/255.0;
521	v_blend[1] = g/255.0;
522	v_blend[2] = b/255.0;
523	v_blend[3] = a;
524	if (v_blend[3] > 1)
525		v_blend[3] = 1;
526	if (v_blend[3] < 0)
527		v_blend[3] = 0;
528}
529#endif
530
531/*
532=============
533V_UpdatePalette
534=============
535*/
536#ifdef	GLQUAKE
537void V_UpdatePalette (void)
538{
539	int		i, j;
540	qboolean	new;
541	byte	*basepal, *newpal;
542	byte	pal[768];
543	float	r,g,b,a;
544	int		ir, ig, ib;
545	qboolean force;
546
547	V_CalcPowerupCshift ();
548
549	new = false;
550
551	for (i=0 ; i<NUM_CSHIFTS ; i++)
552	{
553		if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
554		{
555			new = true;
556			cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
557		}
558		for (j=0 ; j<3 ; j++)
559			if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
560			{
561				new = true;
562				cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
563			}
564	}
565
566// drop the damage value
567	cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150;
568	if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
569		cl.cshifts[CSHIFT_DAMAGE].percent = 0;
570
571// drop the bonus value
572	cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100;
573	if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
574		cl.cshifts[CSHIFT_BONUS].percent = 0;
575
576	force = V_CheckGamma ();
577	if (!new && !force)
578		return;
579
580	V_CalcBlend ();
581
582//Con_Printf("b: %4.2f %4.2f %4.2f %4.6f\n", v_blend[0],	v_blend[1],	v_blend[2],	v_blend[3]);
583
584	a = v_blend[3];
585	r = 255*v_blend[0]*a;
586	g = 255*v_blend[1]*a;
587	b = 255*v_blend[2]*a;
588
589	a = 1-a;
590	for (i=0 ; i<256 ; i++)
591	{
592		ir = i*a + r;
593		ig = i*a + g;
594		ib = i*a + b;
595		if (ir > 255)
596			ir = 255;
597		if (ig > 255)
598			ig = 255;
599		if (ib > 255)
600			ib = 255;
601
602		ramps[0][i] = gammatable[ir];
603		ramps[1][i] = gammatable[ig];
604		ramps[2][i] = gammatable[ib];
605	}
606
607	basepal = host_basepal;
608	newpal = pal;
609
610	for (i=0 ; i<256 ; i++)
611	{
612		ir = basepal[0];
613		ig = basepal[1];
614		ib = basepal[2];
615		basepal += 3;
616
617		newpal[0] = ramps[0][ir];
618		newpal[1] = ramps[1][ig];
619		newpal[2] = ramps[2][ib];
620		newpal += 3;
621	}
622
623	VID_ShiftPalette (pal);
624}
625#else	// !GLQUAKE
626/*
627=============
628V_UpdatePalette
629=============
630*/
631void V_UpdatePalette (void)
632{
633	int		i, j;
634	qboolean	new;
635	byte	*basepal, *newpal;
636	byte	pal[768];
637	int		r,g,b;
638	qboolean force;
639
640	V_CalcPowerupCshift ();
641
642	new = false;
643
644	for (i=0 ; i<NUM_CSHIFTS ; i++)
645	{
646		if (cl.cshifts[i].percent != cl.prev_cshifts[i].percent)
647		{
648			new = true;
649			cl.prev_cshifts[i].percent = cl.cshifts[i].percent;
650		}
651		for (j=0 ; j<3 ; j++)
652			if (cl.cshifts[i].destcolor[j] != cl.prev_cshifts[i].destcolor[j])
653			{
654				new = true;
655				cl.prev_cshifts[i].destcolor[j] = cl.cshifts[i].destcolor[j];
656			}
657	}
658
659// drop the damage value
660	cl.cshifts[CSHIFT_DAMAGE].percent -= host_frametime*150;
661	if (cl.cshifts[CSHIFT_DAMAGE].percent <= 0)
662		cl.cshifts[CSHIFT_DAMAGE].percent = 0;
663
664// drop the bonus value
665	cl.cshifts[CSHIFT_BONUS].percent -= host_frametime*100;
666	if (cl.cshifts[CSHIFT_BONUS].percent <= 0)
667		cl.cshifts[CSHIFT_BONUS].percent = 0;
668
669	force = V_CheckGamma ();
670	if (!new && !force)
671		return;
672
673	basepal = host_basepal;
674	newpal = pal;
675
676	for (i=0 ; i<256 ; i++)
677	{
678		r = basepal[0];
679		g = basepal[1];
680		b = basepal[2];
681		basepal += 3;
682
683		for (j=0 ; j<NUM_CSHIFTS ; j++)
684		{
685			r += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[0]-r))>>8;
686			g += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[1]-g))>>8;
687			b += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[2]-b))>>8;
688		}
689
690		newpal[0] = gammatable[r];
691		newpal[1] = gammatable[g];
692		newpal[2] = gammatable[b];
693		newpal += 3;
694	}
695
696	VID_ShiftPalette (pal);
697}
698
699#endif	// !GLQUAKE
700
701/*
702==============================================================================
703
704						VIEW RENDERING
705
706==============================================================================
707*/
708
709float angledelta (float a)
710{
711	a = anglemod(a);
712	if (a > 180)
713		a -= 360;
714	return a;
715}
716
717/*
718==================
719CalcGunAngle
720==================
721*/
722void CalcGunAngle (void)
723{
724	float	yaw, pitch, move;
725	static float oldyaw = 0;
726	static float oldpitch = 0;
727
728	yaw = r_refdef.viewangles[YAW];
729	pitch = -r_refdef.viewangles[PITCH];
730
731	yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4;
732	if (yaw > 10)
733		yaw = 10;
734	if (yaw < -10)
735		yaw = -10;
736	pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4;
737	if (pitch > 10)
738		pitch = 10;
739	if (pitch < -10)
740		pitch = -10;
741	move = host_frametime*20;
742	if (yaw > oldyaw)
743	{
744		if (oldyaw + move < yaw)
745			yaw = oldyaw + move;
746	}
747	else
748	{
749		if (oldyaw - move > yaw)
750			yaw = oldyaw - move;
751	}
752
753	if (pitch > oldpitch)
754	{
755		if (oldpitch + move < pitch)
756			pitch = oldpitch + move;
757	}
758	else
759	{
760		if (oldpitch - move > pitch)
761			pitch = oldpitch - move;
762	}
763
764	oldyaw = yaw;
765	oldpitch = pitch;
766
767	cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw;
768	cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch);
769}
770
771/*
772==============
773V_BoundOffsets
774==============
775*/
776void V_BoundOffsets (void)
777{
778// absolutely bound refresh reletive to entity clipping hull
779// so the view can never be inside a solid wall
780
781	if (r_refdef.vieworg[0] < cl.simorg[0] - 14)
782		r_refdef.vieworg[0] = cl.simorg[0] - 14;
783	else if (r_refdef.vieworg[0] > cl.simorg[0] + 14)
784		r_refdef.vieworg[0] = cl.simorg[0] + 14;
785	if (r_refdef.vieworg[1] < cl.simorg[1] - 14)
786		r_refdef.vieworg[1] = cl.simorg[1] - 14;
787	else if (r_refdef.vieworg[1] > cl.simorg[1] + 14)
788		r_refdef.vieworg[1] = cl.simorg[1] + 14;
789	if (r_refdef.vieworg[2] < cl.simorg[2] - 22)
790		r_refdef.vieworg[2] = cl.simorg[2] - 22;
791	else if (r_refdef.vieworg[2] > cl.simorg[2] + 30)
792		r_refdef.vieworg[2] = cl.simorg[2] + 30;
793}
794
795/*
796==============
797V_AddIdle
798
799Idle swaying
800==============
801*/
802void V_AddIdle (void)
803{
804	r_refdef.viewangles[ROLL] += v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
805	r_refdef.viewangles[PITCH] += v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
806	r_refdef.viewangles[YAW] += v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
807
808	cl.viewent.angles[ROLL] -= v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value;
809	cl.viewent.angles[PITCH] -= v_idlescale.value * sin(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value;
810	cl.viewent.angles[YAW] -= v_idlescale.value * sin(cl.time*v_iyaw_cycle.value) * v_iyaw_level.value;
811}
812
813
814/*
815==============
816V_CalcViewRoll
817
818Roll is induced by movement and damage
819==============
820*/
821void V_CalcViewRoll (void)
822{
823	float		side;
824
825	side = V_CalcRoll (cl.simangles, cl.simvel);
826	r_refdef.viewangles[ROLL] += side;
827
828	if (v_dmg_time > 0)
829	{
830		r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll;
831		r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch;
832		v_dmg_time -= host_frametime;
833	}
834
835}
836
837
838/*
839==================
840V_CalcIntermissionRefdef
841
842==================
843*/
844void V_CalcIntermissionRefdef (void)
845{
846	entity_t	*view;
847	float		old;
848
849// view is the weapon model
850	view = &cl.viewent;
851
852	VectorCopy (cl.simorg, r_refdef.vieworg);
853	VectorCopy (cl.simangles, r_refdef.viewangles);
854	view->model = NULL;
855
856// allways idle in intermission
857	old = v_idlescale.value;
858	v_idlescale.value = 1;
859	V_AddIdle ();
860	v_idlescale.value = old;
861}
862
863/*
864==================
865V_CalcRefdef
866
867==================
868*/
869void V_CalcRefdef (void)
870{
871	entity_t	*view;
872	int			i;
873	vec3_t		forward, right, up;
874	float		bob;
875	static float oldz = 0;
876
877	V_DriftPitch ();
878
879// view is the weapon model (only visible from inside body)
880	view = &cl.viewent;
881
882	bob = V_CalcBob ();
883
884// refresh position from simulated origin
885	VectorCopy (cl.simorg, r_refdef.vieworg);
886
887	r_refdef.vieworg[2] += bob;
888
889// never let it sit exactly on a node line, because a water plane can
890// dissapear when viewed with the eye exactly on it.
891// the server protocol only specifies to 1/8 pixel, so add 1/16 in each axis
892	r_refdef.vieworg[0] += 1.0/16;
893	r_refdef.vieworg[1] += 1.0/16;
894	r_refdef.vieworg[2] += 1.0/16;
895
896	VectorCopy (cl.simangles, r_refdef.viewangles);
897	V_CalcViewRoll ();
898	V_AddIdle ();
899
900	if (view_message->flags & PF_GIB)
901		r_refdef.vieworg[2] += 8;	// gib view height
902	else if (view_message->flags & PF_DEAD)
903		r_refdef.vieworg[2] -= 16;	// corpse view height
904	else
905		r_refdef.vieworg[2] += 22;	// view height
906
907	if (view_message->flags & PF_DEAD)		// PF_GIB will also set PF_DEAD
908		r_refdef.viewangles[ROLL] = 80;	// dead view angle
909
910
911// offsets
912	AngleVectors (cl.simangles, forward, right, up);
913
914// set up gun position
915	VectorCopy (cl.simangles, view->angles);
916
917	CalcGunAngle ();
918
919	VectorCopy (cl.simorg, view->origin);
920	view->origin[2] += 22;
921
922	for (i=0 ; i<3 ; i++)
923	{
924		view->origin[i] += forward[i]*bob*0.4;
925//		view->origin[i] += right[i]*bob*0.4;
926//		view->origin[i] += up[i]*bob*0.8;
927	}
928	view->origin[2] += bob;
929
930// fudge position around to keep amount of weapon visible
931// roughly equal with different FOV
932	if (scr_viewsize.value == 110)
933		view->origin[2] += 1;
934	else if (scr_viewsize.value == 100)
935		view->origin[2] += 2;
936	else if (scr_viewsize.value == 90)
937		view->origin[2] += 1;
938	else if (scr_viewsize.value == 80)
939		view->origin[2] += 0.5;
940
941	if (view_message->flags & (PF_GIB|PF_DEAD) )
942 		view->model = NULL;
943 	else
944		view->model = cl.model_precache[cl.stats[STAT_WEAPON]];
945	view->frame = view_message->weaponframe;
946	view->colormap = vid.colormap;
947
948// set up the refresh position
949	r_refdef.viewangles[PITCH] += cl.punchangle;
950
951// smooth out stair step ups
952	if ( (view_message->onground != -1) && (cl.simorg[2] - oldz > 0) )
953	{
954		float steptime;
955
956		steptime = host_frametime;
957
958		oldz += steptime * 80;
959		if (oldz > cl.simorg[2])
960			oldz = cl.simorg[2];
961		if (cl.simorg[2] - oldz > 12)
962			oldz = cl.simorg[2] - 12;
963		r_refdef.vieworg[2] += oldz - cl.simorg[2];
964		view->origin[2] += oldz - cl.simorg[2];
965	}
966	else
967		oldz = cl.simorg[2];
968}
969
970/*
971=============
972DropPunchAngle
973=============
974*/
975void DropPunchAngle (void)
976{
977	cl.punchangle -= 10*host_frametime;
978	if (cl.punchangle < 0)
979		cl.punchangle = 0;
980}
981
982/*
983==================
984V_RenderView
985
986The player's clipping box goes from (-16 -16 -24) to (16 16 32) from
987the entity origin, so any view position inside that will be valid
988==================
989*/
990extern vrect_t scr_vrect;
991
992void V_RenderView (void)
993{
994//	if (cl.simangles[ROLL])
995//		Sys_Error ("cl.simangles[ROLL]");	// DEBUG
996cl.simangles[ROLL] = 0;	// FIXME @@@
997
998	if (cls.state != ca_active)
999		return;
1000
1001	view_frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
1002	view_message = &view_frame->playerstate[cl.playernum];
1003
1004	DropPunchAngle ();
1005	if (cl.intermission)
1006	{	// intermission / finale rendering
1007		V_CalcIntermissionRefdef ();
1008	}
1009	else
1010	{
1011		V_CalcRefdef ();
1012	}
1013
1014	R_PushDlights ();
1015	R_RenderView ();
1016
1017#ifndef GLQUAKE
1018	if (crosshair.value)
1019		Draw_Crosshair();
1020#endif
1021
1022}
1023
1024//============================================================================
1025
1026/*
1027=============
1028V_Init
1029=============
1030*/
1031void V_Init (void)
1032{
1033	Cmd_AddCommand ("v_cshift", V_cshift_f);
1034	Cmd_AddCommand ("bf", V_BonusFlash_f);
1035	Cmd_AddCommand ("centerview", V_StartPitchDrift);
1036
1037	Cvar_RegisterVariable (&v_centermove);
1038	Cvar_RegisterVariable (&v_centerspeed);
1039
1040	Cvar_RegisterVariable (&v_iyaw_cycle);
1041	Cvar_RegisterVariable (&v_iroll_cycle);
1042	Cvar_RegisterVariable (&v_ipitch_cycle);
1043	Cvar_RegisterVariable (&v_iyaw_level);
1044	Cvar_RegisterVariable (&v_iroll_level);
1045	Cvar_RegisterVariable (&v_ipitch_level);
1046
1047	Cvar_RegisterVariable (&v_contentblend);
1048
1049	Cvar_RegisterVariable (&v_idlescale);
1050	Cvar_RegisterVariable (&crosshaircolor);
1051	Cvar_RegisterVariable (&crosshair);
1052	Cvar_RegisterVariable (&cl_crossx);
1053	Cvar_RegisterVariable (&cl_crossy);
1054#ifdef GLQUAKE
1055	Cvar_RegisterVariable (&gl_cshiftpercent);
1056#endif
1057
1058	Cvar_RegisterVariable (&cl_rollspeed);
1059	Cvar_RegisterVariable (&cl_rollangle);
1060	Cvar_RegisterVariable (&cl_bob);
1061	Cvar_RegisterVariable (&cl_bobcycle);
1062	Cvar_RegisterVariable (&cl_bobup);
1063
1064	Cvar_RegisterVariable (&v_kicktime);
1065	Cvar_RegisterVariable (&v_kickroll);
1066	Cvar_RegisterVariable (&v_kickpitch);
1067
1068	BuildGammaTable (1.0);	// no gamma yet
1069	Cvar_RegisterVariable (&v_gamma);
1070}
1071
1072
1073