/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #pragma once // include required headers #include "StandardHeaders.h" #include "Vector.h" #include "AABB.h" #include <AzCore/std/containers/vector.h> namespace MCore { /** * The Plane Equation template. * This represents an infinite plane, which is mathematically represented by the following equation: * Ax + By + Cz + d = 0. Where ABC is the XYZ of the planes normal and where xyz is a point on the plane. * The value d is a constant value, which is precalculated. Now if we put a random point inside this equation, when we * already know the normal and the value for d, we can see if the result of Ax + By + Cz + d is 0 or not. * If it is 0, this means the point is on the plane. We can also use this to calculate on what side of the plane a point is. * This is for example used for constructing BSP trees. * So, from this we can conclude that the result of (normal.Dot(point) + d) is the distance from point to the plane, along the planes normal. */ class MCORE_API PlaneEq { public: /** * Axis aligned planes. * Used to determine the most dominant axis, which can be used for planar mapping. * This is for example used to generate the texture coordinates for lightmaps. */ enum EPlane { PLANE_XY = 0, /**< The XY plane, so where Z is constant. */ PLANE_XZ = 1, /**< The XZ plane, so where Y is constant. */ PLANE_YZ = 2 /**< The YZ plane, so where X is constant. */ }; /** * The default constructor. Does not initialize anything. * So this does not result in a usable/valid plane. */ MCORE_INLINE PlaneEq() {} /** * Constructor when you know the normal of the plane and a point on the plane. * @param norm The normal of the plane. * @param pnt A point on the plane. */ MCORE_INLINE PlaneEq(const AZ::Vector3& norm, const AZ::Vector3& pnt) : mNormal(norm) , mDist(-((norm.GetX() * pnt.GetX()) + (norm.GetY() * pnt.GetY()) + (norm.GetZ() * pnt.GetZ()))) {} /** * Constructor when you know the normal and the value of d out of the plane equation (Ax + By + Cz + d = 0) * @param norm The normal of the plane * @param d The value of 'd' out of the plane equation. */ MCORE_INLINE PlaneEq(const AZ::Vector3& norm, float d) : mNormal(norm) , mDist(d) {} /** * Constructor when you know 3 points on the plane (the winding matters here (clockwise vs counter-clockwise) * The normal will be calculated as ((v2-v1).Cross(v3-v1)).Normalize(). * @param v1 The first point on the plane. * @param v2 The second point on the plane. * @param v3 The third point on the plane. */ MCORE_INLINE PlaneEq(const AZ::Vector3& v1, const AZ::Vector3& v2, const AZ::Vector3& v3) : mNormal((v2 - v1).Cross(v3 - v1).GetNormalized()) , mDist(-(mNormal.Dot(v1))) {} /** * Calculates and returns the dominant plane. * A dominant plane is an axis aligned plane, so it can be 3 types of planes, one for each axis. * The way this is calculated is by looking at the normal of the plane, and looking which axis of the normal is the most dominant. * Based on this, corresponding axis aligned plane, can be returned. * @result Returns the type of axis aligned plane. This can be aligned to the XY, XZ or YZ plane. See EPlane for the plane types. */ MCORE_INLINE EPlane CalcDominantPlane() const { return (Math::Abs(mNormal.GetY()) > Math::Abs(mNormal.GetX()) ? (Math::Abs(mNormal.GetZ()) > Math::Abs(mNormal.GetY()) ? PLANE_XY : PLANE_XZ) : (Math::Abs(mNormal.GetZ()) > Math::Abs(mNormal.GetX()) ? PLANE_XY : PLANE_YZ)); } /** * Calculates the distance of a given point to the plane, along the normal. * A mathematical explanation of how this is done can be read in the description of this template/class. * @param v The vector representing the 3D point to use for the calculation. * @result The distance from 'v' to this plane, along the normal of this plane. */ MCORE_INLINE float CalcDistanceTo(const AZ::Vector3& v) const { return mNormal.Dot(v) + mDist; } /** * Construct the plane when the normal of the plane and a point on the plane are known. * @param normal The normal of the plane. * @param pointOnPlane A point on the plane. */ MCORE_INLINE void Construct(const AZ::Vector3& normal, const AZ::Vector3& pointOnPlane) { mNormal = normal; mDist = -((normal.GetX() * pointOnPlane.GetX()) + (normal.GetY() * pointOnPlane.GetY()) + (normal.GetZ() * pointOnPlane.GetZ())); } /** * Construct the plane when the normal of the plane is known, as well as the value of 'd' in the plane equation (Ax + By + Cz + d = 0) * @param normal The normal of the plane. * @param d The value of 'd' in the above mentioned plane equation. */ MCORE_INLINE void Construct(const AZ::Vector3& normal, float d) { mNormal = normal; mDist = d; } /** * Construct the plane when you know three points on the plane. The winding of the vertices matters (clockwise vs counter-clockwise). * The normal is calculated as ((v2-v1).Cross(v3-v1)).Normalize() * @param v1 The first point on the plane. * @param v2 The second point on the plane. * @param v3 The third point on the plane. */ MCORE_INLINE void Construct(const AZ::Vector3& v1, const AZ::Vector3& v2, const AZ::Vector3& v3) { mNormal = (v2 - v1).Cross(v3 - v1).GetNormalized(); mDist = -(mNormal.Dot(v1)); } /** * Get the normal of the plane. * @result Returns the normal of the plane. */ MCORE_INLINE const AZ::Vector3& GetNormal() const { return mNormal; } /** * Get the 'd' out of the plane equation (Ax + By + Cz + d = 0). * @result Returns the 'd' from the plane equation. */ MCORE_INLINE float GetDist() const { return mDist; } /** * Checks if a given axis aligned bounding box (AABB) is partially above (aka in front) this plane or not. * The Frustum class uses this method to check if a box is partially inside a the frustum or not. * @param box The axis aligned bounding box to perform the test with. * @result Returns true when 'box' is partially (or completely) above the plane or not. */ MCORE_INLINE bool PartiallyAbove(const AABB& box) const; /** * Check if a given axis aligned bounding box (AABB) is completely above (aka in front) this plane or not. * The Frustum class uses this method to check if a box is completely inside a the frustum or not. * @param box The axis aligned bounding box to perform the test with. * @result Returns true when 'box' is completely above the plane or not. */ MCORE_INLINE bool CompletelyAbove(const AABB& box) const; /** * Clips a set of 3D points to this plane. * Actually these are not just points, but edges. The edges go from point 0 to 1, from 1 to 2, etc. * Beware that the clipped number of points can be higher as the ones you input to this method. * This method can be used to pretty easily clip polygon data against the plane. * @param pointsIn The array of points (and edges) to be clipped to the planes. * @param pointsOut The array of clipped points (and edges). Note that (pointsOut.GetLength() > pointsIn.GetLength()) can be true. * @result Returns true when the points have been clipped. False is returned when the clipping resulted in 0 output points. */ bool Clip(const AZStd::vector<AZ::Vector3>& pointsIn, AZStd::vector<AZ::Vector3>& pointsOut) const; /** * Clip a set of 3D points to this plane. * Actually these are not just points, but edges. The edges go from point 0 to 1, from 1 to 2, etc. * Beware that the clipped number of points can be higher as the ones you input to this method. * This method can be used to pretty easily clip polygon data against the plane. * @param points The set of points (or edges) to clip. When done, points contains the clipped points. * @result Returns true when the points have been clipped. False is returned when the clipping resulted in 0 points. In that last case 'points' won't be effected and contains just the original input points. */ bool Clip(AZStd::vector<AZ::Vector3>& points) const; /** * Project a vector onto the plane. * @param vectorToProject The vector you wish to project onto the plane. * @result The projected vector. */ MCORE_INLINE AZ::Vector3 Project(const AZ::Vector3& vectorToProject) { return vectorToProject - vectorToProject.Dot(mNormal) * mNormal; } private: AZ::Vector3 mNormal; /**< The normal of the plane. */ float mDist; /**< The D in the plane equation (Ax + By + Cz + D = 0). */ }; // include the inline code #include "PlaneEq.inl" } // namespace MCore