From 8680391e0b578800bedeb3d64eb7291dff93d21e Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <c.oelckers@zdoom.fake>
Date: Fri, 1 Apr 2016 12:22:16 +0200
Subject: [PATCH] - floatified the global view variables.

The software renderer still uses internal copies of the fixed point version.
---
 src/am_map.cpp           |   6 +-
 src/m_fixed.h            |   5 +
 src/p_effect.cpp         |   2 +-
 src/p_maputl.h           |   8 +-
 src/posix/sdl/i_main.cpp |   8 +-
 src/r_bsp.cpp            |   1 +
 src/r_defs.h             |  13 +++
 src/r_local.h            |   4 +
 src/r_main.cpp           |  13 +++
 src/r_plane.cpp          |   8 +-
 src/r_things.cpp         |   4 +-
 src/r_utility.cpp        | 221 ++++++++++++++-------------------------
 src/r_utility.h          |   9 +-
 src/win32/i_main.cpp     |   8 +-
 14 files changed, 142 insertions(+), 168 deletions(-)

diff --git a/src/am_map.cpp b/src/am_map.cpp
index e71177860..172f31bde 100644
--- a/src/am_map.cpp
+++ b/src/am_map.cpp
@@ -1953,13 +1953,13 @@ void AM_drawSubsectors()
 			double secx;
 			double secy;
 			double seczb, seczt;
-			double cmpz = FIXED2DBL(viewz);
+			double cmpz = ViewPos.Z;
 
 			if (players[consoleplayer].camera && sec == players[consoleplayer].camera->Sector)
 			{
 				// For the actual camera sector use the current viewpoint as reference.
-				secx = FIXED2DBL(viewx);
-				secy = FIXED2DBL(viewy);
+				secx = ViewPos.X;
+				secy = ViewPos.Y;
 			}
 			else
 			{
diff --git a/src/m_fixed.h b/src/m_fixed.h
index 1a2e8b780..506b9702c 100644
--- a/src/m_fixed.h
+++ b/src/m_fixed.h
@@ -156,6 +156,11 @@ inline double AngleToFloat(unsigned f)
 	return f * (90. / 0x40000000);
 }
 
+inline double AngleToFloat(int f)
+{
+	return f * (90. / 0x40000000);
+}
+
 #define FLOAT2FIXED(f)		FloatToFixed(f)
 #define FIXED2FLOAT(f)		float(FixedToFloat(f))
 #define FIXED2DBL(f)		FixedToFloat(f)
diff --git a/src/p_effect.cpp b/src/p_effect.cpp
index e84cf8e3c..f71a822f3 100644
--- a/src/p_effect.cpp
+++ b/src/p_effect.cpp
@@ -692,7 +692,7 @@ void P_DrawRailTrail(AActor *source, const DVector3 &start, const DVector3 &end,
 				point = start + r * dir;
 				dir.Z = dirz;
 
-				S_Sound (DVector3(point.X, point.Y, viewz),	CHAN_WEAPON, sound, 1, ATTN_NORM);
+				S_Sound (DVector3(point.X, point.Y, ViewPos.Z),	CHAN_WEAPON, sound, 1, ATTN_NORM);
 			}
 		}
 	}
diff --git a/src/p_maputl.h b/src/p_maputl.h
index d53ab49a7..e075f5f6d 100644
--- a/src/p_maputl.h
+++ b/src/p_maputl.h
@@ -38,12 +38,12 @@ struct intercept_t
 
 inline int P_PointOnLineSidePrecise(double x, double y, const line_t *line)
 {
-	return (y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - x) * line->Delta().Y > -EQUAL_EPSILON;
+	return (y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - x) * line->Delta().Y > EQUAL_EPSILON;
 }
 
 inline int P_PointOnLineSidePrecise(const DVector2 &pt, const line_t *line)
 {
-	return (pt.Y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - pt.X) * line->Delta().Y > -EQUAL_EPSILON;
+	return (pt.Y - line->v1->fY()) * line->Delta().X + (line->v1->fX() - pt.X) * line->Delta().Y > EQUAL_EPSILON;
 }
 
 inline int P_PointOnLineSide (double x, double y, const line_t *line)
@@ -73,12 +73,12 @@ inline int P_PointOnLineSide(const DVector2 & p, const line_t *line)
 
 inline int P_PointOnDivlineSide(double x, double y, const divline_t *line)
 {
-	return (y - line->y) * line->dx + (line->x - x) * line->dy > -EQUAL_EPSILON;
+	return (y - line->y) * line->dx + (line->x - x) * line->dy > EQUAL_EPSILON;
 }
 
 inline int P_PointOnDivlineSide(const DVector2 &pos, const divline_t *line)
 {
-	return (pos.Y - line->y) * line->dx + (line->x - pos.X) * line->dy > -EQUAL_EPSILON;
+	return (pos.Y - line->y) * line->dx + (line->x - pos.X) * line->dy > EQUAL_EPSILON;
 }
 
 //==========================================================================
