void UReporterGraph::DrawLegend(UCanvas* Canvas) { FVector2D CurrentTextPos = GraphScreenSize.Max; CurrentTextPos.X = GraphScreenSize.Min.X; UFont* Font = GetDefaultFont(); int32 DummyY, CurrentX; StringSize(Font, CurrentX, DummyY, TEXT("99.99")); const float InsideLegendWidth = -CurrentX; for(int32 i = 0; i < CurrentData.Num(); i++) { FVector2D ScreenPos = ToScreenSpace(CurrentTextPos, Canvas); if (LegendPosition == ELegendPosition::Outside) { int32 DummyY, CurrentX; StringSize(Font, CurrentX, DummyY, *CurrentData[i].LineName); LegendWidth = CurrentX + 10; } else { LegendWidth = InsideLegendWidth; } FCanvasTextItem TextItem(FVector2D::ZeroVector, FText::FromString(*CurrentData[i].LineName), Font, CurrentData[i].Color); TextItem.EnableShadow(FColor::Black, FVector2D(1, 1)); TextItem.SetColor(CurrentData[i].Color); Canvas->DrawItem(TextItem, ScreenPos.X - LegendWidth, ScreenPos.Y); CurrentTextPos.Y -= (GraphScreenSize.Max.Y - GraphScreenSize.Min.Y) / CurrentData.Num(); } }
void FSpriteGeometryEditMode::DrawGeometryStats(FViewport& InViewport, FSceneView& View, FCanvas& Canvas, const FSpriteGeometryCollection& Geometry, bool bIsRenderGeometry, int32& YPos) { // Draw the type of geometry we're displaying stats for const FText GeometryName = bIsRenderGeometry ? LOCTEXT("RenderGeometry", "Render Geometry (source)") : LOCTEXT("CollisionGeometry", "Collision Geometry (source)"); FCanvasTextItem TextItem(FVector2D(6, YPos), GeometryName, GEngine->GetSmallFont(), FLinearColor::White); TextItem.EnableShadow(FLinearColor::Black); TextItem.Draw(&Canvas); TextItem.Position += FVector2D(6.0f, 18.0f); // Draw the number of shapes TextItem.Text = FText::Format(LOCTEXT("PolygonCount", "Shapes: {0}"), FText::AsNumber(Geometry.Shapes.Num())); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; // Draw the number of vertices int32 NumVerts = 0; for (int32 PolyIndex = 0; PolyIndex < Geometry.Shapes.Num(); ++PolyIndex) { NumVerts += Geometry.Shapes[PolyIndex].Vertices.Num(); } TextItem.Text = FText::Format(LOCTEXT("VerticesCount", "Verts: {0}"), FText::AsNumber(NumVerts)); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; YPos = (int32)TextItem.Position.Y; }
void AAbilitySystemDebugHUD::DrawWithBackground(UFont* InFont, const FString& Text, const FColor& TextColor, EAlignHorizontal::Type HAlign, float& OffsetX, EAlignVertical::Type VAlign, float& OffsetY, float Alpha) { float SizeX, SizeY; Canvas->StrLen(InFont, Text, SizeX, SizeY); const float PosX = (HAlign == EAlignHorizontal::Center) ? OffsetX + (Canvas->ClipX - SizeX) * 0.5f : (HAlign == EAlignHorizontal::Left) ? Canvas->OrgX + OffsetX : Canvas->ClipX - SizeX - OffsetX; const float PosY = (VAlign == EAlignVertical::Center) ? OffsetY + (Canvas->ClipY - SizeY) * 0.5f : (VAlign == EAlignVertical::Top) ? Canvas->OrgY + OffsetY : Canvas->ClipY - SizeY - OffsetY; const float BoxPadding = 5.0f; const float X = PosX - BoxPadding; const float Y = PosY - BoxPadding; const float Z = 0.1f; FCanvasTileItem TileItem(FVector2D(X, Y), FVector2D(SizeX + BoxPadding * 2.0f, SizeY + BoxPadding * 2.0f), FLinearColor(0.75f, 0.75f, 0.75f, Alpha)); Canvas->DrawItem(TileItem); FLinearColor TextCol(TextColor); TextCol.A = Alpha; FCanvasTextItem TextItem(FVector2D(PosX, PosY), FText::FromString(Text), GEngine->GetSmallFont(), TextCol); Canvas->DrawItem(TextItem); OffsetY += 25; }
void UPaperFlipbookThumbnailRenderer::Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* RenderTarget, FCanvas* Canvas) { if (UPaperFlipbook* Flipbook = Cast<UPaperFlipbook>(Object)) { const double DeltaTime = FApp::GetCurrentTime() - GStartTime; const float TotalDuration = Flipbook->GetTotalDuration(); const float PlayTime = (TotalDuration > 0.0f) ? FMath::Fmod(DeltaTime, TotalDuration) : 0.0f; if (UPaperSprite* Sprite = Flipbook->GetSpriteAtTime(PlayTime)) { DrawFrame(Sprite, X, Y, Width, Height, RenderTarget, Canvas); return; } else { // Fallback for empty frames or newly created flipbooks DrawGrid(X, Y, Width, Height, Canvas); } if (TotalDuration == 0.0f) { // Warning text for no frames const FText ErrorText = NSLOCTEXT("FlipbookEditorApp", "ThumbnailWarningNoFrames", "No frames"); FCanvasTextItem TextItem(FVector2D(5.0f, 5.0f), ErrorText, GEngine->GetLargeFont(), FLinearColor::Red); TextItem.EnableShadow(FLinearColor::Black); TextItem.Scale = FVector2D(Width / 128.0f, Height / 128.0f); TextItem.Draw(Canvas); } } }
/** draws high score */ void APlatformerHUD::DrawHighscore() { const FText Highscore = LOCTEXT("Highscore", "High score"); const float SizeX = Canvas->ClipX * 0.4f; const float SizeY = 1000 * UIScale; const float DrawX = (Canvas->ClipX - SizeX) / 2.0f; const float DrawY = (Canvas->ClipY - SizeY) / 2.0f; DrawBorder(DrawX, DrawY, SizeX, SizeY, 1.0f, BlueBorder); const float TextScale = 1.4f; const float TextMargin = 0.03f; float StrSizeX, StrSizeY; Canvas->StrLen(HUDFont, Highscore.ToString(), StrSizeX, StrSizeY); StrSizeX = StrSizeX * TextScale * UIScale; StrSizeY = StrSizeY * TextScale * UIScale; FCanvasTextItem TextItem( FVector2D( (Canvas->ClipX - StrSizeX) / 2.0f, DrawY + SizeY * TextMargin ), Highscore, HUDFont, FLinearColor::White ); TextItem.Scale = FVector2D( TextScale * UIScale, TextScale * UIScale ); TextItem.EnableShadow( FLinearColor::Transparent ); Canvas->DrawItem( TextItem ); const float BorderSize = BlueBorder.LeftBorder->Resource->GetSizeX() * UIScale; FCanvasTileItem TileItem(FVector2D( DrawX + BorderSize, DrawY + SizeY * TextMargin + StrSizeY ), BlueBorder.TopBorder->Resource, FVector2D( SizeX - 2* BorderSize, BlueBorder.TopBorder->Resource->GetSizeY() * UIScale ), FVector2D(0,0),FVector2D((SizeX - 2* BorderSize) / BlueBorder.TopBorder->Resource->GetSizeX() * UIScale,1), FLinearColor::White); TileItem.BlendMode = SE_BLEND_Translucent; Canvas->DrawItem(TileItem); const float StartY = DrawY + SizeY * TextMargin*3 + StrSizeY * TextScale * UIScale; const float ColWidths[] = {70*UIScale, 340*UIScale, 200*UIScale}; const float TotalWidth = ColWidths[0]+ColWidths[1]+ColWidths[2]; for (int32 i=0; i < 10; i++ ) { FText Texts[] = { FText::Format(FText::FromString("{0}."), FText::AsNumber(i+1)), FText::FromString(UPlatformerBlueprintLibrary::DescribeTime(HighscoreTimes[i], false)), FText::FromString(HighscoreNames[i]) }; float Offset = 0; for (uint8 column=0; column < 3; column++) { TextItem.Text = Texts[column]; Canvas->StrLen(HUDFont, TextItem.Text.ToString(), StrSizeX, StrSizeY); StrSizeX = StrSizeX * TextScale * UIScale; StrSizeY = StrSizeY * TextScale * UIScale; TextItem.Position = FVector2D((Canvas->ClipX - TotalWidth) / 2.0f + Offset + ColWidths[column] - StrSizeX, StartY + i * StrSizeY); Canvas->DrawItem( TextItem ); Offset += ColWidths[column]; } } }
const TextItems getWords(const TextBoxList &list) { TextItems items; foreach (const PdfTextBox &box, list) { QString word = box->text().trimmed(); for (int i = 0; i < word.length(); ++i) word[i] = canonicalizedCharacter(word[i]); // DON'T DO: if (!word.isEmpty()) words << word; // since it can mess up highlighting. items.append(TextItem(word, box->boundingBox())); }
void FSpriteEditorViewportClient::DrawRenderStats(FViewport& InViewport, FSceneView& View, FCanvas& Canvas, class UPaperSprite* Sprite, int32& YPos) { FCanvasTextItem TextItem(FVector2D(6, YPos), LOCTEXT("RenderGeomBaked", "Render Geometry (baked)"), GEngine->GetSmallFont(), FLinearColor::White); TextItem.EnableShadow(FLinearColor::Black); TextItem.Draw(&Canvas); TextItem.Position += FVector2D(6.0f, 18.0f); int32 NumOpaqueTriangles = 0; int32 NumMaskedTriangles = 0; int32 NumTranslucentTriangles = 0; AnalyzeSpriteMaterialType(Sprite, /*out*/ NumOpaqueTriangles, /*out*/ NumMaskedTriangles, /*out*/ NumTranslucentTriangles); int32 NumSections = (Sprite->AlternateMaterialSplitIndex != INDEX_NONE) ? 2 : 1; if (NumSections > 1) { TextItem.Text = FText::Format(LOCTEXT("SectionCount", "Sections: {0}"), FText::AsNumber(NumSections)); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } // Draw the number of triangles if (NumOpaqueTriangles > 0) { TextItem.Text = FText::Format(LOCTEXT("OpaqueTriangleCount", "Triangles: {0} (opaque)"), FText::AsNumber(NumOpaqueTriangles)); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } if (NumMaskedTriangles > 0) { TextItem.Text = FText::Format(LOCTEXT("MaskedTriangleCount", "Triangles: {0} (masked)"), FText::AsNumber(NumMaskedTriangles)); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } if (NumTranslucentTriangles > 0) { TextItem.Text = FText::Format(LOCTEXT("TranslucentTriangleCount", "Triangles: {0} (translucent)"), FText::AsNumber(NumTranslucentTriangles)); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } if ((NumOpaqueTriangles + NumMaskedTriangles + NumTranslucentTriangles) == 0) { static const FText NoShapesPrompt = LOCTEXT("NoRenderDataWarning", "Warning: No rendering triangles (create a new shape using the toolbar)"); TextItem.Text = NoShapesPrompt; TextItem.SetColor(FLinearColor::Yellow); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } YPos = (int32)TextItem.Position.Y; }
void AHUD::DrawText(FString const& Text, FLinearColor Color, float ScreenX, float ScreenY, UFont* Font, float Scale, bool bScalePosition) { if (IsCanvasValid_WarnIfNot()) { if (bScalePosition) { ScreenX *= Scale; ScreenY *= Scale; } FCanvasTextItem TextItem( FVector2D( ScreenX, ScreenY ), FText::FromString(Text), Font ? Font : GEngine->GetMediumFont(), Color ); TextItem.Scale = FVector2D( Scale, Scale ); Canvas->DrawItem( TextItem ); } }
void AVisualLoggerHUD::PostRender() { static const FColor TextColor(200, 200, 128, 255); Super::Super::PostRender(); if (bShowHUD) { AVisualLoggerCameraController* DebugCamController = Cast<AVisualLoggerCameraController>(PlayerOwner); if( DebugCamController != NULL ) { FCanvasTextItem TextItem( FVector2D::ZeroVector, FText::GetEmpty(), GEngine->GetSmallFont(), TextColor); TextItem.FontRenderInfo = TextRenderInfo; float X = Canvas->SizeX * 0.025f+1; float Y = Canvas->SizeX * 0.025f+1; FVector const CamLoc = DebugCamController->PlayerCameraManager->GetCameraLocation(); FRotator const CamRot = DebugCamController->PlayerCameraManager->GetCameraRotation(); FCollisionQueryParams TraceParams(NAME_None, true, this); FHitResult Hit; bool bHit = GetWorld()->LineTraceSingle(Hit, CamLoc, CamRot.Vector() * 100000.f + CamLoc, ECC_Pawn, TraceParams); if( bHit ) { TextItem.Text = FText::FromString(FString::Printf(TEXT("Under cursor: '%s'"), *Hit.GetActor()->GetName())); Canvas->DrawItem( TextItem, X, Y ); DrawDebugLine( GetWorld(), Hit.Location, Hit.Location+Hit.Normal*30.f, FColor::White ); } else { TextItem.Text = LOCTEXT("NotActorUnderCursor", "Not actor under cursor" ); } Canvas->DrawItem( TextItem, X, Y ); Y += TextItem.DrawnSize.Y; if (DebugCamController->PickedActor != NULL) { TextItem.Text = FText::FromString(FString::Printf(TEXT("Selected: '%s'"), *DebugCamController->PickedActor->GetName())); Canvas->DrawItem( TextItem, X, Y ); } } } }
void FVisualLoggerCanvasRenderer::DrawOnCanvas(class UCanvas* Canvas, class APlayerController*) { if (GEngine == NULL || CurrentTimeLine.IsValid() == false) { return; } UWorld* World = FLogVisualizer::Get().GetWorld(); if (World == NULL) { return; } UFont* Font = GEngine->GetSmallFont(); FCanvasTextItem TextItem(FVector2D::ZeroVector, FText::GetEmpty(), Font, FLinearColor::White); const FString TimeStampString = FString::Printf(TEXT("%.2f"), SelectedEntry.TimeStamp); LogVisualizer::DrawTextShadowed(Canvas, Font, TimeStampString, SelectedEntry.Location); for (const auto CurrentData : SelectedEntry.DataBlocks) { const FName TagName = CurrentData.TagName; const bool bIsValidByFilter = FCategoryFiltersManager::Get().MatchCategoryFilters(CurrentData.Category.ToString(), ELogVerbosity::All) && FCategoryFiltersManager::Get().MatchCategoryFilters(CurrentData.TagName.ToString(), ELogVerbosity::All); FVisualLogExtensionInterface* Extension = FVisualLogger::Get().GetExtensionForTag(TagName); if (!Extension) { continue; } if (!bIsValidByFilter) { Extension->DisableDrawingForData(FLogVisualizer::Get().GetWorld(), Canvas, NULL, TagName, CurrentData, SelectedEntry.TimeStamp); } else { Extension->DrawData(FLogVisualizer::Get().GetWorld(), Canvas, NULL, TagName, CurrentData, SelectedEntry.TimeStamp); } } if (ULogVisualizerSessionSettings::StaticClass()->GetDefaultObject<ULogVisualizerSessionSettings>()->bEnableGraphsVisualization) { DrawHistogramGraphs(Canvas, NULL); } }
void APlatformerHUD::DrawMessage(FString Message, float PosX, float PosY, float TextScale, FLinearColor TextColor, bool bRedBorder) { if (Canvas) { float SizeX, SizeY; Canvas->StrLen(HUDFont, Message, SizeX, SizeY); const float DrawX = Canvas->ClipX * FMath::Clamp(PosX, 0.0f, 1.0f) - (SizeX * TextScale * 0.5f * UIScale); const float DrawY = Canvas->ClipY * FMath::Clamp(PosY, 0.0f, 1.0f) - (SizeY * TextScale * 0.5f * UIScale); const float BoxPadding = 8.0f * UIScale; DrawBorder(DrawX - BoxPadding, DrawY - BoxPadding, (SizeX * TextScale * UIScale) + (BoxPadding * 2.0f), (SizeY * TextScale * UIScale) + (BoxPadding * 2.0f), 0.4f, bRedBorder ? RedBorder : BlueBorder); FCanvasTextItem TextItem( FVector2D( DrawX, DrawY ), FText::FromString( Message ), HUDFont, TextColor ); TextItem.Scale = FVector2D( TextScale*UIScale, TextScale*UIScale ); TextItem.EnableShadow( FLinearColor::Transparent ); Canvas->DrawItem( TextItem ); } }
static void DrawAngles(FCanvas* Canvas, int32 XPos, int32 YPos, EAxisList::Type ManipAxis, FWidget::EWidgetMode MoveMode, const FRotator& Rotation, const FVector& Translation) { FString OutputString(TEXT("")); if (MoveMode == FWidget::WM_Rotate && Rotation.IsZero() == false) { //Only one value moves at a time const FVector EulerAngles = Rotation.Euler(); if (ManipAxis == EAxisList::X) { OutputString += FString::Printf(TEXT("Roll: %0.2f"), EulerAngles.X); } else if (ManipAxis == EAxisList::Y) { OutputString += FString::Printf(TEXT("Pitch: %0.2f"), EulerAngles.Y); } else if (ManipAxis == EAxisList::Z) { OutputString += FString::Printf(TEXT("Yaw: %0.2f"), EulerAngles.Z); } } else if (MoveMode == FWidget::WM_Translate && Translation.IsZero() == false) { //Only one value moves at a time if (ManipAxis == EAxisList::X) { OutputString += FString::Printf(TEXT(" %0.2f"), Translation.X); } else if (ManipAxis == EAxisList::Y) { OutputString += FString::Printf(TEXT(" %0.2f"), Translation.Y); } else if (ManipAxis == EAxisList::Z) { OutputString += FString::Printf(TEXT(" %0.2f"), Translation.Z); } } if (OutputString.Len() > 0) { FCanvasTextItem TextItem( FVector2D(XPos, YPos), FText::FromString( OutputString ), GEngine->GetSmallFont(), FLinearColor::White ); Canvas->DrawItem( TextItem ); } }
void FSpriteEditorViewportClient::DrawRelatedSprites(FViewport& InViewport, FSceneView& View, FCanvas& Canvas, const FLinearColor& BoundsColor, const FLinearColor& NameColor) { const FLinearColor ShadowColor = FLinearColor::Black; for (int32 SpriteIndex = 0; SpriteIndex < RelatedSprites.Num(); ++SpriteIndex) { FRelatedSprite& RelatedSprite = RelatedSprites[SpriteIndex]; const FVector2D& SourceUV = RelatedSprite.SourceUV; const FVector2D& SourceDimension = RelatedSprite.SourceDimension; if (bShowNamesForSprites) { const FVector2D TextPos = SourceTextureSpaceToScreenSpace(View, SourceUV + SourceDimension*0.5f); const FText AssetNameText = FText::AsCultureInvariant(RelatedSprite.AssetData.AssetName.ToString()); FCanvasTextItem TextItem(TextPos, AssetNameText, GEngine->GetSmallFont(), NameColor); TextItem.EnableShadow(ShadowColor); TextItem.bCentreX = true; TextItem.bCentreY = true; TextItem.Draw(&Canvas); } if (bShowRelatedSprites) { FVector2D BoundsVertices[4]; BoundsVertices[0] = SourceTextureSpaceToScreenSpace(View, SourceUV); BoundsVertices[1] = SourceTextureSpaceToScreenSpace(View, SourceUV + FVector2D(SourceDimension.X, 0)); BoundsVertices[2] = SourceTextureSpaceToScreenSpace(View, SourceUV + FVector2D(SourceDimension.X, SourceDimension.Y)); BoundsVertices[3] = SourceTextureSpaceToScreenSpace(View, SourceUV + FVector2D(0, SourceDimension.Y)); for (int32 VertexIndex = 0; VertexIndex < 4; ++VertexIndex) { const int32 NextVertexIndex = (VertexIndex + 1) % 4; FCanvasLineItem LineItem(BoundsVertices[VertexIndex], BoundsVertices[NextVertexIndex]); LineItem.SetColor(BoundsColor); Canvas.DrawItem(LineItem); } } } }
void FGameplayDebuggerCanvasContext::Print(const FColor& Color, const FString& String) { FTaggedStringParser Parser(Color); Parser.ParseString(String); const float LineHeight = GetLineHeight(); for (int32 NodeIdx = 0; NodeIdx < Parser.NodeList.Num(); NodeIdx++) { const FTaggedStringParser::FNode& NodeData = Parser.NodeList[NodeIdx]; if (NodeData.bNewLine) { if (Canvas.IsValid() && (CursorY + LineHeight) > Canvas->ClipY) { DefaultX += Canvas->ClipX / 2; CursorY = 0.0f; } CursorX = DefaultX; CursorY += LineHeight; } if (NodeData.String.Len() > 0) { float SizeX = 0.0f, SizeY = 0.0f; MeasureString(NodeData.String, SizeX, SizeY); FCanvasTextItem TextItem(FVector2D::ZeroVector, FText::FromString(NodeData.String), Font.Get(), FLinearColor(NodeData.Color)); if (FontRenderInfo.bEnableShadow) { TextItem.EnableShadow(FColor::Black, FVector2D(1, 1)); } DrawItem(TextItem, CursorX, CursorY); CursorX += SizeX; } } MoveToNewLine(); }
void FSingleTileEditorViewportClient::DrawCanvas(FViewport& InViewport, FSceneView& View, FCanvas& Canvas) { const bool bIsHitTesting = Canvas.IsHitTesting(); if (!bIsHitTesting) { Canvas.SetHitProxy(nullptr); } int32 YPos = 42; if (TileBeingEditedIndex != INDEX_NONE) { // Draw the collision geometry stats YPos += 60; //@TODO: Need a better way to determine this from the editor mode if (bShowStats) { bool bHasCollision = false; if (const FPaperTileMetadata* TileData = TileSet->GetTileMetadata(TileBeingEditedIndex)) { if (TileData->HasCollision()) { bHasCollision = true; FSpriteGeometryEditMode::DrawGeometryStats(InViewport, View, Canvas, TileData->CollisionData, false, /*inout*/ YPos); } } if (!bHasCollision) { FCanvasTextItem TextItem(FVector2D(6, YPos), LOCTEXT("NoCollisionDataMainScreen", "No collision data"), GEngine->GetSmallFont(), FLinearColor::White); TextItem.EnableShadow(FLinearColor::Black); TextItem.Draw(&Canvas); } } } FEditorViewportClient::DrawCanvas(InViewport, View, Canvas); }
void LineParser::q(const std::string& s) { size_t pitch = m_pitch; std::string::size_type i = 0; while(i < s.length()) { std::string z; while(i < s.length() && s[i] != '[') z += s[i++]; if (!z.empty()) addItem(TextItem(z, pitch, m_rate, m_volume)); if (i < s.length() && s[i] == '[') { // We have Dectalk control sequence z.erase(); i++; // skip '[' while(i < s.length() && s[i] != ']') z += s[i++]; processDectalkCommand(z, pitch); i++; // skip ']' } //dectalk commands processing; } //while(s[i]); }
void FSpriteEditorViewportClient::DrawSourceRegion(FViewport& InViewport, FSceneView& View, FCanvas& Canvas, const FLinearColor& GeometryVertexColor) { const bool bIsHitTesting = Canvas.IsHitTesting(); UPaperSprite* Sprite = GetSpriteBeingEdited(); const float CornerCollisionVertexSize = 8.0f; const float EdgeCollisionVertexSize = 6.0f; const FLinearColor GeometryLineColor(GeometryVertexColor.R, GeometryVertexColor.G, GeometryVertexColor.B, 0.5f * GeometryVertexColor.A); const bool bDrawEdgeHitProxies = true; const bool bDrawCornerHitProxies = true; FVector2D BoundsVertices[4]; BoundsVertices[0] = SourceTextureSpaceToScreenSpace(View, Sprite->SourceUV); BoundsVertices[1] = SourceTextureSpaceToScreenSpace(View, Sprite->SourceUV + FVector2D(Sprite->SourceDimension.X, 0)); BoundsVertices[2] = SourceTextureSpaceToScreenSpace(View, Sprite->SourceUV + FVector2D(Sprite->SourceDimension.X, Sprite->SourceDimension.Y)); BoundsVertices[3] = SourceTextureSpaceToScreenSpace(View, Sprite->SourceUV + FVector2D(0, Sprite->SourceDimension.Y)); if (bShowNamesForSprites) { const FVector2D TextPos = SourceTextureSpaceToScreenSpace(View, Sprite->SourceUV + FVector2D(Sprite->SourceDimension.X*0.5f, Sprite->SourceDimension.Y*0.5f)); const FText AssetNameText = FText::AsCultureInvariant(Sprite->GetName()); FCanvasTextItem TextItem(TextPos, AssetNameText, GEngine->GetSmallFont(), FLinearColor::White); TextItem.EnableShadow(FLinearColor::Black); TextItem.bCentreX = true; TextItem.bCentreY = true; TextItem.Draw(&Canvas); } for (int32 VertexIndex = 0; VertexIndex < 4; ++VertexIndex) { const int32 NextVertexIndex = (VertexIndex + 1) % 4; // Draw the edge if (bIsHitTesting) { TSharedPtr<FSpriteSelectedSourceRegion> Data = MakeShareable(new FSpriteSelectedSourceRegion()); Data->SpritePtr = Sprite; Data->VertexIndex = 4 + VertexIndex; Canvas.SetHitProxy(new HSpriteSelectableObjectHitProxy(Data)); } FCanvasLineItem LineItem(BoundsVertices[VertexIndex], BoundsVertices[NextVertexIndex]); LineItem.SetColor(GeometryVertexColor); Canvas.DrawItem(LineItem); // Add edge hit proxy if (bDrawEdgeHitProxies) { const FVector2D MidPoint = (BoundsVertices[VertexIndex] + BoundsVertices[NextVertexIndex]) * 0.5f; Canvas.DrawTile(MidPoint.X - EdgeCollisionVertexSize*0.5f, MidPoint.Y - EdgeCollisionVertexSize*0.5f, EdgeCollisionVertexSize, EdgeCollisionVertexSize, 0.f, 0.f, 1.f, 1.f, GeometryVertexColor, GWhiteTexture); } if (bIsHitTesting) { Canvas.SetHitProxy(nullptr); } // Add corner hit proxy if (bDrawCornerHitProxies) { const FVector2D CornerPoint = BoundsVertices[VertexIndex]; if (bIsHitTesting) { TSharedPtr<FSpriteSelectedSourceRegion> Data = MakeShareable(new FSpriteSelectedSourceRegion()); Data->SpritePtr = Sprite; Data->VertexIndex = VertexIndex; Canvas.SetHitProxy(new HSpriteSelectableObjectHitProxy(Data)); } Canvas.DrawTile(CornerPoint.X - CornerCollisionVertexSize * 0.5f, CornerPoint.Y - CornerCollisionVertexSize * 0.5f, CornerCollisionVertexSize, CornerCollisionVertexSize, 0.f, 0.f, 1.f, 1.f, GeometryVertexColor, GWhiteTexture); if (bIsHitTesting) { Canvas.SetHitProxy(nullptr); } } } }
void FVisualLoggerCanvasRenderer::DrawOnCanvas(class UCanvas* Canvas, class APlayerController*) { if (GEngine == NULL) { return; } UWorld* World = FLogVisualizer::Get().GetWorld(); if (World == NULL) { return; } UFont* Font = GEngine->GetSmallFont(); FCanvasTextItem TextItem(FVector2D::ZeroVector, FText::GetEmpty(), Font, FLinearColor::White); const FString TimeStampString = FString::Printf(TEXT("%.2f"), SelectedEntry.TimeStamp); LogVisualizer::DrawTextShadowed(Canvas, Font, TimeStampString, SelectedEntry.Location); if (bDirtyData && FLogVisualizer::Get().GetTimeSliderController().IsValid()) { const FVisualLoggerTimeSliderArgs& TimeSliderArgs = FLogVisualizer::Get().GetTimeSliderController()->GetTimeSliderArgs(); TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get(); const float LocalViewRangeMin = LocalViewRange.GetLowerBoundValue(); const float LocalViewRangeMax = LocalViewRange.GetUpperBoundValue(); const float LocalSequenceLength = LocalViewRangeMax - LocalViewRangeMin; const float WindowHalfWidth = LocalSequenceLength * TimeSliderArgs.CursorSize.Get() * 0.5f; const FVector2D TimeStampWindow(SelectedEntry.TimeStamp - WindowHalfWidth, SelectedEntry.TimeStamp + WindowHalfWidth); CollectedGraphs.Reset(); const TArray<FName>& RowNames = FVisualLoggerDatabase::Get().GetSelectedRows(); for (auto RowName : RowNames) { if (FVisualLoggerDatabase::Get().IsRowVisible(RowName) == false) { continue; } const TArray<FVisualLoggerGraph>& AllGraphs = FVisualLoggerGraphsDatabase::Get().GetGraphsByOwnerName(RowName); for (const FVisualLoggerGraph& CurrentGraph : AllGraphs) { const FName GraphName = CurrentGraph.GetGraphName(); const FName OwnerName = CurrentGraph.GetOwnerName(); if (FVisualLoggerGraphsDatabase::Get().IsGraphVisible(OwnerName, GraphName) == false) { continue; } for (auto DataIt(CurrentGraph.GetConstDataIterator()); DataIt; ++DataIt) { const FVisualLoggerGraphData& GraphData = *DataIt; const bool bIsGraphDataDisabled = FVisualLoggerFilters::Get().IsGraphDataDisabled(GraphName, GraphData.DataName); if (bIsGraphDataDisabled) { continue; } const FName FullGraphName = *FString::Printf(TEXT("%s$%s"), *RowName.ToString(), *GraphName.ToString()); FGraphData &CollectedGraphData = CollectedGraphs.FindOrAdd(FullGraphName); FGraphLineData &LineData = CollectedGraphData.GraphLines.FindOrAdd(GraphData.DataName); LineData.DataName = GraphData.DataName; LineData.Samples = GraphData.Samples; LineData.LeftExtreme = FVector2D::ZeroVector; LineData.RightExtreme = FVector2D::ZeroVector; int32 LeftSideOutsideIndex = INDEX_NONE; int32 RightSideOutsideIndex = INDEX_NONE; for (int32 SampleIndex = 0; SampleIndex < GraphData.Samples.Num(); SampleIndex++) { const FVector2D& SampleValue = GraphData.Samples[SampleIndex]; CollectedGraphData.Min.X = FMath::Min(CollectedGraphData.Min.X, SampleValue.X); CollectedGraphData.Min.Y = FMath::Min(CollectedGraphData.Min.Y, SampleValue.Y); CollectedGraphData.Max.X = FMath::Max(CollectedGraphData.Max.X, SampleValue.X); CollectedGraphData.Max.Y = FMath::Max(CollectedGraphData.Max.Y, SampleValue.Y); const float CurrentTimeStamp = GraphData.TimeStamps[SampleIndex]; if (CurrentTimeStamp < TimeStampWindow.X) { LineData.LeftExtreme = SampleValue; } else if (CurrentTimeStamp > TimeStampWindow.Y) { LineData.RightExtreme = SampleValue; break; } } } } } bDirtyData = false; } if (ULogVisualizerSessionSettings::StaticClass()->GetDefaultObject<ULogVisualizerSessionSettings>()->bEnableGraphsVisualization) { DrawHistogramGraphs(Canvas, NULL); } const TMap<FName, FVisualLogExtensionInterface*>& Extensions = FVisualLogger::Get().GetAllExtensions(); for (const auto& CurrentExtension : Extensions) { CurrentExtension.Value->DrawData(FVisualLoggerEditorInterface::Get(), Canvas); } }
void AGameplayDebuggingHUDComponent::PrintString(FPrintContext& Context, const FString& InString ) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) class FParserHelper { enum TokenType { OpenTag, CloseTag, NewLine, EndOfString, RegularChar, Tab }; enum Tag { DefinedColor, OtherColor, ErrorTag, }; uint8 ReadToken() { uint8 OutToken = RegularChar; TCHAR ch = Index < DataString.Len() ? DataString[Index] : '\0'; switch(ch) { case '\0': OutToken = EndOfString; break; case '{': OutToken = OpenTag; Index++; break; case '}': OutToken = CloseTag; Index++; break; case '\n': OutToken = NewLine; Index++; break; case '\t': OutToken = Tab; Index++; break; default: OutToken = RegularChar; break; } return OutToken; } uint32 ParseTag( FString& OutData ) { FString TagString; int32 OryginalIndex = Index; uint8 token = ReadToken(); while (token != EndOfString && token != CloseTag) { if (token == RegularChar) { TagString.AppendChar(DataString[Index++]); } token = ReadToken(); } int OutTag = ErrorTag; if (token != CloseTag) { Index = OryginalIndex; OutData = FString::Printf( TEXT("{%s"), *TagString); OutData.AppendChar(DataString[Index-1]); return OutTag; } if (GColorList.IsValidColorName(*TagString.ToLower())) { OutTag = DefinedColor; OutData = TagString; } else { FColor Color; if (Color.InitFromString(TagString)) { OutTag = OtherColor; OutData = TagString; } else { OutTag = ErrorTag; OutData = FString::Printf( TEXT("{%s"), *TagString); OutData.AppendChar(DataString[Index-1]); } } //Index++; return OutTag; } struct StringNode { FString String; FColor Color; bool bNewLine; StringNode() : Color(FColor::White), bNewLine(false) {} }; int32 Index; FString DataString; public: TArray<StringNode> Strings; void ParseString(const FString& StringToParse) { Index = 0; DataString = StringToParse; Strings.Add(StringNode()); if (Index >= DataString.Len()) return; uint8 Token = ReadToken(); while (Token != EndOfString) { switch (Token) { case RegularChar: Strings[Strings.Num()-1].String.AppendChar( DataString[Index++] ); break; case NewLine: Strings.Add(StringNode()); Strings[Strings.Num()-1].bNewLine = true; Strings[Strings.Num()-1].Color = Strings[Strings.Num()-2].Color; break; case EndOfString: break; case Tab: { const FString TabString(TEXT(" ")); Strings[Strings.Num()-1].String.Append(TabString); static bool sbTest = false; if (sbTest) { Index++; } break; } case OpenTag: { FString OutData; switch (ParseTag(OutData)) { case DefinedColor: { int32 i = Strings.Add(StringNode()); Strings[i].Color = GColorList.GetFColorByName(*OutData.ToLower()); } break; case OtherColor: { FColor NewColor; if (NewColor.InitFromString( OutData )) { int32 i = Strings.Add(StringNode()); Strings[i].Color = NewColor; break; } } default: Strings[Strings.Num()-1].String += OutData; break; } } break; } Token = ReadToken(); } } }; FParserHelper Helper; Helper.ParseString( InString ); float YMovement = 0; float XL = 0.f, YL = 0.f; CalulateStringSize(Context, Context.Font, TEXT("X"), XL, YL); for (int32 Index=0; Index < Helper.Strings.Num(); ++Index) { if (Index > 0 && Helper.Strings[Index].bNewLine) { if (Context.Canvas != NULL && YMovement + Context.CursorY > Context.Canvas->ClipY) { Context.DefaultX += Context.Canvas->ClipX / 2; Context.CursorX = Context.DefaultX; Context.CursorY = Context.DefaultY; YMovement = 0; } YMovement += YL; Context.CursorX = Context.DefaultX; } const FString Str = Helper.Strings[Index].String; if (Str.Len() > 0 && Context.Canvas != NULL) { float SizeX, SizeY; CalulateStringSize(Context, Context.Font, Str, SizeX, SizeY); FCanvasTextItem TextItem( FVector2D::ZeroVector, FText::FromString(Str), Context.Font, FLinearColor::White ); if (Context.FontRenderInfo.bEnableShadow) { TextItem.EnableShadow( FColor::Black, FVector2D( 1, 1 ) ); } TextItem.SetColor(Helper.Strings[Index].Color); DrawItem( Context, TextItem, Context.CursorX, YMovement + Context.CursorY ); Context.CursorX += SizeX; } } Context.CursorY += YMovement; #endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST) }
void FPhATEdPreviewViewportClient::DrawCanvas( FViewport& InViewport, FSceneView& View, FCanvas& Canvas ) { if (!PhATPtr.IsValid()) { return; } // Turn on/off the ground box SharedData->EditorFloorComp->SetVisibility(SharedData->bDrawGround); float W, H; PhATFont->GetCharSize(TEXT('L'), W, H); const float XOffset = 5.0f; const float YOffset = 32.0f; FCanvasTextItem TextItem( FVector2D::ZeroVector, FText::GetEmpty(), PhATFont, FLinearColor::White ); // Write body/constraint count at top. FString StatusString = FText::Format( NSLOCTEXT("UnrealEd", "BodiesConstraints_F", "{0} Bodies {1} Considered for bounds {2} Ratio {3} Constraints"), FText::AsNumber(SharedData->PhysicsAsset->SkeletalBodySetups.Num()), FText::AsNumber(SharedData->PhysicsAsset->BoundsBodies.Num()), FText::AsNumber(static_cast<float>(SharedData->PhysicsAsset->BoundsBodies.Num())/static_cast<float>(SharedData->PhysicsAsset->SkeletalBodySetups.Num())), FText::AsNumber(SharedData->PhysicsAsset->ConstraintSetup.Num()) ).ToString(); TextItem.Text = FText::FromString( StatusString ); Canvas.DrawItem( TextItem, XOffset, YOffset); TextItem.Text = FText::GetEmpty(); if (SharedData->bRunningSimulation) { #if PLATFORM_MAC TextItem.Text = NSLOCTEXT("UnrealEd", "Sim_Mac", "SIM: Command+RightMouse to interact with bodies"); #else TextItem.Text = NSLOCTEXT("UnrealEd", "Sim", "SIM: Ctrl+RightMouse to interact with bodies"); #endif } else if (SharedData->bSelectionLock) { TextItem.Text = NSLOCTEXT("UnrealEd", "Lock", "LOCK"); }else if(SharedData->EditingMode == FPhATSharedData::PEM_ConstraintEdit) { if(GetWidgetMode() == FWidget::WM_Translate) { TextItem.Text = NSLOCTEXT("UnrealEd", "SingleMove", "hold ALT to move a single reference frame"); }else if(GetWidgetMode() == FWidget::WM_Rotate) { TextItem.Text = NSLOCTEXT("UnrealEd", "DoubleRotate", "hold ALT to rotate both reference frames"); } } Canvas.DrawItem( TextItem, XOffset, Viewport->GetSizeXY().Y - (3 + H) ); // Draw current physics weight if (SharedData->bRunningSimulation) { FString PhysWeightString = FString::Printf(TEXT("Phys Blend: %3.0f pct"), SharedData->EditorSimOptions->PhysicsBlend * 100.f); int32 PWLW, PWLH; StringSize(PhATFont, PWLW, PWLH, *PhysWeightString); TextItem.Text = FText::FromString(PhysWeightString); Canvas.DrawItem( TextItem, Viewport->GetSizeXY().X - (3 + PWLW + 2*W), Viewport->GetSizeXY().Y - (3 + H) ); } int32 HalfX = (Viewport->GetSizeXY().X-XOffset)/2; int32 HalfY = Viewport->GetSizeXY().Y/2; if ((SharedData->bShowHierarchy && SharedData->EditorSimOptions->bShowNamesInHierarchy)) { // Iterate over each graphics bone. for (int32 i = 0; i < SharedData->EditorSkelComp->GetNumComponentSpaceTransforms(); ++i) { FVector BonePos = SharedData->EditorSkelComp->ComponentToWorld.TransformPosition(SharedData->EditorSkelComp->GetComponentSpaceTransforms()[i].GetLocation()); FPlane proj = View.Project(BonePos); if (proj.W > 0.f) // This avoids drawing bone names that are behind us. { int32 XPos = HalfX + (HalfX * proj.X); int32 YPos = HalfY + (HalfY * (proj.Y * -1)); FName BoneName = SharedData->EditorSkelMesh->RefSkeleton.GetBoneName(i); FColor BoneNameColor = FColor::White; //iterate through selected bones and see if any match for(int32 j=0; j< SharedData->SelectedBodies.Num(); ++j) { int32 SelectedBodyIndex = SharedData->SelectedBodies[j].Index; FName SelectedBoneName = SharedData->PhysicsAsset->SkeletalBodySetups[SelectedBodyIndex]->BoneName; if(SelectedBoneName == BoneName) { BoneNameColor = FColor::Green; break; } } if (Canvas.IsHitTesting()) { Canvas.SetHitProxy(new HPhATEdBoneNameProxy(i)); } TextItem.Text = FText::FromString(BoneName.ToString()); TextItem.SetColor(BoneNameColor); Canvas.DrawItem( TextItem, XPos, YPos ); if (Canvas.IsHitTesting()) { Canvas.SetHitProxy(NULL); } } } } // If showing center-of-mass, and physics is started up.. if (SharedData->bShowCOM) { // iterate over each bone for (int32 i = 0; i <SharedData->EditorSkelComp->Bodies.Num(); ++i) { FBodyInstance* BodyInst = SharedData->EditorSkelComp->Bodies[i]; check(BodyInst); FVector BodyCOMPos = BodyInst->GetCOMPosition(); float BodyMass = BodyInst->GetBodyMass(); FPlane Projection = View.Project(BodyCOMPos); if (Projection.W > 0.f) // This avoids drawing bone names that are behind us. { int32 XPos = HalfX + (HalfX * Projection.X); int32 YPos = HalfY + (HalfY * (Projection.Y * -1)); FString COMString = FString::Printf(TEXT("%3.3f"), BodyMass); TextItem.Text = FText::FromString(COMString); TextItem.SetColor(SharedData->COMRenderColor); Canvas.DrawItem( TextItem, XPos, YPos ); } } } }
void FCascadeEdPreviewViewportClient::Draw(FViewport* Viewport, FCanvas* Canvas) { if (!CascadePtr.IsValid()) { return; } Canvas->Clear( GetPreviewBackgroundColor()); // Clear out the lines from the previous frame CascadePreviewScene.ClearLineBatcher(); ULineBatchComponent* LineBatcher = CascadePreviewScene.GetLineBatcher(); CascadePreviewScene.RemoveComponent(LineBatcher); const FVector XAxis(1,0,0); const FVector YAxis(0,1,0); const FVector ZAxis(0,0,1); if (GetDrawElement(OriginAxis)) { FMatrix ArrowMatrix = FMatrix(XAxis, YAxis, ZAxis, FVector::ZeroVector); LineBatcher->DrawDirectionalArrow(ArrowMatrix, FColorList::Red, 10.f, 1.0f, SDPG_World); ArrowMatrix = FMatrix(YAxis, ZAxis, XAxis, FVector::ZeroVector); LineBatcher->DrawDirectionalArrow(ArrowMatrix, FColorList::Green, 10.f, 1.0f, SDPG_World); ArrowMatrix = FMatrix(ZAxis, XAxis, YAxis, FVector::ZeroVector); LineBatcher->DrawDirectionalArrow(ArrowMatrix, FColorList::Blue, 10.f, 1.0f, SDPG_World); } if (GetDrawElement(WireSphere)) { FVector Base(0.f); FColor WireColor = FColor::Red; const int32 NumRings = 16; const float RotatorMultiplier = 360.f / NumRings; const int32 NumSides = 32; for (int32 i = 0; i < NumRings; i++) { FVector RotXAxis; FVector RotYAxis; FVector RotZAxis; FRotationMatrix RotMatrix(FRotator(i * RotatorMultiplier, 0, 0)); RotXAxis = RotMatrix.TransformPosition(XAxis); RotYAxis = RotMatrix.TransformPosition(YAxis); RotZAxis = RotMatrix.TransformPosition(ZAxis); LineBatcher->DrawCircle(Base, RotXAxis, RotYAxis, WireColor, WireSphereRadius, NumSides, SDPG_World); LineBatcher->DrawCircle(Base, RotXAxis, RotZAxis, WireColor, WireSphereRadius, NumSides, SDPG_World); LineBatcher->DrawCircle(Base, RotYAxis, RotZAxis, WireColor, WireSphereRadius, NumSides, SDPG_World); RotMatrix = FRotationMatrix(FRotator(0, i * RotatorMultiplier, 0)); RotXAxis = RotMatrix.TransformPosition(XAxis); RotYAxis = RotMatrix.TransformPosition(YAxis); RotZAxis = RotMatrix.TransformPosition(ZAxis); LineBatcher->DrawCircle(Base, RotXAxis, RotYAxis, WireColor, WireSphereRadius, NumSides, SDPG_World); LineBatcher->DrawCircle(Base, RotXAxis, RotZAxis, WireColor, WireSphereRadius, NumSides, SDPG_World); LineBatcher->DrawCircle(Base, RotYAxis, RotZAxis, WireColor, WireSphereRadius, NumSides, SDPG_World); } } FEngineShowFlags SavedEngineShowFlags = EngineShowFlags; if (GetDrawElement(Bounds)) { EngineShowFlags.SetBounds(true); EngineShowFlags.Game = 1; } EngineShowFlags.SetVectorFields(GetDrawElement(VectorFields)); CascadePreviewScene.AddComponent(LineBatcher,FTransform::Identity); FEditorViewportClient::Draw(Viewport, Canvas); EngineShowFlags = SavedEngineShowFlags; FCanvasTextItem TextItem( FVector2D::ZeroVector, FText::GetEmpty(), GEngine->GetTinyFont(), FLinearColor::White ); if (GetDrawElement(ParticleCounts) || GetDrawElement(ParticleTimes) || GetDrawElement(ParticleEvents) || GetDrawElement(ParticleMemory) || GetDrawElement(ParticleSystemCompleted)) { // 'Up' from the lower left... FString strOutput; const int32 XPosition = Viewport->GetSizeXY().X - 5; int32 YPosition = Viewport->GetSizeXY().Y - (GetDrawElement(ParticleMemory) ? 15 : 5); UParticleSystemComponent* PartComp = CascadePtr.Pin()->GetParticleSystemComponent(); int32 iWidth, iHeight; if (PartComp->EmitterInstances.Num()) { for (int32 i = 0; i < PartComp->EmitterInstances.Num(); i++) { FParticleEmitterInstance* Instance = PartComp->EmitterInstances[i]; if (!Instance || !Instance->SpriteTemplate) { continue; } UParticleLODLevel* LODLevel = Instance->SpriteTemplate->GetCurrentLODLevel(Instance); if (!LODLevel) { continue; } strOutput = TEXT(""); if (Instance->SpriteTemplate->EmitterRenderMode != ERM_None) { UParticleLODLevel* HighLODLevel = Instance->SpriteTemplate->GetLODLevel(0); if (GetDrawElement(ParticleCounts)) { strOutput += FString::Printf(TEXT("%4d/%4d"), Instance->ActiveParticles, HighLODLevel->PeakActiveParticles); } if (GetDrawElement(ParticleTimes)) { if (GetDrawElement(ParticleCounts)) { strOutput += TEXT("/"); } strOutput += FString::Printf(TEXT("%8.4f/%8.4f"), Instance->EmitterTime, Instance->SecondsSinceCreation); } #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (GetDrawElement(ParticleEvents)) { if (GetDrawElement(ParticleCounts) || GetDrawElement(ParticleTimes)) { strOutput += TEXT("/"); } strOutput += FString::Printf(TEXT("Evts: %4d/%4d"), Instance->EventCount, Instance->MaxEventCount); } #endif //#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) UCanvas::ClippedStrLen(GEngine->GetTinyFont(), 1.0f, 1.0f, iWidth, iHeight, *strOutput); TextItem.SetColor( Instance->SpriteTemplate->EmitterEditorColor ); TextItem.Text = FText::FromString( strOutput ); Canvas->DrawItem( TextItem, XPosition - iWidth, YPosition - iHeight ); YPosition -= iHeight - 2; } } if (GetDrawElement(ParticleMemory)) { YPosition = Viewport->GetSizeXY().Y - 5; FString MemoryOutput = FString::Printf(TEXT("Template: %.0f KByte / Instance: %.0f KByte"), (float)ParticleSystemRootSize / 1024.0f + (float)ParticleModuleMemSize / 1024.0f, (float)PSysCompRootSize / 1024.0f + (float)PSysCompResourceSize / 1024.0f); UCanvas::ClippedStrLen(GEngine->GetTinyFont(), 1.0f, 1.0f, iWidth, iHeight, *MemoryOutput); TextItem.SetColor( FLinearColor::White ); TextItem.Text = FText::FromString( MemoryOutput ); Canvas->DrawItem( TextItem, XPosition - iWidth, YPosition - iHeight ); } } else { for (int32 i = 0; i < PartComp->Template->Emitters.Num(); i++) { strOutput = TEXT(""); UParticleEmitter* Emitter = PartComp->Template->Emitters[i]; UParticleLODLevel* LODLevel = Emitter->GetLODLevel(0); if (LODLevel && LODLevel->bEnabled && (Emitter->EmitterRenderMode != ERM_None)) { if (GetDrawElement(ParticleCounts)) { strOutput += FString::Printf(TEXT("%4d/%4d"), 0, LODLevel->PeakActiveParticles); } if (GetDrawElement(ParticleTimes)) { if (GetDrawElement(ParticleCounts)) { strOutput += TEXT("/"); } strOutput += FString::Printf(TEXT("%8.4f/%8.4f"), 0.f, 0.f); } #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (GetDrawElement(ParticleEvents)) { if (GetDrawElement(ParticleCounts) || GetDrawElement(ParticleTimes)) { strOutput += TEXT("/"); } strOutput += FString::Printf(TEXT("Evts: %4d/%4d"), 0, 0); } #endif //#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) UCanvas::ClippedStrLen(GEngine->GetTinyFont(), 1.0f, 1.0f, iWidth, iHeight, *strOutput); TextItem.SetColor( Emitter->EmitterEditorColor ); TextItem.Text = FText::FromString( strOutput ); Canvas->DrawItem( TextItem, XPosition - iWidth, YPosition - iHeight ); YPosition -= iHeight - 2; } } if (GetDrawElement(ParticleMemory)) { YPosition = Viewport->GetSizeXY().Y - 5; FString MemoryOutput = FString::Printf(TEXT("Template: %.0f KByte / Instance: %.0f KByte"), (float)ParticleSystemRootSize / 1024.0f + (float)ParticleModuleMemSize / 1024.0f, (float)PSysCompRootSize / 1024.0f + (float)PSysCompResourceSize / 1024.0f); UCanvas::ClippedStrLen(GEngine->GetTinyFont(), 1.0f, 1.0f, iWidth, iHeight, *MemoryOutput); TextItem.SetColor( FLinearColor::White ); TextItem.Text = FText::FromString( MemoryOutput ); Canvas->DrawItem( TextItem, XPosition - iWidth, YPosition - iHeight ); } } if (GetDrawElement(ParticleSystemCompleted)) { if (PartComp->HasCompleted()) { TextItem.SetColor(FLinearColor::White); TextItem.Text = LOCTEXT("SystemCompleted", "Completed"); TextItem.bCentreX = true; TextItem.bCentreY = true; Canvas->DrawItem(TextItem, Viewport->GetSizeXY().X * 0.5f, Viewport->GetSizeXY().Y - 10); TextItem.bCentreX = false; TextItem.bCentreY = false; } } } //Display a warning message in the preview window if the system has no fixed bounding-box and contains a GPU emitter. if(CascadePtr.Pin()->GetParticleSystem()->bUseFixedRelativeBoundingBox == false) { UParticleSystemComponent* PartComp = CascadePtr.Pin()->GetParticleSystemComponent(); if (PartComp->EmitterInstances.Num()) { //We iterate over the emitter instances to find any that contain a GPU Sprite TypeData module. If found, we draw the warning message. for (int32 i = 0; i < PartComp->EmitterInstances.Num(); i++) { FParticleEmitterInstance* Instance = PartComp->EmitterInstances[i]; if(!Instance || !Instance->SpriteTemplate) { continue; } UParticleLODLevel* LODLevel = Instance->SpriteTemplate->GetCurrentLODLevel(Instance); if (!LODLevel || !LODLevel->TypeDataModule) { continue; } const bool bIsAGPUEmitter = LODLevel->TypeDataModule->IsA(UParticleModuleTypeDataGpu::StaticClass()); if(bIsAGPUEmitter) { const int32 XPosition = 5; const int32 YPosition = Viewport->GetSizeXY().Y - 75.0f; FString strOutput = NSLOCTEXT("Cascade", "NoFixedBounds_Warning", "WARNING: This particle system has no fixed bounding box and contains a GPU emitter.").ToString(); TextItem.SetColor( FLinearColor::White ); TextItem.Text = FText::FromString( strOutput ); Canvas->DrawItem( TextItem, XPosition, YPosition ); break; } } } } int32 DetailMode = CascadePtr.Pin()->GetDetailMode(); if (DetailMode != DM_High) { FString DetailModeOutput = FString::Printf(TEXT("DETAIL MODE: %s"), (DetailMode == DM_Medium)? TEXT("MEDIUM"): TEXT("LOW")); TextItem.SetColor( FLinearColor::Red ); TextItem.Text = FText::FromString( DetailModeOutput ); Canvas->DrawItem( TextItem, 5.0f, Viewport->GetSizeXY().Y - 90.0f ); } if (GEngine->bEnableEditorPSysRealtimeLOD) { TextItem.SetColor( FLinearColor(0.25f, 0.25f, 1.0f) ); TextItem.Text = LOCTEXT("LODPREVIEWMODEENABLED","LOD PREVIEW MODE ENABLED"); Canvas->DrawItem( TextItem, 5.0f, Viewport->GetSizeXY().Y - 105.0f ); } EParticleSignificanceLevel ReqSignificance = CascadePtr.Pin()->GetRequiredSignificance(); if (ReqSignificance != EParticleSignificanceLevel::Low) { FString ReqSigOutput = FString::Printf(TEXT("REQUIRED SIGNIFICANCE: %s"), (ReqSignificance == EParticleSignificanceLevel::Medium) ? TEXT("MEDIUM") : ((ReqSignificance == EParticleSignificanceLevel::High) ? TEXT("HIGH") : TEXT("CRITICAL"))); TextItem.SetColor(FLinearColor::Red); TextItem.Text = FText::FromString(ReqSigOutput); Canvas->DrawItem(TextItem, 5.0f, Viewport->GetSizeXY().Y - 120.0f); } if (bCaptureScreenShot) { UParticleSystem* ParticleSystem = CascadePtr.Pin()->GetParticleSystem(); int32 SrcWidth = Viewport->GetSizeXY().X; int32 SrcHeight = Viewport->GetSizeXY().Y; // Read the contents of the viewport into an array. TArray<FColor> OrigBitmap; if (Viewport->ReadPixels(OrigBitmap)) { check(OrigBitmap.Num() == SrcWidth * SrcHeight); // Resize image to enforce max size. TArray<FColor> ScaledBitmap; int32 ScaledWidth = 512; int32 ScaledHeight = 512; FImageUtils::ImageResize(SrcWidth, SrcHeight, OrigBitmap, ScaledWidth, ScaledHeight, ScaledBitmap, true); // Compress. FCreateTexture2DParameters Params; Params.bDeferCompression = true; ParticleSystem->ThumbnailImage = FImageUtils::CreateTexture2D(ScaledWidth, ScaledHeight, ScaledBitmap, ParticleSystem, TEXT("ThumbnailTexture"), RF_NoFlags, Params); ParticleSystem->ThumbnailImageOutOfDate = false; ParticleSystem->MarkPackageDirty(); } bCaptureScreenShot = false; } }
void FSubtitleManager::DisplaySubtitle( FCanvas* Canvas, FActiveSubtitle* Subtitle, FIntRect& Parms, const FLinearColor& Color ) { // These should be valid in here check( GEngine ); check( Canvas ); CurrentSubtitleHeight = 0.0f; // This can be NULL when there's an asset mixup (especially with localisation) if( !GEngine->GetSubtitleFont() ) { UE_LOG(LogSubtitle, Warning, TEXT( "NULL GEngine->GetSubtitleFont() - subtitles not rendering!" ) ); return; } float FontHeight = GEngine->GetSubtitleFont()->GetMaxCharHeight(); float HeightTest = Canvas->GetRenderTarget()->GetSizeXY().Y; int32 SubtitleHeight = FMath::TruncToInt( ( FontHeight * MULTILINE_SPACING_SCALING ) ); FIntRect BackgroundBoxOffset = DrawStringOutlineBoxOffset; // Needed to add a drop shadow and doing all 4 sides was the only way to make them look correct. If this shows up as a framerate hit we'll have to think of a different way of dealing with this. if( Subtitle->bSingleLine ) { const FText& SubtitleText = Subtitle->Subtitles[Subtitle->Index].Text; if ( !SubtitleText.IsEmpty() ) { // Display lines up from the bottom of the region Parms.Max.Y -= SUBTITLE_CHAR_HEIGHT; FCanvasTextItem TextItem( FVector2D( Parms.Min.X + ( Parms.Width() / 2 ), Parms.Max.Y ), SubtitleText , GEngine->GetSubtitleFont(), Color ); TextItem.Depth = SUBTITLE_SCREEN_DEPTH_FOR_3D; TextItem.bOutlined = true; TextItem.bCentreX = true; TextItem.OutlineColor = FLinearColor::Black; Canvas->DrawItem( TextItem ); CurrentSubtitleHeight += SubtitleHeight; } } else { for( int32 Idx = Subtitle->Subtitles.Num() - 1; Idx >= 0; Idx-- ) { const FText& SubtitleText = Subtitle->Subtitles[Idx].Text; // Display lines up from the bottom of the region if ( !SubtitleText.IsEmpty() ) { FCanvasTextItem TextItem( FVector2D( Parms.Min.X + ( Parms.Width() / 2 ), Parms.Max.Y ), SubtitleText, GEngine->GetSubtitleFont(), Color ); TextItem.Depth = SUBTITLE_SCREEN_DEPTH_FOR_3D; TextItem.bOutlined = true; TextItem.bCentreX = true; TextItem.OutlineColor = FLinearColor::Black; Canvas->DrawItem( TextItem ); Parms.Max.Y -= SubtitleHeight; CurrentSubtitleHeight += SubtitleHeight; // Don't overlap subsequent boxes... BackgroundBoxOffset.Max.Y = BackgroundBoxOffset.Min.Y; } } } }
void FSpriteEditorViewportClient::DrawCanvas(FViewport& Viewport, FSceneView& View, FCanvas& Canvas) { const bool bIsHitTesting = Canvas.IsHitTesting(); if (!bIsHitTesting) { Canvas.SetHitProxy(nullptr); } if (!SpriteEditorPtr.IsValid()) { return; } UPaperSprite* Sprite = GetSpriteBeingEdited(); int32 YPos = 42; static const FText SourceRegionHelpStr = LOCTEXT("SourceRegionHelp", "Drag handles to adjust source region\nDouble-click on an image region to select all connected pixels (Ctrl creates a new sprite)\nHold down Ctrl and drag a rectangle to create a new sprite at that position\nClick on other sprite rectangles to change the active sprite"); switch (CurrentMode) { case ESpriteEditorMode::ViewMode: default: // Display the pivot { FNumberFormattingOptions NoDigitGroupingFormat; NoDigitGroupingFormat.UseGrouping = false; const FText PivotStr = FText::Format(LOCTEXT("PivotPosition", "Pivot: ({0}, {1})"), FText::AsNumber(Sprite->CustomPivotPoint.X, &NoDigitGroupingFormat), FText::AsNumber(Sprite->CustomPivotPoint.Y, &NoDigitGroupingFormat)); FCanvasTextItem TextItem(FVector2D(6, YPos), PivotStr, GEngine->GetSmallFont(), FLinearColor::White); TextItem.EnableShadow(FLinearColor::Black); TextItem.Draw(&Canvas); YPos += 18; } // Baked collision data if (Sprite->BodySetup != nullptr) { FSpriteGeometryEditMode::DrawCollisionStats(Viewport, View, Canvas, Sprite->BodySetup, /*inout*/ YPos); } // Baked render data DrawRenderStats(Viewport, View, Canvas, Sprite, /*inout*/ YPos); // And bounds DrawBoundsAsText(Viewport, View, Canvas, /*inout*/ YPos); break; case ESpriteEditorMode::EditCollisionMode: { // Draw the collision geometry stats YPos += 60; //@TODO: Need a better way to determine this from the editor mode if (Sprite->BodySetup != nullptr) { FSpriteGeometryEditMode::DrawGeometryStats(Viewport, View, Canvas, Sprite->CollisionGeometry, false, /*inout*/ YPos); FSpriteGeometryEditMode::DrawCollisionStats(Viewport, View, Canvas, Sprite->BodySetup, /*inout*/ YPos); } else { FCanvasTextItem TextItem(FVector2D(6, YPos), LOCTEXT("NoCollisionDataMainScreen", "No collision data"), GEngine->GetSmallFont(), FLinearColor::White); TextItem.EnableShadow(FLinearColor::Black); TextItem.Draw(&Canvas); } } break; case ESpriteEditorMode::EditRenderingGeomMode: { // Draw the render geometry stats YPos += 60; //@TODO: Need a better way to determine this from the editor mode FSpriteGeometryEditMode::DrawGeometryStats(Viewport, View, Canvas, Sprite->RenderGeometry, true, /*inout*/ YPos); DrawRenderStats(Viewport, View, Canvas, Sprite, /*inout*/ YPos); // And bounds DrawBoundsAsText(Viewport, View, Canvas, /*inout*/ YPos); } break; case ESpriteEditorMode::EditSourceRegionMode: { // Display tool help { FCanvasTextItem TextItem(FVector2D(6, YPos), SourceRegionHelpStr, GEngine->GetSmallFont(), FLinearColor::White); TextItem.EnableShadow(FLinearColor::Black); TextItem.Draw(&Canvas); YPos += 18; } if (bShowRelatedSprites) { DrawRelatedSprites(Viewport, View, Canvas, SpriteEditingConstants::SourceRegionRelatedBoundsColor, SpriteEditingConstants::SourceRegionRelatedSpriteNameColor); } DrawSourceRegion(Viewport, View, Canvas, SpriteEditingConstants::SourceRegionBoundsColor); } break; } if (bShowSockets && !IsInSourceRegionEditMode()) { FSpriteGeometryEditMode* GeometryEditMode = ModeTools->GetActiveModeTyped<FSpriteGeometryEditMode>(FSpriteGeometryEditMode::EM_SpriteGeometry); FSocketEditingHelper::DrawSocketNames(GeometryEditMode, RenderSpriteComponent, Viewport, View, Canvas); } FEditorViewportClient::DrawCanvas(Viewport, View, Canvas); }
nsresult nsLineBreaker::AppendText(nsIAtom* aHyphenationLanguage, const char16_t* aText, uint32_t aLength, uint32_t aFlags, nsILineBreakSink* aSink) { NS_ASSERTION(aLength > 0, "Appending empty text..."); uint32_t offset = 0; // Continue the current word if (mCurrentWord.Length() > 0) { NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set"); while (offset < aLength && !IsSpace(aText[offset])) { mCurrentWord.AppendElement(aText[offset]); if (!mCurrentWordContainsComplexChar && IsComplexChar(aText[offset])) { mCurrentWordContainsComplexChar = true; } UpdateCurrentWordLanguage(aHyphenationLanguage); ++offset; } if (offset > 0) { mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags)); } if (offset == aLength) return NS_OK; // We encountered whitespace, so we're done with this word nsresult rv = FlushCurrentWord(); if (NS_FAILED(rv)) return rv; } AutoTArray<uint8_t,4000> breakState; if (aSink) { if (!breakState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; } bool noCapitalizationNeeded = true; nsTArray<bool> capitalizationState; if (aSink && (aFlags & BREAK_NEED_CAPITALIZATION)) { if (!capitalizationState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; memset(capitalizationState.Elements(), false, aLength*sizeof(bool)); noCapitalizationNeeded = false; } uint32_t start = offset; bool noBreaksNeeded = !aSink || ((aFlags & NO_BREAKS_NEEDED_FLAGS) == NO_BREAKS_NEEDED_FLAGS && !mBreakHere && !mAfterBreakableSpace); if (noBreaksNeeded && noCapitalizationNeeded) { // Skip to the space before the last word, since either the break data // here is not needed, or no breaks are set in the sink and there cannot // be any breaks in this chunk; and we don't need to do word-initial // capitalization. All we need is the context for the next chunk (if any). offset = aLength; while (offset > start) { --offset; if (IsSpace(aText[offset])) break; } } uint32_t wordStart = offset; bool wordHasComplexChar = false; RefPtr<nsHyphenator> hyphenator; if ((aFlags & BREAK_USE_AUTO_HYPHENATION) && !(aFlags & BREAK_SUPPRESS_INSIDE) && aHyphenationLanguage) { hyphenator = nsHyphenationManager::Instance()->GetHyphenator(aHyphenationLanguage); } for (;;) { char16_t ch = aText[offset]; bool isSpace = IsSpace(ch); bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE); if (aSink && !noBreaksNeeded) { breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) || (mWordBreak == nsILineBreaker::kWordBreak_BreakAll) ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE; } mBreakHere = false; mAfterBreakableSpace = isBreakableSpace; if (isSpace) { if (offset > wordStart && aSink) { if (!(aFlags & BREAK_SUPPRESS_INSIDE)) { if (wordHasComplexChar) { // Save current start-of-word state because GetJISx4051Breaks will // set it to false uint8_t currentStart = breakState[wordStart]; nsContentUtils::LineBreaker()-> GetJISx4051Breaks(aText + wordStart, offset - wordStart, mWordBreak, breakState.Elements() + wordStart); breakState[wordStart] = currentStart; } if (hyphenator) { FindHyphenationPoints(hyphenator, aText + wordStart, aText + offset, breakState.Elements() + wordStart); } } if (!noCapitalizationNeeded) { SetupCapitalization(aText + wordStart, offset - wordStart, capitalizationState.Elements() + wordStart); } } wordHasComplexChar = false; ++offset; if (offset >= aLength) break; wordStart = offset; } else { if (!wordHasComplexChar && IsComplexChar(ch)) { wordHasComplexChar = true; } ++offset; if (offset >= aLength) { // Save this word mCurrentWordContainsComplexChar = wordHasComplexChar; uint32_t len = offset - wordStart; char16_t* elems = mCurrentWord.AppendElements(len); if (!elems) return NS_ERROR_OUT_OF_MEMORY; memcpy(elems, aText + wordStart, sizeof(char16_t)*len); mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags)); // Ensure that the break-before for this word is written out offset = wordStart + 1; UpdateCurrentWordLanguage(aHyphenationLanguage); break; } } } if (aSink) { if (!noBreaksNeeded) { aSink->SetBreaks(start, offset - start, breakState.Elements() + start); } if (!noCapitalizationNeeded) { aSink->SetCapitalization(start, offset - start, capitalizationState.Elements() + start); } } return NS_OK; }
void FSpriteGeometryEditMode::DrawCollisionStats(FViewport& InViewport, FSceneView& View, FCanvas& Canvas, class UBodySetup* BodySetup, int32& YPos) { FCanvasTextItem TextItem(FVector2D(6, YPos), LOCTEXT("CollisionGeomBaked", "Collision Geometry (baked)"), GEngine->GetSmallFont(), FLinearColor::White); TextItem.EnableShadow(FLinearColor::Black); TextItem.Draw(&Canvas); TextItem.Position += FVector2D(6.0f, 18.0f); // Collect stats const FKAggregateGeom& AggGeom3D = BodySetup->AggGeom; int32 NumSpheres = AggGeom3D.SphereElems.Num(); int32 NumBoxes = AggGeom3D.BoxElems.Num(); int32 NumCapsules = AggGeom3D.SphylElems.Num(); int32 NumConvexElems = AggGeom3D.ConvexElems.Num(); int32 NumConvexVerts = 0; bool bIs2D = false; for (const FKConvexElem& ConvexElement : AggGeom3D.ConvexElems) { NumConvexVerts += ConvexElement.VertexData.Num(); } if (NumSpheres > 0) { static const FText SpherePrompt = LOCTEXT("SphereCount", "Spheres: {0}"); static const FText CirclePrompt = LOCTEXT("CircleCount", "Circles: {0}"); TextItem.Text = FText::Format(bIs2D ? CirclePrompt : SpherePrompt, FText::AsNumber(NumSpheres)); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } if (NumBoxes > 0) { static const FText BoxPrompt = LOCTEXT("BoxCount", "Boxes: {0}"); TextItem.Text = FText::Format(BoxPrompt, FText::AsNumber(NumBoxes)); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } if (NumCapsules > 0) { static const FText CapsulePrompt = LOCTEXT("CapsuleCount", "Capsules: {0}"); TextItem.Text = FText::Format(CapsulePrompt, FText::AsNumber(NumCapsules)); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } if (NumConvexElems > 0) { static const FText ConvexPrompt = LOCTEXT("ConvexCount", "Convex Shapes: {0} ({1} verts)"); TextItem.Text = FText::Format(ConvexPrompt, FText::AsNumber(NumConvexElems), FText::AsNumber(NumConvexVerts)); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } if ((NumConvexElems + NumCapsules + NumBoxes + NumSpheres) == 0) { static const FText NoShapesPrompt = LOCTEXT("NoCollisionDataWarning", "Warning: Collision is enabled but there are no shapes"); TextItem.Text = NoShapesPrompt; TextItem.SetColor(FLinearColor::Yellow); TextItem.Draw(&Canvas); TextItem.Position.Y += 18.0f; } YPos = (int32)TextItem.Position.Y; }
nsresult nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32 aLength, PRUint32 aFlags, nsILineBreakSink* aSink) { NS_ASSERTION(aLength > 0, "Appending empty text..."); PRUint32 offset = 0; // Continue the current word if (mCurrentWord.Length() > 0) { NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set"); while (offset < aLength && !IsSpace(aText[offset])) { mCurrentWord.AppendElement(aText[offset]); if (!mCurrentWordContainsComplexChar && IsComplexChar(aText[offset])) { mCurrentWordContainsComplexChar = PR_TRUE; } UpdateCurrentWordLangGroup(aLangGroup); ++offset; } if (offset > 0) { mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags)); } if (offset == aLength) return NS_OK; // We encountered whitespace, so we're done with this word nsresult rv = FlushCurrentWord(); if (NS_FAILED(rv)) return rv; } nsAutoTArray<PRUint8,4000> breakState; if (aSink) { if (!breakState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; } nsTArray<bool> capitalizationState; if (aSink && (aFlags & BREAK_NEED_CAPITALIZATION)) { if (!capitalizationState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; memset(capitalizationState.Elements(), PR_FALSE, aLength); } PRUint32 start = offset; bool noBreaksNeeded = !aSink || (aFlags == (BREAK_SUPPRESS_INITIAL | BREAK_SUPPRESS_INSIDE | BREAK_SKIP_SETTING_NO_BREAKS) && !mBreakHere && !mAfterBreakableSpace); if (noBreaksNeeded) { // Skip to the space before the last word, since either the break data // here is not needed, or no breaks are set in the sink and there cannot // be any breaks in this chunk; all we need is the context for the next // chunk (if any) offset = aLength; while (offset > start) { --offset; if (IsSpace(aText[offset])) break; } } PRUint32 wordStart = offset; bool wordHasComplexChar = false; nsRefPtr<nsHyphenator> hyphenator; if ((aFlags & BREAK_USE_AUTO_HYPHENATION) && !(aFlags & BREAK_SUPPRESS_INSIDE)) { hyphenator = nsHyphenationManager::Instance()->GetHyphenator(aLangGroup); } for (;;) { PRUnichar ch = aText[offset]; bool isSpace = IsSpace(ch); bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE); if (aSink) { breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE; } mBreakHere = PR_FALSE; mAfterBreakableSpace = isBreakableSpace; if (isSpace) { if (offset > wordStart && aSink) { if (!(aFlags & BREAK_SUPPRESS_INSIDE)) { if (wordHasComplexChar) { // Save current start-of-word state because GetJISx4051Breaks will // set it to false PRUint8 currentStart = breakState[wordStart]; nsContentUtils::LineBreaker()-> GetJISx4051Breaks(aText + wordStart, offset - wordStart, breakState.Elements() + wordStart); breakState[wordStart] = currentStart; } if (hyphenator) { FindHyphenationPoints(hyphenator, aText + wordStart, aText + offset, breakState.Elements() + wordStart); } } if (aFlags & BREAK_NEED_CAPITALIZATION) { SetupCapitalization(aText + wordStart, offset - wordStart, capitalizationState.Elements() + wordStart); } } wordHasComplexChar = PR_FALSE; ++offset; if (offset >= aLength) break; wordStart = offset; } else { if (!wordHasComplexChar && IsComplexChar(ch)) { wordHasComplexChar = PR_TRUE; } ++offset; if (offset >= aLength) { // Save this word mCurrentWordContainsComplexChar = wordHasComplexChar; PRUint32 len = offset - wordStart; PRUnichar* elems = mCurrentWord.AppendElements(len); if (!elems) return NS_ERROR_OUT_OF_MEMORY; memcpy(elems, aText + wordStart, sizeof(PRUnichar)*len); mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags)); // Ensure that the break-before for this word is written out offset = wordStart + 1; UpdateCurrentWordLangGroup(aLangGroup); break; } } } if (!noBreaksNeeded) { // aSink must not be null aSink->SetBreaks(start, offset - start, breakState.Elements() + start); if (aFlags & BREAK_NEED_CAPITALIZATION) { aSink->SetCapitalization(start, offset - start, capitalizationState.Elements() + start); } } return NS_OK; }
void UTextureThumbnailRenderer::Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget*, FCanvas* Canvas) { UTexture* Texture = Cast<UTexture>(Object); if (Texture != nullptr && Texture->Resource != nullptr) { UTexture2D* Texture2D = Cast<UTexture2D>(Texture); // Take the alpha channel into account for textures that have one. // This provides a much better preview than just showing RGB, // Because the RGB content in areas with an alpha of 0 is often garbage that will not be seen in normal conditions. // Non-UI textures often have uncorrelated data in the alpha channel (like a skin mask, specular power, etc) so we only preview UI textures this way. const bool bUseTranslucentBlend = Texture2D && Texture2D->HasAlphaChannel() && Texture2D->LODGroup == TEXTUREGROUP_UI; UTextureCube* TextureCube = Cast<UTextureCube>(Texture); UTextureRenderTargetCube* RTTextureCube = Cast<UTextureRenderTargetCube>(Texture); UTextureLightProfile* TextureLightProfile = Cast<UTextureLightProfile>(Texture); TRefCountPtr<FBatchedElementParameters> BatchedElementParameters; if(TextureCube || RTTextureCube) { // is released by the render thread when it was rendered BatchedElementParameters = new FMipLevelBatchedElementParameters((float)0); // If the thumbnail is square then make it 2:1 for cubes. if(Width == Height) { Height = Width / 2; Y += Height / 2; } } else if (TextureLightProfile) { BatchedElementParameters = new FIESLightProfileBatchedElementParameters(TextureLightProfile->Brightness); } else if (Texture2D && Texture2D->IsNormalMap()) { BatchedElementParameters = new FNormalMapBatchedElementParameters(); } if (bUseTranslucentBlend) { // If using alpha, draw a checkerboard underneath first. const int32 CheckerDensity = 8; auto* Checker = UThumbnailManager::Get().CheckerboardTexture; Canvas->DrawTile( 0.0f, 0.0f, Width, Height, // Dimensions 0.0f, 0.0f, CheckerDensity, CheckerDensity, // UVs FLinearColor::White, Checker->Resource); // Tint & Texture } // Use A canvas tile item to draw FCanvasTileItem CanvasTile( FVector2D( X, Y ), Texture->Resource, FVector2D( Width,Height ), FLinearColor::White ); CanvasTile.BlendMode = bUseTranslucentBlend ? SE_BLEND_Translucent : SE_BLEND_Opaque; CanvasTile.BatchedElementParameters = BatchedElementParameters; CanvasTile.Draw( Canvas ); if (TextureLightProfile) { float Brightness = TextureLightProfile->Brightness; // Brightness in Lumens FText BrightnessText = FText::AsNumber( Brightness ); FCanvasTextItem TextItem( FVector2D( 5.0f, 5.0f ), BrightnessText, GEngine->GetLargeFont(), FLinearColor::White ); TextItem.EnableShadow(FLinearColor::Black); TextItem.Scale = FVector2D(Width / 128.0f, Height / 128.0f); TextItem.Draw(Canvas); } } }
void FStaticMeshEditorViewportClient::DrawCanvas( FViewport& InViewport, FSceneView& View, FCanvas& Canvas ) { #if TODO_STATICMESH if ( StaticMesh->bHasBeenSimplified && SimplygonLogo && SimplygonLogo->Resource ) { const float LogoSizeX = 64.0f; const float LogoSizeY = 40.65f; const float Padding = 6.0f; const float LogoX = Viewport->GetSizeXY().X - Padding - LogoSizeX; const float LogoY = Viewport->GetSizeXY().Y - Padding - LogoSizeY; Canvas->DrawTile( LogoX, LogoY, LogoSizeX, LogoSizeY, 0.0f, 0.0f, 1.0f, 1.0f, FLinearColor::White, SimplygonLogo->Resource, SE_BLEND_Opaque ); } #endif // #if TODO_STATICMESH auto StaticMeshEditor = StaticMeshEditorPtr.Pin(); auto StaticMeshEditorViewport = StaticMeshEditorViewportPtr.Pin(); if (!StaticMeshEditor.IsValid() || !StaticMeshEditorViewport.IsValid()) { return; } const int32 HalfX = Viewport->GetSizeXY().X/2; const int32 HalfY = Viewport->GetSizeXY().Y/2; // Draw socket names if desired. if( bShowSockets ) { for(int32 i=0; i<StaticMesh->Sockets.Num(); i++) { UStaticMeshSocket* Socket = StaticMesh->Sockets[i]; if(Socket!=NULL) { FMatrix SocketTM; Socket->GetSocketMatrix(SocketTM, StaticMeshComponent); const FVector SocketPos = SocketTM.GetOrigin(); const FPlane proj = View.Project( SocketPos ); if(proj.W > 0.f) { const int32 XPos = HalfX + ( HalfX * proj.X ); const int32 YPos = HalfY + ( HalfY * (proj.Y * -1) ); FCanvasTextItem TextItem( FVector2D( XPos, YPos ), FText::FromString( Socket->SocketName.ToString() ), GEngine->GetSmallFont(), FLinearColor(FColor(255,196,196)) ); Canvas.DrawItem( TextItem ); const UStaticMeshSocket* SelectedSocket = StaticMeshEditor->GetSelectedSocket(); if (bManipulating && SelectedSocket == Socket) { //Figure out the text height FTextSizingParameters Parameters(GEngine->GetSmallFont(), 1.0f, 1.0f); UCanvas::CanvasStringSize(Parameters, *Socket->SocketName.ToString()); int32 YL = FMath::TruncToInt(Parameters.DrawYL); DrawAngles(&Canvas, XPos, YPos + YL, Widget->GetCurrentAxis(), GetWidgetMode(), Socket->RelativeRotation, Socket->RelativeLocation); } } } } } TArray<SStaticMeshEditorViewport::FOverlayTextItem> TextItems; int32 CurrentLODLevel = StaticMeshEditor->GetCurrentLODLevel(); if (CurrentLODLevel == 0) { CurrentLODLevel = ComputeStaticMeshLOD(StaticMesh->RenderData, StaticMeshComponent->Bounds.Origin, StaticMeshComponent->Bounds.SphereRadius, View); } else { CurrentLODLevel -= 1; } TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "LOD_F", "LOD: {0}"), FText::AsNumber(CurrentLODLevel)))); float CurrentScreenSize = ComputeBoundsScreenSize(StaticMeshComponent->Bounds.Origin, StaticMeshComponent->Bounds.SphereRadius, View); FNumberFormattingOptions FormatOptions; FormatOptions.MinimumFractionalDigits = 3; FormatOptions.MaximumFractionalDigits = 6; FormatOptions.MaximumIntegralDigits = 6; TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "ScreenSize_F", "Current Screen Size: {0}"), FText::AsNumber(CurrentScreenSize, &FormatOptions)))); TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "Triangles_F", "Triangles: {0}"), FText::AsNumber(StaticMeshEditorPtr.Pin()->GetNumTriangles(CurrentLODLevel))))); TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "Vertices_F", "Vertices: {0}"), FText::AsNumber(StaticMeshEditorPtr.Pin()->GetNumVertices(CurrentLODLevel))))); TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "UVChannels_F", "UV Channels: {0}"), FText::AsNumber(StaticMeshEditorPtr.Pin()->GetNumUVChannels(CurrentLODLevel))))); if( StaticMesh->RenderData->LODResources.Num() > 0 ) { if (StaticMesh->RenderData->LODResources[0].DistanceFieldData != nullptr ) { const FDistanceFieldVolumeData& VolumeData = *(StaticMesh->RenderData->LODResources[0].DistanceFieldData); if (VolumeData.Size.GetMax() > 0) { float MemoryMb = (VolumeData.Size.X * VolumeData.Size.Y * VolumeData.Size.Z * VolumeData.DistanceFieldVolume.GetTypeSize()) / (1024.0f * 1024.0f); FNumberFormattingOptions NumberOptions; NumberOptions.MinimumFractionalDigits = 2; NumberOptions.MaximumFractionalDigits = 2; if (VolumeData.bMeshWasClosed) { TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "DistanceFieldRes_F", "Distance Field: {0}x{1}x{2} = {3}Mb"), FText::AsNumber(VolumeData.Size.X), FText::AsNumber(VolumeData.Size.Y), FText::AsNumber(VolumeData.Size.Z), FText::AsNumber(MemoryMb, &NumberOptions)))); } else { TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( NSLOCTEXT("UnrealEd", "DistanceFieldClosed_F", "Distance Field: Mesh was not closed and material was one-sided"))); } } } } TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "ApproxSize_F", "Approx Size: {0}x{1}x{2}"), FText::AsNumber(int32(StaticMesh->GetBounds().BoxExtent.X * 2.0f)), // x2 as artists wanted length not radius FText::AsNumber(int32(StaticMesh->GetBounds().BoxExtent.Y * 2.0f)), FText::AsNumber(int32(StaticMesh->GetBounds().BoxExtent.Z * 2.0f))))); // Show the number of collision primitives if we are drawing collision. if(bShowCollision && StaticMesh->BodySetup) { TextItems.Add(SStaticMeshEditorViewport::FOverlayTextItem( FText::Format(NSLOCTEXT("UnrealEd", "NumPrimitives_F", "Num Primitives: {0}"), FText::AsNumber(StaticMesh->BodySetup->AggGeom.GetElementCount())))); } StaticMeshEditorViewport->PopulateOverlayText(TextItems); if(bDrawUVs) { const int32 YPos = 160; DrawUVsForMesh(Viewport, &Canvas, YPos); } }
nsresult nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aLength, PRUint32 aFlags, nsILineBreakSink* aSink) { NS_ASSERTION(aLength > 0, "Appending empty text..."); if (aFlags & (BREAK_NEED_CAPITALIZATION | BREAK_USE_AUTO_HYPHENATION)) { // Defer to the Unicode path if capitalization or hyphenation is required nsAutoString str; const char* cp = reinterpret_cast<const char*>(aText); CopyASCIItoUTF16(nsDependentCSubstring(cp, cp + aLength), str); return AppendText(aLangGroup, str.get(), aLength, aFlags, aSink); } PRUint32 offset = 0; // Continue the current word if (mCurrentWord.Length() > 0) { NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set"); while (offset < aLength && !IsSpace(aText[offset])) { mCurrentWord.AppendElement(aText[offset]); if (!mCurrentWordContainsComplexChar && IsComplexASCIIChar(aText[offset])) { mCurrentWordContainsComplexChar = PR_TRUE; } ++offset; } if (offset > 0) { mTextItems.AppendElement(TextItem(aSink, 0, offset, aFlags)); } if (offset == aLength) { // We did not encounter whitespace so the word hasn't finished yet. return NS_OK; } // We encountered whitespace, so we're done with this word nsresult rv = FlushCurrentWord(); if (NS_FAILED(rv)) return rv; } nsAutoTArray<PRUint8,4000> breakState; if (aSink) { if (!breakState.AppendElements(aLength)) return NS_ERROR_OUT_OF_MEMORY; } PRUint32 start = offset; bool noBreaksNeeded = !aSink || (aFlags == (BREAK_SUPPRESS_INITIAL | BREAK_SUPPRESS_INSIDE | BREAK_SKIP_SETTING_NO_BREAKS) && !mBreakHere && !mAfterBreakableSpace); if (noBreaksNeeded) { // Skip to the space before the last word, since either the break data // here is not needed, or no breaks are set in the sink and there cannot // be any breaks in this chunk; all we need is the context for the next // chunk (if any) offset = aLength; while (offset > start) { --offset; if (IsSpace(aText[offset])) break; } } PRUint32 wordStart = offset; bool wordHasComplexChar = false; for (;;) { PRUint8 ch = aText[offset]; bool isSpace = IsSpace(ch); bool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE); if (aSink) { breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace) ? gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NORMAL : gfxTextRun::CompressedGlyph::FLAG_BREAK_TYPE_NONE; } mBreakHere = PR_FALSE; mAfterBreakableSpace = isBreakableSpace; if (isSpace) { if (offset > wordStart && wordHasComplexChar) { if (aSink && !(aFlags & BREAK_SUPPRESS_INSIDE)) { // Save current start-of-word state because GetJISx4051Breaks will // set it to false PRUint8 currentStart = breakState[wordStart]; nsContentUtils::LineBreaker()-> GetJISx4051Breaks(aText + wordStart, offset - wordStart, breakState.Elements() + wordStart); breakState[wordStart] = currentStart; } wordHasComplexChar = PR_FALSE; } ++offset; if (offset >= aLength) break; wordStart = offset; } else { if (!wordHasComplexChar && IsComplexASCIIChar(ch)) { wordHasComplexChar = PR_TRUE; } ++offset; if (offset >= aLength) { // Save this word mCurrentWordContainsComplexChar = wordHasComplexChar; PRUint32 len = offset - wordStart; PRUnichar* elems = mCurrentWord.AppendElements(len); if (!elems) return NS_ERROR_OUT_OF_MEMORY; PRUint32 i; for (i = wordStart; i < offset; ++i) { elems[i - wordStart] = aText[i]; } mTextItems.AppendElement(TextItem(aSink, wordStart, len, aFlags)); // Ensure that the break-before for this word is written out offset = wordStart + 1; break; } } } if (!noBreaksNeeded) { aSink->SetBreaks(start, offset - start, breakState.Elements() + start); } return NS_OK; }
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); } }