/// Copyright (c) 2021 Iiro Iivanainen, Harri Linna, Jere Pakkanen, Riikka Vilavaara /// /// Permission is hereby granted, free of charge, to any person obtaining /// a copy of this software and associated documentation files (the /// "Software"), to deal in the Software without restriction, including /// without limitation the rights to use, copy, modify, merge, publish, /// distribute, sublicense, and/or sell copies of the Software, and to /// permit persons to whom the Software is furnished to do so, subject to /// the following conditions: /// /// The above copyright notice and this permission notice shall be included /// in all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, /// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF /// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. /// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY /// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, /// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE /// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Numerics; using SharpGL; using SharpGL.WPF; using SharpGL.SceneGraph; using SharpGL.SceneGraph.Assets; namespace WPFOpenGL { public class OrbitCamera { // Orbit camera settings private Vector3 cameraPosition; private Vector3 cameraDirection; private Vector3 orbitTarget; private Vector3 upDirection = Vector3.UnitY; // Perspective projection settings private float imageWidth; private float imageHeight { get; set; } private float fieldOfView = 45.0f; private float near = 0.1f; private float far = 100.0f; // Mouse sensitivity settings private float orbitTargetSensitivity = 1E-1f; private float rotationSpeed = -2f; private float movementSpeed = 1E-1f; private float scrollSpeed = 1E-2f; public OrbitCamera(Vector3 cameraPosition, Vector3 cameraDirection) { this.cameraPosition = cameraPosition; this.cameraDirection = cameraDirection; this.orbitTarget = Vector3.Zero; } public Vector3 GetPosition() { return cameraPosition; } public Vector3 RightDirection() { return Vector3.Normalize(Vector3.Cross(cameraDirection, upDirection)); } public Vector3 UpDirection() { return Vector3.Normalize(Vector3.Cross(RightDirection(), cameraDirection)); } public void LookAt(OpenGL gl) { gl.LookAt( // camera position cameraPosition.X, cameraPosition.Y, cameraPosition.Z, // target position cameraPosition.X + cameraDirection.X, cameraPosition.Y + cameraDirection.Y, cameraPosition.Z + cameraDirection.Z, // camera orientation as up vector upDirection.X, upDirection.Y, upDirection.Z); } public void Persepective(OpenGL openGL) { imageWidth = (float)openGL.RenderContextProvider.Width; imageHeight = (float)openGL.RenderContextProvider.Height; openGL.MatrixMode(OpenGL.GL_PROJECTION); openGL.LoadIdentity(); openGL.Perspective(fieldOfView, imageWidth / imageHeight, near, far); openGL.MatrixMode(OpenGL.GL_MODELVIEW); } public Vector3 ScreenToRay(Vector2 screenPixel, Vector3 eulerAngle) { float imageAspectRatio = imageWidth / (float)imageHeight; float x = (1f - 2f * ((screenPixel.X) / (float)imageWidth)); float y = (1f - 2f * ((screenPixel.Y) / (float)imageHeight)); float mx = x * (MathF.Tan(MathHelper.DegreesToRadians( fieldOfView / 2f )) * imageAspectRatio); float my = y * (MathF.Tan(MathHelper.DegreesToRadians( fieldOfView / 2f ))); Vector3 dx = -mx * RightDirection(); Vector3 dy = my * UpDirection(); Vector3 rayDirection = Vector3.Normalize((cameraDirection + (dx + dy))); rayDirection = MathHelper.RotateX(rayDirection, MathHelper.DegreesToRadians(-eulerAngle.X)); rayDirection = MathHelper.RotateY(rayDirection, MathHelper.DegreesToRadians(-eulerAngle.Y)); rayDirection = MathHelper.RotateZ(rayDirection, MathHelper.DegreesToRadians(-eulerAngle.Z)); return Vector3.Normalize(rayDirection); } public void ChangeOrbitTarget(float deltaX, float deltaY) { orbitTarget += deltaX * RightDirection() * orbitTargetSensitivity; orbitTarget -= deltaY * UpDirection() * orbitTargetSensitivity; cameraDirection = Vector3.Normalize(orbitTarget - cameraPosition); } public void RotateOrbitCamera(float deltaX, float deltaY) { cameraDirection = orbitTarget - cameraPosition; float Ry = MathHelper.DegreesToRadians(deltaY * rotationSpeed); float Rx = MathHelper.DegreesToRadians(deltaX * rotationSpeed); Vector3 newDirection = cameraDirection; newDirection = MathHelper.RotateX(newDirection, Ry); newDirection = MathHelper.RotateY(newDirection, Rx); cameraPosition = orbitTarget - newDirection; cameraDirection = Vector3.Normalize(newDirection); } public void AdjustOrbitCamera(float deltaX, float deltaY) { cameraPosition += deltaX * movementSpeed * RightDirection(); cameraPosition -= deltaY * movementSpeed * UpDirection(); orbitTarget += deltaX * movementSpeed * RightDirection(); orbitTarget -= deltaY * movementSpeed * UpDirection(); } public void ZoomOrbitCamera(float deltaZ) { Vector3 newPosition = cameraPosition + cameraDirection * deltaZ * scrollSpeed; float distance = (orbitTarget - newPosition).Length(); if (near < distance && distance < far) { cameraPosition = newPosition; cameraDirection = Vector3.Normalize(orbitTarget - cameraPosition); } } } }