diff --git a/src/posix/sdl/i_main.cpp b/src/posix/sdl/i_main.cpp
index 7c08dacdb..54d2c2705 100644
--- a/src/posix/sdl/i_main.cpp
+++ b/src/posix/sdl/i_main.cpp
@@ -180,10 +180,10 @@ static int DoomSpecificInfo (char *buffer, char *end)
 		}
 		else
 		{
-			p += snprintf (buffer+p, size-p, "\n\nviewx = %d", (int)viewx);
-			p += snprintf (buffer+p, size-p, "\nviewy = %d", (int)viewy);
-			p += snprintf (buffer+p, size-p, "\nviewz = %d", (int)viewz);
-			p += snprintf (buffer+p, size-p, "\nviewangle = %x", (unsigned int)viewangle);
+			p += snprintf (buffer+p, size-p, "\n\nviewx = %f", ViewPos.X);
+			p += snprintf (buffer+p, size-p, "\nviewy = %f", ViewPos.Y);
+			p += snprintf (buffer+p, size-p, "\nviewz = %f", ViewPos.Z);
+			p += snprintf (buffer+p, size-p, "\nviewangle = %f", ViewAngle.Degrees);
 		}
 	}
 	buffer[p++] = '\n';
diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp
index e50642347..d0ca0a2f4 100644
--- a/src/r_bsp.cpp
+++ b/src/r_bsp.cpp
@@ -38,6 +38,7 @@
 #include "p_lnspec.h"
 #include "p_setup.h"
 
+#include "r_local.h"
 #include "r_main.h"
 #include "r_plane.h"
 #include "r_draw.h"
diff --git a/src/r_defs.h b/src/r_defs.h
index 4aa0f9d53..f6ff54387 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -352,6 +352,12 @@ public:
 		return TMulScale16(a,x, b,y, c,z) + d;
 	}
 
+	int PointOnSide(const DVector3 &pos) const
+	{
+		double v = a * pos.X + b * pos.Y + c * pos.Z;
+		return v < -EQUAL_EPSILON ? -1 : v > EQUAL_EPSILON ? 1 : 0;
+	}
+
 	// Returns the value of z at (0,0) This is used by the 3D floor code which does not handle slopes
 	fixed_t Zat0 () const
 	{
@@ -375,6 +381,13 @@ public:
 		return (d + a*pos.X + b*pos.Y) * ic / (-65536.0 * 65536.0);
 	}
 
+	// This is for the software renderer
+	fixed_t ZatPointFixed(const DVector2 &pos) const
+	{
+		return xs_CRoundToInt((d + a*pos.X + b*pos.Y) * ic / (-65536.0));
+	}
+
+
 	double ZatPoint(const vertex_t *v) const
 	{
 		return FIXED2DBL(FixedMul(ic, -d - DMulScale16(a, v->fixX(), b, v->fixY())));
diff --git a/src/r_local.h b/src/r_local.h
index 7977e6923..17f413572 100644
--- a/src/r_local.h
+++ b/src/r_local.h
@@ -38,4 +38,8 @@
 #include "r_things.h"
 #include "r_draw.h"
 
+extern  fixed_t viewx, viewy, viewz;
+extern angle_t viewangle;
+extern int viewpitch;
+
 #endif // __R_LOCAL_H__
diff --git a/src/r_main.cpp b/src/r_main.cpp
index 80e2eb859..599b682d5 100644
--- a/src/r_main.cpp
+++ b/src/r_main.cpp
@@ -68,6 +68,10 @@
 #define TEST_ANGLE 2468347904 
 #endif
 
+fixed_t viewx, viewy, viewz;
+angle_t viewangle;
+int viewpitch;
+
 // TYPES -------------------------------------------------------------------
 
 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
@@ -550,6 +554,12 @@ void R_SetupColormap(player_t *player)
 
 void R_SetupFreelook()
 {
+	viewx = FLOAT2FIXED(ViewPos.X);
+	viewy = FLOAT2FIXED(ViewPos.Y);
+	viewz = FLOAT2FIXED(ViewPos.Z);
+	viewangle = ViewAngle.BAMs();
+	viewpitch = ViewPitch.BAMs();
+
 	{
 		fixed_t dy;
 		
@@ -727,6 +737,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth)
 			viewx = FLOAT2FIXED((x1 + r * dx)*2 - x);
 			viewy = FLOAT2FIXED((y1 + r * dy)*2 - y);
 		}
+		ViewAngle = pds->src->Delta().Angle();
 		viewangle = 2*R_PointToAngle2 (pds->src->v1->fixX(), pds->src->v1->fixY(),
 									   pds->src->v2->fixX(), pds->src->v2->fixY()) - startang;
 
@@ -743,6 +754,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth)
 		viewz = FLOAT2FIXED(view.Z);
 		viewangle = va.BAMs();
 	}
