Imports System.Windows.Forms Imports OpenTK.Mathematics Namespace CameraN Public Class Camera Public Event NeedSelectedObject(sender As Object, e As NeedSelectedObjectEventArgs) Public Event PerspectiveChanged(sender As Object, e As EventArgs) 'P R I V A T E F I E L D S Private ReadOnly TAU As Single = Math.PI * 2 Private myCamMode As CameraMode = CameraMode.FLY Private pos As New Vector3(-5000.0F, 3000.0F, 4000.0F) Private myLookat As New Vector3(0F, 0F, 0F) Private myFarPoint As New Vector3(0F, 0F, 0F) Private myNearPoint As New Vector3(0F, 0F, 0F) Private lastMouseX As Integer = -1, lastMouseY As Integer = -1 Private CamAngleX As Single = 0F, CamAngleY As Single = -(Math.PI / 2) Private resetMouse As Boolean = True Private orbitDistance As Single = 500.0F Private orbitTheta As Single = 0.0F, orbitPhi As Single = 0.0F Private currentLookDirection As LookDirection Private lookPositions() As Vector3 = { New Vector3(0, 12500, 0), New Vector3(0, -12500, 0), New Vector3(-12500, 0, 0), New Vector3(12500, 0, 0), New Vector3(0, 0, 12500), New Vector3(0, 0, -12500) } 'A U T O M A T I C P R O P E R T I E S Public Property CamSpeedMultiplier As Single = 1 'P R O P E R T I E S Public ReadOnly Property CamMode As CameraMode Get Return myCamMode End Get End Property Public ReadOnly Property Yaw As Single Get Return CamAngleX End Get End Property Public ReadOnly Property Pitch As Single Get Return CamAngleY End Get End Property Public ReadOnly Property Yaw_Degrees As Single Get Return CamAngleX * (180.0F / 3.14159274F) End Get End Property Public ReadOnly Property Pitch_Degrees As Single Get Return CamAngleY * (180.0F / 3.14159274F) End Get End Property Public Property Position As Vector3 Get Return pos End Get Set(value As Vector3) pos = value End Set End Property Public Property LookAt As Vector3 Get Return myLookat End Get Set(value As Vector3) myLookat = value End Set End Property Public Property NearPoint As Vector3 Get Return myNearPoint End Get Set(value As Vector3) myNearPoint = value End Set End Property Public Property FarPoint As Vector3 Get Return myFarPoint End Get Set(value As Vector3) myFarPoint = value End Set End Property 'C O N S T R U C T O R Public Sub New() SetRotationFromLookAt() End Sub 'F E A T U R E S Private Function Clampf(value As Single, min As Single, max As Single) As Single Return If(value > max, max, If(value < min, min, value)) End Function Private Sub OrientateCam(ang As Single, ang2 As Single) Dim CamLX As Single = CSng(Math.Sin(ang)) * CSng(Math.Sin(-ang2)) Dim CamLY As Single = CSng(Math.Cos(ang2)) Dim CamLZ As Single = CSng(-Math.Cos(ang)) * CSng(Math.Sin(-ang2)) myLookat.X = pos.X + (-CamLX) * 100.0F myLookat.Y = pos.Y + (-CamLY) * 100.0F myLookat.Z = pos.Z + (-CamLZ) * 100.0F End Sub Private Sub OffsetCam(xAmt As Integer, yAmt As Integer, zAmt As Integer) Dim pitch_Renamed As Double = CamAngleY - (Math.PI / 2) Dim CamLX As Single = CSng(Math.Sin(CamAngleX)) * CSng(Math.Cos(-pitch_Renamed)) Dim CamLY As Single = CSng(Math.Sin(pitch_Renamed)) Dim CamLZ As Single = CSng(-Math.Cos(CamAngleX)) * CSng(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 End Sub Public Sub Move(y As Single, ByRef camMtx As Matrix4) OffsetCam(0, y, 0) OrientateCam(CamAngleX, CamAngleY) UpdateMatrix(camMtx) End Sub Public Sub Move(x As Single, z As Single, ByRef camMtx As Matrix4) UpdateCameraOffsetDirectly(x, z, camMtx) OrientateCam(CamAngleX, CamAngleY) UpdateMatrix(camMtx) End Sub Public Sub SetRotationFromLookAt() Dim x_diff As Single = myLookat.X - pos.X Dim y_diff As Single = myLookat.Y - pos.Y Dim z_diff As Single = myLookat.Z - pos.Z Dim dist As Single = CSng(Math.Sqrt(x_diff * x_diff + y_diff * y_diff + z_diff * z_diff)) If z_diff = 0 Then z_diff = 0.001F End If Dim nxz_ratio As Single = -x_diff / z_diff If z_diff < 0 Then CamAngleX = CSng(Math.Atan(nxz_ratio) + Math.PI) Else CamAngleX = CSng(Math.Atan(nxz_ratio)) End If CamAngleY = -3.1459F - (CSng(Math.Asin(y_diff / dist)) - 1.57F) End Sub Public Sub ResetOrbitToSelectedObject() Dim objs As ICameraPoint() = GetSelectedObject() If objs?.Length > 0 Then orbitTheta = -(CalculateCenterYRotationOfObjects(objs) * (CSng(Math.PI) / 180.0F)) orbitPhi = -0.3F orbitDistance = 1200.0F End If End Sub Public Sub UpdateOrbitCamera(ByRef cameraMatrix As Matrix4) If myCamMode = CameraMode.ORBIT Then Dim objs As ICameraPoint() = GetSelectedObject() If objs?.Length > 0 Then Dim centerPos As Numerics.Vector3 = CalculateCenterPositionOfObjects(objs) pos.X = centerPos.X + CSng(Math.Cos(orbitPhi) * -Math.Sin(orbitTheta) * orbitDistance) pos.Y = centerPos.Y + CSng(-Math.Sin(orbitPhi) * orbitDistance) pos.Z = centerPos.Z + CSng(Math.Cos(orbitPhi) * Math.Cos(orbitTheta) * orbitDistance) myLookat.X = centerPos.X myLookat.Y = centerPos.Y myLookat.Z = centerPos.Z UpdateMatrix(cameraMatrix) SetRotationFromLookAt() End If End If End Sub Public Function IsOrbitCamera() As Boolean Return myCamMode = CameraMode.ORBIT End Function Public Sub SetCameraMode(mode As CameraMode, ByRef cameraMatrix As Matrix4) myCamMode = mode If IsOrbitCamera() Then ResetOrbitToSelectedObject() UpdateOrbitCamera(cameraMatrix) End If End Sub Public Sub SetCameraMode_LookDirection(dir As LookDirection, ByRef cameraMatrix As Matrix4) myCamMode = CameraMode.LOOK_DIRECTION currentLookDirection = dir Select Case currentLookDirection Case LookDirection.Top pos = lookPositions(CInt(LookDirection.Top)) myLookat = New Vector3(pos.X, -25000, pos.Z - 1) UpdateMatrix(cameraMatrix) SetRotationFromLookAt() Case LookDirection.Bottom pos = lookPositions(CInt(LookDirection.Bottom)) myLookat = New Vector3(pos.X, 25000, pos.Z + 1) UpdateMatrix(cameraMatrix) SetRotationFromLookAt() Case LookDirection.Left pos = lookPositions(CInt(LookDirection.Left)) myLookat = New Vector3(25000, pos.Y, pos.Z) UpdateMatrix(cameraMatrix) SetRotationFromLookAt() Case LookDirection.Right pos = lookPositions(CInt(LookDirection.Right)) myLookat = New Vector3(-25000, pos.Y, pos.Z) UpdateMatrix(cameraMatrix) SetRotationFromLookAt() Case LookDirection.Front pos = lookPositions(CInt(LookDirection.Front)) myLookat = New Vector3(pos.X, pos.Y, -25000) UpdateMatrix(cameraMatrix) SetRotationFromLookAt() Case LookDirection.Back pos = lookPositions(CInt(LookDirection.Back)) myLookat = New Vector3(pos.X, pos.Y, 25000) UpdateMatrix(cameraMatrix) SetRotationFromLookAt() End Select End Sub Public Sub UpdateCameraMatrixWithMouse(mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4) If myCamMode = CameraMode.ORBIT AndAlso GetSelectedObject() IsNot Nothing Then UpdateCameraMatrixWithMouse_ORBIT(mouseX, mouseY, cameraMatrix) ElseIf myCamMode = CameraMode.LOOK_DIRECTION Then UpdateCameraMatrixWithMouse_LOOK(pos, mouseX, mouseY, cameraMatrix) Else UpdateCameraMatrixWithMouse_FLY(mouseX, mouseY, cameraMatrix) End If End Sub Public Sub UpdateCameraOffsetWithMouse(orgPos As Vector3, mouseX As Integer, mouseY As Integer, w As Integer, h As Integer, ByRef cameraMatrix As Matrix4) If myCamMode = CameraMode.ORBIT AndAlso GetSelectedObject() IsNot Nothing Then UpdateCameraOffsetWithMouse_ORBIT(mouseX, mouseY, cameraMatrix) ElseIf myCamMode = CameraMode.LOOK_DIRECTION Then UpdateCameraMatrixWithMouse_LOOK(pos, mouseX, mouseY, cameraMatrix) Else UpdateCameraOffsetWithMouse_FLY(orgPos, mouseX, mouseY, w, h, cameraMatrix) End If End Sub Public Sub UpdateCameraMatrixWithScrollWheel(amt As Integer, ByRef cameraMatrix As Matrix4) If myCamMode = CameraMode.ORBIT AndAlso GetSelectedObject() IsNot Nothing Then UpdateCameraMatrixWithScrollWheel_ORBIT(amt, cameraMatrix) ElseIf myCamMode = CameraMode.LOOK_DIRECTION Then UpdateCameraMatrixWithScrollWheel_LOOK(amt, cameraMatrix) Else UpdateCameraMatrixWithScrollWheel_FLY(amt, cameraMatrix) End If End Sub Private Sub UpdateCameraMatrixWithScrollWheel_FLY(amt As Integer, ByRef cameraMatrix As Matrix4) OffsetCam(amt, amt, amt) OrientateCam(CamAngleX, CamAngleY) UpdateMatrix(cameraMatrix) End Sub Public Sub RaisePerspectiveChanged() RaiseEvent PerspectiveChanged(Me, New EventArgs) End Sub Public Sub UpdateMatrix(ByRef cameraMatrix As Matrix4) cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, myLookat.X, myLookat.Y, myLookat.Z, 0, 1, 0) RaisePerspectiveChanged() End Sub Private Sub UpdateCameraMatrixWithScrollWheel_LOOK(amt As Integer, ByRef cameraMatrix As Matrix4) OffsetCam(amt, amt, amt) OrientateCam(CamAngleX, CamAngleY) Select Case currentLookDirection Case LookDirection.Top cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, myLookat.X, myLookat.Y, myLookat.Z - 1, 0, 1, 0) RaisePerspectiveChanged() Case LookDirection.Bottom cameraMatrix = Matrix4.LookAt(pos.X, pos.Y, pos.Z, myLookat.X, myLookat.Y, myLookat.Z + 1, 0, 1, 0) RaisePerspectiveChanged() Case Else UpdateMatrix(cameraMatrix) End Select End Sub Private Sub UpdateCameraMatrixWithMouse_LOOK(orgPos As Vector3, mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4) If resetMouse Then lastMouseX = mouseX lastMouseY = mouseY resetMouse = False End If Dim MousePosX As Integer = mouseX - lastMouseX Dim MousePosY As Integer = mouseY - lastMouseY Dim pitch_Renamed As Double = CamAngleY - (Math.PI / 2) Dim yaw_Renamed As Double = CamAngleX - (Math.PI / 2) Dim CamLX As Single = CSng(Math.Sin(yaw_Renamed)) Dim CamLY As Single = CSng(Math.Cos(pitch_Renamed)) Dim CamLZ As Single = CSng(-Math.Cos(yaw_Renamed)) Dim m As Single = 8.0F Select Case 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 - 1000, pos.Z - 1, 0, 1, 0) lookPositions(CInt(currentLookDirection)) = pos 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 + 1000, pos.Z + 1, 0, 1, 0) lookPositions(CInt(currentLookDirection)) = pos 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 + 12500, pos.Y, pos.Z, 0, 1, 0) lookPositions(CInt(currentLookDirection)) = pos 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 - 12500, pos.Y, pos.Z, 0, 1, 0) lookPositions(CInt(currentLookDirection)) = pos 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 - 12500, 0, 1, 0) lookPositions(CInt(currentLookDirection)) = pos 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 + 12500, 0, 1, 0) lookPositions(CInt(currentLookDirection)) = pos End Select RaisePerspectiveChanged() lastMouseX = mouseX lastMouseY = mouseY 'Console.WriteLine("CamAngleX = " + CamAngleX + ", CamAngleY = " + CamAngleY); 'setRotationFromLookAt(); End Sub Private Sub UpdateCameraMatrixWithMouse_FLY(mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4) If resetMouse Then lastMouseX = mouseX lastMouseY = mouseY resetMouse = False End If Dim MousePosX As Integer = mouseX - lastMouseX Dim MousePosY As Integer = 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 Then CamAngleX -= TAU ElseIf CamAngleX < 0 Then CamAngleX += TAU End If ' 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(cameraMatrix) 'Console.WriteLine("CamAngleX = " + CamAngleX + ", CamAngleY = " + CamAngleY); 'setRotationFromLookAt(); End Sub Private Sub UpdateCameraOffsetWithMouse_FLY(orgPos As Vector3, mouseX As Integer, mouseY As Integer, w As Integer, h As Integer, ByRef cameraMatrix As Matrix4) If resetMouse Then lastMouseX = mouseX lastMouseY = mouseY resetMouse = False End If Dim MousePosX As Integer = (-mouseX) + lastMouseX Dim MousePosY As Integer = (-mouseY) + lastMouseY Dim pitch_Renamed As Double = CamAngleY - (Math.PI / 2) Dim yaw_Renamed As Double = CamAngleX - (Math.PI / 2) Dim CamLX As Single = Math.Sin(yaw_Renamed) Dim CamLZ As Single = -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(cameraMatrix) End Sub Public Sub UpdateCameraOffsetDirectly(horz_amount As Integer, vert_amount As Integer, ByRef cameraMatrix As Matrix4) If myCamMode = CameraMode.ORBIT Then UpdateCameraOffsetDirectly_ORBIT(horz_amount / 5, vert_amount / 5, cameraMatrix) Else 'Console.WriteLine(MousePosX+","+ MousePosY); Dim pitch_Renamed As Double = CamAngleY - (Math.PI / 2) Dim yaw_Renamed As Double = CamAngleX - (Math.PI / 2) Dim CamLX As Single = CSng(Math.Sin(yaw_Renamed)) ' float CamLY = (float)Math.Cos(pitch); Dim CamLZ As Single = CSng(-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(cameraMatrix) End If End Sub Private Sub UpdateCameraOffsetDirectly_ORBIT(moveSpeedX As Integer, moveSpeedY As Integer, ByRef cameraMatrix As Matrix4) Dim MousePosX As Integer = moveSpeedX Dim MousePosY As Integer = moveSpeedY orbitTheta += MousePosX * 0.01F * CamSpeedMultiplier orbitPhi -= MousePosY * 0.01F * CamSpeedMultiplier orbitPhi = Clampf(orbitPhi, -1.57F, 1.57F) UpdateOrbitCamera(cameraMatrix) End Sub Private Sub UpdateCameraMatrixWithMouse_ORBIT(mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4) UpdateCameraOffsetWithMouse_ORBIT(mouseX, mouseY, cameraMatrix) End Sub Private Sub UpdateCameraOffsetWithMouse_ORBIT(mouseX As Integer, mouseY As Integer, ByRef cameraMatrix As Matrix4) If resetMouse Then lastMouseX = mouseX lastMouseY = mouseY resetMouse = False End If Dim MousePosX As Integer = (-mouseX) + lastMouseX Dim MousePosY As Integer = (-mouseY) + lastMouseY orbitTheta += MousePosX * 0.01F * CamSpeedMultiplier orbitPhi -= MousePosY * 0.01F * CamSpeedMultiplier orbitPhi = Clampf(orbitPhi, -1.57F, 1.57F) UpdateOrbitCamera(cameraMatrix) lastMouseX = mouseX lastMouseY = mouseY End Sub Private Sub UpdateCameraMatrixWithScrollWheel_ORBIT(amt As Integer, ByRef cameraMatrix As Matrix4) orbitDistance -= amt If orbitDistance < 300.0F Then orbitDistance = 300.0F End If UpdateOrbitCamera(cameraMatrix) End Sub Public Sub ResetMouseStuff() resetMouse = True End Sub Private Function CalculateCenterPositionOfObjects(objs As ICameraPoint()) As Numerics.Vector3 If objs.Length <= 1 Then Dim obj As ICameraPoint = objs.FirstOrDefault If obj Is Nothing Then Return Numerics.Vector3.Zero Else Return New Numerics.Vector3(obj.Position.X, obj.Position.Y, obj.Position.Z) End If End If Dim maxX As Single? = Nothing Dim maxY As Single? = Nothing Dim maxZ As Single? = Nothing Dim minX As Single? = Nothing Dim minY As Single? = Nothing Dim minZ As Single? = Nothing For Each obj As ICameraPoint In objs Dim pos As Numerics.Vector3 = obj.Position If maxX Is Nothing OrElse pos.X > maxX Then maxX = pos.X If maxY Is Nothing OrElse pos.Y > maxY Then maxY = pos.Y If maxZ Is Nothing OrElse pos.Z > maxZ Then maxZ = pos.Z If minX Is Nothing OrElse pos.X < minX Then minX = pos.X If minY Is Nothing OrElse pos.Y < minY Then minY = pos.Y If minZ Is Nothing OrElse pos.Z < minZ Then minZ = pos.Z Next If maxX Is Nothing Then maxX = 0 If maxY Is Nothing Then maxY = 0 If maxZ Is Nothing Then maxZ = 0 If minX Is Nothing Then minX = 0 If minY Is Nothing Then minY = 0 If minZ Is Nothing Then minZ = 0 Dim upper As New Numerics.Vector3(maxX, maxY, maxZ) Dim lower As New Numerics.Vector3(minX, minY, minZ) Dim middle As Numerics.Vector3 = (upper + lower) / 2 Return middle End Function Private Function CalculateCenterYRotationOfObjects(objs As ICameraPoint()) As Single If objs.Length <= 1 Then Dim obj As ICameraPoint = objs.FirstOrDefault If obj Is Nothing Then Return 0 Else Return obj.Rotation.Y End If End If Dim yRot As New List(Of Single) For Each obj As ICameraPoint In objs yRot.Add(obj.Rotation.Y) Next Return yRot.Average End Function Private Function GetSelectedObject() As ICameraPoint() Dim e As New NeedSelectedObjectEventArgs RaiseEvent NeedSelectedObject(Me, e) Dim stopw As New Stopwatch stopw.Start() Do Until e.HasObjectSetted OrElse stopw.ElapsedMilliseconds > 1000 Application.DoEvents() Loop stopw.Stop() Return e.Points End Function 'C A P S E L T C L A S S E S Public Class NeedSelectedObjectEventArgs Inherits EventArgs Private _HasObjectSetted As Boolean = False Public ReadOnly Property HasObjectSetted As Boolean Get Return _HasObjectSetted End Get End Property Private _Points As ICameraPoint() = Nothing Public Property Points As ICameraPoint() Get Return _Points End Get Set(value As ICameraPoint()) _Points = value _HasObjectSetted = True End Set End Property End Class End Class End Namespace