Compare commits
17 Commits
cfde147af4
...
b3e0389675
Author | SHA1 | Date | |
---|---|---|---|
|
b3e0389675 | ||
6bef120e16 | |||
f0081255ed | |||
507fb88756 | |||
|
7148f14b0d | ||
7d383871b8 | |||
|
59377bdd4f | ||
|
6e35f6bcff | ||
|
5ee0e40e6f | ||
|
2207cfbba0 | ||
|
a9cd14ae7f | ||
|
d311284050 | ||
568d67d04f | |||
|
a3347489bc | ||
|
5cc5b232cb | ||
|
8dc892b9fa | ||
22e2b0a419 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -29,7 +29,11 @@ public class LuckyDataTransfer : ModuleRules
|
|||||||
"WebSockets",
|
"WebSockets",
|
||||||
"Json",
|
"Json",
|
||||||
"JsonUtilities",
|
"JsonUtilities",
|
||||||
"ImageWriteQueue"
|
"ImageWriteQueue",
|
||||||
|
"FunctionalTesting",
|
||||||
|
"RenderCore",
|
||||||
|
"RHI",
|
||||||
|
"RHICore"
|
||||||
// ... add other public dependencies that you statically link with here ...
|
// ... add other public dependencies that you statically link with here ...
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -41,7 +45,8 @@ public class LuckyDataTransfer : ModuleRules
|
|||||||
"CoreUObject",
|
"CoreUObject",
|
||||||
"Engine",
|
"Engine",
|
||||||
"Slate",
|
"Slate",
|
||||||
"SlateCore", "ImageWriteQueue",
|
"SlateCore",
|
||||||
|
"ImageWriteQueue"
|
||||||
// ... add private dependencies that you statically link with here ...
|
// ... add private dependencies that you statically link with here ...
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -2,18 +2,45 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "LuckyDataTransferSubsystem.h"
|
#include "LuckyDataTransferSubsystem.h"
|
||||||
|
|
||||||
|
#include "AutomationBlueprintFunctionLibrary.h"
|
||||||
|
#include "ImageUtils.h"
|
||||||
|
#include "RenderingThread.h"
|
||||||
|
#include "RenderUtils.h"
|
||||||
|
#include "RenderGraphUtils.h"
|
||||||
|
#include "RHI.h"
|
||||||
|
#include "RHICommandList.h"
|
||||||
|
#include "ImageWriteQueue.h"
|
||||||
|
#include "ImageWriteTask.h"
|
||||||
|
#include "ImagePixelData.h"
|
||||||
#include "JsonUtilities.h"
|
#include "JsonUtilities.h"
|
||||||
#include "JsonObjectConverter.h"
|
#include "JsonObjectConverter.h"
|
||||||
|
#include "ReviewComments.h"
|
||||||
#include "WebSocketsModule.h"
|
#include "WebSocketsModule.h"
|
||||||
|
#include "Kismet/KismetStringLibrary.h"
|
||||||
|
#include "Camera/CameraActor.h"
|
||||||
|
#include "Camera/CameraComponent.h"
|
||||||
|
#include "Components/SceneCaptureComponent2D.h"
|
||||||
|
#include "Kismet/GameplayStatics.h"
|
||||||
|
#include "Kismet/KismetMathLibrary.h"
|
||||||
|
#include "Kismet/KismetRenderingLibrary.h"
|
||||||
#include "Slate/SceneViewport.h"
|
#include "Slate/SceneViewport.h"
|
||||||
|
#include "Virtualization/VirtualizationTypes.h"
|
||||||
|
|
||||||
ULuckyDataTransferSubsystem::ULuckyDataTransferSubsystem()
|
ULuckyDataTransferSubsystem::ULuckyDataTransferSubsystem()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ULuckyDataTransferSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
void ULuckyDataTransferSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||||
{
|
{
|
||||||
Super::Initialize(Collection);
|
Super::Initialize(Collection);
|
||||||
|
|
||||||
|
if (UGameInstance* GI = GetWorld()->GetGameInstance())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ULuckyDataTransferSubsystem::Deinitialize()
|
void ULuckyDataTransferSubsystem::Deinitialize()
|
||||||
@ -123,6 +150,12 @@ FPayload ULuckyDataTransferSubsystem::InterpretData(const FString& Message)
|
|||||||
return Payload;
|
return Payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IncomingMessage(const FString& Message)
|
||||||
|
{
|
||||||
|
const FString outMessage = Message.IsEmpty() ? TEXT("no valid message") : Message;
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("Incoming message: %s"), *outMessage);
|
||||||
|
}
|
||||||
|
|
||||||
void ULuckyDataTransferSubsystem::CommandReady(const FPayload& Payload)
|
void ULuckyDataTransferSubsystem::CommandReady(const FPayload& Payload)
|
||||||
{
|
{
|
||||||
if (OnCommandReady.IsBound())
|
if (OnCommandReady.IsBound())
|
||||||
@ -137,6 +170,12 @@ bool ULuckyDataTransferSubsystem::MakeObservationPayload(const FObservationPaylo
|
|||||||
return CreateJsonPayload_Observation(Data);
|
return CreateJsonPayload_Observation(Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FString ULuckyDataTransferSubsystem::CreateCaptureSessionID()
|
||||||
|
{
|
||||||
|
SessionID = FGuid::NewGuid().ToString().Left(5);
|
||||||
|
return SessionID;
|
||||||
|
}
|
||||||
|
|
||||||
bool ULuckyDataTransferSubsystem::CreateJsonPayload_Observation(const FObservationPayload& Data)
|
bool ULuckyDataTransferSubsystem::CreateJsonPayload_Observation(const FObservationPayload& Data)
|
||||||
{
|
{
|
||||||
bool bSuccess = false;
|
bool bSuccess = false;
|
||||||
@ -151,3 +190,98 @@ bool ULuckyDataTransferSubsystem::CreateJsonPayload_Observation(const FObservati
|
|||||||
|
|
||||||
return bSuccess;
|
return bSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ULuckyDataTransferSubsystem::RegisterSensor(ALuckySensorPawnBase* Sensor)
|
||||||
|
{
|
||||||
|
if (IsValid(Sensor))
|
||||||
|
{
|
||||||
|
SensorPawns.Add(Sensor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ULuckyDataTransferSubsystem::WriteImageToDisk(const FString& inPath, const double Timestamp, const FString& Comment)
|
||||||
|
{
|
||||||
|
if (SessionID.IsEmpty())
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("The Capture Session ID is empty"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString Path = inPath.IsEmpty() ? TEXT("C:/LuckyRobotsImages") : inPath;
|
||||||
|
|
||||||
|
if (!SensorPawns.IsEmpty())
|
||||||
|
{
|
||||||
|
for (const ALuckySensorPawnBase* Sensor : SensorPawns)
|
||||||
|
{
|
||||||
|
if (IsValid(Sensor) && Sensor->SensorInfo.bActive && Sensor->GetCamera() && Sensor->GetCaptureComponent())
|
||||||
|
{
|
||||||
|
FString Robot = TEXT("Robot_Name");
|
||||||
|
|
||||||
|
FString Episode = SessionID;
|
||||||
|
|
||||||
|
ENQUEUE_RENDER_COMMAND(ReadPixelsAsync)([Sensor, Path, Timestamp, Comment, Episode](FRHICommandListImmediate& RHICmdList)
|
||||||
|
{
|
||||||
|
FTextureResource* Resource = Sensor->RenderTarget->GetResource();
|
||||||
|
FRHITexture* ResourceRHI = Resource->GetTexture2DRHI();
|
||||||
|
|
||||||
|
TArray<FColor> OutPixels;
|
||||||
|
|
||||||
|
if (ensure(ResourceRHI))
|
||||||
|
{
|
||||||
|
OutPixels.SetNum(Resource->GetSizeX() * Resource->GetSizeY());
|
||||||
|
RHICmdList.ReadSurfaceData(
|
||||||
|
ResourceRHI,
|
||||||
|
FIntRect(0, 0, Resource->GetSizeX(), Resource->GetSizeY()),
|
||||||
|
OutPixels,
|
||||||
|
FReadSurfaceDataFlags()
|
||||||
|
);
|
||||||
|
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("Logged pixels: %d"), OutPixels.Num())
|
||||||
|
|
||||||
|
FImageWriteTask* ImageTask = new FImageWriteTask();
|
||||||
|
|
||||||
|
if (Path.Right(1) == "/")
|
||||||
|
{
|
||||||
|
//Path = Path.Left(Path.Len() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString Robot = TEXT("Robot_Name");
|
||||||
|
|
||||||
|
const FString Filename = FString::Printf(
|
||||||
|
TEXT("%s/%s/%s/%s/%s_%s_%s_%d"),
|
||||||
|
*Path,
|
||||||
|
*Robot,
|
||||||
|
*Episode,
|
||||||
|
*Sensor->SensorInfo.Name,
|
||||||
|
*Robot,
|
||||||
|
*Sensor->SensorInfo.Name,
|
||||||
|
*Comment,
|
||||||
|
FMath::Floor<int32>(Timestamp * 1000)
|
||||||
|
);
|
||||||
|
|
||||||
|
//UE_LOG(LogTemp, Warning, TEXT("Evan requested a longer string describing the inner workings of the following string which describes in great detail the file path for the image you've just written to disk. It is: %s"), *Filename);
|
||||||
|
|
||||||
|
ImageTask->Format = EImageFormat::PNG;
|
||||||
|
ImageTask->Filename = Filename;
|
||||||
|
ImageTask->PixelData = MakeUnique<TImagePixelData<FColor>>(FIntPoint(Sensor->RenderTarget->GetSurfaceWidth(), Sensor->RenderTarget->GetSurfaceHeight()), TArray64<FColor>(OutPixels));
|
||||||
|
ImageTask->bOverwriteFile = true;
|
||||||
|
ImageTask->CompressionQuality = (int32)EImageCompressionQuality::Default;
|
||||||
|
|
||||||
|
//Add to write queue (async)
|
||||||
|
FModuleManager::LoadModuleChecked<IImageWriteQueueModule>("ImageWriteQueue").GetWriteQueue().Enqueue(TUniquePtr<IImageWriteTaskBase>(ImageTask));
|
||||||
|
|
||||||
|
ImageTask->OnCompleted = [](bool bSuccess) {
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("Image write completed: %s"), bSuccess ? TEXT("Success") : TEXT("Failed"));
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
|
||||||
|
#include "LuckySensorPawnBase.h"
|
||||||
|
|
||||||
|
#include "Camera/CameraComponent.h"
|
||||||
|
#include "Components/SceneCaptureComponent2D.h"
|
||||||
|
#include "Engine/TextureRenderTarget2D.h"
|
||||||
|
#include "GameFramework/SpringArmComponent.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Sets default values
|
||||||
|
ALuckySensorPawnBase::ALuckySensorPawnBase()
|
||||||
|
{
|
||||||
|
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||||
|
PrimaryActorTick.bCanEverTick = true;
|
||||||
|
|
||||||
|
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
|
||||||
|
SpringArm->bDoCollisionTest = false;
|
||||||
|
RootComponent = SpringArm;
|
||||||
|
|
||||||
|
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
|
||||||
|
Camera->SetupAttachment(SpringArm);
|
||||||
|
Camera->SetRelativeTransform(FTransform());
|
||||||
|
|
||||||
|
CaptureComponent = CreateDefaultSubobject<USceneCaptureComponent2D>(TEXT("CameraComponent"));
|
||||||
|
CaptureComponent->SetupAttachment(RootComponent);
|
||||||
|
|
||||||
|
|
||||||
|
RenderTarget = CreateDefaultSubobject<UTextureRenderTarget2D>(TEXT("RenderTarget"));
|
||||||
|
RenderTarget->UpdateResourceImmediate(true);
|
||||||
|
RenderTarget->ResizeTarget(1, 1);
|
||||||
|
|
||||||
|
|
||||||
|
CaptureComponent->CaptureSource = SCS_FinalColorLDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the game starts or when spawned
|
||||||
|
void ALuckySensorPawnBase::BeginPlay()
|
||||||
|
{
|
||||||
|
CaptureComponent->TextureTarget = RenderTarget;
|
||||||
|
|
||||||
|
Super::BeginPlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every frame
|
||||||
|
void ALuckySensorPawnBase::Tick(float DeltaTime)
|
||||||
|
{
|
||||||
|
Super::Tick(DeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called to bind functionality to input
|
||||||
|
void ALuckySensorPawnBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||||
|
{
|
||||||
|
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "IWebSocket.h"
|
#include "IWebSocket.h"
|
||||||
//#include "LuckyWriteThread.h"
|
#include "LuckySensorPawnBase.h"
|
||||||
#include "ObservationData.h"
|
#include "ObservationData.h"
|
||||||
#include "Subsystems/WorldSubsystem.h"
|
#include "Subsystems/WorldSubsystem.h"
|
||||||
#include "LuckyDataTransferSubsystem.generated.h"
|
#include "LuckyDataTransferSubsystem.generated.h"
|
||||||
@ -108,6 +108,25 @@ public:
|
|||||||
bool MakeObservationPayload(const FObservationPayload& Data);
|
bool MakeObservationPayload(const FObservationPayload& Data);
|
||||||
//---------------------------------------------------------//
|
//---------------------------------------------------------//
|
||||||
|
|
||||||
protected:
|
|
||||||
//LuckyWriteThread* WriteThread = nullptr;
|
//---Image Capture----------------------------------------//
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Websocket")
|
||||||
|
FString CreateCaptureSessionID();
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "Websocket")
|
||||||
|
FString SessionID;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category = "Capture")
|
||||||
|
TMap<FString, ACameraActor*> ActiveCameras;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, Category = "Capture")
|
||||||
|
TArray<ALuckySensorPawnBase*> SensorPawns;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Capture")
|
||||||
|
void RegisterSensor(ALuckySensorPawnBase* Sensor);
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Meta = (AutoCreateRefTerm = "Path, Comment"), Category = "Capture")
|
||||||
|
bool WriteImageToDisk(const FString& Path, const double Timestamp, const FString& Comment);
|
||||||
|
//-------------------------------------------------------//
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Camera/CameraActor.h"
|
||||||
|
#include "GameFramework/Pawn.h"
|
||||||
|
#include "Components/SceneCaptureComponent2D.h"
|
||||||
|
#include "Engine/TextureRenderTarget2D.h"
|
||||||
|
#include "GameFramework/SpringArmComponent.h"
|
||||||
|
#include "LuckySensorPawnBase.generated.h"
|
||||||
|
|
||||||
|
USTRUCT(BlueprintType)
|
||||||
|
struct FSensorInfo
|
||||||
|
{
|
||||||
|
GENERATED_USTRUCT_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
FString Name = FString();
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
FVector Anchor = FVector::ZeroVector;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
float Distance = 0.f;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
FRotator Rotation = FRotator::ZeroRotator;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
bool bActive = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class LUCKYDATATRANSFER_API ALuckySensorPawnBase : public APawn
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Sets default values for this pawn's properties
|
||||||
|
ALuckySensorPawnBase();
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
FSensorInfo SensorInfo = FSensorInfo();
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="Sensor")
|
||||||
|
UCameraComponent* GetCamera() const { return Camera; }
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
UTextureRenderTarget2D* RenderTarget = nullptr;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
USceneCaptureComponent2D* CaptureComponent = nullptr;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
USpringArmComponent* SpringArm = nullptr;
|
||||||
|
|
||||||
|
UFUNCTION(BlueprintCallable, Category="Sensor")
|
||||||
|
USceneCaptureComponent2D* GetCaptureComponent() const { return CaptureComponent; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Called when the game starts or when spawned
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Sensor")
|
||||||
|
UCameraComponent* Camera = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Called every frame
|
||||||
|
virtual void Tick(float DeltaTime) override;
|
||||||
|
|
||||||
|
// Called to bind functionality to input
|
||||||
|
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||||
|
};
|
@ -111,7 +111,12 @@ template <typename ComponentType> void AMujocoVolumeActor::AssignComponentsToArr
|
|||||||
|
|
||||||
void AMujocoVolumeActor::InitializeMujoco()
|
void AMujocoVolumeActor::InitializeMujoco()
|
||||||
{
|
{
|
||||||
if (!Options)
|
InitializeMujocoScene_WithContactExclusion(TMap<FString, FString>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AMujocoVolumeActor::InitializeMujocoScene_WithContactExclusion(const TMap<FString, FString>& ContactExclusion)
|
||||||
|
{
|
||||||
|
if (!Options)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -149,7 +154,7 @@ void AMujocoVolumeActor::InitializeMujoco()
|
|||||||
}
|
}
|
||||||
|
|
||||||
TUniquePtr<FMujocoXmlGenerator> Generator = MakeUnique<FMujocoXmlGenerator>();
|
TUniquePtr<FMujocoXmlGenerator> Generator = MakeUnique<FMujocoXmlGenerator>();
|
||||||
TUniquePtr<tinyxml2::XMLDocument> Doc = Generator->GenerateMujocoXml(Options, Objects, ExportFilename);
|
TUniquePtr<tinyxml2::XMLDocument> Doc = Generator->GenerateMujocoXml(Options, Objects, ExportFilename, ContactExclusion);
|
||||||
|
|
||||||
FString XmlString;
|
FString XmlString;
|
||||||
tinyxml2::XMLPrinter Printer;
|
tinyxml2::XMLPrinter Printer;
|
||||||
@ -164,6 +169,8 @@ void AMujocoVolumeActor::InitializeMujoco()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Here we should check for mujoco dll if we are on windows and LOG an error if there is not + gently quit the game
|
||||||
|
|
||||||
std::array<char, 1024> ErrMsg{};
|
std::array<char, 1024> ErrMsg{};
|
||||||
MujocoModel = MakeMujocoModelPtr(mj_loadXML(TCHAR_TO_ANSI(*ExportFilename), nullptr, ErrMsg.data(), ErrMsg.size()));
|
MujocoModel = MakeMujocoModelPtr(mj_loadXML(TCHAR_TO_ANSI(*ExportFilename), nullptr, ErrMsg.data(), ErrMsg.size()));
|
||||||
if (!MujocoModel)
|
if (!MujocoModel)
|
||||||
@ -196,6 +203,12 @@ void AMujocoVolumeActor::InitializeMujoco()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mjData_& AMujocoVolumeActor::GetMujocoData() const
|
||||||
|
{
|
||||||
|
check(MujocoData.IsValid());
|
||||||
|
return *MujocoData.Get();
|
||||||
|
}
|
||||||
|
|
||||||
void AMujocoVolumeActor::SetActuatorValue(const FString& ActuatorName, double Value)
|
void AMujocoVolumeActor::SetActuatorValue(const FString& ActuatorName, double Value)
|
||||||
{
|
{
|
||||||
if (MujocoModel)
|
if (MujocoModel)
|
||||||
@ -391,11 +404,13 @@ void AMujocoVolumeActor::Tick(float DeltaTime)
|
|||||||
{
|
{
|
||||||
if (MujocoData)
|
if (MujocoData)
|
||||||
{
|
{
|
||||||
mj_step(MujocoModel.Get(), MujocoData.Get());
|
// Default SimStepsPerGameFrame = 16 x 1ms for roughly 60FPS
|
||||||
|
// If ticks are strictly 16.666 ms then simulation will lag behind UE clock
|
||||||
for (int32 Frame = 0; Frame < FrameSkip; ++Frame)
|
// Anyway, we need a proper synchronisation between UE and Sim times - probably using TempoTime?
|
||||||
|
for (int32 SimStep = 0; SimStep < SimStepsPerGameFrame; ++SimStep)
|
||||||
{
|
{
|
||||||
mj_step(MujocoModel.Get(), MujocoData.Get());
|
mj_step(MujocoModel.Get(), MujocoData.Get());
|
||||||
|
PostPhysicStepDelegate.ExecuteIfBound(MujocoData->time);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int32 i = 1; i < BodyComponents.Num(); ++i)
|
for (int32 i = 1; i < BodyComponents.Num(); ++i)
|
||||||
|
@ -1255,7 +1255,11 @@ bool FMujocoXmlGenerator::ParseActorAsset(AActor* Actor, tinyxml2::XMLElement* R
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TUniquePtr<tinyxml2::XMLDocument> FMujocoXmlGenerator::GenerateMujocoXml(const TObjectPtr<UMujocoExportOptions>& ExportOptions, const TArray<UObject*>& Objects, const FString& ExportFilename)
|
TUniquePtr<tinyxml2::XMLDocument> FMujocoXmlGenerator::GenerateMujocoXml(
|
||||||
|
const TObjectPtr<UMujocoExportOptions>& ExportOptions,
|
||||||
|
const TArray<UObject*>& Objects,
|
||||||
|
const FString& ExportFilename,
|
||||||
|
TMap<FString, FString> ContactExclusion)
|
||||||
{
|
{
|
||||||
TUniquePtr<tinyxml2::XMLDocument> Doc = MakeUnique<tinyxml2::XMLDocument>();
|
TUniquePtr<tinyxml2::XMLDocument> Doc = MakeUnique<tinyxml2::XMLDocument>();
|
||||||
Doc->InsertFirstChild(Doc->NewDeclaration("xml version=\"1.0\" encoding=\"utf-8\""));
|
Doc->InsertFirstChild(Doc->NewDeclaration("xml version=\"1.0\" encoding=\"utf-8\""));
|
||||||
@ -1268,6 +1272,30 @@ TUniquePtr<tinyxml2::XMLDocument> FMujocoXmlGenerator::GenerateMujocoXml(const T
|
|||||||
Root->InsertEndChild(Doc->NewElement("equality"));
|
Root->InsertEndChild(Doc->NewElement("equality"));
|
||||||
Root->InsertEndChild(Doc->NewElement("tendon"));
|
Root->InsertEndChild(Doc->NewElement("tendon"));
|
||||||
Root->InsertEndChild(Doc->NewElement("actuator"));
|
Root->InsertEndChild(Doc->NewElement("actuator"));
|
||||||
|
|
||||||
|
// TODO Refacto using property from the MujocoActor aka the robot
|
||||||
|
// TODO Maybe using a ParentObject as parameter which holds that information?
|
||||||
|
// TODO Need actor refactoring anyway - Merge Pawn and Mujoco Actor?
|
||||||
|
if (!ContactExclusion.IsEmpty())
|
||||||
|
{
|
||||||
|
Root->InsertEndChild(Doc->NewElement("contact"));
|
||||||
|
|
||||||
|
for (auto& [Body1, Body2] : ContactExclusion)
|
||||||
|
{
|
||||||
|
// Don't add empty strings to the map please!
|
||||||
|
if (Body1.IsEmpty() || Body2.IsEmpty()) continue;
|
||||||
|
|
||||||
|
// Create the contact exclusion - formatted as (SO100 example)
|
||||||
|
// <exclude body1="Body_Base" body2="Body_Rotation_Pitch"/>
|
||||||
|
const auto Exclude = Doc->NewElement("exclude");
|
||||||
|
Exclude->SetAttribute("body1", TCHAR_TO_ANSI(*Body1));
|
||||||
|
Exclude->SetAttribute("body2", TCHAR_TO_ANSI(*Body2));
|
||||||
|
|
||||||
|
// Add exclusion to the contact tag
|
||||||
|
Root->FirstChildElement("contact")->InsertEndChild(Exclude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ExportOptions->bAddSkyBox)
|
if (ExportOptions->bAddSkyBox)
|
||||||
{
|
{
|
||||||
tinyxml2::XMLElement* TextureElement = AssetRoot->GetDocument()->NewElement("texture");
|
tinyxml2::XMLElement* TextureElement = AssetRoot->GetDocument()->NewElement("texture");
|
||||||
|
@ -21,11 +21,44 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMujocoCompileBegin);
|
|||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMujocoCompileError, FString, Error);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnMujocoCompileError, FString, Error);
|
||||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMujocoCompileSuccess);
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnMujocoCompileSuccess);
|
||||||
|
|
||||||
|
// To be called after mj_step
|
||||||
|
DECLARE_DELEGATE_OneParam(FPostPhysicUpdate, float);
|
||||||
|
|
||||||
|
|
||||||
UCLASS(Blueprintable, BlueprintType)
|
UCLASS(Blueprintable, BlueprintType)
|
||||||
class LUCKYMUJOCO_API AMujocoVolumeActor : public AActor
|
class LUCKYMUJOCO_API AMujocoVolumeActor : public AActor
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
AMujocoVolumeActor();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the sim scene in headless mujoco
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Mujoco")
|
||||||
|
void InitializeMujoco();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the sim scene in headless mujoco with a list of contact exclusion
|
||||||
|
* @param ContactExclusion a list of pairs that should be patched in the xml file for contact exclusion (no friction, no collision)
|
||||||
|
* TODO Can't use a default empty map as parameter in blueprints? We shouldn't need to have 2 functions
|
||||||
|
* TODO ContactExclusion should be stored as a property of MujocoActor and not passed in the scene init
|
||||||
|
* TODO This require to cast the Actor in addition to the components list
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Mujoco")
|
||||||
|
void InitializeMujocoScene_WithContactExclusion(const TMap<FString, FString>& ContactExclusion);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||||
|
virtual void PostRegisterAllComponents() override;
|
||||||
|
virtual void Tick(float DeltaTime) override;
|
||||||
|
virtual void PostInitializeComponents() override;
|
||||||
|
|
||||||
|
private:
|
||||||
TMujocoModelPtr MujocoModel;
|
TMujocoModelPtr MujocoModel;
|
||||||
TMujocoDataPtr MujocoData;
|
TMujocoDataPtr MujocoData;
|
||||||
|
|
||||||
@ -50,16 +83,11 @@ class LUCKYMUJOCO_API AMujocoVolumeActor : public AActor
|
|||||||
UPROPERTY(Transient, VisibleAnywhere, Category = "Mujoco | Debug")
|
UPROPERTY(Transient, VisibleAnywhere, Category = "Mujoco | Debug")
|
||||||
TArray<TSoftObjectPtr<UMujocoTendonComponent>> TendonComponents;
|
TArray<TSoftObjectPtr<UMujocoTendonComponent>> TendonComponents;
|
||||||
|
|
||||||
UFUNCTION(BlueprintCallable, Category = "Mujoco")
|
|
||||||
void InitializeMujoco();
|
|
||||||
|
|
||||||
template <typename ComponentType> void AssignComponentsToArray(UWorld* World, TArray<TSoftObjectPtr<ComponentType>>& ComponentArray);
|
template <typename ComponentType> void AssignComponentsToArray(UWorld* World, TArray<TSoftObjectPtr<ComponentType>>& ComponentArray);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AMujocoVolumeActor();
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mujoco | Simulation", meta = (Min = 1, Max = 100, ClampMin = 0, ClampMax = 100))
|
||||||
|
int32 SimStepsPerGameFrame = 16;
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mujoco | Simulation", meta = (Min = 0, Max = 100, ClampMin = 0, ClampMax = 100))
|
|
||||||
int32 FrameSkip = 0;
|
|
||||||
|
|
||||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Mujoco")
|
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Mujoco")
|
||||||
TObjectPtr<UBillboardComponent> SpriteComponent;
|
TObjectPtr<UBillboardComponent> SpriteComponent;
|
||||||
@ -76,6 +104,33 @@ public:
|
|||||||
UPROPERTY(BlueprintAssignable, Category = "Mujoco | Events")
|
UPROPERTY(BlueprintAssignable, Category = "Mujoco | Events")
|
||||||
FOnMujocoCompileSuccess OnMujocoCompileSuccess;
|
FOnMujocoCompileSuccess OnMujocoCompileSuccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access MuJoCo scene options and data (e.g. Simulation time) - This is mutable, be careful
|
||||||
|
* @return mjData_ - Full access to mujoco scene options and data
|
||||||
|
*/
|
||||||
|
mjData_& GetMujocoData() const;
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// ------- POST UPDATE -------
|
||||||
|
// ---------------------------
|
||||||
|
private:
|
||||||
|
FPostPhysicUpdate PostPhysicStepDelegate;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Register a delegate to be executed after mj_step, useful to fine control actuators
|
||||||
|
* @param Object - The Class to call
|
||||||
|
* @param Func - The Function to call - takes a float as parameter which is the current simulation timestamp
|
||||||
|
*/
|
||||||
|
template<typename UserClass>
|
||||||
|
void BindPostPhysicStepDelegate(UserClass* Object, void (UserClass::*Func)(float))
|
||||||
|
{
|
||||||
|
PostPhysicStepDelegate.BindUObject(Object, Func);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------
|
||||||
|
// ------- ACTUATORS -------
|
||||||
|
// -------------------------
|
||||||
UFUNCTION(BlueprintCallable, Category = "Mujoco")
|
UFUNCTION(BlueprintCallable, Category = "Mujoco")
|
||||||
void SetActuatorValue(const FString& ActuatorName, double Value);
|
void SetActuatorValue(const FString& ActuatorName, double Value);
|
||||||
|
|
||||||
@ -94,6 +149,9 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Mujoco")
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Mujoco")
|
||||||
FVector2D GetActuatorRangeByIndex(int32 ActuatorIndex) const;
|
FVector2D GetActuatorRangeByIndex(int32 ActuatorIndex) const;
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
// ------- JOINTS -------
|
||||||
|
// ----------------------
|
||||||
UFUNCTION(BlueprintCallable, Category = "Mujoco")
|
UFUNCTION(BlueprintCallable, Category = "Mujoco")
|
||||||
void SetJointValue(const FString& JointName, double Value);
|
void SetJointValue(const FString& JointName, double Value);
|
||||||
|
|
||||||
@ -105,16 +163,4 @@ public:
|
|||||||
|
|
||||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Mujoco")
|
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Mujoco")
|
||||||
double GetJointValueByIndex(int32 JointIndex) const;
|
double GetJointValueByIndex(int32 JointIndex) const;
|
||||||
|
|
||||||
|
|
||||||
virtual void PostRegisterAllComponents() override;
|
|
||||||
|
|
||||||
virtual void Tick(float DeltaTime) override;
|
|
||||||
|
|
||||||
virtual void PostInitializeComponents() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
virtual void BeginPlay() override;
|
|
||||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
|
||||||
};
|
};
|
||||||
|
@ -307,13 +307,13 @@ struct LUCKYMUJOCO_API FMujocoOptions
|
|||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
/** Override TimeStep setting. When enabled, the default value (0.002) is applied. */
|
/** Override TimeStep setting. When enabled, the default value (0.001) is applied. */
|
||||||
UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = "Mujoco", meta = (InlineEditConditionToggle))
|
UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = "Mujoco", meta = (InlineEditConditionToggle))
|
||||||
bool bOverrideTimeStep = false;
|
bool bOverrideTimeStep = false;
|
||||||
|
|
||||||
/** Simulation time step in seconds. */
|
/** Simulation time step in seconds. */
|
||||||
UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = "Mujoco", meta = (Attribute = "timestep", EditCondition = "bOverrideTimeStep", ClampMin = 0.0001, ClampMax = 0.01, UIMin = 0.0001, UIMax = 0.01))
|
UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = "Mujoco", meta = (Attribute = "timestep", EditCondition = "bOverrideTimeStep", ClampMin = 0.0001, ClampMax = 0.01, UIMin = 0.0001, UIMax = 0.01))
|
||||||
float TimeStep = 0.002f;
|
float TimeStep = 0.001f; // Default to 1ms
|
||||||
|
|
||||||
/** Override API Rate setting. */
|
/** Override API Rate setting. */
|
||||||
UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = "Mujoco", meta = (InlineEditConditionToggle))
|
UPROPERTY(config, EditAnywhere, BlueprintReadWrite, Category = "Mujoco", meta = (InlineEditConditionToggle))
|
||||||
|
@ -36,5 +36,18 @@ class LUCKYMUJOCO_API FMujocoXmlGenerator
|
|||||||
TMap<FString, TSoftObjectPtr<UObject>> ObjectMap;
|
TMap<FString, TSoftObjectPtr<UObject>> ObjectMap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TUniquePtr<tinyxml2::XMLDocument> GenerateMujocoXml(const TObjectPtr<UMujocoExportOptions>& ExportOptions, const TArray<UObject*>& Objects, const FString& ExportFilename);
|
|
||||||
|
/**
|
||||||
|
* Generate the XML file used by the MuJoCo headless scene representing the physics simulation
|
||||||
|
* @param ExportOptions TODO - What they can be?
|
||||||
|
* @param Objects Actors and Components to be added to the scene
|
||||||
|
* @param ExportFilename pretty much that
|
||||||
|
* @param ContactExclusion a list of pairs that should be patched in the xml file for contact exclusion (no friction, no collision)
|
||||||
|
* TODO ContactExclusion should be stored as a property of MujocoActor and not passed in the scene init
|
||||||
|
*/
|
||||||
|
TUniquePtr<tinyxml2::XMLDocument> GenerateMujocoXml(
|
||||||
|
const TObjectPtr<UMujocoExportOptions>& ExportOptions,
|
||||||
|
const TArray<UObject*>& Objects,
|
||||||
|
const FString& ExportFilename,
|
||||||
|
TMap<FString, FString> ContactExclusion);
|
||||||
};
|
};
|
@ -141,8 +141,8 @@ public:
|
|||||||
CancelButton->SetEnabled(false);
|
CancelButton->SetEnabled(false);
|
||||||
RequestDestroyWindow();
|
RequestDestroyWindow();
|
||||||
|
|
||||||
TUniquePtr<FMujocoXmlGenerator> Generator = MakeUnique<FMujocoXmlGenerator>();
|
const TUniquePtr<FMujocoXmlGenerator> Generator = MakeUnique<FMujocoXmlGenerator>();
|
||||||
TUniquePtr<tinyxml2::XMLDocument> Doc = Generator->GenerateMujocoXml(ExportOptions, Objects, ExportFilename);
|
const TUniquePtr<tinyxml2::XMLDocument> Doc = Generator->GenerateMujocoXml(ExportOptions, Objects, ExportFilename, TMap<FString, FString>{});
|
||||||
|
|
||||||
FString XmlString;
|
FString XmlString;
|
||||||
tinyxml2::XMLPrinter Printer;
|
tinyxml2::XMLPrinter Printer;
|
||||||
|
@ -19,7 +19,8 @@ public class LuckyWorldV2 : ModuleRules
|
|||||||
"SocketIOClient",
|
"SocketIOClient",
|
||||||
"VaRest",
|
"VaRest",
|
||||||
"SIOJson",
|
"SIOJson",
|
||||||
"NavigationSystem"
|
"NavigationSystem",
|
||||||
|
"RenderCore"
|
||||||
});
|
});
|
||||||
|
|
||||||
PrivateDependencyModuleNames.AddRange(new string[] { });
|
PrivateDependencyModuleNames.AddRange(new string[] { });
|
||||||
|
180
Source/LuckyWorldV2/Private/LRRenderUtilLibrary.cpp
Normal file
180
Source/LuckyWorldV2/Private/LRRenderUtilLibrary.cpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#include "LRRenderUtilLibrary.h"
|
||||||
|
|
||||||
|
#include "Components/SceneCaptureComponent2D.h"
|
||||||
|
#include "Engine/TextureRenderTarget2D.h"
|
||||||
|
#include "Kismet/KismetRenderingLibrary.h"
|
||||||
|
#include "GameFramework/Actor.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
|
|
||||||
|
void ULRRenderUtilLibrary::SetupSceneCaptureForMesh(
|
||||||
|
USceneCaptureComponent2D* SceneCapture,
|
||||||
|
UStaticMeshComponent* MeshComp,
|
||||||
|
TEnumAsByte<enum ESceneCaptureSource> CaptureSource)
|
||||||
|
{
|
||||||
|
if (!SceneCapture || !MeshComp)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("SetupSceneCaptureForMesh: Invalid SceneCapture or MeshComp"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MeshComp->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("SetupSceneCaptureForMesh: MeshComp is not valid"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the scene capture to focus on the mesh
|
||||||
|
SceneCapture->ShowOnlyComponent(MeshComp);
|
||||||
|
|
||||||
|
SceneCapture->CaptureSource = CaptureSource;
|
||||||
|
SceneCapture->bCaptureEveryFrame = false;
|
||||||
|
SceneCapture->bCaptureOnMovement = false;
|
||||||
|
SceneCapture->FOVAngle = 90.f;
|
||||||
|
|
||||||
|
// Set a solid background color
|
||||||
|
SceneCapture->CompositeMode = ESceneCaptureCompositeMode::SCCM_Overwrite;
|
||||||
|
|
||||||
|
// Ensure we have a valid texture target
|
||||||
|
if (SceneCapture->TextureTarget)
|
||||||
|
{
|
||||||
|
SceneCapture->TextureTarget->ClearColor = FLinearColor::Transparent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("SetupSceneCaptureForMesh: TextureTarget is null"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust the transform to frame the mesh
|
||||||
|
FVector MeshBounds = MeshComp->Bounds.BoxExtent;
|
||||||
|
FVector MeshOrigin = MeshComp->Bounds.Origin;
|
||||||
|
|
||||||
|
// Calculate the camera distance based on mesh size
|
||||||
|
float MaxExtent = FMath::Max3(MeshBounds.X, MeshBounds.Y, MeshBounds.Z);
|
||||||
|
float CameraDistance = MaxExtent * 2.5f; // Adjusted to ensure mesh fits in view
|
||||||
|
|
||||||
|
// Calculate the camera location - position from front of mesh (+X axis)
|
||||||
|
FVector CameraLocation = MeshOrigin + FVector(CameraDistance, 0.f, 0.f);
|
||||||
|
|
||||||
|
// Calculate the camera rotation to look at mesh center
|
||||||
|
FRotator CameraRotation = (MeshOrigin - CameraLocation).Rotation();
|
||||||
|
|
||||||
|
SceneCapture->SetWorldLocationAndRotation(CameraLocation, CameraRotation);
|
||||||
|
|
||||||
|
// Register component to make sure it will render
|
||||||
|
if (!SceneCapture->IsRegistered())
|
||||||
|
{
|
||||||
|
SceneCapture->RegisterComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UTextureRenderTarget2D* ULRRenderUtilLibrary::CaptureMeshToRenderTarget(
|
||||||
|
UStaticMeshComponent* MeshComp,
|
||||||
|
const FVector2D& ImageSize,
|
||||||
|
UObject* WorldContextObject,
|
||||||
|
TEnumAsByte<enum ESceneCaptureSource> CaptureSource)
|
||||||
|
{
|
||||||
|
if (!MeshComp || !WorldContextObject || !WorldContextObject->GetWorld())
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("CaptureMeshToRenderTarget: Invalid parameters"));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a render target with proper size
|
||||||
|
UTextureRenderTarget2D* RenderTarget = UKismetRenderingLibrary::CreateRenderTarget2D(
|
||||||
|
WorldContextObject,
|
||||||
|
FMath::Max(32, static_cast<int32>(ImageSize.X)),
|
||||||
|
FMath::Max(32, static_cast<int32>(ImageSize.Y)),
|
||||||
|
ETextureRenderTargetFormat::RTF_RGBA8);
|
||||||
|
|
||||||
|
if (!RenderTarget)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("CaptureMeshToRenderTarget: Failed to create render target"));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a temporary actor to hold the scene capture component
|
||||||
|
AActor* TempActor = WorldContextObject->GetWorld()->SpawnActor<AActor>();
|
||||||
|
if (!TempActor)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("CaptureMeshToRenderTarget: Failed to spawn temporary actor"));
|
||||||
|
return RenderTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a root component if it doesn't exist
|
||||||
|
if (!TempActor->GetRootComponent())
|
||||||
|
{
|
||||||
|
USceneComponent* RootComponent = NewObject<USceneComponent>(TempActor, TEXT("RootComponent"));
|
||||||
|
TempActor->SetRootComponent(RootComponent);
|
||||||
|
RootComponent->RegisterComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and set up scene capture component
|
||||||
|
USceneCaptureComponent2D* SceneCapture = NewObject<USceneCaptureComponent2D>(TempActor);
|
||||||
|
if (SceneCapture)
|
||||||
|
{
|
||||||
|
SceneCapture->RegisterComponent();
|
||||||
|
SceneCapture->AttachToComponent(TempActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
|
||||||
|
SceneCapture->TextureTarget = RenderTarget;
|
||||||
|
|
||||||
|
// Setup the scene capture
|
||||||
|
SetupSceneCaptureForMesh(SceneCapture, MeshComp, CaptureSource);
|
||||||
|
|
||||||
|
// Clear the render target
|
||||||
|
UKismetRenderingLibrary::ClearRenderTarget2D(WorldContextObject, RenderTarget, FLinearColor::Transparent);
|
||||||
|
|
||||||
|
// Disable post-processing effects
|
||||||
|
SceneCapture->PostProcessSettings.bOverride_AutoExposureMethod = true;
|
||||||
|
SceneCapture->PostProcessSettings.AutoExposureMethod = EAutoExposureMethod::AEM_Manual;
|
||||||
|
SceneCapture->PostProcessSettings.bOverride_AutoExposureBias = true;
|
||||||
|
SceneCapture->PostProcessSettings.AutoExposureBias = 1.0f;
|
||||||
|
SceneCapture->PostProcessSettings.bOverride_BloomIntensity = true;
|
||||||
|
SceneCapture->PostProcessSettings.BloomIntensity = 0.0f;
|
||||||
|
|
||||||
|
// Trigger a one-time capture
|
||||||
|
SceneCapture->CaptureScene();
|
||||||
|
|
||||||
|
// Force the GPU to complete rendering
|
||||||
|
FlushRenderingCommands();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("CaptureMeshToRenderTarget: Failed to create scene capture component"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the temporary actor
|
||||||
|
TempActor->Destroy();
|
||||||
|
|
||||||
|
return RenderTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ULRRenderUtilLibrary::CaptureSceneNow(USceneCaptureComponent2D* SceneCapture)
|
||||||
|
{
|
||||||
|
if (!SceneCapture || !SceneCapture->IsValidLowLevel())
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("CaptureSceneNow: Invalid scene capture component"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SceneCapture->TextureTarget)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("CaptureSceneNow: Scene capture has no texture target"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure component is registered
|
||||||
|
if (!SceneCapture->IsRegistered())
|
||||||
|
{
|
||||||
|
SceneCapture->RegisterComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger the capture
|
||||||
|
SceneCapture->CaptureScene();
|
||||||
|
|
||||||
|
// Force the GPU to complete rendering
|
||||||
|
FlushRenderingCommands();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
57
Source/LuckyWorldV2/Public/LRRenderUtilLibrary.h
Normal file
57
Source/LuckyWorldV2/Public/LRRenderUtilLibrary.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Fill out your copyright notice in the Description page of Project Settings.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||||
|
#include "LRRenderUtilLibrary.generated.h"
|
||||||
|
|
||||||
|
class USceneCaptureComponent2D;
|
||||||
|
class UTextureRenderTarget2D;
|
||||||
|
class UStaticMeshComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility library for capturing and rendering objects in the Lucky Robots system
|
||||||
|
*/
|
||||||
|
UCLASS(BlueprintType, Blueprintable)
|
||||||
|
class LUCKYWORLDV2_API ULRRenderUtilLibrary : public UBlueprintFunctionLibrary
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Captures a static mesh component to a render target texture
|
||||||
|
* @param MeshComp The static mesh component to capture
|
||||||
|
* @param ImageSize The size of the output image
|
||||||
|
* @param WorldContextObject The world context
|
||||||
|
* @param CaptureSource The type of data to capture (default is BaseColor)
|
||||||
|
* @return The rendered texture
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Lucky Robots|Render Utilities")
|
||||||
|
static UTextureRenderTarget2D* CaptureMeshToRenderTarget(
|
||||||
|
UStaticMeshComponent* MeshComp,
|
||||||
|
const FVector2D& ImageSize,
|
||||||
|
|
||||||
|
UObject* WorldContextObject,
|
||||||
|
TEnumAsByte<enum ESceneCaptureSource> CaptureSource = ESceneCaptureSource::SCS_BaseColor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up a scene capture component to focus on a specific mesh
|
||||||
|
* @param SceneCapture The scene capture component to set up
|
||||||
|
* @param MeshComp The static mesh component to focus on
|
||||||
|
* @param CaptureSource The type of data to capture (default is BaseColor)
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Lucky Robots|Render Utilities")
|
||||||
|
static void SetupSceneCaptureForMesh(
|
||||||
|
USceneCaptureComponent2D* SceneCapture,
|
||||||
|
UStaticMeshComponent* MeshComp,
|
||||||
|
TEnumAsByte<enum ESceneCaptureSource> CaptureSource = ESceneCaptureSource::SCS_BaseColor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captures the current view of a scene capture component to its render target
|
||||||
|
* @param SceneCapture The scene capture component to trigger
|
||||||
|
* @return Success status
|
||||||
|
*/
|
||||||
|
UFUNCTION(BlueprintCallable, Category = "Lucky Robots|Render Utilities")
|
||||||
|
static bool CaptureSceneNow(USceneCaptureComponent2D* SceneCapture);
|
||||||
|
};
|
@ -6,6 +6,38 @@ set "UE_PATH=C:\Program Files\UE_5.5"
|
|||||||
set "PROJECT_PATH=%~dp0"
|
set "PROJECT_PATH=%~dp0"
|
||||||
set "UAT_PATH=%UE_PATH%\Engine\Build\BatchFiles\RunUAT.bat"
|
set "UAT_PATH=%UE_PATH%\Engine\Build\BatchFiles\RunUAT.bat"
|
||||||
|
|
||||||
|
|
||||||
|
:: Check if MuJoCo DLL exists in bin directory
|
||||||
|
if not exist "%MUJOCO_BIN_PATH%\mujoco.dll" (
|
||||||
|
echo Error: MuJoCo DLL not found at %MUJOCO_BIN_PATH%\mujoco.dll
|
||||||
|
echo Please ensure the MuJoCo DLL is properly built and placed in the correct location.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Copy MuJoCo DLL to the plugin binaries directory if it doesn't exist there
|
||||||
|
if not exist "%MUJOCO_DLL_PATH%\mujoco.dll" (
|
||||||
|
echo Copying MuJoCo DLL to plugin binaries directory...
|
||||||
|
copy "%MUJOCO_BIN_PATH%\mujoco.dll" "%MUJOCO_DLL_PATH%\"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Failed to copy MuJoCo DLL
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
:: Add UE and MuJoCo paths to PATH environment variable
|
||||||
|
set "PATH=%UE_PATH%\Engine\Binaries\Win64;%MUJOCO_DLL_PATH%;%MUJOCO_BIN_PATH%;%PATH%"
|
||||||
|
|
||||||
|
:: Set the DLL search path for the current process
|
||||||
|
set "PATH=%PATH%;%MUJOCO_DLL_PATH%;%MUJOCO_BIN_PATH%"
|
||||||
|
|
||||||
|
:: Run the Unreal Editor
|
||||||
|
:: echo Starting Unreal Editor...
|
||||||
|
:: start "" "%UE_PATH%\Engine\Binaries\Win64\UnrealEditor.exe" "%PROJECT_PATH%LuckyWorldV2.uproject"
|
||||||
|
|
||||||
|
endlocal
|
||||||
|
|
||||||
:: Build and package command
|
:: Build and package command
|
||||||
"%UAT_PATH%" ^
|
"%UAT_PATH%" ^
|
||||||
-ScriptsForProject="%PROJECT_PATH%LuckyWorldV2.uproject" ^
|
-ScriptsForProject="%PROJECT_PATH%LuckyWorldV2.uproject" ^
|
||||||
|
Loading…
x
Reference in New Issue
Block a user