+	ViewAngle = AngleToFloat(viewangle);
 
 	viewsin = finesine[viewangle>>ANGLETOFINESHIFT];
 	viewcos = finecosine[viewangle>>ANGLETOFINESHIFT];
@@ -821,6 +833,7 @@ void R_EnterPortal (PortalDrawseg* pds, int depth)
 	viewx = startx;
 	viewy = starty;
 	viewz = startz;
+	ViewAngle = AngleToFloat(viewangle);
 }
 
 //==========================================================================
diff --git a/src/r_plane.cpp b/src/r_plane.cpp
index fe387509f..61b969a4e 100644
--- a/src/r_plane.cpp
+++ b/src/r_plane.cpp
@@ -1082,6 +1082,7 @@ void R_DrawHeightPlanes(fixed_t height)
 				viewy = pl->viewy;
 				viewz = pl->viewz;
 				viewangle = pl->viewangle;
+				ViewAngle = AngleToFloat(viewangle);
 				MirrorFlags = pl->MirrorFlags;
 				R_DrawSinglePlane (pl, pl->sky & 0x7FFFFFFF, pl->Additive, true);
 			}
@@ -1092,6 +1093,7 @@ void R_DrawHeightPlanes(fixed_t height)
 	viewy = oViewY;
 	viewz = oViewZ;
 	viewangle = oViewAngle;
+	ViewAngle = AngleToFloat(viewangle);
 }
 
 
@@ -1248,11 +1250,12 @@ void R_DrawSkyBoxes ()
 		{
 			extralight = pl->extralight;
 			R_SetVisibility (pl->visibility);
-			viewx = pl->viewx - FLOAT2FIXED(sky->Mate->X() + sky->X());
-			viewy = pl->viewy - FLOAT2FIXED(sky->Mate->Y() + sky->Y());
+			viewx = pl->viewx + FLOAT2FIXED(-sky->Mate->X() + sky->X());
+			viewy = pl->viewy + FLOAT2FIXED(-sky->Mate->Y() + sky->Y());
 			viewz = pl->viewz;
 			viewangle = pl->viewangle;
 		}
+		ViewAngle = AngleToFloat(viewangle);
 
 		sky->bInSkybox = true;
 		if (mate != NULL) mate->bInSkybox = true;
@@ -1367,6 +1370,7 @@ void R_DrawSkyBoxes ()
 	R_SetVisibility (savedvisibility);
 	extralight = savedextralight;
 	viewangle = savedangle;
+	ViewAngle = AngleToFloat(viewangle);
 	R_SetViewAngle ();
 
 	CurrentPortalInSkybox = false;
