Files
PCK-Studio/PckStudio.Rendering/Camera/PerspectiveCamera.cs

209 lines
6.0 KiB
C#

/* Copyright (c) 2024-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
// movement code taken from:
// https://github.com/TheCherno/Hazel/blob/master/Hazel/src/Hazel/Renderer/EditorCamera.cpp
using System;
using System.Drawing;
using OpenTK;
namespace PckStudio.Rendering.Camera
{
public class PerspectiveCamera : Camera
{
public float NearClip
{
get => _nearClip;
set
{
_nearClip = value;
UpdateProjection();
}
}
public float FarClip
{
get => _farClip;
set
{
_farClip = value;
UpdateProjection();
}
}
public float Distance
{
get => _spherical.Radius;
set
{
_spherical.Radius = Math.Max(value, 2f);
UpdateViewMatrix();
}
}
public float RotationSpeed { get; set; } = 5f;
public Size ViewportSize
{
get => _viewportSize;
set
{
_viewportSize = value;
UpdateProjection();
}
}
public Vector3 WorldPosition => _position;
public Vector3 FocalPoint
{
get => _focalPoint;
set
{
_focalPoint = value;
UpdateViewMatrix();
}
}
public float Pitch
{
get => _spherical.Theta;
set
{
_spherical.Theta = MathHelper.Clamp(value, -90f, 90f);
UpdateViewMatrix();
}
}
public float Yaw
{
get => _spherical.Phi;
set
{
_spherical.Phi = value % 360f;
UpdateViewMatrix();
}
}
public Vector3 Orientation => -Vector3.UnitZ;
public Vector3 Up => Vector3.UnitY;
public float MinimumFov { get; } = 30f;
public float MaximumFov { get; } = 180f;
public float Fov
{
get => _fov;
set
{
_fov = MathHelper.Clamp(value, MinimumFov, MaximumFov);
UpdateProjection();
}
}
public PerspectiveCamera(float fov, Vector3 target)
{
_fov = fov;
_focalPoint = target;
_nearClip = 1f;
_farClip = 1000f;
UpdateProjection();
}
private Matrix4 viewMatrix;
private float _fov;
private float _nearClip;
private float _farClip;
private Spherical _spherical;
private Size _viewportSize;
private Vector3 _position;
private Vector3 _focalPoint;
public override Matrix4 GetViewProjection()
{
return viewMatrix * projectionMatrix;
}
public Matrix4 GetProjection()
{
return projectionMatrix;
}
private void UpdateViewMatrix()
{
Matrix4 rotation = Matrix4.CreateRotationY(MathHelper.DegreesToRadians(Yaw)) * Matrix4.CreateRotationX(MathHelper.DegreesToRadians(Pitch));
viewMatrix = Matrix4.CreateTranslation(FocalPoint) * rotation * Matrix4.CreateTranslation(0, 0, -Distance);
// Position in Right-handed coordinates
_position = viewMatrix.Inverted().ExtractTranslation();
}
private void UpdateProjection()
{
float aspect = (float)ViewportSize.Width / (float)ViewportSize.Height;
projectionMatrix = Matrix4.CreatePerspectiveFieldOfView((float)MathHelper.DegreesToRadians(Fov), aspect, NearClip, FarClip);
}
private Vector2 GetPanSpeed()
{
float x = Math.Min(ViewportSize.Width / 100.0f, 1.4f); // max = 2.4f
float xFactor = 0.0366f * (x * x) - 0.1778f * x + 0.3021f;
float y = Math.Min(ViewportSize.Height / 100.0f, 1.4f); // max = 2.4f
float yFactor = 0.0366f * (y * y) - 0.1778f * y + 0.3021f;
return new Vector2(xFactor, yFactor);
}
public void Pan(Vector2 delta)
{
Pan(delta.X, delta.Y);
}
public void Pan(float deltaX, float deltaY)
{
Vector2 panSpeed = GetPanSpeed();
// Taken from: blockbench
// https://github.com/JannisX11/blockbench/blob/a56fe01a517ace8d013f67bbd3d02442c44d3141/js/preview/OrbitControls.js#L271-L322
Vector3 left = viewMatrix.Column0.Xyz * -Distance;
Vector3 up = viewMatrix.Column1.Xyz * Distance;
_focalPoint -= left * deltaX * panSpeed.X;
_focalPoint -= up * deltaY * panSpeed.Y;
UpdateViewMatrix();
}
public void Rotate(Vector2 delta)
{
Rotate(delta.X, delta.Y);
}
public void Rotate(float deltaX,float deltaY)
{
Yaw += deltaX * RotationSpeed;
Pitch += deltaY * RotationSpeed;
}
public override string ToString()
{
return $"FOV: {Fov}\nPosition: {WorldPosition}\nRotation: {_spherical}";
}
}
}