// // Copyright (c) 2008-2014 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 "CollisionShape.h" #include "Constraint.h" #include "Context.h" #include "DebugRenderer.h" #include "Log.h" #include "Model.h" #include "Mutex.h" #include "PhysicsEvents.h" #include "PhysicsUtils.h" #include "PhysicsWorld.h" #include "Profiler.h" #include "Ray.h" #include "RigidBody.h" #include "Scene.h" #include "SceneEvents.h" #include "Sort.h" #include #include #include #include #include #include #include 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(world->getWorldUserInfo())->PreStep(timeStep); } void InternalTickCallback(btDynamicsWorld *world, btScalar timeStep) { static_cast(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& 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(colObj0Wrap->getCollisionObject()->getUserPointer()); if (body && !result_.Contains(body) && (body->GetCollisionLayer() & collisionMask_)) result_.Push(body); body = reinterpret_cast(colObj1Wrap->getCollisionObject()->getUserPointer()); if (body && !result_.Contains(body) && (body->GetCollisionLayer() & collisionMask_)) result_.Push(body); return 0.0f; } /// Found rigid bodies. PODVector& 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), 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(); collisionDispatcher_ = new btCollisionDispatcher(collisionConfiguration_); broadphase_ = new btDbvtBroadphase(); solver_ = new btSequentialImpulseConstraintSolver(); world_ = new btDiscreteDynamicsWorld(collisionDispatcher_, broadphase_, solver_, collisionConfiguration_); 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(this), true); world_->setInternalTickCallback(InternalTickCallback, static_cast(this), false); } PhysicsWorld::~PhysicsWorld() { if (scene_) { // Force all remaining constraints, rigid bodies and collision shapes to release themselves for (PODVector::Iterator i = constraints_.Begin(); i != constraints_.End(); ++i) (*i)->ReleaseConstraint(); for (PODVector::Iterator i = rigidBodies_.Begin(); i != rigidBodies_.End(); ++i) (*i)->ReleaseBody(); for (PODVector::Iterator i = collisionShapes_.Begin(); i != collisionShapes_.End(); ++i) (*i)->ReleaseShape(); } delete world_; world_ = 0; delete solver_; solver_ = 0; delete broadphase_; broadphase_ = 0; delete collisionDispatcher_; collisionDispatcher_ = 0; delete collisionConfiguration_; collisionConfiguration_ = 0; } void PhysicsWorld::RegisterObject(Context* context) { context->RegisterFactory(SUBSYSTEM_CATEGORY); ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_VECTOR3, "Gravity", GetGravity, SetGravity, Vector3, DEFAULT_GRAVITY, AM_DEFAULT); ATTRIBUTE(PhysicsWorld, VAR_INT, "Physics FPS", fps_, DEFAULT_FPS, AM_DEFAULT); ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_INT, "Solver Iterations", GetNumIterations, SetNumIterations, int, 10, AM_DEFAULT); ATTRIBUTE(PhysicsWorld, VAR_FLOAT, "Net Max Angular Vel.", maxNetworkAngularVelocity_, DEFAULT_MAX_NETWORK_ANGULAR_VELOCITY, AM_DEFAULT); ATTRIBUTE(PhysicsWorld, VAR_BOOL, "Interpolation", interpolation_, true, AM_FILE); ATTRIBUTE(PhysicsWorld, VAR_BOOL, "Internal Edge Utility", internalEdge_, true, AM_DEFAULT); ACCESSOR_ATTRIBUTE(PhysicsWorld, VAR_BOOL, "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::Update(float timeStep) { PROFILE(UpdatePhysics); float internalTimeStep = 1.0f / fps_; delayedWorldTransforms_.Clear(); if (interpolation_) { int maxSubSteps = (int)(timeStep * fps_) + 1; world_->stepSimulation(timeStep, maxSubSteps, internalTimeStep); } else { timeAcc_ += timeStep; while (timeAcc_ >= internalTimeStep) { world_->stepSimulation(internalTimeStep, 0, internalTimeStep); timeAcc_ -= internalTimeStep; } } // Apply delayed (parented) world transforms now while (!delayedWorldTransforms_.Empty()) { for (HashMap::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_ = Clamp(fps, 1, 1000); MarkNetworkUpdate(); } void PhysicsWorld::SetGravity(Vector3 gravity) { world_->setGravity(ToBtVector3(gravity)); 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); btCollisionWorld::AllHitsRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_)); rayCallback.m_collisionFilterGroup = (short)0xffff; rayCallback.m_collisionFilterMask = 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(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); btCollisionWorld::ClosestRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_)); rayCallback.m_collisionFilterGroup = (short)0xffff; rayCallback.m_collisionFilterMask = 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(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); btSphereShape shape(radius); btCollisionWorld::ClosestConvexResultCallback convexCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_)); convexCallback.m_collisionFilterGroup = (short)0xffff; convexCallback.m_collisionFilterMask = collisionMask; world_->convexSweepTest(&shape, btTransform(btQuaternion::getIdentity(), convexCallback.m_convexFromWorld), btTransform(btQuaternion::getIdentity(), convexCallback.m_convexToWorld), convexCallback); if (convexCallback.hasHit()) { result.body_ = static_cast(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::RemoveCachedGeometry(Model* model) { for (HashMap, SharedPtr >::Iterator i = triMeshCache_.Begin(); i != triMeshCache_.End();) { HashMap, SharedPtr >::Iterator current = i++; if (current->first_.first_ == model) triMeshCache_.Erase(current); } for (HashMap, SharedPtr >::Iterator i = convexCache_.Begin(); i != convexCache_.End();) { HashMap, SharedPtr >::Iterator current = i++; if (current->first_.first_ == model) convexCache_.Erase(current); } } void PhysicsWorld::GetRigidBodies(PODVector& 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& 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& result, const RigidBody* body) { PROFILE(GetCollidingBodies); result.Clear(); for (HashMap, WeakPtr >, 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); } 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, SharedPtr >::Iterator i = triMeshCache_.Begin(); i != triMeshCache_.End();) { HashMap, SharedPtr >::Iterator current = i++; if (current->second_.Refs() == 1) triMeshCache_.Erase(current); } for (HashMap, SharedPtr >::Iterator i = convexCache_.Begin(); i != convexCache_.End();) { HashMap, SharedPtr >::Iterator current = i++; if (current->second_.Refs() == 1) convexCache_.Erase(current); } } void PhysicsWorld::OnNodeSet(Node* node) { // Subscribe to the scene subsystem update, which will trigger the physics simulation step if (node) { scene_ = GetScene(); SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(PhysicsWorld, HandleSceneSubsystemUpdate)); } } 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 PhysicsPreStep; 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); int numContacts = contactManifold->getNumContacts(); // First check that there are actual contacts, as the manifold exists also when objects are close but not touching if (!numContacts) continue; const btCollisionObject* objectA = contactManifold->getBody0(); const btCollisionObject* objectB = contactManifold->getBody1(); RigidBody* bodyA = static_cast(objectA->getUserPointer()); RigidBody* bodyB = static_cast(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 bodyWeakA(bodyA); WeakPtr bodyWeakB(bodyB); Pair, WeakPtr > 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, WeakPtr >, 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_; int numContacts = contactManifold->getNumContacts(); Node* nodeA = bodyA->GetNode(); Node* nodeB = bodyB->GetNode(); WeakPtr nodeWeakA(nodeA); WeakPtr 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 < numContacts; ++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 < numContacts; ++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, WeakPtr >, 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 nodeWeakA(nodeA); WeakPtr 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); } }