[an error occurred while processing this directive]
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //
//
//  Project:   Talina Gaming System (TgS) (∂)
//  File:      TgS Collision - Triangle-Triangle.cpp
//  Author:    Andrew Aye (EMail: andrew.aye@gmail.com, Web: http://www.andrewaye.com) 
//  Version:   3.11
//
// ------------------------------------------------------------------------------------------------------------------------------ //
//
//  Copyright: © 2002-2008, Andrew Aye.  All Rights Reserved.
//
//  This software is free for non-commercial use. Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met: 
//    Redistributions of source code must retain this copyright notice, this list of conditions and the following disclaimers. 
//    Redistributions in binary form must reproduce this copyright notice, this list of conditions and the following
//      disclaimers in the documentation and other materials provided with the distribution. 
//
//  Neither the names of the copyright owner nor the names of its contributors may be used to endorse or promote products derived
//  from this software without specific prior written permission. 
//
//  The intellectual property rights of the algorithms used reside with Andrew Aye.  You may not use this software, in whole or
//  in part, in support of any commercial product without the express written consent of the author.
//
//  There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is".
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //




namespace TGS { // START TGS ///////////////////////////////////////////////////////////////////////////////////////////////////////
namespace COL { // START COL ///////////////////////////////////////////////////////////////////////////////////////////////////////

// ============================================================================================================================== //

// ---- F_ClosestSq ------------------------------------------------------------------------------------------------------------- //
// Input:  tgST0, tgST1: Space triangle primitive
// Output: _tyST00, _tyST01: Parametric parameters to generate point of minimal distance on triangle #1
// Output: _tyST10, _tyST11: Parametric parameters to generate point of minimal distance on triangle #2
// Return: Minimal distance between the two primitives or negative type max if they intersect or are invalid.
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TYPE F_ClosestSq( TYPE *ptyST00, TYPE *ptyST01, TYPE *ptyST10, TYPE *ptyST11, CR_(STRI,DIM) tgST0, CR_(STRI,DIM) tgST1 )
{
    TYPE                                tyDistSq, tyET00, tyET01, tyET10, tyET11, tyTest, tyT0, tyT1, tyG1;

    // == Test Triangle 0 Edges against Triangle 1 ===========

    tyDistSq = TTgCSQ_STLN<TYPE,DIM,1,1>::DO( &tyET00,&tyET01, &tyET10, tgST0, tgST1.Query_Point0(), tgST1.Query_Edge0() );
    tyET11 = TYPE(0.0);

    tyTest = TTgCSQ_STLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, &tyG1, tgST0, tgST1.Query_Point1(), tgST1.Query_Edge1() );
    if (tyTest < tyDistSq)
    {
        tyDistSq = tyTest;
        tyET00 = tyT0;
        tyET01 = tyT1;
        tyET10 = TYPE(1.0) - tyG1;
        tyET11 = tyG1;
    };
    
    tyTest = TTgCSQ_STLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, &tyG1, tgST0, tgST1.Query_Point2(), tgST1.Query_Edge2() );
    if (tyTest < tyDistSq)
    {
        tyDistSq = tyTest;
        tyET00 = tyT0;
        tyET01 = tyT1;
        tyET10 = TYPE(0.0);
        tyET11 = TYPE(1.0) - tyG1;
    };

    // == Test Triangle 1 Edges against Triangle 0 ===========

    tyTest = TTgCSQ_STLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, &tyG1, tgST1, tgST0.Query_Point0(), tgST0.Query_Edge0() );
    if (tyTest < tyDistSq)
    {
        tyDistSq = tyTest;
        tyET00 = tyG1;
        tyET01 = TYPE(0.0);
        tyET10 = tyT0;
        tyET11 = tyT1;
    };

    tyTest = TTgCSQ_STLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, &tyG1, tgST1, tgST0.Query_Point1(), tgST0.Query_Edge1() );
    if (tyTest < tyDistSq)
    {
        tyDistSq = tyTest;
        tyET00 = TYPE(1.0) - tyG1;
        tyET01 = tyG1;
        tyET10 = tyT0;
        tyET11 = tyT1;
    };
    
    tyTest = TTgCSQ_STLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, &tyG1, tgST1, tgST0.Query_Point2(), tgST0.Query_Edge2() );
    if (tyTest < tyDistSq)
    {
        tyDistSq = tyTest;
        tyET00 = TYPE(0.0);
        tyET01 = TYPE(1.0) - tyG1;
        tyET10 = tyT0;
        tyET11 = tyT1;
    };
    
    // == Return Values ========================================

    *ptyST00 = tyET00;
    *ptyST01 = tyET01;
    *ptyST10 = tyET10;
    *ptyST11 = tyET11;

    return (tyDistSq);
};

template TgFLOAT32 F_ClosestSq( P_TgFLOAT32, P_TgFLOAT32, P_TgFLOAT32, P_TgFLOAT32, CR_TgF4STRI, CR_TgF4STRI );


// ============================================================================================================================== //
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //

//                                              Contact Generation Utility Functions  

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
// ============================================================================================================================== //

