Files
Pilz/Pilz.Drawing.Drawing3D.OpenGLRenderer/Camera/Camera.cs
2020-09-24 11:21:53 +02:00

744 lines
26 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using global::System.Windows.Forms;
using global::OpenTK;
namespace Pilz.Drawing.Drawing3D.OpenGLFactory.CameraN
{
public class Camera
{
public event NeedSelectedObjectEventHandler NeedSelectedObject;
public delegate void NeedSelectedObjectEventHandler(object sender, NeedSelectedObjectEventArgs e);
public event PerspectiveChangedEventHandler PerspectiveChanged;
public delegate void PerspectiveChangedEventHandler(object sender, EventArgs e);
// P R I V A T E F I E L D S
private readonly float TAU = (float)(Math.PI * 2d);
private CameraMode myCamMode = CameraMode.FLY;
private Vector3 pos = new Vector3(-5000.0f, 3000.0f, 4000.0f);
private Vector3 myLookat = new Vector3(0f, 0f, 0f);
private Vector3 myFarPoint = new Vector3(0f, 0f, 0f);
private Vector3 myNearPoint = new Vector3(0f, 0f, 0f);
private int lastMouseX = -1;
private int lastMouseY = -1;
private float CamAngleX = 0f;
private float CamAngleY = (float)-(Math.PI / 2d);
private bool resetMouse = true;
private float orbitDistance = 500.0f;
private float orbitTheta = 0.0f;
private float orbitPhi = 0.0f;
private LookDirection currentLookDirection;
private Vector3[] lookPositions = new Vector3[] { new Vector3(0f, 12500f, 0f), new Vector3(0f, -12500, 0f), new Vector3(-12500, 0f, 0f), new Vector3(12500f, 0f, 0f), new Vector3(0f, 0f, 12500f), new Vector3(0f, 0f, -12500) };
// A U T O M A T I C P R O P E R T I E S
public float CamSpeedMultiplier { get; set; } = 1f;
// P R O P E R T I E S
public CameraMode CamMode
{
get
{
return myCamMode;
}
}
public float Yaw
{
get
{
return CamAngleX;
}
}
public float Pitch
{
get
{
return CamAngleY;
}
}
public float Yaw_Degrees
{
get
{
return CamAngleX * (180.0f / 3.14159274f);
}
}
public float Pitch_Degrees
{
get
{
return CamAngleY * (180.0f / 3.14159274f);
}
}
public Vector3 Position
{
get
{
return pos;
}
set
{
pos = value;
}
}
public Vector3 LookAt
{
get
{
return myLookat;
}
set
{
myLookat = value;
}
}
public Vector3 NearPoint
{
get
{
return myNearPoint;
}
set
{
myNearPoint = value;
}
}
public Vector3 FarPoint
{
get
{
return myFarPoint;
}
set
{
myFarPoint = value;
}
}
// C O N S T R U C T O R
public Camera()
{
SetRotationFromLookAt();
}
// F E A T U R E S
private float Clampf(float value, float min, float max)
{
return value > max ? max : value < min ? min : value;
}
private void OrientateCam(float ang, float ang2)
{
float CamLX = (float)Math.Sin(ang) * (float)Math.Sin(-ang2);
float CamLY = (float)Math.Cos(ang2);
float CamLZ = (float)-Math.Cos(ang) * (float)Math.Sin(-ang2);
myLookat.X = pos.X + -CamLX * 100.0f;
myLookat.Y = pos.Y + -CamLY * 100.0f;
myLookat.Z = pos.Z + -CamLZ * 100.0f;
}
private void OffsetCam(int xAmt, int yAmt, int zAmt)
{
double pitch_Renamed = CamAngleY - Math.PI / 2d;
float CamLX = (float)Math.Sin(CamAngleX) * (float)Math.Cos(-pitch_Renamed);
float CamLY = (float)Math.Sin(pitch_Renamed);
float CamLZ = (float)-Math.Cos(CamAngleX) * (float)Math.Cos(-pitch_Renamed);
pos.X = pos.X + xAmt * CamLX * CamSpeedMultiplier;
pos.Y = pos.Y + yAmt * CamLY * CamSpeedMultiplier;
pos.Z = pos.Z + zAmt * CamLZ * CamSpeedMultiplier;
}
public void Move(float y, ref Matrix4 camMtx)
{
OffsetCam(0, (int)y, 0);
OrientateCam(CamAngleX, CamAngleY);
UpdateMatrix(ref camMtx);
}
public void Move(float x, float z, ref Matrix4 camMtx)
{
UpdateCameraOffsetDirectly((int)x, (int)z, ref camMtx);
OrientateCam(CamAngleX, CamAngleY);
UpdateMatrix(ref camMtx);
}
public void SetRotationFromLookAt()
{
float x_diff = myLookat.X - pos.X;
float y_diff = myLookat.Y - pos.Y;
float z_diff = myLookat.Z - pos.Z;
float dist = (float)Math.Sqrt(x_diff * x_diff + y_diff * y_diff + z_diff * z_diff);
if (z_diff == 0f)
{
z_diff = 0.001f;
}
float nxz_ratio = -x_diff / z_diff;
if (z_diff < 0f)
{
CamAngleX = (float)(Math.Atan(nxz_ratio) + Math.PI);
}
else
{
CamAngleX = (float)Math.Atan(nxz_ratio);
}
CamAngleY = -3.1459f - ((float)Math.Asin(y_diff / dist) - 1.57f);
}
public void ResetOrbitToSelectedObject()
{
var objs = GetSelectedObject();
if (objs?.Length > 0 == true)
{
orbitTheta = -(CalculateCenterYRotationOfObjects(objs) * ((float)Math.PI / 180.0f));
orbitPhi = -0.3f;
orbitDistance = 1200.0f;
}
}
public void UpdateOrbitCamera(ref Matrix4 cameraMatrix)
{
if (myCamMode == CameraMode.ORBIT)
{
var objs = GetSelectedObject();
if (objs?.Length > 0 == true)
{
var centerPos = CalculateCenterPositionOfObjects(objs);
pos.X = centerPos.X + (float)(Math.Cos(orbitPhi) * -Math.Sin(orbitTheta) * orbitDistance);
pos.Y = centerPos.Y + (float)(-Math.Sin(orbitPhi) * orbitDistance);
pos.Z = centerPos.Z + (float)(Math.Cos(orbitPhi) * Math.Cos(orbitTheta) * orbitDistance);
myLookat.X = centerPos.X;
myLookat.Y = centerPos.Y;
myLookat.Z = centerPos.Z;
UpdateMatrix(ref cameraMatrix);
SetRotationFromLookAt();
}
}
}
public bool IsOrbitCamera()
{
return myCamMode == CameraMode.ORBIT;
}
public void SetCameraMode(CameraMode mode, ref Matrix4 cameraMatrix)
{
myCamMode = mode;
if (IsOrbitCamera())
{
ResetOrbitToSelectedObject();
UpdateOrbitCamera(ref cameraMatrix);
}
}
public void SetCameraMode_LookDirection(LookDirection dir, ref Matrix4 cameraMatrix)
{
myCamMode = CameraMode.LOOK_DIRECTION;
currentLookDirection = dir;
switch (currentLookDirection)
{
case LookDirection.Top:
{
pos = lookPositions[(int)LookDirection.Top];
myLookat = new Vector3(pos.X, -25000, pos.Z - 1f);
UpdateMatrix(ref cameraMatrix);
SetRotationFromLookAt();
break;
}
case LookDirection.Bottom:
{
pos = lookPositions[(int)LookDirection.Bottom];
myLookat = new Vector3(pos.X, 25000f, pos.Z + 1f);
UpdateMatrix(ref cameraMatrix);
SetRotationFromLookAt();
break;
}
case LookDirection.Left:
{
pos = lookPositions[(int)LookDirection.Left];
myLookat = new Vector3(25000f, pos.Y, pos.Z);
UpdateMatrix(ref cameraMatrix);
SetRotationFromLookAt();
break;
}
case LookDirection.Right:
{
pos = lookPositions[(int)LookDirection.Right];
myLookat = new Vector3(-25000, pos.Y, pos.Z);
UpdateMatrix(ref cameraMatrix);
SetRotationFromLookAt();
break;
}
case LookDirection.Front:
{
pos = lookPositions[(int)LookDirection.Front];
myLookat = new Vector3(pos.X, pos.Y, -25000);
UpdateMatrix(ref cameraMatrix);
SetRotationFromLookAt();
break;
}
case LookDirection.Back:
{
pos = lookPositions[(int)LookDirection.Back];
myLookat = new Vector3(pos.X, pos.Y, 25000f);
UpdateMatrix(ref cameraMatrix);
SetRotationFromLookAt();
break;
}
}
}
public void UpdateCameraMatrixWithMouse(int mouseX, int mouseY, ref Matrix4 cameraMatrix)
{
if (myCamMode == CameraMode.ORBIT && GetSelectedObject() is object)
{
UpdateCameraMatrixWithMouse_ORBIT(mouseX, mouseY, ref cameraMatrix);
}
else if (myCamMode == CameraMode.LOOK_DIRECTION)
{
UpdateCameraMatrixWithMouse_LOOK(pos, mouseX, mouseY, ref cameraMatrix);
}
else
{
UpdateCameraMatrixWithMouse_FLY(mouseX, mouseY, ref cameraMatrix);
}
}
public void UpdateCameraOffsetWithMouse(Vector3 orgPos, int mouseX, int mouseY, int w, int h, ref Matrix4 cameraMatrix)
{
if (myCamMode == CameraMode.ORBIT && GetSelectedObject() is object)
{
UpdateCameraOffsetWithMouse_ORBIT(mouseX, mouseY, ref cameraMatrix);
}
else if (myCamMode == CameraMode.LOOK_DIRECTION)
{
UpdateCameraMatrixWithMouse_LOOK(pos, mouseX, mouseY, ref cameraMatrix);
}
else
{
UpdateCameraOffsetWithMouse_FLY(orgPos, mouseX, mouseY, w, h, ref cameraMatrix);
}
}
public void UpdateCameraMatrixWithScrollWheel(int amt, ref Matrix4 cameraMatrix)
{
if (myCamMode == CameraMode.ORBIT && GetSelectedObject() is object)
{
UpdateCameraMatrixWithScrollWheel_ORBIT(amt, ref cameraMatrix);
}
else if (myCamMode == CameraMode.LOOK_DIRECTION)
{
UpdateCameraMatrixWithScrollWheel_LOOK(amt, ref cameraMatrix);
}
else
{
UpdateCameraMatrixWithScrollWheel_FLY(amt, ref cameraMatrix);
}
}
private void UpdateCameraMatrixWithScrollWheel_FLY(int amt, ref Matrix4 cameraMatrix)
{
OffsetCam(amt, amt, amt);
OrientateCam(CamAngleX, CamAngleY);
UpdateMatrix(ref cameraMatrix);
}
public void RaisePerspectiveChanged()
{
PerspectiveChanged?.Invoke(this, new EventArgs());
}
public void UpdateMatrix(ref Matrix4 cameraMatrix)
{
cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, myLookat.X, myLookat.Y, myLookat.Z, 0f, 1f, 0f);
RaisePerspectiveChanged();
}
private void UpdateCameraMatrixWithScrollWheel_LOOK(int amt, ref Matrix4 cameraMatrix)
{
OffsetCam(amt, amt, amt);
OrientateCam(CamAngleX, CamAngleY);
switch (currentLookDirection)
{
case LookDirection.Top:
{
cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, myLookat.X, myLookat.Y, myLookat.Z - 1f, 0f, 1f, 0f);
RaisePerspectiveChanged();
break;
}
case LookDirection.Bottom:
{
cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, myLookat.X, myLookat.Y, myLookat.Z + 1f, 0f, 1f, 0f);
RaisePerspectiveChanged();
break;
}
default:
{
UpdateMatrix(ref cameraMatrix);
break;
}
}
}
private void UpdateCameraMatrixWithMouse_LOOK(Vector3 orgPos, int mouseX, int mouseY, ref Matrix4 cameraMatrix)
{
if (resetMouse)
{
lastMouseX = mouseX;
lastMouseY = mouseY;
resetMouse = false;
}
int MousePosX = mouseX - lastMouseX;
int MousePosY = mouseY - lastMouseY;
double pitch_Renamed = CamAngleY - Math.PI / 2d;
double yaw_Renamed = CamAngleX - Math.PI / 2d;
float CamLX = (float)Math.Sin(yaw_Renamed);
float CamLY = (float)Math.Cos(pitch_Renamed);
float CamLZ = (float)-Math.Cos(yaw_Renamed);
float m = 8.0f;
switch (currentLookDirection)
{
case LookDirection.Top:
{
pos.X = orgPos.X - MousePosX * CamSpeedMultiplier * CamLX * m - MousePosY * CamSpeedMultiplier * CamLZ * m;
pos.Z = orgPos.Z - MousePosX * CamSpeedMultiplier * CamLZ * m - MousePosY * CamSpeedMultiplier * CamLX * m;
cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X, pos.Y - 1000f, pos.Z - 1f, 0f, 1f, 0f);
lookPositions[(int)currentLookDirection] = pos;
break;
}
case LookDirection.Bottom:
{
pos.X = orgPos.X - MousePosX * CamSpeedMultiplier * CamLX * m + MousePosY * CamSpeedMultiplier * CamLZ * m;
pos.Z = orgPos.Z - MousePosX * CamSpeedMultiplier * CamLZ * m + MousePosY * CamSpeedMultiplier * CamLX * m;
cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X, pos.Y + 1000f, pos.Z + 1f, 0f, 1f, 0f);
lookPositions[(int)currentLookDirection] = pos;
break;
}
case LookDirection.Left:
{
pos.X = orgPos.X - MousePosX * CamSpeedMultiplier * CamLX * m;
pos.Y = orgPos.Y - MousePosY * CamSpeedMultiplier * -1.0f * m;
pos.Z = orgPos.Z - MousePosX * CamSpeedMultiplier * CamLZ * m;
cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X + 12500f, pos.Y, pos.Z, 0f, 1f, 0f);
lookPositions[(int)currentLookDirection] = pos;
break;
}
case LookDirection.Right:
{
pos.X = orgPos.X - MousePosX * CamSpeedMultiplier * CamLX * m;
pos.Y = orgPos.Y - MousePosY * CamSpeedMultiplier * -1.0f * m;
pos.Z = orgPos.Z - MousePosX * CamSpeedMultiplier * CamLZ * m;
cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X - 12500f, pos.Y, pos.Z, 0f, 1f, 0f);
lookPositions[(int)currentLookDirection] = pos;
break;
}
case LookDirection.Front:
{
pos.X = orgPos.X - MousePosX * CamSpeedMultiplier * CamLX * m;
pos.Y = orgPos.Y - MousePosY * CamSpeedMultiplier * -1.0f * m;
pos.Z = orgPos.Z - MousePosX * CamSpeedMultiplier * CamLZ * m;
cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X, pos.Y, pos.Z - 12500f, 0f, 1f, 0f);
lookPositions[(int)currentLookDirection] = pos;
break;
}
case LookDirection.Back:
{
pos.X = orgPos.X - MousePosX * CamSpeedMultiplier * CamLX * m;
pos.Y = orgPos.Y - MousePosY * CamSpeedMultiplier * -1.0f * m;
pos.Z = orgPos.Z - MousePosX * CamSpeedMultiplier * CamLZ * m;
cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, pos.X, pos.Y, pos.Z + 12500f, 0f, 1f, 0f);
lookPositions[(int)currentLookDirection] = pos;
break;
}
}
RaisePerspectiveChanged();
lastMouseX = mouseX;
lastMouseY = mouseY;
// Console.WriteLine("CamAngleX = " + CamAngleX + ", CamAngleY = " + CamAngleY);
// setRotationFromLookAt();
}
private void UpdateCameraMatrixWithMouse_FLY(int mouseX, int mouseY, ref Matrix4 cameraMatrix)
{
if (resetMouse)
{
lastMouseX = mouseX;
lastMouseY = mouseY;
resetMouse = false;
}
int MousePosX = mouseX - lastMouseX;
int MousePosY = mouseY - lastMouseY;
CamAngleX = CamAngleX + 0.01f * MousePosX;
// This next part isn't neccessary, but it keeps the Yaw rotation value within [0, 2*pi] which makes debugging simpler.
if (CamAngleX > TAU)
{
CamAngleX -= TAU;
}
else if (CamAngleX < 0f)
{
CamAngleX += TAU;
}
// Lock pitch rotation within the bounds of [-3.1399.0, -0.0001], otherwise the LookAt function will snap to the
// opposite side and reverse mouse looking controls.
CamAngleY = Clampf(CamAngleY + 0.01f * MousePosY, -3.1399f, -0.0001f);
lastMouseX = mouseX;
lastMouseY = mouseY;
OrientateCam(CamAngleX, CamAngleY);
UpdateMatrix(ref cameraMatrix);
// Console.WriteLine("CamAngleX = " + CamAngleX + ", CamAngleY = " + CamAngleY);
// setRotationFromLookAt();
}
private void UpdateCameraOffsetWithMouse_FLY(Vector3 orgPos, int mouseX, int mouseY, int w, int h, ref Matrix4 cameraMatrix)
{
if (resetMouse)
{
lastMouseX = mouseX;
lastMouseY = mouseY;
resetMouse = false;
}
int MousePosX = -mouseX + lastMouseX;
int MousePosY = -mouseY + lastMouseY;
double pitch_Renamed = CamAngleY - Math.PI / 2d;
double yaw_Renamed = CamAngleX - Math.PI / 2d;
float CamLX = (float)Math.Sin(yaw_Renamed);
float CamLZ = (float)-Math.Cos(yaw_Renamed);
pos.X = orgPos.X - MousePosX * CamSpeedMultiplier * CamLX * 6.0f;
pos.Y = orgPos.Y - MousePosY * CamSpeedMultiplier * -1.0f * 6.0f;
pos.Z = orgPos.Z - MousePosX * CamSpeedMultiplier * CamLZ * 6.0f;
OrientateCam(CamAngleX, CamAngleY);
UpdateMatrix(ref cameraMatrix);
}
public void UpdateCameraOffsetDirectly(int horz_amount, int vert_amount, ref Matrix4 cameraMatrix)
{
if (myCamMode == CameraMode.ORBIT)
{
UpdateCameraOffsetDirectly_ORBIT((int)(horz_amount / 5d), (int)(vert_amount / 5d), ref cameraMatrix);
}
else
{
// Console.WriteLine(MousePosX+","+ MousePosY);
double pitch_Renamed = CamAngleY - Math.PI / 2d;
double yaw_Renamed = CamAngleX - Math.PI / 2d;
float CamLX = (float)Math.Sin(yaw_Renamed);
// float CamLY = (float)Math.Cos(pitch);
float CamLZ = (float)-Math.Cos(yaw_Renamed);
pos.X += horz_amount * CamSpeedMultiplier * CamLX;
pos.Y += vert_amount * CamSpeedMultiplier * -1.0f;
pos.Z += horz_amount * CamSpeedMultiplier * CamLZ;
OrientateCam(CamAngleX, CamAngleY);
UpdateMatrix(ref cameraMatrix);
}
}
private void UpdateCameraOffsetDirectly_ORBIT(int moveSpeedX, int moveSpeedY, ref Matrix4 cameraMatrix)
{
int MousePosX = moveSpeedX;
int MousePosY = moveSpeedY;
orbitTheta += MousePosX * 0.01f * CamSpeedMultiplier;
orbitPhi -= MousePosY * 0.01f * CamSpeedMultiplier;
orbitPhi = Clampf(orbitPhi, -1.57f, 1.57f);
UpdateOrbitCamera(ref cameraMatrix);
}
private void UpdateCameraMatrixWithMouse_ORBIT(int mouseX, int mouseY, ref Matrix4 cameraMatrix)
{
UpdateCameraOffsetWithMouse_ORBIT(mouseX, mouseY, ref cameraMatrix);
}
private void UpdateCameraOffsetWithMouse_ORBIT(int mouseX, int mouseY, ref Matrix4 cameraMatrix)
{
if (resetMouse)
{
lastMouseX = mouseX;
lastMouseY = mouseY;
resetMouse = false;
}
int MousePosX = -mouseX + lastMouseX;
int MousePosY = -mouseY + lastMouseY;
orbitTheta += MousePosX * 0.01f * CamSpeedMultiplier;
orbitPhi -= MousePosY * 0.01f * CamSpeedMultiplier;
orbitPhi = Clampf(orbitPhi, -1.57f, 1.57f);
UpdateOrbitCamera(ref cameraMatrix);
lastMouseX = mouseX;
lastMouseY = mouseY;
}
private void UpdateCameraMatrixWithScrollWheel_ORBIT(int amt, ref Matrix4 cameraMatrix)
{
orbitDistance -= amt;
if (orbitDistance < 300.0f)
{
orbitDistance = 300.0f;
}
UpdateOrbitCamera(ref cameraMatrix);
}
public void ResetMouseStuff()
{
resetMouse = true;
}
private System.Numerics.Vector3 CalculateCenterPositionOfObjects(ICameraPoint[] objs)
{
if (objs.Length <= 1)
{
var obj = objs.FirstOrDefault();
if (obj is null)
{
return System.Numerics.Vector3.Zero;
}
else
{
return new System.Numerics.Vector3(obj.Position.X, obj.Position.Y, obj.Position.Z);
}
}
float? maxX = default;
float? maxY = default;
float? maxZ = default;
float? minX = default;
float? minY = default;
float? minZ = default;
foreach (ICameraPoint obj in objs)
{
var pos = obj.Position;
if (maxX is null || pos.X > maxX)
maxX = pos.X;
if (maxY is null || pos.Y > maxY)
maxY = pos.Y;
if (maxZ is null || pos.Z > maxZ)
maxZ = pos.Z;
if (minX is null || pos.X < minX)
minX = pos.X;
if (minY is null || pos.Y < minY)
minY = pos.Y;
if (minZ is null || pos.Z < minZ)
minZ = pos.Z;
}
if (maxX is null)
maxX = 0;
if (maxY is null)
maxY = 0;
if (maxZ is null)
maxZ = 0;
if (minX is null)
minX = 0;
if (minY is null)
minY = 0;
if (minZ is null)
minZ = 0;
var upper = new System.Numerics.Vector3((float)maxX, (float)maxY, (float)maxZ);
var lower = new System.Numerics.Vector3((float)minX, (float)minY, (float)minZ);
var middle = (upper + lower) / 2f;
return middle;
}
private float CalculateCenterYRotationOfObjects(ICameraPoint[] objs)
{
if (objs.Length <= 1)
{
var obj = objs.FirstOrDefault();
if (obj is null)
{
return 0f;
}
else
{
return obj.Rotation.Y;
}
}
var yRot = new List<float>();
foreach (ICameraPoint obj in objs)
yRot.Add(obj.Rotation.Y);
return yRot.Average();
}
private ICameraPoint[] GetSelectedObject()
{
var e = new NeedSelectedObjectEventArgs();
NeedSelectedObject?.Invoke(this, e);
var stopw = new Stopwatch();
stopw.Start();
while (!e.HasObjectSetted && stopw.ElapsedMilliseconds <= 1000L)
Application.DoEvents();
stopw.Stop();
return e.Points;
}
// C A P S E L T C L A S S E S
public class NeedSelectedObjectEventArgs : EventArgs
{
private bool _HasObjectSetted = false;
public bool HasObjectSetted
{
get
{
return _HasObjectSetted;
}
}
private ICameraPoint[] _Points = null;
public ICameraPoint[] Points
{
get
{
return _Points;
}
set
{
_Points = value;
_HasObjectSetted = true;
}
}
}
}
}