//-------------------------------------------------------------------------------------
// DirectXCollision.h -- C++ Collision Math library
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//  
// Copyright (c) Microsoft Corporation. All rights reserved.
//-------------------------------------------------------------------------------------

#ifdef _MSC_VER
#pragma once
#endif

#include "DirectXMath.h"

namespace DirectX
{

enum ContainmentType
{
    DISJOINT = 0,
    INTERSECTS = 1,
    CONTAINS = 2,
};

enum PlaneIntersectionType
{
    FRONT = 0,
    INTERSECTING = 1,
    BACK = 2,
};

struct BoundingBox;
struct BoundingOrientedBox;
struct BoundingFrustum;

#pragma warning(push)
#pragma warning(disable:4324 4820)

//-------------------------------------------------------------------------------------
// Bounding sphere
//-------------------------------------------------------------------------------------
struct BoundingSphere
{
    XMFLOAT3 Center;            // Center of the sphere.
    float Radius;               // Radius of the sphere.

    // Creators
    BoundingSphere() : Center(0,0,0), Radius( 1.f ) {}
    BoundingSphere( _In_ const XMFLOAT3& center, _In_ float radius )
        : Center(center), Radius(radius) { assert( radius >= 0.f ); };
    BoundingSphere( _In_ const BoundingSphere& sp )
        : Center(sp.Center), Radius(sp.Radius) {}

    // Methods
    BoundingSphere& operator=( _In_ const BoundingSphere& sp ) { Center = sp.Center; Radius = sp.Radius; return *this; }

    void Transform( _Out_ BoundingSphere& Out, _In_ CXMMATRIX M ) const;
    void Transform( _Out_ BoundingSphere& Out, _In_ float Scale, _In_ FXMVECTOR Rotation, _In_ FXMVECTOR Translation ) const;
        // Transform the sphere

    ContainmentType Contains( _In_ FXMVECTOR Point ) const;
    ContainmentType Contains( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2 ) const;
    ContainmentType Contains( _In_ const BoundingSphere& sh ) const;
    ContainmentType Contains( _In_ const BoundingBox& box ) const;
    ContainmentType Contains( _In_ const BoundingOrientedBox& box ) const;
    ContainmentType Contains( _In_ const BoundingFrustum& fr ) const;

    bool Intersects( _In_ const BoundingSphere& sh ) const;
    bool Intersects( _In_ const BoundingBox& box ) const;
    bool Intersects( _In_ const BoundingOrientedBox& box ) const;
    bool Intersects( _In_ const BoundingFrustum& fr ) const;
    
    bool Intersects( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2 ) const;
        // Triangle-sphere test

    PlaneIntersectionType Intersects( _In_ FXMVECTOR Plane ) const;
        // Plane-sphere test
    
    bool Intersects( _In_ FXMVECTOR Origin, _In_ FXMVECTOR Direction, _Out_ float& Dist ) const;
        // Ray-sphere test

    ContainmentType ContainedBy( _In_ FXMVECTOR Plane0, _In_ FXMVECTOR Plane1, _In_ FXMVECTOR Plane2,
                                 _In_ GXMVECTOR Plane3, _In_ CXMVECTOR Plane4, _In_ CXMVECTOR Plane5 ) const;
        // Test sphere against six planes (see BoundingFrustum::GetPlanes)

    // Static methods
    static void CreateMerged( _Out_ BoundingSphere& Out, _In_ const BoundingSphere& S1, _In_ const BoundingSphere& S2 );

    static void CreateFromBoundingBox( _Out_ BoundingSphere& Out, _In_ const BoundingBox& box );
    static void CreateFromBoundingBox( _Out_ BoundingSphere& Out, _In_ const BoundingOrientedBox& box );

    static void CreateFromPoints( _Out_ BoundingSphere& Out, _In_ size_t Count,
                                  _In_reads_bytes_(sizeof(XMFLOAT3)+Stride*(Count-1)) const XMFLOAT3* pPoints, _In_ size_t Stride );

    static void CreateFromFrustum( _Out_ BoundingSphere& Out, _In_ const BoundingFrustum& fr );
};

//-------------------------------------------------------------------------------------
// Axis-aligned bounding box
//-------------------------------------------------------------------------------------
struct BoundingBox
{
    static const size_t CORNER_COUNT = 8;

    XMFLOAT3 Center;            // Center of the box.
    XMFLOAT3 Extents;           // Distance from the center to each side.

