[an error occurred while processing this directive]
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //
//
//  Project:   Talina Gaming System (TgS) (∂)
//  File:      TgS Collision - Triangle-Particle.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_Internal_Sweep -------------------------------------------------------------------------------------------------------- //
//
//   The culling situation for a particle is complicated by its parabolic path of motion.  Back face culling is done on a point by
//  point basis.  Thus, it is possible for a particle to ignore a triangle as it comes up through the back face and then collide
//  with it as it descends through the front face.  
//
// Input:  tgET0: Edge triangle primitive - also undergoing a linear translation
// Input:  tgPC0: Particle - this primitive is undergoing the sweep/translation.
// Input:  tvRV:  Relative velocity of the particle
// Output: tgContact: Contact point if one is registered for the time period.
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgRESULT F_Internal_Sweep( PC_(CONTACT,DIM) ptgContact, CR_(PARTICLE,DIM) tgPC0, M_(VECTOR,DIM) tvRV, CR_(ETRI,DIM) tgET0 )
{
    C_(VECTOR,DIM)                      tvK0 = MATH::F_SUB( tgPC0.Query_Position(), tgET0.Query_Origin() );
    const TYPE                          tyDist = MATH::F_DOT( tgET0.Query_Normal(), tvK0 );
    const TYPE                          tyA_N = MATH::F_DOT( tgET0.Query_Normal(), tgPC0.Query_Acceleration() );
    const TYPE                          tyRV_N = MATH::F_DOT (tgET0.Query_Normal(), tvRV );

    // Check to see if the particle is moving away from the triangle plane.

    if (tyDist > TYPE(0.0) && tyRV_N >= TYPE(0.0) && tyA_N >= TYPE(0.0))
    {
        return (TgE_NOINTERSECT);
    };

    if (tyDist < TYPE(0.0) && tyRV_N <= TYPE(0.0) && tyA_N <= TYPE(0.0))
    {
        return (TgE_NOINTERSECT);
    };

    TYPE                                tyT0;

    if (Near_Zero( tyA_N ))
    {
        //  The particle's acceleration is completely parallel to the triangle plane.  In this case, intersection can only exist
        // if there is a velocity component towards the triangle.  In that case, the equation is only first order (linear). If
        // the velocity's projection onto the triangle normal is positive then the particle can only intersect with the back face
        // of the triangle - a case which is ignored.

        if (tyRV_N >= TYPE(0.0) || Near_Zero( tyRV_N ))
        {
            return (TgE_NOINTERSECT);
        };

        tyT0 = -tyDist / tyRV_N;

        if (tyT0 < TYPE(0.0))
        {
            // Sanity check - this can occur because floating point error and epsilon testing.

            return (TgE_NOINTERSECT);
        };
    }
    else
    {
        //  The particle has an acceleration component towards the triangle plane.  The equation of motion is a quadratic and thus,
        // will have two solutions.  The desired answer will be the first positive root.

        const TYPE                          tyDSC = tyRV_N*tyRV_N - TYPE(2.0)*tyA_N*tyDist;

        if (tyDSC < TYPE(0.0))
        {
            //  There is no real result for the given discriminant.  This should never occur given the other logic before this
            // point.  Register an error and return a non-intersection.

            COUT_NOOBJ(
                ERROR, TgT("%-16.16s(%-48.48s): [PA][ET] Unexpected invalid discriminant in calculation.\n"), TgT("Collision"),
                TgT("F_Internal_Sweep")
            );

            return (TgE_NOINTERSECT);
        };

        //  The math dictates that only one solution can possibly satisfy the constraints on the solution.  Specifically, the
        // requirement that the velocity of the particle at the time of intersection have a negative projection on the triangle's
        // normal eliminates the second root of the solution set.
        //  N_(r + t•a,DIM) < 0.0, t = (-(r•N) ± β) / (a•N)
        //  N•r + t_(N•a,DIM) < 0.0
        //  N•r + ((-(r•N) ± β) / (a•N))T_(N•a,DIM) < 0.0
        //  N•r + (-(r•N) ± β) < 0.0
        //  ± β < 0.0
        //  β is known to always be positive since the solution space is restricted to the real plane.  The root constructed with
        // the positive square root value can never satisfy the velocity requirement, and thus, can always be ignored.

        const TYPE                          tySQRT_DSC = P::SQRT( tyDSC );

        // Check to see if the derived value for T0 would be negative, and if so, return with no intersection.

        if ((tyA_N > TYPE(0.0) && -tyRV_N < tySQRT_DSC) || (tyA_N < TYPE(0.0) && -tyRV_N > tySQRT_DSC))
        {
            return (TgE_NOINTERSECT);
        };

        tyT0 = (-tyRV_N - tySQRT_DSC) / tyA_N;
    };

    // Compute the point on the triangle plane and then test to see if its contained within the triangle itself.

    C_(VECTOR,DIM)                      tvK1 = MATH::F_ADD( tgPC0.Query_Position(), MATH::F_MUL( tvRV, tyT0 ) );

    ptgContact->m_tvPos = MATH::F_ADD( tvK1, MATH::F_MUL( tgPC0.Query_Acceleration(), tyT0*tyT0 ) );
    ptgContact->m_tvNormal = tgET0.Query_Normal();
    ptgContact->m_tyT0 = tyT0;
    ptgContact->m_tyDepth = TYPE(0.0);

    return (TgS_OK);
};

