BuoyancyController.cc 14 KB

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