static void UpdatePlanarReflectionContents_RenderThread(
	FRHICommandListImmediate& RHICmdList, 
	FSceneRenderer* MainSceneRenderer, 
	FSceneRenderer* SceneRenderer, 
	const FPlanarReflectionSceneProxy* SceneProxy,
	FRenderTarget* RenderTarget, 
	FTexture* RenderTargetTexture, 
	const FPlane& MirrorPlane,
	const FName OwnerName, 
	const FResolveParams& ResolveParams, 
	bool bUseSceneColorTexture)
{
	QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderPlanarReflection);

	FBox PlanarReflectionBounds = SceneProxy->WorldBounds;

	bool bIsInAnyFrustum = false;
	for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
	{
		FViewInfo& View = SceneRenderer->Views[ViewIndex];
		if (View.ViewFrustum.IntersectBox(PlanarReflectionBounds.GetCenter(), PlanarReflectionBounds.GetExtent()))
		{
			bIsInAnyFrustum = true;
			break;
		}
	}

	if (bIsInAnyFrustum)
	{
		bool bIsVisibleInAnyView = true;
		for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
		{
			FViewInfo& View = SceneRenderer->Views[ViewIndex];
			FSceneViewState* ViewState = View.ViewState;

			if (ViewState)
			{
				FIndividualOcclusionHistory& OcclusionHistory = ViewState->PlanarReflectionOcclusionHistories.FindOrAdd(SceneProxy->PlanarReflectionId);

				// +1 to buffered frames because the query is submitted late into the main frame, but read at the beginning of a reflection capture frame
				const int32 NumBufferedFrames = FOcclusionQueryHelpers::GetNumBufferedFrames() + 1;
				// +1 to frame counter because we are operating before the main view's InitViews, which is where OcclusionFrameCounter is incremented
				uint32 OcclusionFrameCounter = ViewState->OcclusionFrameCounter + 1;
				FRenderQueryRHIRef& PastQuery = OcclusionHistory.GetPastQuery(OcclusionFrameCounter, NumBufferedFrames);

				if (IsValidRef(PastQuery))
				{
					uint64 NumSamples = 0;
					QUICK_SCOPE_CYCLE_COUNTER(STAT_PlanarReflectionOcclusionQueryResults);

					if (RHIGetRenderQueryResult(PastQuery.GetReference(), NumSamples, true))
					{
						bIsVisibleInAnyView = NumSamples > 0;
						if (bIsVisibleInAnyView)
						{
							break;
						}
					}
				}
			}
		}

		if (bIsVisibleInAnyView)
		{
			FMemMark MemStackMark(FMemStack::Get());

			// update any resources that needed a deferred update
			FDeferredUpdateResource::UpdateResources(RHICmdList);

			{
#if WANTS_DRAW_MESH_EVENTS
				FString EventName;
				OwnerName.ToString(EventName);
				SCOPED_DRAW_EVENTF(RHICmdList, SceneCapture, TEXT("PlanarReflection %s"), *EventName);
#else
				SCOPED_DRAW_EVENT(RHICmdList, UpdatePlanarReflectionContent_RenderThread);
#endif

				const FRenderTarget* Target = SceneRenderer->ViewFamily.RenderTarget;
				SetRenderTarget(RHICmdList, Target->GetRenderTargetTexture(), NULL, true);

				// Note: relying on GBuffer SceneColor alpha being cleared to 1 in the main scene rendering
				check(GetSceneColorClearAlpha() == 1.0f);
				RHICmdList.Clear(true, FLinearColor(0, 0, 0, 1), false, (float)ERHIZBuffer::FarPlane, false, 0, FIntRect());

				// Reflection view late update
				if (SceneRenderer->Views.Num() > 1)
				{
					const FMirrorMatrix MirrorMatrix(MirrorPlane);
					for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
					{
						FViewInfo& ReflectionViewToUpdate = SceneRenderer->Views[ViewIndex];
						const FViewInfo& UpdatedParentView = MainSceneRenderer->Views[ViewIndex];

						ReflectionViewToUpdate.UpdatePlanarReflectionViewMatrix(UpdatedParentView, MirrorMatrix);
					}
				}

				// Render the scene normally
				{
					SCOPED_DRAW_EVENT(RHICmdList, RenderScene);
					SceneRenderer->Render(RHICmdList);
				}

				for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
				{
					FViewInfo& View = SceneRenderer->Views[ViewIndex];
					if (MainSceneRenderer->Scene->GetShadingPath() == EShadingPath::Deferred)
					{
						PrefilterPlanarReflection<true>(RHICmdList, View, SceneProxy, Target);
					}
					else
					{
						PrefilterPlanarReflection<false>(RHICmdList, View, SceneProxy, Target);
					}
				}
				RHICmdList.CopyToResolveTarget(RenderTarget->GetRenderTargetTexture(), RenderTargetTexture->TextureRHI, false, ResolveParams);
			}
			FSceneRenderer::WaitForTasksClearSnapshotsAndDeleteSceneRenderer(RHICmdList, SceneRenderer);
		}
	}
}
示例#2
0
FVector ABxtAsteroidSpawner::GetRandomSpawnLocation() const
{
	const FBox spawnerBounds = GetComponentsBoundingBox(true);
	const float y = FMath::FRandRange(spawnerBounds.Min.Y, spawnerBounds.Max.Y);
	return FVector(spawnerBounds.GetCenter().X - 500.0f, y, 0.0f);
}
/** 
 * Returns an array of visibility data for the given view position, or NULL if none exists. 
 * The data bits are indexed by VisibilityId of each primitive in the scene.
 * This method decompresses data if necessary and caches it based on the bucket and chunk index in the view state.
 */
