/// <summary>
        /// Gibt die am nächsten liegende (vom Mauszeiger überlagerte) GameObject-Instanz inkl. der genauen Mausposition auf dem Objekt zurück
        /// </summary>
        /// <typeparam name="T">Beliebige Unterklasse von GameObject</typeparam>
        /// <param name="ms">Aktueller Mausstatus</param>
        /// <param name="intersectionPoint">Punkt, an dem der Mauszeiger auf das Objekt trifft</param>
        /// <param name="offsetX">optionale Verschiebung des Cursors auf der X-Achse in Pixeln (Standard: 0)</param>
        /// <param name="offsetY">optionale Verschiebung des Cursors auf der X-Achse in Pixeln (Standard: 0)</param>
        /// <param name="precision">Präzision der Messung (Standard: Box für genauere Messung)</param>
        /// <returns>Die GameObject-Instanz, die der Kamera am nächsten ist</returns>
        public static T IsMouseCursorOnAny <T>(MouseState ms, out Vector3 intersectionPoint, int offsetX = 0, int offsetY = 0, MouseIntersectionPrecision precision = MouseIntersectionPrecision.Box) where T : GameObject
        {
            intersectionPoint = Vector3.Zero;
            GameObject[] list = KWEngine.CurrentWorld._gameObjects.FindAll(go => go is T).ToArray();
            if (list.Length == 0)
            {
                return(null);
            }

            Vector2 mc       = HelperGeneral.GetNormalizedMouseCoords(ms.X + offsetX, ms.Y + offsetY);
            Vector3 worldRay = KWEngine.CurrentWindow.Get3DMouseCoords(mc.X, mc.Y);
            Vector3 origin;

            if (KWEngine.CurrentWorld != null && KWEngine.CurrentWorld.IsFirstPersonMode)
            {
                if (KWEngine.Projection == ProjectionType.Perspective)
                {
                    origin    = KWEngine.CurrentWorld.GetFirstPersonObject().Position;
                    origin.Y += KWEngine.CurrentWorld.GetFirstPersonObject().FPSEyeOffset;
                }
                else
                {
                    origin = HelperGeneral.GetRayOriginForOrthographicProjection(mc);
                }
            }
            else
            {
                if (KWEngine.Projection == ProjectionType.Perspective)
                {
                    origin = KWEngine.CurrentWorld.GetCameraPosition();
                }
                else
                {
                    origin = HelperGeneral.GetRayOriginForOrthographicProjection(mc);
                }
            }

            float minDistance = float.MaxValue;
            int   minIndex    = -1;

            for (int i = 0; i < list.Length; i++)
            {
                bool rayHitGameObject = GetRayIntersectionPointOnHitbox(list[i], origin, worldRay, out Vector3 iPoint, precision);
                if (rayHitGameObject)
                {
                    float currentDistance = (origin - iPoint).LengthSquared;
                    if (currentDistance < minDistance)
                    {
                        intersectionPoint = iPoint;
                        minDistance       = currentDistance;
                        minIndex          = i;
                    }
                }
            }
            if (minIndex >= 0)
            {
                return(list[minIndex] as T);
            }
            return(null);
        }
        /// <summary>
        /// Gibt eine Liste von GameObject-Instanzen zurück, die unter dem Mauszeiger liegen (Instanzen müssen mit IsPickable = true gesetzt haben)
        /// </summary>
        /// <param name="ms">Mausinformationen</param>
        /// <param name="offsetX">optionale Verschiebung des Cursors auf der X-Achse in Pixeln (Standard: 0)</param>
        /// <param name="offsetY">optionale Verschiebung des Cursors auf der X-Achse in Pixeln (Standard: 0)</param>
        /// <returns>Liste betroffener GameObject-Instanzen</returns>
        public static List <GameObject> PickGameObjects(MouseState ms, int offsetX = 0, int offsetY = 0)
        {
            List <GameObject> pickedObjects = new List <GameObject>();
            GLWindow          w             = GLWindow.CurrentWindow;

            if (w == null || w.CurrentWorld == null || !w.Focused)
            {
                return(pickedObjects);
            }
            Vector2 mouseCoords = HelperGeneral.GetNormalizedMouseCoords(ms.X + offsetX, ms.Y + offsetY);
            Vector3 ray         = KWEngine.CurrentWindow.Get3DMouseCoords(mouseCoords.X, mouseCoords.Y);
            Vector3 pos;

            if (KWEngine.Projection == ProjectionType.Perspective)
            {
                pos = w.CurrentWorld.GetCameraPosition() + ray;
            }
            else
            {
                pos = HelperGeneral.GetRayOriginForOrthographicProjection(mouseCoords) + ray;
            }

            foreach (GameObject go in w.CurrentWorld.GetGameObjects())
            {
                if (go.IsPickable && go.IsInsideScreenSpace)
                {
                    if (GameObject.IntersectRaySphere(pos, ray, go.GetCenterPointForAllHitboxes(), go.GetMaxDiameter() / 2))
                    {
                        pickedObjects.Add(go);
                    }
                }
            }
            return(pickedObjects);
        }
        /// <summary>
        /// Konvertiert 2D-Mauskoordinaten in 3D-Koordinaten
        /// </summary>
        /// <param name="ms">Mausinformationen</param>
        /// <param name="planeNormal">Kollisionsebene (Standard: Camera)</param>
        /// <param name="planeHeight">Höhe der Kollisionsebene</param>
        /// <param name="offsetX">(optionale) Seitenkorrektur in Pixeln</param>
        /// <param name="offsetY">(optionale) Höhenkorrektur in Pixeln</param>
        /// <returns>3D-Mauskoordinaten</returns>
        public static Vector3 GetMouseIntersectionPoint(MouseState ms, Plane planeNormal, float planeHeight, int offsetX = 0, int offsetY = 0)
        {
            Vector3 normal;

            if (planeNormal == Plane.Y)
            {
                normal = new Vector3(0, 1, 0.000001f);
            }
            else if (planeNormal == Plane.X)
            {
                normal = new Vector3(1, 0, 0);
            }
            else if (planeNormal == Plane.Z)
            {
                normal = new Vector3(0, 0.000001f, 1);
            }
            else
            {
                if (KWEngine.CurrentWorld != null)
                {
                    normal = -KWEngine.CurrentWorld.GetCameraLookAtVector();
                }
                else
                {
                    normal = new Vector3(0, 1, 0.000001f);
                }
            }

            Vector2 mc       = HelperGeneral.GetNormalizedMouseCoords(ms.X + offsetX, ms.Y + offsetY);
            Vector3 worldRay = KWEngine.CurrentWindow.Get3DMouseCoords(mc.X, mc.Y);
            bool    result;
            Vector3 intersection;

            if (KWEngine.Projection == ProjectionType.Perspective)
            {
                result = GameObject.LinePlaneIntersection(out intersection, worldRay, KWEngine.CurrentWindow.CurrentWorld.GetCameraPosition(), normal, normal * planeHeight);
            }
            else
            {
                Vector3 rayOrigin = HelperGeneral.GetRayOriginForOrthographicProjection(mc);
                result = GameObject.LinePlaneIntersection(out intersection, worldRay, rayOrigin, normal, normal * planeHeight);
            }
            if (result)
            {
                return(intersection);
            }
            else
            {
                return(normal * planeHeight);
            }
        }
        /// <summary>
        /// Gibt das GameObject zurück, das unter dem Mauszeiger liegt (Instanzen müssen mit IsPickable = true gesetzt haben)
        /// </summary>
        /// <param name="ms">Mausinformationen</param>
        /// <param name="offsetX">optionale Verschiebung des Cursors auf der X-Achse in Pixeln (Standard: 0)</param>
        /// <param name="offsetY">optionale Verschiebung des Cursors auf der X-Achse in Pixeln (Standard: 0)</param>
        /// <returns>Gewähltes GameObject</returns>
        public static GameObject PickGameObject(MouseState ms, int offsetX = 0, int offsetY = 0)
        {
            GLWindow w = GLWindow.CurrentWindow;

            if (w == null || w.CurrentWorld == null || !w.Focused)
            {
                return(null);
            }
            Vector2 mouseCoords = HelperGeneral.GetNormalizedMouseCoords(ms.X + offsetX, ms.Y + offsetY);
            Vector3 ray         = KWEngine.CurrentWindow.Get3DMouseCoords(mouseCoords.X, mouseCoords.Y);
            Vector3 pos;

            if (KWEngine.Projection == ProjectionType.Perspective)
            {
                pos = w.CurrentWorld.GetCameraPosition() + ray;
            }
            else
            {
                pos = HelperGeneral.GetRayOriginForOrthographicProjection(mouseCoords) + ray;
            }

            GameObject pickedObject   = null;
            float      pickedDistance = float.MaxValue;

            foreach (GameObject go in w.CurrentWorld.GetGameObjects())
            {
                if (go.IsPickable && go.IsInsideScreenSpace)
                {
                    if (GameObject.IntersectRaySphere(pos, ray, go.GetCenterPointForAllHitboxes(), go.GetMaxDiameter() / 2))
                    {
                        float distance = (go.GetCenterPointForAllHitboxes() - pos).LengthSquared;
                        if (distance < pickedDistance)
                        {
                            pickedDistance = distance;
                            pickedObject   = go;
                        }
                    }
                }
            }

            return(pickedObject);
        }
        /// <summary>
        /// Erfragt, ob der Mauszeiger (näherungsweise) auf dem Objekt liegt
        /// </summary>
        /// <param name="g">Zu untersuchendes GameObject</param>
        /// <param name="ms">Mausinformationen</param>
        /// <param name="offsetX">optionale Verschiebung des Cursors auf der X-Achse in Pixeln (Standard: 0)</param>
        /// <param name="offsetY">optionale Verschiebung des Cursors auf der X-Achse in Pixeln (Standard: 0)</param>
        /// <param name="precision">Genauigkeit der Prüfung (Standard: Box für eine genauere Prüfung)</param>
        /// <returns>true, wenn der Mauszeiger auf dem Objekt liegt</returns>
        public static bool IsMouseCursorInsideHitbox(GameObject g, MouseState ms, int offsetX = 0, int offsetY = 0, MouseIntersectionPrecision precision = MouseIntersectionPrecision.Box)
        {
            Vector2 mc       = HelperGeneral.GetNormalizedMouseCoords(ms.X + offsetX, ms.Y + offsetY);
            Vector3 worldRay = KWEngine.CurrentWindow.Get3DMouseCoords(mc.X, mc.Y);
            Vector3 normal;
            bool    result;
            Vector3 intersection;

            if (KWEngine.CurrentWindow.CurrentWorld != null && KWEngine.CurrentWindow.CurrentWorld.IsFirstPersonMode)
            {
                normal    = HelperCamera.GetLookAtVector();
                normal.Y += 0.000001f;
                normal.Z += 0.000001f;
                if (KWEngine.Projection == ProjectionType.Perspective)
                {
                    Vector3 fpPos = KWEngine.CurrentWindow.CurrentWorld.GetFirstPersonObject().Position;
                    fpPos.Y += KWEngine.CurrentWindow.CurrentWorld.GetFirstPersonObject().FPSEyeOffset;
                    result   = GameObject.LinePlaneIntersection(out intersection, worldRay, fpPos, normal, g.GetCenterPointForAllHitboxes());
                }
                else
                {
                    Vector3 fpPos = HelperGeneral.GetRayOriginForOrthographicProjection(mc);
                    result = GameObject.LinePlaneIntersection(out intersection, worldRay, fpPos, normal, g.GetCenterPointForAllHitboxes());
                }
            }
            else
            {
                normal    = -KWEngine.CurrentWindow.CurrentWorld.GetCameraLookAtVector();
                normal.Y += 0.000001f;
                normal.Z += 0.000001f;

                if (KWEngine.Projection == ProjectionType.Perspective)
                {
                    result = GameObject.LinePlaneIntersection(out intersection, worldRay, KWEngine.CurrentWindow.CurrentWorld.GetCameraPosition(), normal, g.GetCenterPointForAllHitboxes());
                }
                else
                {
                    Vector3 rayOrigin = HelperGeneral.GetRayOriginForOrthographicProjection(mc);
                    result = GameObject.LinePlaneIntersection(out intersection, worldRay, rayOrigin, normal, g.GetCenterPointForAllHitboxes());
                }
            }

            if (result)
            {
                foreach (Hitbox hb in g.Hitboxes)
                {
                    if (precision == MouseIntersectionPrecision.Box)
                    {
                        if (IsPointInsideBox(ref intersection, hb))
                        {
                            return(true);
                        }
                    }
                    else
                    {
                        if (IsPointInsideSphere(ref intersection, hb.GetCenter(), hb.DiameterAveraged))
                        {
                            return(true);
                        }
                    }
                }
                return(false);
            }
            else
            {
                return(false);
            }
        }