diff --git a/src/r_things.cpp b/src/r_things.cpp
index 0e4e717f6..e6b4a68c6 100644
--- a/src/r_things.cpp
+++ b/src/r_things.cpp
@@ -802,7 +802,7 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor
 		{
 			// choose a different rotation based on player view
 			spriteframe_t *sprframe = &SpriteFrames[tex->Rotations];
-			angle_t ang = R_PointToAngle (fx, fy);
+			angle_t ang = R_PointToAngle2 (viewx, viewy, fx, fy);
 			angle_t rot;
 			if (sprframe->Texture[0] == sprframe->Texture[1])
 			{
@@ -841,7 +841,7 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor
 			//picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0];
 			// choose a different rotation based on player view
 			spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame];
-			angle_t ang = R_PointToAngle (fx, fy);
+			angle_t ang = R_PointToAngle2 (viewx, viewy, fx, fy);
 			angle_t rot;
 			if (sprframe->Texture[0] == sprframe->Texture[1])
 			{
diff --git a/src/r_utility.cpp b/src/r_utility.cpp
index 148ee9d52..eaf1ef2a7 100644
--- a/src/r_utility.cpp
+++ b/src/r_utility.cpp
@@ -70,12 +70,15 @@ EXTERN_CVAR (Bool, cl_capfps)
 
 struct InterpolationViewer
 {
+	struct instance
+	{
+		DVector3 Pos;
+		DRotator Angles;
+	};
+
 	AActor *ViewActor;
 	int otic;
-	fixed_t oviewx, oviewy, oviewz;
-	fixed_t nviewx, nviewy, nviewz;
-	int oviewpitch, nviewpitch;
-	angle_t oviewangle, nviewangle;
+	instance Old, New;
 };
 
 // PRIVATE DATA DECLARATIONS -----------------------------------------------
@@ -102,11 +105,9 @@ DCanvas			*RenderTarget;		// [RH] canvas to render to
 int 			viewwindowx;
 int 			viewwindowy;
 
-fixed_t 		viewx;
-fixed_t 		viewy;
-fixed_t 		viewz;
-int				viewpitch;
-angle_t 		viewangle;
+DVector3		ViewPos;
+DAngle			ViewAngle;
+DAngle			ViewPitch;
 
 extern "C" 
 {
@@ -574,27 +575,20 @@ EXTERN_CVAR (Bool, cl_noprediction)
 
 void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *iview)
 {
-	fixed_t frac = FLOAT2FIXED(Frac);
 	if (NoInterpolateView)
 	{
 		InterpolationPath.Clear();
 		NoInterpolateView = false;
-		iview->oviewx = iview->nviewx;
-		iview->oviewy = iview->nviewy;
-		iview->oviewz = iview->nviewz;
-		iview->oviewpitch = iview->nviewpitch;
-		iview->oviewangle = iview->nviewangle;
+		iview->Old = iview->New;
 	}
-	int oldgroup = R_PointInSubsector(iview->oviewx, iview->oviewy)->sector->PortalGroup;
-	int newgroup = R_PointInSubsector(iview->nviewx, iview->nviewy)->sector->PortalGroup;
+	int oldgroup = R_PointInSubsector(iview->Old.Pos)->sector->PortalGroup;
+	int newgroup = R_PointInSubsector(iview->New.Pos)->sector->PortalGroup;
 
-	fixed_t oviewangle = iview->oviewangle;
-	fixed_t nviewangle = iview->nviewangle;
-	if ((iview->oviewx != iview->nviewx || iview->oviewy != iview->nviewy) && InterpolationPath.Size() > 0)
+	DAngle oviewangle = iview->Old.Angles.Yaw;
+	DAngle nviewangle = iview->New.Angles.Yaw;
+	if ((iview->Old.Pos.X != iview->New.Pos.X || iview->Old.Pos.Y != iview->New.Pos.Y) && InterpolationPath.Size() > 0)
 	{
-		viewx = iview->nviewx;
-		viewy = iview->nviewy;
-		viewz = iview->nviewz;
+		DVector3 view = iview->New.Pos;
 
 		// Interpolating through line portals is a messy affair.
 		// What needs be done is to store the portal transitions of the camera actor as waypoints
@@ -602,49 +596,47 @@ void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *ivie
 		// Needless to say, this doesn't work for chasecam mode.
 		if (!r_showviewer)
 		{
-			fixed_t pathlen = 0;
-			fixed_t zdiff = 0;
-			fixed_t totalzdiff = 0;
-			angle_t adiff = 0;
-			angle_t totaladiff = 0;
-			fixed_t oviewz = iview->oviewz;
-			fixed_t nviewz = iview->nviewz;
-			DVector3a oldpos = { {FIXED2DBL(iview->oviewx), FIXED2DBL(iview->oviewy), 0.}, 0. };
-			DVector3a newpos = { {FIXED2DBL(iview->nviewx), FIXED2DBL(iview->nviewy), 0. }, 0. };
+			double pathlen = 0;
+			double zdiff = 0;
+			double totalzdiff = 0;
+			DAngle adiff = 0.;
+			DAngle totaladiff = 0.;
+			double oviewz = iview->Old.Pos.Z;
+			double nviewz = iview->New.Pos.Z;
+			DVector3a oldpos = { { iview->Old.Pos.X, iview->Old.Pos.Y, 0 }, 0. };
+			DVector3a newpos = { { iview->New.Pos.X, iview->New.Pos.Y, 0 }, 0. };
 			InterpolationPath.Push(newpos);	// add this to  the array to simplify the loops below
 
 			for (unsigned i = 0; i < InterpolationPath.Size(); i += 2)
 			{
 				DVector3a &start = i == 0 ? oldpos : InterpolationPath[i - 1];
 				DVector3a &end = InterpolationPath[i];
-				pathlen += FLOAT2FIXED((end.pos-start.pos).Length());
-				totalzdiff += FLOAT2FIXED(start.pos.Z);
-				totaladiff += start.angle.BAMs();
+				pathlen += (end.pos-start.pos).Length();
+				totalzdiff += start.pos.Z;
+				totaladiff += start.angle;
 			}
-			fixed_t interpolatedlen = FixedMul(frac, pathlen);
+			double interpolatedlen = Frac * pathlen;
 
 			for (unsigned i = 0; i < InterpolationPath.Size(); i += 2)
 			{
 				DVector3a &start = i == 0 ? oldpos : InterpolationPath[i - 1];
 				DVector3a &end = InterpolationPath[i];
-				fixed_t fraglen = FLOAT2FIXED((end.pos - start.pos).Length());
-				zdiff += FLOAT2FIXED(start.pos.Z);
-				adiff += start.angle.BAMs();
+				double fraglen = (end.pos - start.pos).Length();
+				zdiff += start.pos.Z;
+				adiff += start.angle;
 				if (fraglen <= interpolatedlen)
 				{
 					interpolatedlen -= fraglen;
 				}
 				else
 				{
-					double fragfrac = FIXED2DBL(FixedDiv(interpolatedlen, fraglen));
+					double fragfrac = interpolatedlen / fraglen;
 					oviewz += zdiff;
 					nviewz -= totalzdiff - zdiff;
 					oviewangle += adiff;
 					nviewangle -= totaladiff - adiff;
 					DVector2 viewpos = start.pos + (fragfrac * (end.pos - start.pos));
-					viewx = FLOAT2FIXED(viewpos.X);
-					viewy = FLOAT2FIXED(viewpos.Y);
-					viewz = oviewz + FixedMul(frac, nviewz - oviewz);
+					ViewPos = { viewpos, oviewz + Frac * (nviewz - oviewz) };
 					break;
 				}
 			}
@@ -654,17 +646,15 @@ void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *ivie
 	else
 	{
 		DVector2 disp = Displacements.getOffset(oldgroup, newgroup);
-		viewx = iview->oviewx + FixedMul(frac, iview->nviewx - iview->oviewx - FLOAT2FIXED(disp.X));
-		viewy = iview->oviewy + FixedMul(frac, iview->nviewy - iview->oviewy - FLOAT2FIXED(disp.Y));
-		viewz = iview->oviewz + FixedMul(frac, iview->nviewz - iview->oviewz);
+		ViewPos = iview->Old.Pos + (iview->New.Pos - iview->Old.Pos - disp) * Frac;
 	}
 	if (player != NULL &&
 		!(player->cheats & CF_INTERPVIEW) &&
 		player - players == consoleplayer &&
 		camera == player->mo &&
 		!demoplayback &&
-		iview->nviewx == camera->_f_X() &&
-		iview->nviewy == camera->_f_Y() && 
+		iview->New.Pos.X == camera->X() &&
+		iview->New.Pos.Y == camera->Y() && 
 		!(player->cheats & (CF_TOTALLYFROZEN|CF_FROZEN)) &&
 		player->playerstate == PST_LIVE &&
 		player->mo->reactiontime == 0 &&
@@ -673,53 +663,26 @@ void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *ivie
 		(!netgame || !cl_noprediction) &&
 		!LocalKeyboardTurner)
 	{
-		viewangle = nviewangle + (LocalViewAngle & 0xFFFF0000);
-
-		fixed_t delta = player->centering ? 0 : -(signed)(LocalViewPitch & 0xFFFF0000);
-
-		viewpitch = iview->nviewpitch;
-		if (delta > 0)
-		{
-			// Avoid overflowing viewpitch (can happen when a netgame is stalled)
-			if (viewpitch > INT_MAX - delta)
-			{
-				viewpitch = player->MaxPitch.BAMs();
-			}
-			else
-			{
-				viewpitch = MIN<int>(viewpitch + delta, player->MaxPitch.BAMs());
-			}
-		}
-		else if (delta < 0)
-		{
-			// Avoid overflowing viewpitch (can happen when a netgame is stalled)
-			if (viewpitch < INT_MIN - delta)
-			{
-				viewpitch = player->MinPitch.BAMs();
-			}
-			else
-			{
-				viewpitch = MAX<int>(viewpitch + delta, player->MinPitch.BAMs());
-			}
-		}
+		ViewAngle = (nviewangle + AngleToFloat(LocalViewAngle & 0xFFFF0000)).Normalized180();
+		DAngle delta = player->centering ? DAngle(0.) : AngleToFloat(int(LocalViewPitch & 0xFFFF0000));
+		ViewPitch = clamp<DAngle>((iview->New.Angles.Pitch - delta).Normalized180(), player->MinPitch, player->MaxPitch);
 	}
 	else
 	{
-		viewpitch = iview->oviewpitch + FixedMul (frac, iview->nviewpitch - iview->oviewpitch);
-		viewangle = oviewangle + FixedMul (frac, nviewangle - oviewangle);
+		ViewPitch = (iview->Old.Angles.Pitch + (iview->New.Angles.Pitch - iview->Old.Angles.Pitch) * Frac).Normalized180();
+		ViewAngle = (oviewangle + (nviewangle - oviewangle) * Frac).Normalized180();
 	}
 	
 	// Due to interpolation this is not necessarily the same as the sector the camera is in.
-	viewsector = R_PointInSubsector(viewx, viewy)->sector;
+	viewsector = R_PointInSubsector(ViewPos)->sector;
 	bool moved = false;
 	while (!viewsector->PortalBlocksMovement(sector_t::ceiling))
 	{
 		AActor *point = viewsector->SkyBoxes[sector_t::ceiling];
-		if (viewz > FLOAT2FIXED(point->specialf1))
+		if (ViewPos.Z > point->specialf1)
 		{
-			viewx += FLOAT2FIXED(point->Scale.X);
-			viewy += FLOAT2FIXED(point->Scale.Y);
-			viewsector = R_PointInSubsector(viewx, viewy)->sector;
+			ViewPos += point->Scale;
+			viewsector = R_PointInSubsector(ViewPos)->sector;
 			moved = true;
 		}
 		else break;
@@ -729,11 +692,10 @@ void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *ivie
 		while (!viewsector->PortalBlocksMovement(sector_t::floor))
 		{
 			AActor *point = viewsector->SkyBoxes[sector_t::floor];
-			if (viewz < FLOAT2FIXED(point->specialf1))
+			if (ViewPos.Z < point->specialf1)
 			{
-				viewx += FLOAT2FIXED(point->Scale.X);
-				viewy += FLOAT2FIXED(point->Scale.Y);
-				viewsector = R_PointInSubsector(viewx, viewy)->sector;
+				ViewPos += point->Scale;
+				viewsector = R_PointInSubsector(ViewPos)->sector;
 				moved = true;
 			}
 			else break;
@@ -761,10 +723,8 @@ void R_ResetViewInterpolation ()
 
 void R_SetViewAngle ()
 {
-	angle_t ang = viewangle >> ANGLETOFINESHIFT;
-
-	viewsin = finesine[ang];
-	viewcos = finecosine[ang];
+	viewsin = FLOAT2FIXED(ViewAngle.Sin());
+	viewcos = FLOAT2FIXED(ViewAngle.Cos());
 
 	viewtansin = FixedMul (FocalTangent, viewsin);
 	viewtancos = FixedMul (FocalTangent, viewcos);
@@ -787,7 +747,8 @@ static InterpolationViewer *FindPastViewer (AActor *actor)
 	}
 
 	// Not found, so make a new one
-	InterpolationViewer iview = { NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+	InterpolationViewer iview;
+	memset(&iview, 0, sizeof(iview));
 	iview.ViewActor = actor;
 	iview.otic = -1;
 	InterpolationPath.Clear();
@@ -851,11 +812,7 @@ void R_RebuildViewInterpolation(player_t *player)
 
 	InterpolationViewer *iview = FindPastViewer(player->camera);
 
-	iview->oviewx = iview->nviewx;
-	iview->oviewy = iview->nviewy;
-	iview->oviewz = iview->nviewz;
-	iview->oviewpitch = iview->nviewpitch;
-	iview->oviewangle = iview->nviewangle;
+	iview->Old = iview->New;
 	InterpolationPath.Clear();
 }
 
@@ -955,52 +912,39 @@ void R_SetupFrame (AActor *actor)
 	if (iview->otic != -1 && nowtic > iview->otic)
 	{
 		iview->otic = nowtic;
-		iview->oviewx = iview->nviewx;
-		iview->oviewy = iview->nviewy;
-		iview->oviewz = iview->nviewz;
-		iview->oviewpitch = iview->nviewpitch;
-		iview->oviewangle = iview->nviewangle;
+		iview->Old = iview->New;
 	}
 
 	if (player != NULL && gamestate != GS_TITLELEVEL &&
 		((player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)))
 	{
-		sector_t *oldsector = R_PointInSubsector(iview->oviewx, iview->oviewy)->sector;
+		sector_t *oldsector = R_PointInSubsector(iview->Old.Pos)->sector;
 		// [RH] Use chasecam view
 		DVector3 campos;
-		P_AimCamera (camera, campos, viewsector, unlinked);
-		iview->nviewx = FLOAT2FIXED(campos.X);
-		iview->nviewy = FLOAT2FIXED(campos.Y);
-		iview->nviewz = FLOAT2FIXED(campos.Z);
+		P_AimCamera (camera, campos, viewsector, unlinked);	// fixme: This needs to translate the angle, too.
+		iview->New.Pos = campos;
 		r_showviewer = true;
 		// Interpolating this is a very complicated thing because nothing keeps track of the aim camera's movement, so whenever we detect a portal transition
 		// it's probably best to just reset the interpolation for this move.
 		// Note that this can still cause problems with unusually linked portals
-		if (viewsector->PortalGroup != oldsector->PortalGroup || (unlinked && P_AproxDistance(iview->oviewx - iview->nviewx, iview->oviewy - iview->nviewy) > 256 * FRACUNIT))
+		if (viewsector->PortalGroup != oldsector->PortalGroup || (unlinked && ((iview->New.Pos.XY() - iview->Old.Pos.XY()).LengthSquared()) > 256*256))
 		{
 			iview->otic = nowtic;
-			iview->oviewx = iview->nviewx;
-			iview->oviewy = iview->nviewy;
-			iview->oviewz = iview->nviewz;
-			iview->oviewpitch = iview->nviewpitch;
-			iview->oviewangle = iview->nviewangle;
+			iview->Old = iview->New;
 		}
 	}
 	else
 	{
-		iview->nviewx = camera->_f_X();
-		iview->nviewy = camera->_f_Y();
-		iview->nviewz = FLOAT2FIXED(camera->player ? camera->player->viewz : camera->Z() + camera->GetCameraHeight());
+		iview->New.Pos = { camera->Pos().XY(), camera->player ? camera->player->viewz : camera->Z() + camera->GetCameraHeight() };
 		viewsector = camera->Sector;
 		r_showviewer = false;
 	}
-	iview->nviewpitch = camera->Angles.Pitch.BAMs();
+	iview->New.Angles = camera->Angles;
 	if (camera->player != 0)
 	{
 		player = camera->player;
 	}
 
-	iview->nviewangle = camera->Angles.Yaw.BAMs();
 	if (iview->otic == -1 || r_NoInterpolate)
 	{
 		R_ResetViewInterpolation ();
@@ -1014,13 +958,6 @@ void R_SetupFrame (AActor *actor)
 	}
 	R_InterpolateView (player, r_TicFracF, iview);
 
-#ifdef TEST_X
-	viewx = TEST_X;
-	viewy = TEST_Y;
-	viewz = TEST_Z;
-	viewangle = TEST_ANGLE;
-#endif
-
 	R_SetViewAngle ();
 
 	interpolator.DoInterpolations (r_TicFracF);
@@ -1028,19 +965,19 @@ void R_SetupFrame (AActor *actor)
 	// Keep the view within the sector's floor and ceiling
 	if (viewsector->PortalBlocksMovement(sector_t::ceiling))
 	{
-		fixed_t theZ = viewsector->ceilingplane.ZatPoint(viewx, viewy) - 4 * FRACUNIT;
-		if (viewz > theZ)
+		double theZ = viewsector->ceilingplane.ZatPoint(ViewPos) - 4;
+		if (ViewPos.Z > theZ)
 		{
-			viewz = theZ;
+			ViewPos.Z = theZ;
 		}
 	}
 
 	if (viewsector->PortalBlocksMovement(sector_t::floor))
 	{
-		fixed_t theZ = viewsector->floorplane.ZatPoint(viewx, viewy) + 4 * FRACUNIT;
-		if (viewz < theZ)
+		double theZ = viewsector->floorplane.ZatPoint(ViewPos) + 4;
+		if (ViewPos.Z < theZ)
 		{
-			viewz = theZ;
+			ViewPos.Z = theZ;
 		}
 	}
 
@@ -1058,32 +995,30 @@ void R_SetupFrame (AActor *actor)
 			{
 				an = camera->Angles.Yaw;
 				double power = QuakePower(quakefactor, jiggers.RelIntensity.X, jiggers.RelOffset.X, jiggers.Falloff, jiggers.WFalloff);
-				viewx += FLOAT2FIXED(an.Cos() * power);
-				viewy += FLOAT2FIXED(an.Sin() * power);
+				ViewPos += an.ToVector(power);
 			}
 			if (jiggers.RelIntensity.Y != 0 || jiggers.RelOffset.Y != 0)
 			{
 				an = camera->Angles.Yaw + 90;
 				double power = QuakePower(quakefactor, jiggers.RelIntensity.Y, jiggers.RelOffset.Y, jiggers.Falloff, jiggers.WFalloff);
-				viewx += FLOAT2FIXED(an.Cos() * power);
-				viewy += FLOAT2FIXED(an.Sin() * power);
+				ViewPos += an.ToVector(power);
 			}
 			// FIXME: Relative Z is not relative
 			if (jiggers.RelIntensity.Z != 0 || jiggers.RelOffset.Z != 0)
 			{
-				viewz += FLOAT2FIXED(QuakePower(quakefactor, jiggers.RelIntensity.Z, jiggers.RelOffset.Z, jiggers.Falloff, jiggers.WFalloff));
+				ViewPos.Z += QuakePower(quakefactor, jiggers.RelIntensity.Z, jiggers.RelOffset.Z, jiggers.Falloff, jiggers.WFalloff);
 			}
 			if (jiggers.Intensity.X != 0 || jiggers.Offset.X != 0)
 			{
-				viewx += FLOAT2FIXED(QuakePower(quakefactor, jiggers.Intensity.X, jiggers.Offset.X, jiggers.Falloff, jiggers.WFalloff));
+				ViewPos.Z += QuakePower(quakefactor, jiggers.Intensity.X, jiggers.Offset.X, jiggers.Falloff, jiggers.WFalloff);
 			}
 			if (jiggers.Intensity.Y != 0 || jiggers.Offset.Y != 0)
 			{
-				viewy += FLOAT2FIXED(QuakePower(quakefactor, jiggers.Intensity.Y, jiggers.Offset.Y, jiggers.Falloff, jiggers.WFalloff));
+				ViewPos.Z += QuakePower(quakefactor, jiggers.Intensity.Y, jiggers.Offset.Y, jiggers.Falloff, jiggers.WFalloff);
 			}
 			if (jiggers.Intensity.Z != 0 || jiggers.Offset.Z != 0)
 			{
-				viewz += FLOAT2FIXED(QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z, jiggers.Falloff, jiggers.WFalloff));
+				ViewPos.Z += QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z, jiggers.Falloff, jiggers.WFalloff);
 			}
 		}
 	}
@@ -1102,7 +1037,7 @@ void R_SetupFrame (AActor *actor)
 			secplane_t *plane;
 			int viewside;
 			plane = (i < lightlist.Size()-1) ? &lightlist[i+1].plane : &viewsector->floorplane;
-			viewside = plane->PointOnSide(viewx, viewy, viewz);
+			viewside = plane->PointOnSide(ViewPos);
 			// Reverse the direction of the test if the plane was downward facing.
 			// We want to know if the view is above it, whatever its orientation may be.
 			if (plane->fC() < 0)
@@ -1124,9 +1059,9 @@ void R_SetupFrame (AActor *actor)
 		const sector_t *s = viewsector->GetHeightSec();
 		if (s != NULL)
 		{
-			newblend = s->floorplane.PointOnSide(viewx, viewy, viewz) < 0
+			newblend = s->floorplane.PointOnSide(ViewPos) < 0
 				? s->bottommap
-				: s->ceilingplane.PointOnSide(viewx, viewy, viewz) < 0
+				: s->ceilingplane.PointOnSide(ViewPos) < 0
 				? s->topmap
 				: s->midmap;
 			if (APART(newblend) == 0 && newblend >= numfakecmaps)
diff --git a/src/r_utility.h b/src/r_utility.h
index f37fc01ee..0a63b3f66 100644
--- a/src/r_utility.h
+++ b/src/r_utility.h
@@ -2,6 +2,7 @@
 #define __R_UTIL_H
 
 #include "r_state.h"
+#include "vectors.h"
 //
 // Stuff from r_main.h that's needed outside the rendering code.
 
@@ -11,10 +12,9 @@
 
 extern DCanvas			*RenderTarget;
 
-extern fixed_t			viewx;
-extern fixed_t			viewy;
-extern fixed_t			viewz;
-extern int				viewpitch;
+extern DVector3			ViewPos;
+extern DAngle			ViewAngle;
+extern DAngle			ViewPitch;
 
 extern "C" int			centerx, centerxwide;
 extern "C" int			centery;
@@ -65,7 +65,6 @@ inline int R_PointOnSide(const DVector2 &pos, const node_t *node)
 }
 
 angle_t R_PointToAngle2 (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2);
-inline angle_t R_PointToAngle (fixed_t x, fixed_t y) { return R_PointToAngle2 (viewx, viewy, x, y); }
 inline angle_t R_PointToAnglePrecise (fixed_t viewx, fixed_t viewy, fixed_t x, fixed_t y)
 {
 	return xs_RoundToUInt(g_atan2(double(y-viewy), double(x-viewx)) * (ANGLE_180/M_PI));
diff --git a/src/win32/i_main.cpp b/src/win32/i_main.cpp
index 4e6868a7b..241e9dae2 100644
--- a/src/win32/i_main.cpp
+++ b/src/win32/i_main.cpp
@@ -1080,10 +1080,10 @@ void DoomSpecificInfo (char *buffer, size_t bufflen)
 		}
 		else
 		{
-			buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nviewx = %d", viewx);
-			buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewy = %d", viewy);
-			buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewz = %d", viewz);
-			buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewangle = %x", viewangle);
+			buffer += mysnprintf (buffer, buffend - buffer, "\r\n\r\nviewx = %f", ViewPos.X);
+			buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewy = %f", ViewPos.Y);
+			buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewz = %f", ViewPos.Z);
+			buffer += mysnprintf (buffer, buffend - buffer, "\r\nviewangle = %f", ViewAngle);
 		}
 	}
 	*buffer++ = '\r';