mWorld = new OgreBulletDynamics::DynamicsWorld(mSceneMgr, Ogre::AxisAlignedBox
(Ogre::Vector3 (-10000, -10000, -10000),Ogre::Vector3 (10000, 10000, 10000)), gravityVector);
\end{lstlisting}
+The physics animation needs to be update every time a new frame is rendered:
+\begin{lstlisting}
+mWorld->stepSimulation(evt.timeSinceLastFrame);
+\end{lstlisting}
Each time we create an object that we want to assign some physical properties to, we need to create a rigid body object and assign some collision shape to it. That is useful for non-animated objects that do not change their shape over time. Consider a very simple example: we would like our avatar to be able to throw balls and implement it as follows:
\centering
\epsfig{file=avatar-ball.eps,width=9cm}
\caption{Using Bullet with OgreBullet for collision detection: collision shapes}
-\label{fig:avatar-call}
+\label{fig:avatar-ball}
\end{figure}
-As a result, out avatar can throw balls that behave correctly in the physical world (Figure ...). We set a Bullet container around the avatar in a similar way we did for the box, which makes it possible for the avatar to interact with the physical objects, which are balls in this example. If the avatar collides with the ball, the ball will get a physical impulse an move in the corresponding direction. If a ball will fall on the avatar, it would bounce off. However, the avatar itself would not be affected. We do not want avatar's motion to be affected by other physical objects, since it is controlled by user input. Therefore, we set the avatar's bullet body to be kinematic, which means it can affect other physical bodies, but not vice versa:
+As a result, out avatar can throw balls that behave correctly in the physical world (Figure~\ref{fig:avatar-ball}). We set a Bullet container around the avatar in a similar way we did for the box, which makes it possible for the avatar to interact with the physical objects, which are balls in this example. If the avatar collides with the ball, the ball will get a physical impulse an move in the corresponding direction. If a ball will fall on the avatar, it would bounce off. However, the avatar itself would not be affected. We do not want avatar's motion to be affected by other physical objects, since it is controlled by user input. Therefore, we set the avatar's bullet body to be kinematic, which means it can affect other physical bodies, but not vice versa:
\begin{lstlisting}
playerRididBody->setKinematicObject(true);
\end{lstlisting}
\label{fig:wrong-bone-containers-full}
\end{figure}
-To figure out why the solution did not produce expected results, we could approach the problem from different perspectives. One possibility would be to analyze the OgreBullet's source code more throughly and spot possible mistake we have done (for example, usage of wrong transformation matrices). Another option would be to use a different wrapper for Bullet, like \textit{BtOgre}\footnote{https://github.com/nikki93/btogre}. However, the problem could originate from Bullet itself. When using some of the collision shapes, we got the following message at runtime:
+To figure out why the solution did not produce expected results, we could approach the problem from different perspectives. One possibility would be to analyze the OgreBullet's source code more throughly and spot possible reasons for the proble. Another option would be to use a different wrapper for Bullet, like \textit{BtOgre}\footnote{https://github.com/nikki93/btogre}. However, the problem could originate from Bullet itself. When using some of the collision shapes, we got the following message at runtime:
\begin{quotation}
"Overflow in AABB, object removed from simulation.
Thanks."
\end{quotation}
+In any case, since creating a simple collision geometry without taking animated mesh into account produces correst results, we consider that option to be more effective. An optimal way to implement collision detection for an avatar is to use Bullet's Kinematic Character Controller\footnote{http://www.continuousphysics.com/Bullet/BulletFull/classbtKinematicCharacterController.html}. However, there is no wrapper class for it in OgreBullet. \
-An optimal way to do that is to use Bullet's Kinematic Character Controller\footnote{http://www.continuousphysics.com/Bullet/BulletFull/classbtKinematicCharacterController.html}. However, there is no wrapper class for it in OgreBullet.
-
-After having made a few honest attemps to achieve the desired funtionality by integrating Bullet into our projects, we concluded that Bullet itself was a rather useful framework. It is well documented and has an API available online. Nevertheless, most of the wrappers seem to be unofficial \/"hobby" projects and do not provide clear documentation, which introduces a number of complications in using them. At the same time, we could try to implement a wrapper
+A simple way to perform simple broadphase collision with Bullet is by calling Bullet's collision dispatcher directly:
+\begin{lstlisting}
+// get the pointer to Bullet's dynamics world object
+btDynamicsWorld *bulletWorld = mWorld->getBulletDynamicsWorld();
+int numberOfManifolds = btWorld->getDispatcher()->getNumManifolds();
+btSimpleBroadphase *simpleBroadphase = new btSimpleBroadphase();
+
+for (int i = 0; i < numberOfManifolds; i++)
+{
+ btPersistentManifold* contactManifold = btWorld->getDispatcher()->getManifoldByIndexInternal(i);
+
+ int numberOfContacts = contactManifold->getNumContacts();
+
+ for (int j = 0; j<numberOfContacts; j++)
+ {
+ btManifoldPoint& contactPoint = contactManifold->getContactPoint(j);
+ if (contactPoint.getDistance() < 0.f)
+ {
+ // collision: do something if necessary
+ }
+ }
+}
+\end{lstlisting}
-Besides, using Bullet for only simple collision detection in our case would create much unnecessary overhead. \
+It is necessary to note that for determining exactly which objects collide, we need to keep track of all Bullet rigid bodies that we have create through OgreBullet objects. Although, if we only need to detect collision (for example, if we only add collision geometry for avatar objects and want to know when they collide, as shown on Figure ... ), then using the above algorithm is sufficient. \
+We tried this solution, and the outcome was successful. However, it required considerably more computational power and therefore reduced application's responsiveness. \
\begin{figure}[hp]
\centering
\label{fig:collision-avatar}
\end{figure}
-To make sure that collision detection computations do not significantly affect rendering rate, we decided to use essentially simplified collision detection method instead. Every time we change the position of an object \begin{math}O\end{math} in the virtual world, we calculate the distance between its new position and every other object it can collide with. Collision occurs if the distance between \begin{math}O\end{math} and any other object exceeds a certain limit. For all the static game world objects, distance to any point in the object's bounding box is used to detect collision with a movable object (Figure~\ref{fig:collision-world}). For movable objects, such as avatars, we use positions instead of bounding box geometry, since a bounding box typically encloses the object's mesh in its original state, before it is animated. Therefore, using the distance to the objects center is easier for adjusting calculations (Figure~\ref{fig:collision-avatar}). If collision occurs, we move \begin{math}O\end{math} back to its previous position.\
+To avoid significantly decreasing the frame rendering rate by expensive collision detection computations, we decided to use essentially simplified collision detection method instead. Every time we change the position of an object \begin{math}O\end{math} in the virtual world, we calculate the distance between its new position and every other object it can collide with. Collision occurs if the distance between \begin{math}O\end{math} and any other object exceeds a certain limit. For all the static game world objects, distance to any point in the object's bounding box is used to detect collision with a movable object (Figure~\ref{fig:collision-world}). For movable objects, such as avatars, we use positions instead of bounding box geometry, since a bounding box typically encloses the object's mesh in its original state, before it is animated. Therefore, using the distance to the objects center is easier for adjusting calculations (Figure~\ref{fig:collision-avatar}). If collision occurs, we move \begin{math}O\end{math} back to its previous position.\
Most physics engine also simulate gravity. In our game world, all dynamic objects can only move on a plane, along X and Z axes in OGRE's coordinate system, and never along the Y axis. Therefore, we do not need to simulate gravity and can only translate the objects to required positions, according to user input or kinematic state updates.
return BaseApplication::frameRenderingQueued(evt);
}
\end{lstlisting}
-\
By updating the motion states of the objects each time a new frame is queued for rendering and taking into account the time that passed since last frame was rendered in calculating new positions of the objects, we simulate motion that is smooth. While the local player entity is being controlled by user input, all the remote player entities are not. The information about their kinematic states is extracted from the state update messages, but those are received with the interval of 200 ms. If we decided to only update the positions of remote players every time an update message arrives, the motion would no longer look realistic and the user would see remote players jumping from one position to another instead of moving smoothly. Doing that should be avoided, so remote players' motion needs to be evenly interpolated between the known positions, which we can simply refer to as points. \
Interpolation between points in the virtual world space can be generally done in two ways:
//-------------------------------------------------------------------------------------
void Game::init()
{
+ std::cout << "init\n";
mNumEntitiesInstanced = 0; // how many shapes are created
// create local player and attach camera to it
createPlayer(myPlayerId);
+ createPlayer(6);
+
playerNodes[myPlayerId]->attachObject(daylight);
playerNodes[myPlayerId]->attachObject(sunlight);
transVector.z = 0;
}
}
-
+ bool collision = CollisionCheck();
+
+ // if (!collision)
+ // {
+ playerNodes[myPlayerId]->translate(transVector * evt.timeSinceLastFrame, Ogre::Node::TS_LOCAL);
+ playerNodes[myPlayerId]->yaw(Ogre::Degree(mRotate) * evt.timeSinceLastFrame);
+ // }
- playerNodes[myPlayerId]->translate(transVector * evt.timeSinceLastFrame, Ogre::Node::TS_LOCAL);
- playerNodes[myPlayerId]->yaw(Ogre::Degree(mRotate) * evt.timeSinceLastFrame);
- // CollisionCheck();
+
mRotate = 0;
return true;
//-------------------------------------------------------------------------------------
bool Game::CollisionCheck()
{
+
int numManifolds = btWorld->getDispatcher()->getNumManifolds();
btSimpleBroadphase *simplebp = new btSimpleBroadphase();
- bool overlap = simplebp->testAabbOverlap(btRigidBodies[myPlayerId]->getBroadphaseProxy(), btRigidBodies[6]->getBroadphaseProxy());
- if (overlap)
- std::cout << "overlap\n";
+
+ // bool overlap = simplebp->testAabbOverlap(btRigidBodies[myPlayerId]->getBroadphaseProxy(), btRigidBodies[6]->getBroadphaseProxy());
+
+ //if (overlap)
+ // std::cout << "overlap\n";
for (int i = 0; i < numManifolds; i++)
{
btPersistentManifold* contactManifold = btWorld->getDispatcher()->getManifoldByIndexInternal(i);
- btRigidBody* obA = static_cast<btRigidBody*>(contactManifold->getBody0());
- btRigidBody* obB = static_cast<btRigidBody*>(contactManifold->getBody1());
- if (obA->isKinematicObject())
- // std::cout << "kinematic\n";
+ // btRigidBody* obA = static_cast<btRigidBody*>(contactManifold->getBody0());
+ // btRigidBody* obB = static_cast<btRigidBody*>(contactManifold->getBody1());
+ //if (obA->isKinematicObject())
+ // std::cout << "kinematic\n";
- if (obA == btRigidBodies[myPlayerId])
- std::cout << "me\n";
+ //if (obA == btRigidBodies[myPlayerId])
+ // std::cout << "me\n";
int numContacts = contactManifold->getNumContacts();
- std::cout << numContacts << " numContacts\n";
+
for (int j=0;j<numContacts;j++)
{
- btManifoldPoint& pt = contactManifold->getContactPoint(j);
- if (pt.getDistance()<0.f)
+ btManifoldPoint& contactPoint = contactManifold->getContactPoint(j);
+ if (contactPoint.getDistance() < 0.f)
{
- const btVector3& ptA = pt.getPositionWorldOnA();
- const btVector3& ptB = pt.getPositionWorldOnB();
- const btVector3& normalOnB = pt.m_normalWorldOnB;
+ // const btVector3& ptA = pt.getPositionWorldOnA();
+ // const btVector3& ptB = pt.getPositionWorldOnB();
+ // const btVector3& normalOnB = pt.m_normalWorldOnB;
std::cout << "collision\n";
- std::cout << pt.getDistance() << "\n";
- pt.setDistance(50.0f);
- // return true;
+ // std::cout << pt.getDistance() << "\n";
+ // pt.setDistance(50.0f);
+ std::cout << "true\n";
+ return true;
}
- else
- std::cout << pt.getDistance() << "\n";
+ //else
+ // std::cout << pt.getDistance() << "\n";
}
}
+ std::cout << "false\n";
return false;
}