BuoyancyController.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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. mFlowVelocity( 0.0f, 0.0f ),
  32. mFluidDensity( 2.0f ),
  33. mLinearDrag( 0.0f ),
  34. mAngularDrag( 0.0f ),
  35. mFluidGravity( 0.0f, -9.8f ),
  36. mUseShapeDensity( true ),
  37. mSurfaceNormal( 0.0f, 1.0f )
  38. {
  39. }
  40. //------------------------------------------------------------------------------
  41. BuoyancyController::~BuoyancyController()
  42. {
  43. }
  44. //------------------------------------------------------------------------------
  45. void BuoyancyController::initPersistFields()
  46. {
  47. // Call parent.
  48. Parent::initPersistFields();
  49. addField( "FluidArea", Typeb2AABB, Offset(mFluidArea, BuoyancyController), "The fluid area." );
  50. addField( "FluidDensity", TypeF32, Offset(mFluidDensity, BuoyancyController), "The fluid density." );
  51. addField( "FlowVelocity", TypeVector2, Offset(mFlowVelocity, BuoyancyController), "The fluid flow velocity for drag calculations." );
  52. addField( "LinearDrag", TypeF32, Offset(mLinearDrag, BuoyancyController), "The linear drag co-efficient for the fluid." );
  53. addField( "AngularDrag", TypeF32, Offset(mAngularDrag, BuoyancyController), "The angular drag co-efficient for the fluid." );
  54. addField( "FluidGravity", TypeVector2, Offset(mFluidGravity, BuoyancyController), "The gravity to use inside the fluid." );
  55. addField( "UseShapeDensity", TypeBool, Offset(mUseShapeDensity, BuoyancyController), "Whether to use the collision shape densities or assume a uniform density." );
  56. }
  57. //------------------------------------------------------------------------------
  58. void BuoyancyController::copyTo(SimObject* object)
  59. {
  60. // Call to parent.
  61. Parent::copyTo(object);
  62. // Cast to controller.
  63. BuoyancyController* pController = static_cast<BuoyancyController*>(object);
  64. // Sanity!
  65. AssertFatal(pController != NULL, "BuoyancyController::copyTo() - Object is not the correct type.");
  66. }
  67. //------------------------------------------------------------------------------
  68. /*
  69. * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org
  70. *
  71. * This software is provided 'as-is', without any express or implied
  72. * warranty. In no event will the authors be held liable for any damages
  73. * arising from the use of this software.
  74. * Permission is granted to anyone to use this software for any purpose,
  75. * including commercial applications, and to alter it and redistribute it
  76. * freely, subject to the following restrictions:
  77. * 1. The origin of this software must not be misrepresented; you must not
  78. * claim that you wrote the original software. If you use this software
  79. * in a product, an acknowledgment in the product documentation would be
  80. * appreciated but is not required.
  81. * 2. Altered source versions must be plainly marked as such, and must not be
  82. * misrepresented as being the original software.
  83. * 3. This notice may not be removed or altered from any source distribution.
  84. */
  85. //------------------------------------------------------------------------------
  86. void BuoyancyController::integrate( Scene* pScene, const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
  87. {
  88. // Prepare query filter.
  89. WorldQuery* pWorldQuery = prepareQueryFilter( pScene );
  90. // Query for candidate objects.
  91. pWorldQuery->anyQueryAABB( mFluidArea );
  92. // Fetch results.
  93. typeWorldQueryResultVector& queryResults = pWorldQuery->getQueryResults();
  94. // Iterate the results.
  95. for ( U32 n = 0; n < (U32)queryResults.size(); n++ )
  96. {
  97. // Fetch the scene object.
  98. SceneObject* pSceneObject = queryResults[n].mpSceneObject;
  99. // Skip if asleep.
  100. if ( !pSceneObject->getAwake() )
  101. continue;
  102. // Ignore if it's a static body.
  103. if ( pSceneObject->getBodyType() == b2_staticBody )
  104. continue;
  105. // Fetch the shape count.
  106. const U32 shapeCount = pSceneObject->getCollisionShapeCount();
  107. // Skip if no collision shapes.
  108. if ( shapeCount == 0 )
  109. continue;
  110. // Fetch the body transform.
  111. const b2Transform& bodyTransform = pSceneObject->getBody()->GetTransform();;
  112. Vector2 areaCenter(0.0f, 0.0f);
  113. Vector2 massCenter(0.0f, 0.0f);
  114. F32 area = 0.0f;
  115. F32 mass = 0.0f;
  116. // Yes, so iterate them.
  117. for( U32 i = 0; i < shapeCount; ++i )
  118. {
  119. // Fetch the fixture definition.
  120. const b2FixtureDef fixtureDef = pSceneObject->getCollisionShapeDefinition( i );
  121. // Fetch the shape.
  122. const b2Shape* pShape = fixtureDef.shape;
  123. Vector2 shapeCenter(0.0f, 0.0f);
  124. F32 shapeArea = 0.0f;
  125. // Calculate the area for the shape type.
  126. if ( pShape->GetType() == b2Shape::e_circle )
  127. {
  128. shapeArea = ComputeCircleSubmergedArea( bodyTransform, dynamic_cast<const b2CircleShape*>(pShape), shapeCenter );
  129. }
  130. else if ( pShape->GetType() == b2Shape::e_polygon)
  131. {
  132. shapeArea = ComputePolygonSubmergedArea( bodyTransform, dynamic_cast<const b2PolygonShape*>(pShape), shapeCenter );
  133. }
  134. else if ( pShape->GetType() == b2Shape::e_edge || pShape->GetType() == b2Shape::e_chain )
  135. {
  136. shapeArea = 0.0f;
  137. }
  138. else
  139. {
  140. // Skip if unknown shape type.
  141. continue;
  142. }
  143. // Calculate area.
  144. area += shapeArea;
  145. areaCenter.x += shapeArea * shapeCenter.x;
  146. areaCenter.y += shapeArea * shapeCenter.y;
  147. // Calculate mass.
  148. const F32 shapeDensity = mUseShapeDensity ? fixtureDef.density : 1.0f;
  149. mass += shapeArea*shapeDensity;
  150. massCenter.x += shapeArea * shapeCenter.x * shapeDensity;
  151. massCenter.y += shapeArea * shapeCenter.y * shapeDensity;
  152. }
  153. // Skip not in water.
  154. if( area < b2_epsilon )
  155. continue;
  156. // Calculate area/mass centers.
  157. areaCenter.x /= area;
  158. areaCenter.y /= area;
  159. massCenter.x /= mass;
  160. massCenter.y /= mass;
  161. // Buoyancy
  162. const Vector2 buoyancyForce = -mFluidDensity * area * mFluidGravity;
  163. pSceneObject->applyForce(buoyancyForce, massCenter);
  164. // Linear drag
  165. const Vector2 dragForce = (pSceneObject->getLinearVelocityFromWorldPoint(areaCenter) - mFlowVelocity) * (-mLinearDrag * area);
  166. pSceneObject->applyForce(dragForce, areaCenter );
  167. // Angular drag
  168. pSceneObject->applyTorque( -pSceneObject->getInertia() / pSceneObject->getMass() * area * pSceneObject->getAngularVelocity()*mAngularDrag );
  169. }
  170. }
  171. //------------------------------------------------------------------------------
  172. F32 BuoyancyController::ComputeCircleSubmergedArea( const b2Transform& bodyTransform, const b2CircleShape* pShape, Vector2& center )
  173. {
  174. // Sanity!
  175. AssertFatal( pShape != NULL, "BuoyancyController::ComputeCircleSubmergedArea() - Invalid shape." );
  176. // Calculate the world shape center.
  177. const b2Vec2 worldShapeCenter = b2Mul( bodyTransform, pShape->m_p );
  178. const F32 l = -(b2Dot( mSurfaceNormal, worldShapeCenter ) - mFluidArea.upperBound.y);
  179. // Fetch the circle radius.
  180. const F32 radius = pShape->m_radius;
  181. // Submerged?
  182. if (l < - radius + FLT_MIN)
  183. {
  184. // No, so return zero area submerged.
  185. return 0.0f;
  186. }
  187. // Completely wet?
  188. if (l > pShape->m_radius)
  189. {
  190. // Yes!
  191. center = worldShapeCenter;
  192. return b2_pi * radius * radius;
  193. }
  194. // Partial submersion.
  195. const F32 r2 = radius * radius;
  196. const F32 l2 = l * l;
  197. const F32 area = r2 *( mAsin(l / radius) + b2_pi / 2.0f) + l * mSqrt( r2 - l2 );
  198. const F32 com = -2.0f / 3.0f * mPow(r2 - l2, 1.5f) / area;
  199. // Calculate center.
  200. center.x = worldShapeCenter.x + mSurfaceNormal.x * com;
  201. center.y = worldShapeCenter.y + mSurfaceNormal.y * com;
  202. return area;
  203. }
  204. //------------------------------------------------------------------------------
  205. F32 BuoyancyController::ComputePolygonSubmergedArea( const b2Transform& bodyTransform, const b2PolygonShape* pShape, Vector2& center )
  206. {
  207. // Sanity!
  208. AssertFatal( pShape != NULL, "BuoyancyController::ComputePolygonSubmergedArea() - Invalid shape." );
  209. // Transform plane into shape co-ordinates
  210. b2Vec2 normalL = b2MulT( bodyTransform.q, mSurfaceNormal);
  211. F32 offsetL = mFluidArea.upperBound.y - b2Dot(mSurfaceNormal, bodyTransform.p);
  212. F32 depths[b2_maxPolygonVertices];
  213. S32 diveCount = 0;
  214. S32 intoIndex = -1;
  215. S32 outoIndex = -1;
  216. const S32 vertexCount = pShape->GetVertexCount();
  217. const b2Vec2* pVertices = pShape->m_vertices;
  218. bool lastSubmerged = false;
  219. for ( S32 i = 0; i < vertexCount; ++i )
  220. {
  221. depths[i] = b2Dot(normalL, pVertices[i]) - offsetL;
  222. const bool isSubmerged = depths[i]<-FLT_EPSILON;
  223. if (i > 0)
  224. {
  225. if (isSubmerged)
  226. {
  227. if (!lastSubmerged)
  228. {
  229. intoIndex = i-1;
  230. diveCount++;
  231. }
  232. }
  233. else
  234. {
  235. if (lastSubmerged)
  236. {
  237. outoIndex = i-1;
  238. diveCount++;
  239. }
  240. }
  241. }
  242. lastSubmerged = isSubmerged;
  243. }
  244. switch(diveCount)
  245. {
  246. case 0:
  247. if (lastSubmerged)
  248. {
  249. // Completely submerged
  250. b2MassData md;
  251. pShape->ComputeMass(&md, 1.0f);
  252. center = b2Mul(bodyTransform, md.center);
  253. return md.mass;
  254. }
  255. else
  256. {
  257. // Completely dry
  258. return 0.0;
  259. }
  260. break;
  261. case 1:
  262. if( intoIndex==-1 )
  263. {
  264. intoIndex = vertexCount-1;
  265. }
  266. else
  267. {
  268. outoIndex = vertexCount-1;
  269. }
  270. break;
  271. }
  272. const S32 intoIndex2 = (intoIndex+1) % vertexCount;
  273. const S32 outoIndex2 = (outoIndex+1) % vertexCount;
  274. const F32 intoLambda = (0 - depths[intoIndex]) / (depths[intoIndex2] - depths[intoIndex]);
  275. const F32 outoLambda = (0 - depths[outoIndex]) / (depths[outoIndex2] - depths[outoIndex]);
  276. const b2Vec2 intoVec( pVertices[intoIndex].x*(1-intoLambda)+pVertices[intoIndex2].x*intoLambda,
  277. pVertices[intoIndex].y*(1-intoLambda)+pVertices[intoIndex2].y*intoLambda);
  278. const b2Vec2 outoVec( pVertices[outoIndex].x*(1-outoLambda)+pVertices[outoIndex2].x*outoLambda,
  279. pVertices[outoIndex].y*(1-outoLambda)+pVertices[outoIndex2].y*outoLambda);
  280. // Initialize accumulator
  281. F32 area = 0.0f;
  282. center.SetZero();
  283. b2Vec2 p2 = pVertices[intoIndex2];
  284. b2Vec2 p3;
  285. const F32 k_inv3 = 1.0f / 3.0f;
  286. // An awkward loop from intoIndex2+1 to outIndex2
  287. S32 i = intoIndex2;
  288. while (i != outoIndex2)
  289. {
  290. i = (i+1) % vertexCount;
  291. if (i == outoIndex2)
  292. p3 = outoVec;
  293. else
  294. p3 = pVertices[i];
  295. // Add the triangle formed by intoVec,p2,p3
  296. const b2Vec2 e1 = p2 - intoVec;
  297. const b2Vec2 e2 = p3 - intoVec;
  298. const F32 D = b2Cross(e1, e2);
  299. const F32 triangleArea = 0.5f * D;
  300. area += triangleArea;
  301. // Area weighted centroid
  302. center += triangleArea * k_inv3 * (intoVec + p2 + p3);
  303. p2 = p3;
  304. }
  305. // Normalize and transform centroid
  306. center *= 1.0f / area;
  307. center = b2Mul(bodyTransform, center);
  308. return area;
  309. }
  310. //------------------------------------------------------------------------------
  311. void BuoyancyController::renderOverlay( Scene* pScene, const SceneRenderState* pSceneRenderState, BatchRender* pBatchRenderer )
  312. {
  313. // Call parent.
  314. Parent::renderOverlay( pScene, pSceneRenderState, pBatchRenderer );
  315. // Draw fluid area.
  316. pScene->mDebugDraw.DrawAABB( mFluidArea, ColorF(0.7f, 0.7f, 0.9f) );
  317. }