[an error occurred while processing this directive]
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // // // Project: Talina Gaming System (TgS) (∂) // File: TgS Collision - Cylinder-Box.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: tgBX0: Box primitives // Input: tgCY0: Cylinder primitives // Output: tgAxS: Structure holds the resulting axis separation information necessary to create a contact set. // Output: bIsContained: A boolean array indicating which faces, if projected along their normal, fully contain the cylinder. // Return: False if a separating axis exists, true otherwise // ------------------------------------------------------------------------------------------------------------------------------ // template <typename TYPE, int DIM> TgBOOL F_Axis_Seperation( PC_(AXIS_RESULT,DIM) ptgAxS, PC_TgBOOL bIsContained, CR_(BOX,DIM) tgBX0, CR_(CYLINDER,DIM) tgCY0 ) { TgASSERT( tgCY0.Is_Valid() && tgBX0.Is_Valid() ) TYPE tyTest; // Construct the difference vector between the two origins and calculate the reference frame projections. C_(VECTOR,DIM) tvDS = MATH::F_SUB( tgBX0.Query_Origin(), tgCY0.Query_Origin() ); const TYPE tyDS_CYA = MATH::F_DOT( tvDS, tgCY0.Query_AxisUnit() ); const TYPE tyDS_BX0 = MATH::F_DOT( tvDS, tgBX0.Query_AxisUnit0() ); const TYPE tyDS_BX1 = MATH::F_DOT( tvDS, tgBX0.Query_AxisUnit1() ); const TYPE tyDS_BX2 = MATH::F_DOT( tvDS, tgBX0.Query_AxisUnit2() ); // == Containment Test =================================================================== // To allow for quick selection of a planar contact surface, test to see if the cylinder's projection onto each of the // box's faces is entirely contained. A similar test to see if the box is fully contained by the cylinder's cap surface. const TYPE tyABS_CYA_BX0 = P::ABS( MATH::F_DOT(tgCY0.Query_AxisUnit(),tgBX0.Query_AxisUnit0()) ); const TYPE tyABS_CYA_BX1 = P::ABS( MATH::F_DOT(tgCY0.Query_AxisUnit(),tgBX0.Query_AxisUnit1()) ); const TYPE tyABS_CYA_BX2 = P::ABS( MATH::F_DOT(tgCY0.Query_AxisUnit(),tgBX0.Query_AxisUnit2()) ); const TYPE tyABS_CYC_BX0 = P::SQRT( tyABS_CYA_BX2*tyABS_CYA_BX2 + tyABS_CYA_BX1*tyABS_CYA_BX1 ); const TYPE tyABS_CYC_BX1 = P::SQRT( tyABS_CYA_BX2*tyABS_CYA_BX2 + tyABS_CYA_BX0*tyABS_CYA_BX0 ); const TYPE tyABS_CYC_BX2 = P::SQRT( tyABS_CYA_BX1*tyABS_CYA_BX1 + tyABS_CYA_BX0*tyABS_CYA_BX0 ); const TYPE tyABS_CY_BX0 = tgCY0.Query_Radius()*tyABS_CYC_BX0 + tgCY0.Query_Extent()*tyABS_CYA_BX0; const TYPE tyABS_CY_BX1 = tgCY0.Query_Radius()*tyABS_CYC_BX1 + tgCY0.Query_Extent()*tyABS_CYA_BX1; const TYPE tyABS_CY_BX2 = tgCY0.Query_Radius()*tyABS_CYC_BX2 + tgCY0.Query_Extent()*tyABS_CYA_BX2; bIsContained[0] = (tgBX0.Query_Extent0() >= tyDS_BX0 + tyABS_CY_BX0) && (tyABS_CY_BX0 - tyDS_BX0 <= tgBX0.Query_Extent0()); bIsContained[1] = (tgBX0.Query_Extent1() >= tyDS_BX1 + tyABS_CY_BX1) && (tyABS_CY_BX1 - tyDS_BX1 <= tgBX0.Query_Extent1()); bIsContained[2] = (tgBX0.Query_Extent2() >= tyDS_BX2 + tyABS_CY_BX2) && (tyABS_CY_BX2 - tyDS_BX2 <= tgBX0.Query_Extent2()); // == Axis Separation Tests ============================================================== ptgAxS->m_tyDepth = -LIMITS<TYPE>::MAX; // -- Axis: Box Face/Plane Normals ------------------------- // Box Axis #0 tyTest = P::ABS( tyDS_BX0 ) - (tgBX0.Query_Extent0() + tyABS_CY_BX0); if (tyTest > TYPE(0.0)) { return (TgFALSE); }; if (tyTest > ptgAxS->m_tyDepth) { ptgAxS->m_tvNormal = tyDS_BX0 > TYPE(0.0) ? MATH::F_NEG( tgBX0.Query_AxisUnit0() ) : tgBX0.Query_AxisUnit0(); ptgAxS->m_tyDepth = tyTest; ptgAxS->m_iAxis = 0; if (bIsContained[1] && bIsContained[2]) { return (TgTRUE); }; }; // Box Axis #1 tyTest = P::ABS( tyDS_BX1 ) - (tgBX0.Query_Extent1() + tyABS_CY_BX1); if (tyTest > TYPE(0.0)) { return (TgFALSE); }; if (tyTest > ptgAxS->m_tyDepth) { ptgAxS->m_tvNormal = tyDS_BX1 > TYPE(0.0) ? MATH::F_NEG( tgBX0.Query_AxisUnit1() ) : tgBX0.Query_AxisUnit1(); ptgAxS->m_tyDepth = tyTest; ptgAxS->m_iAxis = 1; if (bIsContained[0] && bIsContained[2]) { return (TgTRUE); }; }; // Box Axis #2 tyTest = P::ABS( tyDS_BX2 ) - (tgBX0.Query_Extent2() + tyABS_CY_BX2); if (tyTest > TYPE(0.0)) { return (TgFALSE); }; if (tyTest > ptgAxS->m_tyDepth) { ptgAxS->m_tvNormal = tyDS_BX2 > TYPE(0.0) ? MATH::F_NEG( tgBX0.Query_AxisUnit2() ) : tgBX0.Query_AxisUnit2(); ptgAxS->m_tyDepth = tyTest; ptgAxS->m_iAxis = 2; if (bIsContained[0] && bIsContained[1]) { return (TgTRUE); }; }; // -- Axis: Cylinder Primary Axis -------------------------- tyTest = P::ABS( tyDS_CYA ) - tgCY0.Query_Extent(); tyTest -= tgBX0.Query_Extent0()*tyABS_CYA_BX0; tyTest -= tgBX0.Query_Extent1()*tyABS_CYA_BX1; tyTest -= tgBX0.Query_Extent2()*tyABS_CYA_BX2; if (tyTest > TYPE(0.0)) { return (TgFALSE); }; if (tyTest > ptgAxS->m_tyDepth) { ptgAxS->m_tvNormal = tyDS_CYA > TYPE(0.0) ? MATH::F_NEG( tgCY0.Query_AxisUnit() ) : tgCY0.Query_AxisUnit(); ptgAxS->m_tyDepth = tyTest; ptgAxS->m_iAxis = 3; }; // -- Axis: Cylinder Radial Direction ---------------------- // The cross-product of the three box edges and the cylinder axis is by definition a radial direction as well. Therefore, it // is not necessary to examine those axes outside of the radial test. T_(VECTOR,DIM) tvCY0, tvBX1; T_(VECTOR,DIM) tvNM; tyTest = F_ClosestSq( &tvBX1, &tvCY0, tgBX0, tgCY0.Query_Segment() ); if (tyTest >= TYPE(0.0)) { const TYPE tyK0 = MATH::F_DOT( MATH::F_SUB( tvCY0, tvBX1 ), tgCY0.Query_AxisUnit() ); tvNM = MATH::F_NORM( MATH::F_SUB( MATH::F_SUB( tvCY0, tvBX1 ), MATH::F_MUL( tyK0, tgCY0.Query_AxisUnit() ) ) ); const TYPE tyDS_N = MATH::F_DOT( tvDS, tvNM ); const TYPE tyBX0_N = tgBX0.Query_Extent0()*P::ABS( MATH::F_DOT( tgBX0.Query_AxisUnit0(), tvNM ) ); const TYPE tyBX1_N = tgBX0.Query_Extent1()*P::ABS( MATH::F_DOT( tgBX0.Query_AxisUnit1(), tvNM ) ); const TYPE tyBX2_N = tgBX0.Query_Extent2()*P::ABS( MATH::F_DOT( tgBX0.Query_AxisUnit2(), tvNM ) ); tyTest = P::ABS( tyDS_N ) - (tgCY0.Query_Radius() + tyBX0_N + tyBX1_N + tyBX2_N); if (tyTest > TYPE(0.0)) { return (TgFALSE); }; if (tyTest > ptgAxS->m_tyDepth) { ptgAxS->m_tvNormal = tyDS_N > TYPE(0.0) ? tvNM : MATH::F_NEG( tvNM ); ptgAxS->m_tyDepth = tyTest; ptgAxS->m_iAxis = 4; }; } else { // The line segment must have intersected the box. The cylinder is highly penetrated. Find the minimal distance // between any box feature and the cylinder axis. Use the difference vector as a separation axis. Box features are // the twelve edges, and the eight vertices. TYPE tyTest, tyMinSq = LIMITS<TYPE>::MAX; T_(VECTOR,DIM) tvK1, tvT0,tvT1, tvMinAxis, tvMinEdge; TTgSEGMENT<TYPE,DIM> tgEdge; for (TgINT iOrigin = 0; iOrigin < 4; ++iOrigin) { C_TgBOOL bAxis = ((0 != (iOrigin & 1)) ^ (0 != (iOrigin & 2))); tgEdge.Set_Origin(tgBX0.Calc_Point( 0 == (iOrigin&1) ? tgBX0.Query_Extent0() : -tgBX0.Query_Extent0(), 0 == (iOrigin&2) ? tgBX0.Query_Extent1() : -tgBX0.Query_Extent1(), bAxis ? tgBX0.Query_Extent2() : -tgBX0.Query_Extent2() )); tvK1 = 0 != (iOrigin&1) ? tgBX0.Query_AxisUnit0() : MATH::F_NEG( tgBX0.Query_AxisUnit0() ) ; tgEdge.Set_DirN( MATH::F_MUL( tgBX0.Query_Extent0(), tvK1 ) ); tyTest = F_ClosestSq( &tvT0,&tvT1, tgCY0.Query_Segment(), tgEdge ); if (tyTest < tyMinSq) { tyMinSq = tyTest; tvMinAxis = tvT0; tvMinEdge = tvT1; }; tvK1 = 0 != (iOrigin&2) ? tgBX0.Query_AxisUnit1() : MATH::F_NEG( tgBX0.Query_AxisUnit1() ) ; tgEdge.Set_DirN( MATH::F_MUL( tgBX0.Query_Extent1(), tvK1 ) ); tyTest = F_ClosestSq( &tvT0,&tvT1, tgCY0.Query_Segment(), tgEdge ); if (tyTest < tyMinSq) { tyMinSq = tyTest; tvMinAxis = tvT0; tvMinEdge = tvT1; }; tvK1 = 0 != (iOrigin&1) ? tgBX0.Query_AxisUnit2() : MATH::F_NEG( tgBX0.Query_AxisUnit2() ) ; tgEdge.Set_DirN( MATH::F_MUL( tgBX0.Query_Extent2(), tvK1 ) ); tyTest = F_ClosestSq( &tvT0,&tvT1, tgCY0.Query_Segment(), tgEdge ); if (tyTest < tyMinSq) { tyMinSq = tyTest; tvMinAxis = tvT0; tvMinEdge = tvT1; }; }; C_(VECTOR,DIM) tvK2 = MATH::F_SUB( tvMinAxis, tvMinEdge ); const TYPE tyK0 = MATH::F_DOT( tvK2, tgCY0.Query_AxisUnit() ); C_(VECTOR,DIM) tvK3 = MATH::F_MUL( tyK0, tgCY0.Query_AxisUnit() ); tvNM = MATH::F_NORM( MATH::F_SUB( tvK2, tvK3 ) ); tvT0 = tgBX0.Calc_Support_Point( MATH::F_NEG( tvNM ) ); tyTest = tgCY0.Query_Radius() + MATH::F_DOT( MATH::F_SUB( tgCY0.Query_Origin(), tvT0 ), tvNM ); if (tyTest > TYPE(0.0)) { return (TgFALSE); }; if (tyTest > ptgAxS->m_tyDepth) { ptgAxS->m_tvNormal = tvNM; ptgAxS->m_tyDepth = tyTest; ptgAxS->m_iAxis = 4; }; }; // -- Axis: Cylinder Rim ----------------------------------- // Possible and if so how? return (TgTRUE); }; template TgBOOL F_Axis_Seperation( PC_TgF4AXIS_RESULT, PC_TgBOOL, CR_TgF4BOX, CR_TgF4CYLINDER ); // ============================================================================================================================== // // ---- F_Contact_Penetrate_BoxAxis --------------------------------------------------------------------------------------------- // // -- Internal Function -- // Input: tgPacket: The current series of contact points for this query-series, and contact generation parameters. // Input: tgAxS: Structure holding the resulting axis separation information necessary to create a contact set. // Input: bIsContained: A boolean array indicating which faces, if projected along their normal, fully contain the cylinder. // Input: tgBX0: Box 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_BoxAxis( PC_(CONTACT_PACKET,DIM) ptgPacket, CR_(AXIS_RESULT,DIM) tgAxS, PC_TgBOOL bIsContained, CR_(BOX,DIM) tgBX0, CR_(CYLINDER,DIM) tgCY0 ) { TgASSERT((TgSIZE)ptgPacket->m_iStride >= sizeof( P_(CONTACT,DIM) )); TgASSERT(tgCY0.Is_Valid() && tgBX0.Is_Valid()); C_TgINT niContact = ptgPacket->m_niContact; P_(CONTACT,DIM) ptgContact; // The axis separation result normal is one of the box axes with the appropriate direction (sign). C_TgINT i0 = (tgAxS.m_iAxis + 1) % 3, i1 = (tgAxS.m_iAxis + 2) % 3; const TYPE tyPlnD = MATH::F_DOT(tgBX0.Query_Origin(),tgAxS.m_tvNormal) + tgBX0.Query_Extent(tgAxS.m_iAxis); C_TgINT iFlag = (bIsContained[i0] ? 0 : (1 << i0)) | (bIsContained[i1] ? 0 : (1 << i1)); // Create a new basis set for the cylinder such that the first axis is equal to the direction of greatest penetration. const TYPE tyCYA_N = MATH::F_DOT(tgCY0.Query_AxisUnit(),tgAxS.m_tvNormal); T_(VECTOR,DIM) tvCYB0, tvCYB1; TYPE tyLength; tvCYB0 = MATH::F_NORM( &tyLength, MATH::F_SUB( MATH::F_MUL( tyCYA_N, tgCY0.Query_AxisUnit() ), tgAxS.m_tvNormal ) ); if (Near_Zero( tyLength )) { tvCYB0 = tgCY0.Query_BasisUnit0(); tvCYB1 = tgCY0.Query_BasisUnit1(); } else { tvCYB1 = MATH::F_CX( tvCYB0, tgCY0.Query_AxisUnit() ); }; // The contact generator is split into two cases depending on the orientation of the cylinder to the separation axis. // 1. Cylinder is standing on the resulting plane (ie. the angle to the plane is greater than 45) // 2. Cylinder is resting/lying on the plane (ie. the angle to the plane is less than 45) // The reason for the separation is in how the extra contacts (outside of the deepest point of penetration) are created. In // the first case the extra points are created around the rim of the cylinder's penetrating cap. The second case has a // segment running parallel to the primary axis along the exterior of the cylinder originating with the point of largest // penetration. if (P::ABS( tyCYA_N ) <= KF32_SQRT1_2) { // The cylinder is considered to be lying down on the surface of the box. const TYPE tyCYB0_N = MATH::F_DOT(tvCYB0,tgAxS.m_tvNormal); const TYPE tyInvCYB0_N = TYPE(1.0) / tyCYB0_N; T_(VECTOR,DIM) tvP0, tvP1, tvContact; TYPE tyT0 = TYPE(0.0),tyT1 = TYPE(0.0), tyTMP, tyDepth; // Construct the termination points of the cylinder axis and project them onto the box face/surface. The box face is // considered to be the control in determining the contact surface because of the choice of axis. For instance if a // segment was taken parallel to the axis, originating at the point of deepest penetration it could have a zero contact // solution with a thin box. Instead, the intersecting volume is examined, and the surface on the box is used to create // the contact solution. const TYPE tyK0 = (MATH::F_DOT(tvP0,tgAxS.m_tvNormal) - tyPlnD)*tyInvCYB0_N; const TYPE tyK1 = (MATH::F_DOT(tvP0,tgAxS.m_tvNormal) - tyPlnD)*tyInvCYB0_N; tvP0 = MATH::F_SUB( MATH::F_SUB( tgCY0.Query_Origin(), tgCY0.Query_HalfAxis() ), MATH::F_MUL( tyK0, tvCYB0 ) ); tvP1 = MATH::F_SUB( MATH::F_ADD( tgCY0.Query_Origin(), tgCY0.Query_HalfAxis() ), MATH::F_MUL( tyK1, tvCYB0 ) ); if (0 == iFlag || TTgCLP_BXLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgBX0,iFlag, tvP0,MATH::F_SUB( tvP1, tvP0 ) ) >= 0) { // Point #0 tvContact = MATH::F_ADD( MATH::F_MUL( TYPE(1.0) - tyT0, tvP0 ), MATH::F_MUL( tyT0, tvP1 ) ); tyTMP = MATH::F_DOT( MATH::F_SUB( tvContact, tgCY0.Query_Origin() ), tvCYB0 ); C_(VECTOR,DIM) tvK0 = MATH::F_SUB( tvContact, MATH::F_MUL( tyTMP, tvCYB0 ) ); const TYPE tyK2 = MATH::F_DOT( tvK0, tgAxS.m_tvNormal ) - tyPlnD; tyDepth = tgCY0.Query_Radius() + P::FSEL( tyK2, -tyTMP, tyTMP ); if (tyDepth> 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( tvContact, MATH::F_MUL( tyDepth, tvCYB0 ) ); ptgContact->m_tvNormal = tgAxS.m_tvNormal; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tyCYB0_N*tyDepth; ++ptgPacket->m_niContact; }; // Point #1 tvContact = MATH::F_ADD( MATH::F_MUL( TYPE(1.0) - tyT1, tvP0 ), MATH::F_MUL( tyT1, tvP1 ) ); tyTMP = MATH::F_DOT( MATH::F_SUB( tvContact, tgCY0.Query_Origin() ), tvCYB0 ); C_(VECTOR,DIM) tvK1 = MATH::F_SUB( tvContact, MATH::F_MUL( tyTMP, tvCYB0 ) ); const TYPE tyK3 = MATH::F_DOT( tvK1, tgAxS.m_tvNormal ) - tyPlnD; tyDepth = tgCY0.Query_Radius() + P::FSEL( tyK3, -tyTMP, tyTMP ); if (tyDepth> 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( tvContact, MATH::F_MUL( tyDepth, tvCYB0 ) ); ptgContact->m_tvNormal = tgAxS.m_tvNormal; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tyCYB0_N*tyDepth; ++ptgPacket->m_niContact; }; }; return (niContact == ptgPacket->m_niContact ? TgE_NOINTERSECT : TgS_OK); }; TYPE tyT0 = TYPE(0.0), tyT1 = TYPE(0.0), tyDepth; // Example: A box is resting, unstable, on the edge of circular table (the centre of gravity when projected through the local // force field does not rest on the table). The desired reaction is for the box is slowly tilt and slide off the table // surface, the box contact face remaining tangential to the table rim at all times // In this example the defining element of the contact surface is the box face itself. The method used to find this surface // is to take the non-seperating box axis, the resulting faces defined by them, and find how they intersect the cylinder's // cap surface. The cylinder cap is assumed to be the interacting surface since other separation axis should be more // dominant in the other cases. // Example: A roll of toilet paper is resting on top of one of those perpetually ever-present game crates. // In this case the non-contact box faces are completely independent of the cylinder volume/space. The defining element of // the contact surface is the cylindrical cap area. In this case the previously mentioned method of using the direction of // greatest penetration, followed by radially equi-distant rim points provides the best solution. const TYPE tyExtent = P::FSEL( tyCYA_N, -tgCY0.Query_Extent(), tgCY0.Query_Extent() ); C_(VECTOR,DIM) tvK2 = MATH::F_MUL( tyExtent, tgCY0.Query_AxisUnit() ); C_(VECTOR,DIM) tvCentreW = MATH::F_ADD( tgCY0.Query_Origin(), tvK2 ); C_(VECTOR,DIM) tvMaxPoint = MATH::F_ADD( tvCentreW, MATH::F_MUL( tgCY0.Query_Radius(), tvCYB0 ) ); TgINT iIdx0 = i0, iIdx1 = i1; T_(VECTOR,DIM) tvInsideDirN, tvX0; TgBOOL bNewBasisSet = TgFALSE; tvInsideDirN = MATH::F_0<TYPE,DIM>(); // First test to see if those box faces not defined by the separation axis form a contact surface with the cylinder. for (TgINT niCount = 0; niCount < 2; ++niCount, SWAP( iIdx0, iIdx1 )) { if (bIsContained[iIdx0]) { // If the cylinder is known to be entirely contained in the orthogonal face, then intersection is impossible. continue; }; // Find the line of intersection between the near cylinder cap plane and the two box faces defined by one axis. // The direction of the interface will remain the same for both faces, only the origin will be different. T_(LINE,DIM) tgL1; T_(VECTOR,DIM) tvK4; MATH::F_UCX( &tvK4, tgCY0.Query_AxisUnit(), tgBX0.Query_AxisUnit( iIdx0 ) ); tgL1.Set_DirN( tvK4 ); const TYPE tyCYA_IX0 = MATH::F_DOT( tgCY0.Query_AxisUnit(), tgBX0.Query_AxisUnit( iIdx0 ) ); const TYPE tyDet = TYPE(1.0) - tyCYA_IX0*tyCYA_IX0; if (Near_Zero( tyDet )) { continue; }; const TYPE tyBXO_IX0 = MATH::F_DOT( tgBX0.Query_Origin(), tgBX0.Query_AxisUnit( iIdx0 ) ); const TYPE tyPlnD_CYA = MATH::F_DOT( tvCentreW, tgCY0.Query_AxisUnit() ); const TYPE tyInvDet = TYPE(1.0) / tyDet; const TYPE tyDN_IX1 = MATH::F_DOT( tgL1.Query_DirN(), tgBX0.Query_AxisUnit( iIdx1 ) ); for (TgINT niFace = 0; niFace < 2; ++niFace) { const TYPE tyPlnD_IX0 = tyBXO_IX0 + (niFace == 0 ? -tgBX0.Query_Extent( iIdx0 ) : tgBX0.Query_Extent( iIdx0 )); const TYPE tyL0 = (tyPlnD_CYA - tyCYA_IX0*tyPlnD_IX0)*tyInvDet; const TYPE tyL1 = (tyPlnD_IX0 - tyCYA_IX0*tyPlnD_CYA)*tyInvDet; C_(VECTOR,DIM) tvK5 = MATH::F_MUL( tyL0, tgCY0.Query_AxisUnit() ); C_(VECTOR,DIM) tvK6 = MATH::F_MUL( tyL1, tgBX0.Query_AxisUnit( iIdx0 ) ); // F_Clip the resulting interface line to the cylindrical interior. tgL1.Set_Origin( MATH::F_ADD( tvK5, tvK6 ) ); if (TgFAILED(F_Clip( &tyT0,&tyT1, static_cast<CR_(TUBE,DIM)>(tgCY0), tgL1 ))) { continue; }; // Find the min and max values along the face by examining the projection onto the opposing orthogonal axis. C_(VECTOR,DIM) tvK3 = MATH::F_SUB( tgBX0.Query_Origin(), tgL1.Query_Origin() ); const TYPE tyDist = MATH::F_DOT( tvK3, tgBX0.Query_AxisUnit( iIdx1 ) ); if (!Near_Zero(tyDN_IX1)) { TYPE tyMin,tyMax; if (tyDN_IX1 > TYPE(0.0)) { tyMin = (tyDist - tgBX0.Query_Extent( iIdx1 )) / tyDN_IX1; tyMax = (tyDist + tgBX0.Query_Extent( iIdx1 )) / tyDN_IX1; } else { tyMin = (tyDist + tgBX0.Query_Extent( iIdx1 )) / tyDN_IX1; tyMax = (tyDist - tgBX0.Query_Extent( iIdx1 )) / tyDN_IX1; }; tyT0 = P::FSEL( tyMin - tyT0, tyMin, P::FSEL( tyT0 - tyMax, tyMax, tyT0 ) ); tyT1 = P::FSEL( tyMin - tyT1, tyMin, P::FSEL( tyT1 - tyMax, tyMax, tyT1 ) ); }; T_(VECTOR,DIM) tvContact; TgBOOL bAddNormal = TgFALSE; TYPE tyDepth; tvContact = MATH::F_ADD( tgL1.Query_Origin(), MATH::F_MUL( tyT0, tgL1.Query_DirN() ) ); tyDepth = tyPlnD - MATH::F_DOT( tvContact, tgAxS.m_tvNormal ); // Point #0 if (tyDepth > 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 = tvContact; ptgContact->m_tvNormal = tgAxS.m_tvNormal; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tyDepth; tvInsideDirN = MATH::F_ADD( tvInsideDirN, tvContact ); bAddNormal = TgTRUE; ++ptgPacket->m_niContact; }; // Point #1 - If the two clipped points are within tolerance of each other - skip the additional point. tvContact = MATH::F_ADD( tgL1.Query_Origin(), MATH::F_MUL( tyT1, tgL1.Query_DirN() ) ); tyDepth = tyPlnD - MATH::F_DOT( tvContact, tgAxS.m_tvNormal ); if (tyDepth > TYPE(0.0) && !Near_Zero( tyT0 - tyT1 )) { 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 = tvContact; ptgContact->m_tvNormal = tgAxS.m_tvNormal; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tyDepth; tvInsideDirN = MATH::F_ADD( tvInsideDirN, tvContact ); bAddNormal = TgTRUE; ++ptgPacket->m_niContact; }; if (bAddNormal && !bNewBasisSet) { // If the point of maximum penetration lies outside of the box face, then the resulting clipped point will be // along the same edge as one of the points already entered. The point's information could be made more useful. // By reconstructing the basis so that a point in the interior of the box along the arc formed between the contact // points added into the system will increase stability and prevent undue penetration into the box face. C_(VECTOR,DIM) tvK4 = MATH::F_SUB( tvMaxPoint, tgBX0.Query_Origin() ); if (P::ABS( MATH::F_DOT( tvK4,tgBX0.Query_AxisUnit( iIdx0 ) ) ) > tgBX0.Query_Extent( iIdx0 )) { bNewBasisSet |= TgTRUE; }; }; }; }; // Second - attempt to build a contact surface through the cylinder cap. if (bNewBasisSet && !Near_Zero(MATH::F_LSQ( tvInsideDirN )) && niContact != ptgPacket->m_niContact) { tvInsideDirN = MATH::F_DIV( tvInsideDirN, static_cast<TYPE>(ptgPacket->m_niContact - niContact) ); tvInsideDirN = MATH::F_SUB( tvInsideDirN, tvCentreW ); const TYPE tyIDN_CYA = MATH::F_DOT( tvInsideDirN, tgCY0.Query_AxisUnit() ); TYPE tyLength; tvCYB0 = MATH::F_NORM( &tyLength, MATH::F_SUB( tvInsideDirN, MATH::F_MUL( tyIDN_CYA, tgCY0.Query_AxisUnit() ) ) ); if (Near_Zero( tyLength )) { COUT_NOOBJ( WARNING, TgT("%-16.16s(%-48.48s): [CY][BX] - Counter Logical State\n"), TgT("Collision"), TgT("F_Contact_Penetrate_BoxAxis") ); TgCOLLISION_ASSERT( TgFALSE ); tvCYB0 = tgCY0.Query_BasisUnit0(); tvCYB1 = tgCY0.Query_BasisUnit1(); } else { tvCYB1 = MATH::F_CX( tvCYB0, tgCY0.Query_AxisUnit() ); }; }; // Create a contact point triple - equidistant along the cylinder's rim (ie. 120 between each arm ) T_(VECTOR,DIM) tvK7; tvK7 = MATH::F_MUL( tvCYB0, tgCY0.Query_Radius() ); if (0 == iFlag || TTgCLP_BXLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgBX0,iFlag, tvCentreW, tvK7 ) >=0) { T_(VECTOR,DIM) tvPnt; tvPnt = MATH::F_ADD( tvCentreW, MATH::F_MUL( tvCYB0, (0 == iFlag ? tgCY0.Query_Radius() : tyT1) ) ); tyDepth = tyPlnD - MATH::F_DOT( tvPnt, tgAxS.m_tvNormal ); if (tyDepth> 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 = tvPnt; ptgContact->m_tvNormal = tgAxS.m_tvNormal; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tyDepth; ++ptgPacket->m_niContact; }; }; if (ptgPacket->m_niContact - niContact >= 3) { // If the contact surface is defined by the box planes - then we can end here. It is necessary to allow the system to // create the point of greatest penetration as well since this will often by the point of tangential contact even when // the surface is defined by the box. return (TgS_OK); }; tvCYB1 = MATH::F_MUL( KF32_SQRT3, tvCYB1 ); tvX0 = MATH::F_MUL( -TYPE(0.5), MATH::F_ADD( tvCYB0, tvCYB1 ) ); tvK7 = MATH::F_MUL( tvX0, tgCY0.Query_Radius() ); if (0 == iFlag || TTgCLP_BXLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgBX0,iFlag, tvCentreW, tvK7 ) >=0 ) { T_(VECTOR,DIM) tvPnt; tvPnt = MATH::F_ADD( tvCentreW, MATH::F_MUL( tvX0, (0 == iFlag ? tgCY0.Query_Radius() : tyT1) ) ); tyDepth = tyPlnD - MATH::F_DOT(tvPnt,tgAxS.m_tvNormal); if (tyDepth > 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 = tvPnt; ptgContact->m_tvNormal = tgAxS.m_tvNormal; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tyDepth; ++ptgPacket->m_niContact; }; }; tvX0 = MATH::F_MUL( -TYPE(0.5), MATH::F_SUB( tvCYB0, tvCYB1 ) ); tvK7 = MATH::F_MUL( tvX0, tgCY0.Query_Radius() ); if (0 == iFlag || TTgCLP_BXLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgBX0,iFlag, tvCentreW, tvK7 ) >= 0 ) { T_(VECTOR,DIM) tvPnt; tvPnt = MATH::F_ADD( tvCentreW, MATH::F_MUL( tvX0, (0 == iFlag ? tgCY0.Query_Radius() : tyT1) ) ); tyDepth = tyPlnD - MATH::F_DOT(tvPnt,tgAxS.m_tvNormal); if (tyDepth > 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 = tvPnt; ptgContact->m_tvNormal = tgAxS.m_tvNormal; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tyDepth; ++ptgPacket->m_niContact; }; }; return (niContact == ptgPacket->m_niContact ? TgE_NOINTERSECT : TgS_OK); }; template TgRESULT F_Contact_Penetrate_BoxAxis( PC_TgF4CONTACT_PACKET, CR_TgF4AXIS_RESULT, PC_TgBOOL, CR_TgF4BOX, CR_TgF4CYLINDER ); // ============================================================================================================================== // // ---- F_Contact_Penetrate_CylAxis --------------------------------------------------------------------------------------------- // // -- Internal Function -- // Input: tgPacket: The current series of contact points for this query-series, and contact generation parameters. // Input: tgAxS: Structure holding the resulting axis separation information necessary to create a contact set. // Input: tgBX0: Box 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_CylAxis( PC_(CONTACT_PACKET,DIM) ptgPacket, CR_(AXIS_RESULT,DIM) tgAxS, CR_(BOX,DIM) tgBX0, CR_(CYLINDER,DIM) tgCY0 ) { TgASSERT((TgSIZE)ptgPacket->m_iStride >= sizeof( P_(CONTACT,DIM) )) TgASSERT(tgCY0.Is_Valid() && tgBX0.Is_Valid()) C_TgINT niContact = ptgPacket->m_niContact; P_(CONTACT,DIM) ptgContact; const TYPE tyBE0_N = tgBX0.Query_Extent0()*MATH::F_DOT( tgBX0.Query_AxisUnit0(), tgAxS.m_tvNormal ); const TYPE tyBE1_N = tgBX0.Query_Extent1()*MATH::F_DOT( tgBX0.Query_AxisUnit1(), tgAxS.m_tvNormal ); const TYPE tyBE2_N = tgBX0.Query_Extent2()*MATH::F_DOT( tgBX0.Query_AxisUnit2(), tgAxS.m_tvNormal ); C_(VECTOR,DIM) tvK0 = MATH::F_MUL( tgCY0.Query_Extent(), tgAxS.m_tvNormal ); C_(VECTOR,DIM) tvCentreW = MATH::F_SUB( tgCY0.Query_Origin(), tvK0 ); TYPE tyE0,tyE1,tyE2, tyDepth; if (P::ABS( tyBE0_N ) > P::ABS( tyBE1_N )) { if (P::ABS( tyBE1_N ) > P::ABS( tyBE2_N )) { tyE0 = P::FSEL( tyBE0_N, tgBX0.Query_Extent0(), -tgBX0.Query_Extent0() ); tyE1 = P::FSEL( tyBE1_N, tgBX0.Query_Extent1(), -tgBX0.Query_Extent1() ); tyE2 = P::FSEL( tyBE2_N, tgBX0.Query_Extent2(), -tgBX0.Query_Extent2() ); } else if (P::ABS( tyBE0_N ) > P::ABS( tyBE2_N )) { tyE0 = P::FSEL( tyBE0_N, tgBX0.Query_Extent0(), -tgBX0.Query_Extent0() ); tyE1 = P::FSEL( tyBE2_N, tgBX0.Query_Extent2(), -tgBX0.Query_Extent2() ); tyE2 = P::FSEL( tyBE1_N, tgBX0.Query_Extent1(), -tgBX0.Query_Extent1() ); } else { tyE0 = P::FSEL( tyBE2_N, tgBX0.Query_Extent2(), -tgBX0.Query_Extent2() ); tyE1 = P::FSEL( tyBE0_N, tgBX0.Query_Extent0(), -tgBX0.Query_Extent0() ); tyE2 = P::FSEL( tyBE1_N, tgBX0.Query_Extent1(), -tgBX0.Query_Extent1() ); }; } else { if (P::ABS( tyBE0_N ) > P::ABS( tyBE2_N )) { tyE0 = P::FSEL( tyBE1_N, tgBX0.Query_Extent1(), -tgBX0.Query_Extent1() ); tyE1 = P::FSEL( tyBE0_N, tgBX0.Query_Extent0(), -tgBX0.Query_Extent0() ); tyE2 = P::FSEL( tyBE2_N, tgBX0.Query_Extent2(), -tgBX0.Query_Extent2() ); } else if (P::ABS( tyBE2_N ) > P::ABS( tyBE1_N )) { tyE0 = P::FSEL( tyBE2_N, tgBX0.Query_Extent2(), -tgBX0.Query_Extent2() ); tyE1 = P::FSEL( tyBE1_N, tgBX0.Query_Extent1(), -tgBX0.Query_Extent1() ); tyE2 = P::FSEL( tyBE0_N, tgBX0.Query_Extent0(), -tgBX0.Query_Extent0() ); } else { tyE0 = P::FSEL( tyBE1_N, tgBX0.Query_Extent1(), -tgBX0.Query_Extent1() ); tyE1 = P::FSEL( tyBE2_N, tgBX0.Query_Extent2(), -tgBX0.Query_Extent2() ); tyE2 = P::FSEL( tyBE0_N, tgBX0.Query_Extent0(), -tgBX0.Query_Extent0() ); }; }; TgINT iContained = 0; for (TgINT i0 = 0; i0 < 4; i0 += 3) { TTgSEGMENT<TYPE,DIM> tgBE; // Check to see if the edge/segment origin is a valid contact point. tgBE.Set_Origin(tgBX0.Calc_Point( tyE0, i0 == 0 ? tyE1 : -tyE1, i0 == 0 ? tyE2 : -tyE2 )); tyDepth = MATH::F_DOT( MATH::F_SUB( tgBE.Query_Origin(), tvCentreW ), tgAxS.m_tvNormal ); if (tyDepth >= TYPE(0.0) && tgCY0.Is_Contained( tgBE.Query_Origin() )) { 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_SUB( tgBE.Query_Origin(), 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; iContained |= (1 << i0); }; // Build the two edges originating at the above vertex. for (TgINT i1 = 1; i1 < 3; ++i1) { T_(VECTOR,DIM) tvEnd; tvEnd = tgBX0.Calc_Point( tyE0, i1 == 1 ? tyE1 : -tyE1, i1 == 1 ? -tyE2 : tyE2 ); // If the edge origin was contained then its possible the entire edge is completely contained. Since a simple point // containment check is fast and easy to do - check to see if the other termination point is within the box - if so, // then add the contact and continue onto the next edge. if (0 != (iContained & (1 << i0))) { tyDepth = MATH::F_DOT( MATH::F_SUB( tvEnd, tvCentreW ), tgAxS.m_tvNormal ); if (tyDepth >= TYPE(0.0) && tgCY0.Is_Contained( tvEnd )) { 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_SUB( tvEnd, 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; iContained |= (1 << i1); continue; }; }; // F_Clip the resulting edge by the cylinder. This will result with either zero or two points. Check the contained // bitfield to make sure that duplicate points (the vertices) are not added into the system. CR_(TUBE,DIM) tgTB0 = static_cast<CR_(TUBE,DIM)>(tgCY0); TTgPLANE<TYPE,DIM> tgCapPlane; TYPE tyT0,tyT1; T_(VECTOR,DIM) tvPnt; tgCapPlane.Set_Normal( tgAxS.m_tvNormal ); tgCapPlane.Set_Constant( MATH::F_DOT(tgAxS.m_tvNormal,tvCentreW) ); tgBE.Set_DirN( MATH::F_SUB( tvEnd, tgBE.Query_Origin() ) ); if (TgFAILED(F_Clip( &tyT0,&tyT1, tgCapPlane, tgBE ))) { // The edge is entirely on the non-negative side of the cap plane. No intersection. Process next edge. continue; }; tgBE.Set_Origin( MATH::F_ADD( MATH::F_MUL( TYPE(1.0) - tyT0, tgBE.Query_Origin() ), MATH::F_MUL( tyT0, tvEnd ) ) ); tgBE.Set_DirN( MATH::F_MUL( tyT1 - tyT0, tgBE.Query_DirN() ) ); if (Near_Zero( tyT1 - tyT0 ) || TgFAILED(F_Clip( &tyT0,&tyT1, tgTB0, tgBE ))) { // F_Clip was reduced to a point or lies outside of the infinite cylindrical space. Process next edge. continue; }; // Point #0 - tvPnt = MATH::F_ADD( tgBE.Query_Origin(), MATH::F_MUL( tyT0, tgBE.Query_DirN() ) ); tyDepth = MATH::F_DOT( MATH::F_SUB( tvPnt, tvCentreW ), tgAxS.m_tvNormal ); if (tyDepth > TYPE(0.0) && !(Near_Zero( tyT0 ) && 0 != (iContained & !(1 << i0)))) { 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_SUB( tvPnt, 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; }; // Point #1 - tvPnt = MATH::F_ADD( tgBE.Query_Origin(), MATH::F_MUL( tyT1, tgBE.Query_DirN() ) ); tyDepth = MATH::F_DOT( MATH::F_SUB( tvPnt, tvCentreW ), tgAxS.m_tvNormal ); if (tyDepth > TYPE(0.0) && !Near_Zero( tyT0 - tyT1 ) && !(Near_One( tyT1 ) && 0 != (iContained & !(1 << i1)))) { 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_SUB( tvPnt, 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; }; // Floating point error can produce some interesting artifacts. In this case, it is possible for the two end // points to be the result of the clipping process within a tolerance value. So though the point check did not // add them as contained - they will be added here and flagged. iContained |= Near_Zero( tyT0 ) ? (1 << i0) : 0; iContained |= Near_One( tyT1 ) ? (1 << i1) : 0; }; }; return (niContact == ptgPacket->m_niContact ? TgE_NOINTERSECT : TgS_OK); }; template TgRESULT F_Contact_Penetrate_CylAxis( PC_TgF4CONTACT_PACKET, CR_TgF4AXIS_RESULT, CR_TgF4BOX, CR_TgF4CYLINDER ); // ============================================================================================================================== // // ---- F_Contact_Penetrate_CylRad ---------------------------------------------------------------------------------------------- // // -- Internal Function -- // Input: tgPacket: The current series of contact points for this query-series, and contact generation parameters. // Input: tgAxS: Structure holding the resulting axis separation information necessary to create a contact set. // Input: tgBX0: Box 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_CylRad( PC_(CONTACT_PACKET,DIM) ptgPacket, CR_(AXIS_RESULT,DIM) tgAxS, CR_(BOX,DIM) tgBX0, CR_(CYLINDER,DIM) tgCY0 ) { TgASSERT((TgSIZE)ptgPacket->m_iStride >= sizeof( P_(CONTACT,DIM) )); TgASSERT(tgCY0.Is_Valid() && tgBX0.Is_Valid()); TgCOLLISION_ASSERT( !Near_Zero(TgCOLLISION_ASSERT*tgCYA.Query_AxisUnit()) ); C_TgINT niContact = ptgPacket->m_niContact; P_(CONTACT,DIM) ptgContact; TYPE tyLength; // Make sure there is no axial component in the normal direction to be used. const TYPE tyCY0_N = MATH::F_DOT( tgAxS.m_tvNormal, tgCY0.Query_BasisUnit0() ); const TYPE tyCY1_N = MATH::F_DOT( tgAxS.m_tvNormal, tgCY0.Query_BasisUnit1() ); C_(VECTOR,DIM) tvK0 = MATH::F_MUL( tyCY0_N, tgCY0.Query_BasisUnit0() ); C_(VECTOR,DIM) tvK1 = MATH::F_MUL( tyCY1_N, tgCY0.Query_BasisUnit1() ); C_(VECTOR,DIM) tvRad = MATH::F_NORM( &tyLength, MATH::F_ADD( tvK0, tvK1 ) ); if (Near_Zero( tyLength )) { return (TgE_FAIL); }; C_(VECTOR,DIM) tvK2 = MATH::F_MUL( tgCY0.Query_Radius(), tvRad ); C_(VECTOR,DIM) tvOrg = MATH::F_ADD( tgCY0.Query_Segment().Query_Origin(), tvK2 ); TYPE tyT0,tyT1, tyDepth; T_(VECTOR,DIM) tvPlane_Point, tvPnt; if (TTgCLP_BXLN<TYPE,DIM,1,1>::DO( &tyT0,&tyT1, tgBX0, tvOrg,tgCY0.Query_Segment().Query_DirN() ) < 0) { return (TgE_NOINTERSECT); }; tvPlane_Point = tgBX0.Calc_Support_Point( MATH::F_NEG( tgAxS.m_tvNormal ) ); tvPnt = MATH::F_ADD( tvOrg, MATH::F_MUL( tyT0, tgCY0.Query_Segment().Query_DirN() ) ); tyDepth = MATH::F_DOT( MATH::F_SUB( tvPnt, tvPlane_Point ), tgAxS.m_tvNormal ); if (tyDepth> TYPE(0.0)) { ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride); ptgContact->m_tvPos = tvPnt; ptgContact->m_tvNormal = tgAxS.m_tvNormal; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tyDepth; ++ptgPacket->m_niContact; }; tvPnt = MATH::F_ADD( tvOrg, MATH::F_MUL( tyT1, tgCY0.Query_Segment().Query_DirN() ) ); tyDepth = MATH::F_DOT( MATH::F_SUB( tvPnt, tvPlane_Point ), tgAxS.m_tvNormal ); if (tyDepth> 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 = tvPnt; ptgContact->m_tvNormal = tgAxS.m_tvNormal; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tyDepth; ++ptgPacket->m_niContact; }; return (niContact == ptgPacket->m_niContact ? TgE_NOINTERSECT : TgS_OK); }; template TgRESULT F_Contact_Penetrate_CylRad( PC_TgF4CONTACT_PACKET, CR_TgF4AXIS_RESULT, CR_TgF4BOX, CR_TgF4CYLINDER ); // ============================================================================================================================== // // ---- F_Contact_Penetrate ----------------------------------------------------------------------------------------------------- // // Input: tgPacket: The current series of contact points for this query-series, and contact generation parameters. // Input: tgBX0: Box 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_(BOX,DIM) tgBX0, CR_(CYLINDER,DIM) tgCY0 ) { TgBLOCK_FCN_NOOBJ(ETgFAC_COLLISION, 0, ETgTEST_PENETRATE, (((TgUINT)ETgBOX<<16)|(TgUINT)ETgCYLINDER)); TgASSERT((TgSIZE)ptgPacket->m_iStride >= sizeof( P_(CONTACT,DIM) )); TgASSERT(tgCY0.Is_Valid() && tgBX0.Is_Valid()); if (0 == ptgPacket->m_niMaxContact || ptgPacket->m_niContact >= ptgPacket->m_niMaxContact || NULL == ptgPacket->m_ptgContact) { return (TgE_FAIL); }; // Find the minimal axis of separation, or return if the primitives are not in contact. TgBOOL bIsContained[4]; TTgAXIS_RESULT<TYPE,DIM> tgAxS; if (!F_Axis_Seperation( &tgAxS, bIsContained, tgBX0, tgCY0 )) { return (TgE_NOINTERSECT); }; TgASSERT( Near_One( MATH::F_LSQ(tgAxS.m_tvNormal) ) && tgAxS.m_tyDepth >= TYPE(0.0) ) // == Contact Generation ==================================================================================================== // switch (tgAxS.m_iAxis) { case 0: case 1: case 2: return (F_Contact_Penetrate_BoxAxis( ptgPacket, tgAxS, bIsContained, tgBX0, tgCY0 )); case 3: return (F_Contact_Penetrate_CylAxis( ptgPacket, tgAxS, tgBX0, tgCY0 )); case 4: return (F_Contact_Penetrate_CylRad( ptgPacket, tgAxS, tgBX0, tgCY0 )); }; return (TgE_FAIL); }; template TgRESULT F_Contact_Penetrate( PC_TgF4CONTACT_PACKET, CR_TgF4BOX, CR_TgF4CYLINDER ); // ============================================================================================================================== // }; // END COL ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }; // END TGS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////[an error occurred while processing this directive]