void UUserWidget::RemoveFromParent() { if ( FullScreenWidget.IsValid() ) { TSharedPtr<SWidget> WidgetHost = FullScreenWidget.Pin(); // If this is a game world add the widget to the current worlds viewport. UWorld* World = GetWorld(); if ( World && World->IsGameWorld() ) { if ( UGameViewportClient* ViewportClient = World->GetGameViewport() ) { TSharedRef<SWidget> WidgetHostRef = WidgetHost.ToSharedRef(); ViewportClient->RemoveViewportWidgetContent(WidgetHostRef); if ( ULocalPlayer* LocalPlayer = GetOwningLocalPlayer() ) { ViewportClient->RemoveViewportWidgetForPlayer(LocalPlayer, WidgetHostRef); } FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this); } } } else { Super::RemoveFromParent(); } }
void AGameplayDebuggingReplicator::TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction) { Super::TickActor(DeltaTime, TickType, ThisTickFunction); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) UWorld* World = GetWorld(); if (!IsGlobalInWorld() || !World || GetNetMode() == ENetMode::NM_Client || !IGameplayDebugger::IsAvailable()) { // global level replicator don't have any local player and it's prepared to work only on servers return; } UGameInstance* GameInstance = World->GetGameInstance(); if (!GameInstance || !World->IsGameWorld()) { return; } PlayerControllersUpdateDelay -= DeltaTime; if (PlayerControllersUpdateDelay <= 0) { for (FConstPlayerControllerIterator Iterator = World->GetPlayerControllerIterator(); Iterator; Iterator++) { APlayerController* PC = *Iterator; if (PC) { IGameplayDebugger& Debugger = IGameplayDebugger::Get(); Debugger.CreateGameplayDebuggerForPlayerController(PC); } } PlayerControllersUpdateDelay = 5; } #endif }
void ACullDistanceVolume::Destroyed() { Super::Destroyed(); UWorld* World = GetWorld(); if (GIsEditor && World && World->IsGameWorld()) { World->bDoDelayedUpdateCullDistanceVolumes = true; } }
void UWidgetLayoutLibrary::RemoveAllWidgets(UObject* WorldContextObject) { UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject); if ( World && World->IsGameWorld() ) { if ( UGameViewportClient* ViewportClient = World->GetGameViewport() ) { ViewportClient->RemoveAllViewportWidgets(); } } }
void UUserWidget::AddToScreen(ULocalPlayer* Player, int32 ZOrder) { if ( !FullScreenWidget.IsValid() ) { // First create and initialize the variable so that users calling this function twice don't // attempt to add the widget to the viewport again. TSharedRef<SConstraintCanvas> FullScreenCanvas = SNew(SConstraintCanvas); FullScreenWidget = FullScreenCanvas; TSharedRef<SWidget> UserSlateWidget = TakeWidget(); FullScreenCanvas->AddSlot() .Offset(BIND_UOBJECT_ATTRIBUTE(FMargin, GetFullScreenOffset)) .Anchors(BIND_UOBJECT_ATTRIBUTE(FAnchors, GetViewportAnchors)) .Alignment(BIND_UOBJECT_ATTRIBUTE(FVector2D, GetFullScreenAlignment)) [ UserSlateWidget ]; // If this is a game world add the widget to the current worlds viewport. UWorld* World = GetWorld(); if ( World && World->IsGameWorld() ) { if ( UGameViewportClient* ViewportClient = World->GetGameViewport() ) { if ( Player ) { ViewportClient->AddViewportWidgetForPlayer(Player, FullScreenCanvas, ZOrder); } else { // We add 10 to the zorder when adding to the viewport to avoid // displaying below any built-in controls, like the virtual joysticks on mobile builds. ViewportClient->AddViewportWidgetContent(FullScreenCanvas, ZOrder + 10); } // Just in case we already hooked this delegate, remove the handler. FWorldDelegates::LevelRemovedFromWorld.RemoveAll(this); // Widgets added to the viewport are automatically removed if the persistent level is unloaded. FWorldDelegates::LevelRemovedFromWorld.AddUObject(this, &UUserWidget::OnLevelRemovedFromWorld); } } } else { FMessageLog("PIE").Warning(FText::Format(LOCTEXT("WidgetAlreadyOnScreen", "The widget '{0}' was already added to the screen."), FText::FromString(GetClass()->GetName()))); } }
FVector2D UWidgetLayoutLibrary::GetViewportSize(UObject* WorldContextObject) { UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject); if ( World && World->IsGameWorld() ) { if ( UGameViewportClient* ViewportClient = World->GetGameViewport() ) { FVector2D ViewportSize; ViewportClient->GetViewportSize(ViewportSize); return ViewportSize; } } return FVector2D(1, 1); }
float UWidgetLayoutLibrary::GetViewportScale(UObject* WorldContextObject) { UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject); if ( World && World->IsGameWorld() ) { if ( UGameViewportClient* ViewportClient = World->GetGameViewport() ) { FVector2D ViewportSize; ViewportClient->GetViewportSize(ViewportSize); return GetDefault<UUserInterfaceSettings>()->GetDPIScaleBasedOnSize(FIntPoint(ViewportSize.X, ViewportSize.Y)); } } return 1; }
void UTKMathFunctionLibrary::ConvertAnchorToAnchor(UObject* WorldContextObject, FAnchors CurrentAnchor, FMargin Offsets, FAnchors TargetAnchor, FMargin& ConvertedOffsets) { if (CurrentAnchor.Minimum == TargetAnchor.Minimum && CurrentAnchor.Maximum == TargetAnchor.Maximum) { ConvertedOffsets = Offsets; return; } FVector2D View = FVector2D(1, 1); UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject); if (World && World->IsGameWorld()) { if (UGameViewportClient* ViewportClient = World->GetGameViewport()) { ViewportClient->GetViewportSize(View); } } FMargin ZeroAnchorOffsets = Offsets; //Convert to 0,0 anchor first. if (CurrentAnchor.Minimum != FVector2D(0, 0) || CurrentAnchor.Maximum != FVector2D(0, 0)) { ZeroAnchorOffsets.Left = View.X * CurrentAnchor.Minimum.X + Offsets.Left; ZeroAnchorOffsets.Top = View.Y * CurrentAnchor.Minimum.Y + Offsets.Top; if (CurrentAnchor.Minimum.X != CurrentAnchor.Maximum.X) { ZeroAnchorOffsets.Right = View.X * CurrentAnchor.Maximum.X - (Offsets.Right + Offsets.Left); } if (CurrentAnchor.Minimum.Y != CurrentAnchor.Maximum.Y) { ZeroAnchorOffsets.Bottom = View.Y * CurrentAnchor.Maximum.Y - (Offsets.Bottom + Offsets.Top); } if (TargetAnchor.Minimum == FVector2D(0, 0) && TargetAnchor.Maximum == FVector2D(0, 0)) { ConvertedOffsets = ZeroAnchorOffsets; return; } } //Convert 0,0 anchor offsets to target anchor offsets. ConvertedOffsets.Left = (-View.X) * TargetAnchor.Minimum.X + ZeroAnchorOffsets.Left; ConvertedOffsets.Top = (-View.Y) * TargetAnchor.Minimum.Y + ZeroAnchorOffsets.Top; ConvertedOffsets.Right = TargetAnchor.Minimum.X != TargetAnchor.Maximum.X ? View.X * TargetAnchor.Maximum.X - (ZeroAnchorOffsets.Left + ZeroAnchorOffsets.Right) : ZeroAnchorOffsets.Right; ConvertedOffsets.Bottom = TargetAnchor.Minimum.Y != TargetAnchor.Maximum.Y ? View.Y * TargetAnchor.Maximum.Y - (ZeroAnchorOffsets.Top + ZeroAnchorOffsets.Bottom) : ZeroAnchorOffsets.Bottom; }
void FGameplayDebugger::OnLevelActorAdded(AActor* InActor) { // This function doesn't help much, because it's only called in EDITOR! // We need a function that is called in the game! So instead of creating it automatically, I'm leaving it // to be created explicitly by any player controller that needs to create it. #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) APlayerController* PC = Cast<APlayerController>(InActor); if (PC && PC->GetNetMode() < ENetMode::NM_Client) { UWorld* PCWorld = PC->GetWorld(); if (PCWorld && PCWorld->IsGameWorld()) { CreateGameplayDebuggerForPlayerController(PC); } } #endif }
bool ULevelStreaming::IsStreamingStatePending() const { UWorld* PersistentWorld = GetWorld(); if (PersistentWorld) { if (IsLevelLoaded() == ShouldBeLoaded() && IsLevelVisible() == ShouldBeVisible()) { FName DesiredPackageName = PersistentWorld->IsGameWorld() ? GetLODPackageName() : GetWorldAssetPackageFName(); if (!LoadedLevel || LoadedLevel->GetOutermost()->GetFName() == DesiredPackageName) { return false; } } return true; } return false; }
bool UWorldComposition::UpdateEditorStreamingState(const FVector& InLocation) { UWorld* OwningWorld = GetWorld(); bool bStateChanged = false; // Handle only editor worlds if (!OwningWorld->IsGameWorld() && !OwningWorld->IsVisibilityRequestPending()) { // Get the list of visible and hidden levels from current view point TArray<FDistanceVisibleLevel> DistanceVisibleLevels; TArray<FDistanceVisibleLevel> DistanceHiddenLevels; GetDistanceVisibleLevels(InLocation, DistanceVisibleLevels, DistanceHiddenLevels); // Hidden levels for (const auto& Level : DistanceHiddenLevels) { ULevelStreaming* EditorStreamingLevel = OwningWorld->GetLevelStreamingForPackageName(TilesStreaming[Level.TileIdx]->GetWorldAssetPackageFName()); if (EditorStreamingLevel && EditorStreamingLevel->IsLevelLoaded() && EditorStreamingLevel->bShouldBeVisibleInEditor != false) { EditorStreamingLevel->bShouldBeVisibleInEditor = false; bStateChanged = true; } } // Visible levels for (const auto& Level : DistanceVisibleLevels) { ULevelStreaming* EditorStreamingLevel = OwningWorld->GetLevelStreamingForPackageName(TilesStreaming[Level.TileIdx]->GetWorldAssetPackageFName()); if (EditorStreamingLevel && EditorStreamingLevel->IsLevelLoaded() && EditorStreamingLevel->bShouldBeVisibleInEditor != true) { EditorStreamingLevel->bShouldBeVisibleInEditor = true; bStateChanged = true; } } } return bStateChanged; }
void UUserWidget::AddToViewport(int32 ZOrder) { if ( !FullScreenWidget.IsValid() ) { TSharedPtr<SWidget> OutUserSlateWidget; TSharedRef<SWidget> RootWidget = MakeViewportWidget(OutUserSlateWidget); FullScreenWidget = RootWidget; // If this is a game world add the widget to the current worlds viewport. UWorld* World = GetWorld(); if ( World && World->IsGameWorld() ) { if ( UGameViewportClient* ViewportClient = World->GetGameViewport() ) { ViewportClient->AddViewportWidgetContent(RootWidget, 10 + ZOrder); } } } }
void USocketIOClientComponent::Connect(const FString& InAddressAndPort, USIOJsonObject* Query /*= nullptr*/, USIOJsonObject* Headers /*= nullptr*/) { //Check if we're limiting this component if(bLimitConnectionToGameWorld) { UWorld* World = GEngine->GetWorldFromContextObject(this, EGetWorldErrorMode::LogAndReturnNull); if (World) { bool bIsGameWorld = (World->IsGameWorld() || World->IsPreviewWorld()); if(!bIsGameWorld) { UE_LOG(SocketIOLog, Log, TEXT("USocketIOClientComponent::Connect attempt in non-game world blocked by bLimitConnectionToGameWorld.")); return; } } else { UE_LOG(SocketIOLog, Warning, TEXT("USocketIOClientComponent::Connect attempt while in invalid world.")); return; } } TSharedPtr<FJsonObject> QueryFJson; TSharedPtr<FJsonObject> HeadersFJson; if (Query != nullptr) { QueryFJson = Query->GetRootObject(); } if (Headers != nullptr) { HeadersFJson = Headers->GetRootObject(); } //Ensure we sync our native max/reconnection attempts before connecting NativeClient->MaxReconnectionAttempts = MaxReconnectionAttempts; NativeClient->ReconnectionDelay = ReconnectionDelayInMs; NativeClient->VerboseLog = bVerboseConnectionLog; ConnectNative(InAddressAndPort, QueryFJson, HeadersFJson); }
void UUserWidget::RemoveFromParent() { if ( FullScreenWidget.IsValid() ) { TSharedPtr<SWidget> WidgetHost = FullScreenWidget.Pin(); // If this is a game world add the widget to the current worlds viewport. UWorld* World = GetWorld(); if ( World && World->IsGameWorld() ) { if ( UGameViewportClient* ViewportClient = World->GetGameViewport() ) { ViewportClient->RemoveViewportWidgetContent(WidgetHost.ToSharedRef()); } } } else { Super::RemoveFromParent(); } }
void USlateBlueprintLibrary::AbsoluteToViewport(UObject* WorldContextObject, const FGeometry& Geometry, FVector2D AbsoluteCoordinate, FVector2D& ScreenPosition, FVector2D& ViewportPosition) { UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject); if ( World && World->IsGameWorld() ) { if ( UGameViewportClient* ViewportClient = World->GetGameViewport() ) { if ( FViewport* Viewport = ViewportClient->Viewport ) { FVector2D ViewportSize; ViewportClient->GetViewportSize(ViewportSize); FVector2D PixelLocation = Viewport->VirtualDesktopPixelToViewport(FIntPoint((int32)AbsoluteCoordinate.X, (int32)AbsoluteCoordinate.Y)) * ViewportSize; float CurrentViewportScale = GetDefault<UUserInterfaceSettings>()->GetDPIScaleBasedOnSize(FIntPoint(ViewportSize.X, ViewportSize.Y)); // If the user has configured a resolution quality we need to multiply // the pixels by the resolution quality to arrive at the true position in // the viewport, as the rendered image will be stretched to fill whatever // size the viewport is at. Scalability::FQualityLevels ScalabilityQuality = Scalability::GetQualityLevels(); const float QualityScale = ( ScalabilityQuality.ResolutionQuality / 100.0f ); // Remove the resolution quality scale. ScreenPosition = PixelLocation * QualityScale; // Remove DPI Scaling. ViewportPosition = PixelLocation / CurrentViewportScale; return; } } } ScreenPosition = FVector2D(0, 0); ViewportPosition = FVector2D(0, 0); }
void AGameState::PostInitializeComponents() { Super::PostInitializeComponents(); FTimerManager& TimerManager = GetWorldTimerManager(); TimerManager.SetTimer(TimerHandle_DefaultTimer, this, &AGameState::DefaultTimer, 1.0f / GetWorldSettings()->GetEffectiveTimeDilation(), true); UWorld* World = GetWorld(); World->GameState = this; if (World->IsGameWorld() && Role == ROLE_Authority) { UpdateServerTimeSeconds(); if (ServerWorldTimeSecondsUpdateFrequency > 0.f) { TimerManager.SetTimer(TimerHandle_UpdateServerTimeSeconds, this, &AGameState::UpdateServerTimeSeconds, ServerWorldTimeSecondsUpdateFrequency, true); } } for (TActorIterator<APlayerState> It(World); It; ++It) { AddPlayerState(*It); } }
void AGameplayDebuggerReplicator::TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction) { Super::TickActor(DeltaTime, TickType, ThisTickFunction); #if ENABLED_GAMEPLAY_DEBUGGER UWorld* World = GetWorld(); const ENetMode NetMode = GetNetMode(); if (!World) { // return without world return; } UGameInstance* GameInstance = World->GetGameInstance(); if (!GameInstance || !World->IsGameWorld()) { return; } if (NetMode != NM_DedicatedServer) { if (bActivationKeyPressed) { ActivationKeyTime += DeltaTime; if (ActivationKeyTime >= GameplayDebuggerHelpers::ActivationKeyTimePch) { GEngine->bEnableOnScreenDebugMessages = false; if (AHUD* const GameHUD = LocalPlayerOwner ? LocalPlayerOwner->GetHUD() : nullptr) { GameHUD->bShowHUD = false; } BindKeyboardInput(InputComponent); ServerActivateGameplayDebugger(true); ClientActivateGameplayDebugger(true); bActivationKeyPressed = false; } } if (bEnabledTargetSelection) { if (GetLocalPlayerOwner()) { SelectTargetToDebug(); } } bool bMarkComponentsAsRenderStateDirty = false; for (UGameplayDebuggerBaseObject* Obj : ReplicatedObjects) { if (Obj && Obj->IsRenderStateDirty()) { if (!bMarkComponentsAsRenderStateDirty) { MarkComponentsRenderStateDirty(); } bMarkComponentsAsRenderStateDirty = true; Obj->CleanRenderStateDirtyFlag(); } } } if (NetMode < NM_Client && LocalPlayerOwner) { TMap<FString, TArray<UGameplayDebuggerBaseObject*> > CategoryToClasses; for (UGameplayDebuggerBaseObject* Obj : ReplicatedObjects) { if (Obj) { FString Category = Obj->GetCategoryName(); if (IsCategoryEnabled(Category)) { CategoryToClasses.FindOrAdd(Category).Add(Obj); } } } for (auto It(CategoryToClasses.CreateIterator()); It; ++It) { TArray<UGameplayDebuggerBaseObject*>& CurrentObjects = It.Value(); for (UGameplayDebuggerBaseObject* Obj : CurrentObjects) { Obj->CollectDataToReplicateOnServer(LocalPlayerOwner, LastSelectedActorToDebug); } } } #endif }
FReply FSceneViewport::OnMouseButtonDown( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent ) { // Start a new reply state // Prevent throttling when interacting with the viewport so we can move around in it CurrentReplyState = FReply::Handled().PreventThrottling(); KeyStateMap.Add(InMouseEvent.GetEffectingButton(), true); UpdateModifierKeys( InMouseEvent ); UpdateCachedMousePos( InGeometry, InMouseEvent ); UpdateCachedGeometry(InGeometry); // Switch to the viewport clients world before processing input FScopedConditionalWorldSwitcher WorldSwitcher( ViewportClient ); if( ViewportClient && GetSizeXY() != FIntPoint::ZeroValue ) { // If we're obtaining focus, we have to copy the modifier key states prior to processing this mouse button event, as this is the only point at which the mouse down // event is processed when focus initially changes and the modifier keys need to be in-place to detect any unique drag-like events. if ( !HasFocus() ) { FModifierKeysState KeysState = FSlateApplication::Get().GetModifierKeys(); ApplyModifierKeys( KeysState ); } // Process the mouse event if( !ViewportClient->InputKey( this, 0, InMouseEvent.GetEffectingButton(), IE_Pressed ) ) { CurrentReplyState = FReply::Unhandled(); } if (ViewportClient->CaptureMouseOnClick() != EMouseCaptureMode::NoCapture && !ViewportClient->IgnoreInput()) { TSharedRef<SViewport> ViewportWidgetRef = ViewportWidget.Pin().ToSharedRef(); // Mouse down should focus viewport for keyboard input CurrentReplyState.SetKeyboardFocus(ViewportWidgetRef, EKeyboardFocusCause::Mouse); UWorld* World = ViewportClient->GetWorld(); if (World && World->IsGameWorld() && World->GetFirstPlayerController()) { CurrentReplyState.CaptureMouse(ViewportWidgetRef); CurrentReplyState.LockMouseToWidget(ViewportWidgetRef); bool bShouldShowMouseCursor = World->GetFirstPlayerController()->ShouldShowMouseCursor(); if (ViewportClient->HideCursorDuringCapture() && bShouldShowMouseCursor) { bCursorHiddenDueToCapture = true; } if (bCursorHiddenDueToCapture || !bShouldShowMouseCursor) { CurrentReplyState.UseHighPrecisionMouseMovement(ViewportWidgetRef); } } else { CurrentReplyState.UseHighPrecisionMouseMovement(ViewportWidgetRef); } } } // Re-set prevent throttling here as it can get reset when inside of InputKey() CurrentReplyState.PreventThrottling(); return CurrentReplyState; }
FReply FSceneViewport::OnMouseButtonDown( const FGeometry& InGeometry, const FPointerEvent& InMouseEvent ) { // Start a new reply state // Prevent throttling when interacting with the viewport so we can move around in it CurrentReplyState = FReply::Handled().PreventThrottling(); KeyStateMap.Add(InMouseEvent.GetEffectingButton(), true); UpdateModifierKeys( InMouseEvent ); UpdateCachedMousePos( InGeometry, InMouseEvent ); UpdateCachedGeometry(InGeometry); // Switch to the viewport clients world before processing input FScopedConditionalWorldSwitcher WorldSwitcher( ViewportClient ); if( ViewportClient && GetSizeXY() != FIntPoint::ZeroValue ) { // If we're obtaining focus, we have to copy the modifier key states prior to processing this mouse button event, as this is the only point at which the mouse down // event is processed when focus initially changes and the modifier keys need to be in-place to detect any unique drag-like events. if ( !HasFocus() ) { FModifierKeysState KeysState = FSlateApplication::Get().GetModifierKeys(); ApplyModifierKeys( KeysState ); } const bool bAnyMenuWasVisible = FSlateApplication::Get().AnyMenusVisible(); // Process the mouse event if (!ViewportClient->InputKey(this, InMouseEvent.GetUserIndex(), InMouseEvent.GetEffectingButton(), IE_Pressed)) { CurrentReplyState = FReply::Unhandled(); } // a new menu was opened if there was previously not a menu visible but now there is const bool bNewMenuWasOpened = !bAnyMenuWasVisible && FSlateApplication::Get().AnyMenusVisible(); if (!ViewportClient->IgnoreInput() && !bNewMenuWasOpened && // We should not focus the viewport if a menu was opened as it would close the menu ( ViewportClient->CaptureMouseOnClick() == EMouseCaptureMode::CapturePermanently || ViewportClient->CaptureMouseOnClick() == EMouseCaptureMode::CaptureDuringMouseDown || ( ViewportClient->CaptureMouseOnClick() == EMouseCaptureMode::CaptureDuringRightMouseDown && InMouseEvent.GetEffectingButton() == EKeys::RightMouseButton ) ) ) { TSharedRef<SViewport> ViewportWidgetRef = ViewportWidget.Pin().ToSharedRef(); // Mouse down should focus viewport for user input CurrentReplyState.SetUserFocus(ViewportWidgetRef, EFocusCause::SetDirectly, true); UWorld* World = ViewportClient->GetWorld(); if (World && World->IsGameWorld() && World->GetFirstPlayerController()) { CurrentReplyState.CaptureMouse(ViewportWidgetRef); CurrentReplyState.LockMouseToWidget(ViewportWidgetRef); bool bShouldShowMouseCursor = World->GetFirstPlayerController()->ShouldShowMouseCursor(); if (ViewportClient->HideCursorDuringCapture() && bShouldShowMouseCursor) { bCursorHiddenDueToCapture = true; MousePosBeforeHiddenDueToCapture = FIntPoint( InMouseEvent.GetScreenSpacePosition().X, InMouseEvent.GetScreenSpacePosition().Y ); } if (bCursorHiddenDueToCapture || !bShouldShowMouseCursor) { CurrentReplyState.UseHighPrecisionMouseMovement(ViewportWidgetRef); } } else { CurrentReplyState.UseHighPrecisionMouseMovement(ViewportWidgetRef); } } } // Re-set prevent throttling here as it can get reset when inside of InputKey() CurrentReplyState.PreventThrottling(); return CurrentReplyState; }
void FFoliageInstanceBaseCache::CompactInstanceBaseCache(AInstancedFoliageActor* IFA) { UWorld* World = IFA->GetWorld(); if (!World || World->IsGameWorld()) { return; } FFoliageInstanceBaseCache& Cache = IFA->InstanceBaseCache; TSet<FFoliageInstanceBaseId> BasesInUse; for (auto& Pair : IFA->FoliageMeshes) { for (const auto& Pair : Pair.Value->ComponentHash) { if (Pair.Key != FFoliageInstanceBaseCache::InvalidBaseId) { BasesInUse.Add(Pair.Key); } } } // Look for any removed maps TSet<FFoliageInstanceBasePtr> InvalidBasePtrs; for (auto& Pair : Cache.InstanceBaseLevelMap) { const auto& WorldAsset = Pair.Key; bool bExists = (WorldAsset == World); // Check sub-levels if (!bExists) { const FName PackageName = FName(*FPackageName::ObjectPathToPackageName(WorldAsset.ToStringReference().ToString())); if (World->WorldComposition) { bExists = World->WorldComposition->DoesTileExists(PackageName); } else { bExists = (World->GetLevelStreamingForPackageName(PackageName) != nullptr); } } if (!bExists) { InvalidBasePtrs.Append(Pair.Value); Cache.InstanceBaseLevelMap.Remove(Pair.Key); } else { // Remove dead links for (int32 i = Pair.Value.Num()-1; i >= 0; --i) { // Base needs to be removed if it's not in use by existing instances or component was removed if (Pair.Value[i].IsNull() || !BasesInUse.Contains(Cache.GetInstanceBaseId(Pair.Value[i]))) { InvalidBasePtrs.Add(Pair.Value[i]); Pair.Value.RemoveAt(i); } } if (Pair.Value.Num() == 0) { Cache.InstanceBaseLevelMap.Remove(Pair.Key); } } } TSet<FFoliageInstanceBaseId> InvalidBaseIds; Cache.InstanceBaseInvMap.Empty(); // Look for any removed base components for (const auto& Pair : Cache.InstanceBaseMap) { const FFoliageInstanceBaseInfo& BaseInfo = Pair.Value; if (InvalidBasePtrs.Contains(BaseInfo.BasePtr)) { InvalidBaseIds.Add(Pair.Key); Cache.InstanceBaseMap.Remove(Pair.Key); } else { // Regenerate inverse map check(!Cache.InstanceBaseInvMap.Contains(BaseInfo.BasePtr)); Cache.InstanceBaseInvMap.Add(BaseInfo.BasePtr, Pair.Key); } } if (InvalidBaseIds.Num()) { for (auto& Pair : IFA->FoliageMeshes) { auto& MeshInfo = Pair.Value; MeshInfo->ComponentHash.Empty(); int32 InstanceIdx = 0; for (FFoliageInstance& Instance : MeshInfo->Instances) { if (InvalidBaseIds.Contains(Instance.BaseId)) { Instance.BaseId = FFoliageInstanceBaseCache::InvalidBaseId; } MeshInfo->ComponentHash.FindOrAdd(Instance.BaseId).Add(InstanceIdx); InstanceIdx++; } } Cache.InstanceBaseMap.Compact(); Cache.InstanceBaseLevelMap.Compact(); } }
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; }