    // Creators
    BoundingBox() : Center(0,0,0), Extents( 1.f, 1.f, 1.f ) {}
    BoundingBox( _In_ const XMFLOAT3& center, _In_ const XMFLOAT3& extents )
        : Center(center), Extents(extents) { assert(extents.x >= 0 && extents.y >= 0 && extents.z >= 0); }
    BoundingBox( _In_ const BoundingBox& box ) : Center(box.Center), Extents(box.Extents) {}
    
    // Methods
    BoundingBox& operator=( _In_ const BoundingBox& box) { Center = box.Center; Extents = box.Extents; return *this; }

    void Transform( _Out_ BoundingBox& Out, _In_ CXMMATRIX M ) const;
    void Transform( _Out_ BoundingBox& Out, _In_ float Scale, _In_ FXMVECTOR Rotation, _In_ FXMVECTOR Translation ) const;

    void GetCorners( _Out_writes_(8) XMFLOAT3* Corners ) const;
        // Gets the 8 corners of the box

    ContainmentType Contains( _In_ FXMVECTOR Point ) const;
    ContainmentType Contains( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2 ) const;
    ContainmentType Contains( _In_ const BoundingSphere& sh ) const;
    ContainmentType Contains( _In_ const BoundingBox& box ) const;
    ContainmentType Contains( _In_ const BoundingOrientedBox& box ) const;
    ContainmentType Contains( _In_ const BoundingFrustum& fr ) const;
    
    bool Intersects( _In_ const BoundingSphere& sh ) const;
    bool Intersects( _In_ const BoundingBox& box ) const;
    bool Intersects( _In_ const BoundingOrientedBox& box ) const;
    bool Intersects( _In_ const BoundingFrustum& fr ) const;

    bool Intersects( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2 ) const;
        // Triangle-Box test

    PlaneIntersectionType Intersects( _In_ FXMVECTOR Plane ) const;
        // Plane-box test

    bool Intersects( _In_ FXMVECTOR Origin, _In_ FXMVECTOR Direction, _Out_ float& Dist ) const;
        // Ray-Box test

    ContainmentType ContainedBy( _In_ FXMVECTOR Plane0, _In_ FXMVECTOR Plane1, _In_ FXMVECTOR Plane2,
                                 _In_ GXMVECTOR Plane3, _In_ CXMVECTOR Plane4, _In_ CXMVECTOR Plane5 ) const;
        // Test box against six planes (see BoundingFrustum::GetPlanes)

    // Static methods
    static void CreateMerged( _Out_ BoundingBox& Out, _In_ const BoundingBox& b1, _In_ const BoundingBox& b2 );

    static void CreateFromSphere( _Out_ BoundingBox& Out, _In_ const BoundingSphere& sh );

    static void CreateFromPoints( _Out_ BoundingBox& Out, _In_ FXMVECTOR pt1, _In_ FXMVECTOR pt2 );
    static void CreateFromPoints( _Out_ BoundingBox& Out, _In_ size_t Count,
                                  _In_reads_bytes_(sizeof(XMFLOAT3)+Stride*(Count-1)) const XMFLOAT3* pPoints, _In_ size_t Stride );
};

//-------------------------------------------------------------------------------------
// Oriented bounding box
//-------------------------------------------------------------------------------------
struct BoundingOrientedBox
{
    static const size_t CORNER_COUNT = 8;

    XMFLOAT3 Center;            // Center of the box.
    XMFLOAT3 Extents;           // Distance from the center to each side.
    XMFLOAT4 Orientation;       // Unit quaternion representing rotation (box -> world).

    // Creators
    BoundingOrientedBox() : Center(0,0,0), Extents( 1.f, 1.f, 1.f ), Orientation(0,0,0, 1.f ) {}
    BoundingOrientedBox( _In_ const XMFLOAT3& _Center, _In_ const XMFLOAT3& _Extents, _In_ const XMFLOAT4& _Orientation )
        : Center(_Center), Extents(_Extents), Orientation(_Orientation)
    {
        assert(_Extents.x >= 0 && _Extents.y >= 0 && _Extents.z >= 0);
    }
    BoundingOrientedBox( _In_ const BoundingOrientedBox& box )
        : Center(box.Center), Extents(box.Extents), Orientation(box.Orientation) {}

    // Methods
    BoundingOrientedBox& operator=( _In_ const BoundingOrientedBox& box ) { Center = box.Center; Extents = box.Extents; Orientation = box.Orientation; return *this; }

