Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion BetaHubBugReporter.uplugin
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"Description": "Bug reporting functionality for BetaHub.io",
"Category": "Other",
"CreatedBy": "Upsoft sp. z o. o.",
"EngineVersion": "5.3.0",
"EngineVersion": [ "5.3.0", "5.4.0", "5.5.0" ],
"CreatedByURL": "https://betahub.io/",
"DocsURL": "https://www.betahub.io/docs/integration-guides/#unreal-plugin-integration",
"MarketplaceURL": "",
Expand All @@ -25,6 +25,16 @@
]
}
],
"Plugins": [
{
"Name": "EnhancedInput",
"Enabled": true
},
{
"Name": "CommonUI",
"Enabled": true
}
],
"AdditionalFiles": [
"ThirdParty/FFmpeg/Windows/ffmpeg.exe"
]
Expand Down
Binary file added Content/BFL_BetaHub.uasset
Binary file not shown.
Binary file removed Content/BetaHubFunctionLibrary.uasset
Binary file not shown.
Binary file added Content/EWBP_AreaCords.uasset
Binary file not shown.
Binary file added Content/EWBP_HiddenPixelsList.uasset
Binary file not shown.
Binary file added Content/IA_ShowBugReportForm.uasset
Binary file not shown.
Binary file added Content/IA_ShowHidingArea.uasset
Binary file not shown.
Binary file added Content/IA_StartDrawingAreaToHide.uasset
Binary file not shown.
Binary file added Content/IA_StopDrawingAreaToHide.uasset
Binary file not shown.
Binary file added Content/IMC_BetaHub.uasset
Binary file not shown.
Binary file added Content/IMC_BetaHubDrawingAreaToHide.uasset
Binary file not shown.
Binary file added Content/WBP_DrawAreaToHide.uasset
Binary file not shown.
9 changes: 7 additions & 2 deletions Source/BetaHubBugReporter/BetaHubBugReporter.Build.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public BetaHubBugReporter(ReadOnlyTargetRules Target) : base(Target)
"JsonUtilities",
"RenderCore",
"RHI",
"EnhancedInput"
// ... add private dependencies that you statically link with here ...
}
);
Expand All @@ -59,7 +60,11 @@ public BetaHubBugReporter(ReadOnlyTargetRules Target) : base(Target)
}
);


RuntimeDependencies.Add("$(TargetOutputDir)/bh_ffmpeg.exe", Path.Combine(PluginDirectory, "ThirdParty/FFmpeg/Windows/ffmpeg.exe"));
RuntimeDependencies.Add("$(TargetOutputDir)/bh_ffmpeg.exe", Path.Combine(PluginDirectory, "ThirdParty/FFmpeg/Windows/ffmpeg.exe"));

if(!Target.bBuildEditor)
{
RuntimeDependencies.Add("$(TargetOutputDir)/save.bh", Path.Combine(PluginDirectory, "save.bh"));
}
}
}
42 changes: 42 additions & 0 deletions Source/BetaHubBugReporter/Private/BH_BlueprintFunctionLibrary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Fill out your copyright notice in the Description page of Project Settings.


#include "BH_BlueprintFunctionLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "BH_GameInstanceSubsystem.h"
#include "BH_SavedAreasToHide.h"

void UBH_BlueprintFunctionLibrary::HideScreenAreaFromReport(const UObject* WorldContextObject, FVector4 AreaToHide)
{
UBH_GameInstanceSubsystem* BetaHubSubsystem = GetBetaHubGameInstanceSubsystem(WorldContextObject);

BetaHubSubsystem->HideScreenAreaFromReport(AreaToHide);
}

void UBH_BlueprintFunctionLibrary::HideScreenAreaFromReportArray(const UObject* WorldContextObject, TArray<FVector4> AreasToHide)
{
UBH_GameInstanceSubsystem* BetaHubSubsystem = GetBetaHubGameInstanceSubsystem(WorldContextObject);

BetaHubSubsystem->HideScreenAreaFromReportArray(AreasToHide);
}

