void FSpriteGeometryEditingHelper::DrawGeometry_CanvasPass(FViewport& InViewport, const FSceneView& View, FCanvas& Canvas, /*inout*/ int32& YPos, const FLinearColor& GeometryVertexColor, const FLinearColor& NegativeGeometryVertexColor) { if (GeometryBeingEdited == nullptr) { return; } // Calculate the texture-space position of the mouse const FVector MousePositionWorldSpace = View.PixelToWorld(InViewport.GetMouseX(), InViewport.GetMouseY(), 0); const FVector2D MousePositionTextureSpace = EditorContext->WorldSpaceToTextureSpace(MousePositionWorldSpace); //@TODO: Move all of the line drawing to the PDI pass FSpriteGeometryCollection& Geometry = GetGeometryChecked(); // Display tool help { static const FText GeomHelpStr = LOCTEXT("GeomEditHelp", "Shift + click to insert a vertex.\nSelect one or more vertices and press Delete to remove them.\nDouble click a vertex to select a polygon\n"); static const FText GeomClickAddPolygon_NoSubtractive = LOCTEXT("GeomClickAddPolygon_NoSubtractive", "Click to start creating a polygon\n"); static const FText GeomClickAddPolygon_AllowSubtractive = LOCTEXT("GeomClickAddPolygon_AllowSubtractive", "Click to start creating a polygon\nCtrl + Click to start creating a subtractive polygon\n"); static const FText GeomAddVerticesHelpStr = LOCTEXT("GeomClickAddVertices", "Click to add points to the polygon\nDouble-click to add a point and close the shape\nClick again on the first point or press Enter to close the shape\nPress Backspace to remove the last added point or Escape to remove the shape\n"); FLinearColor ToolTextColor = FLinearColor::White; const FText* HelpStr; if (IsAddingPolygon()) { if (AddingPolygonIndex == INDEX_NONE) { HelpStr = bAllowSubtractivePolygons ? &GeomClickAddPolygon_AllowSubtractive : &GeomClickAddPolygon_NoSubtractive; } else { HelpStr = &GeomAddVerticesHelpStr; } ToolTextColor = FLinearColor::Yellow; } else { HelpStr = &GeomHelpStr; } FCanvasTextItem TextItem(FVector2D(6, YPos), *HelpStr, GEngine->GetSmallFont(), ToolTextColor); TextItem.EnableShadow(FLinearColor::Black); TextItem.Draw(&Canvas); YPos += 54; } const bool bIsHitTesting = Canvas.IsHitTesting(); // Run thru the geometry shapes and draw hit proxies for them for (int32 ShapeIndex = 0; ShapeIndex < Geometry.Shapes.Num(); ++ShapeIndex) { const FSpriteGeometryShape& Shape = Geometry.Shapes[ShapeIndex]; const bool bIsShapeSelected = IsGeometrySelected(FShapeVertexPair(ShapeIndex, INDEX_NONE)); const FLinearColor LineColorRaw = Shape.bNegativeWinding ? NegativeGeometryVertexColor : GeometryVertexColor; const FLinearColor VertexColor = Shape.bNegativeWinding ? NegativeGeometryVertexColor : GeometryVertexColor; const FLinearColor LineColor = Shape.IsShapeValid() ? LineColorRaw : FMath::Lerp(LineColorRaw, FLinearColor::Red, 0.8f); // Draw the circle shape if necessary if (Shape.ShapeType == ESpriteShapeType::Circle) { if (bIsHitTesting) { TSharedPtr<FSpriteSelectedShape> Data = MakeShareable(new FSpriteSelectedShape(EditorContext, Geometry, ShapeIndex, /*bIsBackground=*/ false)); Canvas.SetHitProxy(new HSpriteSelectableObjectHitProxy(Data)); } // Draw the circle const float RadiusX = Shape.BoxSize.X * 0.5f; const float RadiusY = Shape.BoxSize.Y * 0.5f; const float AngleDelta = 2.0f * PI / SpriteEditingConstantsEX::CircleShapeNumSides; const float LastX = Shape.BoxPosition.X + RadiusX; const float LastY = Shape.BoxPosition.Y; FVector2D LastVertexPos = TextureSpaceToScreenSpace(View, FVector2D(LastX, LastY)); for (int32 SideIndex = 0; SideIndex < SpriteEditingConstantsEX::CircleShapeNumSides; SideIndex++) { const float X = Shape.BoxPosition.X + RadiusX * FMath::Cos(AngleDelta * (SideIndex + 1)); const float Y = Shape.BoxPosition.Y + RadiusY * FMath::Sin(AngleDelta * (SideIndex + 1)); const FVector2D ScreenPos = TextureSpaceToScreenSpace(View, FVector2D(X, Y)); FCanvasLineItem LineItem(LastVertexPos, ScreenPos); LineItem.SetColor(bIsShapeSelected ? SpriteEditingConstantsEX::GeometrySelectedColor : LineColor); LineItem.LineThickness = SpriteEditingConstantsEX::GeometryBorderLineThickness; Canvas.DrawItem(LineItem); LastVertexPos = ScreenPos; } if (bIsHitTesting) { Canvas.SetHitProxy(nullptr); } } // Draw lines connecting the vertices of the shape for (int32 VertexIndex = 0; VertexIndex < Shape.Vertices.Num(); ++VertexIndex) { const int32 NextVertexIndex = (VertexIndex + 1) % Shape.Vertices.Num(); const FVector2D ScreenPos = TextureSpaceToScreenSpace(View, Shape.ConvertShapeSpaceToTextureSpace(Shape.Vertices[VertexIndex])); const FVector2D NextScreenPos = TextureSpaceToScreenSpace(View, Shape.ConvertShapeSpaceToTextureSpace(Shape.Vertices[NextVertexIndex])); const bool bIsThisVertexSelected = IsGeometrySelected(FShapeVertexPair(ShapeIndex, VertexIndex)); const bool bIsNextVertexSelected = IsGeometrySelected(FShapeVertexPair(ShapeIndex, NextVertexIndex)); const bool bIsEdgeSelected = bIsShapeSelected || (bIsThisVertexSelected && bIsNextVertexSelected); // Draw the normal tick if (bShowNormals) { const FVector2D Direction = (NextScreenPos - ScreenPos).GetSafeNormal(); const FVector2D Normal = FVector2D(-Direction.Y, Direction.X); const FVector2D Midpoint = (ScreenPos + NextScreenPos) * 0.5f; const FVector2D NormalPoint = Midpoint - Normal * SpriteEditingConstantsEX::GeometryNormalLength; FCanvasLineItem LineItem(Midpoint, NormalPoint); LineItem.SetColor(SpriteEditingConstantsEX::GeometryNormalColor); Canvas.DrawItem(LineItem); } // Draw the edge { if (bIsHitTesting) { TSharedPtr<FSpriteSelectedEdge> Data = MakeShareable(new FSpriteSelectedEdge(EditorContext, Geometry, ShapeIndex, VertexIndex)); Canvas.SetHitProxy(new HSpriteSelectableObjectHitProxy(Data)); } FCanvasLineItem LineItem(ScreenPos, NextScreenPos); LineItem.SetColor(bIsEdgeSelected ? SpriteEditingConstantsEX::GeometrySelectedColor : LineColor); LineItem.LineThickness = SpriteEditingConstantsEX::GeometryBorderLineThickness; Canvas.DrawItem(LineItem); if (bIsHitTesting) { Canvas.SetHitProxy(nullptr); } } } // Draw the vertices for (int32 VertexIndex = 0; VertexIndex < Shape.Vertices.Num(); ++VertexIndex) { const FVector2D ScreenPos = TextureSpaceToScreenSpace(View, Shape.ConvertShapeSpaceToTextureSpace(Shape.Vertices[VertexIndex])); const float X = ScreenPos.X; const float Y = ScreenPos.Y; const bool bIsVertexSelected = IsGeometrySelected(FShapeVertexPair(ShapeIndex, VertexIndex)); const bool bIsVertexLastAdded = IsAddingPolygon() && (AddingPolygonIndex == ShapeIndex) && (VertexIndex == Shape.Vertices.Num() - 1); const bool bNeedHighlightVertex = bIsShapeSelected || bIsVertexSelected || bIsVertexLastAdded; if (bIsHitTesting) { TSharedPtr<FSpriteSelectedVertex> Data = MakeShareable(new FSpriteSelectedVertex(EditorContext, Geometry, ShapeIndex, VertexIndex)); Canvas.SetHitProxy(new HSpriteSelectableObjectHitProxy(Data)); } const float VertSize = SpriteEditingConstantsEX::GeometryVertexSize; Canvas.DrawTile(ScreenPos.X - VertSize*0.5f, ScreenPos.Y - VertSize*0.5f, VertSize, VertSize, 0.f, 0.f, 1.f, 1.f, bNeedHighlightVertex ? SpriteEditingConstantsEX::GeometrySelectedColor : VertexColor, GWhiteTexture); if (bIsHitTesting) { Canvas.SetHitProxy(nullptr); } } } // Draw a preview cursor for the add polygon tool if (IsAddingPolygon()) { // Figure out where the mouse is back in screen space const FVector2D PotentialVertexScreenPos = TextureSpaceToScreenSpace(View, MousePositionTextureSpace); bool bWillCloseByClicking = false; if (Geometry.Shapes.IsValidIndex(AddingPolygonIndex)) { const FSpriteGeometryShape& Shape = Geometry.Shapes[AddingPolygonIndex]; const FLinearColor LineColorRaw = Shape.bNegativeWinding ? NegativeGeometryVertexColor : GeometryVertexColor; const FLinearColor LineColorValidity = Shape.IsShapeValid() ? LineColorRaw : FMath::Lerp(LineColorRaw, FLinearColor::Red, 0.8f); const FLinearColor LineColor = FMath::Lerp(LineColorValidity, SpriteEditingConstantsEX::GeometrySelectedColor, 0.2f); if (Shape.Vertices.Num() > 0) { // Draw a line from the last vertex to the potential insertion point for the new one { const FVector2D LastScreenPos = TextureSpaceToScreenSpace(View, Shape.ConvertShapeSpaceToTextureSpace(Shape.Vertices[Shape.Vertices.Num() - 1])); FCanvasLineItem LineItem(LastScreenPos, PotentialVertexScreenPos); LineItem.SetColor(LineColor); LineItem.LineThickness = SpriteEditingConstantsEX::GeometryBorderLineThickness; Canvas.DrawItem(LineItem); } // And to the first vertex if there were at least 2 if (Shape.Vertices.Num() >= 2) { const FVector2D FirstScreenPos = TextureSpaceToScreenSpace(View, Shape.ConvertShapeSpaceToTextureSpace(Shape.Vertices[0])); FCanvasLineItem LineItem(PotentialVertexScreenPos, FirstScreenPos); LineItem.SetColor(LineColor); LineItem.LineThickness = SpriteEditingConstantsEX::GeometryBorderLineThickness; Canvas.DrawItem(LineItem); // Determine how close we are to the first vertex (will we close the shape by clicking)? bWillCloseByClicking = (Shape.Vertices.Num() >= 3) && (FVector2D::Distance(FirstScreenPos, PotentialVertexScreenPos) < SpriteEditingConstantsEX::AddPolygonVertexWeldScreenSpaceDistance); } } } // Draw the prospective vert const float VertSize = SpriteEditingConstantsEX::GeometryVertexSize; Canvas.DrawTile(PotentialVertexScreenPos.X - VertSize*0.5f, PotentialVertexScreenPos.Y - VertSize*0.5f, VertSize, VertSize, 0.f, 0.f, 1.f, 1.f, SpriteEditingConstantsEX::GeometrySelectedColor, GWhiteTexture); // Draw a prompt above and to the right of the cursor static const FText CloseButton(LOCTEXT("ClosePolygonPrompt", "Close")); static const FText AddButton(LOCTEXT("AddVertexToPolygonPrompt", "+")); const FText PromptText(bWillCloseByClicking ? CloseButton : AddButton); FCanvasTextItem PromptTextItem(FVector2D(PotentialVertexScreenPos.X + VertSize, PotentialVertexScreenPos.Y - VertSize), PromptText, GEngine->GetSmallFont(), FLinearColor::White); PromptTextItem.EnableShadow(FLinearColor::Black); PromptTextItem.Draw(&Canvas); } }