void CGameHelper::DoExplosionDamage(CFeature* feature, const float3& expPos, float expRad, CUnit* owner, const DamageArray& damages) { CollisionVolume* cv = feature->collisionVolume; if (cv) { float3 dif = (feature->midPos + cv->GetOffsets()) - expPos; float expDist = std::max(dif.Length(), 0.1f); float expMod = (expRad - expDist) / expRad; // always do some damage with explosive stuff // (DDM wreckage etc. is too big to normally // be damaged otherwise, even by BB shells) // NOTE: this will also be only approximate // for non-spherical volumes if ((expRad > 8.0f) && (expDist < (cv->GetBoundingRadius() * 1.1f)) && (expMod < 0.1f)) { expMod = 0.1f; } if (expMod > 0.0f) { feature->DoDamage(damages * expMod, owner, dif * (damages.impulseFactor * expMod / expDist * (damages[0] + damages.impulseBoost))); } } }
void PhysicsSystem::NarrowPhaseCollisions() { for (int i = 0; i < allNodes.size(); i++) { PhysicsNode& first = *allNodes[i]; CollisionVolume* fv = first.GetCollisionVolume(); if (!fv) continue; for (int j = i + 1; j < allNodes.size(); j++) { PhysicsNode& second = *allNodes[j]; CollisionVolume* sv = second.GetCollisionVolume(); if (!sv) continue; switch(fv->GetType()) { case COLLISION_VOL_SPHERE: switch(sv->GetType()) { case COLLISION_VOL_SPHERE: CollisionData data; if (CollisionHelper::SphereSphereCollision(first, second, &data)) { CollisionHelper::AddCollisionImpulse(first, second, data); } continue; } case COLLISION_VOL_PLANE: switch(sv->GetType()) { case COLLISION_VOL_SPHERE: CollisionData data; if (CollisionHelper::PlaneSphereCollision(first, second, &data)) { CollisionHelper::AddCollisionImpulse(first, second, data); } continue; } } } } }
CollisionVolume* TreeBuilder::recConstructTree(Model* const model, BoundingBox* const box, int level) { if(level == 0) { return 0; } else { CollisionVolume* rootVolume = box; if(this->type == OCT) { for(int i = 0; i < 8; ++i) { BoxQuadrent loc = (BoxQuadrent)(TOP_FRONT_LEFT + i); PartialBoundingBox* subQuad = buildSubQuadrent(model, box->getLocalCorners(), loc); //Visualizer::showSphere(subQuad->getCenter(), subQuad->getRadius()); if(triangleInSphere(model, subQuad)) { //BoundingSphere* octSphere = boxToSphere(model, subQuad); rootVolume->setChild(subQuad); subQuad->setChild(recConstructTree(model, subQuad, level-1)); } } } else if(this->type == QUAD) { } return (CollisionVolume*)rootVolume->getChild(); } }
bool CWeapon::AdjustTargetVectorLength( CUnit* targetUnit, float3& targetPos, float3& targetVec, float3& targetDir) const { bool retCode = false; const float tbScale = math::fabsf(targetBorder); CollisionVolume* cvOld = targetUnit->collisionVolume; CollisionVolume cvNew = CollisionVolume(targetUnit->collisionVolume); CollisionQuery cq; // test for "collision" with a temporarily volume // (scaled uniformly by the absolute target-border // factor) cvNew.RescaleAxes(tbScale, tbScale, tbScale); cvNew.SetTestType(CollisionVolume::COLVOL_HITTEST_DISC); targetUnit->collisionVolume = &cvNew; if (CCollisionHandler::DetectHit(targetUnit, weaponMuzzlePos, ZeroVector, NULL)) { // our weapon muzzle is inside the target unit's volume; this // means we do not need to make any adjustments to targetVec targetVec = ZeroVector; } else { targetDir.SafeNormalize(); // otherwise, perform a raytrace to find the proper length correction // factor for non-spherical coldet volumes based on the ray's ingress // (for positive TB values) or egress (for negative TB values) position; // this either increases or decreases the length of <targetVec> but does // not change its direction cvNew.SetTestType(CollisionVolume::COLVOL_HITTEST_CONT); // make the ray-segment long enough so it can reach the far side of the // scaled collision volume (helps to ensure a ray-intersection is found) // // note: ray-intersection is NOT guaranteed if the volume itself has a // non-zero offset, since here we are "shooting" at the target UNIT's // midpoint const float3 targetOffset = targetDir * (cvNew.GetBoundingRadius() * 2.0f); const float3 targetRayPos = targetPos + targetOffset; if (CCollisionHandler::DetectHit(targetUnit, weaponMuzzlePos, targetRayPos, &cq)) { if (targetBorder > 0.0f) { targetVec -= (targetDir * ((targetPos - cq.p0).Length())); } if (targetBorder < 0.0f) { targetVec += (targetDir * ((cq.p1 - targetPos).Length())); } } retCode = true; } targetUnit->collisionVolume = cvOld; // true indicates we took the else-branch and targetDir is now normalized return retCode; }
float CGameHelper::GuiTraceRayFeature(const float3& start, const float3& dir, float length, CFeature*& feature) { float nearHit = length; GML_RECMUTEX_LOCK(quad); // GuiTraceRayFeature std::vector<int> quads = qf->GetQuadsOnRay(start, dir, length); std::vector<int>::iterator qi; for (qi = quads.begin(); qi != quads.end(); ++qi) { const CQuadField::Quad& quad = qf->GetQuad(*qi); std::list<CFeature*>::const_iterator ui; // NOTE: switch this to custom volumes fully? // (not used for any LOF checks, maybe wasteful) for (ui = quad.features.begin(); ui != quad.features.end(); ++ui) { CFeature* f = *ui; if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam)) { continue; } if (f->noSelect) { continue; } CollisionVolume* cv = f->collisionVolume; const float3& midPosOffset = cv? cv->GetOffsets(): ZeroVector; const float3 dif = (f->midPos + midPosOffset) - start; float closeLength = dif.dot(dir); if (closeLength < 0) continue; if (closeLength > nearHit) continue; float3 closeVect = dif - dir * closeLength; if (closeVect.SqLength() < f->sqRadius) { nearHit = closeLength; feature = f; } } } return nearHit; }
static void GetRectangleCollisionVolume(const SRectangle& r, CollisionVolume& v, float3& rm) { float3 vScales; // rectangle dimensions (WS) vScales.x = WS(r.x2 - r.x1); vScales.z = WS(r.z2 - r.z1); vScales.y = 1.0f; // rectangle mid-point (WS) rm.x = WS(r.x1 + r.x2) >> 1; rm.z = WS(r.z1 + r.z2) >> 1; rm.y = 0.0f; #define CV CollisionVolume v.Init(vScales, ZeroVector, CV::COLVOL_TYPE_BOX, CV::COLVOL_HITTEST_CONT, CV::COLVOL_AXIS_Y); #undef CV }