// ---- F_Internal_CoP ---------------------------------------------------------------------------------------------------------- //
// Transfer the point generated by a clipping operation to the contact list.
// Input:  tgCL: F_Clip list of points
// Input:  tyT0: Time of contact
// Input:  tvNormal: The normal of contact
// Output: tgPacket: Contact points are added to it
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgRESULT F_Internal_CoP( PC_(CONTACT_PACKET,DIM) ptgPacket, CR_(CLIP_LIST,DIM) tgData, const TYPE tyT0, M_(VECTOR,DIM) tvNormal )
{
    C_TgINT                             niPoint = MIN( tgData.m_niPoint, ptgPacket->m_niMaxContact - ptgPacket->m_niContact );
    P_(CONTACT,DIM)                     ptgContact;

    switch (niPoint)
    {
        case 6:
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
            ptgContact->m_tvPos = tgData.m_ptvPoint[5];
            ptgContact->m_tvNormal = tvNormal;
            ptgContact->m_tyT0 = tyT0;
            ptgContact->m_tyDepth = TYPE(0.0);

            ++ptgPacket->m_niContact;

        case 5:
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
            ptgContact->m_tvPos = tgData.m_ptvPoint[4];
            ptgContact->m_tvNormal = tvNormal;
            ptgContact->m_tyT0 = tyT0;
            ptgContact->m_tyDepth = TYPE(0.0);

            ++ptgPacket->m_niContact;

        case 4:
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
            ptgContact->m_tvPos = tgData.m_ptvPoint[3];
            ptgContact->m_tvNormal = tvNormal;
            ptgContact->m_tyT0 = tyT0;
            ptgContact->m_tyDepth = TYPE(0.0);

            ++ptgPacket->m_niContact;

        case 3:
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);

            ptgContact->m_tvPos = tgData.m_ptvPoint[2];
            ptgContact->m_tvNormal = tvNormal;
            ptgContact->m_tyT0 = tyT0;
            ptgContact->m_tyDepth = TYPE(0.0);

            ++ptgPacket->m_niContact;

        case 2:
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
            ptgContact->m_tvPos = tgData.m_ptvPoint[1];
            ptgContact->m_tvNormal = tvNormal;
            ptgContact->m_tyT0 = tyT0;
            ptgContact->m_tyDepth = TYPE(0.0);

            ++ptgPacket->m_niContact;

        case 1:
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
            ptgContact->m_tvPos = tgData.m_ptvPoint[0];
            ptgContact->m_tvNormal = tvNormal;
            ptgContact->m_tyT0 = tyT0;
            ptgContact->m_tyDepth = TYPE(0.0);

            ++ptgPacket->m_niContact;

        case 0:
            return (niPoint < tgData.m_niPoint ? TgS_MAXCONTACTS : TgS_OK);

        default:
            break;
    };
    TgASSERT(TgFALSE);
    return (TgE_FAIL);
};

template TgRESULT F_Internal_CoP( PC_TgF4CONTACT_PACKET, CR_TgF4CLIP_LIST, C_TgFLOAT32, M_TgF4VECTOR );


// ============================================================================================================================== //
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //

//                                            Static Triangle - Triangle Intersection

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
// ============================================================================================================================== //

// ---- F_Contact_Test ---------------------------------------------------------------------------------------------------------- //
// Input:  tgST0, tgST1: Space triangle primitive
// Return: True if the two triangles are in contact
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgBOOL F_Contact_Test( CR_(STRI,DIM) tgST0, CR_(STRI,DIM) tgST1 )
{
    const TYPE                          tyTriD0 = MATH::F_DOT(tgST0.Query_Normal(),tgST0.Query_Origin());
    const TYPE                          tyTriD1 = MATH::F_DOT(tgST1.Query_Normal(),tgST1.Query_Origin());
    TYPE                                tyMin,tyMax;

    // == Triangle Normal ====================================

    tgST1.Project( &tyMin,&tyMax, tgST0.Query_Normal() );
    if (tyMin > tyTriD0 || tyMax < tyTriD0)
    {
        return (TgFALSE);
    };

    tgST0.Project( &tyMin,&tyMax, tgST1.Query_Normal() );
    if (tyMin > tyTriD1 || tyMax < tyTriD1)
    {
        return (TgFALSE);
    };

    // == Cross-Product Normal ===============================

    if (!Near_Zero( MATH::F_LSQ( MATH::F_CX( tgST0.Query_Normal(), tgST1.Query_Normal() ) ) )) 
    {
        T_(VECTOR,DIM)                      tvNormal;

        for (TgINT iE0 = 0; iE0 < 9; ++iE0)
        {
            tvNormal = MATH::F_CX( tgST0.Query_Edge( (iE0 & 3) ), tgST1.Query_Edge( (iE0 >> 3) ) );

            if (F_Is_Seperating_Axis( tvNormal, tgST0.Query_PT(), tgST1.Query_PT() ))
            {
                return (TgFALSE);
            };
        };
    }
    else
    {
        // Normal extruded space of triangle 0

        if (
            F_Sign_Dist( tgST0.Query_EdgePlane0(), tgST1.Query_Point0() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST0.Query_EdgePlane0(), tgST1.Query_Point1() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST0.Query_EdgePlane0(), tgST1.Query_Point2() ) < TYPE(0.0)
        ) {
            return (TgFALSE);
        };

        if (
            F_Sign_Dist( tgST0.Query_EdgePlane1(), tgST1.Query_Point0() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST0.Query_EdgePlane1(), tgST1.Query_Point1() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST0.Query_EdgePlane1(), tgST1.Query_Point2() ) < TYPE(0.0)
        ) {
            return (TgFALSE);
        };

        if (
            F_Sign_Dist( tgST0.Query_EdgePlane2(), tgST1.Query_Point0() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST0.Query_EdgePlane2(), tgST1.Query_Point1() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST0.Query_EdgePlane2(), tgST1.Query_Point2() ) < TYPE(0.0)
        ) {
            return (TgFALSE);
        };

        // Normal extruded space of triangle 1

        if (
            F_Sign_Dist( tgST1.Query_EdgePlane0(), tgST0.Query_Point0() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST1.Query_EdgePlane0(), tgST0.Query_Point1() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST1.Query_EdgePlane0(), tgST0.Query_Point2() ) < TYPE(0.0)
        ) {
            return (TgFALSE);
        };

        if (
            F_Sign_Dist( tgST1.Query_EdgePlane1(), tgST0.Query_Point0() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST1.Query_EdgePlane1(), tgST0.Query_Point1() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST1.Query_EdgePlane1(), tgST0.Query_Point2() ) < TYPE(0.0)
        ) {
            return (TgFALSE);
        };

        if (
            F_Sign_Dist( tgST1.Query_EdgePlane2(), tgST0.Query_Point0() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST1.Query_EdgePlane2(), tgST0.Query_Point1() ) < TYPE(0.0) &&
            F_Sign_Dist( tgST1.Query_EdgePlane2(), tgST0.Query_Point2() ) < TYPE(0.0)
        ) {
            return (TgFALSE);
        };
    };

    return (TgTRUE);
};
        
