// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. // ----------------------------------------------------------------------------- // Original code from SharpDX project. https://github.com/sharpdx/SharpDX/ // Greetings to Alexandre Mutel. Original code published with the following license: // ----------------------------------------------------------------------------- // Copyright (c) 2010-2014 SharpDX - Alexandre Mutel // // 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.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace FlaxEngine { /// /// Defines the viewport dimensions using float coordinates for (X,Y,Width,Height). /// [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct Viewport : IEquatable { /// /// Position of the pixel coordinate of the upper-left corner of the viewport. /// public float X; /// /// Position of the pixel coordinate of the upper-left corner of the viewport. /// public float Y; /// /// Width dimension of the viewport. /// public float Width; /// /// Height dimension of the viewport. /// public float Height; /// /// Gets or sets the minimum depth of the clip volume. /// public float MinDepth; /// /// Gets or sets the maximum depth of the clip volume. /// public float MaxDepth; /// /// Initializes a new instance of the struct. /// /// The x coordinate of the upper-left corner of the viewport in pixels. /// The y coordinate of the upper-left corner of the viewport in pixels. /// The width of the viewport in pixels. /// The height of the viewport in pixels. public Viewport(float x, float y, float width, float height) { X = x; Y = y; Width = width; Height = height; MinDepth = 0f; MaxDepth = 1f; } /// /// Initializes a new instance of the struct. /// /// The x coordinate of the upper-left corner of the viewport in pixels. /// The y coordinate of the upper-left corner of the viewport in pixels. /// The width of the viewport in pixels. /// The height of the viewport in pixels. /// The minimum depth of the clip volume. /// The maximum depth of the clip volume. public Viewport(float x, float y, float width, float height, float minDepth, float maxDepth) { X = x; Y = y; Width = width; Height = height; MinDepth = minDepth; MaxDepth = maxDepth; } /// /// Initializes a new instance of the struct. /// /// A bounding box that defines the location and size of the viewport in a render target. public Viewport(Rectangle bounds) { X = bounds.X; Y = bounds.Y; Width = bounds.Width; Height = bounds.Height; MinDepth = 0f; MaxDepth = 1f; } /// /// Initializes a new instance of the struct. /// /// The location of the upper-left corner of the viewport in pixels. /// The size of the viewport in pixels. public Viewport(Float2 location, Float2 size) { X = location.X; Y = location.Y; Width = size.X; Height = size.Y; MinDepth = 0f; MaxDepth = 1f; } /// /// Gets the size of the viewport. /// /// The bounds. public Rectangle Bounds { get => new Rectangle(X, Y, Width, Height); set { X = value.X; Y = value.Y; Width = value.Width; Height = value.Height; } } /// /// Gets or sets the size of the viewport (width and height). /// public Float2 Size { get => new Float2(Width, Height); set { Width = value.X; Height = value.Y; } } /// /// Gets the aspect ratio used by the viewport. /// public float AspectRatio => !Mathf.IsZero(Height) ? Width / Height : 0f; /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public bool Equals(ref Viewport other) { return Mathf.NearEqual(X, other.X) && Mathf.NearEqual(Y, other.Y) && Mathf.NearEqual(Width, other.Width) && Mathf.NearEqual(Height, other.Height) && Mathf.NearEqual(MinDepth, other.MinDepth) && Mathf.NearEqual(MaxDepth, other.MaxDepth); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Viewport other) { return Equals(ref other); } /// /// Determines whether the specified object is equal to this instance. /// /// The object to compare with this instance. /// true if the specified object is equal to this instance; otherwise, false. public override bool Equals(object obj) { return obj is Viewport other && Equals(ref other); } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { unchecked { int hashCode = X.GetHashCode(); hashCode = (hashCode * 397) ^ Y.GetHashCode(); hashCode = (hashCode * 397) ^ Width.GetHashCode(); hashCode = (hashCode * 397) ^ Height.GetHashCode(); hashCode = (hashCode * 397) ^ MinDepth.GetHashCode(); hashCode = (hashCode * 397) ^ MaxDepth.GetHashCode(); return hashCode; } } /// /// Implements the operator ==. /// /// The left. /// The right. /// The result of the operator. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Viewport left, Viewport right) { return left.Equals(ref right); } /// /// Implements the operator !=. /// /// The left. /// The right. /// The result of the operator. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Viewport left, Viewport right) { return !left.Equals(ref right); } /// /// Retrieves a string representation of this object. /// /// A that represents this instance. public override string ToString() { return string.Format(CultureInfo.CurrentCulture, "{{X:{0} Y:{1} Width:{2} Height:{3} MinDepth:{4} MaxDepth:{5}}}", X, Y, Width, Height, MinDepth, MaxDepth); } /// /// Projects a 3D vector from object space into screen space. /// /// The vector to project. /// The projection matrix. /// The view matrix. /// The world matrix. /// The projected vector. public Vector3 Project(Vector3 source, Matrix projection, Matrix view, Matrix world) { Matrix.Multiply(ref world, ref view, out Matrix matrix); Matrix.Multiply(ref matrix, ref projection, out matrix); Project(ref source, ref matrix, out Vector3 vector); return vector; } /// /// Projects a 3D vector from object space into screen space. /// /// The vector to project. /// A combined WorldViewProjection matrix. /// The projected vector. public void Project(ref Vector3 source, ref Matrix matrix, out Vector3 vector) { Vector3.Transform(ref source, ref matrix, out vector); var w = source.X * matrix.M14 + source.Y * matrix.M24 + source.Z * matrix.M34 + matrix.M44; if (!Mathf.IsZero(w)) { vector /= w; } vector.X = (vector.X + 1f) * 0.5f * Width + X; vector.Y = (-vector.Y + 1f) * 0.5f * Height + Y; vector.Z = vector.Z * (MaxDepth - MinDepth) + MinDepth; } /// /// Converts a screen space point into a corresponding point in world space. /// /// The vector to project. /// The projection matrix. /// The view matrix. /// The world matrix. /// The unprojected Vector. public Vector3 Unproject(Vector3 source, Matrix projection, Matrix view, Matrix world) { Matrix.Multiply(ref world, ref view, out Matrix matrix); Matrix.Multiply(ref matrix, ref projection, out matrix); Matrix.Invert(ref matrix, out matrix); Unproject(ref source, ref matrix, out Vector3 vector); return vector; } /// /// Converts a screen space point into a corresponding point in world space. /// /// The vector to project. /// An inverted combined WorldViewProjection matrix. /// The unprojected vector. public void Unproject(ref Vector3 source, ref Matrix matrix, out Vector3 vector) { vector.X = (source.X - X) / Width * 2f - 1f; vector.Y = -((source.Y - Y) / Height * 2f - 1f); vector.Z = (source.Z - MinDepth) / (MaxDepth - MinDepth); var w = vector.X * matrix.M14 + vector.Y * matrix.M24 + vector.Z * matrix.M34 + matrix.M44; Vector3.Transform(ref vector, ref matrix, out vector); if (!Mathf.IsZero(w)) { vector /= w; } } } }