    void Transform( _Out_ BoundingOrientedBox& Out, _In_ CXMMATRIX M ) const;
    void Transform( _Out_ BoundingOrientedBox& Out, _In_ float Scale, _In_ FXMVECTOR Rotation, _In_ FXMVECTOR Translation ) const;

    void GetCorners( _Out_writes_(8) XMFLOAT3* Corners ) const;
        // Gets the 8 corners of the box

    ContainmentType Contains( _In_ FXMVECTOR Point ) const;
    ContainmentType Contains( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2 ) const;
    ContainmentType Contains( _In_ const BoundingSphere& sh ) const;
    ContainmentType Contains( _In_ const BoundingBox& box ) const;
    ContainmentType Contains( _In_ const BoundingOrientedBox& box ) const;
    ContainmentType Contains( _In_ const BoundingFrustum& fr ) const;

    bool Intersects( _In_ const BoundingSphere& sh ) const;
    bool Intersects( _In_ const BoundingBox& box ) const;
    bool Intersects( _In_ const BoundingOrientedBox& box ) const;
    bool Intersects( _In_ const BoundingFrustum& fr ) const;

    bool Intersects( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2 ) const;
        // Triangle-OrientedBox test

    PlaneIntersectionType Intersects( _In_ FXMVECTOR Plane ) const;
        // Plane-OrientedBox test
    
    bool Intersects( _In_ FXMVECTOR Origin, _In_ FXMVECTOR Direction, _Out_ float& Dist ) const;
        // Ray-OrientedBox test

    ContainmentType ContainedBy( _In_ FXMVECTOR Plane0, _In_ FXMVECTOR Plane1, _In_ FXMVECTOR Plane2,
                                 _In_ GXMVECTOR Plane3, _In_ CXMVECTOR Plane4, _In_ CXMVECTOR Plane5 ) const;
        // Test OrientedBox against six planes (see BoundingFrustum::GetPlanes)

    // Static methods
    static void CreateFromBoundingBox( _Out_ BoundingOrientedBox& Out, _In_ const BoundingBox& box );

    static void CreateFromPoints( _Out_ BoundingOrientedBox& Out, _In_ size_t Count,
                                  _In_reads_bytes_(sizeof(XMFLOAT3)+Stride*(Count-1)) const XMFLOAT3* pPoints, _In_ size_t Stride );
};

//-------------------------------------------------------------------------------------
// Bounding frustum
//-------------------------------------------------------------------------------------
struct BoundingFrustum
{
    static const size_t CORNER_COUNT = 8;

    XMFLOAT3 Origin;            // Origin of the frustum (and projection).
    XMFLOAT4 Orientation;       // Quaternion representing rotation.

    float RightSlope;           // Positive X slope (X/Z).
    float LeftSlope;            // Negative X slope.
    float TopSlope;             // Positive Y slope (Y/Z).
    float BottomSlope;          // Negative Y slope.
    float Near, Far;            // Z of the near plane and far plane.

    // Creators
    BoundingFrustum() : Origin(0,0,0), Orientation(0,0,0, 1.f), RightSlope( 1.f ), LeftSlope( -1.f ),
                        TopSlope( 1.f ), BottomSlope( -1.f ), Near(0), Far( 1.f ) {}
    BoundingFrustum( _In_ const XMFLOAT3& _Origin, _In_ const XMFLOAT4& _Orientation,
                     _In_ float _RightSlope, _In_ float _LeftSlope, _In_ float _TopSlope, _In_ float _BottomSlope,
                     _In_ float _Near, _In_ float _Far )
        : Origin(_Origin), Orientation(_Orientation),
          RightSlope(_RightSlope), LeftSlope(_LeftSlope), TopSlope(_TopSlope), BottomSlope(_BottomSlope),
          Near(_Near), Far(_Far) { assert( _Near <= _Far ); }
    BoundingFrustum( _In_ const BoundingFrustum& fr )
        : Origin(fr.Origin), Orientation(fr.Orientation), RightSlope(fr.RightSlope), LeftSlope(fr.LeftSlope),
          TopSlope(fr.TopSlope), BottomSlope(fr.BottomSlope), Near(fr.Near), Far(fr.Far) {}
    BoundingFrustum( _In_ CXMMATRIX Projection ) { CreateFromMatrix( *this, Projection ); }