template TgBOOL F_Contact_Test( CR_TgF4STRI, CR_TgF4STRI );


// ---- F_Contact_Intersect ----------------------------------------------------------------------------------------------------- //
// Input:  tgPacket: The current series of contact points for this query-series, and contact generation parameters.
// Input:  tgST0, tgST1: Space triangle primitive
// Output: tgPacket: Points of intersection between the two primitives are added to it
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgRESULT F_Contact_Intersect( PC_(CONTACT_PACKET,DIM) ptgPacket, CR_(STRI,DIM) tgST0, CR_(STRI,DIM) tgST1 )
{
    if (!F_Contact_Test( tgST0, tgST1 ))
    {
        return (TgE_NOINTERSECT);
    };

    TYPE                                tyK0;
    T_(VECTOR,DIM)                      tvN0xN1 = MATH::F_NORM( &tyK0, MATH::F_CX( tgST0.Query_Normal(), tgST1.Query_Normal() ) );
    const TYPE                          tyN0_N1 = MATH::F_DOT(tgST0.Query_Normal(),tgST1.Query_Normal());
    const TYPE                          tyDet = TYPE(1.0) - tyN0_N1*tyN0_N1;

    if (Near_Zero( tyK0 ) || Near_Zero( tyDet ))
    {
        // Co-Planar Triangles

        TTgCLIP_LIST<TYPE,DIM>              tgCL;

        F_Clip( &tgCL, tgST0,tgST1.Query_PT() );
        return (F_Internal_CoP( ptgPacket, tgCL, TYPE(0.0), tgST0.Query_Normal() ));
    };

    const TYPE                          tyD0 = MATH::F_DOT(tgST0.Query_Origin(),tgST0.Query_Normal());
    const TYPE                          tyD1 = MATH::F_DOT(tgST1.Query_Origin(),tgST1.Query_Normal());

    const TYPE                          tyT0 = (tyD0 - tyD1*tyN0_N1) / tyDet;
    const TYPE                          tyT1 = (tyD1 - tyD0*tyN0_N1) / tyDet;

    C_(VECTOR,DIM)                      tvK0 = MATH::F_MUL( tyT0, tgST0.Query_Normal() );
    C_(VECTOR,DIM)                      tvK1 = MATH::F_MUL( tyT1, tgST1.Query_Normal() );
    C_(VECTOR,DIM)                      tvS0 = MATH::F_ADD( tvK0, tvK1 );

    TTgCLIP_LIST<TYPE,DIM>              tgCL;

    TTgCLP_STLN<TYPE,DIM,1,1>::DO( &tgCL, tgST0, tvS0, tvN0xN1 );
    F_Clip( &tgCL, tgST1 );

    C_TgINT                             niPoint = MIN( tgCL.m_niPoint, ptgPacket->m_niMaxContact - ptgPacket->m_niContact );
    P_(CONTACT,DIM)                     ptgContact;

    switch (niPoint)
    {
        case 2:
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
            ptgContact->m_tvPos = tgCL.m_ptvPoint[1];
            ptgContact->m_tvNormal = tvN0xN1;
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = TYPE(0.0);

            ++ptgPacket->m_niContact;

        case 1:
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
            ptgContact->m_tvPos = tgCL.m_ptvPoint[0];
            ptgContact->m_tvNormal = tvN0xN1;
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = TYPE(0.0);

            ++ptgPacket->m_niContact;

        case 0:
            return (niPoint < tgCL.m_niPoint ? TgS_MAXCONTACTS : TgS_OK);

        default:
            break;
    };
    TgASSERT(TgFALSE);
    return (TgE_FAIL);
};

