PhysicsWorld.h
[spoiler][code]
//
// Copyright © 2008-2015 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the “Software”), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#pragma once
#include “…/Container/HashSet.h”
#include “…/IO/VectorBuffer.h”
#include “…/Math/BoundingBox.h”
#include “…/Math/Sphere.h”
#include “…/Math/Vector3.h”
#include “…/Scene/Component.h”
#include <Bullet/LinearMath/btIDebugDraw.h>
class btCollisionConfiguration;
class btCollisionShape;
class btBroadphaseInterface;
class btConstraintSolver;
class btDiscreteDynamicsWorld;
class btDispatcher;
class btDynamicsWorld;
class btPersistentManifold;
class btSoftBodyWorldInfo; //===============================
namespace Urho3D
{
class CollisionShape;
class Deserializer;
class Constraint;
class Model;
class Node;
class Ray;
class RigidBody;
class Scene;
class Serializer;
class XMLElement;
//=====================================
class SoftBody;
class IndexBuffer;
class VertexBuffer;
//=====================================
struct CollisionGeometryData;
/// Physics raycast hit.
struct URHO3D_API PhysicsRaycastResult
{
/// Construct with defaults.
PhysicsRaycastResult() :
body_(0)
{
}
/// Test for inequality, added to prevent GCC from complaining.
bool operator !=(const PhysicsRaycastResult& rhs) const
{
return position_ != rhs.position_ || normal_ != rhs.normal_ || distance_ != rhs.distance_ || body_ != rhs.body_;
}
/// Hit worldspace position.
Vector3 position_;
/// Hit worldspace normal.
Vector3 normal_;
/// Hit distance from ray origin.
float distance_;
/// Rigid body that was hit.
RigidBody* body_;
};
/// Delayed world transform assignment for parented rigidbodies.
struct DelayedWorldTransform
{
/// Rigid body.
RigidBody* rigidBody_;
/// Parent rigid body.
RigidBody* parentRigidBody_;
/// New world position.
Vector3 worldPosition_;
/// New world rotation.
Quaternion worldRotation_;
};
static const float DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY = 100.0f;
/// Physics simulation world component. Should be added only to the root scene node.
class URHO3D_API PhysicsWorld : public Component, public btIDebugDraw
{
OBJECT(PhysicsWorld);
friend void InternalPreTickCallback(btDynamicsWorld* world, btScalar timeStep);
friend void InternalTickCallback(btDynamicsWorld* world, btScalar timeStep);
public:
/// Construct.
PhysicsWorld(Context* scontext);
/// Destruct.
virtual ~PhysicsWorld();
/// Register object factory.
static void RegisterObject(Context* context);
//=========================================
// softbody
btSoftBodyWorldInfo* GetSoftBodyInfo() { return m_softBodyWorldInfo; }
btSoftBodyWorldInfo* m_softBodyWorldInfo;
//=========================================
/// Check if an AABB is visible for debug drawing.
virtual bool isVisible(const btVector3& aabbMin, const btVector3& aabbMax);
/// Draw a physics debug line.
virtual void drawLine(const btVector3& from, const btVector3& to, const btVector3& color);
/// Log warning from the physics engine.
virtual void reportErrorWarning(const char* warningString);
/// Draw a physics debug contact point. Not implemented.
virtual void drawContactPoint
(const btVector3& pointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color);
/// Draw physics debug 3D text. Not implemented.
virtual void draw3dText(const btVector3& location, const char* textString);
/// Set debug draw flags.
virtual void setDebugMode(int debugMode) { debugMode_ = debugMode; }
/// Return debug draw flags.
virtual int getDebugMode() const { return debugMode_; }
/// Visualize the component as debug geometry.
virtual void DrawDebugGeometry(DebugRenderer* debug, bool depthTest);
/// Step the simulation forward.
void Update(float timeStep);
/// Refresh collisions only without updating dynamics.
void UpdateCollisions();
/// Set simulation substeps per second.
void SetFps(int fps);
/// Set gravity.
void SetGravity(const Vector3& gravity);
/// Set maximum number of physics substeps per frame. 0 (default) is unlimited. Positive values cap the amount. Use a negative value to enable an adaptive timestep. This may cause inconsistent physics behavior.
void SetMaxSubSteps(int num);
/// Set number of constraint solver iterations.
void SetNumIterations(int num);
/// Set whether to interpolate between simulation steps.
void SetInterpolation(bool enable);
/// Set whether to use Bullet's internal edge utility for trimesh collisions. Disabled by default.
void SetInternalEdge(bool enable);
/// Set split impulse collision mode. This is more accurate, but slower. Disabled by default.
void SetSplitImpulse(bool enable);
/// Set maximum angular velocity for network replication.
void SetMaxNetworkAngularVelocity(float velocity);
/// Perform a physics world raycast and return all hits.
void Raycast
(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
/// Perform a physics world raycast and return the closest hit.
void RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
/// Perform a physics world swept sphere test and return the closest hit.
void SphereCast
(PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask = M_MAX_UNSIGNED);
/// Perform a physics world swept convex test using a user-supplied collision shape and return the first hit.
void ConvexCast(PhysicsRaycastResult& result, CollisionShape* shape, const Vector3& startPos, const Quaternion& startRot,
const Vector3& endPos, const Quaternion& endRot, unsigned collisionMask = M_MAX_UNSIGNED);
/// Perform a physics world swept convex test using a user-supplied Bullet collision shape and return the first hit.
void ConvexCast(PhysicsRaycastResult& result, btCollisionShape* shape, const Vector3& startPos, const Quaternion& startRot,
const Vector3& endPos, const Quaternion& endRot, unsigned collisionMask = M_MAX_UNSIGNED);
/// Invalidate cached collision geometry for a model.
void RemoveCachedGeometry(Model* model);
/// Return rigid bodies by a sphere query.
void GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& sphere, unsigned collisionMask = M_MAX_UNSIGNED);
/// Return rigid bodies by a box query.
void GetRigidBodies(PODVector<RigidBody*>& result, const BoundingBox& box, unsigned collisionMask = M_MAX_UNSIGNED);
/// Return rigid bodies that have been in collision with a specific body on the last simulation step.
void GetRigidBodies(PODVector<RigidBody*>& result, const RigidBody* body);
/// Return gravity.
Vector3 GetGravity() const;
/// Return maximum number of physics substeps per frame.
int GetMaxSubSteps() const { return maxSubSteps_; }
/// Return number of constraint solver iterations.
int GetNumIterations() const;
/// Return whether interpolation between simulation steps is enabled.
bool GetInterpolation() const { return interpolation_; }
/// Return whether Bullet's internal edge utility for trimesh collisions is enabled.
bool GetInternalEdge() const { return internalEdge_; }
/// Return whether split impulse collision mode is enabled.
bool GetSplitImpulse() const;
/// Return simulation steps per second.
int GetFps() const { return fps_; }
/// Return maximum angular velocity for network replication.
float GetMaxNetworkAngularVelocity() const { return maxNetworkAngularVelocity_; }
/// Add a rigid body to keep track of. Called by RigidBody.
void AddRigidBody(RigidBody* body);
/// Remove a rigid body. Called by RigidBody.
void RemoveRigidBody(RigidBody* body);
//====================================
/// Add a soft body to keep track of. Called by SoftBody.
void AddSoftBody(SoftBody* body);
/// Remove a soft body. Called by SoftBody.
void RemoveSoftBody(SoftBody* body);
//====================================
/// Add a collision shape to keep track of. Called by CollisionShape.
void AddCollisionShape(CollisionShape* shape);
/// Remove a collision shape. Called by CollisionShape.
void RemoveCollisionShape(CollisionShape* shape);
/// Add a constraint to keep track of. Called by Constraint.
void AddConstraint(Constraint* joint);
/// Remove a constraint. Called by Constraint.
void RemoveConstraint(Constraint* joint);
/// Add a delayed world transform assignment. Called by RigidBody.
void AddDelayedWorldTransform(const DelayedWorldTransform& transform);
/// Add debug geometry to the debug renderer.
void DrawDebugGeometry(bool depthTest);
/// Set debug renderer to use. Called both by PhysicsWorld itself and physics components.
void SetDebugRenderer(DebugRenderer* debug);
/// Set debug geometry depth test mode. Called both by PhysicsWorld itself and physics components.
void SetDebugDepthTest(bool enable);
/// Return the Bullet physics world.
btDiscreteDynamicsWorld* GetWorld() { return world_; }
/// Clean up the geometry cache.
void CleanupGeometryCache();
/// Return trimesh collision geometry cache.
HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >& GetTriMeshCache() { return triMeshCache_; }
/// Return convex collision geometry cache.
HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >& GetConvexCache() { return convexCache_; }
/// Set node dirtying to be disregarded.
void SetApplyingTransforms(bool enable) { applyingTransforms_ = enable; }
/// Return whether node dirtying should be disregarded.
bool IsApplyingTransforms() const { return applyingTransforms_; }
protected:
/// Handle scene being assigned.
virtual void OnSceneSet(Scene* scene);
private:
/// Handle the scene subsystem update event, step simulation here.
void HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData);
/// Trigger update before each physics simulation step.
void PreStep(float timeStep);
/// Trigger update after each physics simulation step.
void PostStep(float timeStep);
/// Send accumulated collision events.
void SendCollisionEvents();
/// Bullet collision configuration.
btCollisionConfiguration* collisionConfiguration_;
/// Bullet collision dispatcher.
btDispatcher* collisionDispatcher_;
/// Bullet collision broadphase.
btBroadphaseInterface* broadphase_;
/// Bullet constraint solver.
btConstraintSolver* solver_;
/// Bullet physics world.
btDiscreteDynamicsWorld* world_;
/// Extra weak pointer to scene to allow for cleanup in case the world is destroyed before other components.
WeakPtr<Scene> scene_;
/// Rigid bodies in the world.
PODVector<RigidBody*> rigidBodies_;
//===================================
/// Soft bodies in the world.
PODVector<SoftBody*> softBodies_;
//===================================
/// Collision shapes in the world.
PODVector<CollisionShape*> collisionShapes_;
/// Constraints in the world.
PODVector<Constraint*> constraints_;
/// Collision pairs on this frame.
HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*> currentCollisions_;
/// Collision pairs on the previous frame. Used to check if a collision is "new." Manifolds are not guaranteed to exist anymore.
HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*> previousCollisions_;
/// Delayed (parented) world transform assignments.
HashMap<RigidBody*, DelayedWorldTransform> delayedWorldTransforms_;
/// Cache for trimesh geometry data by model and LOD level.
HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> > triMeshCache_;
/// Cache for convex geometry data by model and LOD level.
HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> > convexCache_;
/// Preallocated event data map for physics collision events.
VariantMap physicsCollisionData_;
/// Preallocated event data map for node collision events.
VariantMap nodeCollisionData_;
/// Preallocated buffer for physics collision contact data.
VectorBuffer contacts_;
/// Simulation substeps per second.
unsigned fps_;
/// Maximum number of simulation substeps per frame. 0 (default) unlimited, or negative values for adaptive timestep.
int maxSubSteps_;
/// Time accumulator for non-interpolated mode.
float timeAcc_;
/// Maximum angular velocity for network replication.
float maxNetworkAngularVelocity_;
/// Interpolation flag.
bool interpolation_;
/// Use internal edge utility flag.
bool internalEdge_;
/// Applying transforms flag.
bool applyingTransforms_;
/// Debug renderer.
DebugRenderer* debugRenderer_;
/// Debug draw flags.
int debugMode_;
/// Debug draw depth test mode.
bool debugDepthTest_;
};
/// Register Physics library objects.
void URHO3D_API RegisterPhysicsLibrary(Context* context);
}
[/code][/spoiler]
PhysicsWorld.cpp
[spoiler][code]
//
// Copyright © 2008-2015 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the “Software”), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include “…/Precompiled.h”
#include “…/Core/Context.h”
#include “…/Core/Mutex.h”
#include “…/Core/Profiler.h”
#include “…/Graphics/DebugRenderer.h”
#include “…/Graphics/Model.h”
#include “…/IO/Log.h”
#include “…/Math/Ray.h”
#include “…/Physics/CollisionShape.h”
#include “…/Physics/Constraint.h”
#include “…/Physics/PhysicsEvents.h”
#include “…/Physics/PhysicsUtils.h”
#include “…/Physics/PhysicsWorld.h”
#include “…/Physics/RigidBody.h”
#include “…/Scene/Scene.h”
#include “…/Scene/SceneEvents.h”
#include “…/Physics/SoftBody.h”
#include <Bullet/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <Bullet/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
#include <Bullet/BulletCollision/CollisionDispatch/btInternalEdgeUtility.h>
#include <Bullet/BulletCollision/CollisionShapes/btBoxShape.h>
#include <Bullet/BulletCollision/CollisionShapes/btSphereShape.h>
#include <Bullet/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>
#include <Bullet/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
//==========================================
#include <Bullet/BulletSoftBody/btSoftBody.h>
#include <Bullet/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h>
#include <Bullet/BulletSoftBody/btSoftRigidDynamicsWorld.h>
#define TEST_SOFTBODY
//==========================================
extern ContactAddedCallback gContactAddedCallback;
namespace Urho3D
{
const char* PHYSICS_CATEGORY = “Physics”;
extern const char* SUBSYSTEM_CATEGORY;
static const int MAX_SOLVER_ITERATIONS = 256;
static const int DEFAULT_FPS = 60;
static const Vector3 DEFAULT_GRAVITY = Vector3(0.0f, -9.81f, 0.0f);
static bool CompareRaycastResults(const PhysicsRaycastResult& lhs, const PhysicsRaycastResult& rhs)
{
return lhs.distance_ < rhs.distance_;
}
void InternalPreTickCallback(btDynamicsWorld* world, btScalar timeStep)
{
static_cast<PhysicsWorld*>(world->getWorldUserInfo())->PreStep(timeStep);
}
void InternalTickCallback(btDynamicsWorld* world, btScalar timeStep)
{
static_cast<PhysicsWorld*>(world->getWorldUserInfo())->PostStep(timeStep);
}
static bool CustomMaterialCombinerCallback(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0Wrap, int partId0,
int index0, const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1)
{
btAdjustInternalEdgeContacts(cp, colObj1Wrap, colObj0Wrap, partId1, index1);
cp.m_combinedFriction = colObj0Wrap->getCollisionObject()->getFriction() * colObj1Wrap->getCollisionObject()->getFriction();
cp.m_combinedRestitution =
colObj0Wrap->getCollisionObject()->getRestitution() * colObj1Wrap->getCollisionObject()->getRestitution();
return true;
}
/// Callback for physics world queries.
struct PhysicsQueryCallback : public btCollisionWorld::ContactResultCallback
{
/// Construct.
PhysicsQueryCallback(PODVector<RigidBody*>& result, unsigned collisionMask) :
result_(result),
collisionMask_(collisionMask)
{
}
/// Add a contact result.
virtual btScalar addSingleResult(btManifoldPoint&, const btCollisionObjectWrapper* colObj0Wrap, int, int,
const btCollisionObjectWrapper* colObj1Wrap, int, int)
{
RigidBody* body = reinterpret_cast<RigidBody*>(colObj0Wrap->getCollisionObject()->getUserPointer());
if (body && !result_.Contains(body) && (body->GetCollisionLayer() & collisionMask_))
result_.Push(body);
body = reinterpret_cast<RigidBody*>(colObj1Wrap->getCollisionObject()->getUserPointer());
if (body && !result_.Contains(body) && (body->GetCollisionLayer() & collisionMask_))
result_.Push(body);
return 0.0f;
}
/// Found rigid bodies.
PODVector<RigidBody*>& result_;
/// Collision mask for the query.
unsigned collisionMask_;
};
PhysicsWorld::PhysicsWorld(Context* context) :
Component(context),
collisionConfiguration_(0),
collisionDispatcher_(0),
broadphase_(0),
solver_(0),
world_(0),
fps_(DEFAULT_FPS),
maxSubSteps_(0),
timeAcc_(0.0f),
maxNetworkAngularVelocity_(DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY),
interpolation_(true),
internalEdge_(true),
applyingTransforms_(false),
debugRenderer_(0),
debugMode_(btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawConstraints | btIDebugDraw::DBG_DrawConstraintLimits)
{
gContactAddedCallback = CustomMaterialCombinerCallback;
//===============================================
// collisionConfiguration_ = new btDefaultCollisionConfiguration();
#ifndef TEST_SOFTBODY
collisionConfiguration_ = new btDefaultCollisionConfiguration();
#else
collisionConfiguration_ = new btSoftBodyRigidBodyCollisionConfiguration();
#endif
//===============================================
collisionDispatcher_ = new btCollisionDispatcher(collisionConfiguration_);
broadphase_ = new btDbvtBroadphase();
solver_ = new btSequentialImpulseConstraintSolver();
//===============================================
// world_ = new btDiscreteDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_);
#ifndef TEST_SOFTBODY
world_ = new btDiscreteDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_);
#else
world_ = new btSoftRigidDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_, NULL);
#endif
//===============================================
world_->setGravity(ToBtVector3(DEFAULT_GRAVITY));
world_->getDispatchInfo().m_useContinuous = true;
world_->getSolverInfo().m_splitImpulse = false; // Disable by default for performance
world_->setDebugDrawer(this);
world_->setInternalTickCallback(InternalPreTickCallback, static_cast<void*>(this), true);
world_->setInternalTickCallback(InternalTickCallback, static_cast<void*>(this), false);
//==========================================
#ifdef TEST_SOFTBODY
m_softBodyWorldInfo = new btSoftBodyWorldInfo();
m_softBodyWorldInfo->m_dispatcher = collisionDispatcher_;
m_softBodyWorldInfo->m_broadphase = broadphase_;
m_softBodyWorldInfo->air_density = (btScalar)1.2;
m_softBodyWorldInfo->water_density = 0;
m_softBodyWorldInfo->water_offset = 0;
m_softBodyWorldInfo->water_normal = btVector3(0, 0, 0);
m_softBodyWorldInfo->m_gravity.setValue(0, -10, 0);
m_softBodyWorldInfo->m_sparsesdf.Initialize();
softBodies_.Clear();
#endif
//===========================================
}
PhysicsWorld::~PhysicsWorld()
{
if (scene_)
{
// Force all remaining constraints, rigid bodies and collision shapes to release themselves
for (PODVector<Constraint*>::Iterator i = constraints_.Begin(); i != constraints_.End(); ++i)
(*i)->ReleaseConstraint();
for (PODVector<RigidBody*>::Iterator i = rigidBodies_.Begin(); i != rigidBodies_.End(); ++i)
(*i)->ReleaseBody();
for (PODVector<CollisionShape*>::Iterator i = collisionShapes_.Begin(); i != collisionShapes_.End(); ++i)
(*i)->ReleaseShape();
//====================================
for (PODVector<SoftBody*>::Iterator i = softBodies_.Begin(); i != softBodies_.End(); ++i)
(*i)->ReleaseBody();
//====================================
}
delete world_;
world_ = 0;
delete solver_;
solver_ = 0;
delete broadphase_;
broadphase_ = 0;
delete collisionDispatcher_;
collisionDispatcher_ = 0;
delete collisionConfiguration_;
collisionConfiguration_ = 0;
//=================================
if (m_softBodyWorldInfo)
{
m_softBodyWorldInfo->m_dispatcher = 0;
m_softBodyWorldInfo->m_broadphase = 0;
delete m_softBodyWorldInfo;
m_softBodyWorldInfo = 0;
}
//=================================
}
void PhysicsWorld::RegisterObject(Context* context)
{
context->RegisterFactory(SUBSYSTEM_CATEGORY);
MIXED_ACCESSOR_ATTRIBUTE("Gravity", GetGravity, SetGravity, Vector3, DEFAULT_GRAVITY, AM_DEFAULT);
ATTRIBUTE("Physics FPS", int, fps_, DEFAULT_FPS, AM_DEFAULT);
ATTRIBUTE("Max Substeps", int, maxSubSteps_, 0, AM_DEFAULT);
ACCESSOR_ATTRIBUTE("Solver Iterations", GetNumIterations, SetNumIterations, int, 10, AM_DEFAULT);
ATTRIBUTE("Net Max Angular Vel.", float, maxNetworkAngularVelocity_, DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY, AM_DEFAULT);
ATTRIBUTE("Interpolation", bool, interpolation_, true, AM_FILE);
ATTRIBUTE("Internal Edge Utility", bool, internalEdge_, true, AM_DEFAULT);
ACCESSOR_ATTRIBUTE("Split Impulse", GetSplitImpulse, SetSplitImpulse, bool, false, AM_DEFAULT);
}
bool PhysicsWorld::isVisible(const btVector3& aabbMin, const btVector3& aabbMax)
{
if (debugRenderer_)
return debugRenderer_->IsInside(BoundingBox(ToVector3(aabbMin), ToVector3(aabbMax)));
else
return false;
}
void PhysicsWorld::drawLine(const btVector3& from, const btVector3& to, const btVector3& color)
{
if (debugRenderer_)
debugRenderer_->AddLine(ToVector3(from), ToVector3(to), Color(color.x(), color.y(), color.z()), debugDepthTest_);
}
void PhysicsWorld::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
{
if (debug)
{
PROFILE(PhysicsDrawDebug);
debugRenderer_ = debug;
debugDepthTest_ = depthTest;
world_->debugDrawWorld();
debugRenderer_ = 0;
}
}
void PhysicsWorld::reportErrorWarning(const char* warningString)
{
LOGWARNING("Physics: " + String(warningString));
}
void PhysicsWorld::drawContactPoint(const btVector3& pointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime,
const btVector3& color)
{
}
void PhysicsWorld::draw3dText(const btVector3& location, const char* textString)
{
}
void PhysicsWorld::Update(float timeStep)
{
PROFILE(UpdatePhysics);
float internalTimeStep = 1.0f / fps_;
int maxSubSteps = (int)(timeStep * fps_) + 1;
if (maxSubSteps_ < 0)
{
internalTimeStep = timeStep;
maxSubSteps = 1;
}
else if (maxSubSteps_ > 0)
maxSubSteps = Min(maxSubSteps, maxSubSteps_);
delayedWorldTransforms_.Clear();
if (interpolation_)
world_->stepSimulation(timeStep, maxSubSteps, internalTimeStep);
else
{
timeAcc_ += timeStep;
while (timeAcc_ >= internalTimeStep && maxSubSteps > 0)
{
world_->stepSimulation(internalTimeStep, 0, internalTimeStep);
timeAcc_ -= internalTimeStep;
--maxSubSteps;
}
}
// Apply delayed (parented) world transforms now
while (!delayedWorldTransforms_.Empty())
{
for (HashMap<RigidBody*, DelayedWorldTransform>::Iterator i = delayedWorldTransforms_.Begin();
i != delayedWorldTransforms_.End(); ++i)
{
const DelayedWorldTransform& transform = i->second_;
// If parent's transform has already been assigned, can proceed
if (!delayedWorldTransforms_.Contains(transform.parentRigidBody_))
{
transform.rigidBody_->ApplyWorldTransform(transform.worldPosition_, transform.worldRotation_);
delayedWorldTransforms_.Erase(i);
}
}
}
}
void PhysicsWorld::UpdateCollisions()
{
world_->performDiscreteCollisionDetection();
}
void PhysicsWorld::SetFps(int fps)
{
fps_ = (unsigned)Clamp(fps, 1, 1000);
MarkNetworkUpdate();
}
void PhysicsWorld::SetGravity(const Vector3& gravity)
{
world_->setGravity(ToBtVector3(gravity));
MarkNetworkUpdate();
}
void PhysicsWorld::SetMaxSubSteps(int num)
{
maxSubSteps_ = num;
MarkNetworkUpdate();
}
void PhysicsWorld::SetNumIterations(int num)
{
num = Clamp(num, 1, MAX_SOLVER_ITERATIONS);
world_->getSolverInfo().m_numIterations = num;
MarkNetworkUpdate();
}
void PhysicsWorld::SetInterpolation(bool enable)
{
interpolation_ = enable;
}
void PhysicsWorld::SetInternalEdge(bool enable)
{
internalEdge_ = enable;
MarkNetworkUpdate();
}
void PhysicsWorld::SetSplitImpulse(bool enable)
{
world_->getSolverInfo().m_splitImpulse = enable;
MarkNetworkUpdate();
}
void PhysicsWorld::SetMaxNetworkAngularVelocity(float velocity)
{
maxNetworkAngularVelocity_ = Clamp(velocity, 1.0f, 32767.0f);
MarkNetworkUpdate();
}
void PhysicsWorld::Raycast(PODVector& result, const Ray& ray, float maxDistance, unsigned collisionMask)
{
PROFILE(PhysicsRaycast);
if (maxDistance >= M_INFINITY)
LOGWARNING("Infinite maxDistance in physics raycast is not supported");
btCollisionWorld::AllHitsRayResultCallback
rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_));
rayCallback.m_collisionFilterGroup = (short)0xffff;
rayCallback.m_collisionFilterMask = (short)collisionMask;
world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);
for (int i = 0; i < rayCallback.m_collisionObjects.size(); ++i)
{
PhysicsRaycastResult newResult;
newResult.body_ = static_cast<RigidBody*>(rayCallback.m_collisionObjects[i]->getUserPointer());
newResult.position_ = ToVector3(rayCallback.m_hitPointWorld[i]);
newResult.normal_ = ToVector3(rayCallback.m_hitNormalWorld[i]);
newResult.distance_ = (newResult.position_ - ray.origin_).Length();
result.Push(newResult);
}
Sort(result.Begin(), result.End(), CompareRaycastResults);
}
void PhysicsWorld::RaycastSingle(PhysicsRaycastResult& result, const Ray& ray, float maxDistance, unsigned collisionMask)
{
PROFILE(PhysicsRaycastSingle);
if (maxDistance >= M_INFINITY)
LOGWARNING("Infinite maxDistance in physics raycast is not supported");
btCollisionWorld::ClosestRayResultCallback
rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_));
rayCallback.m_collisionFilterGroup = (short)0xffff;
rayCallback.m_collisionFilterMask = (short)collisionMask;
world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);
if (rayCallback.hasHit())
{
result.position_ = ToVector3(rayCallback.m_hitPointWorld);
result.normal_ = ToVector3(rayCallback.m_hitNormalWorld);
result.distance_ = (result.position_ - ray.origin_).Length();
result.body_ = static_cast<RigidBody*>(rayCallback.m_collisionObject->getUserPointer());
}
else
{
result.position_ = Vector3::ZERO;
result.normal_ = Vector3::ZERO;
result.distance_ = M_INFINITY;
result.body_ = 0;
}
}
void PhysicsWorld::SphereCast(PhysicsRaycastResult& result, const Ray& ray, float radius, float maxDistance, unsigned collisionMask)
{
PROFILE(PhysicsSphereCast);
if (maxDistance >= M_INFINITY)
LOGWARNING("Infinite maxDistance in physics sphere cast is not supported");
btSphereShape shape(radius);
btCollisionWorld::ClosestConvexResultCallback
convexCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_));
convexCallback.m_collisionFilterGroup = (short)0xffff;
convexCallback.m_collisionFilterMask = (short)collisionMask;
world_->convexSweepTest(&shape, btTransform(btQuaternion::getIdentity(), convexCallback.m_convexFromWorld),
btTransform(btQuaternion::getIdentity(), convexCallback.m_convexToWorld), convexCallback);
if (convexCallback.hasHit())
{
result.body_ = static_cast<RigidBody*>(convexCallback.m_hitCollisionObject->getUserPointer());
result.position_ = ToVector3(convexCallback.m_hitPointWorld);
result.normal_ = ToVector3(convexCallback.m_hitNormalWorld);
result.distance_ = (result.position_ - ray.origin_).Length();
}
else
{
result.body_ = 0;
result.position_ = Vector3::ZERO;
result.normal_ = Vector3::ZERO;
result.distance_ = M_INFINITY;
}
}
void PhysicsWorld::ConvexCast(PhysicsRaycastResult& result, CollisionShape* shape, const Vector3& startPos,
const Quaternion& startRot, const Vector3& endPos, const Quaternion& endRot, unsigned collisionMask)
{
if (!shape || !shape->GetCollisionShape())
{
LOGERROR(“Null collision shape for convex cast”);
result.body_ = 0;
result.position_ = Vector3::ZERO;
result.normal_ = Vector3::ZERO;
result.distance_ = M_INFINITY;
return;
}
// If shape is attached in a rigidbody, set its collision group temporarily to 0 to make sure it is not returned in the sweep result
RigidBody* bodyComp = shape->GetComponent<RigidBody>();
btRigidBody* body = bodyComp ? bodyComp->GetBody() : (btRigidBody*)0;
btBroadphaseProxy* proxy = body ? body->getBroadphaseProxy() : (btBroadphaseProxy*)0;
short group = 0;
if (proxy)
{
group = proxy->m_collisionFilterGroup;
proxy->m_collisionFilterGroup = 0;
}
// Take the shape's offset position & rotation into account
Node* shapeNode = shape->GetNode();
Matrix3x4 startTransform(startPos, startRot, shapeNode ? shapeNode->GetWorldScale() : Vector3::ONE);
Matrix3x4 endTransform(endPos, endRot, shapeNode ? shapeNode->GetWorldScale() : Vector3::ONE);
Vector3 effectiveStartPos = startTransform * shape->GetPosition();
Vector3 effectiveEndPos = endTransform * shape->GetPosition();
Quaternion effectiveStartRot = startRot * shape->GetRotation();
Quaternion effectiveEndRot = endRot * shape->GetRotation();
ConvexCast(result, shape->GetCollisionShape(), effectiveStartPos, effectiveStartRot, effectiveEndPos, effectiveEndRot, collisionMask);
// Restore the collision group
if (proxy)
proxy->m_collisionFilterGroup = group;
}
void PhysicsWorld::ConvexCast(PhysicsRaycastResult& result, btCollisionShape* shape, const Vector3& startPos,
const Quaternion& startRot, const Vector3& endPos, const Quaternion& endRot, unsigned collisionMask)
{
if (!shape)
{
LOGERROR(“Null collision shape for convex cast”);
result.body_ = 0;
result.position_ = Vector3::ZERO;
result.normal_ = Vector3::ZERO;
result.distance_ = M_INFINITY;
return;
}
if (!shape->isConvex())
{
LOGERROR("Can not use non-convex collision shape for convex cast");
result.body_ = 0;
result.position_ = Vector3::ZERO;
result.normal_ = Vector3::ZERO;
result.distance_ = M_INFINITY;
return;
}
PROFILE(PhysicsConvexCast);
btCollisionWorld::ClosestConvexResultCallback convexCallback(ToBtVector3(startPos), ToBtVector3(endPos));
convexCallback.m_collisionFilterGroup = (short)0xffff;
convexCallback.m_collisionFilterMask = (short)collisionMask;
world_->convexSweepTest(static_cast<btConvexShape*>(shape), btTransform(ToBtQuaternion(startRot),
convexCallback.m_convexFromWorld), btTransform(ToBtQuaternion(endRot), convexCallback.m_convexToWorld),
convexCallback);
if (convexCallback.hasHit())
{
result.body_ = static_cast<RigidBody*>(convexCallback.m_hitCollisionObject->getUserPointer());
result.position_ = ToVector3(convexCallback.m_hitPointWorld);
result.normal_ = ToVector3(convexCallback.m_hitNormalWorld);
result.distance_ = (result.position_ - startPos).Length();
}
else
{
result.body_ = 0;
result.position_ = Vector3::ZERO;
result.normal_ = Vector3::ZERO;
result.distance_ = M_INFINITY;
}
}
void PhysicsWorld::RemoveCachedGeometry(Model* model)
{
for (HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator i = triMeshCache_.Begin();
i != triMeshCache_.End()
{
HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator current = i++;
if (current->first_.first_ == model)
triMeshCache_.Erase(current);
}
for (HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator i = convexCache_.Begin();
i != convexCache_.End()
{
HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator current = i++;
if (current->first_.first_ == model)
convexCache_.Erase(current);
}
}
void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const Sphere& sphere, unsigned collisionMask)
{
PROFILE(PhysicsSphereQuery);
result.Clear();
btSphereShape sphereShape(sphere.radius_);
btRigidBody* tempRigidBody = new btRigidBody(1.0f, 0, &sphereShape);
tempRigidBody->setWorldTransform(btTransform(btQuaternion::getIdentity(), ToBtVector3(sphere.center_)));
// Need to activate the temporary rigid body to get reliable results from static, sleeping objects
tempRigidBody->activate();
world_->addRigidBody(tempRigidBody);
PhysicsQueryCallback callback(result, collisionMask);
world_->contactTest(tempRigidBody, callback);
world_->removeRigidBody(tempRigidBody);
delete tempRigidBody;
}
void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const BoundingBox& box, unsigned collisionMask)
{
PROFILE(PhysicsBoxQuery);
result.Clear();
btBoxShape boxShape(ToBtVector3(box.HalfSize()));
btRigidBody* tempRigidBody = new btRigidBody(1.0f, 0, &boxShape);
tempRigidBody->setWorldTransform(btTransform(btQuaternion::getIdentity(), ToBtVector3(box.Center())));
tempRigidBody->activate();
world_->addRigidBody(tempRigidBody);
PhysicsQueryCallback callback(result, collisionMask);
world_->contactTest(tempRigidBody, callback);
world_->removeRigidBody(tempRigidBody);
delete tempRigidBody;
}
void PhysicsWorld::GetRigidBodies(PODVector<RigidBody*>& result, const RigidBody* body)
{
PROFILE(GetCollidingBodies);
result.Clear();
for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
i != currentCollisions_.End(); ++i)
{
if (i->first_.first_ == body)
result.Push(i->first_.second_);
else if (i->first_.second_ == body)
result.Push(i->first_.first_);
}
}
Vector3 PhysicsWorld::GetGravity() const
{
return ToVector3(world_->getGravity());
}
int PhysicsWorld::GetNumIterations() const
{
return world_->getSolverInfo().m_numIterations;
}
bool PhysicsWorld::GetSplitImpulse() const
{
return world_->getSolverInfo().m_splitImpulse != 0;
}
void PhysicsWorld::AddRigidBody(RigidBody* body)
{
rigidBodies_.Push(body);
}
void PhysicsWorld::RemoveRigidBody(RigidBody* body)
{
rigidBodies_.Remove(body);
// Remove possible dangling pointer from the delayedWorldTransforms structure
delayedWorldTransforms_.Erase(body);
}
//===================================
void PhysicsWorld::AddSoftBody(SoftBody* body)
{
softBodies_.Push(body);
}
void PhysicsWorld::RemoveSoftBody(SoftBody* body)
{
softBodies_.Remove(body);
}
//===================================
void PhysicsWorld::AddCollisionShape(CollisionShape* shape)
{
collisionShapes_.Push(shape);
}
void PhysicsWorld::RemoveCollisionShape(CollisionShape* shape)
{
collisionShapes_.Remove(shape);
}
void PhysicsWorld::AddConstraint(Constraint* constraint)
{
constraints_.Push(constraint);
}
void PhysicsWorld::RemoveConstraint(Constraint* constraint)
{
constraints_.Remove(constraint);
}
void PhysicsWorld::AddDelayedWorldTransform(const DelayedWorldTransform& transform)
{
delayedWorldTransforms_[transform.rigidBody_] = transform;
}
void PhysicsWorld::DrawDebugGeometry(bool depthTest)
{
DebugRenderer* debug = GetComponent();
DrawDebugGeometry(debug, depthTest);
}
void PhysicsWorld::SetDebugRenderer(DebugRenderer* debug)
{
debugRenderer_ = debug;
}
void PhysicsWorld::SetDebugDepthTest(bool enable)
{
debugDepthTest_ = enable;
}
void PhysicsWorld::CleanupGeometryCache()
{
// Remove cached shapes whose only reference is the cache itself
for (HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator i = triMeshCache_.Begin();
i != triMeshCache_.End()
{
HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator current = i++;
if (current->second_.Refs() == 1)
triMeshCache_.Erase(current);
}
for (HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator i = convexCache_.Begin();
i != convexCache_.End()
{
HashMap<Pair<Model*, unsigned>, SharedPtr >::Iterator current = i++;
if (current->second_.Refs() == 1)
convexCache_.Erase(current);
}
}
void PhysicsWorld::OnSceneSet(Scene* scene)
{
// Subscribe to the scene subsystem update, which will trigger the physics simulation step
if (scene)
{
scene_ = GetScene();
SubscribeToEvent(scene_, E_SCENESUBSYSTEMUPDATE, HANDLER(PhysicsWorld, HandleSceneSubsystemUpdate));
}
else
UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
}
void PhysicsWorld::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
{
using namespace SceneSubsystemUpdate;
Update(eventData[P_TIMESTEP].GetFloat());
}
void PhysicsWorld::PreStep(float timeStep)
{
// Send pre-step event
using namespace PhysicsPreStep;
VariantMap& eventData = GetEventDataMap();
eventData[P_WORLD] = this;
eventData[P_TIMESTEP] = timeStep;
SendEvent(E_PHYSICSPRESTEP, eventData);
// Start profiling block for the actual simulation step
#ifdef URHO3D_PROFILING
Profiler* profiler = GetSubsystem();
if (profiler)
profiler->BeginBlock(“StepSimulation”);
#endif
}
void PhysicsWorld::PostStep(float timeStep)
{
#ifdef URHO3D_PROFILING
Profiler* profiler = GetSubsystem();
if (profiler)
profiler->EndBlock();
#endif
SendCollisionEvents();
// Send post-step event
using namespace PhysicsPostStep;
VariantMap& eventData = GetEventDataMap();
eventData[P_WORLD] = this;
eventData[P_TIMESTEP] = timeStep;
SendEvent(E_PHYSICSPOSTSTEP, eventData);
}
void PhysicsWorld::SendCollisionEvents()
{
PROFILE(SendCollisionEvents);
currentCollisions_.Clear();
physicsCollisionData_.Clear();
nodeCollisionData_.Clear();
int numManifolds = collisionDispatcher_->getNumManifolds();
if (numManifolds)
{
physicsCollisionData_[PhysicsCollision::P_WORLD] = this;
for (int i = 0; i < numManifolds; ++i)
{
btPersistentManifold* contactManifold = collisionDispatcher_->getManifoldByIndexInternal(i);
// First check that there are actual contacts, as the manifold exists also when objects are close but not touching
if (!contactManifold->getNumContacts())
continue;
const btCollisionObject* objectA = contactManifold->getBody0();
const btCollisionObject* objectB = contactManifold->getBody1();
RigidBody* bodyA = static_cast<RigidBody*>(objectA->getUserPointer());
RigidBody* bodyB = static_cast<RigidBody*>(objectB->getUserPointer());
// If it's not a rigidbody, maybe a ghost object
if (!bodyA || !bodyB)
continue;
// Skip collision event signaling if both objects are static, or if collision event mode does not match
if (bodyA->GetMass() == 0.0f && bodyB->GetMass() == 0.0f)
continue;
if (bodyA->GetCollisionEventMode() == COLLISION_NEVER || bodyB->GetCollisionEventMode() == COLLISION_NEVER)
continue;
if (bodyA->GetCollisionEventMode() == COLLISION_ACTIVE && bodyB->GetCollisionEventMode() == COLLISION_ACTIVE &&
!bodyA->IsActive() && !bodyB->IsActive())
continue;
WeakPtr<RigidBody> bodyWeakA(bodyA);
WeakPtr<RigidBody> bodyWeakB(bodyB);
Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> > bodyPair;
if (bodyA < bodyB)
bodyPair = MakePair(bodyWeakA, bodyWeakB);
else
bodyPair = MakePair(bodyWeakB, bodyWeakA);
// First only store the collision pair as weak pointers and the manifold pointer, so user code can safely destroy
// objects during collision event handling
currentCollisions_[bodyPair] = contactManifold;
}
for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator i = currentCollisions_.Begin();
i != currentCollisions_.End(); ++i)
{
RigidBody* bodyA = i->first_.first_;
RigidBody* bodyB = i->first_.second_;
if (!bodyA || !bodyB)
continue;
btPersistentManifold* contactManifold = i->second_;
Node* nodeA = bodyA->GetNode();
Node* nodeB = bodyB->GetNode();
WeakPtr<Node> nodeWeakA(nodeA);
WeakPtr<Node> nodeWeakB(nodeB);
bool trigger = bodyA->IsTrigger() || bodyB->IsTrigger();
bool newCollision = !previousCollisions_.Contains(i->first_);
physicsCollisionData_[PhysicsCollision::P_NODEA] = nodeA;
physicsCollisionData_[PhysicsCollision::P_NODEB] = nodeB;
physicsCollisionData_[PhysicsCollision::P_BODYA] = bodyA;
physicsCollisionData_[PhysicsCollision::P_BODYB] = bodyB;
physicsCollisionData_[PhysicsCollision::P_TRIGGER] = trigger;
contacts_.Clear();
for (int j = 0; j < contactManifold->getNumContacts(); ++j)
{
btManifoldPoint& point = contactManifold->getContactPoint(j);
contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
contacts_.WriteVector3(ToVector3(point.m_normalWorldOnB));
contacts_.WriteFloat(point.m_distance1);
contacts_.WriteFloat(point.m_appliedImpulse);
}
physicsCollisionData_[PhysicsCollision::P_CONTACTS] = contacts_.GetBuffer();
// Send separate collision start event if collision is new
if (newCollision)
{
SendEvent(E_PHYSICSCOLLISIONSTART, physicsCollisionData_);
// Skip rest of processing if either of the nodes or bodies is removed as a response to the event
if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
continue;
}
// Then send the ongoing collision event
SendEvent(E_PHYSICSCOLLISION, physicsCollisionData_);
if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
continue;
nodeCollisionData_[NodeCollision::P_BODY] = bodyA;
nodeCollisionData_[NodeCollision::P_OTHERNODE] = nodeB;
nodeCollisionData_[NodeCollision::P_OTHERBODY] = bodyB;
nodeCollisionData_[NodeCollision::P_TRIGGER] = trigger;
nodeCollisionData_[NodeCollision::P_CONTACTS] = contacts_.GetBuffer();
if (newCollision)
{
nodeA->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData_);
if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
continue;
}
nodeA->SendEvent(E_NODECOLLISION, nodeCollisionData_);
if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
continue;
contacts_.Clear();
for (int j = 0; j < contactManifold->getNumContacts(); ++j)
{
btManifoldPoint& point = contactManifold->getContactPoint(j);
contacts_.WriteVector3(ToVector3(point.m_positionWorldOnB));
contacts_.WriteVector3(-ToVector3(point.m_normalWorldOnB));
contacts_.WriteFloat(point.m_distance1);
contacts_.WriteFloat(point.m_appliedImpulse);
}
nodeCollisionData_[NodeCollision::P_BODY] = bodyB;
nodeCollisionData_[NodeCollision::P_OTHERNODE] = nodeA;
nodeCollisionData_[NodeCollision::P_OTHERBODY] = bodyA;
nodeCollisionData_[NodeCollision::P_CONTACTS] = contacts_.GetBuffer();
if (newCollision)
{
nodeB->SendEvent(E_NODECOLLISIONSTART, nodeCollisionData_);
if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
continue;
}
nodeB->SendEvent(E_NODECOLLISION, nodeCollisionData_);
}
}
// Send collision end events as applicable
{
physicsCollisionData_[PhysicsCollisionEnd::P_WORLD] = this;
for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, btPersistentManifold*>::Iterator
i = previousCollisions_.Begin(); i != previousCollisions_.End(); ++i)
{
if (!currentCollisions_.Contains(i->first_))
{
RigidBody* bodyA = i->first_.first_;
RigidBody* bodyB = i->first_.second_;
if (!bodyA || !bodyB)
continue;
bool trigger = bodyA->IsTrigger() || bodyB->IsTrigger();
// Skip collision event signaling if both objects are static, or if collision event mode does not match
if (bodyA->GetMass() == 0.0f && bodyB->GetMass() == 0.0f)
continue;
if (bodyA->GetCollisionEventMode() == COLLISION_NEVER || bodyB->GetCollisionEventMode() == COLLISION_NEVER)
continue;
if (bodyA->GetCollisionEventMode() == COLLISION_ACTIVE && bodyB->GetCollisionEventMode() == COLLISION_ACTIVE &&
!bodyA->IsActive() && !bodyB->IsActive())
continue;
Node* nodeA = bodyA->GetNode();
Node* nodeB = bodyB->GetNode();
WeakPtr<Node> nodeWeakA(nodeA);
WeakPtr<Node> nodeWeakB(nodeB);
physicsCollisionData_[PhysicsCollisionEnd::P_BODYA] = bodyA;
physicsCollisionData_[PhysicsCollisionEnd::P_BODYB] = bodyB;
physicsCollisionData_[PhysicsCollisionEnd::P_NODEA] = nodeA;
physicsCollisionData_[PhysicsCollisionEnd::P_NODEB] = nodeB;
physicsCollisionData_[PhysicsCollisionEnd::P_TRIGGER] = trigger;
SendEvent(E_PHYSICSCOLLISIONEND, physicsCollisionData_);
// Skip rest of processing if either of the nodes or bodies is removed as a response to the event
if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
continue;
nodeCollisionData_[NodeCollisionEnd::P_BODY] = bodyA;
nodeCollisionData_[NodeCollisionEnd::P_OTHERNODE] = nodeB;
nodeCollisionData_[NodeCollisionEnd::P_OTHERBODY] = bodyB;
nodeCollisionData_[NodeCollisionEnd::P_TRIGGER] = trigger;
nodeA->SendEvent(E_NODECOLLISIONEND, nodeCollisionData_);
if (!nodeWeakA || !nodeWeakB || !i->first_.first_ || !i->first_.second_)
continue;
nodeCollisionData_[NodeCollisionEnd::P_BODY] = bodyB;
nodeCollisionData_[NodeCollisionEnd::P_OTHERNODE] = nodeA;
nodeCollisionData_[NodeCollisionEnd::P_OTHERBODY] = bodyA;
nodeB->SendEvent(E_NODECOLLISIONEND, nodeCollisionData_);
}
}
}
previousCollisions_ = currentCollisions_;
}
void RegisterPhysicsLibrary(Context* context)
{
CollisionShape::RegisterObject(context);
RigidBody::RegisterObject(context);
Constraint::RegisterObject(context);
PhysicsWorld::RegisterObject(context);
SoftBody::RegisterObject(context); // Register softbody ============================
}
}
[/code][/spoiler]