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()}"; + } + } +}