const uint8* FSceneViewState::GetPrecomputedVisibilityData(FViewInfo& View, const FScene* Scene)
{
	const uint8* PrecomputedVisibilityData = NULL;
	if (Scene->PrecomputedVisibilityHandler && GAllowPrecomputedVisibility && View.Family->EngineShowFlags.PrecomputedVisibility)
	{
		const FPrecomputedVisibilityHandler& Handler = *Scene->PrecomputedVisibilityHandler;
		FViewElementPDI VisibilityCellsPDI(&View, NULL);

		// Draw visibility cell bounds for debugging if enabled
		if ((GShowPrecomputedVisibilityCells || View.Family->EngineShowFlags.PrecomputedVisibilityCells) && !GShowRelevantPrecomputedVisibilityCells)
		{
			for (int32 BucketIndex = 0; BucketIndex < Handler.PrecomputedVisibilityCellBuckets.Num(); BucketIndex++)
			{
				for (int32 CellIndex = 0; CellIndex < Handler.PrecomputedVisibilityCellBuckets[BucketIndex].Cells.Num(); CellIndex++)
				{
					const FPrecomputedVisibilityCell& CurrentCell = Handler.PrecomputedVisibilityCellBuckets[BucketIndex].Cells[CellIndex];
					// Construct the cell's bounds
					const FBox CellBounds(CurrentCell.Min, CurrentCell.Min + FVector(Handler.PrecomputedVisibilityCellSizeXY, Handler.PrecomputedVisibilityCellSizeXY, Handler.PrecomputedVisibilityCellSizeZ));
					if (View.ViewFrustum.IntersectBox(CellBounds.GetCenter(), CellBounds.GetExtent()))
					{
						DrawWireBox(&VisibilityCellsPDI, CellBounds, FColor(50, 50, 255), SDPG_World);
					}
				}
			}
		}

		// Calculate the bucket that ViewOrigin falls into
		// Cells are hashed into buckets to reduce search time
		const float FloatOffsetX = (View.ViewMatrices.ViewOrigin.X - Handler.PrecomputedVisibilityCellBucketOriginXY.X) / Handler.PrecomputedVisibilityCellSizeXY;
		// FMath::TruncToInt rounds toward 0, we want to always round down
		const int32 BucketIndexX = FMath::Abs((FMath::TruncToInt(FloatOffsetX) - (FloatOffsetX < 0.0f ? 1 : 0)) / Handler.PrecomputedVisibilityCellBucketSizeXY % Handler.PrecomputedVisibilityNumCellBuckets);
		const float FloatOffsetY = (View.ViewMatrices.ViewOrigin.Y -Handler.PrecomputedVisibilityCellBucketOriginXY.Y) / Handler.PrecomputedVisibilityCellSizeXY;
		const int32 BucketIndexY = FMath::Abs((FMath::TruncToInt(FloatOffsetY) - (FloatOffsetY < 0.0f ? 1 : 0)) / Handler.PrecomputedVisibilityCellBucketSizeXY % Handler.PrecomputedVisibilityNumCellBuckets);
		const int32 PrecomputedVisibilityBucketIndex = BucketIndexY * Handler.PrecomputedVisibilityCellBucketSizeXY + BucketIndexX;

		check(PrecomputedVisibilityBucketIndex < Handler.PrecomputedVisibilityCellBuckets.Num());
		const FPrecomputedVisibilityBucket& CurrentBucket = Handler.PrecomputedVisibilityCellBuckets[PrecomputedVisibilityBucketIndex];
		for (int32 CellIndex = 0; CellIndex < CurrentBucket.Cells.Num(); CellIndex++)
		{
			const FPrecomputedVisibilityCell& CurrentCell = CurrentBucket.Cells[CellIndex];
			// Construct the cell's bounds
			const FBox CellBounds(CurrentCell.Min, CurrentCell.Min + FVector(Handler.PrecomputedVisibilityCellSizeXY, Handler.PrecomputedVisibilityCellSizeXY, Handler.PrecomputedVisibilityCellSizeZ));
			// Check if ViewOrigin is inside the current cell
			if (CellBounds.IsInside(View.ViewMatrices.ViewOrigin))
			{
				// Reuse a cached decompressed chunk if possible
				if (CachedVisibilityChunk
					&& CachedVisibilityHandlerId == Scene->PrecomputedVisibilityHandler->GetId()
					&& CachedVisibilityBucketIndex == PrecomputedVisibilityBucketIndex
					&& CachedVisibilityChunkIndex == CurrentCell.ChunkIndex)
				{
					checkSlow(CachedVisibilityChunk->Num() >= CurrentCell.DataOffset + CurrentBucket.CellDataSize);
					PrecomputedVisibilityData = &(*CachedVisibilityChunk)[CurrentCell.DataOffset];
				}
				else
				{
					const FCompressedVisibilityChunk& CompressedChunk = Handler.PrecomputedVisibilityCellBuckets[PrecomputedVisibilityBucketIndex].CellDataChunks[CurrentCell.ChunkIndex];
					CachedVisibilityBucketIndex = PrecomputedVisibilityBucketIndex;
					CachedVisibilityChunkIndex = CurrentCell.ChunkIndex;
					CachedVisibilityHandlerId = Scene->PrecomputedVisibilityHandler->GetId();

					if (CompressedChunk.bCompressed)
					{
						// Decompress the needed visibility data chunk
						DecompressedVisibilityChunk.Reset();
						DecompressedVisibilityChunk.AddUninitialized(CompressedChunk.UncompressedSize);
						verify(FCompression::UncompressMemory(
							COMPRESS_ZLIB, 
							DecompressedVisibilityChunk.GetData(),
							CompressedChunk.UncompressedSize,
							CompressedChunk.Data.GetData(),
							CompressedChunk.Data.Num()));
						CachedVisibilityChunk = &DecompressedVisibilityChunk;
					}
					else
					{
						CachedVisibilityChunk = &CompressedChunk.Data;
					}

					checkSlow(CachedVisibilityChunk->Num() >= CurrentCell.DataOffset + CurrentBucket.CellDataSize);
					// Return a pointer to the cell containing ViewOrigin's decompressed visibility data
					PrecomputedVisibilityData = &(*CachedVisibilityChunk)[CurrentCell.DataOffset];
				}

				if (GShowRelevantPrecomputedVisibilityCells)
				{
					// Draw the currently used visibility cell with green wireframe for debugging
					DrawWireBox(&VisibilityCellsPDI, CellBounds, FColor(50, 255, 50), SDPG_Foreground);
				}
				else
				{
					break;
				}
			}
			else if (GShowRelevantPrecomputedVisibilityCells)
			{
				// Draw all cells in the current visibility bucket as blue wireframe
				DrawWireBox(&VisibilityCellsPDI, CellBounds, FColor(50, 50, 255), SDPG_World);
			}
		}
	}
	return PrecomputedVisibilityData;
}
void FLogVisualizer::UpdateCameraPosition(FName RowName, int32 ItemIndes)
{
    const FVisualLoggerDBRow& DBRow = FVisualLoggerDatabase::Get().GetRowByName(RowName);
    auto &Entries = DBRow.GetItems();
    if (DBRow.GetCurrentItemIndex() == INDEX_NONE || Entries.IsValidIndex(DBRow.GetCurrentItemIndex()) == false)
    {
        return;
    }

    UWorld* World = GetWorld();

    FVector CurrentLocation = Entries[DBRow.GetCurrentItemIndex()].Entry.Location;

    FVector Extent(150);
    bool bFoundActor = false;
    FName OwnerName = Entries[DBRow.GetCurrentItemIndex()].OwnerName;
    for (FActorIterator It(World); It; ++It)
    {
        AActor* Actor = *It;
        if (Actor->GetFName() == OwnerName)
        {
            FVector Orgin;
            Actor->GetActorBounds(false, Orgin, Extent);
            bFoundActor = true;
            break;
        }
    }


    const float DefaultCameraDistance = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>()->DefaultCameraDistance;
    Extent = Extent.SizeSquared() < FMath::Square(DefaultCameraDistance) ? FVector(DefaultCameraDistance) : Extent;

#if WITH_EDITOR
    UEditorEngine *EEngine = Cast<UEditorEngine>(GEngine);
    if (GIsEditor && EEngine != NULL)
    {
        for (auto ViewportClient : EEngine->AllViewportClients)
        {
            ViewportClient->FocusViewportOnBox(FBox::BuildAABB(CurrentLocation, Extent));
        }
    }
    else if (AVisualLoggerCameraController::IsEnabled(World) && AVisualLoggerCameraController::Instance.IsValid() && AVisualLoggerCameraController::Instance->GetSpectatorPawn())
    {
        ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(AVisualLoggerCameraController::Instance->Player);
        if (LocalPlayer && LocalPlayer->ViewportClient && LocalPlayer->ViewportClient->Viewport)
        {

            FViewport* Viewport = LocalPlayer->ViewportClient->Viewport;

            FBox BoundingBox = FBox::BuildAABB(CurrentLocation, Extent);
            const FVector Position = BoundingBox.GetCenter();
            float Radius = BoundingBox.GetExtent().Size();

            FViewportCameraTransform ViewTransform;
            ViewTransform.TransitionToLocation(Position, nullptr, true);

            float NewOrthoZoom;
            const float AspectRatio = 1.777777f;
            CA_SUPPRESS(6326);
            uint32 MinAxisSize = (AspectRatio > 1.0f) ? Viewport->GetSizeXY().Y : Viewport->GetSizeXY().X;
            float Zoom = Radius / (MinAxisSize / 2.0f);

            NewOrthoZoom = Zoom * (Viewport->GetSizeXY().X*15.0f);
            NewOrthoZoom = FMath::Clamp<float>(NewOrthoZoom, 250, MAX_FLT);
            ViewTransform.SetOrthoZoom(NewOrthoZoom);

            AVisualLoggerCameraController::Instance->GetSpectatorPawn()->TeleportTo(ViewTransform.GetLocation(), ViewTransform.GetRotation(), false, true);
        }
    }
#endif
}
void UParticleModuleCollision::Update(FParticleEmitterInstance* Owner, int32 Offset, float DeltaTime)
{
	SCOPE_CYCLE_COUNTER(STAT_ParticleCollisionTime);
	check(Owner);
	check(Owner->Component);
	UWorld* World = Owner->Component->GetWorld();
	if (Owner->ActiveParticles == 0 || (bDropDetail && World && World->bDropDetail))
	{
		return;
	}

	//Gets the owning actor of the component. Can be NULL if the component is spawned with the World as an Outer, e.g. in UGameplayStatics::SpawnEmitterAtLocation().
	AActor* Actor = Owner->Component->GetOwner();

	UParticleLODLevel* LODLevel	= Owner->SpriteTemplate->GetCurrentLODLevel(Owner);
	check(LODLevel);

	const int32 MeshRotationOffset = Owner->GetMeshRotationOffset();
	const bool bMeshRotationActive = Owner->IsMeshRotationActive();

	const FTransform& OwnerTM = Owner->Component->GetAsyncComponentToWorld();
	const FVector ParentScale = OwnerTM.GetScale3D();

	FParticleEventInstancePayload* EventPayload = NULL;
	if (LODLevel->EventGenerator)
	{
		EventPayload = (FParticleEventInstancePayload*)(Owner->GetModuleInstanceData(LODLevel->EventGenerator));
		if (EventPayload && 
			(EventPayload->bCollisionEventsPresent == false) && 
			(EventPayload->bDeathEventsPresent == false))
		{
			EventPayload = NULL;
		}
	}

	FParticleCollisionInstancePayload* CollisionInstPayload = (FParticleCollisionInstancePayload*)(Owner->GetModuleInstanceData(this));

	const TArray<FVector>& PlayerLocations = Owner->Component->GetPlayerLocations();
	TArray<float> PlayerLODDistanceFactor = Owner->Component->GetPlayerLODDistanceFactor();	//Make a copy because we need to square it later
	const int32 PlayerCount = PlayerLocations.Num();

	if (World->IsGameWorld())
	{
		bool bIgnoreAllCollision = false;

		// LOD collision based on visibility
		// This is at the 'emitter instance' level as it will be true or false for the whole instance...
		if (bCollideOnlyIfVisible && ((World->TimeSeconds - Owner->Component->LastRenderTime) > 0.1f))
		{
			// no collision if not recently rendered
			bIgnoreAllCollision = true;
		}
		else
		{
			// If the MaxCollisionDistance is greater than WORLD_MAX, they obviously want the check disabled...
			if (MaxCollisionDistance < WORLD_MAX)
			{
				// If we have at least a few particles, do a simple check vs. the bounds
				if (Owner->ActiveParticles > 7)
				{
					if (CollisionInstPayload->CurrentLODBoundsCheckCount == 0)
					{
						FBox BoundingBox;
						BoundingBox.Init();
						if (Owner->Component->Template && Owner->Component->Template->bUseFixedRelativeBoundingBox)
						{
							BoundingBox = Owner->Component->Template->FixedRelativeBoundingBox.TransformBy(OwnerTM);
						}
						else
						{
							// A frame behind, but shouldn't be an issue...
							BoundingBox = Owner->Component->GetAsyncBounds().GetBox();
						}

						// see if any player is within the extended bounds...
						bIgnoreAllCollision = true;
						// Check for the system itself beyond beyond the bounds
						// LOD collision by distance
						bool bCloseEnough = false;
						for (int32 PlyrIdx = 0; PlyrIdx < PlayerCount; PlyrIdx++)
						{
							// Invert the LOD distance factor here because we are using it to *expand* the 
							// bounds rather than shorten the distance checked as it is usually used for.
							float InvDistanceFactor = 1.0f / PlayerLODDistanceFactor[PlyrIdx];
							FBox CheckBounds = BoundingBox;
							float BoxExpansionValue = MaxCollisionDistance * InvDistanceFactor;
							BoxExpansionValue += BoxExpansionValue * 0.075f;
							// Expand it by the max collision distance (and a little bit extra)
							CheckBounds = CheckBounds.ExpandBy(BoxExpansionValue);
							if (CheckBounds.IsInside(PlayerLocations[PlyrIdx]))
							{
								// If one is close enough, that's all it takes!
								bCloseEnough = true;
								break;
							}
						}
						if (bCloseEnough == true)
						{
							bIgnoreAllCollision = false;
						}
					}
					CollisionInstPayload->CurrentLODBoundsCheckCount++;
					// Every 30 frames recheck the overall bounds...
					if (CollisionInstPayload->CurrentLODBoundsCheckCount > 30)
					{
						CollisionInstPayload->CurrentLODBoundsCheckCount = 0;
					}
				}
			}
		}

		if (bIgnoreAllCollision == true)
		{
			// Turn off collision on *all* existing particles...
			// We don't want it to turn back on and have particles 
			// already embedded start performing collision checks.
			BEGIN_UPDATE_LOOP;
			{
				Particle.Flags |= STATE_Particle_IgnoreCollisions;
			}
			END_UPDATE_LOOP;
			return;
		}

		// Square the LODDistanceFactor values now, so we don't have to do it
		// per particle in the update loop below...
		for (int32 SquareIdx = 0; SquareIdx < PlayerLocations.Num(); SquareIdx++)
		{
			PlayerLODDistanceFactor[SquareIdx] *= PlayerLODDistanceFactor[SquareIdx];
		}
	}

	float SquaredMaxCollisionDistance = FMath::Square(MaxCollisionDistance);
	BEGIN_UPDATE_LOOP;
	{
		if ((Particle.Flags & STATE_Particle_CollisionIgnoreCheck) != 0)
		{
			CONTINUE_UPDATE_LOOP;
		}

		PARTICLE_ELEMENT(FParticleCollisionPayload, CollisionPayload);
		if ((Particle.Flags & STATE_Particle_DelayCollisions) != 0)
		{
			if (CollisionPayload.Delay > Particle.RelativeTime)
			{
				CONTINUE_UPDATE_LOOP;
			}
			Particle.Flags &= ~STATE_Particle_DelayCollisions;
		}

		FVector			Location;
		FVector			OldLocation;

		// Location won't be calculated till after tick so we need to calculate an intermediate one here.
		Location	= Particle.Location + Particle.Velocity * DeltaTime;
		if (LODLevel->RequiredModule->bUseLocalSpace)
		{
			// Transform the location and old location into world space
			Location		= OwnerTM.TransformPosition(Location);
			OldLocation		= OwnerTM.TransformPosition(Particle.OldLocation);
		}
		else
		{
			OldLocation	= Particle.OldLocation;
		}
		FVector	Direction = (Location - OldLocation).GetSafeNormal();

		// Determine the size
		FVector Size = Particle.Size * ParentScale;
		FVector	Extent(0.0f);

		// Setup extent for mesh particles. 
		UParticleModuleTypeDataMesh* MeshType = Cast<UParticleModuleTypeDataMesh>(LODLevel->TypeDataModule);
		if (MeshType && MeshType->Mesh)
		{
			Extent = MeshType->Mesh->GetBounds().BoxExtent;
			Extent = MeshType->bCollisionsConsiderPartilceSize ? Extent * Size : Extent;
		}
		
		FHitResult Hit;

		Hit.Normal.X = 0.0f;
		Hit.Normal.Y = 0.0f;
		Hit.Normal.Z = 0.0f;

		check( Owner->Component );

		FVector End = Location + Direction * Size / DirScalar;

		if ((World->IsGameWorld() == true) && (MaxCollisionDistance < WORLD_MAX))
		{
			// LOD collision by distance
			bool bCloseEnough = false;
			for (int32 CheckIdx = 0; CheckIdx < PlayerCount; CheckIdx++)
			{
				float CheckValue = (PlayerLocations[CheckIdx] - End).SizeSquared() * PlayerLODDistanceFactor[CheckIdx];
				if (CheckValue < SquaredMaxCollisionDistance)
				{
					bCloseEnough = true;
					break;
				}
			}
			if (bCloseEnough == false)
			{
				Particle.Flags |= STATE_Particle_IgnoreCollisions;
				CONTINUE_UPDATE_LOOP;
			}
		}

		AActor* IgnoreActor = bIgnoreSourceActor ? Actor : NULL;

		if (PerformCollisionCheck(Owner, &Particle, Hit, IgnoreActor, End, OldLocation, Extent))
		{
			bool bDecrementMaxCount = true;
			bool bIgnoreCollision = false;
			if (Hit.GetActor())
			{
				bDecrementMaxCount = !bPawnsDoNotDecrementCount || !Cast<APawn>(Hit.GetActor());
				bIgnoreCollision = bIgnoreTriggerVolumes && Hit.GetActor()->IsA(ATriggerBase::StaticClass());
				//@todo.SAS. Allow for PSys to say what it wants to collide w/?
			}

			if (bIgnoreCollision == false)
			{
				if (bDecrementMaxCount && (bOnlyVerticalNormalsDecrementCount == true))
				{
					if ((Hit.Normal.IsNearlyZero() == false) && (FMath::Abs(Hit.Normal.Z) + VerticalFudgeFactor) < 1.0f)
					{
						//UE_LOG(LogParticles, Log, TEXT("Particle from %s had a non-vertical hit!"), *(Owner->Component->Template->GetPathName()));
						bDecrementMaxCount = false;
					}
				}

				if (bDecrementMaxCount)
				{
					CollisionPayload.UsedCollisions--;
				}

				if (CollisionPayload.UsedCollisions > 0)
				{
					if (LODLevel->RequiredModule->bUseLocalSpace)
					{
						// Transform the particle velocity to world space
						FVector OldVelocity		= OwnerTM.TransformVector(Particle.Velocity);
						FVector	BaseVelocity	= OwnerTM.TransformVector(Particle.BaseVelocity);
						BaseVelocity			= BaseVelocity.MirrorByVector(Hit.Normal) * CollisionPayload.UsedDampingFactor;

						Particle.BaseVelocity		= OwnerTM.InverseTransformVector(BaseVelocity);
						Particle.BaseRotationRate	= Particle.BaseRotationRate * CollisionPayload.UsedDampingFactorRotation.X;
						if (bMeshRotationActive && MeshRotationOffset > 0)
						{
							FMeshRotationPayloadData* PayloadData = (FMeshRotationPayloadData*)((uint8*)&Particle + MeshRotationOffset);
							PayloadData->RotationRateBase *= CollisionPayload.UsedDampingFactorRotation;
						}

						// Reset the current velocity and manually adjust location to bounce off based on normal and time of collision.
						FVector NewVelocity	= Direction.MirrorByVector(Hit.Normal) * (Location - OldLocation).Size() * CollisionPayload.UsedDampingFactor;
						Particle.Velocity		= FVector::ZeroVector;

						// New location
						FVector	NewLocation		= Location + NewVelocity * (1.f - Hit.Time);
						Particle.Location		= OwnerTM.InverseTransformPosition(NewLocation);

						if (bApplyPhysics)
						{
							check(IsInGameThread());
							UPrimitiveComponent* PrimitiveComponent = Hit.Component.Get();
							if(PrimitiveComponent && PrimitiveComponent->IsAnySimulatingPhysics())
							{
								FVector vImpulse;
								vImpulse = -(NewVelocity - OldVelocity) * ParticleMass.GetValue(Particle.RelativeTime, Owner->Component);
								PrimitiveComponent->AddImpulseAtLocation(vImpulse, Hit.Location, Hit.BoneName);
							}
						}
					}
					else
					{
						FVector vOldVelocity = Particle.Velocity;

						// Reflect base velocity and apply damping factor.
						Particle.BaseVelocity		= Particle.BaseVelocity.MirrorByVector(Hit.Normal) * CollisionPayload.UsedDampingFactor;
						Particle.BaseRotationRate	= Particle.BaseRotationRate * CollisionPayload.UsedDampingFactorRotation.X;
						if (bMeshRotationActive && MeshRotationOffset > 0)
						{
							FMeshRotationPayloadData* PayloadData = (FMeshRotationPayloadData*)((uint8*)&Particle + MeshRotationOffset);
							PayloadData->RotationRateBase *= CollisionPayload.UsedDampingFactorRotation;
						}

						// Reset the current velocity and manually adjust location to bounce off based on normal and time of collision.
						FVector vNewVelocity	= Direction.MirrorByVector(Hit.Normal) * (Location - OldLocation).Size() * CollisionPayload.UsedDampingFactor;
						Particle.Velocity		= FVector::ZeroVector;
						Particle.Location	   += vNewVelocity * (1.f - Hit.Time);

						if (bApplyPhysics)
						{
							check(IsInGameThread());
							UPrimitiveComponent* PrimitiveComponent = Hit.Component.Get();
							if(PrimitiveComponent && PrimitiveComponent->IsAnySimulatingPhysics())
							{
								FVector vImpulse;
								vImpulse = -(vNewVelocity - vOldVelocity) * ParticleMass.GetValue(Particle.RelativeTime, Owner->Component);
								PrimitiveComponent->AddImpulseAtLocation(vImpulse, Hit.Location, Hit.BoneName);
							}
						}
					}

					if (EventPayload && (EventPayload->bCollisionEventsPresent == true))
					{
						LODLevel->EventGenerator->HandleParticleCollision(Owner, EventPayload, &CollisionPayload, &Hit, &Particle, Direction);
					}
				}
				else
				{
					if (LODLevel->RequiredModule->bUseLocalSpace == true)
					{
						Size = OwnerTM.TransformVector(Size);
					}
					Particle.Location = Hit.Location + (Size / 2.0f);
					if (LODLevel->RequiredModule->bUseLocalSpace == true)
					{
						// We need to transform the location back relative to the PSys.
						// NOTE: LocalSpace makes sense only for stationary emitters that use collision.
						Particle.Location = OwnerTM.InverseTransformPosition(Particle.Location);
					}
					switch (CollisionCompletionOption)
					{
					case EPCC_Kill:
						{
							if (EventPayload && (EventPayload->bDeathEventsPresent == true))
							{
								LODLevel->EventGenerator->HandleParticleKilled(Owner, EventPayload, &Particle);
							}
							KILL_CURRENT_PARTICLE;
						}
						break;
					case EPCC_Freeze:
						{
							Particle.Flags |= STATE_Particle_Freeze;
						}
						break;
					case EPCC_HaltCollisions:
						{
							Particle.Flags |= STATE_Particle_IgnoreCollisions;
						}
						break;
					case EPCC_FreezeTranslation:
						{
							Particle.Flags |= STATE_Particle_FreezeTranslation;
						}
						break;
					case EPCC_FreezeRotation:
						{
							Particle.Flags |= STATE_Particle_FreezeRotation;
						}
						break;
					case EPCC_FreezeMovement:
						{
							Particle.Flags |= STATE_Particle_FreezeRotation;
							Particle.Flags |= STATE_Particle_FreezeTranslation;
						}
						break;
					}

					if (EventPayload && (EventPayload->bCollisionEventsPresent == true))
					{
						LODLevel->EventGenerator->HandleParticleCollision(Owner, EventPayload, &CollisionPayload, &Hit, &Particle, Direction);
					}
				}
				Particle.Flags |= STATE_Particle_CollisionHasOccurred;
			}
		}
	}
	END_UPDATE_LOOP;
}
void ASpacePartioner::Initialize(const FBox& inNewBounds, const bool& inDrawDebugInfo)
{
	bInitialized = true;
	bDrawDebugInfo = inDrawDebugInfo;
	OctreeData = new FSimpleOctree(inNewBounds.GetCenter(), inNewBounds.GetExtent().GetMax()); // const FVector & InOrigin, float InExtent
}
示例#7
0
FORCEINLINE_DEBUGGABLE bool FNavMeshPath::DoesPathIntersectBoxImplementation(const FBox& Box, const FVector& StartLocation, uint32 StartingIndex, int32* IntersectingSegmentIndex) const
{
	bool bIntersects = false;	
	const TArray<FNavigationPortalEdge>& CorridorEdges = GetPathCorridorEdges();

	// note that it's a bit simplified. It works
	if (CorridorEdges.IsValidIndex(StartingIndex))
	{
		FVector Start = StartLocation;
		for (int32 PortalIndex = StartingIndex; PortalIndex < CorridorEdges.Num(); ++PortalIndex)
		{
			const FNavigationPortalEdge& Edge = CorridorEdges[PortalIndex];
			const FVector End = Edge.Right + (Edge.Left - Edge.Right) / 2;
			if (FVector::DistSquared(Start, End) > SMALL_NUMBER)
			{
				const FVector Direction = (End - Start);
				if (FMath::LineBoxIntersection(Box, Start, End, Direction))
				{
					bIntersects = true;
					if (IntersectingSegmentIndex != NULL)
					{
						*IntersectingSegmentIndex = PortalIndex;
					}
					break;
				}
			}

			Start = End;
		}

		// test the last portal->path end line
		if (bIntersects == false)
		{
			ensure(PathPoints.Num() == 2);
			const FVector End = PathPoints.Last().Location;
			if (FVector::DistSquared(StartLocation, End) > SMALL_NUMBER)
			{
				const FVector Direction = (End - Start);
				if (FMath::LineBoxIntersection(Box, Start, End, Direction))
				{
					bIntersects = true;
					if (IntersectingSegmentIndex != NULL)
					{
						*IntersectingSegmentIndex = CorridorEdges.Num();
					}
				}
			}
		}
	}
	
	// just check if path's end is inside the tested box
	if (bIntersects == false && Box.IsInside(PathPoints.Last().Location))
	{
		bIntersects = true;
		if (IntersectingSegmentIndex != NULL)
		{
			*IntersectingSegmentIndex = CorridorEdges.Num();
		}
	}

	return bIntersects;
}
void AGameplayDebuggerReplicator::DrawDebugData(class UCanvas* Canvas, class APlayerController* PC, bool bHideMenu)
{
#if ENABLED_GAMEPLAY_DEBUGGER
	if (!LocalPlayerOwner && IsActorTickEnabled())
	{
		return;
	}

	const bool bAllowToDraw = Canvas && Canvas->SceneView && (Canvas->SceneView->ViewActor == LocalPlayerOwner->AcknowledgedPawn || Canvas->SceneView->ViewActor == LocalPlayerOwner->GetPawnOrSpectator());
	if (!bAllowToDraw)
	{
		// check for spectator debug camera during debug camera
		if (DebugCameraController.IsValid() == false || Canvas->SceneView->ViewActor->GetInstigatorController() != DebugCameraController.Get())
		{
			return;
		}
	}

	const float DebugInfoStartX = UGameplayDebuggerModuleSettings::StaticClass()->GetDefaultObject<UGameplayDebuggerModuleSettings>()->DebugInfoStart.X;
	const float DebugInfoStartY = UGameplayDebuggerModuleSettings::StaticClass()->GetDefaultObject<UGameplayDebuggerModuleSettings>()->DebugInfoStart.Y;
	const FVector SelectedActorLoc = LastSelectedActorToDebug ? LastSelectedActorToDebug->GetActorLocation() + FVector(0, 0, LastSelectedActorToDebug->GetSimpleCollisionHalfHeight()) : DebugTools::InvalidLocation;
	
	UGameplayDebuggerHelper::FPrintContext DefaultContext(GEngine->GetSmallFont(), Canvas, DebugInfoStartX, DebugInfoStartY);
	DefaultContext.FontRenderInfo.bEnableShadow = true;
	const bool bDrawFullData = SelectedActorLoc != DebugTools::InvalidLocation;
	const FVector ScreenLoc = SelectedActorLoc != DebugTools::InvalidLocation ? UGameplayDebuggerHelper::ProjectLocation(DefaultContext, SelectedActorLoc) : FVector::ZeroVector;
	UGameplayDebuggerHelper::FPrintContext OverHeadContext(GEngine->GetSmallFont(), Canvas, ScreenLoc.X, ScreenLoc.Y);

	UGameplayDebuggerHelper::SetOverHeadContext(OverHeadContext);
	UGameplayDebuggerHelper::SetDefaultContext(DefaultContext);

	if (DefaultContext.Canvas != nullptr)
	{
		float XL, YL;
		const FString ToolName = FString::Printf(TEXT("Gameplay Debugger [Timestamp: %05.03f]"), GetWorld()->TimeSeconds);
		UGameplayDebuggerHelper::CalulateStringSize(DefaultContext, nullptr, ToolName, XL, YL);
		UGameplayDebuggerHelper::PrintString(DefaultContext, FColorList::White, ToolName, DefaultContext.Canvas->ClipX / 2.0f - XL / 2.0f, 0);
	}

	if (!bHideMenu)
	{
		DrawMenu(DefaultContext, OverHeadContext);
	}

	TMap<FString, TArray<UGameplayDebuggerBaseObject*> > CategoryToClasses;
	for (UGameplayDebuggerBaseObject* Obj : ReplicatedObjects)
	{
		if (Obj)
		{
			FString Category = Obj->GetCategoryName();
			CategoryToClasses.FindOrAdd(Category).Add(Obj);
		}
	}
	CategoryToClasses.KeySort(TLess<FString>());

	for (auto It(CategoryToClasses.CreateIterator()); It; ++It)
	{
		const FGameplayDebuggerCategorySettings* Element = Categories.FindByPredicate([&](const FGameplayDebuggerCategorySettings& C){ return It.Key() == C.CategoryName; });
		if (Element == nullptr || Element->bPIE == false)
		{
			continue;
		}

		UGameplayDebuggerHelper::PrintString(UGameplayDebuggerHelper::GetDefaultContext(), FString::Printf(TEXT("\n{R=0,G=255,B=0,A=255}%s\n"), *It.Key()));
		TArray<UGameplayDebuggerBaseObject*>& CurrentObjects = It.Value();
		for (UGameplayDebuggerBaseObject* Obj : CurrentObjects)
		{
			Obj->DrawCollectedData(LocalPlayerOwner, LastSelectedActorToDebug);
		}
	}

	const IConsoleVariable* cvarHighlightSelectedActor = IConsoleManager::Get().FindConsoleVariable(TEXT("ai.gd.HighlightSelectedActor"));
	const bool bHighlightSelectedActor = !cvarHighlightSelectedActor || cvarHighlightSelectedActor->GetInt();
	if (LastSelectedActorToDebug && bHighlightSelectedActor)
	{
		FBox ComponentsBoundingBox = LastSelectedActorToDebug->GetComponentsBoundingBox(false);
		DrawDebugBox(GetWorld(), ComponentsBoundingBox.GetCenter(), ComponentsBoundingBox.GetExtent(), FColor::Red, false);
		DrawDebugSolidBox(GetWorld(), ComponentsBoundingBox.GetCenter(), ComponentsBoundingBox.GetExtent(), FColor::Red.WithAlpha(25));
	}
#endif
}
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
}
void UGameplayDebuggingComponent::ServerCollectNavmeshData_Implementation(FVector_NetQuantize10 TargetLocation)
{
#if WITH_RECAST
    UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
    ARecastNavMesh* NavData = GetNavData();
    if (NavData == NULL)
    {
        NavmeshRepData.Empty();
        return;
    }

    const double Timer1 = FPlatformTime::Seconds();

    TArray<int32> Indices;
    int32 TileX = 0;
    int32 TileY = 0;
    const int32 DeltaX[] = { 0, 1, 1, 0, -1, -1, -1,  0,  1 };
    const int32 DeltaY[] = { 0, 0, 1, 1,  1,  0, -1, -1, -1 };

    NavData->BeginBatchQuery();

    // add 3x3 neighborhood of target
    int32 TargetTileX = 0;
    int32 TargetTileY = 0;
    NavData->GetNavMeshTileXY(TargetLocation, TargetTileX, TargetTileY);
    for (int32 i = 0; i < ARRAY_COUNT(DeltaX); i++)
    {
        const int32 NeiX = TargetTileX + DeltaX[i];
        const int32 NeiY = TargetTileY + DeltaY[i];
        if (FMath::Abs(NeiX - TileX) > 1 || FMath::Abs(NeiY - TileY) > 1)
        {
            NavData->GetNavMeshTilesAt(NeiX, NeiY, Indices);
        }
    }

    const FNavDataConfig& NavConfig = NavData->GetConfig();
    FColor NavMeshColors[RECAST_MAX_AREAS];
    NavMeshColors[RECAST_DEFAULT_AREA] = NavConfig.Color.DWColor() > 0 ? NavConfig.Color : NavMeshRenderColor_RecastMesh;
    for (uint8 i = 0; i < RECAST_DEFAULT_AREA; ++i)
    {
        NavMeshColors[i] = NavData->GetAreaIDColor(i);
    }

    TArray<uint8> UncompressedBuffer;
    FMemoryWriter ArWriter(UncompressedBuffer);

    int32 NumIndices = Indices.Num();
    ArWriter << NumIndices;

    for (int32 i = 0; i < NumIndices; i++)
    {
        FRecastDebugGeometry NavMeshGeometry;
        NavMeshGeometry.bGatherPolyEdges = false;
        NavMeshGeometry.bGatherNavMeshEdges = false;
        NavData->GetDebugGeometry(NavMeshGeometry, Indices[i]);

        NavMeshDebug::FTileData TileData;
        const FBox TileBoundingBox = NavData->GetNavMeshTileBounds(Indices[i]);
        TileData.Location = TileBoundingBox.GetCenter();
        for (int32 VertIndex = 0; VertIndex < NavMeshGeometry.MeshVerts.Num(); ++VertIndex)
        {
            const NavMeshDebug::FShortVector SV = NavMeshGeometry.MeshVerts[VertIndex] - TileData.Location;
            TileData.Verts.Add(SV);
        }

        for (int32 iArea = 0; iArea < RECAST_MAX_AREAS; iArea++)
        {
            const int32 NumTris = NavMeshGeometry.AreaIndices[iArea].Num();
            if (NumTris)
            {
                NavMeshDebug::FAreaPolys AreaPolys;
                for (int32 AreaIndicesIndex = 0; AreaIndicesIndex < NavMeshGeometry.AreaIndices[iArea].Num(); ++AreaIndicesIndex)
                {
                    AreaPolys.Indices.Add(NavMeshGeometry.AreaIndices[iArea][AreaIndicesIndex]);
                }
                AreaPolys.Color = NavMeshColors[iArea];
                TileData.Areas.Add(AreaPolys);
            }
        }

        TileData.Links.Reserve(NavMeshGeometry.OffMeshLinks.Num());
        for (int32 iLink = 0; iLink < NavMeshGeometry.OffMeshLinks.Num(); iLink++)
        {
            const FRecastDebugGeometry::FOffMeshLink& SrcLink = NavMeshGeometry.OffMeshLinks[iLink];
            NavMeshDebug::FOffMeshLink Link;
            Link.Left = SrcLink.Left - TileData.Location;
            Link.Right = SrcLink.Right - TileData.Location;
            Link.Color = ((SrcLink.Direction && SrcLink.ValidEnds) || (SrcLink.ValidEnds & FRecastDebugGeometry::OMLE_Left)) ? DarkenColor(NavMeshColors[SrcLink.AreaID]) : NavMeshRenderColor_OffMeshConnectionInvalid;
            Link.PackedFlags.Radius = (int8)SrcLink.Radius;
            Link.PackedFlags.Direction = SrcLink.Direction;
            Link.PackedFlags.ValidEnds = SrcLink.ValidEnds;
            TileData.Links.Add(Link);
        }

        ArWriter << TileData;
    }
    NavData->FinishBatchQuery();

    const double Timer2 = FPlatformTime::Seconds();

    const int32 HeaderSize = sizeof(int32);
    NavmeshRepData.Init(0, HeaderSize + FMath::TruncToInt(1.1f * UncompressedBuffer.Num()));

    const int32 UncompressedSize = UncompressedBuffer.Num();
    int32 CompressedSize = NavmeshRepData.Num() - HeaderSize;
    uint8* DestBuffer = NavmeshRepData.GetData();
    FMemory::Memcpy(DestBuffer, &UncompressedSize, HeaderSize);
    DestBuffer += HeaderSize;

    FCompression::CompressMemory((ECompressionFlags)(COMPRESS_ZLIB | COMPRESS_BiasMemory),
                                 (void*)DestBuffer, CompressedSize, (void*)UncompressedBuffer.GetData(), UncompressedSize);

    NavmeshRepData.SetNum(CompressedSize + HeaderSize, false);

    const double Timer3 = FPlatformTime::Seconds();
    UE_LOG(LogGDT, Log, TEXT("Preparing navmesh data: %.1fkB took %.3fms (collect: %.3fms + compress %d%%: %.3fms)"),
           NavmeshRepData.Num() / 1024.0f, 1000.0f * (Timer3 - Timer1),
           1000.0f * (Timer2 - Timer1),
           FMath::TruncToInt(100.0f * NavmeshRepData.Num() / UncompressedBuffer.Num()), 1000.0f * (Timer3 - Timer2));
#endif

    if (World && World->GetNetMode() != NM_DedicatedServer)
    {
        OnRep_UpdateNavmesh();
    }
}
示例#11
0
inline FString convert(FBox& x)
{
  return x.GetSize().ToString();
}
void UNavMeshRenderingComponent::GatherData(struct FNavMeshSceneProxyData* CurrentData) const
{
#if WITH_RECAST
	const ARecastNavMesh* NavMesh = Cast<ARecastNavMesh>(GetOwner());

	if (CurrentData && NavMesh && NavMesh->bEnableDrawing && (!NavMesh->bShowOnlyDefaultAgent || NavMesh->IsSupportingDefaultAgent()))
	{
		FHitProxyId HitProxyId = FHitProxyId();
		CurrentData->Reset();
		CurrentData->bEnableDrawing = NavMesh->bEnableDrawing;
		CurrentData->bShowOnlyDefaultAgent = NavMesh->bShowOnlyDefaultAgent;
		CurrentData->bSupportsDefaultAgent = NavMesh->IsSupportingDefaultAgent();
		CurrentData->bDrawPathCollidingGeometry = NavMesh->bDrawPathCollidingGeometry;

		CurrentData->NavMeshDrawOffset.Z = NavMesh->DrawOffset;
		CurrentData->NavMeshGeometry.bGatherPolyEdges = NavMesh->bDrawPolyEdges;
		CurrentData->NavMeshGeometry.bGatherNavMeshEdges = NavMesh->bDrawNavMeshEdges;

		const FNavDataConfig& NavConfig = NavMesh->GetConfig();
		CurrentData->NavMeshColors[RECAST_DEFAULT_AREA] = NavConfig.Color.DWColor() > 0 ? NavConfig.Color : NavMeshRenderColor_RecastMesh;
		for (uint8 i = 0; i < RECAST_DEFAULT_AREA; ++i)
		{
			CurrentData->NavMeshColors[i] = NavMesh->GetAreaIDColor(i);
		}

		// just a little trick to make sure navmeshes with different sized are not drawn with same offset
		CurrentData->NavMeshDrawOffset.Z += NavMesh->GetConfig().AgentRadius / 10.f;

		NavMesh->BeginBatchQuery();
		NavMesh->GetDebugGeometry(CurrentData->NavMeshGeometry);
		const TArray<FVector>& MeshVerts = CurrentData->NavMeshGeometry.MeshVerts;

		// @fixme, this is going to double up on lots of interior lines
		if (NavMesh->bDrawTriangleEdges)
		{
			for (int32 AreaIdx = 0; AreaIdx < RECAST_MAX_AREAS; ++AreaIdx)
			{
				const TArray<int32>& MeshIndices = CurrentData->NavMeshGeometry.AreaIndices[AreaIdx];
				for (int32 Idx=0; Idx<MeshIndices.Num(); Idx += 3)
				{
					CurrentData->BatchedElements.AddLine(MeshVerts[MeshIndices[Idx+0]] + CurrentData->NavMeshDrawOffset, MeshVerts[MeshIndices[Idx+1]] + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TriangleEdges,HitProxyId,DefaultEdges_LineThickness, 0, true);
					CurrentData->BatchedElements.AddLine(MeshVerts[MeshIndices[Idx+1]] + CurrentData->NavMeshDrawOffset, MeshVerts[MeshIndices[Idx+2]] + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TriangleEdges,HitProxyId,DefaultEdges_LineThickness, 0, true);
					CurrentData->BatchedElements.AddLine(MeshVerts[MeshIndices[Idx+2]] + CurrentData->NavMeshDrawOffset, MeshVerts[MeshIndices[Idx+0]] + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TriangleEdges,HitProxyId,DefaultEdges_LineThickness, 0, true);
				}
			}
		}

		// make lines for tile edges
		if (NavMesh->bDrawPolyEdges)
		{
			const TArray<FVector>& TileEdgeVerts = CurrentData->NavMeshGeometry.PolyEdges;
			for (int32 Idx=0; Idx < TileEdgeVerts.Num(); Idx += 2)
			{
				CurrentData->TileEdgeLines.Add( FDebugRenderSceneProxy::FDebugLine(TileEdgeVerts[Idx] + CurrentData->NavMeshDrawOffset, TileEdgeVerts[Idx+1] + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TileEdges));
			}
		}

		// make lines for navmesh edges
		if (NavMesh->bDrawNavMeshEdges)
		{
			const FColor EdgesColor = DarkenColor(CurrentData->NavMeshColors[RECAST_DEFAULT_AREA]);
			const TArray<FVector>& NavMeshEdgeVerts = CurrentData->NavMeshGeometry.NavMeshEdges;
			for (int32 Idx=0; Idx < NavMeshEdgeVerts.Num(); Idx += 2)
			{
				CurrentData->NavMeshEdgeLines.Add( FDebugRenderSceneProxy::FDebugLine(NavMeshEdgeVerts[Idx] + CurrentData->NavMeshDrawOffset, NavMeshEdgeVerts[Idx+1] + CurrentData->NavMeshDrawOffset, EdgesColor));
			}
		}

		if (NavMesh->bDrawTileBounds)
		{
			FBox OuterBBox;
			int32 NumTilesX, NumTilesY;
			NavMesh->GetDebugTileBounds(OuterBBox, NumTilesX, NumTilesY);

			float DrawZ = (OuterBBox.Min.Z + OuterBBox.Max.Z) * 0.5f;		// @hack average
			FVector LL(OuterBBox.Min.X, OuterBBox.Min.Y, DrawZ);
			FVector UR(OuterBBox.Max.X, OuterBBox.Max.Y, DrawZ);
			FVector UL(LL.X, UR.Y, DrawZ);
			FVector LR(UR.X, LL.Y, DrawZ);
			CurrentData->BatchedElements.AddLine(LL, UL, NavMeshRenderColor_TileBounds,HitProxyId,DefaultEdges_LineThickness, 0, true);
			CurrentData->BatchedElements.AddLine(UL, UR, NavMeshRenderColor_TileBounds,HitProxyId,DefaultEdges_LineThickness, 0, true);
			CurrentData->BatchedElements.AddLine(UR, LR, NavMeshRenderColor_TileBounds,HitProxyId,DefaultEdges_LineThickness, 0, true);
			CurrentData->BatchedElements.AddLine(LR, LL, NavMeshRenderColor_TileBounds,HitProxyId,DefaultEdges_LineThickness, 0, true);

			float const TileSizeX = (LL.X - LR.X) / NumTilesX;
			float const TileSizeY = (LL.Y - UL.Y) / NumTilesY;

			for (int32 Idx=1; Idx<NumTilesX; ++Idx)
			{
				float const DrawX = FMath::Lerp(LL.X, LR.X, (float)Idx/(float)NumTilesX);
				CurrentData->BatchedElements.AddLine(FVector(DrawX, LL.Y, DrawZ), FVector(DrawX, UL.Y, DrawZ), NavMeshRenderColor_TileBounds,HitProxyId,DefaultEdges_LineThickness, 0, true);
			}
			for (int32 Idx=1; Idx<NumTilesY; ++Idx)
			{
				float const DrawY = FMath::Lerp(LL.Y, UL.Y, (float)Idx/(float)NumTilesY);
				CurrentData->BatchedElements.AddLine(FVector(LL.X, DrawY, DrawZ), FVector(LR.X, DrawY, DrawZ), NavMeshRenderColor_TileBounds,HitProxyId,DefaultEdges_LineThickness, 0, true);
			}
		}

		if (NavMesh->bDrawPathCollidingGeometry)
		{
			// draw all geometry gathered in navoctree
			const FNavigationOctree* NavOctree = NavMesh->GetWorld()->GetNavigationSystem()->GetNavOctree();

			for (FNavigationOctree::TConstIterator<> It(*NavOctree); It.HasPendingNodes(); It.Advance())
			{
				const FNavigationOctree::FNode& Node = It.GetCurrentNode();
				for (FNavigationOctree::ElementConstIt ElementIt(Node.GetElementIt()); ElementIt; ElementIt++)
				{
					const FNavigationOctreeElement& Element = *ElementIt;
					if (Element.ShouldUseGeometry(&NavMesh->NavDataConfig) && Element.Data.CollisionData.Num())
					{
						const FRecastGeometryCache CachedGeometry(Element.Data.CollisionData.GetTypedData());
						AppendGeometry(CurrentData->PathCollidingGeomVerts, CurrentData->PathCollidingGeomIndices,
							CachedGeometry.Verts, CachedGeometry.Header.NumVerts, CachedGeometry.Indices, CachedGeometry.Header.NumFaces);
					}
				}
			}
		}
		
		// offset all navigation-link positions
		if (!NavMesh->bDrawClusters)
		{
			for (int32 OffMeshLineIndex = 0; OffMeshLineIndex < CurrentData->NavMeshGeometry.OffMeshLinks.Num(); ++OffMeshLineIndex)
			{
				FRecastDebugGeometry::FOffMeshLink& Link = CurrentData->NavMeshGeometry.OffMeshLinks[OffMeshLineIndex];
				const bool bLinkValid = (Link.ValidEnds & FRecastDebugGeometry::OMLE_Left) && (Link.ValidEnds & FRecastDebugGeometry::OMLE_Right);

				if (NavMesh->bDrawFailedNavLinks || (NavMesh->bDrawNavLinks && bLinkValid))
				{
					const FVector V0 = Link.Left + CurrentData->NavMeshDrawOffset;
					const FVector V1 = Link.Right + CurrentData->NavMeshDrawOffset;
					const FColor LinkColor = ((Link.Direction && Link.ValidEnds) || (Link.ValidEnds & FRecastDebugGeometry::OMLE_Left)) ? DarkenColor(CurrentData->NavMeshColors[Link.AreaID]) : NavMeshRenderColor_OffMeshConnectionInvalid;

					CacheArc(CurrentData->NavLinkLines, V0, V1, 0.4f, 4, LinkColor, LinkLines_LineThickness);

					const FVector VOffset(0, 0, FVector::Dist(V0, V1) * 1.333f);
					CacheArrowHead(CurrentData->NavLinkLines, V1, V0+VOffset, 30.f, LinkColor, LinkLines_LineThickness);
					if (Link.Direction)
					{
						CacheArrowHead(CurrentData->NavLinkLines, V0, V1+VOffset, 30.f, LinkColor, LinkLines_LineThickness);
					}

					// if the connection as a whole is valid check if there are any of ends is invalid
					if (LinkColor != NavMeshRenderColor_OffMeshConnectionInvalid)
					{
						if (Link.Direction && (Link.ValidEnds & FRecastDebugGeometry::OMLE_Left) == 0)
						{
							// left end invalid - mark it
							DrawWireCylinder(CurrentData->NavLinkLines, V0, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), NavMeshRenderColor_OffMeshConnectionInvalid, Link.Radius, NavMesh->AgentMaxStepHeight, 16, 0, DefaultEdges_LineThickness);
						}
						if ((Link.ValidEnds & FRecastDebugGeometry::OMLE_Right) == 0)
						{
							DrawWireCylinder(CurrentData->NavLinkLines, V1, FVector(1,0,0), FVector(0,1,0), FVector(0,0,1), NavMeshRenderColor_OffMeshConnectionInvalid, Link.Radius, NavMesh->AgentMaxStepHeight, 16, 0, DefaultEdges_LineThickness);
						}
					}
				}					
			}
		}

		if (NavMesh->bDrawTileLabels || NavMesh->bDrawPolygonLabels || NavMesh->bDrawDefaultPolygonCost)
		{
			// calculate appropriate points for displaying debug labels
			const int32 TilesCount = NavMesh->GetNavMeshTilesCount();
			CurrentData->DebugLabels.Reserve(TilesCount);

			for (int32 TileIndex = 0; TileIndex < TilesCount; ++TileIndex)
			{
				int32 X = -1;
				int32 Y = -1;
				int32 Layer = -1;
				NavMesh->GetNavMeshTileXY(TileIndex, X, Y, Layer);

				if (X >= 0 && Y >= 0)
				{
					const FBox TileBoundingBox = NavMesh->GetNavMeshTileBounds(TileIndex);
					FVector TileLabelLocation = TileBoundingBox.GetCenter();
					TileLabelLocation.Z = TileBoundingBox.Max.Z;

					FNavLocation NavLocation(TileLabelLocation);
					if (!NavMesh->ProjectPoint(TileLabelLocation, NavLocation, FVector(NavMesh->TileSizeUU/100, NavMesh->TileSizeUU/100, TileBoundingBox.Max.Z-TileBoundingBox.Min.Z)))
					{
						NavMesh->ProjectPoint(TileLabelLocation, NavLocation, FVector(NavMesh->TileSizeUU/2, NavMesh->TileSizeUU/2, TileBoundingBox.Max.Z-TileBoundingBox.Min.Z));
					}

					if (NavMesh->bDrawTileLabels)
					{
						CurrentData->DebugLabels.Add(FNavMeshSceneProxyData::FDebugText(
							/*Location*/NavLocation.Location + CurrentData->NavMeshDrawOffset
							, /*Text*/FString::Printf(TEXT("(%d,%d:%d)"), X, Y, Layer)
							));
					}

					if (NavMesh->bDrawPolygonLabels || NavMesh->bDrawDefaultPolygonCost)
					{
						TArray<FNavPoly> Polys;
						NavMesh->GetPolysInTile(TileIndex, Polys);

						if (NavMesh->bDrawDefaultPolygonCost)
						{
							float DefaultCosts[RECAST_MAX_AREAS];
							float FixedCosts[RECAST_MAX_AREAS];

							NavMesh->GetDefaultQueryFilter()->GetAllAreaCosts(DefaultCosts, FixedCosts, RECAST_MAX_AREAS);

							for(int k = 0; k < Polys.Num(); ++k)
							{
								uint32 AreaID = NavMesh->GetPolyAreaID(Polys[k].Ref);

								CurrentData->DebugLabels.Add(FNavMeshSceneProxyData::FDebugText(
									/*Location*/Polys[k].Center + CurrentData->NavMeshDrawOffset
									, /*Text*/FString::Printf(TEXT("\\%.3f; %.3f\\"), DefaultCosts[AreaID], FixedCosts[AreaID])
									));
							}
						}
						else
						{
							for(int k = 0; k < Polys.Num(); ++k)
							{
								CurrentData->DebugLabels.Add(FNavMeshSceneProxyData::FDebugText(
									/*Location*/Polys[k].Center + CurrentData->NavMeshDrawOffset
									, /*Text*/FString::Printf(TEXT("[%08X]"), Polys[k].Ref)
									));
							}
						}
					}							
				}
			}
		}

		CurrentData->bSkipDistanceCheck = GIsEditor && (GEngine->GetDebugLocalPlayer() == NULL);
		CurrentData->bDrawClusters = NavMesh->bDrawClusters;
		NavMesh->FinishBatchQuery();

		// Draw Mesh
		if (NavMesh->bDrawClusters)
		{
			for (int32 Idx = 0; Idx < CurrentData->NavMeshGeometry.Clusters.Num(); ++Idx)
			{
				const TArray<int32>& MeshIndices = CurrentData->NavMeshGeometry.Clusters[Idx].MeshIndices;

				if (MeshIndices.Num() == 0)
				{
					continue;
				}

				FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
				for (int32 VertIdx=0; VertIdx < MeshVerts.Num(); ++VertIdx)
				{
					AddVertexHelper(DebugMeshData, MeshVerts[VertIdx] + CurrentData->NavMeshDrawOffset);
				}
				for (int32 TriIdx=0; TriIdx < MeshIndices.Num(); TriIdx+=3)
				{
					AddTriangleHelper(DebugMeshData, MeshIndices[TriIdx], MeshIndices[TriIdx+1], MeshIndices[TriIdx+2]);
				}

				DebugMeshData.ClusterColor = GetClusterColor(Idx);
				CurrentData->MeshBuilders.Add(DebugMeshData);
			}
		}
		else
		{			
			for (int32 AreaType = 0; AreaType < RECAST_MAX_AREAS; ++AreaType)
			{
				const TArray<int32>& MeshIndices = CurrentData->NavMeshGeometry.AreaIndices[AreaType];

				if (MeshIndices.Num() == 0)
				{
					continue;
				}

				FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
				for (int32 VertIdx=0; VertIdx < MeshVerts.Num(); ++VertIdx)
				{
					AddVertexHelper(DebugMeshData, MeshVerts[VertIdx] + CurrentData->NavMeshDrawOffset);
				}
				for (int32 TriIdx=0; TriIdx < MeshIndices.Num(); TriIdx+=3)
				{
					AddTriangleHelper(DebugMeshData, MeshIndices[TriIdx], MeshIndices[TriIdx+1], MeshIndices[TriIdx+2]);
				}

				DebugMeshData.ClusterColor = CurrentData->NavMeshColors[AreaType];
				CurrentData->MeshBuilders.Add(DebugMeshData);
			}
		}

		// Draw path generation input geometry
		if (CurrentData->bDrawPathCollidingGeometry)
		{
			FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
			for (int32 VertIdx=0; VertIdx < CurrentData->PathCollidingGeomVerts.Num(); ++VertIdx)
			{
				AddVertexHelper(DebugMeshData, CurrentData->PathCollidingGeomVerts[VertIdx]);
			}
			DebugMeshData.Indices.Append(CurrentData->PathCollidingGeomIndices);
			DebugMeshData.ClusterColor = NavMeshRenderColor_PathCollidingGeom;
			CurrentData->MeshBuilders.Add(DebugMeshData);
		}

		if (CurrentData->NavMeshGeometry.BuiltMeshIndices.Num() > 0)
		{
			FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
			for (int32 VertIdx=0; VertIdx < MeshVerts.Num(); ++VertIdx)
			{
				AddVertexHelper(DebugMeshData, MeshVerts[VertIdx] + CurrentData->NavMeshDrawOffset);
			}
			DebugMeshData.Indices.Append(CurrentData->NavMeshGeometry.BuiltMeshIndices);
			DebugMeshData.ClusterColor = NavMeshRenderColor_RecastTileBeingRebuilt;
			CurrentData->MeshBuilders.Add(DebugMeshData);

			// if we got here it means there's something being built, or is fresh so we better redraw in some time
			FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(
				FSimpleDelegateGraphTask::FDelegate::CreateUObject(NavMesh, &ARecastNavMesh::MarkComponentsRenderStateDirty)
				, TEXT("Requesting navmesh redraw")
				, NULL
				, ENamedThreads::GameThread
				);
		}

		if (NavMesh->bDrawClusters)
		{
			for (int i = 0; i < CurrentData->NavMeshGeometry.ClusterLinks.Num(); i++)
			{
				const FRecastDebugGeometry::FClusterLink& CLink = CurrentData->NavMeshGeometry.ClusterLinks[i];
				const FVector V0 = CLink.FromCluster + CurrentData->NavMeshDrawOffset;
				const FVector V1 = CLink.ToCluster + CurrentData->NavMeshDrawOffset + FVector(0,0,20.0f);

				CacheArc(CurrentData->ClusterLinkLines, V0, V1, 0.4f, 4, FColor::Black, ClusterLinkLines_LineThickness);
				const FVector VOffset(0, 0, FVector::Dist(V0, V1) * 1.333f);
				CacheArrowHead(CurrentData->ClusterLinkLines, V1, V0+VOffset, 30.f, FColor::Black, ClusterLinkLines_LineThickness);
			}
		}

		// cache segment links
		if (NavMesh->bDrawNavLinks)
		{
			for (int32 iArea = 0; iArea < RECAST_MAX_AREAS; iArea++)
			{
				const TArray<int32>& Indices = CurrentData->NavMeshGeometry.OffMeshSegmentAreas[iArea];
				FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
				int32 VertBase = 0;

				for (int32 i = 0; i < Indices.Num(); i++)
				{
					FRecastDebugGeometry::FOffMeshSegment& SegInfo = CurrentData->NavMeshGeometry.OffMeshSegments[Indices[i]];
					const FVector A0 = SegInfo.LeftStart + CurrentData->NavMeshDrawOffset;
					const FVector A1 = SegInfo.LeftEnd + CurrentData->NavMeshDrawOffset;
					const FVector B0 = SegInfo.RightStart + CurrentData->NavMeshDrawOffset;
					const FVector B1 = SegInfo.RightEnd + CurrentData->NavMeshDrawOffset;
					const FVector Edge0 = B0 - A0;
					const FVector Edge1 = B1 - A1;
					const float Len0 = Edge0.Size();
					const float Len1 = Edge1.Size();
					const FColor SegColor = DarkenColor(CurrentData->NavMeshColors[SegInfo.AreaID]);
					const FColor ColA = (SegInfo.ValidEnds & FRecastDebugGeometry::OMLE_Left) ? FColor::White : FColor::Black;
					const FColor ColB = (SegInfo.ValidEnds & FRecastDebugGeometry::OMLE_Right) ? FColor::White : FColor::Black;

					const int32 NumArcPoints = 8;
					const float ArcPtsScale = 1.0f / NumArcPoints;

					FVector Prev0 = EvalArc(A0, Edge0, Len0*0.25f, 0);
					FVector Prev1 = EvalArc(A1, Edge1, Len1*0.25f, 0);
					AddVertexHelper(DebugMeshData, Prev0, ColA);
					AddVertexHelper(DebugMeshData, Prev1, ColA);
					for (int32 j = 1; j <= NumArcPoints; j++)
					{
						const float u = j * ArcPtsScale;
						FVector Pt0 = EvalArc(A0, Edge0, Len0*0.25f, u);
						FVector Pt1 = EvalArc(A1, Edge1, Len1*0.25f, u);

						AddVertexHelper(DebugMeshData, Pt0, (j == NumArcPoints) ? ColB : FColor::White);
						AddVertexHelper(DebugMeshData, Pt1, (j == NumArcPoints) ? ColB : FColor::White);

						AddTriangleHelper(DebugMeshData, VertBase+0, VertBase+2, VertBase+1);
						AddTriangleHelper(DebugMeshData, VertBase+2, VertBase+3, VertBase+1);
						AddTriangleHelper(DebugMeshData, VertBase+0, VertBase+1, VertBase+2);
						AddTriangleHelper(DebugMeshData, VertBase+2, VertBase+1, VertBase+3);

						VertBase += 2;
						Prev0 = Pt0;
						Prev1 = Pt1;
					}
					VertBase += 2;

					DebugMeshData.ClusterColor = SegColor;
				}

				if (DebugMeshData.Indices.Num())
				{
					CurrentData->MeshBuilders.Add(DebugMeshData);
				}
			}
		}

		CurrentData->NavMeshGeometry.PolyEdges.Empty();
		CurrentData->NavMeshGeometry.NavMeshEdges.Empty();
	}