template TgRESULT F_Contact_Intersect( PC_TgF4CONTACT_PACKET, CR_TgF4STRI, CR_TgF4STRI );


// ============================================================================================================================== //
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //

//                                           Dynamic Triangle - Triangle Intersection

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //
// ============================================================================================================================== //

// ============================================================================================================================== //

// ---- F_Contact_Test ---------------------------------------------------------------------------------------------------------- //
// Input:  tgST0, tgST1: Space triangle primitive - Triangle #2 (tgST1) is being swept.
// Input:  tgDT: A structure holding the swept primitive displacement for the entire duration of the test period.
// Return: True if the primitives contact anywhere in 4D space.
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgBOOL F_Contact_Test( CR_(STRI,DIM) tgST0, CR_(STRI,DIM) tgST1, CR_(DELTA,DIM) tgDT )
{
    T_(AXIS_TEST,DIM)                   tgAxTest; //« Parameter data output variable
    //TYPE                                tySkipT = -LIMITS<TYPE>::MAX; //« T-value for axes associated with reduced features.

    tgAxTest.m_tyLimitT = TYPE(1.0); // The maximum t-value by which a contact must happen resulting in non-contact.
    tgAxTest.m_tyMinT = -LIMITS<TYPE>::MAX;
    tgAxTest.m_tyMaxT =  LIMITS<TYPE>::MAX;

    // -- Axis: Triangle 0 Normal ------------------------------

    const TYPE                          tyS0_NM = MATH::F_DOT(tgST0.Query_Origin(),tgST0.Query_Normal());

    tgAxTest.m_tyMin0 = tyS0_NM;
    tgAxTest.m_tyMax0 = tyS0_NM;
    tgST1.Project( &tgAxTest.m_tyMin1, &tgAxTest.m_tyMax1, tgST0.Query_Normal() );
    tgAxTest.m_tySpeed = MATH::F_DOT(tgDT.m_tvDT,tgST0.Query_Normal());

    if (0 >= F_Test_Seperating_Axis( &tgAxTest ))
    {
        return (TgFALSE);
    };

    // Check to see if the triangle's are parallel.

    if (MATH::F_LSQ( MATH::F_CX(tgST0.Query_Normal(),tgST1.Query_Normal()) ) > LIMITS<TYPE>::EPSILON)
    {
        // -- Axis: Triangle 1 Normal ------------------------------

        const TYPE                          tyS1_NM = MATH::F_DOT(tgST1.Query_Origin(),tgST1.Query_Normal());

        tgST0.Project( &tgAxTest.m_tyMin0, &tgAxTest.m_tyMax0, tgST1.Query_Normal() );
        tgAxTest.m_tyMin1 = tyS1_NM;
        tgAxTest.m_tyMax1 = tyS1_NM;
        tgAxTest.m_tySpeed = MATH::F_DOT( tgDT.m_tvDT, tgST1.Query_Normal() );

        if (0 >= F_Test_Seperating_Axis( &tgAxTest ))
        {
            return (TgFALSE);
        };

        // == Triangle Cross-Product =============================

        T_(VECTOR,DIM)                      tvNormal;

        for (TgINT iE0 = 0; iE0 < 9; ++iE0)
        {
            tvNormal = MATH::F_CX( tgST0.Query_Edge( (iE0 & 3) ), tgST1.Query_Edge( (iE0 >> 3) ) );

            tgST0.Project( &tgAxTest.m_tyMin0, &tgAxTest.m_tyMax0, tvNormal );
            tgST1.Project( &tgAxTest.m_tyMin1, &tgAxTest.m_tyMax1, tvNormal );
            tgAxTest.m_tySpeed = MATH::F_DOT( tgDT.m_tvDT, tvNormal );

            if (0 >= F_Test_Seperating_Axis( &tgAxTest ))
            {
                return (TgFALSE);
            };
        };
    }
    else
    {
        T_(VECTOR,DIM)                      tvNormal;

        // == Triangle 0 Space ===================================

        for (TgINT iE0 = 0; iE0 < 3; ++iE0)
        {
            tvNormal = MATH::F_CX( tgST0.Query_Normal(), tgST0.Query_Edge( iE0 ) );

            tgST0.Project( &tgAxTest.m_tyMin0, &tgAxTest.m_tyMax0, tvNormal );
            tgST1.Project( &tgAxTest.m_tyMin1, &tgAxTest.m_tyMax1, tvNormal );
            tgAxTest.m_tySpeed = MATH::F_DOT( tgDT.m_tvDT, tvNormal );

            if (0 >= F_Test_Seperating_Axis( &tgAxTest ))
            {
                return (TgFALSE);
            };
        };

        // == Triangle 1 Space ===================================

        for (TgINT iE0 = 0; iE0 < 3; ++iE0)
        {
            tvNormal = MATH::F_CX( tgST1.Query_Normal(), tgST1.Query_Edge( iE0 ) );

            tgST0.Project( &tgAxTest.m_tyMin0, &tgAxTest.m_tyMax0, tvNormal );
            tgST1.Project( &tgAxTest.m_tyMin1, &tgAxTest.m_tyMax1, tvNormal );
            tgAxTest.m_tySpeed = MATH::F_DOT( tgDT.m_tvDT, tvNormal );

            if (0 >= F_Test_Seperating_Axis( &tgAxTest ))
            {
                return (TgFALSE);
            };
        };
    };

    return (TgTRUE);
}