    // Methods
    BoundingFrustum& operator=( _In_ const BoundingFrustum& fr ) { Origin=fr.Origin; Orientation=fr.Orientation;
                                                                   RightSlope=fr.RightSlope; LeftSlope=fr.LeftSlope;
                                                                   TopSlope=fr.TopSlope; BottomSlope=fr.BottomSlope;
                                                                   Near=fr.Near; Far=fr.Far; return *this; }

    void Transform( _Out_ BoundingFrustum& Out, _In_ CXMMATRIX M ) const;
    void Transform( _Out_ BoundingFrustum& Out, _In_ float Scale, _In_ FXMVECTOR Rotation, _In_ FXMVECTOR Translation ) const;

    void GetCorners( _Out_writes_(8) XMFLOAT3* Corners ) const;
        // Gets the 8 corners of the frustum

    ContainmentType Contains( _In_ FXMVECTOR Point ) const;
    ContainmentType Contains( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2 ) const;
    ContainmentType Contains( _In_ const BoundingSphere& sp ) const;
    ContainmentType Contains( _In_ const BoundingBox& box ) const;
    ContainmentType Contains( _In_ const BoundingOrientedBox& box ) const;
    ContainmentType Contains( _In_ const BoundingFrustum& fr ) const;
        // Frustum-Frustum test

    bool Intersects( _In_ const BoundingSphere& sh ) const;
    bool Intersects( _In_ const BoundingBox& box ) const;
    bool Intersects( _In_ const BoundingOrientedBox& box ) const;
    bool Intersects( _In_ const BoundingFrustum& fr ) const;

    bool Intersects( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2 ) const;
        // Triangle-Frustum test

    PlaneIntersectionType Intersects( _In_ FXMVECTOR Plane ) const;
        // Plane-Frustum test

    bool Intersects( _In_ FXMVECTOR Origin, _In_ FXMVECTOR Direction, _Out_ float& Dist ) const;
        // Ray-Frustum test

    ContainmentType ContainedBy( _In_ FXMVECTOR Plane0, _In_ FXMVECTOR Plane1, _In_ FXMVECTOR Plane2,
                                 _In_ GXMVECTOR Plane3, _In_ CXMVECTOR Plane4, _In_ CXMVECTOR Plane5 ) const;
        // Test frustum against six planes (see BoundingFrustum::GetPlanes)

    void GetPlanes( _Out_opt_ XMVECTOR* NearPlane, _Out_opt_ XMVECTOR* FarPlane, _Out_opt_ XMVECTOR* RightPlane,
                    _Out_opt_ XMVECTOR* LeftPlane, _Out_opt_ XMVECTOR* TopPlane, _Out_opt_ XMVECTOR* BottomPlane ) const;
        // Create 6 Planes representation of Frustum

    // Static methods
    static void CreateFromMatrix( _Out_ BoundingFrustum& Out, _In_ CXMMATRIX Projection );
};

//-----------------------------------------------------------------------------
// Triangle intersection testing routines.
//-----------------------------------------------------------------------------
namespace TriangleTests
{
    bool Intersects( _In_ FXMVECTOR Origin, _In_ FXMVECTOR Direction, _In_ FXMVECTOR V0, _In_ GXMVECTOR V1, _In_ CXMVECTOR V2, _Out_ float& Dist );
        // Ray-Triangle

    bool Intersects( _In_ FXMVECTOR A0, _In_ FXMVECTOR A1, _In_ FXMVECTOR A2, _In_ GXMVECTOR B0, _In_ CXMVECTOR B1, _In_ CXMVECTOR B2 );
        // Triangle-Triangle

    PlaneIntersectionType Intersects( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2, _In_ GXMVECTOR Plane );
        // Plane-Triangle

    ContainmentType ContainedBy( _In_ FXMVECTOR V0, _In_ FXMVECTOR V1, _In_ FXMVECTOR V2,
                                 _In_ GXMVECTOR Plane0, _In_ CXMVECTOR Plane1, _In_ CXMVECTOR Plane2,
                                 _In_ CXMVECTOR Plane3, _In_ CXMVECTOR Plane4, _In_ CXMVECTOR Plane5 );
        // Test a triangle against six planes at once (see BoundingFrustum::GetPlanes)
};

#pragma warning(pop)

/****************************************************************************
 *
 * Implementation
 *
 ****************************************************************************/

#pragma warning(push)
#pragma warning(disable : 4068 4616 6001)

#pragma prefast(push)
#pragma prefast(disable : 25000, "FXMVECTOR is 16 bytes")

#include "DirectXCollision.inl"

#pragma prefast(pop)
#pragma warning(pop)

}; // namespace DirectX

