[an error occurred while processing this directive]
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //
//
//  Project:   Talina Gaming System (TgS) (∂)
//  File:      TgS Collision - Cylinder-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_Axis_Seperation ------------------------------------------------------------------------------------------------------- //
// Input:  tgST0: Space Triangle primitive
// Input:  tgCY0: Cylinder primitive
// Output: tgAxS: Structure holds the resulting axis separation information necessary to create a contact set.
// Return: False if a separating axis exists, true otherwise
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgBOOL F_Axis_Seperation( PC_(AXIS_RESULT,DIM) ptgAxS, CR_(STRI,DIM) tgST0, CR_(CYLINDER,DIM) tgCY0 )
{
    TgASSERT( tgCY0.Is_Valid() && tgST0.Is_Valid() )

    TYPE                                tyMinCyl, tyMaxCyl, tyMinTri, tyMaxTri;
    TYPE                                tyTest, tyMinDepth, tyMaxDepth;

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

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

    tgCY0.Project( &tyMinCyl, &tyMaxCyl, tgST0.Query_Normal() );

    if (tyMaxCyl < tyTriD || tyMinCyl > tyTriD)
    {
        return (TgFALSE);
    };

    ptgAxS->m_tvNormal = MATH::F_NEG( tgST0.Query_Normal() );
    ptgAxS->m_tyDepth = tyTriD - tyMinCyl;
    ptgAxS->m_iAxis = 1;

    // -- Axis: Cylinder Primary Axis --------------------------

    const TYPE                          tyNormTest = MATH::F_DOT(tgST0.Query_Normal(),tgCY0.Query_AxisUnit());

    if (!Near_One( tyNormTest ))
    {
        tyMinCyl = MATH::F_DOT(tgCY0.Query_Origin(),tgCY0.Query_AxisUnit());
        tyMaxCyl = tyMinCyl + tgCY0.Query_Extent();
        tyMinCyl -= tgCY0.Query_Extent();

        tgST0.Project( &tyMinTri, &tyMaxTri, tgCY0.Query_AxisUnit() );

        if (tyMaxCyl < tyMinTri || tyMinCyl > tyMaxTri)
        {
            return (TgFALSE);
        };

        tyMinDepth = tyMaxCyl - tyMinTri;
        tyMaxDepth = tyMaxTri - tyMinCyl;

        if (tyMinDepth > tyMaxDepth)
        {
            if (tyMaxDepth < ptgAxS->m_tyDepth)
            {
                ptgAxS->m_tvNormal = tgCY0.Query_AxisUnit();
                ptgAxS->m_tyDepth = tyMaxDepth;
                ptgAxS->m_iAxis = 2;
            };
        }
        else
        {
            if (tyMinDepth < ptgAxS->m_tyDepth)
            {
                ptgAxS->m_tvNormal = MATH::F_NEG( tgCY0.Query_AxisUnit() );
                ptgAxS->m_tyDepth = tyMinDepth;
                ptgAxS->m_iAxis = 3;
            };
        };
    };

    // -- Axis: Radial Cylindrical Sweep -----------------------

    TYPE                                tyET0,tyET1, tyCyl, tyDistSq;

    tyDistSq = F_ClosestSq( &tyET0,&tyET1, &tyCyl, tgST0, tgCY0.Query_Segment() );

    if (tyDistSq > tgCY0.Query_RadiusSq())
    {
        return (TgFALSE);
    };

    if (Is_Point_Culled( tgST0.Query_CT(), tyET0,tyET1 ))
    {
        return (TgTRUE);
    };

    C_(VECTOR,DIM)                      tvK0 = MATH::F_MUL( tyET0, tgST0.Query_Edge0() );
    C_(VECTOR,DIM)                      tvK1 = MATH::F_MUL( tyET1, tgST0.Query_Edge2() );
    T_(VECTOR,DIM)                      tvProxCyl, tvProxTri;

    tvProxCyl = MATH::F_ADD( tgCY0.Query_Segment().Query_Origin(), MATH::F_MUL( tyCyl, tgCY0.Query_Segment().Query_DirN() ) );
    tvProxTri = MATH::F_ADD( tgST0.Query_Origin(), MATH::F_SUB( tvK0, tvK1 ) );

    if (Near_Zero( tyCyl ) || Near_One( tyCyl ) )
    {
        // -- Axis: Cap-Surface Component --------------------------

        const TYPE                          tyDet = TYPE(1.0) - tyNormTest*tyNormTest;

        if (tyDet <= LIMITS<TYPE>::ROOTEPSILON)
        {
            // The plane upon which the cylinder cap rests is parallel to that of the triangle.  Return the last best result.

            return (TgTRUE);
        };

        // Define the line of intersection between the two planar surfaces.

        const TYPE                          tyInvDet = TYPE(1.0) / tyDet;

        const TYPE                          tyCylD = MATH::F_DOT(tgCY0.Query_AxisUnit(),tvProxCyl);
        const TYPE                          tyTA = tyInvDet*( tyCylD - tyNormTest*tyTriD );
        const TYPE                          tyTB = tyInvDet*( tyTriD - tyNormTest*tyCylD );

        TTgLINE<TYPE,DIM>                   tgLN;

        tgLN.Set_Origin( MATH::F_ADD( MATH::F_MUL( tyTA, tgCY0.Query_AxisUnit() ), MATH::F_MUL( tyTB, tgST0.Query_Normal() ) ) );
        tgLN.Set_DirN( MATH::F_CX( tgCY0.Query_AxisUnit(), tgST0.Query_Normal() ) );

        TTgCLIP_LIST<TYPE,DIM>              ClipList;

        F_Clip( &ClipList, tgST0, tgLN );

        if (ClipList.m_niPoint != 2)
        {
            // No intersection can exist between the triangle and cylinder cap.  Error state.  Return the last best result.

            return (TgTRUE);
        };

        // Find the minimal distance to the cap rim.

        TTgSEGMENT<TYPE,DIM>                tgG0;
        TYPE                                tyG0;

        tgG0.Set_Origin( ClipList.m_ptvPoint[0] );
        tgG0.Set_DirN( MATH::F_SUB( ClipList.m_ptvPoint[1], ClipList.m_ptvPoint[0] ) );

        tyDistSq = F_ClosestSq( &tyG0, tgG0, tvProxCyl );

        if (tyDistSq > tgCY0.Query_RadiusSq())
        {
            // Error state.  Return the last best result.

            return (TgTRUE);
        };

        tvProxTri = MATH::F_ADD( tgG0.Query_Origin(), MATH::F_MUL( tyG0, tgG0.Query_DirN() ) );
    };

    T_(VECTOR,DIM)                      tvNormal = MATH::F_NORM( &tyTest, MATH::F_SUB( tvProxCyl, tvProxTri ) );

    if (Near_Zero( tyTest ))
    {
        return (TgTRUE); // Error state.  Return the last best result.
    };

    tyTest = P::FSEL( tgCY0.Query_Radius() - tyTest, tgCY0.Query_Radius() - tyTest, TYPE(0.0) );

    if (1 == ptgAxS->m_iAxis && Near_One( MATH::F_DOT( tgST0.Query_Normal(), tvNormal ) ))
    {
        return (TgTRUE); // Bias towards having the contacts created for a triangle contact.
    };

    if (tyTest < ptgAxS->m_tyDepth)
    {
        ptgAxS->m_tvNormal = tvNormal;
        ptgAxS->m_tvPoint = tvProxCyl;
        ptgAxS->m_tyDepth = tyTest;
        ptgAxS->m_iAxis = 4;
    };

    return (TgTRUE);
};

