BuoyancyController.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _BUOYANCY_CONTROLLER_H_
  23. #include "2d/controllers/BuoyancyController.h"
  24. #endif
  25. // Script bindings.
  26. #include "BuoyancyController_ScriptBinding.h"
  27. //------------------------------------------------------------------------------
  28. IMPLEMENT_CONOBJECT(BuoyancyController);
  29. //------------------------------------------------------------------------------
  30. BuoyancyController::BuoyancyController() :
  31. mSurfaceNormal( 0.0f, 1.0f ),
  32. mSurfaceOffset( 0.0f ),
  33. mFlowVelocity( 0.0f, 0.0f ),
  34. mFluidDensity( 2.0f ),
  35. mLinearDrag( 0.0f ),
  36. mAngularDrag( 0.0f ),
  37. mFluidGravity( 0.0f, -9.8f ),
  38. mUseShapeDensity( true )
  39. {
  40. }
  41. //------------------------------------------------------------------------------
  42. BuoyancyController::~BuoyancyController()
  43. {
  44. }
  45. //------------------------------------------------------------------------------
  46. void BuoyancyController::initPersistFields()
  47. {
  48. // Call parent.
  49. Parent::initPersistFields();
  50. addField( "SurfaceNormal", TypeVector2, Offset(mSurfaceNormal, BuoyancyController), "The outer surface normal." );
  51. addField( "SurfaceOffset", TypeF32, Offset(mSurfaceOffset, BuoyancyController), "The height of the fluid surface along the normal." );
  52. addField( "FluidDensity", TypeF32, Offset(mFluidDensity, BuoyancyController), "The fluid density." );
  53. addField( "FlowVelocity", TypeVector2, Offset(mFlowVelocity, BuoyancyController), "The fluid flow velocity for drag calculations." );
  54. addField( "LinearDrag", TypeF32, Offset(mLinearDrag, BuoyancyController), "The linear drag co-efficient for the fluid." );
  55. addField( "AngularDrag", TypeF32, Offset(mAngularDrag, BuoyancyController), "The angular drag co-efficient for the fluid." );
  56. addField( "FluidGravity", TypeVector2, Offset(mFluidGravity, BuoyancyController), "The gravity to use inside the fluid." );
  57. addField( "UseShapeDensity", TypeBool, Offset(mUseShapeDensity, BuoyancyController), "Whether to use the collision shape densities or assume a uniform density." );
  58. }
  59. //------------------------------------------------------------------------------
  60. void BuoyancyController::copyTo(SimObject* object)
  61. {
  62. // Call to parent.
  63. Parent::copyTo(object);
  64. // Cast to controller.
  65. BuoyancyController* pController = static_cast<BuoyancyController*>(object);
  66. // Sanity!
  67. AssertFatal(pController != NULL, "BuoyancyController::copyTo() - Object is not the correct type.");
  68. }
  69. //------------------------------------------------------------------------------
  70. /*
  71. * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
  72. *
  73. * This software is provided 'as-is', without any express or implied
  74. * warranty. In no event will the authors be held liable for any damages
  75. * arising from the use of this software.
  76. * Permission is granted to anyone to use this software for any purpose,
  77. * including commercial applications, and to alter it and redistribute it
  78. * freely, subject to the following restrictions:
  79. * 1. The origin of this software must not be misrepresented; you must not
  80. * claim that you wrote the original software. If you use this software
  81. * in a product, an acknowledgment in the product documentation would be
  82. * appreciated but is not required.
  83. * 2. Altered source versions must be plainly marked as such, and must not be
  84. * misrepresented as being the original software.
  85. * 3. This notice may not be removed or altered from any source distribution.
  86. */
  87. //------------------------------------------------------------------------------
  88. void BuoyancyController::integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
  89. {
  90. // Process all the scene objects.
  91. for( SceneObjectSet::iterator itr = begin(); itr != end(); ++itr )
  92. {
  93. // Fetch the scene object.
  94. SceneObject* pSceneObject = *itr;
  95. // Ignore sleeping bodies.
  96. if ( !pSceneObject->getAwake() )
  97. continue;
  98. // Fetch the shape count.
  99. const U32 shapeCount = pSceneObject->getCollisionShapeCount();
  100. Vector2 areaCenter(0.0f, 0.0f);
  101. Vector2 massCenter(0.0f, 0.0f);
  102. F32 area = 0.0f;
  103. F32 mass = 0.0f;
  104. // Skip if we have no collision shapes.
  105. if ( shapeCount == 0 )
  106. continue;
  107. // Yes, so iterate them.
  108. for( U32 i = 0; i < shapeCount; ++i )
  109. {
  110. // Fetch the fixture definition.
  111. b2FixtureDef fixtureDef = pSceneObject->getCollisionShapeDefinition( i );
  112. // Fetch the shape.
  113. const b2Shape* pShape = fixtureDef.shape;
  114. Vector2 shapeCenter(0.0f, 0.0f);
  115. F32 shapeArea = 0.0f;
  116. if ( pShape->GetType() == b2Shape::e_circle )
  117. {
  118. shapeArea = ComputeCircleSubmergedArea( pSceneObject->getBody(), dynamic_cast<const b2CircleShape*>(pShape), shapeCenter );
  119. }
  120. else if ( pShape->GetType() == b2Shape::e_polygon)
  121. {
  122. shapeArea = ComputePolygonSubmergedArea( pSceneObject->getBody(), dynamic_cast<const b2PolygonShape*>(pShape), shapeCenter );
  123. }
  124. else if ( pShape->GetType() == b2Shape::e_edge)
  125. {
  126. shapeArea = 0.0f;
  127. }
  128. else if ( pShape->GetType() == b2Shape::e_chain)
  129. {
  130. shapeArea = 0.0f;
  131. }
  132. else
  133. {
  134. // Skip if unknown shape type.
  135. continue;
  136. }
  137. area += shapeArea;
  138. areaCenter.x += shapeArea * shapeCenter.x;
  139. areaCenter.y += shapeArea * shapeCenter.y;
  140. const F32 shapeDensity = mUseShapeDensity ? fixtureDef.density : 1.0f;
  141. mass += shapeArea*shapeDensity;
  142. massCenter.x += shapeArea * shapeCenter.x * shapeDensity;
  143. massCenter.y += shapeArea * shapeCenter.y * shapeDensity;
  144. }
  145. areaCenter.x /= area;
  146. areaCenter.y /= area;
  147. const b2Vec2 localCentroid = b2MulT(pSceneObject->getTransform(), areaCenter);
  148. massCenter.x /= mass;
  149. massCenter.y /= mass;
  150. // Skip not in water.
  151. if( area < b2_epsilon )
  152. continue;
  153. // Buoyancy
  154. Vector2 buoyancyForce = -mFluidDensity * area * mFluidGravity;
  155. pSceneObject->applyForce(buoyancyForce, massCenter);
  156. // Linear drag
  157. Vector2 dragForce = pSceneObject->getLinearVelocityFromWorldPoint(areaCenter) - mFlowVelocity;
  158. dragForce *= -mLinearDrag*area;
  159. pSceneObject->applyForce(dragForce, areaCenter );
  160. // Angular drag
  161. pSceneObject->applyTorque( -pSceneObject->getInertia() / pSceneObject->getMass() * area * pSceneObject->getAngularVelocity()*mAngularDrag );
  162. }
  163. }
  164. //------------------------------------------------------------------------------
  165. F32 BuoyancyController::ComputeCircleSubmergedArea( const b2Body* pBody, const b2CircleShape* pShape, Vector2& center )
  166. {
  167. // Sanity!
  168. AssertFatal( pBody != NULL, "BuoyancyController::ComputeCircleSubmergedArea() - Invalid body." );
  169. AssertFatal( pShape != NULL, "BuoyancyController::ComputeCircleSubmergedArea() - Invalid shape." );
  170. // Fetch the circle radius.
  171. const F32 radius = pShape->m_radius;
  172. const b2Vec2 p = b2Mul( pBody->GetTransform(), pShape->m_p );
  173. const F32 l = -(b2Dot( mSurfaceNormal, p ) - mSurfaceOffset);
  174. if (l < - radius + FLT_MIN)
  175. {
  176. // Completely dry
  177. return 0.0f;
  178. }
  179. if (l > pShape->m_radius)
  180. {
  181. // Completely wet
  182. center = p;
  183. return b2_pi * radius * radius;
  184. }
  185. // Partial submersion.
  186. const F32 r2 = radius * radius;
  187. const F32 l2 = l * l;
  188. const F32 area = r2 *( mAsin(l / radius) + b2_pi / 2.0f) + l * mSqrt( r2 - l2 );
  189. const F32 com = -2.0f / 3.0f * mPow(r2 - l2, 1.5f) / area;
  190. center.x = p.x + mSurfaceNormal.x * com;
  191. center.y = p.y + mSurfaceNormal.y * com;
  192. return area;
  193. }
  194. //------------------------------------------------------------------------------
  195. F32 BuoyancyController::ComputePolygonSubmergedArea( const b2Body* pBody, const b2PolygonShape* pShape, Vector2& center )
  196. {
  197. // Sanity!
  198. AssertFatal( pBody != NULL, "BuoyancyController::ComputePolygonSubmergedArea() - Invalid body." );
  199. AssertFatal( pShape != NULL, "BuoyancyController::ComputePolygonSubmergedArea() - Invalid shape." );
  200. //Transform plane into shape co-ordinates
  201. b2Vec2 normalL = b2MulT( pBody->GetTransform().q, mSurfaceNormal);
  202. F32 offsetL = mSurfaceOffset - b2Dot(mSurfaceNormal, pBody->GetTransform().p);
  203. F32 depths[b2_maxPolygonVertices];
  204. S32 diveCount = 0;
  205. S32 intoIndex = -1;
  206. S32 outoIndex = -1;
  207. const S32 vertexCount = pShape->GetVertexCount();
  208. const b2Vec2* pVertices = pShape->m_vertices;
  209. bool lastSubmerged = false;
  210. for ( S32 i = 0; i < vertexCount; ++i )
  211. {
  212. depths[i] = b2Dot(normalL, pVertices[i]) - offsetL;
  213. const bool isSubmerged = depths[i]<-FLT_EPSILON;
  214. if (i > 0)
  215. {
  216. if (isSubmerged)
  217. {
  218. if (!lastSubmerged)
  219. {
  220. intoIndex = i-1;
  221. diveCount++;
  222. }
  223. }
  224. else
  225. {
  226. if (lastSubmerged)
  227. {
  228. outoIndex = i-1;
  229. diveCount++;
  230. }
  231. }
  232. }
  233. lastSubmerged = isSubmerged;
  234. }
  235. switch(diveCount)
  236. {
  237. case 0:
  238. if (lastSubmerged)
  239. {
  240. // Completely submerged
  241. b2MassData md;
  242. pShape->ComputeMass(&md, 1.0f);
  243. center = b2Mul(pBody->GetTransform(), md.center);
  244. return md.mass;
  245. }
  246. else
  247. {
  248. // Completely dry
  249. return 0.0;
  250. }
  251. break;
  252. case 1:
  253. if( intoIndex==-1 )
  254. {
  255. intoIndex = vertexCount-1;
  256. }
  257. else
  258. {
  259. outoIndex = vertexCount-1;
  260. }
  261. break;
  262. }
  263. const S32 intoIndex2 = (intoIndex+1) % vertexCount;
  264. const S32 outoIndex2 = (outoIndex+1) % vertexCount;
  265. const F32 intoLambda = (0 - depths[intoIndex]) / (depths[intoIndex2] - depths[intoIndex]);
  266. const F32 outoLambda = (0 - depths[outoIndex]) / (depths[outoIndex2] - depths[outoIndex]);
  267. const b2Vec2 intoVec( pVertices[intoIndex].x*(1-intoLambda)+pVertices[intoIndex2].x*intoLambda,
  268. pVertices[intoIndex].y*(1-intoLambda)+pVertices[intoIndex2].y*intoLambda);
  269. const b2Vec2 outoVec( pVertices[outoIndex].x*(1-outoLambda)+pVertices[outoIndex2].x*outoLambda,
  270. pVertices[outoIndex].y*(1-outoLambda)+pVertices[outoIndex2].y*outoLambda);
  271. // Initialize accumulator
  272. F32 area = 0.0f;
  273. center.SetZero();
  274. b2Vec2 p2 = pVertices[intoIndex2];
  275. b2Vec2 p3;
  276. const F32 k_inv3 = 1.0f / 3.0f;
  277. // An awkward loop from intoIndex2+1 to outIndex2
  278. S32 i = intoIndex2;
  279. while (i != outoIndex2)
  280. {
  281. i = (i+1) % vertexCount;
  282. if (i == outoIndex2)
  283. p3 = outoVec;
  284. else
  285. p3 = pVertices[i];
  286. // Add the triangle formed by intoVec,p2,p3
  287. const b2Vec2 e1 = p2 - intoVec;
  288. const b2Vec2 e2 = p3 - intoVec;
  289. const F32 D = b2Cross(e1, e2);
  290. const F32 triangleArea = 0.5f * D;
  291. area += triangleArea;
  292. // Area weighted centroid
  293. center += triangleArea * k_inv3 * (intoVec + p2 + p3);
  294. p2 = p3;
  295. }
  296. // Normalize and transform centroid
  297. center *= 1.0f / area;
  298. center = b2Mul(pBody->GetTransform(), center);
  299. return area;
  300. }