PerspectiveCamera - Add orbital controls

This commit is contained in:
miku-666
2024-02-25 00:28:40 +01:00
parent c972db7bc4
commit f8a74fab2a
5 changed files with 207 additions and 116 deletions

View File

@@ -172,6 +172,7 @@
<Compile Include="Rendering\SkinRenderer.Designer.cs">
<DependentUpon>SkinRenderer.cs</DependentUpon>
</Compile>
<Compile Include="Rendering\Spherical.cs" />
<Compile Include="Rendering\TextureChangingEventArgs.cs" />
<Compile Include="Rendering\TextureVertex.cs" />
<Compile Include="Rendering\Texture\CubeTexture.cs" />

View File

@@ -25,16 +25,38 @@ namespace PckStudio.Rendering.Camera
{
internal class PerspectiveCamera : Camera
{
public float Distance
public float NearClip
{
get => _distance;
get => _nearClip;
set
{
_distance = value;
_nearClip = value;
UpdateProjection();
}
}
public float FarClip
{
get => _farClip;
set
{
_farClip = value;
UpdateProjection();
}
}
public float Distance
{
get => _spherical.Radius;
set
{
_spherical.Radius = value;
UpdateViewMatrix();
}
}
public float RotationSpeed { get; set; } = 5f;
public Size ViewportSize
{
get => _viewportSize;
@@ -46,6 +68,7 @@ namespace PckStudio.Rendering.Camera
}
public Vector3 WorldPosition => _position;
public Vector3 FocalPoint
{
get => _focalPoint;
@@ -56,48 +79,61 @@ namespace PckStudio.Rendering.Camera
}
}
public Vector2 Rotation
public float Pitch
{
get => _rotation;
get => _spherical.Theta;
set
{
_rotation.X = MathHelper.Clamp(value.X, -180f, 180f);
_rotation.Y = MathHelper.Clamp(value.Y, -180f, 180f);
_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 Vector3 Up => Vector3.UnitY;
public float MinimumFov { get; set; } = 30f;
public float MaximumFov { get; set; } = 120f;
public float MaximumFov { get; set; } = 180f;
public float Fov
{
get => fov;
get => _fov;
set
{
fov = MathHelper.Clamp(value, MinimumFov, MaximumFov);
_fov = MathHelper.Clamp(value, MinimumFov, MaximumFov);
UpdateProjection();
}
}
public PerspectiveCamera(float fov, Vector3 position)
{
Fov = fov;
_fov = fov;
_position = position;
_focalPoint = Vector3.Zero;
_nearClip = 1f;
_farClip = 1000f;
UpdateProjection();
}
private Matrix4 viewMatrix;
private float fov;
private float _fov;
private float _distance;
private float _nearClip;
private float _farClip;
private Spherical _spherical;
private Size _viewportSize;
private Vector3 _position;
private Vector2 _rotation;
private Vector3 _focalPoint;
public override Matrix4 GetViewProjection()
@@ -105,24 +141,24 @@ namespace PckStudio.Rendering.Camera
return viewMatrix * projectionMatrix;
}
public Matrix4 GetProjection()
{
return projectionMatrix;
}
private void UpdateViewMatrix()
{
// m_Yaw = m_Pitch = 0.0f; // Lock the camera's rotation
_position = CalculatePosition();
Matrix4 rotation = Matrix4.CreateRotationY(MathHelper.DegreesToRadians(Yaw)) * Matrix4.CreateRotationX(MathHelper.DegreesToRadians(Pitch));
Quaternion orientation = GetOrientation();
var rotation = Matrix4.CreateTranslation(_position);
rotation *= Matrix4.CreateFromQuaternion(orientation);
rotation *= Matrix4.CreateTranslation(_position).Inverted();
viewMatrix = Matrix4.CreateTranslation(_position) * rotation;
viewMatrix = viewMatrix.Inverted();
viewMatrix = Matrix4.CreateTranslation(-FocalPoint) * rotation * Matrix4.CreateTranslation(0, 0, -Distance);
_position = viewMatrix.Inverted().ExtractTranslation();
}
private void UpdateProjection()
{
float aspect = (float)ViewportSize.Width / (float)ViewportSize.Height;
projectionMatrix = Matrix4.CreatePerspectiveFieldOfView((float)MathHelper.DegreesToRadians(Fov), aspect, 1f, 1000f);
projectionMatrix = Matrix4.CreatePerspectiveFieldOfView((float)MathHelper.DegreesToRadians(Fov), aspect, NearClip, FarClip);
}
private Vector2 GetPanSpeed()
@@ -144,56 +180,30 @@ namespace PckStudio.Rendering.Camera
public void Pan(float deltaX, float deltaY)
{
Vector2 panSpeed = GetPanSpeed();
_focalPoint += -GetRightDirection() * deltaX * panSpeed.X * _distance;
_focalPoint += GetUpDirection() * deltaY * panSpeed.Y * _distance;
// 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)
public void Rotate(Vector2 delta)
{
Rotate(delta.X, delta.Y);
}
public void Rotate(float deltaX,float deltaY)
{
const float RotationSpeed = 0.8f;
float yawSign = GetUpDirection().Y < 0 ? -1.0f : 1.0f;
_rotation.Y += yawSign * deltaX * RotationSpeed;
_rotation.X += deltaY * RotationSpeed;
UpdateViewMatrix();
Yaw += deltaX * RotationSpeed;
Pitch += deltaY * RotationSpeed;
}
public Vector3 GetUpDirection()
{
return GetOrientation() * Up;
}
public Vector3 GetRightDirection()
{
return GetOrientation() * Vector3.UnitX;
}
public Vector3 GetForwardDirection()
{
return GetOrientation() * Orientation;
}
private Vector3 CalculatePosition()
{
Vector3 forwadDirection = GetForwardDirection();
return FocalPoint - forwadDirection * Distance;
}
private Quaternion GetOrientation()
{
return new Quaternion(new Vector3(-Rotation));
}
public override string ToString()
{
return $"FOV: {Fov}\nPosition: {WorldPosition}\nRotation: {Rotation}";
return $"FOV: {Fov}\nPosition: {WorldPosition}\nRotation: {_spherical}";
}
}
}

View File

@@ -44,7 +44,7 @@ namespace PckStudio.Rendering
// reToolStripMenuItem
//
this.reToolStripMenuItem.Name = "reToolStripMenuItem";
this.reToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.reToolStripMenuItem.Size = new System.Drawing.Size(158, 22);
this.reToolStripMenuItem.Text = "Re-Init";
this.reToolStripMenuItem.Click += new System.EventHandler(this.reInitToolStripMenuItem_Click);
//
@@ -54,25 +54,44 @@ namespace PckStudio.Rendering
this.reToolStripMenuItem,
this.guidelineModeToolStripMenuItem});
this.contextMenuStrip1.Name = "contextMenuStrip1";
this.contextMenuStrip1.Size = new System.Drawing.Size(181, 70);
this.contextMenuStrip1.Size = new System.Drawing.Size(159, 48);
//
// guidelineModeToolStripMenuItem
//
this.guidelineModeToolStripMenuItem.Name = "guidelineModeToolStripMenuItem";
this.guidelineModeToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.guidelineModeToolStripMenuItem.Size = new System.Drawing.Size(158, 22);
this.guidelineModeToolStripMenuItem.Text = "Guideline Mode";
this.guidelineModeToolStripMenuItem.Click += new System.EventHandler(this.guidelineModeToolStripMenuItem_Click);
#if DEBUG
//
// debugLabel
//
this.debugLabel = new System.Windows.Forms.Label();
this.debugLabel.AutoSize = true;
this.debugLabel.BackColor = System.Drawing.Color.Transparent;
this.debugLabel.ForeColor = System.Drawing.SystemColors.ControlLight;
this.debugLabel.Location = new System.Drawing.Point(3, 4);
this.debugLabel.Name = "debugLabel";
this.debugLabel.Size = new System.Drawing.Size(37, 13);
this.debugLabel.TabIndex = 2;
this.debugLabel.Text = "debug";
#endif
//
// SkinRenderer
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.Controls.Add(this.debugLabel);
this.Name = "SkinRenderer";
this.contextMenuStrip1.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.ToolStripMenuItem reToolStripMenuItem;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
private System.Windows.Forms.ToolStripMenuItem guidelineModeToolStripMenuItem;
#if DEBUG
private System.Windows.Forms.Label debugLabel;
#endif
}
}

