void Gravity::ForceUpdate(RigidBody & body, REAL dt) { if (body.HasInfiniteMass()) return; body.AddForce(gravity*body.GetMass()); }
void PhysicsWorld::SendCollisionEvents() { ATOMIC_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); // First only store the collision pair as weak pointers and the manifold pointer, so user code can safely destroy // objects during collision event handling Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> > bodyPair; if (bodyA < bodyB) { bodyPair = MakePair(bodyWeakA, bodyWeakB); currentCollisions_[bodyPair].manifold_ = contactManifold; } else { bodyPair = MakePair(bodyWeakB, bodyWeakA); currentCollisions_[bodyPair].flippedManifold_ = contactManifold; } } for (HashMap<Pair<WeakPtr<RigidBody>, WeakPtr<RigidBody> >, ManifoldPair>::Iterator i = currentCollisions_.Begin(); i != currentCollisions_.End(); ++i) { RigidBody* bodyA = i->first_.first_; RigidBody* bodyB = i->first_.second_; if (!bodyA || !bodyB) continue; 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(); // "Pointers not flipped"-manifold, send unmodified normals btPersistentManifold* contactManifold = i->second_.manifold_; if (contactManifold) { 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); } } // "Pointers flipped"-manifold, flip normals also contactManifold = i->second_.flippedManifold_; if (contactManifold) { 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; // Flip perspective to body B contacts_.Clear(); contactManifold = i->second_.manifold_; if (contactManifold) { 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); } } contactManifold = i->second_.flippedManifold_; if (contactManifold) { 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> >, ManifoldPair>::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 CreateRagdoll::HandleNodeCollision(StringHash eventType, VariantMap& eventData) { using namespace NodeCollision; // Get the other colliding body, make sure it is moving (has nonzero mass) RigidBody* otherBody = static_cast<RigidBody*>(eventData[P_OTHERBODY].GetPtr()); if (otherBody->GetMass() > 0.0f) { // We do not need the physics components in the AnimatedModel's root scene node anymore node_->RemoveComponent<RigidBody>(); node_->RemoveComponent<CollisionShape>(); // Create RigidBody & CollisionShape components to bones CreateRagdollBone("Bip01_Pelvis", SHAPE_BOX, Vector3(0.3f, 0.2f, 0.25f), Vector3(0.0f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 0.0f)); CreateRagdollBone("Bip01_Spine1", SHAPE_BOX, Vector3(0.35f, 0.2f, 0.3f), Vector3(0.15f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 0.0f)); CreateRagdollBone("Bip01_L_Thigh", SHAPE_CAPSULE, Vector3(0.175f, 0.45f, 0.175f), Vector3(0.25f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_R_Thigh", SHAPE_CAPSULE, Vector3(0.175f, 0.45f, 0.175f), Vector3(0.25f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_L_Calf", SHAPE_CAPSULE, Vector3(0.15f, 0.55f, 0.15f), Vector3(0.25f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_R_Calf", SHAPE_CAPSULE, Vector3(0.15f, 0.55f, 0.15f), Vector3(0.25f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_Head", SHAPE_BOX, Vector3(0.2f, 0.2f, 0.2f), Vector3(0.1f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 0.0f)); CreateRagdollBone("Bip01_L_UpperArm", SHAPE_CAPSULE, Vector3(0.15f, 0.35f, 0.15f), Vector3(0.1f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_R_UpperArm", SHAPE_CAPSULE, Vector3(0.15f, 0.35f, 0.15f), Vector3(0.1f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_L_Forearm", SHAPE_CAPSULE, Vector3(0.125f, 0.4f, 0.125f), Vector3(0.2f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); CreateRagdollBone("Bip01_R_Forearm", SHAPE_CAPSULE, Vector3(0.125f, 0.4f, 0.125f), Vector3(0.2f, 0.0f, 0.0f), Quaternion(0.0f, 0.0f, 90.0f)); // Create Constraints between bones CreateRagdollConstraint("Bip01_L_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3::BACK, Vector3::FORWARD, Vector2(45.0f, 45.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_R_Thigh", "Bip01_Pelvis", CONSTRAINT_CONETWIST, Vector3::BACK, Vector3::FORWARD, Vector2(45.0f, 45.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_L_Calf", "Bip01_L_Thigh", CONSTRAINT_HINGE, Vector3::BACK, Vector3::BACK, Vector2(90.0f, 0.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_R_Calf", "Bip01_R_Thigh", CONSTRAINT_HINGE, Vector3::BACK, Vector3::BACK, Vector2(90.0f, 0.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_Spine1", "Bip01_Pelvis", CONSTRAINT_HINGE, Vector3::FORWARD, Vector3::FORWARD, Vector2(45.0f, 0.0f), Vector2(-10.0f, 0.0f)); CreateRagdollConstraint("Bip01_Head", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3::LEFT, Vector3::LEFT, Vector2(0.0f, 30.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_L_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3::DOWN, Vector3::UP, Vector2(45.0f, 45.0f), Vector2::ZERO, false); CreateRagdollConstraint("Bip01_R_UpperArm", "Bip01_Spine1", CONSTRAINT_CONETWIST, Vector3::DOWN, Vector3::UP, Vector2(45.0f, 45.0f), Vector2::ZERO, false); CreateRagdollConstraint("Bip01_L_Forearm", "Bip01_L_UpperArm", CONSTRAINT_HINGE, Vector3::BACK, Vector3::BACK, Vector2(90.0f, 0.0f), Vector2::ZERO); CreateRagdollConstraint("Bip01_R_Forearm", "Bip01_R_UpperArm", CONSTRAINT_HINGE, Vector3::BACK, Vector3::BACK, Vector2(90.0f, 0.0f), Vector2::ZERO); // Disable keyframe animation from all bones so that they will not interfere with the ragdoll AnimatedModel* model = GetComponent<AnimatedModel>(); Skeleton& skeleton = model->GetSkeleton(); for (unsigned i = 0; i < skeleton.GetNumBones(); ++i) skeleton.GetBone(i)->animated_ = false; // Finally remove self from the scene node. Note that this must be the last operation performed in the function Remove(); } }