template TgBOOL F_Contact_Test( CR_TgF4STRI, CR_TgF4STRI, CR_TgF4DELTA );


// ============================================================================================================================== //

// ---- F_Axis_Seperation --------------------------------------------------------------------------------------------------------- //
// Input:  tgST0, tgST1: Space triangle primitive - Triangle #2 (tgST1) is being swept.
// Input:  tyLimitT: Current normalized time of contact for the contact query set.
// Input:  tgDT: A structure holding the swept primitive displacement for the entire duration of the test period.
// Output: tgNFO: Structure holds the resulting axis separation information necessary to create a contact set.
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgRESULT F_Axis_Seperation( PC_(AXIS_INFO,DIM) ptgNFO, const TYPE tyLimitT, CR_(STRI,DIM) tgST0, CR_(STRI,DIM) tgST1, CR_(DELTA,DIM) tgDT )
{
    T_(AXIS_TEST,DIM)                   tgAxTest; //« Parameter data output variable
    //TYPE                                tySkipT = -LIMITS<TYPE>::MAX; //« T-value for axes associated with reduced features.
    TgINT                               iResult;

    tgAxTest.m_tyLimitT = tyLimitT; // The maximum t-value by which a contact must happen resulting in non-contact.
    tgAxTest.m_tyMinT = -LIMITS<TYPE>::MAX;
    tgAxTest.m_tyMaxT =  LIMITS<TYPE>::MAX;


    // -- Axis: Triangle 0 Normal ------------------------------

    const TYPE                          tyS0_NM = MATH::F_DOT( tgST0.Query_Origin(), tgST0.Query_Normal() );

    tgAxTest.m_tyMin0 = tyS0_NM;
    tgAxTest.m_tyMax0 = tyS0_NM;
    tgST1.Project( &tgAxTest.m_tyMin1, &tgAxTest.m_tyMax1, tgST0.Query_Normal() );
    tgAxTest.m_tySpeed = MATH::F_DOT( tgDT.m_tvDT, tgST0.Query_Normal() );

    iResult = F_Test_Seperating_Axis( &tgAxTest );
    switch (iResult) {
        case  1: //« Update has occurred
            ptgNFO->m_eSide = (tgAxTest.m_tyMax1 < tgAxTest.m_tyMin0) ? T_(AXIS_INFO,DIM)::NEGATIVE : T_(AXIS_INFO,DIM)::POSITIVE;
            ptgNFO->m_tvNormal = tgST0.Query_Normal();
            ptgNFO->m_tyMinT = tgAxTest.m_tyMinT;
        case  0: //« Contact occurred in valid interval, but earlier contact already recorded.
            break;
        case -1: //« No contact occurred during valid interval, thus primitives are separated on this axis.
            return (TgE_NOINTERSECT);
    };

    // Check to see if the triangle's are parallel.

    if (MATH::F_LSQ( MATH::F_CX( tgST0.Query_Normal(), tgST1.Query_Normal() ) ) > LIMITS<TYPE>::EPSILON)
    {
        // -- Axis: Triangle 1 Normal ------------------------------

        const TYPE                          tyS1_NM = MATH::F_DOT(tgST1.Query_Origin(),tgST1.Query_Normal());

        tgST0.Project( &tgAxTest.m_tyMin0, &tgAxTest.m_tyMax0, tgST1.Query_Normal() );
        tgAxTest.m_tyMin1 = tyS1_NM;
        tgAxTest.m_tyMax1 = tyS1_NM;
        tgAxTest.m_tySpeed = MATH::F_DOT( tgDT.m_tvDT, tgST1.Query_Normal() );

        iResult = F_Test_Seperating_Axis( &tgAxTest );
        switch (iResult) {
            case  1: //« Update has occurred
                ptgNFO->m_eSide = (tgAxTest.m_tyMax1 < tgAxTest.m_tyMin0) ? T_(AXIS_INFO,DIM)::NEGATIVE : T_(AXIS_INFO,DIM)::POSITIVE;
                ptgNFO->m_tvNormal = tgST1.Query_Normal();
                ptgNFO->m_tyMinT = tgAxTest.m_tyMinT;
            case  0: //« Contact occurred in valid interval, but earlier contact already recorded.
                break;
            case -1: //« No contact occurred during valid interval, thus primitives are separated on this axis.
                return (TgE_NOINTERSECT);
        };

        // == Triangle Cross-Product =============================

        T_(VECTOR,DIM)                      tvNormal;

        for (TgINT iE0 = 0; iE0 < 9; ++iE0)
        {
            tvNormal = MATH::F_CX( tgST0.Query_Edge( (iE0 & 3) ), tgST1.Query_Edge( (iE0 >> 3) ) );

            tgST0.Project( &tgAxTest.m_tyMin0, &tgAxTest.m_tyMax0, tvNormal );
            tgST1.Project( &tgAxTest.m_tyMin1, &tgAxTest.m_tyMax1, tvNormal );
            tgAxTest.m_tySpeed = MATH::F_DOT( tgDT.m_tvDT, tvNormal );

            iResult = F_Test_Seperating_Axis( &tgAxTest );
            switch (iResult) {
                case  1: //« Update has occurred
                    ptgNFO->m_eSide = (tgAxTest.m_tyMax1 < tgAxTest.m_tyMin0) ? T_(AXIS_INFO,DIM)::NEGATIVE : T_(AXIS_INFO,DIM)::POSITIVE;
                    ptgNFO->m_tvNormal = tvNormal;
                    ptgNFO->m_tyMinT = tgAxTest.m_tyMinT;
                case  0: //« Contact occurred in valid interval, but earlier contact already recorded.
                    break;
                case -1: //« No contact occurred during valid interval, thus primitives are separated on this axis.
                    return (TgE_NOINTERSECT);
            };
        };
    }
    else
    {
        T_(VECTOR,DIM)                      tvNormal;

        // == Triangle 0 Space ===================================

        for (TgINT iE0 = 0; iE0 < 3; ++iE0)
        {
            tvNormal = MATH::F_CX( tgST0.Query_Normal(), tgST0.Query_Edge( iE0 ) );

            tgST0.Project( &tgAxTest.m_tyMin0, &tgAxTest.m_tyMax0, tvNormal );
            tgST1.Project( &tgAxTest.m_tyMin1, &tgAxTest.m_tyMax1, tvNormal );
            tgAxTest.m_tySpeed = MATH::F_DOT( tgDT.m_tvDT, tvNormal );

            iResult = F_Test_Seperating_Axis( &tgAxTest );
            switch (iResult) {
                case  1: //« Update has occurred
                    ptgNFO->m_eSide = (tgAxTest.m_tyMax1 < tgAxTest.m_tyMin0) ? T_(AXIS_INFO,DIM)::NEGATIVE : T_(AXIS_INFO,DIM)::POSITIVE;
                    ptgNFO->m_tvNormal = tvNormal;
                    ptgNFO->m_tyMinT = tgAxTest.m_tyMinT;
                case  0: //« Contact occurred in valid interval, but earlier contact already recorded.
                    break;
                case -1: //« No contact occurred during valid interval, thus primitives are separated on this axis.
                    return (TgE_NOINTERSECT);
            };
        };

        // == Triangle 1 Space ===================================

        for (TgINT iE0 = 0; iE0 < 3; ++iE0)
        {
            tvNormal = MATH::F_CX( tgST1.Query_Normal(), tgST1.Query_Edge( iE0 ) );

            tgST0.Project( &tgAxTest.m_tyMin0, &tgAxTest.m_tyMax0, tvNormal );
            tgST1.Project( &tgAxTest.m_tyMin1, &tgAxTest.m_tyMax1, tvNormal );
            tgAxTest.m_tySpeed = MATH::F_DOT( tgDT.m_tvDT, tvNormal );

            iResult = F_Test_Seperating_Axis( &tgAxTest );
            switch (iResult) {
                case  1: //« Update has occurred
                    ptgNFO->m_eSide = (tgAxTest.m_tyMax1 < tgAxTest.m_tyMin0) ? T_(AXIS_INFO,DIM)::NEGATIVE : T_(AXIS_INFO,DIM)::POSITIVE;
                    ptgNFO->m_tvNormal = tvNormal;
                    ptgNFO->m_tyMinT = tgAxTest.m_tyMinT;
                case  0: //« Contact occurred in valid interval, but earlier contact already recorded.
                    break;
                case -1: //« No contact occurred during valid interval, thus primitives are separated on this axis.
                    return (TgE_NOINTERSECT);
            };
        };
    };

    return (TgS_OK);
};