template TgRESULT F_Internal_Sweep( PC_TgF4CONTACT, CR_TgF4PARTICLE, M_TgF4VECTOR, CR_TgF4ETRI );


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

// ---- F_Contact_Sweep --------------------------------------------------------------------------------------------------------- //
// Input:  tgPacket:  Contact generation parameters
// Input:  tyPM: Current normalized time of first contact for the contact query set.
// Input:  tgET0: Edge triangle primitive - also undergoing a linear translation
// Input:  tgPC0: Particle - this primitive is undergoing the sweep/translation.
// 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_(PARTICLE,DIM) tgPC0, CR_(ETRI,DIM) tgET0, CR_(DELTA,DIM) tgDT )
{
    TgBLOCK_FCN_NOOBJ(ETgFAC_COLLISION, 0, ETgTEST_SWEEP, (((TgUINT)ETgPARTICLE<<16)|(TgUINT)ETgTRIANGLE))

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

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

    T_(CONTACT,DIM)                     tgContact;

    C_TgRESULT tgResult = F_Internal_Sweep( &tgContact, tgPC0, MATH::F_SUB( tgPC0.Query_Velocity(), tgDT.m_tvDT ), tgET0 );

    if (TgFAILED( tgResult ))
    {
        return (tgResult);
    };

    if (tgContact.m_tyT0 > *ptyPM + ptgPacket->m_tySweepTol || !tgET0.Is_Contained( tgContact.m_tvPos ))
    {
        return (TgE_NOINTERSECT);
    };

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

    P_(CONTACT,DIM)                     ptgContact;

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

    ptgContact->m_tvPos = tgContact.m_tvPos;
    ptgContact->m_tvNormal = tgContact.m_tvNormal;
    ptgContact->m_tyT0 = tgContact.m_tyT0;
    ptgContact->m_tyDepth = tgContact.m_tyDepth;

    ++ptgPacket->m_niContact;

    return (TgS_OK);
};

template TgRESULT F_Contact_Sweep( PC_TgF4CONTACT_PACKET, P_TgFLOAT32, CR_TgF4PARTICLE, CR_TgF4ETRI, CR_TgF4DELTA );


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

// ---- F_Contact_Sweep --------------------------------------------------------------------------------------------------------- //
// Input:  tgPacket:  Contact generation parameters
// Input:  tyPM: Current normalized time of first contact for the contact query set.
// Input:  tgST0: Space triangle primitive - also undergoing a linear translation
// Input:  tgPC0: Particle - this primitive is undergoing the sweep/translation.
// 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_(PARTICLE,DIM) tgPC0, CR_(STRI,DIM) tgST0, CR_(DELTA,DIM) tgDT )
{
    TgBLOCK_FCN_NOOBJ(ETgFAC_COLLISION, 0, ETgTEST_SWEEP, (((TgUINT)ETgPARTICLE<<16)|(TgUINT)ETgTRIANGLE))

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

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

    T_(CONTACT,DIM)                     tgContact;

    C_TgRESULT tgResult = F_Internal_Sweep( &tgContact, tgPC0, MATH::F_SUB( tgPC0.Query_Velocity(), tgDT.m_tvDT ), tgST0.Query_ET() );

    if (TgFAILED( tgResult ))
    {
        return (tgResult);
    };

    if (tgContact.m_tyT0 > *ptyPM + ptgPacket->m_tySweepTol || !tgST0.Is_Contained( tgContact.m_tvPos ))
    {
        return (TgE_NOINTERSECT);
    };

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

    P_(CONTACT,DIM)                     ptgContact;

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

    ptgContact->m_tvPos = tgContact.m_tvPos;
    ptgContact->m_tvNormal = tgContact.m_tvNormal;
    ptgContact->m_tyT0 = tgContact.m_tyT0;
    ptgContact->m_tyDepth = tgContact.m_tyDepth;

    ++ptgPacket->m_niContact;

    return (TgS_OK);
};

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


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

