void UEnvQueryGenerator_ActorsOfClass::GenerateItems(FEnvQueryInstance& QueryInstance) const { float RadiusValue = 0.0f; float DensityValue = 0.0f; UWorld* World = GEngine->GetWorldFromContextObject(QueryInstance.Owner.Get()); // @TODO add some logging here if (World == NULL || QueryInstance.GetParamValue(Radius, RadiusValue, TEXT("Radius")) == false || SearchedActorClass == NULL) { return; } const float RadiusSq = FMath::Square(RadiusValue); TArray<FVector> ContextLocations; QueryInstance.PrepareContext(SearchCenter, ContextLocations); for (TActorIterator<AActor> ItActor = TActorIterator<AActor>(World, SearchedActorClass); ItActor; ++ItActor) { for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ++ContextIndex) { if (FVector::DistSquared(ContextLocations[ContextIndex], ItActor->GetActorLocation()) < RadiusSq) { QueryInstance.AddItemData<UEnvQueryItemType_Actor>(*ItActor); break; } } } }
float UEnvQueryGenerator_Donut::GetArcBisectorAngle(FEnvQueryInstance& QueryInstance) const { float BisectAngle = 0.0f; FVector Direction; if (bDefineArc) { if (ArcDirection.DirMode == EEnvDirection::TwoPoints) { TArray<FVector> Start; TArray<FVector> End; QueryInstance.PrepareContext(ArcDirection.LineFrom, Start); QueryInstance.PrepareContext(ArcDirection.LineTo, End); if (Start.Num() > 0 && End.Num() > 0) { const FVector LineDir = (End[0] - Start[0]).GetSafeNormal(); const FRotator LineRot = LineDir.Rotation(); BisectAngle = LineRot.Yaw; } } else { TArray<FRotator> Rot; QueryInstance.PrepareContext(ArcDirection.Rotation, Rot); if (Rot.Num() > 0) { BisectAngle = Rot[0].Yaw; } } } return BisectAngle; }
void UEnvQueryTest_Dot::GatherLineDirections(TArray<FVector>& Directions, FEnvQueryInstance& QueryInstance, const FVector& ItemLocation, TSubclassOf<UEnvQueryContext> LineFrom, TSubclassOf<UEnvQueryContext> LineTo) const { TArray<FVector> ContextLocationFrom; if (IsContextPerItem(LineFrom)) { ContextLocationFrom.Add(ItemLocation); } else { QueryInstance.PrepareContext(LineFrom, ContextLocationFrom); } for (int32 FromIndex = 0; FromIndex < ContextLocationFrom.Num(); FromIndex++) { TArray<FVector> ContextLocationTo; if (IsContextPerItem(LineTo)) { ContextLocationTo.Add(ItemLocation); } else { QueryInstance.PrepareContext(LineTo, ContextLocationTo); } for (int32 ToIndex = 0; ToIndex < ContextLocationTo.Num(); ToIndex++) { const FVector Dir = (ContextLocationTo[ToIndex] - ContextLocationFrom[FromIndex]).GetSafeNormal(); Directions.Add(Dir); } } }
void UEnvQueryGenerator_OnCircle::GenerateItems(FEnvQueryInstance& QueryInstance) const { float AngleDegree = 360.f; float RadiusValue = 0.f; float ItemSpace = 1.0f; if (QueryInstance.GetParamValue(Angle, AngleDegree, TEXT("Angle")) == false || QueryInstance.GetParamValue(Radius, RadiusValue, TEXT("Radius")) == false || QueryInstance.GetParamValue(ItemSpacing, ItemSpace, TEXT("ItemSpacing")) == false || AngleDegree <= 0.f || AngleDegree > 360.f || RadiusValue <= 0.f) { return; } AngleRadians = FMath::DegreesToRadians(Angle.Value); // first generate points on a circle const float CircumferenceLength = 2.f * PI * RadiusValue; const float ArcAnglePercentage = Angle.Value / 360.f; const float ArcLength = CircumferenceLength * ArcAnglePercentage; const int32 StepsCount = FMath::CeilToInt(ArcLength / ItemSpace) + 1; const float AngleStep = AngleDegree / (StepsCount - 1); FVector StartDirection = CalcDirection(QueryInstance); TArray<FVector> CenterLocationCandidates; QueryInstance.PrepareContext(CircleCenter, CenterLocationCandidates); StartDirection = StartDirection.RotateAngleAxis(-AngleDegree/2, FVector::UpVector) * RadiusValue; int NumCenterLocations = CenterLocationCandidates.Num(); if (NumCenterLocations > 0) { for (int i = 0; i < NumCenterLocations; ++i) { GenerateItemsForCircle(CenterLocationCandidates[i], StartDirection, StepsCount, AngleStep, QueryInstance); } } else { FVector CenterLocation(0); AActor* Querier = Cast<AActor>(QueryInstance.Owner.Get()); if (Querier) { CenterLocation = Querier->GetActorLocation(); } GenerateItemsForCircle(CenterLocation, StartDirection, StepsCount, AngleStep, QueryInstance); } }
FVector UEnvQueryGenerator_OnCircle::CalcDirection(FEnvQueryInstance& QueryInstance) const { AActor* Querier = Cast<AActor>(QueryInstance.Owner.Get()); check(Querier != NULL); FVector Direction; if (bDefineArc) { // By default, use Querier rotation for arc direction. Direction = Querier->GetActorForwardVector(); if (ArcDirection.DirMode == EEnvDirection::TwoPoints) { TArray<FVector> Start; TArray<FVector> End; QueryInstance.PrepareContext(ArcDirection.LineFrom, Start); QueryInstance.PrepareContext(ArcDirection.LineTo, End); if (Start.Num() > 0 && End.Num() > 0) { Direction = (End[0] - Start[0]).GetSafeNormal(); } else { UE_VLOG(Querier, LogEQS, Warning, TEXT("UEnvQueryGenerator_OnCircle::CalcDirection failed to calc direction in %s. Using querier facing."), *QueryInstance.QueryName); } } else { TArray<FRotator> Rot; QueryInstance.PrepareContext(ArcDirection.Rotation, Rot); if (Rot.Num() > 0) { Direction = Rot[0].Vector(); } else { UE_VLOG(Querier, LogEQS, Warning, TEXT("UEnvQueryGenerator_OnCircle::CalcDirection failed to calc direction in %s. Using querier facing."), *QueryInstance.QueryName); } } } else { // Don't rotate based on querier! Instead, use a stable rotation so the points on the circle don't rotate! Direction = FVector(1, 0, 0); } return Direction; }
void UEnvQueryGenerator_ActorsOfClass::GenerateItems(FEnvQueryInstance& QueryInstance) const { SearchRadius.BindData(QueryInstance.Owner.Get(), QueryInstance.QueryID); float RadiusValue = SearchRadius.GetValue(); UWorld* World = GEngine->GetWorldFromContextObject(QueryInstance.Owner.Get()); if (World == NULL || SearchedActorClass == NULL) { return; } const float RadiusSq = FMath::Square(RadiusValue); TArray<FVector> ContextLocations; QueryInstance.PrepareContext(SearchCenter, ContextLocations); for (TActorIterator<AActor> ItActor = TActorIterator<AActor>(World, SearchedActorClass); ItActor; ++ItActor) { for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ++ContextIndex) { if (FVector::DistSquared(ContextLocations[ContextIndex], ItActor->GetActorLocation()) < RadiusSq) { QueryInstance.AddItemData<UEnvQueryItemType_Actor>(*ItActor); break; } } } }
void UEnvQueryGenerator_SimpleGrid::GenerateItems(FEnvQueryInstance& QueryInstance) const { UObject* BindOwner = QueryInstance.Owner.Get(); GridSize.BindData(BindOwner, QueryInstance.QueryID); SpaceBetween.BindData(BindOwner, QueryInstance.QueryID); float RadiusValue = GridSize.GetValue(); float DensityValue = SpaceBetween.GetValue(); const int32 ItemCount = FPlatformMath::TruncToInt((RadiusValue * 2.0f / DensityValue) + 1); const int32 ItemCountHalf = ItemCount / 2; TArray<FVector> ContextLocations; QueryInstance.PrepareContext(GenerateAround, ContextLocations); TArray<FNavLocation> GridPoints; GridPoints.Reserve(ItemCount * ItemCount * ContextLocations.Num()); for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { for (int32 IndexX = 0; IndexX <= ItemCount; ++IndexX) { for (int32 IndexY = 0; IndexY <= ItemCount; ++IndexY) { const FNavLocation TestPoint = FNavLocation(ContextLocations[ContextIndex] - FVector(DensityValue * (IndexX - ItemCountHalf), DensityValue * (IndexY - ItemCountHalf), 0)); GridPoints.Add(TestPoint); } } } ProjectAndFilterNavPoints(GridPoints, QueryInstance); StoreNavPoints(GridPoints, QueryInstance); }
void UEnvQueryGenerator_BlueprintBase::GenerateItems(FEnvQueryInstance& QueryInstance) const { TArray<FVector> ContextLocations; QueryInstance.PrepareContext(Context, ContextLocations); CachedQueryInstance = &QueryInstance; const_cast<UEnvQueryGenerator_BlueprintBase*>(this)->DoItemGeneration(ContextLocations); CachedQueryInstance = NULL; }
FVector UEnvQueryGenerator_OnCircle::CalcDirection(FEnvQueryInstance& QueryInstance) const { AActor* Querier = Cast<AActor>(QueryInstance.Owner.Get()); FVector Direction = Querier->GetActorRotation().Vector(); if (bDefineArc) { if (ArcDirection.DirMode == EEnvDirection::TwoPoints) { TArray<FVector> Start; TArray<FVector> End; QueryInstance.PrepareContext(ArcDirection.LineFrom, Start); QueryInstance.PrepareContext(ArcDirection.LineTo, End); if (Start.Num() > 0 && End.Num() > 0) { Direction = (End[0] - Start[0]).SafeNormal(); } else { UE_VLOG(Querier, LogEQS, Warning, TEXT("UEnvQueryGenerator_OnCircle::CalcDirection failed to calc direction in %s. Using querier facing."), *QueryInstance.QueryName); } } else { TArray<FRotator> Rot; QueryInstance.PrepareContext(ArcDirection.Rotation, Rot); if (Rot.Num() > 0) { Direction = Rot[0].Vector(); } else { UE_VLOG(Querier, LogEQS, Warning, TEXT("UEnvQueryGenerator_OnCircle::CalcDirection failed to calc direction in %s. Using querier facing."), *QueryInstance.QueryName); } } } return Direction; }
void UEnvQueryGenerator_ProjectedPoints::StoreNavPoints(const TArray<FNavLocation>& Points, FEnvQueryInstance& QueryInstance) const { const int32 InitialElementsCount = QueryInstance.Items.Num(); QueryInstance.ReserveItemData(InitialElementsCount + Points.Num()); for (int32 Idx = 0; Idx < Points.Num(); Idx++) { // store using default function to handle creating new data entry QueryInstance.AddItemData<UEnvQueryItemType_Point>(Points[Idx]); } FEnvQueryOptionInstance& OptionInstance = QueryInstance.Options[QueryInstance.OptionIndex]; OptionInstance.bHasNavLocations = (ProjectionData.TraceMode == EEnvQueryTrace::Navigation); }
void UEnvQueryGenerator_OnCircle::GenerateItems(FEnvQueryInstance& QueryInstance) const { CircleRadius.BindData(QueryInstance.Owner.Get(), QueryInstance.QueryID); SpaceBetween.BindData(QueryInstance.Owner.Get(), QueryInstance.QueryID); ArcAngle.BindData(QueryInstance.Owner.Get(), QueryInstance.QueryID); float AngleDegree = ArcAngle.GetValue(); float RadiusValue = CircleRadius.GetValue(); float ItemSpace = SpaceBetween.GetValue(); if ((AngleDegree <= 0.f) || (AngleDegree > 360.f) || (RadiusValue <= 0.f) || (ItemSpace <= 0.f)) { return; } AngleRadians = FMath::DegreesToRadians(AngleDegree); // first generate points on a circle const float CircumferenceLength = 2.f * PI * RadiusValue; const float ArcAnglePercentage = AngleDegree / 360.f; const float ArcLength = CircumferenceLength * ArcAnglePercentage; const int32 StepsCount = FMath::CeilToInt(ArcLength / ItemSpace) + 1; const float AngleStep = AngleDegree / (StepsCount - 1); FVector StartDirection = CalcDirection(QueryInstance); StartDirection = StartDirection.RotateAngleAxis(-AngleDegree/2, FVector::UpVector) * RadiusValue; // gather context with raw data, so it can be used by derived generators FEnvQueryContextData ContextData; const bool bSuccess = QueryInstance.PrepareContext(CircleCenter, ContextData); if (bSuccess && ContextData.ValueType && ContextData.ValueType->IsChildOf(UEnvQueryItemType_VectorBase::StaticClass())) { UEnvQueryItemType_VectorBase* DefTypeOb = (UEnvQueryItemType_VectorBase*)ContextData.ValueType->GetDefaultObject(); const uint16 DefTypeValueSize = DefTypeOb->GetValueSize(); uint8* RawData = ContextData.RawData.GetData(); for (int32 ValueIndex = 0; ValueIndex < ContextData.NumValues; ValueIndex++) { const FVector ContextItemLocation = DefTypeOb->GetItemLocation(RawData); GenerateItemsForCircle(RawData, DefTypeOb, ContextItemLocation, StartDirection, StepsCount, AngleStep, QueryInstance); RawData += DefTypeValueSize; } } }
void UEnvQueryTest_Dot::GatherLineDirections(TArray<FVector>& Directions, FEnvQueryInstance& QueryInstance, const FRotator& ItemRotation, TSubclassOf<UEnvQueryContext> LineDirection) const { TArray<FRotator> ContextRotations; if (IsContextPerItem(LineDirection)) { ContextRotations.Add(ItemRotation); } else { QueryInstance.PrepareContext(LineDirection, ContextRotations); } for (int32 RotationIndex = 0; RotationIndex < ContextRotations.Num(); RotationIndex++) { const FVector Dir = ContextRotations[RotationIndex].Vector(); Directions.Add(Dir); } }
void UEnvQueryTest_Distance::RunTest(FEnvQueryInstance& QueryInstance) const { UObject* QueryOwner = QueryInstance.Owner.Get(); if (QueryOwner == nullptr) { return; } FloatValueMin.BindData(QueryOwner, QueryInstance.QueryID); float MinThresholdValue = FloatValueMin.GetValue(); FloatValueMax.BindData(QueryOwner, QueryInstance.QueryID); float MaxThresholdValue = FloatValueMax.GetValue(); // don't support context Item here, it doesn't make any sense TArray<FVector> ContextLocations; if (!QueryInstance.PrepareContext(DistanceTo, ContextLocations)) { return; } switch (TestMode) { case EEnvTestDistance::Distance3D: for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) { const FVector ItemLocation = GetItemLocation(QueryInstance, It.GetIndex()); for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { const float Distance = CalcDistance3D(ItemLocation, ContextLocations[ContextIndex]); It.SetScore(TestPurpose, FilterType, Distance, MinThresholdValue, MaxThresholdValue); } } break; case EEnvTestDistance::Distance2D: for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) { const FVector ItemLocation = GetItemLocation(QueryInstance, It.GetIndex()); for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { const float Distance = CalcDistance2D(ItemLocation, ContextLocations[ContextIndex]); It.SetScore(TestPurpose, FilterType, Distance, MinThresholdValue, MaxThresholdValue); } } break; case EEnvTestDistance::DistanceZ: for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) { const FVector ItemLocation = GetItemLocation(QueryInstance, It.GetIndex()); for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { const float Distance = CalcDistanceZ(ItemLocation, ContextLocations[ContextIndex]); It.SetScore(TestPurpose, FilterType, Distance, MinThresholdValue, MaxThresholdValue); } } break; case EEnvTestDistance::DistanceAbsoluteZ: for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) { const FVector ItemLocation = GetItemLocation(QueryInstance, It.GetIndex()); for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { const float Distance = CalcDistanceAbsoluteZ(ItemLocation, ContextLocations[ContextIndex]); It.SetScore(TestPurpose, FilterType, Distance, MinThresholdValue, MaxThresholdValue); } } break; default: checkNoEntry(); return; } }
void UEnvQueryGenerator_PathingGrid::ProjectAndFilterNavPoints(TArray<FNavLocation>& Points, FEnvQueryInstance& QueryInstance) const { Super::ProjectAndFilterNavPoints(Points, QueryInstance); #if WITH_RECAST UObject* DataOwner = QueryInstance.Owner.Get(); PathToItem.BindData(DataOwner, QueryInstance.QueryID); ScanRangeMultiplier.BindData(DataOwner, QueryInstance.QueryID); bool bPathToItem = PathToItem.GetValue(); float RangeMultiplierValue = ScanRangeMultiplier.GetValue(); ARecastNavMesh* NavMeshData = const_cast<ARecastNavMesh*>(static_cast<const ARecastNavMesh*>(FEQSHelpers::FindNavigationDataForQuery(QueryInstance))); if (NavMeshData == nullptr || DataOwner == nullptr) { return; } TArray<FVector> ContextLocations; QueryInstance.PrepareContext(GenerateAround, ContextLocations); FSharedNavQueryFilter NavigationFilterCopy = NavigationFilter ? UNavigationQueryFilter::GetQueryFilter(*NavMeshData, DataOwner, NavigationFilter)->GetCopy() : NavMeshData->GetDefaultQueryFilter()->GetCopy(); NavigationFilterCopy->SetBacktrackingEnabled(!bPathToItem); { TArray<NavNodeRef> Polys; TArray<FNavLocation> HitLocations; const FVector ProjectionExtent(ProjectionData.ExtentX, ProjectionData.ExtentX, (ProjectionData.ProjectDown + ProjectionData.ProjectUp) / 2); for (int32 ContextIdx = 0; ContextIdx < ContextLocations.Num() && Points.Num(); ContextIdx++) { float CollectDistanceSq = 0.0f; for (int32 Idx = 0; Idx < Points.Num(); Idx++) { const float TestDistanceSq = FVector::DistSquared(Points[Idx].Location, ContextLocations[ContextIdx]); CollectDistanceSq = FMath::Max(CollectDistanceSq, TestDistanceSq); } const float MaxPathDistance = FMath::Sqrt(CollectDistanceSq) * RangeMultiplierValue; Polys.Reset(); FRecastDebugPathfindingData NodePoolData; NodePoolData.Flags = ERecastDebugPathfindingFlags::Basic; NavMeshData->GetPolysWithinPathingDistance(ContextLocations[ContextIdx], MaxPathDistance, Polys, NavigationFilterCopy, nullptr, &NodePoolData); for (int32 Idx = Points.Num() - 1; Idx >= 0; Idx--) { bool bHasPath = PathGridHelpers::HasPath(NodePoolData, Points[Idx].NodeRef); if (!bHasPath && Points[Idx].NodeRef != INVALID_NAVNODEREF) { // try projecting it again, maybe it will match valid poly on different height HitLocations.Reset(); FVector TestPt(Points[Idx].Location.X, Points[Idx].Location.Y, ContextLocations[ContextIdx].Z); NavMeshData->ProjectPointMulti(TestPt, HitLocations, ProjectionExtent, TestPt.Z - ProjectionData.ProjectDown, TestPt.Z + ProjectionData.ProjectUp, NavigationFilterCopy, nullptr); for (int32 HitIdx = 0; HitIdx < HitLocations.Num(); HitIdx++) { const bool bHasPathTest = PathGridHelpers::HasPath(NodePoolData, HitLocations[HitIdx].NodeRef); if (bHasPathTest) { Points[Idx] = HitLocations[HitIdx]; Points[Idx].Location.Z += ProjectionData.PostProjectionVerticalOffset; bHasPath = true; break; } } } if (!bHasPath) { Points.RemoveAt(Idx); } } } } #endif // WITH_RECAST }
void UEnvQueryTest_PathfindingBatch::RunTest(FEnvQueryInstance& QueryInstance) const { UObject* QueryOwner = QueryInstance.Owner.Get(); BoolValue.BindData(QueryOwner, QueryInstance.QueryID); PathFromContext.BindData(QueryOwner, QueryInstance.QueryID); SkipUnreachable.BindData(QueryOwner, QueryInstance.QueryID); FloatValueMin.BindData(QueryOwner, QueryInstance.QueryID); FloatValueMax.BindData(QueryOwner, QueryInstance.QueryID); ScanRangeMultiplier.BindData(QueryOwner, QueryInstance.QueryID); bool bWantsPath = BoolValue.GetValue(); bool bPathToItem = PathFromContext.GetValue(); bool bDiscardFailed = SkipUnreachable.GetValue(); float MinThresholdValue = FloatValueMin.GetValue(); float MaxThresholdValue = FloatValueMax.GetValue(); float RangeMultiplierValue = ScanRangeMultiplier.GetValue(); UNavigationSystem* NavSys = QueryInstance.World->GetNavigationSystem(); if (NavSys == nullptr) { return; } ANavigationData* NavData = FindNavigationData(*NavSys, QueryOwner); ARecastNavMesh* NavMeshData = Cast<ARecastNavMesh>(NavData); if (NavMeshData == nullptr) { return; } TArray<FVector> ContextLocations; if (!QueryInstance.PrepareContext(Context, ContextLocations)) { return; } TArray<FNavigationProjectionWork> TestPoints; TArray<float> CollectDistanceSq; CollectDistanceSq.Init(0.0f, ContextLocations.Num()); FSharedNavQueryFilter NavigationFilter = FilterClass != nullptr ? UNavigationQueryFilter::GetQueryFilter(*NavMeshData, FilterClass)->GetCopy() : NavMeshData->GetDefaultQueryFilter()->GetCopy(); NavigationFilter->SetBacktrackingEnabled(!bPathToItem); const dtQueryFilter* NavQueryFilter = ((const FRecastQueryFilter*)NavigationFilter->GetImplementation())->GetAsDetourQueryFilter(); { // scope for perf timers // can't use FEnvQueryInstance::ItemIterator yet, since it has built in scoring functionality for (int32 ItemIdx = 0; ItemIdx < QueryInstance.Items.Num(); ItemIdx++) { if (QueryInstance.Items[ItemIdx].IsValid()) { const FVector ItemLocation = GetItemLocation(QueryInstance, ItemIdx); TestPoints.Add(FNavigationProjectionWork(ItemLocation)); for (int32 ContextIdx = 0; ContextIdx < ContextLocations.Num(); ContextIdx++) { const float TestDistanceSq = FVector::DistSquared(ItemLocation, ContextLocations[ContextIdx]); CollectDistanceSq[ContextIdx] = FMath::Max(CollectDistanceSq[ContextIdx], TestDistanceSq); } } } NavMeshData->BatchProjectPoints(TestPoints, NavMeshData->GetDefaultQueryExtent(), NavigationFilter); } TArray<FRecastDebugPathfindingData> NodePoolData; NodePoolData.SetNum(ContextLocations.Num()); { // scope for perf timer TArray<NavNodeRef> Polys; for (int32 ContextIdx = 0; ContextIdx < ContextLocations.Num(); ContextIdx++) { const float MaxPathDistance = FMath::Sqrt(CollectDistanceSq[ContextIdx]) * RangeMultiplierValue; Polys.Reset(); NodePoolData[ContextIdx].Flags = ERecastDebugPathfindingFlags::PathLength; NavMeshData->GetPolysWithinPathingDistance(ContextLocations[ContextIdx], MaxPathDistance, Polys, NavigationFilter, nullptr, &NodePoolData[ContextIdx]); } } int32 ProjectedItemIdx = 0; if (GetWorkOnFloatValues()) { NodePoolHelpers::PathParamFunc Func[] = { nullptr, NodePoolHelpers::GetPathCost, NodePoolHelpers::GetPathLength }; FEnvQueryInstance::ItemIterator It(this, QueryInstance); for (It.IgnoreTimeLimit(); It; ++It, ProjectedItemIdx++) { for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { const float PathValue = Func[TestMode](NodePoolData[ContextIndex], TestPoints[ProjectedItemIdx], NavQueryFilter); It.SetScore(TestPurpose, FilterType, PathValue, MinThresholdValue, MaxThresholdValue); if (bDiscardFailed && PathValue >= BIG_NUMBER) { It.ForceItemState(EEnvItemStatus::Failed); } } } } else { FEnvQueryInstance::ItemIterator It(this, QueryInstance); for (It.IgnoreTimeLimit(); It; ++It, ProjectedItemIdx++) { for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { const bool bFoundPath = NodePoolHelpers::HasPath(NodePoolData[ContextIndex], TestPoints[ProjectedItemIdx]); It.SetScore(TestPurpose, FilterType, bFoundPath, bWantsPath); } } } }
void UEnvQueryGenerator_Donut::GenerateItems(FEnvQueryInstance& QueryInstance) const { TArray<FVector> CenterPoints; QueryInstance.PrepareContext(Center, CenterPoints); if (CenterPoints.Num() <= 0) { return; } UObject* BindOwner = QueryInstance.Owner.Get(); InnerRadius.BindData(BindOwner, QueryInstance.QueryID); OuterRadius.BindData(BindOwner, QueryInstance.QueryID); NumberOfRings.BindData(BindOwner, QueryInstance.QueryID); PointsPerRing.BindData(BindOwner, QueryInstance.QueryID); ArcAngle.BindData(BindOwner, QueryInstance.QueryID); float ArcAngleValue = ArcAngle.GetValue(); float InnerRadiusValue = InnerRadius.GetValue(); float OuterRadiusValue = OuterRadius.GetValue(); int32 NumRings = NumberOfRings.GetValue(); int32 NumPoints = PointsPerRing.GetValue(); if ((InnerRadiusValue <= 0.f) || (OuterRadiusValue <= 0.f) || (InnerRadiusValue > OuterRadiusValue) || (NumRings < 1) || (NumPoints < 1)) { return; } const float ArcBisectDeg = GetArcBisectorAngle(QueryInstance); const float ArcAngleDeg = FMath::Clamp(ArcAngleValue, 0.0f, 360.0f); const float RadiusDelta = (OuterRadiusValue - InnerRadiusValue) / (NumRings - 1); const float AngleDelta = 2.0 * PI / NumPoints; float SectionAngle = FMath::DegreesToRadians(ArcBisectDeg); TArray<FNavLocation> Points; Points.Reserve(NumPoints * NumRings); for (int32 SectionIdx = 0; SectionIdx < NumPoints; SectionIdx++, SectionAngle += AngleDelta) { if (IsAngleAllowed(SectionAngle, ArcBisectDeg, ArcAngleDeg, bDefineArc)) { const float SinValue = FMath::Sin(SectionAngle); const float CosValue = FMath::Cos(SectionAngle); float RingRadius = InnerRadiusValue; for (int32 RingIdx = 0; RingIdx < NumRings; RingIdx++, RingRadius += RadiusDelta) { const FVector RingPos(RingRadius * CosValue, RingRadius * SinValue, 0.0f); for (int32 ContextIdx = 0; ContextIdx < CenterPoints.Num(); ContextIdx++) { const FNavLocation PointPos = FNavLocation(CenterPoints[ContextIdx] + RingPos); Points.Add(PointPos); } } } } ProjectAndFilterNavPoints(Points, QueryInstance); StoreNavPoints(Points, QueryInstance); }
void UEnvQueryTest::NormalizeItemScores(FEnvQueryInstance& QueryInstance) { if (!IsScoring()) { return; } float WeightValue = 0.0f; if (!QueryInstance.GetParamValue(Weight, WeightValue, TEXT("Weight"))) { return; } float MinScore = 0; float MaxScore = -BIG_NUMBER; if (ClampMinType == EEnvQueryTestClamping::FilterThreshold) { bool bSuccess = QueryInstance.GetParamValue(FloatFilterMin, MinScore, TEXT("FloatFilterMin")); if (!bSuccess) { UE_LOG(LogEQS, Warning, TEXT("Unable to get FloatFilterMin parameter value from EnvQueryInstance %s"), FloatFilterMin.IsNamedParam() ? *FloatFilterMin.ParamName.ToString() : TEXT("<No name specified>")); } } else if (ClampMinType == EEnvQueryTestClamping::SpecifiedValue) { bool bSuccess = QueryInstance.GetParamValue(ScoreClampingMin, MinScore, TEXT("ScoreClampingMin")); if (!bSuccess) { UE_LOG(LogEQS, Warning, TEXT("Unable to get ClampMinType parameter value from EnvQueryInstance %s"), ScoreClampingMin.IsNamedParam() ? *ScoreClampingMin.ParamName.ToString() : TEXT("<No name specified>")); } } if (ClampMaxType == EEnvQueryTestClamping::FilterThreshold) { bool bSuccess = QueryInstance.GetParamValue(FloatFilterMax, MaxScore, TEXT("FloatFilterMax")); if (!bSuccess) { UE_LOG(LogEQS, Warning, TEXT("Unable to get FloatFilterMax parameter value from EnvQueryInstance %s"), FloatFilterMax.IsNamedParam() ? *FloatFilterMax.ParamName.ToString() : TEXT("<No name specified>")); } } else if (ClampMaxType == EEnvQueryTestClamping::SpecifiedValue) { bool bSuccess = QueryInstance.GetParamValue(ScoreClampingMax, MaxScore, TEXT("ScoreClampingMax")); if (!bSuccess) { UE_LOG(LogEQS, Warning, TEXT("Unable to get ScoreClampingMax parameter value from EnvQueryInstance %s"), ScoreClampingMax.IsNamedParam() ? *ScoreClampingMax.ParamName.ToString() : TEXT("<No name specified>")); } } FEnvQueryItemDetails* DetailInfo = QueryInstance.ItemDetails.GetData(); if ((ClampMinType == EEnvQueryTestClamping::None) || (ClampMaxType == EEnvQueryTestClamping::None) ) { for (int32 ItemIndex = 0; ItemIndex < QueryInstance.Items.Num(); ItemIndex++, DetailInfo++) { if (!QueryInstance.Items[ItemIndex].IsValid()) { continue; } float TestValue = DetailInfo->TestResults[QueryInstance.CurrentTest]; if (TestValue != UEnvQueryTypes::SkippedItemValue) { if (ClampMinType == EEnvQueryTestClamping::None) { MinScore = FMath::Min(MinScore, TestValue); } if (ClampMaxType == EEnvQueryTestClamping::None) { MaxScore = FMath::Max(MaxScore, TestValue); } } } } DetailInfo = QueryInstance.ItemDetails.GetData(); if (bNormalizeFromZero && MinScore > 0.0f) { MinScore = 0.0f; } if (MinScore != MaxScore) { for (int32 ItemIndex = 0; ItemIndex < QueryInstance.ItemDetails.Num(); ItemIndex++, DetailInfo++) { if (QueryInstance.Items[ItemIndex].IsValid() == false) { continue; } float WeightedScore = 0.0f; float& TestValue = DetailInfo->TestResults[QueryInstance.CurrentTest]; if (TestValue != UEnvQueryTypes::SkippedItemValue) { const float ClampedScore = FMath::Clamp(TestValue, MinScore, MaxScore); const float NormalizedScore = (ClampedScore - MinScore) / (MaxScore - MinScore); // TODO? Add an option to invert the normalized score before applying an equation. const float NormalizedScoreForEquation = /*bMirrorNormalizedScore ? (1.0f - NormalizedScore) :*/ NormalizedScore; switch (ScoringEquation) { case EEnvTestScoreEquation::Linear: WeightedScore = WeightValue * NormalizedScoreForEquation; break; case EEnvTestScoreEquation::InverseLinear: { // For now, we're avoiding having a separate flag for flipping the direction of the curve // because we don't have usage cases yet and want to avoid too complex UI. If we decide // to add that flag later, we'll need to remove this option, since it should just be "mirror // curve" plus "Linear". float InverseNormalizedScore = (1.0f - NormalizedScoreForEquation); WeightedScore = WeightValue * InverseNormalizedScore; break; } case EEnvTestScoreEquation::Square: WeightedScore = WeightValue * (NormalizedScoreForEquation * NormalizedScoreForEquation); break; case EEnvTestScoreEquation::Constant: // I know, it's not "constant". It's "Constant, or zero". The tooltip should explain that. WeightedScore = (NormalizedScoreForEquation > 0) ? WeightValue : 0.0f; break; default: break; } } else { TestValue = 0.0f; WeightedScore = 0.0f; } #if USE_EQS_DEBUGGER DetailInfo->TestWeightedScores[QueryInstance.CurrentTest] = WeightedScore; #endif QueryInstance.Items[ItemIndex].Score += WeightedScore; } } }
void UEnvQueryTest_Trace::RunTest(FEnvQueryInstance& QueryInstance) const { UObject* DataOwner = QueryInstance.Owner.Get(); BoolValue.BindData(DataOwner, QueryInstance.QueryID); TraceFromContext.BindData(DataOwner, QueryInstance.QueryID); ItemHeightOffset.BindData(DataOwner, QueryInstance.QueryID); ContextHeightOffset.BindData(DataOwner, QueryInstance.QueryID); bool bWantsHit = BoolValue.GetValue(); bool bTraceToItem = TraceFromContext.GetValue(); float ItemZ = ItemHeightOffset.GetValue(); float ContextZ = ContextHeightOffset.GetValue(); TArray<FVector> ContextLocations; if (!QueryInstance.PrepareContext(Context, ContextLocations)) { return; } FCollisionQueryParams TraceParams(TEXT("EnvQueryTrace"), TraceData.bTraceComplex); TraceParams.bTraceAsyncScene = true; TArray<AActor*> IgnoredActors; if (QueryInstance.PrepareContext(Context, IgnoredActors)) { TraceParams.AddIgnoredActors(IgnoredActors); } ECollisionChannel TraceCollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceData.TraceChannel); FVector TraceExtent(TraceData.ExtentX, TraceData.ExtentY, TraceData.ExtentZ); FRunTraceSignature TraceFunc; switch (TraceData.TraceShape) { case EEnvTraceShape::Line: TraceFunc.BindUObject(this, bTraceToItem ? &UEnvQueryTest_Trace::RunLineTraceTo : &UEnvQueryTest_Trace::RunLineTraceFrom); break; case EEnvTraceShape::Box: TraceFunc.BindUObject(this, bTraceToItem ? &UEnvQueryTest_Trace::RunBoxTraceTo : &UEnvQueryTest_Trace::RunBoxTraceFrom); break; case EEnvTraceShape::Sphere: TraceFunc.BindUObject(this, bTraceToItem ? &UEnvQueryTest_Trace::RunSphereTraceTo : &UEnvQueryTest_Trace::RunSphereTraceFrom); break; case EEnvTraceShape::Capsule: TraceFunc.BindUObject(this, bTraceToItem ? &UEnvQueryTest_Trace::RunCapsuleTraceTo : &UEnvQueryTest_Trace::RunCapsuleTraceFrom); break; default: return; } for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { ContextLocations[ContextIndex].Z += ContextZ; } for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) { const FVector ItemLocation = GetItemLocation(QueryInstance, It.GetIndex()) + FVector(0, 0, ItemZ); AActor* ItemActor = GetItemActor(QueryInstance, It.GetIndex()); for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { const bool bHit = TraceFunc.Execute(ItemLocation, ContextLocations[ContextIndex], ItemActor, QueryInstance.World, TraceCollisionChannel, TraceParams, TraceExtent); It.SetScore(TestPurpose, FilterType, bHit, bWantsHit); } } }
void UEnvQueryGenerator_PathingGrid::GenerateItems(FEnvQueryInstance& QueryInstance) const { #if WITH_RECAST const ARecastNavMesh* NavMesh = FEQSHelpers::FindNavMeshForQuery(QueryInstance); if (NavMesh == NULL) { return; } float PathDistanceValue = 0.0f; float DensityValue = 0.0f; bool bFromContextValue = true; if (!QueryInstance.GetParamValue(MaxPathDistance, PathDistanceValue, TEXT("MaxPathDistance")) || !QueryInstance.GetParamValue(Density, DensityValue, TEXT("Density")) || !QueryInstance.GetParamValue(PathFromContext, bFromContextValue, TEXT("PathFromContext"))) { return; } const int32 ItemCount = FPlatformMath::TruncToInt((PathDistanceValue * 2.0f / DensityValue) + 1); const int32 ItemCountHalf = ItemCount / 2; TArray<FVector> ContextLocations; QueryInstance.PrepareContext(GenerateAround, ContextLocations); QueryInstance.ReserveItemData(ItemCountHalf * ItemCountHalf * ContextLocations.Num()); TArray<NavNodeRef> NavNodeRefs; NavMesh->BeginBatchQuery(); int32 DataOffset = 0; for (int32 ContextIndex = 0; ContextIndex < ContextLocations.Num(); ContextIndex++) { // find all node refs in pathing distance FBox AllowedBounds; NavNodeRefs.Reset(); FindNodeRefsInPathDistance(NavMesh, ContextLocations[ContextIndex], PathDistanceValue, bFromContextValue, NavNodeRefs, AllowedBounds); // cast 2D grid on generated node refs for (int32 IndexX = 0; IndexX < ItemCount; ++IndexX) { for (int32 IndexY = 0; IndexY < ItemCount; ++IndexY) { const FVector TestPoint = ContextLocations[ContextIndex] - FVector(DensityValue * (IndexX - ItemCountHalf), DensityValue * (IndexY - ItemCountHalf), 0); if (!AllowedBounds.IsInsideXY(TestPoint)) { continue; } // trace line on navmesh, and process all hits with collected node refs TArray<FNavLocation> Hits; NavMesh->ProjectPointMulti(TestPoint, Hits, FVector::ZeroVector, AllowedBounds.Min.Z, AllowedBounds.Max.Z); for (int32 HitIndex = 0; HitIndex < Hits.Num(); HitIndex++) { if (IsNavLocationInPathDistance(NavMesh, Hits[HitIndex], NavNodeRefs)) { // store generated point QueryInstance.AddItemData<UEnvQueryItemType_Point>(Hits[HitIndex].Location); } } } } } NavMesh->FinishBatchQuery(); #endif // WITH_RECAST }