template TgBOOL F_Axis_Seperation( PC_TgF4AXIS_RESULT, CR_TgF4STRI, CR_TgF4CYLINDER );


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

// ---- F_Internal_ClipTriToCyl ------------------------------------------------------------------------------------------------- //
//  -- Internal Function -- 
// Input:  tgST0: Space Triangle primitive
// Input:  tgCY0: Cylinder primitive
// Output: tgCL: The resulting points of the clipping operation
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM> TgFORCEINLINE
TgBOOL F_Internal_ClipTriToCyl( PC_(CLIP_LIST,DIM) ptgCL, CR_(STRI,DIM) tgST0, CR_(CYLINDER,DIM) tgCY0 )
{
    TgBOOL                              bC0,bC1, bTest0,bTest1, bRet = TgFALSE;
    TYPE                                tyT0,tyT1;

    // Triangle Edge 0 

    bRet |= TTgCLP_CYLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgCY0, tgST0.Query_Point0(),tgST0.Query_Edge0() );

    bC0 = Near_Zero( tyT0 );
    bC1 = Near_One( tyT0 );
    bTest0 = (bC0 && !tgST0.Test_Point0()) || (bC1 && !tgST0.Test_Point1()) || (!bC0 && !bC1 && !tgST0.Test_Edge0());

    bC0 = Near_Zero( tyT1 );
    bC1 = Near_One( tyT1 );
    bTest1 = (bC0 && !tgST0.Test_Point0()) || (bC1 && !tgST0.Test_Point1()) || (!bC0 && !bC1 && !tgST0.Test_Edge0());

    if (!bTest0)
    {
        ptgCL->m_ptvPoint[ptgCL->m_niPoint++] = MATH::F_ADD( tgST0.Query_Point0(), MATH::F_MUL( tyT0, tgST0.Query_Edge0() ) );
    };

    if (!bTest1)
    {
        ptgCL->m_ptvPoint[ptgCL->m_niPoint++] = MATH::F_ADD( tgST0.Query_Point0(), MATH::F_MUL( tyT1, tgST0.Query_Edge0() ) );
    };


    // Triangle Edge 1 

    bRet |= TTgCLP_CYLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgCY0, tgST0.Query_Point1(),tgST0.Query_Edge1() );

    bC0 = Near_Zero( tyT0 );
    bC1 = Near_One( tyT0 );
    bTest0 = (bC0 && !tgST0.Test_Point1()) || (bC1 && !tgST0.Test_Point2()) || (!bC0 && !bC1 && !tgST0.Test_Edge1());

    bC0 = Near_Zero( tyT1 );
    bC1 = Near_One( tyT1 );
    bTest1 = (bC0 && !tgST0.Test_Point1()) || (bC1 && !tgST0.Test_Point2()) || (!bC0 && !bC1 && !tgST0.Test_Edge1());

    if (!bTest0)
    {
        ptgCL->m_ptvPoint[ptgCL->m_niPoint++] = MATH::F_ADD( tgST0.Query_Point1(), MATH::F_MUL( tyT0, tgST0.Query_Edge1() ) );
    };

    if (!bTest1)
    {
        ptgCL->m_ptvPoint[ptgCL->m_niPoint++] = MATH::F_ADD( tgST0.Query_Point1(), MATH::F_MUL( tyT1, tgST0.Query_Edge1() ) );
    };


    // Triangle Edge 2 

    bRet |= TTgCLP_CYLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgCY0, tgST0.Query_Point2(),tgST0.Query_Edge2() );

    bC0 = Near_Zero( tyT0 );
    bC1 = Near_One( tyT0 );
    bTest0 = (bC0 && !tgST0.Test_Point2()) || (bC1 && !tgST0.Test_Point0()) || (!bC0 && !bC1 && !tgST0.Test_Edge2());

    bC0 = Near_Zero( tyT1 );
    bC1 = Near_One( tyT1 );
    bTest1 = (bC0 && !tgST0.Test_Point2()) || (bC1 && !tgST0.Test_Point0()) || (!bC0 && !bC1 && !tgST0.Test_Edge2());

    if (!bTest0)
    {
        ptgCL->m_ptvPoint[ptgCL->m_niPoint++] = MATH::F_ADD( tgST0.Query_Point2(), MATH::F_MUL( tyT0, tgST0.Query_Edge2() ) );
    };

    if (!bTest1)
    {
        ptgCL->m_ptvPoint[ptgCL->m_niPoint++] = MATH::F_ADD( tgST0.Query_Point2(), MATH::F_MUL( tyT1, tgST0.Query_Edge2() ) );
    };


    return (bRet);
};


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