template TgRESULT F_Axis_Seperation( PC_TgF4AXIS_INFO, C_TgFLOAT32, CR_TgF4STRI, CR_TgF4STRI, CR_TgF4DELTA );


// ============================================================================================================================== //

// ---- F_Contact_Sweep --------------------------------------------------------------------------------------------------------- //
//
//  Axis Separation technique used to determine if two triangles are intersecting.
//
// Input:  tgPacket:  Contact generation parameters
// Input:  tyPM: Current normalized time of first contact for the contact query set.
// Input:  tgST0, tgST1: Space triangle primitive - Triangle #2 (tgST1) is being swept.
// Input:  tgDT: A structure holding the swept primitive displacement for the entire duration of the test period.
// Output: tgPacket: Contact points are added or replace the current set depending on the time comparison and given parameters
// Output: tyPM: New normalized time of first contact
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgRESULT F_Contact_Sweep( PC_(CONTACT_PACKET,DIM) ptgPacket, TYPE *ptyPM, CR_(STRI,DIM) tgST0, CR_(STRI,DIM) tgST1, CR_(DELTA,DIM) tgDT )
{
    TgBLOCK_FCN_NOOBJ(ETgFAC_COLLISION, 0, ETgTEST_SWEEP, (((TgUINT)ETgTRIANGLE<<16)|(TgUINT)ETgBOX))

    TgASSERT((TgSIZE)ptgPacket->m_iStride >= sizeof( P_(CONTACT,DIM) ))
    TgASSERT(tgST0.Is_Valid() && tgST1.Is_Valid())

    if (0 == ptgPacket->m_niMaxContact || ptgPacket->m_niContact >= ptgPacket->m_niMaxContact || NULL == ptgPacket->m_ptgContact)
    {
        return (TgE_FAIL);
    };

    TgFEBUG_COLLISION_TRIANGLE_CREATEID(iDBG_TriID, tgST1, ETgFEBUG_COLLISION_ENTERFCN);

    TTgAXIS_INFO<TYPE,DIM>              tgNFO;

    tgNFO.m_eSide = TTgAXIS_INFO<TYPE,DIM>::UNKNOWN;
    tgNFO.m_tyMinT =  LIMITS<TYPE>::MAX;

    C_TgRESULT tgResult = F_Axis_Seperation( &tgNFO, *ptyPM + ptgPacket->m_tySweepTol, tgST0, tgST1, tgDT );

    if (TgFAILED(tgResult))
    {
        TgASSERT(TgE_NOINTERSECT == tgResult)
        return (tgResult);
    };

    // == Contact Generation ==================================================================================================== //

    TgASSERT((tgNFO.m_eSide != TTgAXIS_INFO<TYPE,DIM>::UNKNOWN))

    if (tgNFO.m_eSide == TTgAXIS_INFO<TYPE,DIM>::PENETRATED || tgNFO.m_tyMinT < TYPE(0.0))
    {
        ptgPacket->m_niContact = 0;
        *ptyPM = TYPE(0.0);
        return (TgE_PREPENETRATION);
    };

    // Make sure this contact has not occurred more than tolerance later than the current sweep time.

    if (tgNFO.m_tyMinT > *ptyPM + ptgPacket->m_tySweepTol)
    {
        return (TgE_NOINTERSECT);
    };

    if (tgNFO.m_tyMinT < *ptyPM - ptgPacket->m_tySweepTol)
    {
        ptgPacket->m_niContact = 0;
        *ptyPM = tgNFO.m_tyMinT;
    };

    // Contact generation - interpolate the position of the triangle's at the given time, and compute the contact points.

    C_(VECTOR,DIM)                      tvOffset = MATH::F_MUL( tgNFO.m_tyMinT, tgDT.m_tvDT );
    T_(AXIS_PROJECT,DIM)                tgP0, tgP1;

    F_Axis_ProjInfo( &tgP0, tgNFO.m_tvNormal, tgST0.Query_PT() );
    F_Axis_ProjInfo( &tgP1, tgNFO.m_tvNormal, tgST1.Query_PT() );

    //  The two triangles are situated so that they are just touching each other.  Examine how the triangle vertices were
    // projected onto the axis to determine the geometric relation of the contact - and then create the contact points.

    C_TgINT                             niVertD0 = (tgNFO.m_eSide == T_(AXIS_INFO,DIM)::NEGATIVE) ? tgP0.m_niMinDepth : tgP0.m_niMaxDepth;
    PC_(VECTOR,DIM)                     atvVert0 = (tgNFO.m_eSide == T_(AXIS_INFO,DIM)::NEGATIVE) ? tgP0.m_atvMinVert : tgP0.m_atvMaxVert;
    C_TgINT                             niVertD1 = (tgNFO.m_eSide == T_(AXIS_INFO,DIM)::NEGATIVE) ? tgP1.m_niMaxDepth : tgP1.m_niMinDepth;
    PC_(VECTOR,DIM)                     atvVert1 = (tgNFO.m_eSide == T_(AXIS_INFO,DIM)::NEGATIVE) ? tgP1.m_atvMaxVert : tgP1.m_atvMinVert;
    C_(VECTOR,DIM)                      tvK0 = MATH::F_NEG( tgNFO.m_tvNormal );
    C_(VECTOR,DIM)                      tvNormal = (tgNFO.m_eSide == T_(AXIS_INFO,DIM)::NEGATIVE) ? tvK0 : tgNFO.m_tvNormal;
    P_(CONTACT,DIM)                     ptgContact;

    if (1 == niVertD0) //« Triangle 0 vertex touching a feature of Triangle 1
    {
        ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
        ptgContact->m_tvPos = atvVert0[0];
        ptgContact->m_tvNormal = tvNormal;
        ptgContact->m_tyT0 = tgNFO.m_tyMinT;
        ptgContact->m_tyDepth = TYPE(0.0);

        ++ptgPacket->m_niContact;

        return (TgS_OK);
    };

    if (1 == niVertD1) //« Triangle 1 vertex touching a feature of Triangle 0
    {
        ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
        ptgContact->m_tvPos = MATH::F_ADD( atvVert1[0], tvOffset );
        ptgContact->m_tvNormal = tvNormal;
        ptgContact->m_tyT0 = tgNFO.m_tyMinT;
        ptgContact->m_tyDepth = TYPE(0.0);

        ++ptgPacket->m_niContact;

        return (TgS_OK);
    };

    if (2 == niVertD0 && 2 == niVertD1) //« Edge-Edge Contact
    {
        TgINT                               niCode, niPoint;
        T_(VECTOR,DIM)                      tvS0,tvS1;
        
        niCode = F_Internal_Intersect(
            &tvS0,&tvS1, atvVert0[0],
            MATH::F_SUB( atvVert0[1], atvVert0[0] ),
            MATH::F_ADD( tvOffset,    atvVert1[0] ),
            MATH::F_SUB( atvVert1[1], atvVert1[0] )
        );

        niPoint = MIN( niCode, ptgPacket->m_niMaxContact - ptgPacket->m_niContact );

        switch (niPoint) {
            case 2:
                ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
                ptgContact->m_tvPos = tvS1;
                ptgContact->m_tvNormal = tvNormal;
                ptgContact->m_tyT0 = tgNFO.m_tyMinT;
                ptgContact->m_tyDepth = TYPE(0.0);

                ++ptgPacket->m_niContact;

            case 1:
                ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
                ptgContact->m_tvPos = tvS0;
                ptgContact->m_tvNormal = tvNormal;
                ptgContact->m_tyT0 = tgNFO.m_tyMinT;
                ptgContact->m_tyDepth = TYPE(0.0);

                ++ptgPacket->m_niContact;

                return (niCode != niPoint ? TgS_MAXCONTACTS : TgS_OK);

            default:
                return (TgE_NOINTERSECT);
        };
    };

    TTgCLIP_LIST<TYPE,DIM>              tgCL;

    if (2 == niVertD0 && 3 == niVertD1) //« Edge-Face Contact
    {
        TTgCLF_STLN<TYPE,DIM,1,1>::DO( &tgCL, tgST1, MATH::F_SUB( atvVert0[0], tvOffset ), MATH::F_SUB( atvVert0[1], atvVert0[0] ) );
        C_TgINT niPoint = MIN( tgCL.m_niPoint, ptgPacket->m_niMaxContact - ptgPacket->m_niContact );

        switch (niPoint) {
            case 2:
                ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
                ptgContact->m_tvPos = MATH::F_ADD( tgCL.m_ptvPoint[1], tvOffset );
                ptgContact->m_tvNormal = tvNormal;
                ptgContact->m_tyT0 = tgNFO.m_tyMinT;
                ptgContact->m_tyDepth = TYPE(0.0);

                ++ptgPacket->m_niContact;

            case 1:
                ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
                ptgContact->m_tvPos = MATH::F_ADD( tgCL.m_ptvPoint[0], tvOffset );
                ptgContact->m_tvNormal = tvNormal;
                ptgContact->m_tyT0 = tgNFO.m_tyMinT;
                ptgContact->m_tyDepth = TYPE(0.0);

                ++ptgPacket->m_niContact;

                return (tgCL.m_niPoint != niPoint ? TgS_MAXCONTACTS : TgS_OK);

            default:
                return (TgE_NOINTERSECT);
        };
    };

    if (3 == niVertD0 && 2 == niVertD1) //« Face-Edge Contact
    {
        TTgCLF_STLN<TYPE,DIM,1,1>::DO( &tgCL, tgST0, MATH::F_ADD( tvOffset, atvVert1[0] ), MATH::F_SUB( atvVert1[1], atvVert1[0] ) );
        C_TgINT niPoint = MIN( tgCL.m_niPoint, ptgPacket->m_niMaxContact - ptgPacket->m_niContact );

        switch (niPoint) {
            case 2:
                ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
                ptgContact->m_tvPos = tgCL.m_ptvPoint[1];
                ptgContact->m_tvNormal = tvNormal;
                ptgContact->m_tyT0 = tgNFO.m_tyMinT;
                ptgContact->m_tyDepth = TYPE(0.0);

                ++ptgPacket->m_niContact;

            case 1:
                ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);
                ptgContact->m_tvPos = tgCL.m_ptvPoint[0];
                ptgContact->m_tvNormal = tvNormal;
                ptgContact->m_tyT0 = tgNFO.m_tyMinT;
                ptgContact->m_tyDepth = TYPE(0.0);

                ++ptgPacket->m_niContact;

                return (tgCL.m_niPoint != niPoint ? TgS_MAXCONTACTS : TgS_OK);

            default:
                return (TgE_NOINTERSECT);
        };
    };

    TgASSERT(3 == niVertD0 && 3 == niVertD1)

    F_Clip( &tgCL, tgST0,tgST1.Query_PT() );

    return (F_Internal_CoP( ptgPacket, tgCL, tgNFO.m_tyMinT, tvNormal ));
};

template TgRESULT F_Contact_Sweep( PC_TgF4CONTACT_PACKET, P_TgFLOAT32, CR_TgF4STRI, CR_TgF4STRI, CR_TgF4DELTA );


// ============================================================================================================================== //

}; // END COL //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}; // END TGS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[an error occurred while processing this directive]