void FSpriteEditorViewportClient::NotifySpriteBeingEditedHasChanged() { //@TODO: Ideally we do this before switching EndTransaction(); // Refresh the viewport in case we were not in realtime mode Invalidate(); // Update components to know about the new sprite being edited UPaperSprite* Sprite = GetSpriteBeingEdited(); RenderSpriteComponent->SetSprite(Sprite); UpdateSourceTextureSpriteFromSprite(Sprite); InternalActivateNewMode(CurrentMode); //@TODO: Only do this if the sprite isn't visible (may consider doing a flashing pulse around the source region rect?) RequestFocusOnSelection(/*bInstant=*/ true); if (Sprite != nullptr) { // Create and display a notification about the new sprite being edited const FText NotificationErrorText = FText::Format(LOCTEXT("SwitchingToSprite", "Editing {0}"), FText::AsCultureInvariant(Sprite->GetName())); FNotificationInfo Info(NotificationErrorText); Info.ExpireDuration = 2.0f; FSlateNotificationManager::Get().AddNotification(Info); } }
void FSpriteEditorViewportClient::Tick(float DeltaSeconds) { if (UPaperSprite* Sprite = GetSpriteBeingEdited()) { //@TODO: Doesn't need to happen every frame, only when properties are updated // Update the source texture view sprite (in case the texture has changed) UpdateSourceTextureSpriteFromSprite(Sprite); // Reposition the sprite (to be at the correct relative location to it's parent, undoing the pivot behavior) const FVector2D PivotInTextureSpace = Sprite->ConvertPivotSpaceToTextureSpace(FVector2D::ZeroVector); const FVector PivotInWorldSpace = TextureSpaceToWorldSpace(PivotInTextureSpace); RenderSpriteComponent->SetRelativeLocation(PivotInWorldSpace); bool bSourceTextureViewComponentVisibility = bShowSourceTexture || IsInSourceRegionEditMode(); if (bSourceTextureViewComponentVisibility != SourceTextureViewComponent->IsVisible()) { RequestFocusOnSelection(/*bInstant=*/ true); SourceTextureViewComponent->SetVisibility(bSourceTextureViewComponentVisibility); } bool bRenderTextureViewComponentVisibility = !IsInSourceRegionEditMode(); if (bRenderTextureViewComponentVisibility != RenderSpriteComponent->IsVisible()) { RequestFocusOnSelection(/*bInstant=*/ true); RenderSpriteComponent->SetVisibility(bRenderTextureViewComponentVisibility); } const FVector2D BoxSize(Sprite->GetSourceSize()); const FVector2D BoxLocation(Sprite->GetSourceUV() + (BoxSize * 0.5f)); FBox2D SpriteBounds(ForceInitToZero); SpriteBounds.Min = BoxSize - BoxLocation * 0.5f; SpriteBounds.Max = BoxSize + BoxLocation * 0.5f; if (FSpriteGeometryEditMode* GeometryEditMode = ModeTools->GetActiveModeTyped<FSpriteGeometryEditMode>(FSpriteGeometryEditMode::EM_SpriteGeometry)) { GeometryEditMode->SetNewGeometryPreferredBounds(SpriteBounds); } } FPaperEditorViewportClient::Tick(DeltaSeconds); if (!GIntraFrameDebuggingGameThread) { OwnedPreviewScene.GetWorld()->Tick(LEVELTICK_All, DeltaSeconds); } }
void FSingleTileEditorViewportClient::SetTileIndex(int32 InTileIndex) { const int32 OldTileIndex = TileBeingEditedIndex; const bool bNewIndexValid = (InTileIndex >= 0) && (InTileIndex < TileSet->GetTileCount()); TileBeingEditedIndex = bNewIndexValid ? InTileIndex : INDEX_NONE; FSpriteGeometryEditMode* GeometryEditMode = ModeTools->GetActiveModeTyped<FSpriteGeometryEditMode>(FSpriteGeometryEditMode::EM_SpriteGeometry); check(GeometryEditMode); FSpriteGeometryCollection* GeomToEdit = nullptr; if (TileBeingEditedIndex != INDEX_NONE) { if (FPaperTileMetadata* Metadata = TileSet->GetMutableTileMetadata(InTileIndex)) { GeomToEdit = &(Metadata->CollisionData); } } // Tell the geometry editor about the new tile (if it exists) GeometryEditMode->SetGeometryBeingEdited(GeomToEdit, /*bAllowCircles=*/ true, /*bAllowSubtractivePolygons=*/ false); // Update the visual representation UPaperSprite* DummySprite = nullptr; if (TileBeingEditedIndex != INDEX_NONE) { DummySprite = NewObject<UPaperSprite>(); DummySprite->SpriteCollisionDomain = ESpriteCollisionMode::None; DummySprite->PivotMode = ESpritePivotMode::Center_Center; DummySprite->CollisionGeometry.GeometryType = ESpritePolygonMode::SourceBoundingBox; DummySprite->RenderGeometry.GeometryType = ESpritePolygonMode::SourceBoundingBox; FSpriteAssetInitParameters SpriteReinitParams; SpriteReinitParams.Texture = TileSet->GetTileSheetTexture(); //@TODO: Should analyze the texture (*at a higher level, not per tile click!*) to pick the correct material FVector2D UV; TileSet->GetTileUV(TileBeingEditedIndex, /*out*/ UV); SpriteReinitParams.Offset = FIntPoint((int32)UV.X, (int32)(UV.Y)); SpriteReinitParams.Dimension = TileSet->GetTileSize(); SpriteReinitParams.SetPixelsPerUnrealUnit(1.0f); DummySprite->InitializeSprite(SpriteReinitParams); } PreviewTileSpriteComponent->SetSprite(DummySprite); // Update the default geometry bounds const FVector2D HalfTileSize(TileSet->GetTileSize().X * 0.5f, TileSet->GetTileSize().Y * 0.5f); FBox2D DesiredBounds(ForceInitToZero); DesiredBounds.Min = -HalfTileSize; DesiredBounds.Max = HalfTileSize; GeometryEditMode->SetNewGeometryPreferredBounds(DesiredBounds); // Zoom to fit when we start editing a tile and weren't before, but leave it alone if we just changed tiles, in case they zoomed in or out further if ((TileBeingEditedIndex != INDEX_NONE) && (OldTileIndex == INDEX_NONE)) { RequestFocusOnSelection(/*bInstant=*/ true); } // Trigger a details panel customization rebuild OnSingleTileIndexChanged.Broadcast(TileBeingEditedIndex, OldTileIndex); // Redraw the viewport Invalidate(); }
void FSpriteEditorViewportClient::UpdateSourceTextureSpriteFromSprite(UPaperSprite* SourceSprite) { UPaperSprite* TargetSprite = SourceTextureViewComponent->GetSprite(); check(TargetSprite); if (SourceSprite != nullptr) { if ((SourceSprite->GetSourceTexture() != TargetSprite->GetSourceTexture()) || (TargetSprite->PixelsPerUnrealUnit != SourceSprite->PixelsPerUnrealUnit)) { FComponentReregisterContext ReregisterSprite(SourceTextureViewComponent); FSpriteAssetInitParameters SpriteReinitParams; SpriteReinitParams.SetTextureAndFill(SourceSprite->SourceTexture); SpriteReinitParams.DefaultMaterialOverride = SourceSprite->DefaultMaterial; SpriteReinitParams.AlternateMaterialOverride = SourceSprite->AlternateMaterial; SpriteReinitParams.SetPixelsPerUnrealUnit(SourceSprite->PixelsPerUnrealUnit); TargetSprite->InitializeSprite(SpriteReinitParams); RequestFocusOnSelection(/*bInstant=*/ true); } // Position the sprite for the mode its meant to be in FVector2D CurrentPivotPosition; ESpritePivotMode::Type CurrentPivotMode = TargetSprite->GetPivotMode(/*out*/CurrentPivotPosition); FVector Translation(1.0f * PaperAxisZ); if (IsInSourceRegionEditMode()) { if (CurrentPivotMode != ESpritePivotMode::Bottom_Left) { TargetSprite->SetPivotMode(ESpritePivotMode::Bottom_Left, FVector2D::ZeroVector); TargetSprite->PostEditChange(); } SourceTextureViewComponent->SetSpriteColor(FLinearColor::White); SourceTextureViewComponent->SetWorldTransform(FTransform(Translation)); } else { const FVector2D PivotPosition = SourceSprite->GetPivotPosition(); if (CurrentPivotMode != ESpritePivotMode::Custom || CurrentPivotPosition != PivotPosition) { TargetSprite->SetPivotMode(ESpritePivotMode::Custom, PivotPosition); TargetSprite->PostEditChange(); } // Tint the source texture darker to help distinguish the two SourceTextureViewComponent->SetSpriteColor(SpriteEditingConstants::SourceTextureDarkTintColor); const bool bRotated = SourceSprite->IsRotatedInSourceImage(); if (bRotated) { FQuat Rotation(PaperAxisZ, FMath::DegreesToRadians(90.0f)); SourceTextureViewComponent->SetWorldTransform(FTransform(Rotation, Translation)); } else { SourceTextureViewComponent->SetWorldTransform(FTransform(Translation)); } } } else { // No source sprite, so don't draw the target either TargetSprite->SourceTexture = nullptr; } }