void UBH_BlueprintFunctionLibrary::SetHiddenAreaColor(const UObject* WorldContextObject, FLinearColor NewColor)
{
UBH_GameInstanceSubsystem* BetaHubSubsystem = GetBetaHubGameInstanceSubsystem(WorldContextObject);

BetaHubSubsystem->SetHiddenAreaColor(NewColor.ToFColor(false));
}

TArray<FVector4> UBH_BlueprintFunctionLibrary::GetSavedHiddenAreas(const UObject* WorldContextObject)
{
UBH_GameInstanceSubsystem* BetaHubSubsystem = GetBetaHubGameInstanceSubsystem(WorldContextObject);

BetaHubSubsystem->GetSavedHiddenAreasObject();

return TArray<FVector4>();
}

UBH_GameInstanceSubsystem* UBH_BlueprintFunctionLibrary::GetBetaHubGameInstanceSubsystem(const UObject* WorldContextObject)
{
return WorldContextObject->GetWorld()->GetGameInstance()->GetSubsystem<UBH_GameInstanceSubsystem>();
}
75 changes: 74 additions & 1 deletion Source/BetaHubBugReporter/Private/BH_GameInstanceSubsystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "HAL/PlatformProcess.h"
#include "Engine/Engine.h"
#include "UObject/ConstructorHelpers.h"
#include "Kismet/GameplayStatics.h"

void UBH_GameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Expand All @@ -24,6 +25,8 @@ void UBH_GameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)

Manager = NewObject<UBH_Manager>(this);
Manager->StartService(GetGameInstance());

LoadHiddenAreas();
}
}

Expand All @@ -33,4 +36,74 @@ void UBH_GameInstanceSubsystem::Deinitialize()
{
Manager->StopService();
}
}
}

void UBH_GameInstanceSubsystem::HideScreenAreaFromReport(FVector4 AreaToHide)
{
Manager->HideScreenAreaFromReport(AreaToHide);

GetSavedHiddenAreasObject()->AddAreaToHide(AreaToHide);

SaveHiddenAreas();
}

void UBH_GameInstanceSubsystem::HideScreenAreaFromReportArray(TArray<FVector4> AreasToHide)
{
Manager->HideScreenAreaFromReportArray(AreasToHide);

GetSavedHiddenAreasObject()->AddAreasToHide(AreasToHide);

SaveHiddenAreas();
}

void UBH_GameInstanceSubsystem::SetHiddenAreaColor(FLinearColor NewColor)
{
Manager->SetHiddenAreaColor(NewColor.ToFColor(false));

GetSavedHiddenAreasObject()->SetHiddenAreaColor(NewColor);

SaveHiddenAreas();
}

UBH_SavedAreasToHide* UBH_GameInstanceSubsystem::GetSavedHiddenAreasObject()
{
if (!SavedHiddenAreasObject)
{
SavedHiddenAreasObject = Cast<UBH_SavedAreasToHide>(UGameplayStatics::CreateSaveGameObject(UBH_SavedAreasToHide::StaticClass()));
}
return SavedHiddenAreasObject;
}

void UBH_GameInstanceSubsystem::SaveHiddenAreas()
{
TArray<uint8> SaveData;

UBH_SavedAreasToHide* SaveObject = GetSavedHiddenAreasObject();

UGameplayStatics::SaveGameToMemory(SaveObject, SaveData);

FFileHelper::SaveArrayToFile(SaveData, *(FPaths::ProjectPluginsDir() + "\\BetaHubBugReporter\\save.bh"));
}

