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();
}
Beispiel #3
0
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;
}