mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/PCK-Studio.git
synced 2026-05-23 14:47:26 +00:00
PerspectiveCamera - Add orbital controls
This commit is contained in:
@@ -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" />
|
||||
|
||||
@@ -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}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
25
PCK-Studio/Rendering/SkinRenderer.Designer.cs
generated
25
PCK-Studio/Rendering/SkinRenderer.Designer.cs
generated
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
65
PCK-Studio/Rendering/Spherical.cs
Normal file
65
PCK-Studio/Rendering/Spherical.cs
Normal 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()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user