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