EPathFollowingRequestResult::Type AAIController::MoveToLocation(const FVector& Dest, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bProjectDestinationToNavigation, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass) { SCOPE_CYCLE_COUNTER(STAT_MoveToLocation); EPathFollowingRequestResult::Type Result = EPathFollowingRequestResult::Failed; bool bCanRequestMove = true; UE_VLOG(this, LogNavigation, Log, TEXT("MoveToLocation: Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)"), *Dest.ToString(), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe); // Check input is valid if( Dest.ContainsNaN() ) { UE_VLOG(this, LogNavigation, Error, TEXT("AAIController::MoveToLocation: Destination is not valid! Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)"), *Dest.ToString(), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe); ensure( !Dest.ContainsNaN() ); bCanRequestMove = false; } FVector GoalLocation = Dest; // fail if projection to navigation is required but it either failed or there's not navigation component if (bCanRequestMove && bProjectDestinationToNavigation && (NavComponent == NULL || !NavComponent->ProjectPointToNavigation(Dest, GoalLocation))) { UE_VLOG_LOCATION(this, Dest, 30.f, FLinearColor::Red, TEXT("AAIController::MoveToLocation failed to project destination location to navmesh")); bCanRequestMove = false; } if (bCanRequestMove && PathFollowingComponent && PathFollowingComponent->HasReached(GoalLocation, AcceptanceRadius, !bStopOnOverlap)) { UE_VLOG(this, LogNavigation, Log, TEXT("MoveToLocation: already at goal!")); PathFollowingComponent->SetLastMoveAtGoal(true); OnMoveCompleted(0, EPathFollowingResult::Success); Result = EPathFollowingRequestResult::AlreadyAtGoal; bCanRequestMove = false; } if (bCanRequestMove) { FNavPathSharedPtr Path = FindPath(GoalLocation, bUsePathfinding, UNavigationQueryFilter::GetQueryFilter(FilterClass)); if (Path.IsValid()) { bAllowStrafe = bCanStrafe; const uint32 RequestID = RequestMove(Path, NULL, AcceptanceRadius, bStopOnOverlap); Result = (RequestID != INVALID_MOVEREQUESTID) ? EPathFollowingRequestResult::RequestSuccessful : EPathFollowingRequestResult::Failed; } } if (Result == EPathFollowingRequestResult::Failed) { if (PathFollowingComponent) { PathFollowingComponent->SetLastMoveAtGoal(false); } OnMoveCompleted(INVALID_MOVEREQUESTID, EPathFollowingResult::Invalid); } return Result; }
/** * Constructs a raw mesh from legacy raw triangles. */ static bool BuildRawMeshFromRawTriangles( FRawMesh& OutRawMesh, FMeshBuildSettings& OutBuildSettings, struct FStaticMeshTriangle* RawTriangles, int32 NumTriangles, const TCHAR* MeshName ) { int32 NumWedges = NumTriangles * 3; OutRawMesh.Empty(); OutRawMesh.FaceMaterialIndices.Empty(NumTriangles); OutRawMesh.FaceSmoothingMasks.Empty(NumWedges); OutRawMesh.VertexPositions.Empty(NumWedges); OutRawMesh.WedgeIndices.Empty(NumWedges); int32 NumTexCoords = 0; bool bHaveNormals = false; bool bHaveTangents = false; bool bHaveColors = false; bool bCorruptFlags = false; bool bCorruptNormals = false; bool bCorruptTangents = false; bool bCorruptPositions = false; for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex) { FStaticMeshTriangle* Tri = RawTriangles + TriIndex; OutRawMesh.FaceMaterialIndices.Add(Tri->MaterialIndex); OutRawMesh.FaceSmoothingMasks.Add(Tri->SmoothingMask); bCorruptFlags |= (RawTriangles[0].bExplicitNormals & 0xFFFFFFFE) != 0 || (RawTriangles[0].bOverrideTangentBasis & 0xFFFFFFFE) != 0; for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) { FVector Position = Tri->Vertices[CornerIndex]; OutRawMesh.WedgeIndices.Add(OutRawMesh.VertexPositions.Add(Position)); NumTexCoords = FMath::Max(NumTexCoords, Tri->NumUVs); bHaveNormals |= Tri->TangentZ[CornerIndex] != FVector::ZeroVector; bHaveTangents |= (Tri->TangentX[CornerIndex] != FVector::ZeroVector && Tri->TangentY[CornerIndex] != FVector::ZeroVector); bHaveColors |= Tri->Colors[CornerIndex] != FColor::White; bCorruptPositions |= Position.ContainsNaN(); bCorruptTangents |= Tri->TangentX[CornerIndex].ContainsNaN() || Tri->TangentY[CornerIndex].ContainsNaN(); bCorruptNormals |= Tri->TangentZ[CornerIndex].ContainsNaN(); } } bool bTooManyTexCoords = NumTexCoords > 8; NumTexCoords = FMath::Min(NumTexCoords, 8); // FStaticMeshTriangle has at most 8 texture coordinates. NumTexCoords = FMath::Min<int32>(NumTexCoords, MAX_MESH_TEXTURE_COORDS); for (int32 TexCoordIndex = 0; TexCoordIndex < FMath::Max(1, NumTexCoords); ++TexCoordIndex) { OutRawMesh.WedgeTexCoords[TexCoordIndex].AddZeroed(NumTriangles * 3); } for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex) { FStaticMeshTriangle* Tri = RawTriangles + TriIndex; for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) { for (int32 TexCoordIndex = 0; TexCoordIndex < NumTexCoords; ++TexCoordIndex) { FVector2D UV = TexCoordIndex < Tri->NumUVs ? Tri->UVs[CornerIndex][TexCoordIndex] : FVector2D(0.0f,0.0f); OutRawMesh.WedgeTexCoords[TexCoordIndex][TriIndex * 3 + CornerIndex] = UV; } } } if (bHaveNormals && !bCorruptNormals) { OutRawMesh.WedgeTangentZ.AddZeroed(NumTriangles * 3); for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex) { if (RawTriangles[TriIndex].bExplicitNormals || RawTriangles[TriIndex].bOverrideTangentBasis) { for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) { OutRawMesh.WedgeTangentZ[TriIndex * 3 + CornerIndex] = RawTriangles[TriIndex].TangentZ[CornerIndex]; } } } } if (bHaveTangents && !bCorruptTangents) { OutRawMesh.WedgeTangentX.AddZeroed(NumTriangles * 3); OutRawMesh.WedgeTangentY.AddZeroed(NumTriangles * 3); for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex) { if (RawTriangles[TriIndex].bOverrideTangentBasis) { for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) { OutRawMesh.WedgeTangentX[TriIndex * 3 + CornerIndex] = RawTriangles[TriIndex].TangentX[CornerIndex]; OutRawMesh.WedgeTangentY[TriIndex * 3 + CornerIndex] = RawTriangles[TriIndex].TangentY[CornerIndex]; } } } } if (bHaveColors) { OutRawMesh.WedgeColors.AddUninitialized(NumTriangles * 3); for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex) { for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex) { OutRawMesh.WedgeColors[TriIndex * 3 + CornerIndex] = RawTriangles[TriIndex].Colors[CornerIndex]; } } } if (bCorruptPositions || bCorruptFlags || bCorruptNormals || bCorruptTangents || bTooManyTexCoords) { UE_LOG(LogStaticMesh,Verbose,TEXT("Legacy raw triangles CORRUPT (%s%s%s%s%s) for %s"), bCorruptPositions ? TEXT("NaN positions,") : TEXT(""), bCorruptFlags ? TEXT("flags,") : TEXT(""), bCorruptNormals ? TEXT("NaN normals,") : TEXT(""), bCorruptTangents ? TEXT("NaN tangents,") : TEXT(""), bTooManyTexCoords ? TEXT(">8 texcoords") : TEXT(""), MeshName ); } if (bCorruptPositions) { OutRawMesh = FRawMesh(); } OutBuildSettings.bRecomputeTangents = !bHaveTangents || bCorruptTangents; OutBuildSettings.bRecomputeNormals = !bHaveNormals || bCorruptNormals; return !(bCorruptPositions || bCorruptFlags || bTooManyTexCoords) && OutRawMesh.IsValid(); }
EPathFollowingRequestResult::Type AAIController::MoveToLocation(const FVector& Dest, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bProjectDestinationToNavigation, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass) { SCOPE_CYCLE_COUNTER(STAT_MoveToLocation); EPathFollowingRequestResult::Type Result = EPathFollowingRequestResult::Failed; bool bCanRequestMove = true; UE_VLOG(this, LogAINavigation, Log, TEXT("MoveToLocation: Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d) Filter(%s)") , TEXT_AI_LOCATION(Dest), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe, *GetNameSafe(FilterClass)); // Check input is valid if (Dest.ContainsNaN()) { UE_VLOG(this, LogAINavigation, Error, TEXT("AAIController::MoveToLocation: Destination is not valid! Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)") , TEXT_AI_LOCATION(Dest), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe); ensure(!Dest.ContainsNaN()); bCanRequestMove = false; } FVector GoalLocation = Dest; // fail if projection to navigation is required but it failed if (bCanRequestMove && bProjectDestinationToNavigation) { UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld()); const FNavAgentProperties& AgentProps = GetNavAgentPropertiesRef(); FNavLocation ProjectedLocation; if (NavSys && !NavSys->ProjectPointToNavigation(Dest, ProjectedLocation, AgentProps.GetExtent(), &AgentProps)) { UE_VLOG_LOCATION(this, LogAINavigation, Error, Dest, 30.f, FLinearColor::Red, TEXT("AAIController::MoveToLocation failed to project destination location to navmesh")); bCanRequestMove = false; } GoalLocation = ProjectedLocation.Location; } if (bCanRequestMove && PathFollowingComponent && PathFollowingComponent->HasReached(GoalLocation, AcceptanceRadius, !bStopOnOverlap)) { UE_VLOG(this, LogAINavigation, Log, TEXT("MoveToLocation: already at goal!")); // make sure previous move request gets aborted PathFollowingComponent->AbortMove(TEXT("Aborting move due to new move request finishing with AlreadyAtGoal"), FAIRequestID::AnyRequest); PathFollowingComponent->SetLastMoveAtGoal(true); OnMoveCompleted(FAIRequestID::CurrentRequest, EPathFollowingResult::Success); Result = EPathFollowingRequestResult::AlreadyAtGoal; bCanRequestMove = false; } if (bCanRequestMove) { FPathFindingQuery Query; const bool bValidQuery = PreparePathfinding(Query, GoalLocation, NULL, bUsePathfinding, FilterClass); const FAIRequestID RequestID = bValidQuery ? RequestPathAndMove(Query, NULL, AcceptanceRadius, bStopOnOverlap, NULL) : FAIRequestID::InvalidRequest; if (RequestID.IsValid()) { bAllowStrafe = bCanStrafe; Result = EPathFollowingRequestResult::RequestSuccessful; } } if (Result == EPathFollowingRequestResult::Failed) { if (PathFollowingComponent) { PathFollowingComponent->SetLastMoveAtGoal(false); } OnMoveCompleted(FAIRequestID::InvalidRequest, EPathFollowingResult::Invalid); } return Result; }