#endif //WITH_RECAST
}
示例#13
0
void AHUD::GetActorsInSelectionRectangle(TSubclassOf<class AActor> ClassFilter, const FVector2D& FirstPoint, const FVector2D& SecondPoint, TArray<AActor*>& OutActors, bool bIncludeNonCollidingComponents, bool bActorMustBeFullyEnclosed)
{
	// Because this is a HUD function it is likely to get called each tick,
	// so make sure any previous contents of the out actor array have been cleared!
	OutActors.Reset();

	//Create Selection Rectangle from Points
	FBox2D SelectionRectangle(0);

	//This method ensures that an appropriate rectangle is generated, 
	//		no matter what the coordinates of first and second point actually are.
	SelectionRectangle += FirstPoint;
	SelectionRectangle += SecondPoint;


	//The Actor Bounds Point Mapping
	const FVector BoundsPointMapping[8] =
	{
		FVector(1, 1, 1),
		FVector(1, 1, -1),
		FVector(1, -1, 1),
		FVector(1, -1, -1),
		FVector(-1, 1, 1),
		FVector(-1, 1, -1),
		FVector(-1, -1, 1),
		FVector(-1, -1, -1)
	};

	//~~~

	//For Each Actor of the Class Filter Type
	for (TActorIterator<AActor> Itr(GetWorld(), ClassFilter); Itr; ++Itr)
	{
		AActor* EachActor = *Itr;

		//Get Actor Bounds				//casting to base class, checked by template in the .h
		const FBox EachActorBounds = Cast<AActor>(EachActor)->GetComponentsBoundingBox(bIncludeNonCollidingComponents); /* All Components? */

		//Center
		const FVector BoxCenter = EachActorBounds.GetCenter();

		//Extents
		const FVector BoxExtents = EachActorBounds.GetExtent();

		// Build 2D bounding box of actor in screen space
		FBox2D ActorBox2D(0);
		for (uint8 BoundsPointItr = 0; BoundsPointItr < 8; BoundsPointItr++)
		{
			// Project vert into screen space.
			const FVector ProjectedWorldLocation = Project(BoxCenter + (BoundsPointMapping[BoundsPointItr] * BoxExtents));
			// Add to 2D bounding box
			ActorBox2D += FVector2D(ProjectedWorldLocation.X, ProjectedWorldLocation.Y);
		}

		//Selection Box must fully enclose the Projected Actor Bounds
		if (bActorMustBeFullyEnclosed)
		{
			if(SelectionRectangle.IsInside(ActorBox2D))
			{
				OutActors.Add(Cast<AActor>(EachActor));
			}
		}
		//Partial Intersection with Projected Actor Bounds
		else
		{
			if (SelectionRectangle.Intersect(ActorBox2D))
			{
				OutActors.Add(Cast<AActor>(EachActor));
			}
		}
	}
}