[an error occurred while processing this directive]
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // // // Project: Talina Gaming System (TgS) (∂) // File: TgS Collision - Sphere-Sphere.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". // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // // ============================================================================================================================== // // Let S0 and S1 represent two spheres, with radius R0, R1, centers C0,C1 and velocities V0, V1 respectively. // W.O.L.O.G. Perform the following calculations within S0 frame of reference. // Assume the frame of reference in non-rotational (only translation). // // SWEEP TEST // // As with all sweep tests, an arbitrary body is chosen as the principal reference frame so that only the relative // motion of one of the bodies is relevant. The solution is as follows, // (1) Create a line (RV) using the centre of the second sphere and the vector of relative motion. // (2) Form a right-sided triangle. // (a) The hypotenuse is the sum of the sphere radii. // (b) The known side is the line from the centre of the first sphere to the closest point on line RV. // (c) The length of the remaining side (along RV) is the distance from the point of closest proximity and the // point of first contact between the two spheres. // // .(C0) // /|\ // / | \ // / | \ // / | \ // / | \ // /(R0) | \(R0) // (R1)/ (R1)/ | // / / | // / / | // .--------.---------.--------------> (RV) // (C1) (P1) (P0) // // Let RV = V1 - V0, the relative velocity of S1 in the frame of reference of S0 // Let RV_N be the normalized RV vector, and RV_L the length of the original RV vector // Let DS = C1 - C0, the vector joining the two sphere centers // Let RS = R0+R1, the sum of the radii of the two spheres // // Test 1: Prepenetration - The spheres are in contact if DS•DS < RS•RS // Test 2: Movement - RV_L ~= 0, no contact takes place // Test 3: Parallel - ψ = DS•RV_N // Contact can not occur under the following conditions: // [ψ > 0] - Second sphere is moving away from the first sphere // [ψ < -(RV_L+RS)] - Motion is insufficient for them to come within proximity // Test 4: Perpendicular - φ = DS - ψ•RV_N // Contact can not occur under the following conditions: // [φ•φ > RS•RS] - The sphere path does not bring them within proximity // Result: Υ = RS•RS - φ•φ, ζ = ψ + √Υ // [ζ < RV_L] - The two sphere's contact at (ζ / RV_L) of the complete path of motion. // // ============================================================================================================================== // namespace TGS { // START TGS /////////////////////////////////////////////////////////////////////////////////////////////////////// namespace COL { // START COL /////////////////////////////////////////////////////////////////////////////////////////////////////// // ============================================================================================================================== // // ---- F_Closest --------------------------------------------------------------------------------------------------------------- // // Input: tgSP0,tgSP1: Sphere primitives // Output: tvSP0,tvSP1: Point of closest proximity on the sphere #1 and #2 respectively // Return: Minimal distance between the two primitives or negative type max if they intersect or are invalid. // ------------------------------------------------------------------------------------------------------------------------------ // template <typename TYPE, int DIM> TYPE F_Closest( PC_(VECTOR,DIM) ptvSP0, PC_(VECTOR,DIM) ptvSP1, CR_(SPHERE,DIM) tgSP0, CR_(SPHERE,DIM) tgSP1 ) { TgASSERT(tgSP0.Is_Valid() && tgSP1.Is_Valid()); TYPE tyDS; C_(VECTOR,DIM) tvDS = MATH::F_NORM( &tyDS, MATH::F_SUB( tgSP1.Query_Origin(), tgSP0.Query_Origin() ) ); const TYPE tySumRad = tgSP0.Query_Radius() + tgSP1.Query_Radius(); if (tyDS <= tySumRad) { return (-LIMITS<TYPE>::MAX); }; *ptvSP0 = MATH::F_ADD( tgSP0.Query_Origin(), MATH::F_MUL( tgSP0.Query_Radius(), tvDS ) ); *ptvSP1 = MATH::F_SUB( tgSP1.Query_Origin(), MATH::F_MUL( tgSP1.Query_Radius(), tvDS ) ); return (tyDS - tySumRad); }; template TgFLOAT32 F_Closest( PC_TgF4VECTOR, PC_TgF4VECTOR, CR_TgF4SPHERE, CR_TgF4SPHERE ); // ============================================================================================================================== // // ---- F_Contact_Penetrate ----------------------------------------------------------------------------------------------------- // // Input: tgPacket: The current series of contact points for this query-series, and contact generation parameters. // Input: tgSP0,tgSP1: Sphere primitives - contact points are generated on sphere #2 (tgSP1) // 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_(SPHERE,DIM) tgSP0, CR_(SPHERE,DIM) tgSP1 ) { TgBLOCK_FCN_NOOBJ(ETgFAC_COLLISION, 0, ETgTEST_PENETRATE, (((TgUINT)ETgSPHERE<<16)|(TgUINT)ETgSPHERE)); TgASSERT((TgSIZE)ptgPacket->m_iStride >= sizeof( P_(CONTACT,DIM) )); TgASSERT(tgSP1.Is_Valid() && tgSP0.Is_Valid()); if (0 == ptgPacket->m_niMaxContact || ptgPacket->m_niContact >= ptgPacket->m_niMaxContact || NULL == ptgPacket->m_ptgContact) { return (TgE_FAIL); }; TYPE tyDS; T_(VECTOR,DIM) tvDS = MATH::F_NORM( &tyDS, MATH::F_SUB( tgSP1.Query_Origin(), tgSP0.Query_Origin() ) ); const TYPE tySumRad = tgSP1.Query_Radius() + tgSP0.Query_Radius(); if (tyDS > tySumRad) { return (TgE_NOINTERSECT); }; if (tyDS <= LIMITS<TYPE>::EPSILON) { tvDS = T_(VECTOR,DIM)::UNIT_Y; }; P_(CONTACT,DIM) ptgContact; ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride); ptgContact->m_tvPos = MATH::F_SUB( tgSP1.Query_Origin(), MATH::F_MUL( tvDS, tgSP1.Query_Radius() ) ); ptgContact->m_tvNormal = tvDS; ptgContact->m_tyT0 = TYPE(0.0); ptgContact->m_tyDepth = tySumRad - tyDS; ++ptgPacket->m_niContact; return (TgS_OK); }; template TgRESULT F_Contact_Penetrate( PC_TgF4CONTACT_PACKET, CR_TgF4SPHERE, CR_TgF4SPHERE ); // ============================================================================================================================== // // ---- F_Contact_Sweep --------------------------------------------------------------------------------------------------------- // // Input: tgPacket: The current series of contact points for this query-series, and contact generation parameters. // Input: tyPM: Current normalized time of first contact. // Input: bPenetrate: If the swept primitives are in penetration, if true the function will return points of penetration. // Input: tgSP0,tgSP1: Sphere primitives // 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_(SPHERE,DIM) tgSP0, CR_(SPHERE,DIM) tgSP1, CR_(DELTA,DIM) tgDT ) { TgBLOCK_FCN_NOOBJ(ETgFAC_COLLISION, 0, ETgTEST_SWEEP, (((TgUINT)ETgSPHERE<<16)|(TgUINT)ETgSPHERE)); TgASSERT(tgSP1.Is_Valid() && tgSP0.Is_Valid()); if (0 == ptgPacket->m_niMaxContact || ptgPacket->m_niContact >= ptgPacket->m_niMaxContact || NULL == ptgPacket->m_ptgContact) { return (TgE_FAIL); }; // Check for pre-penetration. const TYPE tyRS = tgSP1.Query_Radius() + tgSP0.Query_Radius(); const TYPE tyRS2 = tyRS*tyRS; C_(VECTOR,DIM) tvDS = MATH::F_SUB( tgSP0.Query_Origin(), tgSP1.Query_Origin() ); const TYPE tyDS_DS = MATH::F_LSQ( tvDS ); if (tyDS_DS <= tyRS2) { // Pre-Penetration. if (*ptyPM > ptgPacket->m_tySweepTol) { ptgPacket->m_niContact = 0; }; *ptyPM = TYPE(0.0); C_TgBOOL bPenetrate = TgTRUE == ptgPacket->m_bReport_Penetration; if (bPenetrate && TgS_MAXCONTACTS == F_Contact_Penetrate( ptgPacket, tgSP0, tgSP1 )) { return (TgE_PREPENETRATION_MAXCONTACTS); }; return (TgE_PREPENETRATION); }; TYPE tyTMPC = tgDT.m_tyDT - LIMITS<TYPE>::EPSILON; const TYPE tyDS_UDT = MATH::F_DOT(tvDS,tgDT.m_tvUDT); tyTMPC = P::FSEL( tyTMPC, tyDS_UDT - LIMITS<TYPE>::EPSILON, TYPE(-1.0) ); // Negligible distance towards each other. tyTMPC = P::FSEL( tyTMPC, tyRS + *ptyPM*tgDT.m_tyDT - tyDS_UDT, TYPE(-1.0) ); // Separation along displacement is too large. tyTMPC = P::FSEL( tyTMPC, tyRS2 - tyDS_DS + tyDS_UDT*tyDS_UDT, TYPE(-1.0) ); // Orthogonal separation is too large. tyTMPC = P::FSEL( tyTMPC, tyDS_UDT - P::SQRT( tyTMPC ), TYPE(-1.0) ); // Time occurs after first time of contact. if (tyTMPC > (*ptyPM + ptgPacket->m_tySweepTol)*tgDT.m_tyDT) { return (TgE_NOINTERSECT); // Outside of sweep space. }; const TYPE tyT0 = tyTMPC / tgDT.m_tyDT; if (tyT0 < *ptyPM - ptgPacket->m_tySweepTol) { ptgPacket->m_niContact = 0; *ptyPM = tyT0; }; C_(VECTOR,DIM) tvSP0 = MATH::F_ADD( tgSP1.Query_Origin(), MATH::F_MUL( tyTMPC, tgDT.m_tvUDT ) ); C_(VECTOR,DIM) tvNormal = MATH::F_NORM( MATH::F_SUB( tvSP0, tgSP0.Query_Origin() ) ); P_(CONTACT,DIM) ptgContact; ptgContact = (P_(CONTACT,DIM))((PC_TgUINT08)ptgPacket->m_ptgContact + ptgPacket->m_niContact*ptgPacket->m_iStride); ptgContact->m_tvPos = MATH::F_SUB( tvSP0, MATH::F_MUL( tvNormal, tgSP1.Query_Radius() ) ); ptgContact->m_tvNormal = tvNormal; ptgContact->m_tyT0 = tyT0; ptgContact->m_tyDepth = TYPE(0.0); ++ptgPacket->m_niContact; return (TgS_OK); }; template TgRESULT F_Contact_Sweep( PC_TgF4CONTACT_PACKET, P_TgFLOAT32, CR_TgF4SPHERE, CR_TgF4SPHERE, CR_TgF4DELTA ); // ============================================================================================================================== // }; // END COL ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }; // END TGS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////[an error occurred while processing this directive]