View File

@@ -83,6 +83,8 @@ namespace PckStudio.Rendering
}
}
public float MouseSensetivity { get; set; } = 0.01f;
public bool ClampModel { get; set; } = false;
public bool ShowGuideLines
{
@@ -156,17 +158,6 @@ namespace PckStudio.Rendering
private GuidelineMode guidelineMode { get; set; } = GuidelineMode.None;
private Vector2 _globalModelRotation;
private Vector2 GlobalModelRotation
{
get => _globalModelRotation;
set
{
_globalModelRotation.X = MathHelper.Clamp(value.X, -60f, 60f);
_globalModelRotation.Y = value.Y % 360f;
}
}
public Size TextureSize { get; private set; } = new Size(64, 64);
public Vector2 TillingFactor => new Vector2(1f / TextureSize.Width, 1f / TextureSize.Height);
private const float OverlayScale = 1.12f;
@@ -699,15 +690,6 @@ namespace PckStudio.Rendering
Debug.Assert(error == ErrorCode.NoError, error.ToString());
}
private void ReleaseMouse()
{
if (IsMouseHidden)
{
IsMouseHidden = false;
Cursor.Position = PreviousMouseLocation;
}
}
private void OnANIMUpdate()
{
head.SetEnabled(0, !ANIM.GetFlag(SkinAnimFlag.HEAD_DISABLED));
@@ -770,9 +752,11 @@ namespace PckStudio.Rendering
showWireFrame = !showWireFrame;
return true;
case Keys.R:
GlobalModelRotation = Vector2.Zero;
Vector3 skinTransform = new Vector3(0f, 24f, 0f);
Camera.Distance = DefaultCameraDistance;
Camera.FocalPoint = Vector3.Zero;
Camera.FocalPoint = head.GetCenter(0) + skinTransform;
Camera.Yaw = 0f;
Camera.Pitch = 0f;
return true;
case Keys.A:
ReleaseMouse();
@@ -837,7 +821,7 @@ namespace PckStudio.Rendering
GL.Enable(EnableCap.DepthTest); // Enable correct Z Drawings
GL.Enable(EnableCap.LineSmooth);
Matrix4 viewProjection = Camera.GetViewProjection();
Matrix4 globalMatrix = Matrix4.CreateTranslation(0f, -24f, 0f);
Matrix4 globalMatrix = Matrix4.CreateTranslation(0f, 0f, 0f);
// Render Skybox
{
@@ -849,9 +833,9 @@ namespace PckStudio.Rendering
_skyboxTexture.Bind();
var view = new Matrix4(new Matrix3(Matrix4.LookAt(Camera.WorldPosition, Camera.WorldPosition + Camera.Orientation, Camera.Up)))
* Matrix4.CreateRotationY(MathHelper.DegreesToRadians(skyboxRotation));
var proj = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(Camera.Fov), AspectRatio, 1f, 1000f);
var viewproj = view * proj;
* Matrix4.CreateRotationY(MathHelper.DegreesToRadians(Camera.Yaw))
* Matrix4.CreateRotationX(MathHelper.DegreesToRadians(Camera.Pitch));
var viewproj = view * Camera.GetProjection();
skyboxShader.SetUniformMat4("ViewProjection", ref viewproj);
Renderer.Draw(skyboxShader, _skyboxRenderBuffer);
GL.DepthMask(true);
@@ -968,29 +952,36 @@ namespace PckStudio.Rendering
framebufferTexture.Unbind();
#endif
SwapBuffers();
#if DEBUG
debugLabel.Text = Camera.ToString();
#endif
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
// Rotate the model
if (e.Button == MouseButtons.Left)
float deltaX = (Cursor.Position.X - CurrentMouseLocation.X) * MouseSensetivity;
float deltaY = (Cursor.Position.Y - CurrentMouseLocation.Y) * MouseSensetivity;
switch (e.Button)
{
float deltaX = (Cursor.Position.X - CurrentMouseLocation.X) * 0.5f;
float deltaY = (Cursor.Position.Y - CurrentMouseLocation.Y) * 0.5f;
GlobalModelRotation += new Vector2(-deltaY, deltaX) * Camera.Distance * 0.015f;
Cursor.Position = new Point((int)Math.Round(Screen.PrimaryScreen.Bounds.Width / 2d), (int)Math.Round(Screen.PrimaryScreen.Bounds.Height / 2d));
CurrentMouseLocation = Cursor.Position;
return;
}
// Move the model
if (e.Button == MouseButtons.Right)
{
float deltaX = (Cursor.Position.X - CurrentMouseLocation.X) * 0.05f;
float deltaY = (Cursor.Position.Y - CurrentMouseLocation.Y) * 0.05f;
case MouseButtons.None:
case MouseButtons.Middle:
case MouseButtons.XButton1:
case MouseButtons.XButton2:
break;
case MouseButtons.Left:
Camera.Rotate(deltaX, deltaY);
goto default;
case MouseButtons.Right:
Camera.Pan(deltaX, deltaY);
goto default;
default:
Cursor.Position = new Point((int)Math.Round(Screen.PrimaryScreen.Bounds.Width / 2d), (int)Math.Round(Screen.PrimaryScreen.Bounds.Height / 2d));
CurrentMouseLocation = Cursor.Position;
break;
}
}
@@ -999,24 +990,29 @@ namespace PckStudio.Rendering
Camera.Distance -= e.Delta / System.Windows.Input.Mouse.MouseWheelDeltaForOneLine;
}
protected override void OnMouseUp(MouseEventArgs e)
{
ReleaseMouse();
base.OnMouseUp(e);
}
private void ReleaseMouse()
{
if (IsMouseHidden)
{
IsMouseHidden = false;
Cursor.Position = PreviousMouseLocation;
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button == MouseButtons.Right || e.Button == MouseButtons.Left)
{
if (!IsMouseHidden)
{
IsMouseHidden = true;
}
PreviousMouseLocation = Cursor.Position;
CurrentMouseLocation = Cursor.Position;
}
CurrentMouseLocation = PreviousMouseLocation = Cursor.Position;
}
protected override void OnMouseUp(MouseEventArgs e)
{
ReleaseMouse();
base.OnMouseUp(e);
}
private void RenderBodyPart(ShaderProgram shader, Matrix4 partMatrix, Matrix4 globalMatrix, params string[] additionalData)

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK;
namespace PckStudio.Rendering
{
internal struct Spherical
{
private Vector3 vector;
public Spherical()
{
vector = new Vector3();
}
/// <summary>
/// Radial distance
/// </summary>
public float Radius
{
get => vector.X;
set => vector.X = value;
}
/// <summary>
/// Polar angle
/// </summary>
public float Theta
{
get => vector.Y;
set => vector.Y = value;
}
/// <summary>
/// Azimuthal angle
/// </summary>
public float Phi
{
get => vector.Z;
set => vector.Z = value;
}
public Vector3 GetPosition()
{
float sineAzimuthal = (float)Math.Sin(MathHelper.DegreesToRadians(Phi));
float cosineAzimuthal = (float)Math.Cos(MathHelper.DegreesToRadians(Phi));
float sinePolar = (float)Math.Sin(MathHelper.DegreesToRadians(Theta));
float cosinePolar = (float)Math.Cos(MathHelper.DegreesToRadians(Theta));
Vector3 direction = new Vector3();
direction.X = cosinePolar * sineAzimuthal;
direction.Y = sineAzimuthal;
direction.Z = sinePolar * cosineAzimuthal;
return direction * Radius;
}
public override string ToString()
{
return $"Radius: {Radius}; Theta: {Theta}; Phi: {Phi}; Position: {GetPosition()}";
}
}
}