From 90896acd439d8f67ce784e925a7bcb6829f4d72d Mon Sep 17 00:00:00 2001
From: volte <100103+volte@users.noreply.github.com>
Date: Mon, 3 Jan 2022 09:46:00 -0500
Subject: [PATCH] Additional camera movement actions in visual mode (#682)

Added "Orbit" and "Move Camera to Cursor" visual mode actions (not bound by default)
---
 Source/Core/Resources/Actions.cfg             |  21 +++
 Source/Core/VisualModes/VisualCamera.cs       |   2 +-
 Source/Core/VisualModes/VisualMode.cs         | 155 ++++++++++++++++--
 .../VisualModes/BaseVisualMode.cs             |   1 -
 4 files changed, 160 insertions(+), 19 deletions(-)

diff --git a/Source/Core/Resources/Actions.cfg b/Source/Core/Resources/Actions.cfg
index d6c8f596d..e739692bb 100755
--- a/Source/Core/Resources/Actions.cfg
+++ b/Source/Core/Resources/Actions.cfg
@@ -644,6 +644,27 @@ movedown
 	allowscroll = true;
 	disregardshift = true;
 }
+									  
+orbit
+{
+	title = "Orbit";
+	category = "visual";
+	description = "When held, the mouse will orbit the camera around the point at the cursor, rather than look around.";
+	allowkeys = true;
+	allowmouse = true;
+	allowscroll = false;
+	disregardshift = true;	
+}
+
+movecameratocursor
+{
+	title = "Move Camera to Cursor";
+	category = "visual";
+	description = "Place the camera nearby the point at the cursor.";
+	allowkeys = true;
+	allowmouse = true;
+	allowscroll = false;
+}
 
 testmap
 {
diff --git a/Source/Core/VisualModes/VisualCamera.cs b/Source/Core/VisualModes/VisualCamera.cs
index 7646d9852..6108b5485 100755
--- a/Source/Core/VisualModes/VisualCamera.cs
+++ b/Source/Core/VisualModes/VisualCamera.cs
@@ -14,7 +14,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 	{
 		#region ================== Constants
 
-		private const double ANGLE_FROM_MOUSE = 0.0001;
+		public const double ANGLE_FROM_MOUSE = 0.0001;
 		public const double MAX_ANGLEZ_LOW = 91.0 / Angle2D.PIDEG;
 		public const double MAX_ANGLEZ_HIGH = (360.0 - 91.0) / Angle2D.PIDEG;
 		public const double THING_Z_OFFSET = 41.0;
diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs
index d0834ff85..75955385f 100755
--- a/Source/Core/VisualModes/VisualMode.cs
+++ b/Source/Core/VisualModes/VisualMode.cs
@@ -45,6 +45,8 @@ namespace CodeImp.DoomBuilder.VisualModes
 		#region ================== Constants
 
 		private const double MOVE_SPEED_MULTIPLIER = 0.001;
+		protected const float PICK_RANGE = 0.98f;
+		private const float MOVE_CAMERA_DISTANCE = 64.0f;
 		
 		#endregion
 
@@ -64,6 +66,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 		private bool keyright;
 		private bool keyup;
 		private bool keydown;
+		private bool orbit;
 
 		//mxd
 		private List<VisualThing> selectedVisualThings;
@@ -75,6 +78,12 @@ namespace CodeImp.DoomBuilder.VisualModes
 		private Vector3D playerStartPosition;
 		private double playerStartAngle;
 
+		private Vector3D orbitPoint;
+		private double orbitXY;
+		private double orbitZ;
+		private double orbitRadius;
+		private bool orbitPointPicked;
+
 		// For picking
 		protected PickingMode pickingmode;
 
@@ -90,7 +99,7 @@ namespace CodeImp.DoomBuilder.VisualModes
 		protected List<VisualSector> visiblesectors;
 		protected List<VisualGeometry> visiblegeometry;
 		protected HashSet<VisualSlope> usedslopehandles;
-		
+
 		#endregion
 
 		#region ================== Properties
@@ -395,7 +404,64 @@ namespace CodeImp.DoomBuilder.VisualModes
 		public override void OnMouseInput(Vector2D delta)
 		{
 			base.OnMouseInput(delta);
-			General.Map.VisualCamera.ProcessMouseInput(delta);
+
+			if (orbit)
+			{
+				Vector3D start = General.Map.VisualCamera.Position;
+				
+				if (!orbitPointPicked)
+				{
+					Vector3D hitPosition = GetHitPosition();
+					orbitPoint = hitPosition;
+					
+					if (!hitPosition.IsFinite())
+					{
+						return;
+					}
+					
+					orbitPointPicked = true;
+
+					Vector3D pickDelta = start - orbitPoint;
+					orbitRadius = pickDelta.GetLength();
+					orbitXY = pickDelta.GetNormal().GetAngleXY();
+					orbitZ = -pickDelta.GetNormal().GetAngleZ();
+				}
+				
+				// Change camera angles with the mouse changes
+				orbitXY -= delta.x * VisualCamera.ANGLE_FROM_MOUSE;
+				if(General.Settings.InvertYAxis)
+					orbitZ -= delta.y * VisualCamera.ANGLE_FROM_MOUSE;
+				else
+					orbitZ += delta.y * VisualCamera.ANGLE_FROM_MOUSE;
+				
+				orbitXY = Angle2D.Normalized(orbitXY);
+				orbitZ = Angle2D.Normalized(orbitZ);
+
+				if (orbitZ < VisualCamera.MAX_ANGLEZ_LOW) orbitZ = VisualCamera.MAX_ANGLEZ_LOW;
+				if (orbitZ > VisualCamera.MAX_ANGLEZ_HIGH) orbitZ = VisualCamera.MAX_ANGLEZ_HIGH;
+				
+				Vector3D orbitDelta = Vector3D.FromAngleXYZ(orbitXY, orbitZ);
+
+				Vector3D newPosition = orbitPoint - orbitDelta * orbitRadius;
+				Vector3D positionUpdate = newPosition - General.Map.VisualCamera.Position;
+
+				General.Map.VisualCamera.Position += positionUpdate * General.Map.VisualCamera.MoveMultiplier;
+				start = General.Map.VisualCamera.Position;
+				
+				// Recalculate angles to ensure we're always looking at the orbit point
+				Vector3D updatedDelta = start - orbitPoint;
+				orbitXY = updatedDelta.GetNormal().GetAngleXY();
+				orbitZ = -updatedDelta.GetNormal().GetAngleZ();
+				
+				General.Map.VisualCamera.AngleZ = orbitZ;
+				General.Map.VisualCamera.AngleXY = orbitXY;
+
+				General.Map.VisualCamera.ProcessMouseInput(new Vector2D());
+			}
+			else
+			{
+				General.Map.VisualCamera.ProcessMouseInput(delta);	
+			}
 		}
 
 		[BeginAction("moveforward", BaseAction = true)]
@@ -445,6 +511,19 @@ namespace CodeImp.DoomBuilder.VisualModes
 		{
 			keyright = false;
 		}
+		
+		[BeginAction("orbit", BaseAction = true)]
+		public virtual void BeginOrbit()
+		{
+			orbit = true;
+		}
+
+		[EndAction("orbit", BaseAction = true)]
+		public virtual void EndOrbit()
+		{
+			orbit = false;
+			orbitPointPicked = false;
+		}
 
 		[BeginAction("moveup", BaseAction = true)]
 		public virtual void BeginMoveUp()
@@ -469,6 +548,28 @@ namespace CodeImp.DoomBuilder.VisualModes
 		{
 			keydown = false;
 		}
+		
+		[BeginAction("movecameratocursor", BaseAction = true)]
+		protected virtual void MoveCameraToCursor() 
+		{
+			if (orbit)
+			{
+				orbitRadius = MOVE_CAMERA_DISTANCE;
+			}
+			else
+			{
+				Vector3D hitPosition = GetHitPosition();
+				if (!hitPosition.IsFinite())
+				{
+					return;
+				}
+
+				Vector3D start = General.Map.VisualCamera.Position;
+				Vector3D delta = start - hitPosition;
+				General.Map.VisualCamera.Position = hitPosition + delta.GetFixedLength(MOVE_CAMERA_DISTANCE);
+			}
+		}
+
 
 		//mxd
 		[BeginAction("movethingleft", BaseAction = true)]
@@ -510,14 +611,14 @@ namespace CodeImp.DoomBuilder.VisualModes
 		}
 
 		//mxd. 
-		public Vector2D GetHitPosition() 
+		public Vector3D GetHitPosition() 
 		{
 			Vector3D start = General.Map.VisualCamera.Position;
 			Vector3D delta = General.Map.VisualCamera.Target - General.Map.VisualCamera.Position;
 			delta = delta.GetFixedLength(General.Settings.ViewDistance * 0.98f);
 			VisualPickResult target = PickObject(start, start + delta);
 
-			if(target.picked == null) return new Vector2D(double.NaN, double.NaN);
+			if(target.picked == null) return new Vector3D(double.NaN, double.NaN, double.NaN);
 
 			// Now find where exactly did we hit
 			VisualGeometry vg = target.picked as VisualGeometry;
@@ -525,13 +626,19 @@ namespace CodeImp.DoomBuilder.VisualModes
 
 
 			VisualThing vt = target.picked as VisualThing;
-			if(vt != null) return GetIntersection(start, start + delta, vt.CenterV3D, RenderDevice.V3D(vt.Center - vt.PositionV3));
+			if (vt != null)
+			{
+				Vector3D normal = start - vt.CenterV3D;
+				normal.z = 0;
+				normal = normal.GetNormal();
+				return GetIntersection(start, start + delta, vt.CenterV3D, normal);
+			}
 
-			return new Vector2D(double.NaN, double.NaN);
+			return new Vector3D(double.NaN, double.NaN, double.NaN);
 		}
 
 		//mxd. This checks intersection between line and plane 
-		private static Vector2D GetIntersection(Vector3D start, Vector3D end, Vector3D planeCenter, Vector3D planeNormal) 
+		private static Vector3D GetIntersection(Vector3D start, Vector3D end, Vector3D planeCenter, Vector3D planeNormal) 
 		{
 			Vector3D delta = new Vector3D(planeCenter.x - start.x, planeCenter.y - start.y, planeCenter.z - start.z);
 			return start + Vector3D.DotProduct(planeNormal, delta) / Vector3D.DotProduct(planeNormal, end - start) * (end - start);
@@ -1204,19 +1311,33 @@ namespace CodeImp.DoomBuilder.VisualModes
 			Vector3D camdeltapos = new Vector3D();
 			Vector3D upvec = new Vector3D(0.0, 0.0, 1.0);
 
-			// Move the camera
 			double multiplier;
 			if(General.Interface.ShiftState) multiplier = MOVE_SPEED_MULTIPLIER * 2.0f; else multiplier = MOVE_SPEED_MULTIPLIER;
-			if(keyforward) camdeltapos += camvec * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
-			if(keybackward) camdeltapos -= camvec * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
-			if(keyleft) camdeltapos -= camvecstrafe * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
-			if(keyright) camdeltapos += camvecstrafe * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
-			if(keyup) camdeltapos += upvec * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
-			if(keydown) camdeltapos += -upvec * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
-			
-			// Move the camera
-			General.Map.VisualCamera.ProcessMovement(camdeltapos);
 			
+			if (orbit)
+			{
+				if (keyforward) orbitRadius -= General.Settings.MoveSpeed * multiplier * deltatime;
+				if (keybackward) orbitRadius += General.Settings.MoveSpeed * multiplier * deltatime;
+
+				if (orbitRadius < 1) orbitRadius = 1f;
+
+				General.Map.VisualCamera.ProcessMovement(new Vector3D());
+			}
+			else
+			{
+				// Move the camera
+				
+				if(keyforward) camdeltapos += camvec * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
+				if(keybackward) camdeltapos -= camvec * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
+				if(keyleft) camdeltapos -= camvecstrafe * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
+				if(keyright) camdeltapos += camvecstrafe * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
+				if(keyup) camdeltapos += upvec * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
+				if(keydown) camdeltapos += -upvec * cammovemul * General.Settings.MoveSpeed * multiplier * deltatime;
+				
+				// Move the camera
+				General.Map.VisualCamera.ProcessMovement(camdeltapos);
+			}
+
 			// Apply new camera matrices
 			renderer.PositionAndLookAt(General.Map.VisualCamera.Position, General.Map.VisualCamera.Target);
 			
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
index cae7904db..e2917e115 100755
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
@@ -51,7 +51,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
 		// Object picking
 		private const long PICK_INTERVAL = 80;
 		private const long PICK_INTERVAL_PAINT_SELECT = 10; // biwa
-		private const float PICK_RANGE = 0.98f;
 
 		// Gravity
 		private const float GRAVITY = -0.06f;
-- 
GitLab