// ---- F_Contact_Sweep --------------------------------------------------------------------------------------------------------- //
// Input:  tgPacket:  Contact generation parameters
// Input:  tyPM: Current normalized time of first contact for the contact query set.
// Input:  tgET0: Edge triangle primitive
// Input:  tgPC0: Particle - this primitive is undergoing the sweep/translation.
// 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_(ETRI,DIM) tgET0, CR_(PARTICLE,DIM) tgPC0 )
{
    TgBLOCK_FCN_NOOBJ(ETgFAC_COLLISION, 0, ETgTEST_SWEEP, (((TgUINT)ETgTRIANGLE<<16)|(TgUINT)ETgPARTICLE))

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

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

    T_(CONTACT,DIM)                     tgContact;

    C_TgRESULT tgResult = F_Internal_Sweep( &tgContact, tgPC0, tgPC0.Query_Velocity(), tgET0 );

    if (TgFAILED( tgResult ))
    {
        return (tgResult);
    };

    if (tgContact.m_tyT0 > *ptyPM + ptgPacket->m_tySweepTol || !tgET0.Is_Contained( tgContact.m_tvPos ))
    {
        return (TgE_NOINTERSECT);
    };

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

    P_(CONTACT,DIM)                     ptgContact;

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

    ptgContact->m_tvPos = tgContact.m_tvPos;
    ptgContact->m_tvNormal = tgContact.m_tvNormal;
    ptgContact->m_tyT0 = tgContact.m_tyT0;
    ptgContact->m_tyDepth = tgContact.m_tyDepth;

    ++ptgPacket->m_niContact;

    return (TgS_OK);
};

template TgRESULT F_Contact_Sweep( PC_TgF4CONTACT_PACKET, P_TgFLOAT32, CR_TgF4ETRI, CR_TgF4PARTICLE );


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

// ---- F_Contact_Sweep --------------------------------------------------------------------------------------------------------- //
// Input:  tgPacket:  Contact generation parameters
// Input:  tyPM: Current normalized time of first contact for the contact query set.
// Input:  tgST0: Space triangle primitive
// Input:  tgPC0: Particle - this primitive is undergoing the sweep/translation.
// 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_(PARTICLE,DIM) tgPC0 )
{
    TgBLOCK_FCN_NOOBJ(ETgFAC_COLLISION, 0, ETgTEST_SWEEP, (((TgUINT)ETgTRIANGLE<<16)|(TgUINT)ETgPARTICLE))

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

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

    T_(CONTACT,DIM)                     tgContact;

    C_TgRESULT tgResult = F_Internal_Sweep( &tgContact, tgPC0, tgPC0.Query_Velocity(), tgST0.Query_ET() );

    if (TgFAILED( tgResult ))
    {
        return (tgResult);
    };

    if (tgContact.m_tyT0 > *ptyPM + ptgPacket->m_tySweepTol || !tgST0.Is_Contained( tgContact.m_tvPos ))
    {
        return (TgE_NOINTERSECT);
    };

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

    P_(CONTACT,DIM)                     ptgContact;

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

    ptgContact->m_tvPos = tgContact.m_tvPos;
    ptgContact->m_tvNormal = tgContact.m_tvNormal;
    ptgContact->m_tyT0 = tgContact.m_tyT0;
    ptgContact->m_tyDepth = tgContact.m_tyDepth;

    ++ptgPacket->m_niContact;

    return (TgS_OK);
};

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


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

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