From f8a74fab2a864e997ff4643e0cd97a9a30d64018 Mon Sep 17 00:00:00 2001
From: miku-666 <74728189+NessieHax@users.noreply.github.com>
Date: Sun, 25 Feb 2024 00:28:40 +0100
Subject: [PATCH] PerspectiveCamera - Add orbital controls
---
PCK-Studio/PckStudio.csproj | 1 +
.../Rendering/Camera/PerspectiveCamera.cs | 134 ++++++++++--------
PCK-Studio/Rendering/SkinRenderer.Designer.cs | 25 +++-
PCK-Studio/Rendering/SkinRenderer.cs | 98 ++++++-------
PCK-Studio/Rendering/Spherical.cs | 65 +++++++++
5 files changed, 207 insertions(+), 116 deletions(-)
create mode 100644 PCK-Studio/Rendering/Spherical.cs
diff --git a/PCK-Studio/PckStudio.csproj b/PCK-Studio/PckStudio.csproj
index c64e6a28..0a7d1e8c 100644
--- a/PCK-Studio/PckStudio.csproj
+++ b/PCK-Studio/PckStudio.csproj
@@ -172,6 +172,7 @@
SkinRenderer.cs
+
diff --git a/PCK-Studio/Rendering/Camera/PerspectiveCamera.cs b/PCK-Studio/Rendering/Camera/PerspectiveCamera.cs
index 6df8e3f8..025b8f58 100644
--- a/PCK-Studio/Rendering/Camera/PerspectiveCamera.cs
+++ b/PCK-Studio/Rendering/Camera/PerspectiveCamera.cs
@@ -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}";
}
-
}
}
diff --git a/PCK-Studio/Rendering/SkinRenderer.Designer.cs b/PCK-Studio/Rendering/SkinRenderer.Designer.cs
index ca88f97e..bebd7919 100644
--- a/PCK-Studio/Rendering/SkinRenderer.Designer.cs
+++ b/PCK-Studio/Rendering/SkinRenderer.Designer.cs
@@ -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
}
}
diff --git a/PCK-Studio/Rendering/SkinRenderer.cs b/PCK-Studio/Rendering/SkinRenderer.cs
index 0744bb5f..c6a70f34 100644
--- a/PCK-Studio/Rendering/SkinRenderer.cs
+++ b/PCK-Studio/Rendering/SkinRenderer.cs
@@ -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)
diff --git a/PCK-Studio/Rendering/Spherical.cs b/PCK-Studio/Rendering/Spherical.cs
new file mode 100644
index 00000000..fc4cb255
--- /dev/null
+++ b/PCK-Studio/Rendering/Spherical.cs
@@ -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();
+ }
+
+ ///
+ /// Radial distance
+ ///
+ public float Radius
+ {
+ get => vector.X;
+ set => vector.X = value;
+ }
+
+ ///
+ /// Polar angle
+ ///
+ public float Theta
+ {
+ get => vector.Y;
+ set => vector.Y = value;
+ }
+
+ ///
+ /// Azimuthal angle
+ ///
+ 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()}";
+ }
+ }
+}