// ---- F_Contact_Penetrate ----------------------------------------------------------------------------------------------------- //
// Input:  tgPacket: The current series of contact points for this query-series, and contact generation parameters.
// Input:  tgST0: Space Triangle primitive
// Input:  tgCY0: Cylinder primitive - contact points are generated on this primitive
// Output: tgPacket: Points of penetration between the two primitives are added to it
// Return: Result Code
// ------------------------------------------------------------------------------------------------------------------------------ //

template <typename TYPE, int DIM>
TgRESULT F_Contact_Penetrate( PC_(CONTACT_PACKET,DIM) ptgPacket, CR_(STRI,DIM) tgST0, CR_(CYLINDER,DIM) tgCY0 )
{
    TgBLOCK_FCN_NOOBJ(ETgFAC_COLLISION, 0, ETgTEST_PENETRATE, (((TgUINT)ETgCAPSULE<<16)|(TgUINT)ETgTRIANGLE))

    TgASSERT((TgSIZE)ptgPacket->m_iStride >= sizeof(T_(CONTACT,DIM)) && tgCY0.Is_Valid() && tgST0.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, tgST0, etgDEBUG_COLLISION_ENTERFCN );

    // Primitive Culling - Set of criteria required for the primitive to be considered penetrating the triangle.

    C_(VECTOR,DIM)                      tvK0 = MATH::F_SUB( tgCY0.Query_Origin(), tgST0.Query_Origin() );
    const TYPE                          tyDS_N = MATH::F_DOT( tgST0.Query_Normal(), tvK0 );
    const TYPE                          tyEX_N = MATH::F_DOT( tgST0.Query_Normal(), tgCY0.Query_HalfAxis() );
    const TYPE                          tyS0_N = tyDS_N - tyEX_N;
    const TYPE                          tyS1_N = tyDS_N + tyEX_N;
    const TYPE                          tyRadius = tgCY0.Query_Radius();

    if ((tyS0_N > tyRadius && tyS1_N > tyRadius) || (tyS0_N < TYPE(0.0) && tyS1_N < TYPE(0.0)))
    {
        // Either both of the capsule's end points are below the plane or more than radius above the plane.

        return (TgE_NOINTERSECT);
    };

    TgFEBUG_COLLISION_TRIANGLE( iDBG_TriID, etgDEBUG_COLLISION_PASSED_REJECT );

    // Find the minimal axis of separation, or return if the primitives are not in contact.

    TTgAXIS_RESULT<TYPE,DIM>            tgAxS;

    if (!F_Axis_Seperation( &tgAxS, tgST0,tgCY0 ))
    {
        return (TgE_NOINTERSECT);
    };

    TgASSERT( Near_One( MATH::F_LSQ(tgAxS.m_tvNormal) ) && tgAxS.m_tyDepth >= TYPE(0.0) )

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

    //C_TgINT                             niContact = ptgPacket->m_niContact;
    P_(CONTACT,DIM)                     ptgContact;

    if (tgAxS.m_iAxis >= 4)
    {
        // -- Axis: Cap-Surface Component --------------------------------------------------------------------------------------

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

        ptgContact->m_tvPos = MATH::F_SUB( tgAxS.m_tvPoint, MATH::F_MUL( tgCY0.Query_Radius(), tgAxS.m_tvNormal ) );
        ptgContact->m_tvNormal = tgAxS.m_tvNormal;
        ptgContact->m_tyT0 = TYPE(0.0);
        ptgContact->m_tyDepth = tgAxS.m_tyDepth;

        ++ptgPacket->m_niContact;

        return (TgS_OK);
    };

    if (tgAxS.m_iAxis >= 2)
    {
        // -- Axis: Cylinder Primary Axis --------------------------------------------------------------------------------------

        T_(CLIP_LIST,DIM)                   tgCL;

        if (F_Internal_ClipTriToCyl( &tgCL, tgST0,tgCY0 ) && 0 == tgCL.m_niPoint)
        {
            return (TgE_NOINTERSECT);
        };

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

        for (TgINT iIdx = 0; iIdx < niMax; ++iIdx)
        {
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);

            C_(VECTOR,DIM)                      tvK1 = MATH::F_SUB( tgCL.m_ptvPoint[iIdx], tgCY0.Query_Origin() );
            const TYPE                          tyP_AX = MATH::F_DOT( tvK1, tgCY0.Query_AxisUnit() );
            TYPE                                tyDepth = tgCY0.Query_Extent() - P::ABS( tyP_AX );

            tyDepth = P::FSEL( tyDepth, tyDepth, TYPE(0.0) );

            ptgContact->m_tvPos = MATH::F_SUB( tgCL.m_ptvPoint[iIdx], MATH::F_MUL( tyDepth, tgAxS.m_tvNormal ) );
            ptgContact->m_tvNormal = tgAxS.m_tvNormal;
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = tyDepth;

            ++ptgPacket->m_niContact;
        };

        if (0 != tgCL.m_niPoint)
        {
            return (niMax == tgCL.m_niPoint ? TgS_OK : TgS_MAXCONTACTS);
        };

        // Error catch - default to triangle normal for this error state.
    };

    // -- Triangle Normal ------------------------------------------------------------------------------------------------------

    const TYPE                          tyAX_N = MATH::F_DOT(tgCY0.Query_AxisUnit(),tgST0.Query_Normal());

    M_(VECTOR,DIM)                      tvBX0 = tgCY0.Query_BasisUnit0();
    M_(VECTOR,DIM)                      tvBX1 = tgCY0.Query_BasisUnit1();

    T_(VECTOR,DIM)                      tvT0, tvX0;
    TYPE                                tyX,tyY,tyZ;
    const TYPE                          tyUseBX = P::ABS( tyAX_N ) - (TYPE(1.0) - LIMITS<TYPE>::EPSILON);

    tvT0 = MATH::F_SUB( MATH::F_MUL( tyAX_N, tgCY0.Query_AxisUnit() ), tgST0.Query_Normal() );
    tyX = P::FSEL( tyUseBX, tvBX0.X(), tvT0.X() );
    tyY = P::FSEL( tyUseBX, tvBX0.Y(), tvT0.Y() );
    tyZ = P::FSEL( tyUseBX, tvBX0.Z(), tvT0.Z() );
    tvX0 = MATH::F_NORM( MATH::F_SETV<TYPE,DIM>( tyX,tyY,tyZ ) );

    const TYPE                          tyDN = P::FSEL( tyAX_N, TYPE(-1.0), TYPE(1.0) );
    C_(VECTOR,DIM)                      tvC0 = MATH::F_ADD( tgCY0.Query_Origin(), MATH::F_MUL( tyDN, tgCY0.Query_HalfAxis() ) );

    if (tyAX_N > TYPE(0.5))
    {
        // Method: Produce three points along symmetrical points along the cylinder rim.  If the cylinder plane is not parallel
        // to the triangle plane then one of these points must be the point of deepest penetration.

        T_(VECTOR,DIM)                      tvX1;

        tvT0 = MATH::F_CX( tvX0, tgCY0.Query_AxisUnit() );
        tyX = P::FSEL( tyUseBX, tvBX1.X(), tvT0.X() );
        tyY = P::FSEL( tyUseBX, tvBX1.Y(), tvT0.Y() );
        tyZ = P::FSEL( tyUseBX, tvBX1.Z(), tvT0.Z() );
        tvX1 = MATH::F_SETV<TYPE,DIM>( tyX,tyY,tyZ );
        MATH::F_NORM( tvX1 );
        tvX1 = MATH::F_MUL( tvX1, KF32_SQRT3 );

        // Generate the line segment for each of these points from the middle of the cylinder's end cap - and then clip it against
        // the triangle using feature reduction information.  Chose the largest t value returned to create the contact point.

        TYPE                                tyT0,tyT1, tyTest;
        TgINT                               iCode;
        C_TgINT                             niContact = ptgPacket->m_niContact;

        tvT0 = MATH::F_MUL( tgCY0.Query_Radius(), tvX0 );

        if (TTgCLF_STLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, &iCode, tgST0, tvC0,tvT0 ) >= 0 && 0 != (iCode & 12))
        {
            const TYPE                          tyL0 = 0 != (iCode & 4) ? tyT0 : -tyT1;
            const TYPE                          tyL1 = 0 != (iCode & 8) ? tyT1 : -tyT0;
            const TYPE                          tyLN = P::FSEL( tyL1 - tyL0, tyL1, tyL0 );
            C_(VECTOR,DIM)                      tvK1 = MATH::F_ADD( tvC0, MATH::F_MUL( tyLN, tvT0 ) );

            tyTest = MATH::F_DOT( MATH::F_SUB( tgST0.Query_Origin(), tvK1 ), tgST0.Query_Normal() );

            if (tyTest >= TYPE(0.0))
            {
                if (ptgPacket->m_niContact >= ptgPacket->m_niMaxContact)
                {
                    return (TgS_MAXCONTACTS);
                };

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

                ptgContact->m_tvPos = MATH::F_ADD( tvC0, MATH::F_MUL( tyLN, tvT0 ) );
                ptgContact->m_tvNormal = tgST0.Query_Normal();
                ptgContact->m_tyT0 = TYPE(0.0);
                ptgContact->m_tyDepth = tyTest;

                ++ptgPacket->m_niContact;
            };
        };

        tvT0 = MATH::F_MUL( TYPE(-0.5)*tgCY0.Query_Radius(), MATH::F_ADD( tvX0, tvX1 ) );

        if (TTgCLF_STLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, &iCode, tgST0, tvC0,tvT0 ) >= 0 && 0 != (iCode & 12))
        {
            const TYPE                          tyL0 = 0 != (iCode & 4) ? tyT0 : -tyT1;
            const TYPE                          tyL1 = 0 != (iCode & 8) ? tyT1 : -tyT0;
            const TYPE                          tyLN = P::FSEL( tyL1 - tyL0, tyL1, tyL0 );
            C_(VECTOR,DIM)                      tvK1 = MATH::F_ADD( tvC0, MATH::F_MUL( tyLN, tvT0 ) );

            tyTest = MATH::F_DOT( MATH::F_SUB( tgST0.Query_Origin(), tvK1 ), tgST0.Query_Normal() );

            if (tyTest >= TYPE(0.0))
            {
                if (ptgPacket->m_niContact >= ptgPacket->m_niMaxContact)
                {
                    return (TgS_MAXCONTACTS);
                };

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

                ptgContact->m_tvPos = MATH::F_ADD( tvC0, MATH::F_MUL( tyLN, tvT0 ) );
                ptgContact->m_tvNormal = tgST0.Query_Normal();
                ptgContact->m_tyT0 = TYPE(0.0);
                ptgContact->m_tyDepth = tyTest;

                ++ptgPacket->m_niContact;
            };
        };

        tvT0 = MATH::F_MUL( TYPE(-0.5)*tgCY0.Query_Radius(), MATH::F_SUB( tvX0, tvX1 ) );

        if (TTgCLF_STLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, &iCode, tgST0, tvC0,tvT0 ) >= 0 && 0 != (iCode & 12))
        {
            const TYPE                          tyL0 = 0 != (iCode & 4) ? tyT0 : -tyT1;
            const TYPE                          tyL1 = 0 != (iCode & 8) ? tyT1 : -tyT0;
            const TYPE                          tyLN = P::FSEL( tyL1 - tyL0, tyL1, tyL0 );
            C_(VECTOR,DIM)                      tvK1 = MATH::F_ADD( tvC0, MATH::F_MUL( tyLN, tvT0 ) );

            tyTest = MATH::F_DOT( MATH::F_SUB( tgST0.Query_Origin(), tvK1 ), tgST0.Query_Normal() );

            if (tyTest >= TYPE(0.0))
            {
                if (ptgPacket->m_niContact >= ptgPacket->m_niMaxContact)
                {
                    return (TgS_MAXCONTACTS);
                };

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

                ptgContact->m_tvPos = MATH::F_ADD( tvC0, MATH::F_MUL( tyLN, tvT0 ) );
                ptgContact->m_tvNormal = tgST0.Query_Normal();
                ptgContact->m_tyT0 = TYPE(0.0);
                ptgContact->m_tyDepth = tyTest;

                ++ptgPacket->m_niContact;
            };
        };

        if (niContact != ptgPacket->m_niContact)
        {
            return (TgS_OK);
        };

        T_(CLIP_LIST,DIM)                   tgCL;

        F_Internal_ClipTriToCyl( &tgCL, tgST0,tgCY0 );

        if (0 == tgCL.m_niPoint)
        {
            return (TgE_NOINTERSECT);
        };

        C_TgINT                             niMax = MIN( tgCL.m_niPoint, ptgPacket->m_niMaxContact - ptgPacket->m_niContact );

        tvT0 = MATH::F_MUL( TYPE(-2.0)*tgCY0.Query_Extent(), tgST0.Query_Normal() );

        for (TgINT iIdx = 0; iIdx < niMax; ++iIdx)
        {
            if (TTgCLP_CYLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgCY0, tgCL.m_ptvPoint[iIdx],tvT0 ) < 0)
            {
                continue;
            };

            if (ptgPacket->m_niContact >= ptgPacket->m_niMaxContact)
            {
                return (TgS_MAXCONTACTS);
            };

            const TYPE                          tyLN = P::FSEL( tyT1 - tyT0, tyT1, tyT0 );
            C_(VECTOR,DIM)                      tvK2 = MATH::F_ADD( tgCL.m_ptvPoint[iIdx], MATH::F_MUL( tyLN, tvT0 ) );

            tyTest = MATH::F_DOT( MATH::F_SUB( tgST0.Query_Origin(), tvK2 ), tgST0.Query_Normal() );

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

            ptgContact->m_tvPos = MATH::F_ADD( tgCL.m_ptvPoint[iIdx], MATH::F_MUL( tyLN, tvT0 ) );
            ptgContact->m_tvNormal = tgST0.Query_Normal();
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = tyTest;

            ++ptgPacket->m_niContact;
        };

        return (niContact != ptgPacket->m_niContact ? TgS_OK : TgE_NOINTERSECT);
    }
    else
    {
        C_(VECTOR,DIM)                      tvD0 = MATH::F_MUL( TYPE(-2.0)*tyDN, tgCY0.Query_HalfAxis() );
        C_(VECTOR,DIM)                      tvS0 = MATH::F_ADD( tvC0, tvX0 );
        TYPE                                tyTest, tyT0,tyT1;
        C_TgINT                             niContact = ptgPacket->m_niContact;
        TgINT                               iCode;

        if (TTgCLF_STLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, &iCode,tgST0, tvS0,tvD0 ) < 0 || 0 == (iCode & 12))
        {
            return (TgE_NOINTERSECT);
        };

        C_(VECTOR,DIM)                      tvK1 = MATH::F_ADD( tvS0, MATH::F_MUL( tyT0, tvD0 ) );

        tyTest = MATH::F_DOT( MATH::F_SUB( tgST0.Query_Origin(), tvK1 ), tgST0.Query_Normal() );

        if (0 == (iCode & 4) && tyTest >= TYPE(0.0))
        {
            ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride);

            ptgContact->m_tvPos = MATH::F_ADD( tvS0, MATH::F_MUL( tyT0, tvD0 ) );
            ptgContact->m_tvNormal = tgST0.Query_Normal();
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = tyTest;

            ++ptgPacket->m_niContact;
        };

        C_(VECTOR,DIM)                      tvK2 = MATH::F_ADD( tvS0, MATH::F_MUL( tyT1, tvD0 ) );

        tyTest = MATH::F_DOT( MATH::F_SUB( tgST0.Query_Origin(), tvK2 ), tgST0.Query_Normal() );

        if (0 == (iCode & 8) && tyTest >= TYPE(0.0))
        {
            if (ptgPacket->m_niContact >= ptgPacket->m_niMaxContact)
            {
                return (TgS_MAXCONTACTS);
            };

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

            ptgContact->m_tvPos = MATH::F_ADD( tvS0, MATH::F_MUL( tyT1, tvD0 ) );
            ptgContact->m_tvNormal = tgST0.Query_Normal();
            ptgContact->m_tyT0 = TYPE(0.0);
            ptgContact->m_tyDepth = tyTest;

            ++ptgPacket->m_niContact;
        };

        return (niContact != ptgPacket->m_niContact ? TgS_OK : TgE_NOINTERSECT);
    };
};

template TgRESULT F_Contact_Penetrate( PC_TgF4CONTACT_PACKET, CR_TgF4STRI, CR_TgF4CYLINDER );


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

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