void UBH_GameInstanceSubsystem::LoadHiddenAreas()
{
TArray<uint8> LoadData;

FString Path;

#if WITH_EDITOR
Path = FPaths::ProjectPluginsDir() + "\\BetaHubBugReporter\\save.bh";
#else
Path = FPaths::ProjectDir() + "\\Binaries\\Win64\\save.bh";
#endif

if (FFileHelper::LoadFileToArray(LoadData, *Path))
{
SavedHiddenAreasObject = Cast<UBH_SavedAreasToHide>(UGameplayStatics::LoadGameFromMemory(LoadData));

UE_LOG(LogBetaHub, Log, TEXT("Hidden Areas loaded!"));

Manager->HideScreenAreaFromReportArray(SavedHiddenAreasObject->GetSavedAreasToHideArray());
Manager->SetHiddenAreaColor(SavedHiddenAreasObject->GetSavedAreasColor());
}
}
98 changes: 92 additions & 6 deletions Source/BetaHubBugReporter/Private/BH_GameRecorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ UBH_GameRecorder::UBH_GameRecorder(const FObjectInitializer& ObjectInitializer)
, ViewportHeight(0)
, FrameWidth(0)
, FrameHeight(0)
, RawFrameBufferPool(3)
, HiddenAreaColor(FColor::Black)
, LastCaptureTime(0)
, RawFrameBufferQueue()
, RawFrameBufferPool(3)
, MainEditorWindow(nullptr)
, LargestSize(0, 0)
, MaxVideoWidth(512) // Initialize with minimum value
Expand Down Expand Up @@ -193,6 +194,18 @@ void UBH_GameRecorder::Tick(float DeltaTime)
{
PendingPixels[i] = PendingLinearPixels[i].ToFColor(false);
}

TArray<int32> HiddenIdices;

for (FVector4 DisabledRect : HiddenAreas)
{
HiddenIdices.Append(GetPixelIndicesFromViewportRectangle(FVector2D(DisabledRect.X, DisabledRect.Y), FVector2D(DisabledRect.Z, DisabledRect.W), TextureBuffer->GetWidth(), TextureBuffer->GetHeight()));
}

for (int32 Index : HiddenIdices)
{
PendingPixels[Index] = HiddenAreaColor;
}

// Resize image to frame
ResizeImageToFrame(PendingPixels, TextureBuffer->GetWidth(), TextureBuffer->GetHeight(), FrameWidth, FrameHeight, ResizedPixels);
Expand Down Expand Up @@ -235,6 +248,56 @@ void UBH_GameRecorder::ResizeImageToFrame(
}
}

TArray<int32> UBH_GameRecorder::GetPixelIndicesFromViewportRectangle(const FVector2D& TopLeftViewportCoords, const FVector2D& BottomRightViewportCoords, int32 TextureWidth, int32 TextureHeight)
{
TArray<int32> PixelIndices;

// Get viewport size
FVector2D ViewportSize;
GEngine->GameViewport->GetViewportSize(ViewportSize);

// Check if viewport size and texture size are valid
if (ViewportSize.X <= 0 || ViewportSize.Y <= 0 || TextureWidth <= 0 || TextureHeight <= 0)
{
return PixelIndices; // Return empty if sizes are invalid
}

// Normalize the viewport coordinates (0 to 1)
float NormalizedTopLeftX = TopLeftViewportCoords.X / ViewportSize.X;
float NormalizedTopLeftY = TopLeftViewportCoords.Y / ViewportSize.Y;
float NormalizedBottomRightX = BottomRightViewportCoords.X / ViewportSize.X;
float NormalizedBottomRightY = BottomRightViewportCoords.Y / ViewportSize.Y;

// Convert normalized coordinates to texture coordinates
int32 TextureX1 = FMath::Clamp(static_cast<int32>(NormalizedTopLeftX * TextureWidth), 0, TextureWidth - 1);
int32 TextureY1 = FMath::Clamp(static_cast<int32>(NormalizedTopLeftY * TextureHeight), 0, TextureHeight - 1);
int32 TextureX2 = FMath::Clamp(static_cast<int32>(NormalizedBottomRightX * TextureWidth), 0, TextureWidth - 1);
int32 TextureY2 = FMath::Clamp(static_cast<int32>(NormalizedBottomRightY * TextureHeight), 0, TextureHeight - 1);

// Ensure coordinates are properly ordered (Top-left and bottom-right)
int32 StartX = FMath::Min(TextureX1, TextureX2);
int32 EndX = FMath::Max(TextureX1, TextureX2);
int32 StartY = FMath::Min(TextureY1, TextureY2);
int32 EndY = FMath::Max(TextureY1, TextureY2);

// Iterate through the rectangle area to collect indices
for (int32 Y = StartY; Y <= EndY; ++Y)
{
for (int32 X = StartX; X <= EndX; ++X)
{
int32 PixelIndex = Y * TextureWidth + X;

// Check if the pixel index is within the bounds of the array
if (PixelIndex >= 0 && PixelIndex < PendingPixels.Num())
{
PixelIndices.Add(PixelIndex);
}
}
}

return PixelIndices;
}

