btHfFluid.cpp
Go to the documentation of this file.
00001 /*
00002  Bullet Continuous Collision Detection and Physics Library
00003  Copyright (c) 2003-2009 Erwin Coumans  http://bulletphysics.com
00004 
00005  This software is provided 'as-is', without any express or implied warranty.
00006  In no event will the authors be held liable for any damages arising from the use of this software.
00007  Permission is granted to anyone to use this software for any purpose,
00008  including commercial applications, and to alter it and redistribute it freely,
00009  subject to the following restrictions:
00010 
00011  1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
00012  2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
00013  3. This notice may not be removed or altered from any source distribution.
00014 
00015  Experimental Buoyancy fluid demo written by John McCutchan
00016  */
00017 
00018 #include <stdio.h>
00019 #include <iostream>
00020 #include <uwsim/BulletHfFluid/btHfFluid.h>
00021 #include <uwsim/BulletHfFluid/btHfFluidCollisionShape.h>
00022 #include <uwsim/BulletHfFluid/btHfFluidBuoyantConvexShape.h>
00023 #include <BulletCollision/NarrowPhaseCollision/btPersistentManifold.h>
00024 #include <BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h>
00025 #include <BulletCollision/CollisionDispatch/btCollisionWorld.h>
00026 #include <BulletCollision/CollisionDispatch/btManifoldResult.h>
00027 #include <BulletCollision/CollisionShapes/btTriangleShape.h>
00028 #include <BulletDynamics/Dynamics/btRigidBody.h>
00029 //#include "../../OpenGL/GLDebugDrawer.h"
00030 
00031 btHfFluid::btHfFluid(btScalar gridCellWidth, int numNodesWidth, int numNodesLength)
00032 {
00033   m_eta = NULL;
00034   m_temp = NULL;
00035   m_ground = NULL;
00036   m_height[0] = NULL;
00037   m_height[1] = NULL;
00038   m_u[0] = NULL;
00039   m_u[1] = NULL;
00040   m_v[0] = NULL;
00041   m_v[1] = NULL;
00042   m_r[0] = NULL;
00043   m_r[1] = NULL;
00044   m_flags = NULL;
00045   m_fillRatio = NULL;
00046   m_internalType = CO_HF_FLUID;
00047   m_heightIndex = 0;
00048   m_velocityIndex = 0;
00049   m_rIndex = 0;
00050   setGridDimensions(gridCellWidth, numNodesWidth, numNodesLength);
00051 
00052   btScalar maxHeight = 20.0;
00053   m_aabbMin = btVector3(0.0, 0.0, -10.0);
00054   m_aabbMax = btVector3(m_gridWidth, m_gridLength, maxHeight);
00055 
00056   setCollisionShape(new btHfFluidCollisionShape(this));
00057 
00058   m_globalVelocityU = btScalar(0.0f);
00059   m_globalVelocityV = btScalar(0.0f);
00060   m_gravity = btScalar(-1.0f);
00061 
00062   m_volumeDisplacementScale = btScalar(0.5f);
00063   m_horizontalVelocityScale = btScalar(0.5f);
00064 
00065   m_epsEta = btScalar(0.01f);
00066   m_epsHeight = btScalar(0.001f);
00067 }
00068 
00069 btHfFluid::btHfFluid(btScalar gridCellWidth, int minX, int maxX, int minY, int maxY, int depth, int height)
00070 {
00071   m_eta = NULL;
00072   m_temp = NULL;
00073   m_ground = NULL;
00074   m_height[0] = NULL;
00075   m_height[1] = NULL;
00076   m_u[0] = NULL;
00077   m_u[1] = NULL;
00078   m_v[0] = NULL;
00079   m_v[1] = NULL;
00080   m_r[0] = NULL;
00081   m_r[1] = NULL;
00082   m_flags = NULL;
00083   m_fillRatio = NULL;
00084   m_internalType = CO_HF_FLUID;
00085   m_heightIndex = 0;
00086   m_velocityIndex = 0;
00087   m_rIndex = 0;
00088 
00089   setGridDimensions(gridCellWidth, ceil(maxX - minX) / gridCellWidth, ceil(maxY - minY) / gridCellWidth);
00090 
00091   m_aabbMin = btVector3(minX, minY, depth);
00092   m_aabbMax = btVector3(maxX, maxY, height);
00093 
00094   setCollisionShape(new btHfFluidCollisionShape(this));
00095 
00096   m_globalVelocityU = btScalar(0.0f);
00097   m_globalVelocityV = btScalar(0.0f);
00098   m_gravity = btScalar(-1.0f);
00099 
00100   m_volumeDisplacementScale = btScalar(0.5f);
00101   m_horizontalVelocityScale = btScalar(0.5f);
00102 
00103   m_epsEta = btScalar(0.01f);
00104   m_epsHeight = btScalar(0.001f);
00105 }
00106 
00107 btHfFluid::~btHfFluid()
00108 {
00109   btCollisionShape* collisionShape = getCollisionShape();
00110   delete collisionShape;
00111   btAlignedFree (m_temp);
00112   btAlignedFree (m_height[0]);
00113   btAlignedFree(m_height[1]);
00114   btAlignedFree (m_ground);
00115   btAlignedFree (m_eta);
00116   btAlignedFree (m_u[0]);
00117   btAlignedFree(m_u[1]);
00118   btAlignedFree (m_v[0]);
00119   btAlignedFree(m_v[1]);
00120   btAlignedFree (m_flags);
00121   btAlignedFree (m_fillRatio);
00122 }
00123 
00124 void btHfFluid::predictMotion(btScalar dt)
00125 {
00126   transferDisplaced(dt);
00127 
00128   advectEta(dt);
00129   advectVelocityU(dt);
00130   advectVelocityV(dt);
00131   updateHeight(dt);
00132   computeFlagsAndFillRatio();
00133   updateVelocity(dt);
00134   setReflectBoundaryLeft();
00135   setReflectBoundaryRight();
00136   setReflectBoundaryTop();
00137   setReflectBoundaryBottom();
00138   debugTests();
00139   //m_heightIndex = (m_heightIndex + 1) % 2;
00140   //m_velocityIndex = (m_velocityIndex + 1) % 2;
00141 }
00142 
00143 void btHfFluid::prep()
00144 {
00145   for (int i = 0; i < m_numNodesLength * m_numNodesWidth; i++)
00146   {
00147     m_height[0][i] = m_eta[i] + m_ground[i];
00148     m_height[1][i] = m_eta[i] + m_ground[i];
00149   }
00150   computeFlagsAndFillRatio();
00151 }
00152 
00153 btScalar btHfFluid::widthPos(int i) const
00154 {
00155   return m_gridCellWidth * i + m_aabbMin.getX();
00156 }
00157 
00158 btScalar btHfFluid::lengthPos(int j) const
00159 {
00160   return m_gridCellWidth * j + m_aabbMin.getX();
00161 }
00162 
00163 int btHfFluid::arrayIndex(int i, int j) const
00164 {
00165   btAssert(i >= 0);
00166   btAssert(i < m_numNodesWidth);
00167   btAssert(j >= 0);
00168   btAssert(j < m_numNodesLength);
00169   int index = i + (j * m_numNodesWidth);
00170   return index;
00171 }
00172 
00173 int btHfFluid::arrayIndex(btScalar i, btScalar j) const
00174 {
00175   int ii = (int)i; // truncate / floor
00176   int ij = (int)j; // truncate / floor
00177 
00178   return arrayIndex(ii, ij);
00179 }
00180 
00181 const btScalar* btHfFluid::getHeightArray() const
00182 {
00183   return m_height[m_heightIndex];
00184 }
00185 
00186 const btScalar* btHfFluid::getGroundArray() const
00187 {
00188   return m_ground;
00189 }
00190 const btScalar* btHfFluid::getEtaArray() const
00191 {
00192   return m_eta;
00193 }
00194 const btScalar* btHfFluid::getVelocityUArray() const
00195 {
00196   return m_u[m_velocityIndex];
00197 }
00198 const btScalar* btHfFluid::getVelocityVArray() const
00199 {
00200   return m_v[m_velocityIndex];
00201 }
00202 
00203 const bool* btHfFluid::getFlagsArray() const
00204 {
00205   return m_flags;
00206 }
00207 
00208 btScalar* btHfFluid::getHeightArray()
00209 {
00210   return m_height[m_heightIndex];
00211 }
00212 btScalar* btHfFluid::getGroundArray()
00213 {
00214   return m_ground;
00215 }
00216 btScalar* btHfFluid::getEtaArray()
00217 {
00218   return m_eta;
00219 }
00220 btScalar* btHfFluid::getVelocityUArray()
00221 {
00222   return m_u[m_velocityIndex];
00223 }
00224 btScalar* btHfFluid::getVelocityVArray()
00225 {
00226   return m_v[m_velocityIndex];
00227 }
00228 
00229 bool* btHfFluid::getFlagsArray()
00230 {
00231   return m_flags;
00232 }
00233 
00234 void btHfFluid::setFluidHeight(int x, int y, btScalar height)
00235 {
00236   int index = arrayIndex(x, y);
00237   setFluidHeight(index, height);
00238 }
00239 
00240 void btHfFluid::setFluidHeight(int index, btScalar height)
00241 {
00242   m_eta[index] = height - m_ground[index];
00243   m_height[m_heightIndex][index] = m_ground[index] + m_eta[index];
00244   m_flags[index] = true;
00245 }
00246 
00247 void btHfFluid::addFluidHeight(int x, int y, btScalar height)
00248 {
00249   int index = arrayIndex(x, y);
00250   m_eta[index] += height;
00251   m_height[m_heightIndex][index] = m_ground[index] + m_eta[index];
00252   m_flags[index] = true;
00253 }
00254 
00255 void btHfFluid::getAabbForColumn(int i, int j, btVector3& aabbMin, btVector3& aabbMax)
00256 {
00257   btVector3 com = getWorldTransform().getOrigin();
00258   int sw = arrayIndex(i, j);
00259   int se = arrayIndex(i + 1, j);
00260   int nw = arrayIndex(i, j + 1);
00261   int ne = arrayIndex(i + 1, j + 1);
00262 
00263   btScalar h = m_height[m_heightIndex][sw];
00264   btScalar g = m_ground[sw];
00265 
00266   aabbMin = btVector3(widthPos(i), lengthPos(j), g);
00267   aabbMax = btVector3(widthPos(i + 1), lengthPos(j + 1), h);
00268 
00269   aabbMin += com;
00270   aabbMax += com;
00271 }
00272 
00273 void btHfFluid::foreachGroundTriangle(btTriangleCallback* callback, const btVector3& aabbMin, const btVector3& aabbMax)
00274 {
00275   btVector3 verts[3];
00276 
00277   btScalar minX, minZ, maxX, maxZ;
00278   int startNodeX, startNodeZ, endNodeX, endNodeZ;
00279 
00280   minX = aabbMin.getX();
00281   minZ = aabbMin.getY();
00282   maxX = aabbMax.getX();
00283   maxZ = aabbMax.getY();
00284 
00285   startNodeX = (int)((minX - m_aabbMin.getX()) * m_gridCellWidthInv);
00286   startNodeZ = (int)((minZ - m_aabbMin.getY()) * m_gridCellWidthInv);
00287 
00288   endNodeX = (int)((maxX - m_aabbMin.getX()) * m_gridCellWidthInv);
00289   endNodeZ = (int)((maxZ - m_aabbMin.getY()) * m_gridCellWidthInv);
00290 
00291   endNodeX++;
00292   endNodeZ++;
00293 
00294   startNodeX = btMax(1, startNodeX);
00295   startNodeZ = btMax(1, startNodeZ);
00296   endNodeX = btMin(m_numNodesWidth - 1, endNodeX);
00297   endNodeZ = btMin(m_numNodesLength - 1, endNodeZ);
00298 
00299 #ifdef __BRUTE__
00300   for (int i = 1; i < m_numNodesWidth-1; i++)
00301   {
00302     for (int j = 1; j < m_numNodesLength-1; j++)
00303     {
00304       // triangle 1
00305       verts[0] = btVector3(widthPos(i), m_ground[arrayIndex(i,j)], lengthPos(j));
00306       verts[1] = btVector3(widthPos(i), m_ground[arrayIndex(i,j+1)], lengthPos(j+1));
00307       verts[2] = btVector3(widthPos(i+1), m_ground[arrayIndex(i+1,j)], lengthPos(j));
00308       callback->processTriangle(verts,i,j);
00309       // triangle 2
00310       verts[0] = btVector3(widthPos(i+1), m_ground[arrayIndex(i+1,j)], lengthPos(j));
00311       verts[1] = btVector3(widthPos(i), m_ground[arrayIndex(i,j+1)], lengthPos(j+1));
00312       verts[2] = btVector3(widthPos(i+1), m_ground[arrayIndex(i+1,j+1)], lengthPos(j+1));
00313       callback->processTriangle(verts,i,j);
00314     }
00315   }
00316 #else
00317 
00318   for (int i = startNodeX; i < endNodeX; i++)
00319   {
00320     for (int j = startNodeZ; j < endNodeZ; j++)
00321     {
00322       // triangle 1
00323       verts[0] = btVector3(widthPos(i), m_ground[arrayIndex(i, j)], lengthPos(j));
00324       verts[1] = btVector3(widthPos(i), m_ground[arrayIndex(i, j + 1)], lengthPos(j + 1));
00325       verts[2] = btVector3(widthPos(i + 1), m_ground[arrayIndex(i + 1, j)], lengthPos(j));
00326       callback->processTriangle(verts, i, j);
00327       // triangle 2
00328       verts[0] = btVector3(widthPos(i + 1), m_ground[arrayIndex(i + 1, j)], lengthPos(j));
00329       verts[1] = btVector3(widthPos(i), m_ground[arrayIndex(i, j + 1)], lengthPos(j + 1));
00330       verts[2] = btVector3(widthPos(i + 1), m_ground[arrayIndex(i + 1, j + 1)], lengthPos(j + 1));
00331       callback->processTriangle(verts, i, j);
00332     }
00333   }
00334 #endif
00335 }
00336 
00337 void btHfFluid::foreachFluidColumn(btHfFluidColumnCallback* callback, const btVector3& aabbMin,
00338                                    const btVector3& aabbMax)
00339 {
00340   btScalar minX, minZ, maxX, maxZ;
00341   int startNodeX, startNodeZ, endNodeX, endNodeZ;
00342 
00343   minX = aabbMin.getX();
00344   minZ = aabbMin.getY();
00345   maxX = aabbMax.getX();
00346   maxZ = aabbMax.getY();
00347 
00348   startNodeX = (int)((minX - m_aabbMin.getX()) * m_gridCellWidthInv);
00349   startNodeZ = (int)((minZ - m_aabbMin.getY()) * m_gridCellWidthInv);
00350 
00351   endNodeX = (int)((maxX - m_aabbMin.getX()) * m_gridCellWidthInv);
00352   endNodeZ = (int)((maxZ - m_aabbMin.getY()) * m_gridCellWidthInv);
00353 
00354   endNodeX++;
00355   endNodeZ++;
00356 
00357   startNodeX = btMax(1, startNodeX);
00358   startNodeZ = btMax(1, startNodeZ);
00359   endNodeX = btMin(m_numNodesWidth - 2, endNodeX);
00360   endNodeZ = btMin(m_numNodesLength - 2, endNodeZ);
00361 
00362   bool r;
00363   for (int i = startNodeX; i < endNodeX; i++)
00364   {
00365     for (int j = startNodeZ; j < endNodeZ; j++)
00366     {
00367       if (m_flags[arrayIndex(i, j)] == false)
00368         continue;
00369 
00370       r = callback->processColumn(this, i, j);
00371       if (!r)
00372         return;
00373     }
00374   }
00375 }
00376 
00377 void btHfFluid::foreachSurfaceTriangle(btTriangleCallback* callback, const btVector3& aabbMin, const btVector3& aabbMax)
00378 {
00379   btVector3 verts[3];
00380 
00381   btScalar minX, minZ, maxX, maxZ;
00382   int startNodeX, startNodeZ, endNodeX, endNodeZ;
00383 
00384   minX = aabbMin.getX();
00385   minZ = aabbMin.getY();
00386   maxX = aabbMax.getX();
00387   maxZ = aabbMax.getY();
00388 
00389   startNodeX = (int)((minX - m_aabbMin.getX()) * m_gridCellWidthInv);
00390   startNodeZ = (int)((minZ - m_aabbMin.getY()) * m_gridCellWidthInv);
00391 
00392   endNodeX = (int)((maxX - m_aabbMin.getX()) * m_gridCellWidthInv);
00393   endNodeZ = (int)((maxZ - m_aabbMin.getY()) * m_gridCellWidthInv);
00394 
00395   endNodeX++;
00396   endNodeZ++;
00397 
00398   startNodeX = btMax(1, startNodeX);
00399   startNodeZ = btMax(1, startNodeZ);
00400   endNodeX = m_numNodesWidth - 1;
00401   endNodeZ = m_numNodesLength - 1;
00402 
00403   for (int i = startNodeX; i < endNodeX; i++)
00404   {
00405     for (int j = startNodeZ; j < endNodeZ; j++)
00406     {
00407       if (!m_flags[arrayIndex(i, j)])
00408         continue;
00409       // triangle 1
00410       verts[0] = btVector3(widthPos(i), m_height[m_heightIndex][arrayIndex(i, j)], lengthPos(j));
00411       verts[1] = btVector3(widthPos(i), m_height[m_heightIndex][arrayIndex(i, j + 1)], lengthPos(j + 1));
00412       verts[2] = btVector3(widthPos(i + 1), m_height[m_heightIndex][arrayIndex(i + 1, j)], lengthPos(j));
00413       callback->processTriangle(verts, i, j);
00414       // triangle 2
00415       verts[0] = btVector3(widthPos(i + 1), m_height[m_heightIndex][arrayIndex(i + 1, j)], lengthPos(j));
00416       verts[1] = btVector3(widthPos(i), m_height[m_heightIndex][arrayIndex(i, j + 1)], lengthPos(j + 1));
00417       verts[2] = btVector3(widthPos(i + 1), m_height[m_heightIndex][arrayIndex(i + 1, j + 1)], lengthPos(j + 1));
00418       callback->processTriangle(verts, i, j);
00419     }
00420   }
00421 }
00422 
00423 void btHfFluid::setGridDimensions(btScalar gridCellWidth, int numNodesWidth, int numNodesLength)
00424 {
00425   m_gridWidth = gridCellWidth * numNodesWidth;
00426   m_gridLength = gridCellWidth * numNodesLength;
00427   m_gridCellWidth = gridCellWidth;
00428   m_numNodesWidth = numNodesWidth;
00429   m_numNodesLength = numNodesLength;
00430   m_gridCellWidthInv = btScalar(1.0) / gridCellWidth;
00431 
00432   allocateArrays();
00433 }
00434 
00435 btScalar btHfFluid::bilinearInterpolate(const btScalar* array, btScalar iPos, btScalar jPos)
00436 {
00437   int i = (int)iPos;
00438   int j = (int)jPos;
00439 
00440   btScalar iParam1 = iPos - btScalar(i);
00441   btScalar iParam0 = btScalar(1.0) - iParam1;
00442 
00443   btScalar jParam1 = jPos - btScalar(j);
00444   btScalar jParam0 = btScalar(1.0) - jParam1;
00445 
00446   btScalar SW = array[arrayIndex(i, j)];
00447   btScalar SE = array[arrayIndex(i + 1, j)];
00448   btScalar NW = array[arrayIndex(i, j + 1)];
00449   btScalar NE = array[arrayIndex(i + 1, j + 1)];
00450 
00451   btScalar a = jParam0 * SW + jParam1 * NW;
00452   btScalar b = jParam0 * SE + jParam1 * NE;
00453   return iParam0 * a + iParam1 * b;
00454 }
00455 
00456 btScalar btHfFluid::advect(const btScalar* array, btScalar i, btScalar j, btScalar di, btScalar dj, btScalar dt)
00457 {
00458   // trace particle backwards in time
00459   btScalar srcI = i - di * dt * m_gridCellWidthInv;
00460   btScalar srcJ = j - dj * dt * m_gridCellWidthInv;
00461 
00462   // srcI and srcJ are indices into the array,
00463   // we need to clamp them to within the domain
00464   srcI = btMax(srcI, btScalar(0.0));
00465   srcI = btMin(srcI, btScalar(m_numNodesWidth - 1));
00466   srcJ = btMax(srcJ, btScalar(0.0));
00467   srcJ = btMin(srcJ, btScalar(m_numNodesLength - 1));
00468 
00469   return bilinearInterpolate(array, srcI, srcJ);
00470 }
00471 
00472 void btHfFluid::advectEta(btScalar dt)
00473 {
00474   for (int i = 1; i < m_numNodesWidth - 1; i++)
00475   {
00476     for (int j = 1; j < m_numNodesLength - 1; j++)
00477     {
00478       int index = arrayIndex(i, j);
00479 
00480       if (!m_flags[index])
00481         continue;
00482 
00483       btScalar u = m_globalVelocityU;
00484       btScalar v = m_globalVelocityV;
00485 
00486       u += (m_u[m_velocityIndex][index] + m_u[m_velocityIndex][index + 1]) * btScalar(0.5);
00487       v += (m_v[m_velocityIndex][index] + m_v[m_velocityIndex][index + m_numNodesWidth]) * btScalar(0.5);
00488 
00489       m_temp[index] = advect(m_eta, btScalar(i), btScalar(j), u, v, dt);
00490     }
00491   }
00492   for (int i = 1; i < m_numNodesWidth - 1; i++)
00493   {
00494     for (int j = 1; j < m_numNodesLength - 1; j++)
00495     {
00496       int index = arrayIndex(i, j);
00497       m_eta[index] = m_temp[index];
00498     }
00499   }
00500 }
00501 
00502 void btHfFluid::updateHeight(btScalar dt)
00503 {
00504   for (int j = 1; j < m_numNodesLength - 1; j++)
00505   {
00506     for (int i = 1; i < m_numNodesWidth - 1; i++)
00507     {
00508       int index = arrayIndex(i, j);
00509       if (!m_flags[index])
00510       {
00511         m_height[m_heightIndex][index] = m_ground[index] + m_eta[index];
00512         continue;
00513       }
00514       btScalar deta = -btScalar(0.5f) * m_eta[index] * dt * m_gridCellWidthInv
00515           * ((m_u[m_velocityIndex][index + 1] - m_u[m_velocityIndex][index])
00516               + (m_v[m_velocityIndex][index + m_numNodesWidth] - m_v[m_velocityIndex][index]));
00517       m_eta[index] += deta;
00518       m_height[m_heightIndex][index] = m_ground[index] + btMax(m_eta[index], btScalar(0.0f));
00519     }
00520   }
00521 }
00522 
00523 void btHfFluid::advectVelocityU(btScalar dt)
00524 {
00525   for (int i = 1; i < m_numNodesWidth - 1; i++)
00526   {
00527     for (int j = 1; j < m_numNodesLength - 1; j++)
00528     {
00529       int index = arrayIndex(i, j);
00530       if (!m_flags[index])
00531       {
00532         continue;
00533       }
00534       btScalar u = m_globalVelocityU;
00535       btScalar v = m_globalVelocityV;
00536 
00537       u += m_u[m_velocityIndex][index];
00538       v += (m_v[m_velocityIndex][index] + m_v[m_velocityIndex][index + 1]
00539           + m_v[m_velocityIndex][index + m_numNodesWidth] + m_v[m_velocityIndex][index + m_numNodesWidth + 1])
00540           * btScalar(0.25);
00541 
00542       m_temp[index] = advect(m_u[m_velocityIndex], btScalar(i), btScalar(j), u, v, dt);
00543     }
00544   }
00545 
00546   for (int i = 1; i < m_numNodesWidth - 1; i++)
00547   {
00548     for (int j = 1; j < m_numNodesLength - 1; j++)
00549     {
00550       int index = arrayIndex(i, j);
00551       m_u[m_velocityIndex][index] = m_temp[index];
00552     }
00553   }
00554 }
00555 
00556 void btHfFluid::advectVelocityV(btScalar dt)
00557 {
00558   for (int i = 1; i < m_numNodesWidth - 1; i++)
00559   {
00560     for (int j = 1; j < m_numNodesLength - 1; j++)
00561     {
00562       int index = arrayIndex(i, j);
00563       if (!m_flags[index])
00564       {
00565         continue;
00566       }
00567       btScalar u = m_globalVelocityU;
00568       btScalar v = m_globalVelocityV;
00569 
00570       u += (m_u[m_velocityIndex][index] + m_u[m_velocityIndex][index + 1]
00571           + m_u[m_velocityIndex][index + m_numNodesWidth] + m_u[m_velocityIndex][index + m_numNodesWidth + 1])
00572           * btScalar(0.25);
00573       v += m_v[m_velocityIndex][index];
00574 
00575       m_temp[index] = advect(m_v[m_velocityIndex], btScalar(i), btScalar(j), u, v, dt);
00576     }
00577   }
00578   for (int i = 1; i < m_numNodesWidth - 1; i++)
00579   {
00580     for (int j = 1; j < m_numNodesLength - 1; j++)
00581     {
00582       int index = arrayIndex(i, j);
00583       m_v[m_velocityIndex][index] = m_temp[index];
00584     }
00585   }
00586 }
00587 
00588 void btHfFluid::addDisplaced(int i, int j, btScalar r)
00589 {
00590   m_r[m_rIndex][arrayIndex(i, j)] += r;
00591 }
00592 
00593 void btHfFluid::transferDisplaced(btScalar dt)
00594 {
00595   for (int i = 2; i < m_numNodesWidth - 2; i++)
00596   {
00597     for (int j = 2; j < m_numNodesLength - 2; j++)
00598     {
00599       btScalar deltaR = m_r[m_rIndex][arrayIndex(i, j)] - m_r[(m_rIndex + 1) % 2][arrayIndex(i, j)];
00600       // deltaR is in volume, but we want to change the height..
00601       deltaR = deltaR / (m_gridCellWidth * m_gridCellWidth);
00602       deltaR *= m_volumeDisplacementScale;
00603       btScalar qdeltaR = deltaR / btScalar(4.0f);
00604       m_eta[arrayIndex(i - 1, j - 1)] += qdeltaR;
00605       m_eta[arrayIndex(i - 1, j + 1)] += qdeltaR;
00606       m_eta[arrayIndex(i + 1, j - 1)] += qdeltaR;
00607       m_eta[arrayIndex(i + 1, j + 1)] += qdeltaR;
00608       m_eta[arrayIndex(i, j)] -= deltaR;
00609       // OPTIMIZATION: zero out next frames r value
00610       m_r[(m_rIndex + 1) % 2][arrayIndex(i, j)] = btScalar(0.0);
00611     }
00612   }
00613   m_rIndex = (m_rIndex + 1) % 2; // flip frame
00614 }
00615 
00616 void btHfFluid::updateVelocity(btScalar dt)
00617 {
00618   for (int j = 1; j < m_numNodesLength - 1; j++)
00619   {
00620     for (int i = 2; i < m_numNodesWidth - 1; i++)
00621     {
00622       int index = arrayIndex(i, j);
00623       if (!m_flags[index])
00624       {
00625         continue;
00626       }
00627       m_u[m_velocityIndex][index] += m_gravity * dt * m_gridCellWidthInv
00628           * (m_height[m_heightIndex][index] - m_height[m_heightIndex][index - 1]);
00629     }
00630   }
00631 
00632   for (int j = 2; j < m_numNodesLength - 1; j++)
00633   {
00634     for (int i = 1; i < m_numNodesWidth - 1; i++)
00635     {
00636       int index = arrayIndex(i, j);
00637       if (!m_flags[index])
00638       {
00639         continue;
00640       }
00641       m_v[m_velocityIndex][index] += m_gravity * dt * m_gridCellWidthInv
00642           * (m_height[m_heightIndex][index] - m_height[m_heightIndex][index - m_numNodesWidth]);
00643     }
00644   }
00645 }
00646 
00647 void btHfFluid::setReflectBoundaryLeft()
00648 {
00649   for (int j = 0; j < m_numNodesLength; j++)
00650   {
00651     int indexL = arrayIndex(0, j);
00652 
00653     m_height[m_heightIndex][indexL] = m_height[m_heightIndex][indexL + 1];
00654     m_u[m_velocityIndex][indexL + 1] = btScalar(0.0);
00655     m_v[m_velocityIndex][indexL] = btScalar(0.0);
00656   }
00657 }
00658 
00659 void btHfFluid::setReflectBoundaryRight()
00660 {
00661   for (int j = 0; j < m_numNodesLength; j++)
00662   {
00663     int indexR = arrayIndex(m_numNodesWidth - 1, j);
00664 
00665     m_height[m_heightIndex][indexR] = m_height[m_heightIndex][indexR - 1];
00666     m_u[m_velocityIndex][indexR - 1] = btScalar(0.0);
00667     m_v[m_velocityIndex][indexR] = btScalar(0.0);
00668   }
00669 }
00670 
00671 void btHfFluid::setReflectBoundaryBottom()
00672 {
00673   for (int i = 0; i < m_numNodesWidth; i++)
00674   {
00675     int indexT = arrayIndex(i, 0);
00676 
00677     m_height[m_heightIndex][indexT] = m_height[m_heightIndex][indexT + m_numNodesWidth];
00678     m_v[m_velocityIndex][indexT + m_numNodesWidth] = btScalar(0.0);
00679     m_u[m_velocityIndex][indexT] = btScalar(0.0);
00680   }
00681 }
00682 
00683 void btHfFluid::setReflectBoundaryTop()
00684 {
00685   for (int i = 0; i < m_numNodesWidth; i++)
00686   {
00687     int indexB = arrayIndex(i, m_numNodesLength - 1);
00688 
00689     m_height[m_heightIndex][indexB] = m_height[m_heightIndex][indexB - m_numNodesWidth];
00690     m_v[m_velocityIndex][indexB - m_numNodesWidth] = btScalar(0.0);
00691     m_u[m_velocityIndex][indexB] = btScalar(0.0);
00692   }
00693 }
00694 
00695 void btHfFluid::setAbsorbBoundaryLeft(btScalar dt)
00696 {
00697   for (int j = 0; j < m_numNodesLength; j++)
00698   {
00699     int indexL = arrayIndex(0, j);
00700 
00701     btScalar c = btSqrt(m_eta[indexL + 1] * m_gravity);
00702     m_height[m_heightIndex][indexL] = ((m_gridCellWidthInv * m_height[(m_heightIndex + 1) % 2][indexL + 1])
00703         + (dt * c * m_height[m_heightIndex][indexL + 1])) / (m_gridCellWidthInv + dt * c);
00704     m_u[m_velocityIndex][indexL + 1] = btScalar(0.0f);
00705     m_v[m_velocityIndex][indexL + 1] = btScalar(0.0);
00706   }
00707 }
00708 
00709 void btHfFluid::setAbsorbBoundaryRight(btScalar dt)
00710 {
00711 }
00712 
00713 void btHfFluid::setAbsorbBoundaryTop(btScalar dt)
00714 {
00715 }
00716 
00717 void btHfFluid::setAbsorbBoundaryBottom(btScalar dt)
00718 {
00719 }
00720 
00721 void btHfFluid::computeFlagsAndFillRatio()
00722 {
00723   for (int i = 1; i < m_numNodesWidth - 1; i++)
00724   {
00725     for (int j = 1; j < m_numNodesLength - 1; j++)
00726     {
00727       btScalar h = m_height[m_heightIndex][arrayIndex(i, j)];
00728       btScalar hMin = computeHmin(i, j);
00729       btScalar hMax = computeHmax(i, j);
00730       btScalar etaMax = computeEtaMax(i, j);
00731       if (h <= hMin && etaMax < m_epsEta)
00732       {
00733         m_flags[arrayIndex(i, j)] = false;
00734         m_fillRatio[arrayIndex(i, j)] = btScalar(0.0f);
00735       }
00736       else if (h > hMax)
00737       {
00738         m_flags[arrayIndex(i, j)] = true;
00739         m_fillRatio[arrayIndex(i, j)] = btScalar(1.0f);
00740       }
00741       else
00742       {
00743         m_flags[arrayIndex(i, j)] = true;
00744         m_fillRatio[arrayIndex(i, j)] = (h - hMin) / (hMax - hMin);
00745       }
00746 
00747     }
00748   }
00749 }
00750 
00751 btScalar btHfFluid::computeHmin(int i, int j)
00752 {
00753   btAssert(i > 0);
00754   btAssert(i < m_numNodesWidth - 1);
00755   btAssert(j > 0);
00756   btAssert(j < m_numNodesLength - 1);
00757 
00758   btScalar h1 = m_height[m_heightIndex][arrayIndex(i - 1, j - 1)];
00759   btScalar h2 = m_height[m_heightIndex][arrayIndex(i - 1, j + 1)];
00760   btScalar h3 = m_height[m_heightIndex][arrayIndex(i + 1, j - 1)];
00761   btScalar h4 = m_height[m_heightIndex][arrayIndex(i + 1, j + 1)];
00762   btScalar h = m_height[m_heightIndex][arrayIndex(i, j)];
00763   btScalar minh = btMin(h1, btMin(h2, btMin(h3, h4)));
00764 
00765   return (minh + h) * btScalar(0.5f);
00766 }
00767 
00768 btScalar btHfFluid::computeHmax(int i, int j)
00769 {
00770   btAssert(i > 0);
00771   btAssert(i < m_numNodesWidth - 1);
00772   btAssert(j > 0);
00773   btAssert(j < m_numNodesLength - 1);
00774 
00775   btScalar h1 = m_height[m_heightIndex][arrayIndex(i - 1, j - 1)];
00776   btScalar h2 = m_height[m_heightIndex][arrayIndex(i - 1, j + 1)];
00777   btScalar h3 = m_height[m_heightIndex][arrayIndex(i + 1, j - 1)];
00778   btScalar h4 = m_height[m_heightIndex][arrayIndex(i + 1, j + 1)];
00779   btScalar h = m_height[m_heightIndex][arrayIndex(i, j)];
00780   btScalar maxh = btMax(h1, btMax(h2, btMax(h3, h4)));
00781 
00782   return (maxh + h) * btScalar(0.5f) + m_epsHeight;
00783 }
00784 
00785 btScalar btHfFluid::computeEtaMax(int i, int j)
00786 {
00787   btAssert(i > 0);
00788   btAssert(i < m_numNodesWidth - 1);
00789   btAssert(j > 0);
00790   btAssert(j < m_numNodesLength - 1);
00791 
00792   btScalar eta1 = m_eta[arrayIndex(i - 1, j - 1)];
00793   btScalar eta2 = m_eta[arrayIndex(i - 1, j + 1)];
00794   btScalar eta3 = m_eta[arrayIndex(i + 1, j - 1)];
00795   btScalar eta4 = m_eta[arrayIndex(i + 1, j + 1)];
00796   btScalar eta = m_eta[arrayIndex(i, j)];
00797   btScalar maxeta = btMax(eta1, btMax(eta2, btMax(eta3, eta4)));
00798 
00799   return (maxeta + eta) * btScalar(0.5f);
00800 }
00801 
00802 void btHfFluid::allocateArrays()
00803 {
00804   if (m_temp)
00805     btAlignedFree (m_temp);
00806   if (m_height[0])
00807   {
00808     btAlignedFree (m_height[0]);
00809     btAlignedFree(m_height[1]);
00810   }
00811   if (m_ground)
00812     btAlignedFree (m_ground);
00813   if (m_eta)
00814     btAlignedFree (m_eta);
00815   if (m_u[0])
00816   {
00817     btAlignedFree (m_u[0]);
00818     btAlignedFree(m_u[1]);
00819   }
00820   if (m_v)
00821   {
00822     btAlignedFree (m_v[0]);
00823     btAlignedFree(m_v[1]);
00824   }
00825   if (m_r)
00826   {
00827     btAlignedFree (m_r[0]);
00828     btAlignedFree(m_r[1]);
00829   }
00830   if (m_flags)
00831     btAlignedFree (m_flags);
00832   if (m_fillRatio)
00833     btAlignedFree (m_fillRatio);
00834 
00835   m_heightIndex = 0;
00836   m_velocityIndex = 0;
00837   m_temp = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00838   m_height[0] = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00839   m_height[1] = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00840   m_ground = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00841   m_eta = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00842   m_u[0] = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00843   m_u[1] = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00844   m_v[0] = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00845   m_v[1] = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00846   m_r[0] = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00847   m_r[1] = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00848   m_fillRatio = (btScalar*)btAlignedAlloc(sizeof(btScalar) * m_numNodesWidth * m_numNodesLength, 16);
00849   m_flags = (bool*)btAlignedAlloc(sizeof(bool) * m_numNodesWidth * m_numNodesLength, 16);
00850 
00851   {
00852     int bufferSize = sizeof(btScalar) * m_numNodesWidth * m_numNodesLength;
00853     /*printf("[HfFluid] Fluid buffer size %d bytes\n", bufferSize);
00854      printf("[HfFluid] Temp %d buffers\n", 1);
00855      printf("[HfFluid] Height %d buffers\n", 2);
00856      printf("[HfFluid] Velocity %d buffers\n", 4);
00857      printf("[HfFluid] Eta %d buffers\n", 1);
00858      printf("[HfFluid] Fill Ratio %d buffers\n", 1);
00859      printf("[HfFluid] ===============================\n");
00860      printf("[HfFluid] Total %d buffers\n", 9);
00861      printf("[HfFluid] Total %d KB\n", (9 * bufferSize)/1024);*/
00862   }
00863   for (int i = 0; i < m_numNodesWidth * m_numNodesLength; i++)
00864   {
00865     m_eta[i] = btScalar(0.0);
00866     m_u[0][i] = btScalar(0.0);
00867     m_u[1][i] = btScalar(0.0);
00868     m_v[0][i] = btScalar(0.0);
00869     m_v[1][i] = btScalar(0.0);
00870     m_r[0][i] = btScalar(0.0);
00871     m_r[1][i] = btScalar(0.0);
00872     m_height[0][i] = btScalar(0.0);
00873     m_height[1][i] = btScalar(0.0);
00874     m_ground[i] = btScalar(-10.0);
00875     m_flags[i] = false;
00876     m_fillRatio[i] = btScalar(0.0);
00877     m_temp[i] = btScalar(0.0);
00878   }
00879 }
00880 
00881 void btHfFluid::debugTests()
00882 {
00883   static btScalar total_volume = btScalar(0.0f);
00884   btScalar new_total_volume = btScalar(0.0f);
00885   for (int i = 0; i < m_numNodesWidth * m_numNodesLength; i++)
00886   {
00887     new_total_volume += m_eta[i] * m_gridCellWidth * m_gridCellWidth;
00888   }
00889   //printf("volume = %f volume delta = %f\n", new_total_volume, new_total_volume - total_volume);
00890   total_volume = new_total_volume;
00891 }
00892 
00893 // You can enforce a global velocity at the surface of the fluid
00894 // default: 0.0 and 0.0
00895 void btHfFluid::setGlobaVelocity(btScalar globalVelocityU, btScalar globalVelocityV)
00896 {
00897   m_globalVelocityU = globalVelocityU;
00898   m_globalVelocityV = globalVelocityV;
00899 }
00900 
00901 void btHfFluid::getGlobalVelocity(btScalar& globalVelocityU, btScalar& globalVelocityV) const
00902 {
00903   globalVelocityU = m_globalVelocityU;
00904   globalVelocityV = m_globalVelocityV;
00905 }
00906 
00907 // Control force of gravity, should match physics world
00908 // default: -10.0
00909 void btHfFluid::setGravity(btScalar gravity)
00910 {
00911   m_gravity = gravity;
00912 }
00913 btScalar btHfFluid::getGravity() const
00914 {
00915   return m_gravity;
00916 }
00917 
00918 // When a body is submerged into the fluid, the displaced fluid
00919 // is spread to adjacent cells. You can control the percentage of this
00920 // by setting this value between 0.0 and 1.0
00921 // default: 0.5
00922 void btHfFluid::setVolumeDisplacementScale(btScalar volumeDisplacementScale)
00923 {
00924   m_volumeDisplacementScale = volumeDisplacementScale;
00925 }
00926 
00927 btScalar btHfFluid::getVolumeDisplacementScale() const
00928 {
00929   return m_volumeDisplacementScale;
00930 }
00931 
00932 // The horizontal velocity of the fluid can influence bodies submerged
00933 // in the fluid. You can control how much influence by setting this
00934 // between 0.0 and 1.0
00935 // default: 0.5
00936 void btHfFluid::setHorizontalVelocityScale(btScalar horizontalVelocityScale)
00937 {
00938   m_horizontalVelocityScale = horizontalVelocityScale;
00939 }
00940 
00941 btScalar btHfFluid::getHorizontalVelocityScale() const
00942 {
00943   return m_horizontalVelocityScale;
00944 }
00945 
00946 static btScalar rangeOverlap(btScalar lo1, btScalar hi1, btScalar lo2, btScalar hi2, btScalar& loOut, btScalar& hiOut)
00947 {
00948   if (!(lo1 <= hi2 && lo2 <= hi1))
00949     return btScalar(0.0f);
00950 
00951   if (lo1 >= lo2 && lo1 <= hi2 && hi1 >= lo2 && hi1 <= hi2)
00952   {
00953     hiOut = hi1;
00954     loOut = lo1;
00955     return hi1 - lo1;
00956   }
00957   else if (lo2 >= lo1 && lo2 <= hi1 && hi2 >= lo1 && hi2 <= hi1)
00958   {
00959     hiOut = hi2;
00960     loOut = lo2;
00961     return hi2 - lo2;
00962   }
00963   else if (hi1 >= lo2 && lo1 <= lo2)
00964   {
00965     hiOut = hi1;
00966     loOut = lo2;
00967     return hi1 - lo2;
00968   }
00969   else
00970   {
00971     hiOut = hi2;
00972     loOut = lo1;
00973     return hi2 - lo1;
00974   }
00975 }
00976 
00977 btHfFluidColumnRigidBodyCallback::btHfFluidColumnRigidBodyCallback(btRigidBody* rigidBody, btIDebugDraw* debugDraw,
00978                                                                    btScalar density, btScalar floatyness)
00979 {
00980   m_rigidBody = rigidBody;
00981   m_buoyantShape = (btHfFluidBuoyantConvexShape*)rigidBody->getCollisionShape();
00982   m_debugDraw = debugDraw;
00983   m_rigidBody->getAabb(m_aabbMin, m_aabbMax);
00984   m_volume = btScalar(0.0f);
00985   m_density = density;
00986   m_floatyness = floatyness;
00987   m_numVoxels = m_buoyantShape->getNumVoxels();
00988   m_voxelPositionsXformed = (btVector3*)btAlignedAlloc(sizeof(btVector3) * m_numVoxels, 16);
00989   m_voxelSubmerged = (bool*)btAlignedAlloc(sizeof(bool) * m_numVoxels, 16);
00990   for (int i = 0; i < m_numVoxels; i++)
00991   {
00992     btVector3 p = m_buoyantShape->getVoxelPositionsArray()[i];
00993     p = m_rigidBody->getWorldTransform().getBasis() * p;
00994     p += m_rigidBody->getWorldTransform().getOrigin();
00995     m_voxelPositionsXformed[i] = p;
00996     m_voxelSubmerged[i] = false;
00997   }
00998 }
00999 
01000 btHfFluidColumnRigidBodyCallback::~btHfFluidColumnRigidBodyCallback()
01001 {
01002   if (m_voxelPositionsXformed)
01003     btAlignedFree (m_voxelPositionsXformed);
01004   if (m_voxelSubmerged)
01005     btAlignedFree (m_voxelSubmerged);
01006 }
01007 
01008 static bool sphereVsAABB(const btVector3& aabbMin, const btVector3& aabbMax, const btVector3& sphereCenter,
01009                          const btScalar sphereRadius)
01010 {
01011   btScalar totalDistance = 0;
01012 
01013   // Accumulate the distance of the sphere's center on each axis
01014   for (int i = 0; i < 3; ++i)
01015   {
01016 
01017     // If the sphere's center is outside the aabb, we've got distance on this axis
01018     if (sphereCenter[i] < aabbMin[i])
01019     {
01020       btScalar borderDistance = aabbMin[i] - sphereCenter[i];
01021       totalDistance += borderDistance * borderDistance;
01022 
01023     }
01024     else if (sphereCenter[i] > aabbMax[i])
01025     {
01026       btScalar borderDistance = sphereCenter[i] - aabbMax[i];
01027       totalDistance += borderDistance * borderDistance;
01028 
01029     }
01030     // Otherwise the sphere's center is within the box on this axis, so the
01031     // distance will be 0 and we do not need to accumulate anything at all
01032 
01033   }
01034 
01035   // If the distance to the box is lower than the sphere's radius, both are overlapping
01036   return (totalDistance <= (sphereRadius * sphereRadius));
01037 }
01038 
01039 bool btHfFluidColumnRigidBodyCallback::processColumn(btHfFluid* fluid, int w, int l)
01040 {
01041   btVector3 columnAabbMin, columnAabbMax;
01042 
01043   fluid->getAabbForColumn(w, l, columnAabbMin, columnAabbMax);
01044 
01045   bool applyBuoyancyImpulse = true;
01046   bool applyFluidVelocityImpulse = true;
01047   bool applyFluidDisplace = true;
01048 
01049   btScalar dt = btScalar(1.0f / 60.0f);
01050 
01051   btScalar columnVolume = btScalar(0.0f);
01052 
01053   for (int i = 0; i < m_buoyantShape->getNumVoxels(); i++)
01054   {
01055     if (m_voxelSubmerged[i])
01056       continue;
01057 
01058     if (sphereVsAABB(columnAabbMin, columnAabbMax, m_voxelPositionsXformed[i], m_buoyantShape->getVoxelRadius()))
01059     {
01060 
01061       m_voxelSubmerged[i] = true;
01062       btScalar voxelVolume = m_buoyantShape->getVolumePerVoxel();
01063       columnVolume += voxelVolume;
01064 
01065       btVector3 application_point = m_voxelPositionsXformed[i];
01066       btVector3 relative_position = application_point - m_rigidBody->getCenterOfMassPosition();
01067 
01068       if (applyBuoyancyImpulse)
01069       {
01070         //btScalar massDisplacedWater = voxelVolume * m_density * m_floatyness;  <-- this density is object density, changed to water density(constant)
01071         btScalar massDisplacedWater = voxelVolume * 1027 * m_floatyness;
01072         btScalar force = massDisplacedWater * -fluid->getGravity();
01073         btScalar impulseMag = force * dt;
01074         //std::cout<<"Aquin: "<<impulseMag<<std::endl;
01075         btVector3 impulseVec = btVector3(btScalar(0.0f), btScalar(0.0f), btScalar(1.0f)) * impulseMag;
01076         //std::cout<<"Aquin: "<<impulseVec.x()<<" "<<impulseVec.y()<<" "<<impulseVec.z()<<" "<<std::endl;
01077 //#define CENTER_IMPULSE_ONLY 1
01078 #ifdef CENTER_IMPULSE_ONLY
01079         m_rigidBody->applyCentralImpulse (impulseVec);
01080 #else
01081         m_rigidBody->applyImpulse(impulseVec, relative_position);
01082 #endif
01083       }
01084     }
01085   }
01086 
01087   if (columnVolume > btScalar(0.0))
01088   {
01089     m_volume += columnVolume;
01090 
01091     if (applyFluidDisplace)
01092     {
01093       fluid->addDisplaced(w, l, columnVolume);
01094     }
01095 
01096     if (applyFluidVelocityImpulse)
01097     {
01098       int index = fluid->arrayIndex(w, l);
01099       btScalar u = fluid->getVelocityUArray()[index];
01100       btScalar v = fluid->getVelocityVArray()[index];
01101       btVector3 vd = btVector3(u, btScalar(0.0f), v);
01102       btVector3 impulse = vd * dt * fluid->getHorizontalVelocityScale();
01103       //std::cout<<"Aquin: "<<impulse.x()<<" "<<impulse.y()<<" "<<impulse.z()<<" "<<std::endl;
01104       m_rigidBody->applyCentralImpulse(impulse);
01105     }
01106   }
01107 
01108   return true;
01109 }
01110 


uwsim
Author(s): Mario Prats
autogenerated on Mon Oct 6 2014 08:24:07