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); } } }
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 }
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(); } }
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 }
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)); } } } }