void Physics::ResolveCollision(Shape& A, Shape& B, Collision& C) { //Calculate relative velocity Vec2 rvel = B.vel - A.vel; //Project relative velocity onto normal double nvel = rvel * C.N; //If nvel is positive, the objects are moving away from each other if (nvel > 0.0) return; //Separate the objects (displacement proportional to mass) double A_invmass = A.invmass; double B_invmass = B.invmass; Vec2 displacement = 0.05 * C.penetration / (A_invmass + B_invmass) * C.N; A.Displace(-A_invmass * displacement); B.Displace(B_invmass * displacement); //Choose the lower restitution (probably not the best idea..) double e = min(A.restitution, B.restitution); //Calculate impulse scalar double j = -(1 + e) * nvel / (A.invmass + B.invmass); //Apply impulse Impulse(A, -j, C.N); Impulse(B, j, C.N); }
//------------------------------------------------------------------------ bool CMelee::PerformRayTest(const Vec3 &pos, const Vec3 &dir, float strength, bool remote) { IEntity *pOwner = m_pWeapon->GetOwner(); IPhysicalEntity *pIgnore = pOwner ? pOwner->GetPhysics() : 0; ray_hit hit; int n = gEnv->pPhysicalWorld->RayWorldIntersection(pos, dir.normalized() * m_pShared->meleeparams.range, ent_all | ent_water, rwi_stop_at_pierceable | rwi_ignore_back_faces, &hit, 1, &pIgnore, pIgnore ? 1 : 0); //===================OffHand melee (also in PerformCylincerTest)=================== if(m_ignoredEntity && (n > 0)) { if(IEntity *pHeldObject = gEnv->pEntitySystem->GetEntity(m_ignoredEntity)) { IPhysicalEntity *pHeldObjectPhysics = pHeldObject->GetPhysics(); if(pHeldObjectPhysics == hit.pCollider) { return false; } } } //================================================================================= if (n > 0) { Hit(&hit, dir, strength, remote); Impulse(hit.pt, dir, hit.n, hit.pCollider, hit.partid, hit.ipart, hit.surface_idx, strength); } return n > 0; }
// Compute the second friction constraint impulse inline const Impulse ContactSolver::computeFriction2Impulse(decimal deltaLambda, const ContactPointSolver& contactPoint) const { return Impulse(-contactPoint.frictionVector2 * deltaLambda, -contactPoint.r1CrossT2 * deltaLambda, contactPoint.frictionVector2 * deltaLambda, contactPoint.r2CrossT2 * deltaLambda); }
void CMelee::ApplyMeleeDamage(const Vec3& point, const Vec3& dir, const Vec3& normal, IPhysicalEntity* physicalEntity, EntityId entityID, int partId, int ipart, int surfaceIdx, bool remote, int iPrim) { const float blend = m_slideKick ? 0.4f : SATURATE(m_pMeleeParams->meleeparams.impulse_up_percentage); Vec3 impulseDir = Vec3(0, 0, 1) * blend + dir * (1.0f - blend); impulseDir.normalize(); int hitTypeID = Hit(point, dir, normal, physicalEntity, entityID, partId, ipart, surfaceIdx, remote); IActor* pActor = gEnv->bMultiplayer && entityID ? g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(entityID) : NULL; if(!gEnv->bMultiplayer || !pActor) //MP handles melee impulses to actors in GameRulesClientServer.cpp. See: ClApplyActorMeleeImpulse) { Impulse(point, impulseDir, normal, physicalEntity, entityID, partId, ipart, surfaceIdx, hitTypeID, iPrim); } }
//------------------------------------------------------------------------ bool CMelee::PerformCylinderTest(const Vec3 &pos, const Vec3 &dir, float strength, bool remote) { IEntity *pOwner = m_pWeapon->GetOwner(); IPhysicalEntity *pIgnore = pOwner ? pOwner->GetPhysics() : 0; IEntity *pHeldObject = NULL; if(m_ignoredEntity) { pHeldObject = gEnv->pEntitySystem->GetEntity(m_ignoredEntity); } primitives::cylinder cyl; cyl.r = 0.25f; cyl.axis = dir; cyl.hh = m_pShared->meleeparams.range / 2.0f; cyl.center = pos + dir.normalized() * cyl.hh; float n = 0.0f; geom_contact *contacts; intersection_params params; WriteLockCond lockContacts; params.bStopAtFirstTri = false; params.bNoBorder = true; params.bNoAreaContacts = true; n = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(primitives::cylinder::type, &cyl, Vec3(ZERO), ent_rigid | ent_sleeping_rigid | ent_independent | ent_static | ent_terrain | ent_water, &contacts, 0, geom_colltype0 | geom_colltype_foliage | geom_colltype_player, ¶ms, 0, 0, &pIgnore, pIgnore ? 1 : 0, lockContacts); int ret = (int)n; float closestdSq = 9999.0f; geom_contact *closestc = 0; geom_contact *currentc = contacts; for (int i = 0; i < ret; i++) { geom_contact *contact = currentc; if (contact) { IPhysicalEntity *pCollider = gEnv->pPhysicalWorld->GetPhysicalEntityById(contact->iPrim[0]); if (pCollider) { IEntity *pEntity = gEnv->pEntitySystem->GetEntityFromPhysics(pCollider); if (pEntity) { if ((pEntity == pOwner) || (pHeldObject && (pEntity == pHeldObject))) { ++currentc; continue; } } float distSq = (pos - currentc->pt).len2(); if (distSq < closestdSq) { closestdSq = distSq; closestc = contact; } } } ++currentc; } if (closestc) { IPhysicalEntity *pCollider = gEnv->pPhysicalWorld->GetPhysicalEntityById(closestc->iPrim[0]); Hit(closestc, dir, strength, remote); Impulse(closestc->pt, dir, closestc->n, pCollider, closestc->iPrim[1], 0, closestc->id[1], strength); } return closestc != 0; }
// Compute a penetration constraint impulse inline const Impulse ContactSolver::computePenetrationImpulse(decimal deltaLambda, const ContactPointSolver& contactPoint) const { return Impulse(-contactPoint.normal * deltaLambda, -contactPoint.r1CrossN * deltaLambda, contactPoint.normal * deltaLambda, contactPoint.r2CrossN * deltaLambda); }