World of Rigid Bodies (WoRB)
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Geometry.h
Go to the documentation of this file.
00001 #ifndef _WORB_GEOMETRY_H_INCLUDED
00002 #define _WORB_GEOMETRY_H_INCLUDED
00003 
00004 /**
00005  *  @file      Geometry.h
00006  *  @brief     Definitions for the Geometry and its derived classes (Sphere, Cuboid etc)
00007  *             which implement collision detection ('space awereness') algorithms.
00008  *  @author    Mikica Kocic
00009  *  @version   0.19
00010  *  @date      2012-05-12
00011  *  @copyright GNU Public License.
00012  */
00013 
00014 #include "Constants.h"
00015 
00016 namespace WoRB 
00017 {
00018     class CollisionResolver;
00019 
00020     void Printf( const char* format, ... );
00021 
00022     /////////////////////////////////////////////////////////////////////////////////////
00023 
00024     /** Represents a geometry used to detect collisions against.
00025      * Cannot be explicitly instantiated.
00026      */
00027     class Geometry
00028     {
00029     protected:
00030 
00031         enum GeometryClass
00032         {
00033             _Sphere,
00034             _Cuboid,
00035             _HalfSpace,
00036             _TruePlane
00037         };
00038 
00039         /** Holds the geometry class of the object.
00040          */
00041         GeometryClass Class;
00042 
00043         /** Protected constructor; disallows explicit instantiation of the class.
00044          */
00045         Geometry( GeometryClass type, RigidBody* body = 0 )
00046             : Class( type )
00047             , Body( body )
00048         {
00049         }
00050 
00051     public:
00052 
00053         bool IsCuboid ()    const { return Class == _Cuboid;    }
00054         bool IsSphere ()    const { return Class == _Sphere;    }
00055         bool IsHalfSpace () const { return Class == _HalfSpace; }
00056         bool IsTruePlane () const { return Class == _TruePlane; }
00057 
00058         /** Returns class of the geometry as a string.
00059          */
00060         const char* GetName () const
00061         {
00062             switch( Class )
00063             {
00064                 case _Sphere:    return "Sphere";
00065                 case _Cuboid:    return "Cuboid";
00066                 case _HalfSpace: return "HalfSpace";
00067                 case _TruePlane: return "TruePlane";
00068             }
00069             return "(unknown)";
00070         }
00071 
00072         /** The rigid body that is represented by this geometry.
00073          * Null in case of a scenery object (like a half-plane).
00074          */
00075         RigidBody* Body;
00076 
00077         /** Gets the position vector of the geometry.
00078          */
00079         Quaternion Position () const
00080         {
00081             // Column with index 3 holds the position
00082             //
00083             return Body ? Body->ToWorld.Column(3) : 0.0; 
00084         }
00085 
00086         /** Gets the unit base vector (axes) of the geometry, given by the index.
00087          */
00088         Quaternion Axis( unsigned index ) const
00089         {
00090             // Axes are columns of the q-tensor with indices 0-2
00091             //
00092             return Body ? Body->ToWorld.Column( index ) : 0.0;
00093         }
00094 
00095         /////////////////////////////////////////////////////////////////////////////////
00096 
00097         /** Detects and registers a collision between this and the other geometry
00098          */
00099         void Detect( CollisionResolver& owner, const Geometry* B ) const;
00100     };
00101 
00102     /////////////////////////////////////////////////////////////////////////////////////
00103 
00104     /** Represents a half-space defined by a plane where the normal of the plane 
00105      * points out of the half-space.
00106      */
00107     class HalfSpace : public Geometry
00108     {
00109     public:
00110 
00111         HalfSpace ()
00112             : Geometry( Geometry::_HalfSpace )
00113             , Offset( 0 )
00114         {
00115         }
00116 
00117         /** The plane normal
00118          */
00119         Quaternion Direction;
00120 
00121         /** The distance of the plane from the origin.
00122          */
00123         double Offset;
00124     };
00125 
00126     /////////////////////////////////////////////////////////////////////////////////////
00127 
00128     /** Represents a true plane.
00129      */
00130     class TruePlane : public Geometry
00131     {
00132     public:
00133 
00134         TruePlane ()
00135             : Geometry( Geometry::_TruePlane )
00136             , Offset( 0 )
00137         {
00138         }
00139 
00140         /** The plane normal
00141          */
00142         Quaternion Direction;
00143 
00144         /** The distance of the plane from the origin.
00145          */
00146         double Offset;
00147     };
00148 
00149     /////////////////////////////////////////////////////////////////////////////////////
00150 
00151     /** Encapsulates a sphere.
00152      */
00153     class Sphere : public Geometry
00154     {
00155     public:
00156 
00157         Sphere ()
00158             : Geometry( Geometry::_Sphere )
00159             , Radius( 0 )
00160         {
00161         }
00162 
00163         /** Holds the radius of the sphere.
00164          */
00165         double Radius;
00166 
00167         /** Gets the volume of the sphere.
00168          */
00169         double Volume () const
00170         {
00171             return ( 4.0/3.0 * Const::Pi ) * Radius * Radius * Radius;
00172         }
00173 
00174         /** Sets body mass and principal moment of inertia of the sphere.
00175          */
00176         void SetMass( double mass )
00177         {
00178             Body->SetupMass( mass );
00179 
00180             double Ixx = (2.0/5.0) * mass * Radius * Radius;
00181             Body->SetMomentOfInertia( QTensor( Ixx, Ixx, Ixx ) );
00182 
00183             Body->CalculateDerivedQuantities( /*fromMomenta*/ false );
00184         }
00185 
00186         /////////////////////////////////////////////////////////////////////////////////
00187 
00188         /** Tests for intersection of the sphere and a half-space.
00189          */
00190         bool Intersects( const HalfSpace& plane ) const
00191         {
00192             // Find the distance from the origin
00193             double distance = plane.Direction.Dot( Position() ) - Radius;
00194 
00195             // Check for the intersection
00196             return distance <= plane.Offset;
00197         }
00198 
00199         /** Tests for intersection between two spheres.
00200          */
00201         bool Intersects( const Sphere& B ) const
00202         {
00203             // Find the vector between the objects
00204             Quaternion displacement = Position() - B.Position();
00205 
00206             // See if it is large enough.
00207             double sumRadius = Radius + B.Radius;
00208             return displacement.ImSquaredNorm() < sumRadius * sumRadius;
00209         }
00210 
00211         /////////////////////////////////////////////////////////////////////////////////
00212 
00213         /** Checks for collision between the sphere and a half-space.
00214          */
00215         unsigned Check( CollisionResolver& owner, const HalfSpace& plane ) const;
00216 
00217         /** Checks for collision between the sphere and a true plane.
00218          */
00219         unsigned Check( CollisionResolver& owner, const TruePlane& plane ) const;
00220 
00221         /** Checks for collision between two spheres.
00222          */
00223         unsigned Check( CollisionResolver& owner, const Sphere& B ) const;
00224     };
00225 
00226     /////////////////////////////////////////////////////////////////////////////////////
00227 
00228     /** Encapsulates a cuboid (rectangular parallelepiped).
00229      */
00230     class Cuboid : public Geometry
00231     {
00232     public:
00233 
00234         Cuboid ()
00235             : Geometry( Geometry::_Cuboid )
00236         {
00237         }
00238 
00239         /** Holds the half-extent of the cuboid along each of its local axes.
00240          */
00241         Quaternion HalfExtent;
00242 
00243         /** Gets the volume of the cuboid.
00244          */
00245         double Volume () const
00246         {
00247             return 8.0 * HalfExtent.x * HalfExtent.y * HalfExtent.z;
00248         }
00249 
00250         /** Sets body mass and principal moment of inertia of the cuboid.
00251          */
00252         void SetMass( double mass )
00253         {
00254             Body->SetupMass( mass );
00255 
00256             Quaternion extent = 2.0 * HalfExtent;
00257             Quaternion sq = extent.ComponentWiseProduct( extent );
00258 
00259             Body->SetMomentOfInertia( QTensor(
00260                 mass * ( sq.y + sq.z ) / 12,
00261                 mass * ( sq.x + sq.z ) / 12,
00262                 mass * ( sq.x + sq.y ) / 12
00263             ) );
00264 
00265             Body->CalculateDerivedQuantities( /*fromMomenta*/ false );
00266         }
00267 
00268         /////////////////////////////////////////////////////////////////////////////////
00269 
00270         /** Tests for intersection of the cuboid and a half-space.
00271          */
00272         bool Intersects( const HalfSpace& plane ) const
00273         {
00274             // Calculate the projected radius of the cuboid onto the plane direction
00275             double projectedRadius = ProjectOn( plane.Direction );
00276 
00277             // Calculate how far the cuboid is from the origin
00278             double distance = plane.Direction.Dot( Position() ) - projectedRadius;
00279 
00280             // Check for the intersection
00281             return distance <= plane.Offset;
00282         }
00283 
00284         /** Tests for intersection between this and some other cuboid.
00285          */
00286         bool Intersects( const Cuboid& B ) const
00287         {
00288             // Find the displacement between the centra of two cuboids
00289             //
00290             Quaternion displacement = B.Position() - Position();
00291 
00292             // Now, first, check axes of A, then axes of B, and finally their cross products
00293             //
00294             return IsOverlapOnAxis( B, Axis(0),                    displacement )
00295                 && IsOverlapOnAxis( B, Axis(1),                    displacement )
00296                 && IsOverlapOnAxis( B, Axis(2),                    displacement )
00297                 && IsOverlapOnAxis( B,                B.Axis(0),   displacement )
00298                 && IsOverlapOnAxis( B,                B.Axis(1),   displacement )
00299                 && IsOverlapOnAxis( B,                B.Axis(2),   displacement )
00300                 && IsOverlapOnAxis( B, Axis(0).Cross( B.Axis(0) ), displacement )
00301                 && IsOverlapOnAxis( B, Axis(0).Cross( B.Axis(1) ), displacement )
00302                 && IsOverlapOnAxis( B, Axis(0).Cross( B.Axis(2) ), displacement )
00303                 && IsOverlapOnAxis( B, Axis(1).Cross( B.Axis(0) ), displacement )
00304                 && IsOverlapOnAxis( B, Axis(1).Cross( B.Axis(1) ), displacement )
00305                 && IsOverlapOnAxis( B, Axis(1).Cross( B.Axis(2) ), displacement )
00306                 && IsOverlapOnAxis( B, Axis(2).Cross( B.Axis(0) ), displacement )
00307                 && IsOverlapOnAxis( B, Axis(2).Cross( B.Axis(1) ), displacement )
00308                 && IsOverlapOnAxis( B, Axis(2).Cross( B.Axis(2) ), displacement );
00309         }
00310 
00311         /////////////////////////////////////////////////////////////////////////////////
00312 
00313         /** Checks for collision between the cuboid and a half-space.
00314          */
00315         unsigned Check( CollisionResolver& owner, const HalfSpace& plane ) const;
00316 
00317         /** Checks for collision between the cuboid and a point.
00318          */
00319         unsigned Check( CollisionResolver& owner, const Quaternion& point ) const;
00320 
00321         /** Checks for collision between this and some other cuboid.
00322          */
00323         unsigned Check( CollisionResolver& owner, const Cuboid& B ) const;
00324 
00325         /** Checks for collision between the cuboid and a sphere.
00326          */
00327         unsigned Check( CollisionResolver& owner, const Sphere& B ) const;
00328 
00329         /////////////////////////////////////////////////////////////////////////////////
00330 
00331     private:
00332 
00333         /////////////////////////////////////////////////////////////////////////////////
00334         /** @name Private methods: Intersection detection related                      */
00335                                                                                    /*@{*/
00336         /** Clamps the given value to some +/- max value.
00337          */
00338         static inline double Clamp( double x, double max ) {
00339             return x > max ? max : x < -max ? -max : x;
00340         }
00341 
00342         /** Gets the sum of cuboid's half-extent's projections on the given vector.
00343          */
00344         double ProjectOn( const Quaternion& vector ) const
00345         {
00346             return HalfExtent.x * fabs( vector.Dot( Axis(0) ) )
00347                  + HalfExtent.y * fabs( vector.Dot( Axis(1) ) )
00348                  + HalfExtent.z * fabs( vector.Dot( Axis(2) ) );
00349         }
00350 
00351         /** Checks if this and another cuboid overlap along the given direction. 
00352          * @return The ammount of penetration, where positive value indicates overlap.
00353          */
00354         double GetPenetrationOnAxis( const Cuboid& B,
00355             const Quaternion& axis, const Quaternion& displacement
00356             ) const
00357         {
00358             Quaternion direction = axis.Unit ();
00359 
00360             // Project the half-extents and displacement onto axis
00361             double proj_A = ProjectOn( direction );
00362             double proj_B = B.ProjectOn( direction );
00363             double distance = fabs( displacement.Dot( direction ) );
00364 
00365             // Return the overlap 
00366             return proj_A + proj_B - distance;
00367         }
00368 
00369         /** Checks if this and another cuboid overlap along the given direction.
00370          */
00371         bool IsOverlapOnAxis( const Cuboid& B,
00372             const Quaternion& direction, const Quaternion& displacement
00373             ) const
00374         {
00375             // Skip almost parallel axes.
00376             if ( direction.ImSquaredNorm() < 1e-4 ) {
00377                 return true;
00378             }
00379 
00380             return GetPenetrationOnAxis( B, direction, displacement ) > 0;
00381         }
00382 
00383         /** Checks for overlap along the given direction.
00384          * Keeps track of the smallest penetration.
00385          */
00386         bool CheckOverlapOnAxis( const Cuboid& B,
00387             const Quaternion& direction, const Quaternion& displacement, 
00388             double& smallestPenetration, 
00389             unsigned tag_A, unsigned tag_B, 
00390             unsigned& indexTag_A, unsigned& indexTag_B
00391             ) const
00392         {
00393             // Skip almost parallel axes.
00394             if ( direction.ImSquaredNorm() < 1e-4 ) {
00395                 return true;
00396             }
00397 
00398             // Get penetration depth on axis.
00399             double penetration = GetPenetrationOnAxis( B, direction, displacement );
00400 
00401             if ( penetration < 0 ) { // no penetration
00402                 return false;
00403             }
00404             else if ( penetration < smallestPenetration /*- 1e-6 */ ) {
00405                 smallestPenetration = penetration;
00406                 indexTag_A = tag_A;
00407                 indexTag_B = tag_B;
00408             }
00409             return true;
00410         }
00411 
00412         /** Finds point of contact between two points on two cuboid edges.
00413          */
00414         static Quaternion FindContactPointOnEdges(
00415             const  Quaternion& pt_A,   //!< Point on the edge of the cuboid A
00416             const  Quaternion& axis_A, //!< Axis of the cuboid A
00417             double size_A,             //!< Half-extent of A's axis
00418             const  Quaternion& pt_B,   //!< Point on the edge of the cuboid B
00419             const  Quaternion& axis_B, //!< Axis of the cuboid B
00420             double size_B,             //!< Half-extent of B's axis
00421             bool   use_A               //!< Which cuboid to use, if point is on the edge
00422             );
00423 
00424         /** Registers a contact between this and the other cuboid body along an axis.
00425          * @return 1 if ok, 0 if failed to allocate space for the contact.
00426          */
00427         unsigned RegisterContactOnAxis_Thorough
00428         (
00429             CollisionResolver& owner,       //!< The collision registry
00430             const Cuboid& B,                //!< The second cuboid
00431             const Quaternion& displacement, //!< The displacement between B and A centra
00432             unsigned axis                   //!< A's axis
00433             ) const;
00434 
00435         /** Registers a contact between this and the other cuboid body along an axis.
00436          * Faster variant that does not check every vertex but the closest one.
00437          * @return 1 if ok, 0 if failed to allocate space for the contact.
00438          */
00439         unsigned RegisterContactOnAxis
00440         (
00441             CollisionResolver& owner,       //!< The collision registry
00442             const Cuboid& B,                //!< The second cuboid
00443             const Quaternion& displacement, //!< The displacement between B and A centra
00444             const Quaternion& axis,         //!< The axis of this couboid (cuboid A)
00445             double penetration              //!< The penetration depth
00446             ) const;
00447                                                                                    /*@}*/
00448         /////////////////////////////////////////////////////////////////////////////////
00449     };
00450 
00451 } // namespace WoRB
00452 
00453 #endif // _WORB_GEOMETRY_H_INCLUDED