bool UBH_GameRecorder::IsTickable() const
{
return bIsRecording;
Expand All @@ -245,7 +308,7 @@ TStatId UBH_GameRecorder::GetStatId() const
RETURN_QUICK_DECLARE_CYCLE_STAT(UBH_GameRecorder, STATGROUP_Tickables);
}

void UBH_GameRecorder::OnBackBufferReady(SWindow& Window, const FTexture2DRHIRef& BackBuffer)
void UBH_GameRecorder::OnBackBufferReady(SWindow& Window, const FTextureRHIRef& BackBuffer)
{
#if WITH_EDITOR
// Log window title and size for debugging
Expand Down Expand Up @@ -293,7 +356,7 @@ void UBH_GameRecorder::OnBackBufferReady(SWindow& Window, const FTexture2DRHIRef
{
FViewport* Viewport = GEngine->GameViewport->GetGameViewport();

FTexture2DRHIRef GameBuffer = BackBuffer;
FTextureRHIRef GameBuffer = BackBuffer;
if (!GameBuffer)
{
UE_LOG(LogBetaHub, Error, TEXT("Failed to get game buffer. Will try next time..."));
Expand Down Expand Up @@ -331,11 +394,18 @@ void UBH_GameRecorder::OnBackBufferReady(SWindow& Window, const FTexture2DRHIRef

if (!bCopyTextureStarted && StagingTexture != nullptr)
{
#if ENGINE_MINOR_VERSION >= 5
AsyncTask(ENamedThreads::GameThread, [this, BackBuffer]()
{
ReadPixels(BackBuffer);
});
#else
ReadPixels(BackBuffer);
#endif
}
}

void UBH_GameRecorder::ReadPixels(const FTexture2DRHIRef& BackBuffer)
void UBH_GameRecorder::ReadPixels(const FTextureRHIRef& BackBuffer)
{
if (!GEngine || !GEngine->GameViewport) return;

Expand Down Expand Up @@ -364,7 +434,7 @@ void UBH_GameRecorder::ReadPixels(const FTexture2DRHIRef& BackBuffer)
return;
}

FTexture2DRHIRef Texture = BackBuffer;
FTextureRHIRef Texture = BackBuffer;

FRHICopyTextureInfo CopyInfo;
RHICmdList.CopyTexture(Texture, StagingTexture, CopyInfo);
Expand All @@ -386,14 +456,15 @@ void UBH_GameRecorder::ReadPixels(const FTexture2DRHIRef& BackBuffer)
RawFrameBufferQueue.Enqueue(TextureBuffer);

RHICmdList.UnmapStagingSurface(StagingTexture);

}
);

CopyTextureFence.BeginFence();
bCopyTextureStarted = true;
}

void UBH_GameRecorder::OnBackBufferResized(const FTexture2DRHIRef& BackBuffer)
void UBH_GameRecorder::OnBackBufferResized(const FTextureRHIRef& BackBuffer)
{
FIntVector OriginalSize = BackBuffer->GetDesc().GetSize();

Expand Down Expand Up @@ -481,6 +552,21 @@ void UBH_GameRecorder::SetMaxVideoDimensions(int32 InMaxWidth, int32 InMaxHeight
MaxVideoHeight = FMath::Max(InMaxHeight, 512);
}

void UBH_GameRecorder::HideScreenAreaFromReport(FVector4 AreaToHide)
{
HiddenAreas.Add(AreaToHide);
}

void UBH_GameRecorder::HideScreenAreaFromReportArray(TArray<FVector4> AreasToHide)
{
HiddenAreas.Append(AreasToHide);
}

void UBH_GameRecorder::SetHiddenAreaColor(FColor NewColor)
{
HiddenAreaColor = NewColor;
}

#if ENGINE_MINOR_VERSION < 4
bool ConvertRAWSurfaceDataToFLinearColor(EPixelFormat Format, uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out, FReadSurfaceDataFlags InFlags)
{
Expand Down
Loading