// // Created by dawsh on 1/25/24. // #pragma once #include #include "Plane.h" #include "Shape.h" #include namespace J3ML::Geometry { /// A frustum can be set to one of the two common different forms. enum class FrustumType { Invalid = 0, /// Set the Frustum type to this value to define the orthographic projection formula. In orthographic projection, /// 3D images are projected onto a 2D plane essentially by flattening the object along one direction (the plane normal). /// The size of the projected images appear the same independent of their distance to the camera, and distant objects will /// not appear smaller. The shape of the Frustum is identical to an oriented bounding box (OBB). Orthographic, /// Set the Frustum type to this value to use the perspective projection formula. With perspective projection, the 2D /// image is formed by projecting 3D points towards a single point (the eye point/tip) of the Frustum, and computing the /// point of intersection of the line of the projection and the near plane of the Frustum. /// This corresponds to the optics in the real-world, and objects become smaller as they move to the distance. /// The shape of the Frustum is a rectangular pyramid capped from the tip. Perspective }; /// The Frustum class offers choosing between the two common conventions for the value ranges in /// post-projective space. If you are using either the OpenGL or Direct3D API, you must feed the API data that matches /// the correct convention. enum class FrustumProjectiveSpace { Invalid = 0, /// If this option is chosen, the post-projective unit cube of the Frustum /// is modelled after the OpenGL API convention, meaning that in projected space, /// points inside the Frustum have the X and Y range in [-1, 1] and Z ranges in [-1, 1], /// where the near plane maps to Z=-1 and the far plane maps to Z=1. /// @note If you are submitting projection matrices to GPU hardware using the OpenGL API, /// you **must** use this convention. (or otherwise more than half of the precision of the GL depth buffer is wasted) GL, /// If this option is chosen, the post-projective unit cube is modelled after the /// Direct3D API convention, which differs from the GL convention that Z ranges in [0, 1] instead. /// Near plane maps to Z=0, and far plane maps to Z=1. The X and Y ranges in [-1, 1] as is with GL. /// @note If you are submitting projection matrices to GPU hardware using the Direct3D API, /// you **must** use this convention. D3D, }; /// The handedness rule in J3ML bundles together two different conventions related to the camera: /// * the chirality of the world and view spaces, /// * the fixed local front direction of the Frustum. /// @note The world and view spaces are always assumed to the same chirality, meaning that Frustum::ViewMatrix() /// (and hence Frustum::WorldMatrix()) always returns a matrix with a positive determinant, i.e. it does not mirror. /// If FrustumRightHanded is chosen, then Frustum::ProjectionMatrix() is a mirroring matrix, since the post-projective space /// is always left-handed. /// @note Even though in the local space of the camera +Y is always up, in the world space one can use any 'world up' direction /// as one pleases, by orienting the camera via the Frustum::up vector. enum class FrustumHandedness { Invalid = 0, /// If a Frustum is left-handed, then in the local space of the Frustum (the view space), the camera looks towards +Z, /// while +Y goes towards up, and +X goes towards right. /// @note The fixed-pipeline D3D9 API traditionally used the FrustumLeftHanded convention. Left, /// If a Frustum is right-handed, then the camera looks towards -Z, +Y is up, and +X is right. /// @note The fixed-pipeline OpenGL API traditionally used the FrustumRightHanded convention. Right }; /// Represents either an orthographic or a perspective viewing frustum. class Frustum : public Shape { public: /// Members /// Specifies whether this frustum is a perspective or an orthographic frustum. FrustumType type; /// Specifies whether the [-1, 1] or [0, 1] range is used for the post-projective depth range. FrustumProjectiveSpace projectiveSpace; /// Specifies the chirality of world and view spaces FrustumHandedness handedness; /// The eye point of this frustum Vector3 pos; /// The normalized direction this frustum is watching towards. Vector3 front; /// The normalized up direction for this frustum. /// This vector is specified in world (global) space. This vector is always normalized. /// @note The vectors front and up must always be perpendicular to each other. This means that this up vector is not /// a static/constant up vector, e.g. (0, 1, 0), but changes according to when the camera pitches up and down to /// preserve the condition that front and up are always perpendicular /// @note In the _local_ space of the Frustum, the direction +y is _always_ the up direction and cannot be changed. This /// coincides to how Direct3D and OpenGL view and projection matrices are constructed Vector3 up; /// Distance from the eye point to the front plane /// This parameter must be positive. If perspective projection is used, this parameter must be strictly positive /// (0 is not allowed). If orthographic projection is used, 0 is possible (but uncommon, and not recommended). /// When using the Frustum class to derive perspective projection matrices for a GPU, it should be noted that too /// small values cause poor resolution of Z values near the back plane in post-perspective space, if non-linear /// depth is used (which is common). The larger this value is, the more resolution there is for the Z values across the /// depth range. Too large values cause clipping of geometry when they come very near the camera. */ float nearPlaneDistance; /// Distance from the eye point to the back plane of the projection matrix. /// This parameter must be strictly greater than nearPlaneDistance. The range [nearPlaneDistance, farPlaneDistance] // specifies the visible range of objects inside the Frustum. When using the Frustum class for deriving perspective // projection matrix for GPU rendering, it should be remembered that any geometry farther from the camera (in Z value) // than this distance will be clipped from the view, and not rendered. float farPlaneDistance; union { /// Horizontal field-of-view, in radians. This field is only valid if type == PerspectiveFrustum. /** @see type. */ float horizontalFov; /// The width of the orthographic frustum. This field is only valid if type == OrthographicFrustum. /** @see type. */ float orthographicWidth; }; union { /// Vertical field-of-view, in radians. This field is only valid if type == PerspectiveFrustum. /** @see type. */ float verticalFov; /// The height of the orthographic frustum. This field is only valid if type == OrthographicFrustum. /** @see type. */ float orthographicHeight; }; void WorldMatrixChanged(); void ProjectionMatrixChanged(); /// Frustums are typically used in batch culling operations. /// Therefore the matrices associated with a frustum are cached for immediate access. Matrix4x4 worldMatrix; Matrix4x4 projectionMatrix; Matrix4x4 viewProjectionMatrix; public: /// Methods Frustum(); static Frustum CreateFrustumFromCamera(const CoordinateFrame& cam, float aspect, float fovY, float zNear, float zFar); AABB MinimalEnclosingAABB() const; OBB MinimalEnclosingOBB() const; void SetKind(FrustumProjectiveSpace projectiveSpace, FrustumHandedness handedness); void SetViewPlaneDistances(float nearPlaneDistance, float farPlaneDistance); void SetFrame(const Vector3& pos, const Vector3& front, const Vector3& up); void SetPos(const Vector3& pos); void SetFront(const Vector3& front); void SetUp(const Vector3& up); void SetPerspective(float horizontalFov, float verticalFov); void SetOrthographic(float orthographicWidth, float orthographicHeight); FrustumHandedness Handedness() const { return handedness; } FrustumType Type() const { return type; } FrustumProjectiveSpace ProjectiveSpace() const { return projectiveSpace;} const Vector3 &Pos() const {return pos;} const Vector3 &Front() const { return front; } const Vector3 &Up() const { return up; } float NearPlaneDistance() const { return nearPlaneDistance; } float FarPlaneDistance() const { return farPlaneDistance;} float HorizontalFov() const { return horizontalFov;} float VerticalFov() const { return verticalFov;} float OrthographicWidth() const { return orthographicWidth; } float OrthograhpicHeight() const { return orthographicHeight; } int NumEdges() const { return 12; } float AspectRatio() const; void SetHorizontalFovAndAspectRatio(float horizontalFov, float aspectRatio); Vector3 CornerPoint(int cornerIndex) const; Vector3 NearPlanePos(float x, float y) const; Vector3 FarPlanePos(float x, float y) const; Vector3 WorldRight() const; Plane TopPlane() const; Plane BottomPlane() const; Plane RightPlane() const; Plane LeftPlane() const; Plane FarPlane() const; Plane NearPlane() const; float NearPlaneWidth() const; float NearPlaneHeight() const; void Translate(const Vector3& offset); void Transform(const Matrix3x3& transform); void Transform(const Matrix4x4& transform); void Transform(const Quaternion& transform); Polyhedron ToPolyhedron() const; bool Intersects(const Ray& ray) const; //bool Intersects(const Line& line) const; bool Intersects(const LineSegment& lineSegment) const; bool Intersects(const AABB& aabb) const; bool Intersects(const OBB& obb) const; bool Intersects(const Plane& plane) const; bool Intersects(const Triangle& triangle) const; bool Intersects(const Polygon& lineSegment) const; bool Intersects(const Sphere& aabb) const; bool Intersects(const Capsule& obb) const; bool Intersects(const Frustum& plane) const; bool Intersects(const Polyhedron& triangle) const; }; }