|
World of Rigid Bodies (WoRB)
|
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