std::vector<CSolidObject*> CQuadField::GetSolidsExact( const float3& pos, const float radius, const unsigned int physicalStateBits, const unsigned int collisionStateBits ) { GML_RECMUTEX_LOCK(qnum); // GetSolidsExact const std::vector<int>& quads = GetQuads(pos, radius); const int tempNum = gs->tempNum++; std::vector<CSolidObject*> solids; std::vector<int>::const_iterator qi; std::list<CUnit*>::iterator ui; std::list<CFeature*>::iterator fi; for (qi = quads.begin(); qi != quads.end(); ++qi) { for (ui = baseQuads[*qi].units.begin(); ui != baseQuads[*qi].units.end(); ++ui) { CUnit* u = *ui; if (u->tempNum == tempNum) continue; if (!u->HasPhysicalStateBit(physicalStateBits)) continue; if (!u->HasCollidableStateBit(collisionStateBits)) continue; if ((pos - u->midPos).SqLength() >= Square(radius + u->radius)) continue; u->tempNum = tempNum; solids.push_back(u); } for (fi = baseQuads[*qi].features.begin(); fi != baseQuads[*qi].features.end(); ++fi) { CFeature* f = *fi; if (f->tempNum == tempNum) continue; if (!f->HasPhysicalStateBit(physicalStateBits)) continue; if (!f->HasCollidableStateBit(collisionStateBits)) continue; if ((pos - f->midPos).SqLength() >= Square(radius + f->radius)) continue; f->tempNum = tempNum; solids.push_back(f); } } return solids; }
float GuiTraceRay( const float3& start, const float3& dir, const float length, const CUnit* exclude, CUnit*& hitUnit, CFeature*& hitFeature, bool useRadar, bool groundOnly, bool ignoreWater ) { hitUnit = NULL; hitFeature = NULL; if (dir == ZeroVector) return -1.0f; // ground and water-plane intersection const float guiRayLength = length; const float groundRayLength = ground->LineGroundCol(start, start + dir * guiRayLength, false); const float waterRayLength = math::floorf(math::fabs(start.y / std::min(dir.y, -0.00001f))); float minRayLength = groundRayLength; float minIngressDist = length; float minEgressDist = length; bool hitFactory = false; // if ray cares about water, take minimum // of distance to ground and water surface if (!ignoreWater) minRayLength = std::min(groundRayLength, waterRayLength); if (groundOnly) return minRayLength; GML_RECMUTEX_LOCK(quad); // GuiTraceRay int* begQuad = NULL; int* endQuad = NULL; quadField->GetQuadsOnRay(start, dir, length, begQuad, endQuad); std::list<CUnit*>::const_iterator ui; std::list<CFeature*>::const_iterator fi; CollisionQuery cq; for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) { const CQuadField::Quad& quad = quadField->GetQuad(*quadPtr); // Unit Intersection for (ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* unit = *ui; const bool unitIsEnemy = !teamHandler->Ally(unit->allyteam, gu->myAllyTeam); const bool unitOnRadar = (useRadar && radarHandler->InRadar(unit, gu->myAllyTeam)); const bool unitInSight = (unit->losStatus[gu->myAllyTeam] & (LOS_INLOS | LOS_CONTRADAR)); const bool unitVisible = !unitIsEnemy || unitOnRadar || unitInSight || gu->spectatingFullView; if (unit == exclude) continue; // test this bit only in synced traces, rely on noSelect here if (false && !unit->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; if (unit->noSelect) continue; if (!unitVisible) continue; CollisionVolume cv(unit->collisionVolume); if (unit->isIcon || (!unitInSight && unitOnRadar && unitIsEnemy)) { // for iconified units, just pretend the collision // volume is a sphere of radius <unit->IconRadius> // (count radar blips as such too) cv.InitSphere(unit->iconRadius); } if (CCollisionHandler::MouseHit(unit, start, start + dir * guiRayLength, &cv, &cq)) { // get the distance to the ray-volume ingress point // (not likely to generate inside-hit special cases) const float ingressDist = cq.GetIngressPosDist(start, dir); const float egressDist = cq.GetEgressPosDist(start, dir); const bool factoryUnderCursor = unit->unitDef->IsFactoryUnit(); const bool factoryHitBeforeUnit = ((hitFactory && ingressDist < minIngressDist) || (!hitFactory && egressDist < minIngressDist)); const bool unitHitInsideFactory = ((hitFactory && ingressDist < minEgressDist) || (!hitFactory && ingressDist < minIngressDist)); // give units in a factory higher priority than the factory itself if (hitUnit == NULL || (factoryUnderCursor && factoryHitBeforeUnit) || (!factoryUnderCursor && unitHitInsideFactory)) { hitFactory = factoryUnderCursor; minIngressDist = ingressDist; minEgressDist = egressDist; hitUnit = unit; hitFeature = NULL; } } } // Feature Intersection for (fi = quad.features.begin(); fi != quad.features.end(); ++fi) { CFeature* f = *fi; if (!gu->spectatingFullView && !f->IsInLosForAllyTeam(gu->myAllyTeam)) continue; // test this bit only in synced traces, rely on noSelect here if (false && !f->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; if (f->noSelect) continue; if (CCollisionHandler::DetectHit(f, start, start + dir * guiRayLength, &cq, true)) { const float hitDist = cq.GetHitPosDist(start, dir); const bool factoryHitBeforeUnit = ( hitFactory && hitDist < minEgressDist); const bool unitHitInsideFactory = (!hitFactory && hitDist < minIngressDist); // we want the closest feature (intersection point) on the ray // give features in a factory (?) higher priority than the factory itself if (hitUnit == NULL || factoryHitBeforeUnit || unitHitInsideFactory) { hitFactory = false; minIngressDist = hitDist; hitFeature = f; hitUnit = NULL; } } } } if ((minRayLength > 0.0f) && ((minRayLength + 200.0f) < minIngressDist)) { minIngressDist = minRayLength; hitUnit = NULL; hitFeature = NULL; } return minIngressDist; }
// called by {CRifle, CBeamLaser, CLightningCannon}::Fire(), CWeapon::HaveFreeLineOfFire(), and Skirmish AIs float TraceRay( const float3& start, const float3& dir, float length, int avoidFlags, const CUnit* owner, CUnit*& hitUnit, CFeature*& hitFeature, CollisionQuery* hitColQuery ) { const bool ignoreEnemies = ((avoidFlags & Collision::NOENEMIES ) != 0); const bool ignoreAllies = ((avoidFlags & Collision::NOFRIENDLIES) != 0); const bool ignoreFeatures = ((avoidFlags & Collision::NOFEATURES ) != 0); const bool ignoreNeutrals = ((avoidFlags & Collision::NONEUTRALS ) != 0); const bool ignoreGround = ((avoidFlags & Collision::NOGROUND ) != 0); const bool ignoreUnits = ignoreEnemies && ignoreAllies && ignoreNeutrals; hitFeature = NULL; hitUnit = NULL; if (dir == ZeroVector) return -1.0f; if (!ignoreFeatures || !ignoreUnits) { GML_RECMUTEX_LOCK(quad); // TraceRay CollisionQuery cq; int* begQuad = NULL; int* endQuad = NULL; quadField->GetQuadsOnRay(start, dir, length, begQuad, endQuad); // locally point somewhere non-NULL; we cannot pass hitColQuery // to DetectHit directly because each call resets it internally if (hitColQuery == NULL) hitColQuery = &cq; // feature intersection if (!ignoreFeatures) { for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) { const CQuadField::Quad& quad = quadField->GetQuad(*quadPtr); for (std::list<CFeature*>::const_iterator ui = quad.features.begin(); ui != quad.features.end(); ++ui) { CFeature* f = *ui; // NOTE: // if f is non-blocking, ProjectileHandler will not test // for collisions with projectiles so we can skip it here if (!f->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; if (CCollisionHandler::DetectHit(f, start, start + dir * length, &cq, true)) { const float len = cq.GetHitPosDist(start, dir); // we want the closest feature (intersection point) on the ray if (len < length) { length = len; hitFeature = f; *hitColQuery = cq; } } } } } // unit intersection if (!ignoreUnits) { for (int* quadPtr = begQuad; quadPtr != endQuad; ++quadPtr) { const CQuadField::Quad& quad = quadField->GetQuad(*quadPtr); for (std::list<CUnit*>::const_iterator ui = quad.units.begin(); ui != quad.units.end(); ++ui) { CUnit* u = *ui; if (u == owner) continue; if (!u->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; if (ignoreAllies && u->allyteam == owner->allyteam) continue; if (ignoreNeutrals && u->IsNeutral()) continue; if (ignoreEnemies && u->allyteam != owner->allyteam) continue; if (CCollisionHandler::DetectHit(u, start, start + dir * length, &cq, true)) { const float len = cq.GetHitPosDist(start, dir); // we want the closest unit (intersection point) on the ray if (len < length) { length = len; hitUnit = u; *hitColQuery = cq; } } } } if (hitUnit) hitFeature = NULL; } } if (!ignoreGround) { // ground intersection const float groundLength = ground->LineGroundCol(start, start + dir * length); if (length > groundLength && groundLength > 0.0f) { length = groundLength; hitUnit = NULL; hitFeature = NULL; } } return length; }