From 67fa8675039931de8946c7f4ef96efde1d30bc20 Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 7 Aug 2024 10:11:43 +0300 Subject: [PATCH 01/74] feat: initial file commit --- Resources/Icon128.png | Bin 0 -> 12699 bytes Resources/PlaceholderButtonIcon.svg | 3 + RpmNextGen.uplugin | 41 ++ .../Private/Api/Assets/AssetApi.cpp | 46 ++ .../Private/Api/Assets/AssetLoader.cpp | 76 +++ .../Private/Api/Auth/ApiKeyAuthStrategy.cpp | 30 + .../Private/Api/Characters/CharacterApi.cpp | 133 +++++ .../RpmNextGen/Private/Api/Common/WebApi.cpp | 69 +++ .../Private/Api/Common/WebApiWithAuth.cpp | 63 +++ Source/RpmNextGen/Private/RpmActor.cpp | 141 +++++ Source/RpmNextGen/Private/RpmNextGen.cpp | 20 + .../Private/Samples/AssetButtonWidget.cpp | 112 ++++ .../Samples/CharacterCustomizationWidget.cpp | 160 ++++++ .../Private/Settings/RpmSettings.cpp | 16 + .../Private/Settings/RpmSettingsData.cpp | 13 + .../RpmNextGen/Public/Api/Assets/AssetApi.h | 22 + .../Public/Api/Assets/AssetLoader.h | 29 + .../Public/Api/Assets/Models/Asset.h | 32 ++ .../Api/Assets/Models/AssetListRequest.h | 51 ++ .../Api/Assets/Models/AssetListResponse.h | 17 + .../Public/Api/Auth/ApiKeyAuthStrategy.h | 15 + .../RpmNextGen/Public/Api/Auth/ApiRequest.h | 56 ++ .../Public/Api/Auth/IAuthenticationStrategy.h | 16 + .../Api/Auth/Models/RefreshTokenRequest.h | 52 ++ .../Api/Auth/Models/RefreshTokenResponse.h | 53 ++ .../Public/Api/Characters/CharacterApi.h | 63 +++ .../Models/CharacterCreateRequest.h | 25 + .../Models/CharacterCreateResponse.h | 15 + .../Models/CharacterFindByIdRequest.h | 13 + .../Models/CharacterFindByIdResponse.h | 15 + .../Models/CharacterPreviewRequest.h | 27 + .../Models/CharacterUpdateRequest.h | 26 + .../Models/CharacterUpdateResponse.h | 15 + .../Api/Characters/Models/RpmCharacter.h | 32 ++ .../Public/Api/Common/Models/ApiResponse.h | 19 + Source/RpmNextGen/Public/Api/Common/WebApi.h | 46 ++ .../Public/Api/Common/WebApiWithAuth.h | 29 + Source/RpmNextGen/Public/RpmActor.h | 74 +++ Source/RpmNextGen/Public/RpmNextGen.h | 15 + .../Public/Samples/AssetButtonWidget.h | 48 ++ .../Samples/CharacterCustomizationWidget.h | 67 +++ .../RpmNextGen/Public/Settings/RpmSettings.h | 16 + .../Public/Settings/RpmSettingsData.h | 35 ++ Source/RpmNextGen/RpmNextGen.Build.cs | 60 ++ .../Private/Auth/DevAuthTokenCache.cpp | 49 ++ .../Private/Auth/DeveloperAccountApi.cpp | 67 +++ .../Auth/DeveloperTokenAuthStrategy.cpp | 68 +++ .../Private/EditorAssetLoader.cpp | 85 +++ .../RpmNextGenEditor/Private/EditorCache.cpp | 74 +++ .../Private/LoginWindowCommands.cpp | 10 + .../Private/LoginWindowStyle.cpp | 58 ++ .../Private/RpmNextGenEditor.cpp | 102 ++++ .../Private/SRpmDeveloperLoginWidget.cpp | 531 ++++++++++++++++++ .../Public/Auth/DevAuthTokenCache.h | 75 +++ .../Public/Auth/DeveloperAccountApi.h | 85 +++ .../Public/Auth/DeveloperLoginRequest.h | 58 ++ .../Public/Auth/DeveloperLoginResponse.h | 48 ++ .../Public/Auth/DeveloperTokenAuthStrategy.h | 23 + .../Public/EditorAssetLoader.h | 18 + Source/RpmNextGenEditor/Public/EditorCache.h | 23 + .../Public/LoginWindowCommands.h | 20 + .../Public/LoginWindowStyle.h | 28 + .../Public/RpmNextGenEditor.h | 26 + .../Public/SRpmDeveloperLoginWidget.h | 79 +++ .../RpmNextGenEditor.Build.cs | 62 ++ 65 files changed, 3495 insertions(+) create mode 100644 Resources/Icon128.png create mode 100644 Resources/PlaceholderButtonIcon.svg create mode 100644 RpmNextGen.uplugin create mode 100644 Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp create mode 100644 Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp create mode 100644 Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp create mode 100644 Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp create mode 100644 Source/RpmNextGen/Private/Api/Common/WebApi.cpp create mode 100644 Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp create mode 100644 Source/RpmNextGen/Private/RpmActor.cpp create mode 100644 Source/RpmNextGen/Private/RpmNextGen.cpp create mode 100644 Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp create mode 100644 Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp create mode 100644 Source/RpmNextGen/Private/Settings/RpmSettings.cpp create mode 100644 Source/RpmNextGen/Private/Settings/RpmSettingsData.cpp create mode 100644 Source/RpmNextGen/Public/Api/Assets/AssetApi.h create mode 100644 Source/RpmNextGen/Public/Api/Assets/AssetLoader.h create mode 100644 Source/RpmNextGen/Public/Api/Assets/Models/Asset.h create mode 100644 Source/RpmNextGen/Public/Api/Assets/Models/AssetListRequest.h create mode 100644 Source/RpmNextGen/Public/Api/Assets/Models/AssetListResponse.h create mode 100644 Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h create mode 100644 Source/RpmNextGen/Public/Api/Auth/ApiRequest.h create mode 100644 Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h create mode 100644 Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenRequest.h create mode 100644 Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenResponse.h create mode 100644 Source/RpmNextGen/Public/Api/Characters/CharacterApi.h create mode 100644 Source/RpmNextGen/Public/Api/Characters/Models/CharacterCreateRequest.h create mode 100644 Source/RpmNextGen/Public/Api/Characters/Models/CharacterCreateResponse.h create mode 100644 Source/RpmNextGen/Public/Api/Characters/Models/CharacterFindByIdRequest.h create mode 100644 Source/RpmNextGen/Public/Api/Characters/Models/CharacterFindByIdResponse.h create mode 100644 Source/RpmNextGen/Public/Api/Characters/Models/CharacterPreviewRequest.h create mode 100644 Source/RpmNextGen/Public/Api/Characters/Models/CharacterUpdateRequest.h create mode 100644 Source/RpmNextGen/Public/Api/Characters/Models/CharacterUpdateResponse.h create mode 100644 Source/RpmNextGen/Public/Api/Characters/Models/RpmCharacter.h create mode 100644 Source/RpmNextGen/Public/Api/Common/Models/ApiResponse.h create mode 100644 Source/RpmNextGen/Public/Api/Common/WebApi.h create mode 100644 Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h create mode 100644 Source/RpmNextGen/Public/RpmActor.h create mode 100644 Source/RpmNextGen/Public/RpmNextGen.h create mode 100644 Source/RpmNextGen/Public/Samples/AssetButtonWidget.h create mode 100644 Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h create mode 100644 Source/RpmNextGen/Public/Settings/RpmSettings.h create mode 100644 Source/RpmNextGen/Public/Settings/RpmSettingsData.h create mode 100644 Source/RpmNextGen/RpmNextGen.Build.cs create mode 100644 Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp create mode 100644 Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp create mode 100644 Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp create mode 100644 Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp create mode 100644 Source/RpmNextGenEditor/Private/EditorCache.cpp create mode 100644 Source/RpmNextGenEditor/Private/LoginWindowCommands.cpp create mode 100644 Source/RpmNextGenEditor/Private/LoginWindowStyle.cpp create mode 100644 Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp create mode 100644 Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp create mode 100644 Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h create mode 100644 Source/RpmNextGenEditor/Public/Auth/DeveloperAccountApi.h create mode 100644 Source/RpmNextGenEditor/Public/Auth/DeveloperLoginRequest.h create mode 100644 Source/RpmNextGenEditor/Public/Auth/DeveloperLoginResponse.h create mode 100644 Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h create mode 100644 Source/RpmNextGenEditor/Public/EditorAssetLoader.h create mode 100644 Source/RpmNextGenEditor/Public/EditorCache.h create mode 100644 Source/RpmNextGenEditor/Public/LoginWindowCommands.h create mode 100644 Source/RpmNextGenEditor/Public/LoginWindowStyle.h create mode 100644 Source/RpmNextGenEditor/Public/RpmNextGenEditor.h create mode 100644 Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h create mode 100644 Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs diff --git a/Resources/Icon128.png b/Resources/Icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..1231d4aad4d0d462fb7b178eb5b30aa61a10df0b GIT binary patch literal 12699 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 a;7#~yJh_ha>A&A^fRdb=Y>l)<=>Gxy=2LS3 literal 0 HcmV?d00001 diff --git a/Resources/PlaceholderButtonIcon.svg b/Resources/PlaceholderButtonIcon.svg new file mode 100644 index 0000000..7302447 --- /dev/null +++ b/Resources/PlaceholderButtonIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/RpmNextGen.uplugin b/RpmNextGen.uplugin new file mode 100644 index 0000000..d336f5b --- /dev/null +++ b/RpmNextGen.uplugin @@ -0,0 +1,41 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "RpmNextGen", + "Description": "", + "Category": "Other", + "CreatedBy": "Ready Player Me", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "RpmNextGen", + "Type": "Runtime", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64", + "Mac", + "Linux", + "Android", + "IOS" + ] + }, + { + "Name": "RpmNextGenEditor", + "Type": "Editor", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64", + "Mac", + "Linux" + ] + } + ] +} \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp new file mode 100644 index 0000000..c375dc9 --- /dev/null +++ b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp @@ -0,0 +1,46 @@ +#include "Api/Assets/AssetApi.h" +#include "RpmSettings.h" +#include "Api/Assets/Models/AssetListRequest.h" +#include "Api/Assets/Models/AssetListResponse.h" + +FAssetApi::FAssetApi() +{ + URpmSettings* Settings = GetMutableDefault(); + ApiBaseUrl = Settings->ApiBaseUrl; + OnApiResponse.BindRaw(this, &FAssetApi::HandleListAssetResponse); +} + + +void FAssetApi::ListAssetsAsync(const FAssetListRequest& Request) +{ + FString QueryString = Request.BuildQueryString(); + const FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets%s"), *ApiBaseUrl, *QueryString); + FApiRequest ApiRequest = FApiRequest(); + ApiRequest.Url = Url; + ApiRequest.Method = GET; + UE_LOG(LogTemp, Warning, TEXT("Try request from url %s"), *Url); + + DispatchRawWithAuth(ApiRequest); +} + +void FAssetApi::HandleListAssetResponse(FString Response, bool bWasSuccessful) +{ + if(bWasSuccessful) + { + FAssetListResponse AssetListResponse = FAssetListResponse(); + if(FJsonObjectConverter::JsonObjectStringToUStruct(Response, &AssetListResponse, 0, 0)) + { + OnListAssetsResponse.ExecuteIfBound(AssetListResponse, true); + return; + } + } + else + { + UE_LOG(LogTemp, Warning, TEXT("API Response was unsuccessful or failed to parse")); + } + OnListAssetsResponse.ExecuteIfBound(FAssetListResponse(), false); +} + +void FAssetApi::HandleListAssetTypeResponse(FString Response, bool bWasSuccessful) +{ +} diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp new file mode 100644 index 0000000..dd84148 --- /dev/null +++ b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp @@ -0,0 +1,76 @@ +#include "Api/Assets/AssetLoader.h" +#include "HttpModule.h" +#include "glTFRuntime/Public/glTFRuntimeFunctionLibrary.h" +#include "Engine/World.h" +#include "Interfaces/IHttpResponse.h" +#include "Misc/FileHelper.h" +#include "HAL/PlatformFilemanager.h" +#include "Engine/World.h" +#include "Engine/Engine.h" +#include "GameFramework/Actor.h" + +FAssetLoader::FAssetLoader() +{ + GltfConfig = new FglTFRuntimeConfig(); + GltfConfig->TransformBaseType = EglTFRuntimeTransformBaseType::YForward; + + // Set the download directory within the project + DownloadDirectory = FPaths::ProjectContentDir() / TEXT("ReadyPlayerMe/"); + // Ensure the directory exists + IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); + if (!PlatformFile.DirectoryExists(*DownloadDirectory)) + { + PlatformFile.CreateDirectory(*DownloadDirectory); + } +} + +FAssetLoader::FAssetLoader(FglTFRuntimeConfig* Config) : GltfConfig(Config) +{ +} + +FAssetLoader::~FAssetLoader() +{ +} + +void FAssetLoader::LoadGLBFromURL(const FString& URL) +{ + TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->OnProcessRequestComplete().BindRaw(this, &FAssetLoader::OnDownloadComplete); + HttpRequest->SetURL(URL); + HttpRequest->SetVerb(TEXT("GET")); + HttpRequest->ProcessRequest(); +} + +void FAssetLoader::OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) +{ + + if (bWasSuccessful && Response.IsValid()) + { + TArray Content = Response->GetContent(); + OnAssetDataReceived.ExecuteIfBound(true, Content); + + FString FileName = FPaths::GetCleanFilename(Request->GetURL()); + FString FilePath = DownloadDirectory / FileName; + + // Write the file to disk + if (FFileHelper::SaveArrayToFile(Response->GetContent(), *FilePath)) + { + UE_LOG(LogTemp, Log, TEXT("Downloaded GLB file to %s"), *FilePath); + UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(Content, *GltfConfig); + OnAssetDownloaded.ExecuteIfBound(true, FilePath, gltfAsset); + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to save GLB file to %s"), *FilePath); + } + + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to download GLB file from URL")); + } + OnAssetDataReceived.ExecuteIfBound(false, TArray()); + OnAssetDownloaded.ExecuteIfBound(false, FString(), nullptr); +} + + diff --git a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp new file mode 100644 index 0000000..ba0e57f --- /dev/null +++ b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp @@ -0,0 +1,30 @@ +#include "Api/Auth/ApiKeyAuthStrategy.h" +#include "RpmSettings.h" + +class URpmSettings; + +FApiKeyAuthStrategy::FApiKeyAuthStrategy() +{ +} + +void FApiKeyAuthStrategy::AddAuthToRequest(FApiRequest& Request) +{ + URpmSettings *Settings = GetMutableDefault(); + Request.Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); + UE_LOG(LogTemp, Log, TEXT("Added API key to request %s"), *Settings->ApiKey); + UE_LOG(LogTemp, Log, TEXT("Is OnAuthComplete bound = %d"), OnAuthComplete.IsBound()); + + OnAuthComplete.ExecuteIfBound(true, false); +} + +void FApiKeyAuthStrategy::OnRefreshTokenResponse(FString Response, bool bWasSuccessful) +{ +} + +void FApiKeyAuthStrategy::TryRefresh(FApiRequest& Request) +{ + UE_LOG(LogTemp, Log, TEXT("Trying refresh")); +} + + + diff --git a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp new file mode 100644 index 0000000..d700d3c --- /dev/null +++ b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp @@ -0,0 +1,133 @@ +#include "Api/Characters/CharacterApi.h" +#include "HttpModule.h" +#include "RpmSettings.h" +#include "Api/Characters/Models/CharacterFindByIdRequest.h" +#include "Api/Characters/Models/CharacterPreviewRequest.h" +#include "Api/Characters/Models/CharacterUpdateRequest.h" +#include "GenericPlatform/GenericPlatformHttp.h" +#include "Interfaces/IHttpResponse.h" + +FCharacterApi::FCharacterApi() +{ + URpmSettings *Settings = GetMutableDefault(); + BaseUrl = FString::Printf(TEXT("%s/v1/characters"), *Settings->ApiBaseUrl) ; + Http = &FHttpModule::Get(); +} + +FCharacterApi::~FCharacterApi() +{ +} + +void FCharacterApi::CreateAsync(const FCharacterCreateRequest& Request) +{ + FApiRequest ApiRequest; + ApiRequest.Url = FString::Printf(TEXT("%s"), *BaseUrl); + ApiRequest.Method = POST; + ApiRequest.Payload = ConvertToJsonString(Request); + DispatchRaw(ApiRequest, Create); +} + +void FCharacterApi::UpdateAsync(const FCharacterUpdateRequest& Request) +{ + FApiRequest ApiRequest; + ApiRequest.Url = FString::Printf(TEXT("%s/%s"), *BaseUrl, *Request.Id ); + ApiRequest.Method = PATCH; + ApiRequest.Payload = ConvertToJsonString(Request); + DispatchRaw(ApiRequest, Update); +} + +void FCharacterApi::FindByIdAsync(const FCharacterFindByIdRequest& Request) +{ + FApiRequest ApiRequest; + ApiRequest.Url = FString::Printf(TEXT("%s/%s"), *BaseUrl, *Request.Id ); + ApiRequest.Method = GET; + DispatchRaw(ApiRequest, FindById); +} + +FString FCharacterApi::GeneratePreviewUrl(const FCharacterPreviewRequest& Request) +{ + FString QueryString = BuildQueryString(Request.Params.Assets); + FString url = FString::Printf(TEXT("%s/%s/preview%s"), *BaseUrl, *Request.Id, *QueryString); + return url; +} + +void FCharacterApi::DispatchRaw(const FApiRequest& Data, const ECharacterResponseType& ResponseType) +{ + TSharedRef Request = Http->CreateRequest(); + Request->SetURL(Data.Url); + Request->SetVerb(Data.GetVerb()); + Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + + //TODO move to auth strategy + URpmSettings *Settings = GetMutableDefault(); + Request->SetHeader(TEXT("X-API-KEY"), Settings->ApiKey); + UE_LOG(LogTemp, Warning, TEXT("Making request to Url: %s | Verb: %s with data %s"), *Data.Url, *Data.GetVerb(), *Data.Payload); + for (const auto& Header : Data.Headers) + { + if(!Request->GetHeader(Header.Key).IsEmpty()) + continue; + Request->SetHeader(Header.Key, Header.Value); + + } + + if (!Data.Payload.IsEmpty()) + { + Request->SetContentAsString(Data.Payload); + } + Request->OnProcessRequestComplete().BindLambda([this, ResponseType](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) + { + UE_LOG(LogTemp, Warning, TEXT("RunCallback")) + OnProcessResponse(Request, Response, bWasSuccessful, ResponseType); + }); + Request->ProcessRequest(); +} + +void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, const ECharacterResponseType& ResponseType) +{ + bool success = bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()); + FCharacterCreateResponse CharacterCreateResponse; + FCharacterUpdateResponse CharacterUpdateResponse; + FCharacterFindByIdResponse CharacterFindByIdResponse; + UE_LOG(LogTemp, Warning, TEXT("Character API Response: %s. Response Code: %d"), *Response->GetContentAsString(), Response->GetResponseCode()); + + switch (ResponseType) + { + case Create: + success = success && FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &CharacterCreateResponse, 0, 0); + OnCharacterCreateResponse.ExecuteIfBound(CharacterCreateResponse, success); + break; + case Update: + success = success && FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &CharacterUpdateResponse, 0, 0); + OnCharacterUpdateResponse.ExecuteIfBound(CharacterUpdateResponse, success); + break; + case FindById: + success = success && FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &CharacterFindByIdResponse, 0, 0); + OnCharacterFindResponse.ExecuteIfBound(CharacterFindByIdResponse, success); + break; + + } +// if (bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())) +// { +// UE_LOG(LogTemp, Warning, TEXT("WebApi Response Success: %s. Response Code: %d"), *Response->GetContentAsString(), Response->GetResponseCode()); +// OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), true); +// return; +// } +// FString ErrorMessage = Response.IsValid() ? Response->GetContentAsString() : TEXT("Request failed"); +// UE_LOG(LogTemp, Warning, TEXT("WebApi request failed: %s"), *ErrorMessage); +// OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), false); +} + +FString FCharacterApi::BuildQueryString(const TMap& QueryParams) +{ + FString QueryString; + if (QueryParams.Num() > 0) + { + QueryString.Append(TEXT("?")); + for (const auto& Param : QueryParams) + { + QueryString.Append(FString::Printf(TEXT("%s=%s&"), *FGenericPlatformHttp::UrlEncode(Param.Key), *FGenericPlatformHttp::UrlEncode(Param.Value))); + } + QueryString.RemoveFromEnd(TEXT("&")); + } + return QueryString; +} diff --git a/Source/RpmNextGen/Private/Api/Common/WebApi.cpp b/Source/RpmNextGen/Private/Api/Common/WebApi.cpp new file mode 100644 index 0000000..680e548 --- /dev/null +++ b/Source/RpmNextGen/Private/Api/Common/WebApi.cpp @@ -0,0 +1,69 @@ +#include "Api/Common/WebApi.h" +#include "HttpModule.h" +#include "GenericPlatform/GenericPlatformHttp.h" +#include "Interfaces/IHttpResponse.h" + +FWebApi::FWebApi() +{ + Http = &FHttpModule::Get(); +} + +FWebApi::~FWebApi() {} + +// template +// void FWebApi::Dispatch(const FApiRequest& Data) +// { +// TFApiRequestBody payload = TFApiRequestBody(Data.Payload); +// FString PayloadString = ConvertToJsonString(payload.Data); +// DispatchRaw(FApiRequest{Data.Url, Data.Method, Data.Headers, PayloadString}); +// } + +void FWebApi::DispatchRaw(const FApiRequest& Data) +{ + TSharedRef Request = Http->CreateRequest(); + Request->SetURL(Data.Url); + Request->SetVerb(Data.GetVerb()); + + for (const auto& Header : Data.Headers) + { + Request->SetHeader(Header.Key, Header.Value); + //UE_LOG(LogTemp, Log, TEXT("Header Key: %s | Value: %s"), *Header.Key, *Header.Value); + } + + if (!Data.Payload.IsEmpty()) + { + Request->SetContentAsString(Data.Payload); + } + + Request->OnProcessRequestComplete().BindRaw(this, &FWebApi::OnProcessResponse); + Request->ProcessRequest(); +} + +void FWebApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) +{ + + if (bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())) + { + UE_LOG(LogTemp, Warning, TEXT("WebApi from URL %s Response Success: %s."), *Request->GetURL(), *Response->GetContentAsString()); + OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), true); + return; + } + FString ErrorMessage = Response.IsValid() ? Response->GetContentAsString() : TEXT("Request failed"); + UE_LOG(LogTemp, Warning, TEXT("WebApi from URL %s request failed: %s"), *Request->GetURL(), *ErrorMessage); + OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), false); +} + +FString FWebApi::BuildQueryString(const TMap& QueryParams) +{ + FString QueryString; + if (QueryParams.Num() > 0) + { + QueryString.Append(TEXT("?")); + for (const auto& Param : QueryParams) + { + QueryString.Append(FString::Printf(TEXT("%s=%s&"), *FGenericPlatformHttp::UrlEncode(Param.Key), *FGenericPlatformHttp::UrlEncode(Param.Value))); + } + QueryString.RemoveFromEnd(TEXT("&")); + } + return QueryString; +} diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp new file mode 100644 index 0000000..d207bde --- /dev/null +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -0,0 +1,63 @@ +#include "Api/Common/WebApiWithAuth.h" + +FWebApiWithAuth::FWebApiWithAuth() : AuthenticationStrategy(nullptr), ApiRequestData(nullptr) +{ + FWebApi(); +} + +FWebApiWithAuth::FWebApiWithAuth(IAuthenticationStrategy* InAuthenticationStrategy) : AuthenticationStrategy(nullptr) +{ + SetAuthenticationStrategy(InAuthenticationStrategy); +} + +void FWebApiWithAuth::SetAuthenticationStrategy(IAuthenticationStrategy* InAuthenticationStrategy) +{ + AuthenticationStrategy = InAuthenticationStrategy; + UE_LOG( LogTemp, Log, TEXT("About to bind = %d"), AuthenticationStrategy->OnAuthComplete.IsBound()); + + AuthenticationStrategy->OnAuthComplete.BindRaw(this, &FWebApiWithAuth::OnAuthComplete); + UE_LOG( LogTemp, Log, TEXT("Set auth strategy. OnAuthComplete Isbound = %d"), AuthenticationStrategy->OnAuthComplete.IsBound()); +} + +void FWebApiWithAuth::OnAuthComplete(bool bWasSuccessful, bool bWasRefreshed) +{ + if(bWasSuccessful) + { + UE_LOG(LogTemp, Log, TEXT("Dispatching raw data")); + DispatchRaw(*ApiRequestData); + return; + } + if(!bWasRefreshed) + { + UE_LOG(LogTemp, Log, TEXT("Auth failed, trying to refresh")); + AuthenticationStrategy->TryRefresh(*ApiRequestData); + return; + } + UE_LOG(LogTemp, Log, TEXT("Auth failed")); + OnApiResponse.ExecuteIfBound(TEXT("Auth failed"), false); +} + +// template +// void FWebApiWithAuth::DispatchWithAuth( +// const TFApiRequest& Data +// ) +// { +// TFApiRequestBody payload = TFApiRequestBody(Data.Payload); +// FString PayloadString = ConvertToJsonString(payload.Data); +// DispatchRawWithAuth(TFApiRequest{Data.Url, Data.Method, Data.Headers, PayloadString}); +// } + + +void FWebApiWithAuth::DispatchRawWithAuth( + FApiRequest& Data +) +{ + if (AuthenticationStrategy == nullptr) + { + UE_LOG(LogTemp, Log, TEXT("Auth strategy is null")); + DispatchRaw(Data); + return; + } + this->ApiRequestData = &Data; + AuthenticationStrategy->AddAuthToRequest(Data); +} diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp new file mode 100644 index 0000000..6540ff6 --- /dev/null +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -0,0 +1,141 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "RpmActor.h" +#include "glTFRuntimeAsset.h" +#include "glTFRuntimeFunctionLibrary.h" +#include "HttpModule.h" +#include "RpmSettings.h" +#include "Api/Characters/CharacterApi.h" +#include "Api/Characters/Models/RpmCharacter.h" +#include "Interfaces/IHttpResponse.h" + +class URpmSettings; +class IHttpRequest; + +ARpmActor::ARpmActor() +{ + // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. + PrimaryActorTick.bCanEverTick = true; + RpmSettings = GetMutableDefault(); + CharacterApi = MakeShared(); + CharacterApi->OnCharacterCreateResponse.BindUObject(this, &ARpmActor::HandleCharacterCreateResponse); + CharacterApi->OnCharacterUpdateResponse.BindUObject(this, &ARpmActor::HandleCharacterUpdateResponse); + CharacterApi->OnCharacterFindResponse.BindUObject(this, &ARpmActor::HandleCharacterFindResponse); + + AssetRoot = CreateDefaultSubobject(TEXT("AssetRoot")); + RootComponent = AssetRoot; + BaseSkeletalMeshComponent = CreateDefaultSubobject(TEXT("BaseSkeletalMesh")); + BaseSkeletalMeshComponent->SetupAttachment(AssetRoot); + AddedMeshComponents = TArray(); + PreviewAssetMap = TMap(); + OnSkeletalMeshCallback.BindDynamic(this, &ARpmActor::HandleSkeletalMeshLoaded); +} + +void ARpmActor::HandleCharacterCreateResponse(FCharacterCreateResponse CharacterCreateResponse, bool bWasSuccessful) +{ + UE_LOG(LogTemp, Warning, TEXT("HandleCharacterCreateResponse")); + Character = CharacterCreateResponse.Data; + LoadCharacter(Character); +} + +void ARpmActor::HandleCharacterUpdateResponse(FCharacterUpdateResponse CharacterUpdateResponse, bool bWasSuccessful) +{ + UE_LOG(LogTemp, Warning, TEXT("HandleCharacterUpdateResponse")); + Character = CharacterUpdateResponse.Data; +} + +void ARpmActor::HandleCharacterFindResponse(FCharacterFindByIdResponse CharacterFindByIdResponse, bool bWasSuccessful) +{ + UE_LOG(LogTemp, Warning, TEXT("HandleCharacterFindResponse")); + Character = CharacterFindByIdResponse.Data; +} + +void ARpmActor::CreateCharacter(FString AppId) +{ + //TODO update to fetch first app id + FCharacterCreateRequest CharacterCreateRequest = FCharacterCreateRequest(); + CharacterCreateRequest.Data.Assets = TMap(); + CharacterCreateRequest.Data.Assets.Add("baseModel", "663a61055134180105bcdf0d"); + CharacterCreateRequest.Data.ApplicationId = AppId; + + CharacterApi->CreateAsync(CharacterCreateRequest); +} + +USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* SkeletalMesh, const FString& Name) +{ + USkeletalMeshComponent* NewSkeletalMeshComponent = NewObject(this, *Name); + NewSkeletalMeshComponent->SetupAttachment(RootComponent); + NewSkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); + NewSkeletalMeshComponent->SkeletalMesh->SetSkeleton(BaseSkeletalMeshComponent->SkeletalMesh->GetSkeleton()); + NewSkeletalMeshComponent->RegisterComponent(); + AddInstanceComponent(NewSkeletalMeshComponent); + AddedMeshComponents.Add(NewSkeletalMeshComponent); + return NewSkeletalMeshComponent; +} + +void ARpmActor::LoadglTFAsset(UglTFRuntimeAsset* Asset, const FString& AssetName) +{ + + FglTFRuntimeSkeletalMeshConfig* SkeletalMeshConfig = new FglTFRuntimeSkeletalMeshConfig(); + Asset->LoadSkeletalMeshRecursiveAsync("", {}, OnSkeletalMeshCallback, *SkeletalMeshConfig ); +} + +void ARpmActor::HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh) +{ + //hide starter mesh + BaseSkeletalMeshComponent->SetVisibility(false); + CreateSkeletalMeshComponent(SkeletalMesh, "Asset"); +} + +void ARpmActor::OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedPtr HttpResponse, bool bIsSuccessful) +{ + if (bIsSuccessful) + { + UE_LOG(LogTemp, Warning, TEXT("Request success from url %s "), *HttpRequest->GetURL()); + FglTFRuntimeConfig Config; + Config.TransformBaseType = EglTFRuntimeTransformBaseType::YForward; + UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(HttpResponse->GetContent(),Config); + LoadglTFAsset(gltfAsset, "Asset"); + } + else + { + // Handle error + } +} + +void ARpmActor::LoadAsset(FAsset AssetData) +{ + if(Character.Id.IsEmpty()) + { + UE_LOG(LogTemp, Warning, TEXT("Character Id is empty")); + return; + } + + PreviewAssetMap.Add(AssetData.Type, AssetData.Id); + FCharacterPreviewRequest PreviewRequest; + PreviewRequest.Id = Character.Id; + PreviewRequest.Params.Assets = PreviewAssetMap; + for (auto PreviewAsset : PreviewAssetMap) + { + UE_LOG(LogTemp, Warning, TEXT("PreviewAssetMap: %s | Value %s"), *PreviewAsset.Key, *PreviewAsset.Value); + } + const FString& Url = CharacterApi->GeneratePreviewUrl(PreviewRequest); + LoadCharacterUrl(Url); +} + +void ARpmActor::LoadCharacterUrl(const FString Url) +{ + TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + UE_LOG(LogTemp, Warning, TEXT("Requesting from url %s"), *Url); + + HttpRequest->SetURL(Url); + HttpRequest->SetVerb("GET"); + HttpRequest->SetHeader("Content-Type", "application/json"); + HttpRequest->OnProcessRequestComplete().BindUObject(this, &ARpmActor::OnAssetDataLoaded); + HttpRequest->ProcessRequest(); +} + +void ARpmActor::LoadCharacter(FRpmCharacter CharacterData) +{ + LoadCharacterUrl(CharacterData.GlbUrl); +} diff --git a/Source/RpmNextGen/Private/RpmNextGen.cpp b/Source/RpmNextGen/Private/RpmNextGen.cpp new file mode 100644 index 0000000..5f3e828 --- /dev/null +++ b/Source/RpmNextGen/Private/RpmNextGen.cpp @@ -0,0 +1,20 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "RpmNextGen.h" + +#define LOCTEXT_NAMESPACE "FRpmNextGenModule" + +void FRpmNextGenModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module +} + +void FRpmNextGenModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FRpmNextGenModule, RpmNextGen) \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp new file mode 100644 index 0000000..4167b09 --- /dev/null +++ b/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp @@ -0,0 +1,112 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "AssetButtonWidget.h" +#include "HttpModule.h" +#include "IImageWrapper.h" +#include "IImageWrapperModule.h" +#include "Async/Async.h" +#include "Components/Button.h" +#include "Components/Image.h" +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" + +void UAssetButtonWidget::NativeConstruct() +{ + Super::NativeConstruct(); + if (AssetButton) + { + AssetButton->OnClicked.AddDynamic(this, &UAssetButtonWidget::HandleButtonClicked); + } +} + +void UAssetButtonWidget::InitializeButton(const FAsset& InAssetData, const FVector2D& InImageSize) +{ + AssetData = InAssetData; + + if (AssetImage) + { + // Set the image size + AssetImage->SetDesiredSizeOverride(InImageSize); + + // Set the image from URL + SetImageFromURL(AssetImage, AssetData.IconUrl); + } +} + +void UAssetButtonWidget::HandleButtonClicked() +{ + OnAssetButtonClicked.Broadcast(AssetData); +} + +void UAssetButtonWidget::SetImageFromURL(UImage* Image, const FString& URL) +{ + if (!Image) + { + UE_LOG(LogTemp, Error, TEXT("SetImageFromURL: Image is null")); + return; + } + + TWeakObjectPtr WeakImagePtr(Image); + + TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->OnProcessRequestComplete().BindUObject(this, &UAssetButtonWidget::OnImageDownloaded, WeakImagePtr); + HttpRequest->SetURL(URL); + HttpRequest->SetVerb("GET"); + HttpRequest->ProcessRequest(); +} + +void UAssetButtonWidget::OnImageDownloaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TWeakObjectPtr Image) +{ + if (!Image.IsValid()) + { + UE_LOG(LogTemp, Error, TEXT("OnImageDownloaded: Image is null or no longer valid")); + return; + } + + if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) + { + TArray ImageData = Response->GetContent(); + IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); + EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); + if (ImageFormat != EImageFormat::Invalid) + { + TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); + + if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) + { + TArray UncompressedBGRA; + if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) + { + UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); + if (Texture) + { + void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); + Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); + + Texture->UpdateResource(); + + // Set the brush from the texture + FSlateBrush Brush; + Brush.SetResourceObject(Texture); + Brush.ImageSize = Image->Brush.ImageSize; // Use the same size as the Image + AsyncTask(ENamedThreads::GameThread, [Image, Brush]() { + if (Image.IsValid()) + { + Image->SetBrush(Brush); + } + }); + } + } + } + } + else + { + UE_LOG(LogTemp, Error, TEXT("Unsupported image format: %s"), *Request->GetURL()); + } + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to download image from URL: %s"), *Request->GetURL()); + } +} \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp new file mode 100644 index 0000000..5c4e440 --- /dev/null +++ b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp @@ -0,0 +1,160 @@ +#include "CharacterCustomizationWidget.h" +#include "AssetButtonWidget.h" +#include "HttpModule.h" +#include "RpmSettings.h" +#include "Components/HorizontalBox.h" +#include "Components/Image.h" +#include "Api/Assets/Models/Asset.h" +#include "Api/Assets/Models/AssetListRequest.h" +#include "Api/Assets/Models/AssetListResponse.h" +#include "Api/Characters/CharacterApi.h" +#include "Interfaces/IHttpResponse.h" + +void UCharacterCustomizationWidget::NativeConstruct() +{ + Super::NativeConstruct(); + URpmSettings* Settings = GetMutableDefault(); + ApplicationID = Settings->ApplicationId; + InitializeCustomizationOptions(); +} + +void UCharacterCustomizationWidget::InitializeCustomizationOptions() +{ + UE_LOG(LogTemp, Warning, TEXT("Initialize customization options called. Application ID: %s"), *ApplicationID); + + URpmSettings* Settings = GetMutableDefault(); + FString ApiBaseUrl = Settings->ApiBaseUrl; + + UE_LOG(LogTemp, Warning, TEXT("Asset API is valid")); + + // Setup request and parameters + FAssetListRequest Request = FAssetListRequest(); + FAssetListQueryParams Params; + Params.ApplicationId = ApplicationID; + Params.ExcludeTypes = "baseModel"; + Request.Params = Params; + FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets%s"), *ApiBaseUrl, *Request.BuildQueryString()); + + TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->SetURL(Url); + HttpRequest->SetVerb("GET"); + HttpRequest->SetHeader("Content-Type", "application/json"); + HttpRequest->SetHeader("X-API-KEY", Settings->ApiKey); + HttpRequest->OnProcessRequestComplete().BindLambda([this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) + { + if (bWasSuccessful) + { + // Parse the response to fill Assets + FAssetListResponse AssetListResponse = FAssetListResponse(); + if (FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &AssetListResponse, 0, 0)) + { + OnAssetsFetched(AssetListResponse, true); + return; + } + } + else + { + // Handle error + } + OnAssetsFetched(FAssetListResponse(), false); // Call with empty response on failure + }); + HttpRequest->ProcessRequest(); + + UE_LOG(LogTemp, Warning, TEXT("Made request")); +} + +void UCharacterCustomizationWidget::OnAssetsFetched(const FAssetListResponse& AssetListResponse, bool bWasSuccessful) +{ + UE_LOG(LogTemp, Warning, TEXT("OnAssetsFetched called. Success: %s"), bWasSuccessful ? TEXT("true") : TEXT("false")); + + if (AssetListResponse.Data.Num() > 0) + { + UE_LOG(LogTemp, Warning, TEXT("Number of assets fetched: %d"), AssetListResponse.Data.Num()); + AssetDataArray = AssetListResponse.Data; + } + else + { + UE_LOG(LogTemp, Warning, TEXT("No assets fetched")); + } + // Broadcast the delegate to notify Blueprints + OnAssetsFetchedDelegate.Broadcast(bWasSuccessful, AssetDataArray); +} + +void UCharacterCustomizationWidget::PopulateBoxWithFilter(UPanelWidget* ParentBox, const FString& AssetType) +{ + if (!ParentBox) + { + UE_LOG(LogTemp, Error, TEXT("ParentBox is null")); + return; + } + + for (const FAsset& AssetData : AssetDataArray) + { + if (AssetData.Type == AssetType) + { + CreateAssetWidget(AssetData, ParentBox); + } + } +} + +void UCharacterCustomizationWidget::PopulateBox(UPanelWidget* ParentBox) +{ + if (!ParentBox) + { + UE_LOG(LogTemp, Error, TEXT("ParentBox is null")); + return; + } + + for (const FAsset& AssetData : AssetDataArray) + { + CreateAssetWidget(AssetData, ParentBox); + } +} + +void UCharacterCustomizationWidget::ClearBox(UPanelWidget* ParentBox) +{ + if (!ParentBox) + { + UE_LOG(LogTemp, Error, TEXT("ParentBox is null")); + return; + } + + ParentBox->ClearChildren(); +} + +void UCharacterCustomizationWidget::UpdateCustomizationOptions(UPanelWidget* ParentBox) +{ + ClearBox(ParentBox); + InitializeCustomizationOptions(); + PopulateBox(ParentBox); +} + +void UCharacterCustomizationWidget::CreateAssetWidget(const FAsset& AssetData, UPanelWidget* ParentBox) +{ + if (!ParentBox) + { + UE_LOG(LogTemp, Error, TEXT("ParentBox is null")); + return; + } + + if (!AssetButtonWidgetClass) + { + UE_LOG(LogTemp, Error, TEXT("AssetButtonWidgetClass is null")); + return; + } + + UAssetButtonWidget* AssetButton = CreateWidget(this, AssetButtonWidgetClass); + if (AssetButton) + { + AssetButton->InitializeButton(AssetData, ImageSize); + AssetButton->OnAssetButtonClicked.AddDynamic(this, &UCharacterCustomizationWidget::OnAssetButtonClicked); + ParentBox->AddChild(AssetButton); + } +} + +void UCharacterCustomizationWidget::OnAssetButtonClicked(const FAsset& AssetData) +{ + FString GlbUrl = AssetData.GlbUrl; + UE_LOG(LogTemp, Log, TEXT("Asset Button Clicked: %s"), *GlbUrl); + OnAssetButtonSelected.Broadcast(AssetData); +} diff --git a/Source/RpmNextGen/Private/Settings/RpmSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmSettings.cpp new file mode 100644 index 0000000..e6e389a --- /dev/null +++ b/Source/RpmNextGen/Private/Settings/RpmSettings.cpp @@ -0,0 +1,16 @@ +#include "RpmSettings.h" + +URpmSettingsData* RpmSettings::SettingsInstance = nullptr; + +URpmSettingsData* RpmSettings::GetRpmSettings() +{ + // Check if the settings instance is already initialized + if (!SettingsInstance) + { + // Initialize the settings instance if it is not already + SettingsInstance = GetMutableDefault(); + } + + // Return the settings instance + return SettingsInstance; +} \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Settings/RpmSettingsData.cpp b/Source/RpmNextGen/Private/Settings/RpmSettingsData.cpp new file mode 100644 index 0000000..a2a1405 --- /dev/null +++ b/Source/RpmNextGen/Private/Settings/RpmSettingsData.cpp @@ -0,0 +1,13 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "RpmSettingsData.h" + +URpmSettingsData::URpmSettingsData() +{ + ApiBaseUrl = TEXT("https://api.readyplayer.me"); + ApiBaseAuthUrl = TEXT("https://readyplayer.me/api/auth"); + ApplicationId = TEXT(""); + ApiKey = TEXT(""); + ApiProxyUrl = TEXT(""); +} diff --git a/Source/RpmNextGen/Public/Api/Assets/AssetApi.h b/Source/RpmNextGen/Public/Api/Assets/AssetApi.h new file mode 100644 index 0000000..a8d7548 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Assets/AssetApi.h @@ -0,0 +1,22 @@ +#pragma once +#include "Api/Common/WebApiWithAuth.h" + +struct FAssetListRequest; +struct FAssetListResponse; + +DECLARE_DELEGATE_TwoParams(FOnListAssetsResponse, const FAssetListResponse&, bool); +DECLARE_DELEGATE_TwoParams(FOnListAssetTypeResponse, const FAssetListResponse&, bool); + +class RPMNEXTGEN_API FAssetApi : public FWebApiWithAuth +{ +public: + FAssetApi(); + void ListAssetsAsync(const FAssetListRequest& Request); + FOnListAssetsResponse OnListAssetsResponse; + FOnListAssetTypeResponse OnListAssetTypeResponse; +private: + void HandleListAssetResponse(FString Response, bool bWasSuccessful); + void HandleListAssetTypeResponse(FString Response, bool bWasSuccessful); + + FString ApiBaseUrl; +}; diff --git a/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h new file mode 100644 index 0000000..a862bcb --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h @@ -0,0 +1,29 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Common/WebApi.h" +#include "HAL/PlatformFilemanager.h" +#include "Interfaces/IHttpRequest.h" + +struct FglTFRuntimeConfig; +class UglTFRuntimeAsset; + +DECLARE_DELEGATE_TwoParams(FOnAssetDataReceived, bool, TArray); +DECLARE_DELEGATE_ThreeParams(FOnAssetDownloaded, bool, FString, UglTFRuntimeAsset*); + +class RPMNEXTGEN_API FAssetLoader : public FWebApi +{ +public: + FAssetLoader(); + FAssetLoader(FglTFRuntimeConfig* Config); + virtual ~FAssetLoader(); + + void LoadGLBFromURL(const FString& URL); + + FOnAssetDataReceived OnAssetDataReceived; + FOnAssetDownloaded OnAssetDownloaded; +protected: + FglTFRuntimeConfig* GltfConfig; + void virtual OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); + FString DownloadDirectory; +}; diff --git a/Source/RpmNextGen/Public/Api/Assets/Models/Asset.h b/Source/RpmNextGen/Public/Api/Assets/Models/Asset.h new file mode 100644 index 0000000..edffd8a --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Assets/Models/Asset.h @@ -0,0 +1,32 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Common/Models/ApiResponse.h" +#include "Asset.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FAsset : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "id")) + FString Id; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "name")) + FString Name; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "glbUrl")) + FString GlbUrl; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "iconUrl")) + FString IconUrl; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "createdAt")) + FString Type; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "createdAt")) + FDateTime CreatedAt; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "updatedAt")) + FDateTime UpdatedAt; +}; \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Api/Assets/Models/AssetListRequest.h b/Source/RpmNextGen/Public/Api/Assets/Models/AssetListRequest.h new file mode 100644 index 0000000..4f66f21 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Assets/Models/AssetListRequest.h @@ -0,0 +1,51 @@ +#pragma once + +#include "CoreMinimal.h" +#include "AssetListRequest.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FAssetListQueryParams +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "applicationId")) + FString ApplicationId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "type")) + FString Type; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "excludeTypes")) + FString ExcludeTypes; + +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FAssetListRequest +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FAssetListQueryParams Params; + + FString BuildQueryString() const; +}; + +inline FString FAssetListRequest::BuildQueryString() const +{ + if (Params.ApplicationId.IsEmpty() && Params.Type.IsEmpty() && Params.ExcludeTypes.IsEmpty()) return FString(); + FString QueryString = TEXT("?"); + if (!Params.ApplicationId.IsEmpty()) + { + QueryString += TEXT("applicationId=") + Params.ApplicationId + TEXT("&"); + } + if (!Params.Type.IsEmpty()) + { + QueryString += TEXT("type=") + Params.Type + TEXT("&"); + } + if (!Params.ExcludeTypes.IsEmpty()) + { + QueryString += TEXT("excludeTypes=") + Params.ExcludeTypes + TEXT("&"); + } + QueryString.RemoveFromEnd(TEXT("&")); + return QueryString; +} diff --git a/Source/RpmNextGen/Public/Api/Assets/Models/AssetListResponse.h b/Source/RpmNextGen/Public/Api/Assets/Models/AssetListResponse.h new file mode 100644 index 0000000..d31cd77 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Assets/Models/AssetListResponse.h @@ -0,0 +1,17 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Assets/Models/Asset.h" +#include "AssetListResponse.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FAssetListResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "data")) + TArray Data; + bool bSuccess; + int64 Status; + FString Error; +}; diff --git a/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h b/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h new file mode 100644 index 0000000..c5c0b72 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h @@ -0,0 +1,15 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Auth/IAuthenticationStrategy.h" +#include "Api/Common/WebApi.h" + +class RPMNEXTGEN_API FApiKeyAuthStrategy : public IAuthenticationStrategy +{ +public: + FApiKeyAuthStrategy(); + virtual void AddAuthToRequest(FApiRequest& Request) override; + virtual void OnRefreshTokenResponse(FString Response, bool bWasSuccessful) override; + virtual void TryRefresh(FApiRequest& Request) override; + FOnAuthComplete OnAuthComplete; +}; diff --git a/Source/RpmNextGen/Public/Api/Auth/ApiRequest.h b/Source/RpmNextGen/Public/Api/Auth/ApiRequest.h new file mode 100644 index 0000000..45ba306 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Auth/ApiRequest.h @@ -0,0 +1,56 @@ +#pragma once + +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" +#include "ApiRequest.generated.h" + +UENUM(BlueprintType) +enum ERequestMethod { + GET, + POST, + PUT, + DELETE, + PATCH +}; + + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FApiRequest +{ + GENERATED_BODY() + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FString Url; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + TEnumAsByte Method = GET; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + TMap Headers; + + FString Payload; + + FString GetVerb() const + { + switch (Method) + { + case GET: default: + return TEXT("GET"); + case POST: + return TEXT("POST"); + case PUT: + return TEXT("PUT"); + case PATCH: + return TEXT("PATCH"); + case DELETE: + return TEXT("DELETE"); + } + } +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FApiRequestBody +{ + GENERATED_BODY() + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "data")) + FString Data; +}; \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h new file mode 100644 index 0000000..9bfe108 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h @@ -0,0 +1,16 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Auth/ApiRequest.h" + +DECLARE_DELEGATE_TwoParams(FOnAuthComplete, bool, bool); + +class RPMNEXTGEN_API IAuthenticationStrategy +{ +public: + virtual ~IAuthenticationStrategy() = default; + virtual void AddAuthToRequest(FApiRequest& Request) = 0; + virtual void TryRefresh(FApiRequest& Request) = 0; + virtual void OnRefreshTokenResponse(FString Response, bool bWasSuccessful) = 0; + FOnAuthComplete OnAuthComplete; +}; \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenRequest.h b/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenRequest.h new file mode 100644 index 0000000..4a65fb3 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenRequest.h @@ -0,0 +1,52 @@ +#pragma once + +#include "CoreMinimal.h" +#include "JsonObjectConverter.h" +#include "RefreshTokenRequest.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FRefreshTokenRequestBody +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FString Token; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FString RefreshToken; + + FString ToJsonString() const + { + FString OutputString; + FJsonObjectConverter::UStructToJsonObjectString(*this, OutputString); + return OutputString; + } + + static bool FromJsonString(const FString& JsonString, FRefreshTokenRequestBody& OutStruct) + { + return FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutStruct, 0, 0); + } +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FRefreshTokenRequest +{ + GENERATED_BODY() + + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FRefreshTokenRequestBody Payload; + + FString ToJsonString() const + { + FString OutputString; + FJsonObjectConverter::UStructToJsonObjectString(*this, OutputString); + return OutputString; + } + + static bool FromJsonString(const FString& JsonString, FRefreshTokenRequest& OutStruct) + { + return FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutStruct, 0, 0); + } +}; diff --git a/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenResponse.h b/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenResponse.h new file mode 100644 index 0000000..8a2f7b7 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenResponse.h @@ -0,0 +1,53 @@ +#pragma once + +#include "CoreMinimal.h" +#include "JsonObjectConverter.h" +#include "Api/Common/Models/ApiResponse.h" +#include "RefreshTokenResponse.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FRefreshTokenResponseBody : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me|Auth|Response") + FString Token; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me|Auth|Response") + FString RefreshToken; + + FString ToJsonString() const + { + FString OutputString; + FJsonObjectConverter::UStructToJsonObjectString(*this, OutputString); + return OutputString; + } + + static bool FromJsonString(const FString& JsonString, FRefreshTokenResponseBody& OutStruct) + { + return FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutStruct, 0, 0); + } +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FRefreshTokenResponse : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FRefreshTokenResponseBody Data; + + FString ToJsonString() const + { + FString OutputString; + FJsonObjectConverter::UStructToJsonObjectString(*this, OutputString); + return OutputString; + } + + static bool FromJsonString(const FString& JsonString, FRefreshTokenResponse& OutStruct) + { + return FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutStruct, 0, 0); + } +}; + + diff --git a/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h b/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h new file mode 100644 index 0000000..2ef6830 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h @@ -0,0 +1,63 @@ +#pragma once + +#include "CoreMinimal.h" +#include "JsonObjectConverter.h" +#include "Api/Common/WebApi.h" +#include "Models/CharacterCreateRequest.h" +#include "Models/CharacterCreateResponse.h" +#include "Models/CharacterFindByIdRequest.h" +#include "Models/CharacterFindByIdResponse.h" +#include "Models/CharacterPreviewRequest.h" +#include "Models/CharacterUpdateRequest.h" +#include "Models/CharacterUpdateResponse.h" + +UENUM(BlueprintType) +enum ECharacterResponseType { + Create, + Update, + FindById +}; + +DECLARE_DELEGATE_TwoParams(FOnCharacterCreateResponse, FCharacterCreateResponse, bool); +DECLARE_DELEGATE_TwoParams(FOnCharacterUpdatResponse, FCharacterUpdateResponse, bool); +DECLARE_DELEGATE_TwoParams(FOnCharacterFindResponse, FCharacterFindByIdResponse, bool); + +class RPMNEXTGEN_API FCharacterApi : public TSharedFromThis +{ +public: + FCharacterApi(); + virtual ~FCharacterApi(); + FOnWebApiResponse OnApiResponse; + + void CreateAsync(const FCharacterCreateRequest& Request); + void UpdateAsync(const FCharacterUpdateRequest& Request); + void FindByIdAsync(const FCharacterFindByIdRequest& Request); + FString GeneratePreviewUrl(const FCharacterPreviewRequest& Request); + + FOnCharacterCreateResponse OnCharacterCreateResponse; + FOnCharacterUpdatResponse OnCharacterUpdateResponse; + FOnCharacterFindResponse OnCharacterFindResponse; + + +protected: + void DispatchRaw(const FApiRequest& Data, const ECharacterResponseType& ResponseType); + + void OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, const ECharacterResponseType& ResponseType); + + FString BuildQueryString(const TMap& QueryParams); + + template + FString ConvertToJsonString(const T& Data); + + FHttpModule* Http; +private: + FString BaseUrl; +}; + +template +FString FCharacterApi::ConvertToJsonString(const T& Data) +{ + FString JsonString; + FJsonObjectConverter::UStructToJsonObjectString(Data, JsonString); + return JsonString; +} diff --git a/Source/RpmNextGen/Public/Api/Characters/Models/CharacterCreateRequest.h b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterCreateRequest.h new file mode 100644 index 0000000..fefa7f9 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterCreateRequest.h @@ -0,0 +1,25 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Common/Models/ApiResponse.h" +#include "CharacterCreateRequest.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FCharacterCreateRequestBody +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "applicationId")) + FString ApplicationId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "assets")) + TMap Assets; +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FCharacterCreateRequest +{ + GENERATED_BODY() + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FCharacterCreateRequestBody Data; +}; \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Api/Characters/Models/CharacterCreateResponse.h b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterCreateResponse.h new file mode 100644 index 0000000..321ca68 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterCreateResponse.h @@ -0,0 +1,15 @@ +#pragma once + +#include "CoreMinimal.h" +#include "RpmCharacter.h" +#include "Api/Common/Models/ApiResponse.h" +#include "CharacterCreateResponse.generated.h" + +USTRUCT() +struct RPMNEXTGEN_API FCharacterCreateResponse : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY( EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "data")) + FRpmCharacter Data; +}; diff --git a/Source/RpmNextGen/Public/Api/Characters/Models/CharacterFindByIdRequest.h b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterFindByIdRequest.h new file mode 100644 index 0000000..24ee0be --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterFindByIdRequest.h @@ -0,0 +1,13 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CharacterFindByIdRequest.generated.h" + +USTRUCT(BlueprintType) +struct FCharacterFindByIdRequest +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FString Id; +}; diff --git a/Source/RpmNextGen/Public/Api/Characters/Models/CharacterFindByIdResponse.h b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterFindByIdResponse.h new file mode 100644 index 0000000..aa9ed26 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterFindByIdResponse.h @@ -0,0 +1,15 @@ +#pragma once + +#include "CoreMinimal.h" +#include "RpmCharacter.h" +#include "Api/Common/Models/ApiResponse.h" +#include "CharacterFindByIdResponse.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FCharacterFindByIdResponse : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "data")) + FRpmCharacter Data; +}; diff --git a/Source/RpmNextGen/Public/Api/Characters/Models/CharacterPreviewRequest.h b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterPreviewRequest.h new file mode 100644 index 0000000..71fda5a --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterPreviewRequest.h @@ -0,0 +1,27 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CharacterPreviewRequest.generated.h" + + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FCharacterPreviewQueryParams +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "assets")) + TMap Assets; +}; + + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FCharacterPreviewRequest +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FString Id; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FCharacterPreviewQueryParams Params; +}; diff --git a/Source/RpmNextGen/Public/Api/Characters/Models/CharacterUpdateRequest.h b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterUpdateRequest.h new file mode 100644 index 0000000..c73cfea --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterUpdateRequest.h @@ -0,0 +1,26 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CharacterUpdateRequest.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FCharacterUpdateRequestBody +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "assets")) + TMap Assets; +}; + + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FCharacterUpdateRequest +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "applicationId")) + FString Id; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FCharacterUpdateRequestBody Payload; +}; diff --git a/Source/RpmNextGen/Public/Api/Characters/Models/CharacterUpdateResponse.h b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterUpdateResponse.h new file mode 100644 index 0000000..5b01792 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Characters/Models/CharacterUpdateResponse.h @@ -0,0 +1,15 @@ +#pragma once + +#include "CoreMinimal.h" +#include "RpmCharacter.h" +#include "Api/Common/Models/ApiResponse.h" +#include "CharacterUpdateResponse.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FCharacterUpdateResponse : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "data")) + FRpmCharacter Data; +}; diff --git a/Source/RpmNextGen/Public/Api/Characters/Models/RpmCharacter.h b/Source/RpmNextGen/Public/Api/Characters/Models/RpmCharacter.h new file mode 100644 index 0000000..eec8bfd --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Characters/Models/RpmCharacter.h @@ -0,0 +1,32 @@ +#pragma once + +#include "CoreMinimal.h" +#include "RpmCharacter.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FRpmCharacter +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "id")) + FString Id; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "createdByApplicationId")) + FString CreatedByApplicationId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "glbUrl")) + FString GlbUrl; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "iconUrl")) + FString IconUrl; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "assets")) + TMap Assets; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "createdAt")) + FDateTime CreatedAt; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "updatedAt")) + FDateTime UpdatedAt; +}; + \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Api/Common/Models/ApiResponse.h b/Source/RpmNextGen/Public/Api/Common/Models/ApiResponse.h new file mode 100644 index 0000000..8411e98 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Common/Models/ApiResponse.h @@ -0,0 +1,19 @@ +#pragma once + +#include "CoreMinimal.h" +#include "ApiResponse.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGEN_API FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + bool IsSuccess = true; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + int64 Status = 200; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FString Error; +}; \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Api/Common/WebApi.h b/Source/RpmNextGen/Public/Api/Common/WebApi.h new file mode 100644 index 0000000..5745cad --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Common/WebApi.h @@ -0,0 +1,46 @@ +#pragma once + +#include "CoreMinimal.h" +#include "JsonObjectConverter.h" +#include "Api/Auth/ApiRequest.h" +#include "Interfaces/IHttpRequest.h" +#include "Misc/ScopeExit.h" + +class FHttpModule; +DECLARE_DELEGATE_TwoParams(FOnWebApiResponse, FString, bool); + +class RPMNEXTGEN_API FWebApi +{ +public: + FWebApi(); + virtual ~FWebApi(); + + FOnWebApiResponse OnApiResponse; +protected: + + // template + // void Dispatch( + // const FApiRequest& Data + // ); + + void DispatchRaw( + const FApiRequest& Data + ); + + void OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); + + FString BuildQueryString(const TMap& QueryParams); + + template + FString ConvertToJsonString(const T& Data); + + FHttpModule* Http; +}; + +template +FString FWebApi::ConvertToJsonString(const T& Data) +{ + FString JsonString; + FJsonObjectConverter::UStructToJsonObjectString(Data, JsonString); + return JsonString; +} \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h new file mode 100644 index 0000000..eab8e1a --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h @@ -0,0 +1,29 @@ +#pragma once + +#include "CoreMinimal.h" +#include "WebApi.h" +#include "Api/Auth/IAuthenticationStrategy.h" + + +class RPMNEXTGEN_API FWebApiWithAuth : public FWebApi +{ +public: + FWebApiWithAuth(); + FWebApiWithAuth(IAuthenticationStrategy* InAuthenticationStrategy); + + void SetAuthenticationStrategy(IAuthenticationStrategy* InAuthenticationStrategy); + + // template + // void DispatchWithAuth( + // const TFApiRequest& Data + // ); + + void OnAuthComplete(bool bWasSuccessful, bool bWasRefreshed); + + void DispatchRawWithAuth(FApiRequest& Data); +protected: + FApiRequest* ApiRequestData; + +private: + IAuthenticationStrategy* AuthenticationStrategy; +}; diff --git a/Source/RpmNextGen/Public/RpmActor.h b/Source/RpmNextGen/Public/RpmActor.h new file mode 100644 index 0000000..e230758 --- /dev/null +++ b/Source/RpmNextGen/Public/RpmActor.h @@ -0,0 +1,74 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "glTFRuntimeParser.h" +#include "Api/Assets/Models/Asset.h" +#include "Api/Characters/Models/CharacterCreateResponse.h" +#include "Api/Characters/Models/CharacterFindByIdResponse.h" +#include "Api/Characters/Models/CharacterUpdateResponse.h" +#include "RpmActor.generated.h" + +class IHttpResponse; +class IHttpRequest; +class FCharacterApi; +struct FRpmCharacter; +class URpmSettings; +class UglTFRuntimeAsset; + +/** + * + */ +UCLASS() +class RPMNEXTGEN_API ARpmActor : public AActor +{ + GENERATED_BODY() +public: + + ARpmActor(); + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + USkeletalMeshComponent* CreateSkeletalMeshComponent(USkeletalMesh* SkeletalMesh, const FString& Name); + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + void LoadglTFAsset(UglTFRuntimeAsset *Asset, const FString& AssetName); + + void OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedPtr HttpResponse, bool bIsSuccessful); + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + void LoadAsset(FAsset AssetData); + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + void LoadCharacterUrl(FString Url); + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + void LoadCharacter(FRpmCharacter CharacterData); + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Ready Player Me"); + USkeletalMeshComponent* BaseSkeletalMeshComponent; + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + void CreateCharacter(FString AppId); +protected: + FRpmCharacter Character; + + + TArray AddedMeshComponents; + + UFUNCTION() + virtual void HandleCharacterCreateResponse(FCharacterCreateResponse CharacterCreateResponse, bool bWasSuccessful); + UFUNCTION() + virtual void HandleCharacterUpdateResponse(FCharacterUpdateResponse CharacterUpdateResponse, bool bWasSuccessful); + UFUNCTION() + virtual void HandleCharacterFindResponse(FCharacterFindByIdResponse CharacterFindByIdResponse, bool bWasSuccessful); + UFUNCTION() + virtual void HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh); + TMap PreviewAssetMap; +private: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category="Ready Player Me") + USceneComponent* AssetRoot; + URpmSettings* RpmSettings; + FglTFRuntimeSkeletalMeshAsync OnSkeletalMeshCallback; + TSharedPtr CharacterApi; +}; diff --git a/Source/RpmNextGen/Public/RpmNextGen.h b/Source/RpmNextGen/Public/RpmNextGen.h new file mode 100644 index 0000000..d788306 --- /dev/null +++ b/Source/RpmNextGen/Public/RpmNextGen.h @@ -0,0 +1,15 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FRpmNextGenModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; +}; diff --git a/Source/RpmNextGen/Public/Samples/AssetButtonWidget.h b/Source/RpmNextGen/Public/Samples/AssetButtonWidget.h new file mode 100644 index 0000000..f231e46 --- /dev/null +++ b/Source/RpmNextGen/Public/Samples/AssetButtonWidget.h @@ -0,0 +1,48 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Api/Assets/Models/Asset.h" +#include "Blueprint/UserWidget.h" +#include "Interfaces/IHttpRequest.h" +#include "AssetButtonWidget.generated.h" + +class UImage; +class UButton; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAssetButtonClicked, const FAsset&, AssetData); + +/** + * + */ +UCLASS() +class RPMNEXTGEN_API UAssetButtonWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + UPROPERTY(BlueprintAssignable, Category = "Events") + FOnAssetButtonClicked OnAssetButtonClicked; + + UFUNCTION(BlueprintCallable, Category = "Setup") + void InitializeButton(const FAsset& InAssetData, const FVector2D& InImageSize); + +protected: + virtual void NativeConstruct() override; + +private: + UPROPERTY(meta = (BindWidget)) + UButton* AssetButton; + + UPROPERTY(meta = (BindWidget)) + UImage* AssetImage; + + FAsset AssetData; + + UFUNCTION() + void HandleButtonClicked(); + + void SetImageFromURL(UImage* Image, const FString& URL); + void OnImageDownloaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TWeakObjectPtr Image); +}; diff --git a/Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h b/Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h new file mode 100644 index 0000000..768ec6e --- /dev/null +++ b/Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h @@ -0,0 +1,67 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Api/Assets/Models/Asset.h" +#include "Blueprint/UserWidget.h" +#include "Api/Characters/CharacterApi.h" +#include "CharacterCustomizationWidget.generated.h" + +class FCharacterApi; +struct FAssetListResponse; +class FApiKeyAuthStrategy; +class UImage; +class UVerticalBox; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAssetsFetchedDelegate, bool, bWasSuccessful, const TArray&, AssetDataArray); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAssetSelected, const FAsset&, AssetData); + +/** + * + */ +UCLASS() +class RPMNEXTGEN_API UCharacterCustomizationWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + virtual void NativeConstruct() override; + + UFUNCTION(BlueprintCallable, Category = "Customization") + void PopulateBox(UPanelWidget* ParentBox); + + UFUNCTION(BlueprintCallable, Category = "Customization") + void ClearBox(UPanelWidget* ParentBox); + + UFUNCTION(BlueprintCallable, Category = "Customization") + void UpdateCustomizationOptions(UPanelWidget* ParentBox); + + UFUNCTION(BlueprintCallable, Category = "Customization") + void PopulateBoxWithFilter(UPanelWidget* ParentBox, const FString& AssetType); + + UPROPERTY(BlueprintAssignable, Category = "Customization") + FOnAssetsFetchedDelegate OnAssetsFetchedDelegate; + + UPROPERTY(BlueprintAssignable, Category = "Customization") + FOnAssetSelected OnAssetButtonSelected; + + // Property to define the size of the images + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Customization", meta = (ClampMin = "0.0", UIMin = "0.0", ToolTip = "Recommended to keep this square (e.g., 200x200)")) + FVector2D ImageSize = FVector2D(200.0f, 200.0f); // Default size of 200x200 + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Customization") + TSubclassOf AssetButtonWidgetClass; + +private: + void InitializeCustomizationOptions(); + void OnAssetsFetched(const FAssetListResponse& AssetListResponse, bool bWasSuccessful); + void CreateAssetWidget(const FAsset& AssetData, UPanelWidget* ParentBox); + + UFUNCTION() + void OnAssetButtonClicked(const FAsset& AssetData); + + FString ApplicationID; + TArray AssetDataArray; + TSharedPtr CharacterApi; +}; diff --git a/Source/RpmNextGen/Public/Settings/RpmSettings.h b/Source/RpmNextGen/Public/Settings/RpmSettings.h new file mode 100644 index 0000000..82ef9dd --- /dev/null +++ b/Source/RpmNextGen/Public/Settings/RpmSettings.h @@ -0,0 +1,16 @@ +#pragma once + + +#include "CoreMinimal.h" +#include "RpmSettingsData.h" // Include your settings header + +class RpmSettings +{ +public: + // Static method to get the settings instance + static URpmSettingsData* GetRpmSettings(); + +private: + // Static instance of the settings + static URpmSettingsData* SettingsInstance; +}; diff --git a/Source/RpmNextGen/Public/Settings/RpmSettingsData.h b/Source/RpmNextGen/Public/Settings/RpmSettingsData.h new file mode 100644 index 0000000..17933b0 --- /dev/null +++ b/Source/RpmNextGen/Public/Settings/RpmSettingsData.h @@ -0,0 +1,35 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/DeveloperSettings.h" +#include "RpmSettingsData.generated.h" + +/** + * + */ +UCLASS(config = Game, defaultconfig, meta = (DisplayName = "Ready Player Me")) +class RPMNEXTGEN_API URpmSettingsData : public UDeveloperSettings +{ + GENERATED_BODY() + +public: + URpmSettingsData(); + + UPROPERTY(EditAnywhere, Config, Category = "API") + FString ApiBaseUrl; + + UPROPERTY(EditAnywhere, Config, Category = "API") + FString ApiBaseAuthUrl; + + UPROPERTY(EditAnywhere, Config, Category = "API") + FString ApplicationId; + + UPROPERTY(EditAnywhere, Config, Category = "API") + FString ApiKey; + + UPROPERTY(EditAnywhere, Config, Category = "API") + FString ApiProxyUrl; + +}; diff --git a/Source/RpmNextGen/RpmNextGen.Build.cs b/Source/RpmNextGen/RpmNextGen.Build.cs new file mode 100644 index 0000000..0b5af8d --- /dev/null +++ b/Source/RpmNextGen/RpmNextGen.Build.cs @@ -0,0 +1,60 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class RpmNextGen : ModuleRules +{ + public RpmNextGen(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "glTFRuntime", + "DeveloperSettings", + "glTFRuntime", + "UnrealEd", + "UMG", + "ImageWrapper" + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "Json", + "JsonUtilities", + "HTTP", + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp b/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp new file mode 100644 index 0000000..7110ccc --- /dev/null +++ b/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp @@ -0,0 +1,49 @@ +#include "Auth/DevAuthTokenCache.h" +#include "EditorCache.h" +#include "Misc/ConfigCacheIni.h" + +FDeveloperAuth DevAuthTokenCache::AuthData = FDeveloperAuth(); +bool DevAuthTokenCache::bIsInitialized = false; + +void DevAuthTokenCache::Initialize() +{ + if (!bIsInitialized) + { + AuthData.Name = EditorCache::GetString(CacheKeyName, FString()); + AuthData.Token = EditorCache::GetString(CacheKeyToken, FString()); + AuthData.RefreshToken = EditorCache::GetString(CacheKeyRefreshToken, FString()); + AuthData.IsDemo = EditorCache::GetBool(CacheKeyIsDemo, false); + + if (!AuthData.IsValid()) + { + UE_LOG(LogTemp, Warning, TEXT("DevAuthTokenCache: Invalid AuthData %s"), *AuthData.ToJsonString()); + ClearAuthData(); + } + + bIsInitialized = true; + } +} + +FDeveloperAuth DevAuthTokenCache::GetAuthData() +{ + Initialize(); + return AuthData; +} + +void DevAuthTokenCache::SetAuthData(const FDeveloperLoginResponse& LoginResponse) +{ + AuthData = FDeveloperAuth(LoginResponse.Data, false); + EditorCache::SetString( CacheKeyName, AuthData.Name); + EditorCache::SetString( CacheKeyToken, AuthData.Token); + EditorCache::SetString( CacheKeyRefreshToken, AuthData.RefreshToken); + EditorCache::SetBool( CacheKeyIsDemo, AuthData.IsDemo); +} + +void DevAuthTokenCache::ClearAuthData() +{ + AuthData = FDeveloperAuth(); + EditorCache::RemoveKey(CacheKeyName); + EditorCache::RemoveKey(CacheKeyToken); + EditorCache::RemoveKey(CacheKeyRefreshToken); + EditorCache::RemoveKey(CacheKeyIsDemo); +} diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp new file mode 100644 index 0000000..166bd50 --- /dev/null +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp @@ -0,0 +1,67 @@ +#include "Auth/DeveloperAccountApi.h" +#include "JsonObjectConverter.h" +#include "RpmSettings.h" + +FDeveloperAccountApi::FDeveloperAccountApi(IAuthenticationStrategy* InAuthenticationStrategy) : FWebApiWithAuth(InAuthenticationStrategy) +{ + if (URpmSettings* Settings = GetMutableDefault()) + { + ApiBaseUrl = Settings->ApiBaseUrl; + } +} + +void FDeveloperAccountApi::ListApplicationsAsync(const FApplicationListRequest& Request) +{ + const FString QueryString = BuildQueryString(Request.Params); + const FString Url = FString::Printf(TEXT("%s/v1/applications%s"), *ApiBaseUrl, *QueryString); + FApiRequest ApiRequest; + ApiRequest.Url = Url; + OnApiResponse.BindRaw(this, &FDeveloperAccountApi::HandleAppListResponse); + DispatchRawWithAuth(ApiRequest); +} + +void FDeveloperAccountApi::ListOrganizationsAsync(const FOrganizationListRequest& Request) +{ + const FString QueryString = BuildQueryString(Request.Params); + const FString Url = FString::Printf(TEXT("%s/v1/organizations%s"), *ApiBaseUrl, *QueryString); + FApiRequest ApiRequest; + ApiRequest.Url = Url; + OnApiResponse.BindRaw(this, &FDeveloperAccountApi::HandleOrgListResponse); + DispatchRawWithAuth(ApiRequest); +} + + +void FDeveloperAccountApi::HandleAppListResponse(FString Data, bool bWasSuccessful) +{ + FApplicationListResponse ApplicationListResponse; + if (bWasSuccessful && !Data.IsEmpty() && FJsonObjectConverter::JsonObjectStringToUStruct(Data, &ApplicationListResponse, 0, 0)) + { + OnApplicationListResponse.ExecuteIfBound(ApplicationListResponse, true); + return; + } + OnApplicationListResponse.ExecuteIfBound(ApplicationListResponse, false); +} + +void FDeveloperAccountApi::HandleOrgListResponse(FString Data, bool bWasSuccessful) +{ + FOrganizationListResponse OrganizationListResponse; + if (bWasSuccessful && !Data.IsEmpty() && FJsonObjectConverter::JsonObjectStringToUStruct(Data, &OrganizationListResponse, 0, 0)) + { + OnOrganizationResponse.ExecuteIfBound(OrganizationListResponse, true); + return; + } + OnOrganizationResponse.ExecuteIfBound(OrganizationListResponse, false); +} + + +FString FDeveloperAccountApi::BuildQueryString(const TMap& Params) +{ + if (Params.Num() == 0) return FString(); + FString QueryString = TEXT("?"); + for (const auto& Param : Params) + { + QueryString += Param.Key + TEXT("=") + Param.Value + TEXT("&"); + } + QueryString.RemoveFromEnd(TEXT("&")); + return QueryString; +} diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp new file mode 100644 index 0000000..170e606 --- /dev/null +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp @@ -0,0 +1,68 @@ +#include "Auth/DeveloperTokenAuthStrategy.h" +#include "Auth/DevAuthTokenCache.h" +#include "Api/Auth/ApiRequest.h" +#include "Api/Auth/Models/RefreshTokenResponse.h" + +DeveloperTokenAuthStrategy::DeveloperTokenAuthStrategy() +{ + AuthWebApi = FWebApiWithAuth(); + AuthWebApi.OnApiResponse.BindRaw(this, &DeveloperTokenAuthStrategy::OnRefreshTokenResponse); + AuthWebApi.SetAuthenticationStrategy(this); +} + +void DeveloperTokenAuthStrategy::AddAuthToRequest(FApiRequest& Request) +{ + Request.Headers.Add(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *DevAuthTokenCache::GetAuthData().Token)); + OnAuthComplete.ExecuteIfBound(true, false); +} + +void DeveloperTokenAuthStrategy::TryRefresh(FApiRequest& Request) +{ + if (!Request.Headers.Contains(TEXT("Authorization"))) + { + return; + } + + FRefreshTokenRequest RefreshRequest; + RefreshRequest.Payload.Token = DevAuthTokenCache::GetAuthData().Token; + RefreshRequest.Payload.RefreshToken = DevAuthTokenCache::GetAuthData().RefreshToken; + + RefreshTokenAsync(RefreshRequest); +} + +void DeveloperTokenAuthStrategy::OnRefreshTokenResponse(FString Response, bool bWasSuccessful) +{ + if (!bWasSuccessful) + { + // Log or handle failure here if needed + OnAuthComplete.ExecuteIfBound(false, true); + return; + } + + FRefreshTokenResponse RefreshTokenResponse; + const bool bParsedSuccessfully = FJsonObjectConverter::JsonObjectStringToUStruct(Response, &RefreshTokenResponse, 0, 0); + + if (bParsedSuccessfully && !RefreshTokenResponse.Data.Token.IsEmpty()) + { + DevAuthTokenCache::GetAuthData().Token = RefreshTokenResponse.Data.Token; + UE_LOG(LogTemp, Warning, TEXT("DeveloperTokenAuthStrategy Token refreshed successfully: %s"), *RefreshTokenResponse.Data.Token); + OnAuthComplete.ExecuteIfBound(true, true); + } + else + { + UE_LOG(LogTemp, Warning, TEXT("Failed to refresh token")); + OnAuthComplete.ExecuteIfBound(false, true); + } +} + + +void DeveloperTokenAuthStrategy::RefreshTokenAsync(const FRefreshTokenRequest& Request) +{ + FApiRequest ApiRequest = FApiRequest(); + ApiRequest.Url = TEXT("https://readyplayer.me/api/auth/refresh"); + ApiRequest.Method = POST; + ApiRequest.Headers.Add(TEXT("Content-Type"), TEXT("application/json")); + ApiRequest.Payload = Request.ToJsonString(); + + AuthWebApi.DispatchRawWithAuth(ApiRequest); +} diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp new file mode 100644 index 0000000..eb67a8a --- /dev/null +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -0,0 +1,85 @@ +#include "EditorAssetLoader.h" + +#include "glTFRuntimeAssetActor.h" +#include "glTFRuntimeFunctionLibrary.h" +#include "TransientObjectSaverLibrary.h" +#include "Animation/SkeletalMeshActor.h" + +FEditorAssetLoader::FEditorAssetLoader() +{ + OnAssetDownloaded.BindRaw(this, &FEditorAssetLoader::OnAssetDownloadComplete); +} + +FEditorAssetLoader::~FEditorAssetLoader() +{ +} + +void FEditorAssetLoader::OnAssetDownloadComplete(bool bWasSuccessful, FString FilePath, UglTFRuntimeAsset* gltfAsset) +{ + if(bWasSuccessful) + { + LoadGltfAssetToWorld(gltfAsset); + SaveAsUAsset(gltfAsset, FilePath); + } +} + + +void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) +{ + if (!GEditor) + { + UE_LOG(LogTemp, Error, TEXT("GEditor is not available.")); + return; + } + + // Get the editor world context + UWorld* EditorWorld = GEditor->GetEditorWorldContext().World(); + if (!EditorWorld) + { + UE_LOG(LogTemp, Error, TEXT("No valid editor world found.")); + return; + } + + if (gltfAsset) + { + FTransform Transform = FTransform::Identity; + // Spawn an actor of type AglTFRuntimeAssetActor in the editor world + AglTFRuntimeAssetActor* NewActor = EditorWorld->SpawnActorDeferred(AglTFRuntimeAssetActor::StaticClass(), Transform); + if (NewActor) + { + NewActor->SetFlags(RF_Transient); + NewActor->Asset = gltfAsset; + NewActor->bAllowSkeletalAnimations = false; + NewActor->bAllowNodeAnimations = false; + NewActor->StaticMeshConfig.bGenerateStaticMeshDescription = true; + NewActor->FinishSpawning(Transform); + NewActor->DispatchBeginPlay(); + GEditor->SelectNone(true, true, true); + GEditor->SelectActor(NewActor, true, true, false, true); + + // Register the actor in the editor world and update the editor + GEditor->SelectActor(NewActor, true, true); + GEditor->EditorUpdateComponents(); + + UE_LOG(LogTemp, Log, TEXT("Successfully loaded GLB asset into the editor world")); + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to spawn AglTFRuntimeAssetActor in the editor world")); + } + //return; + + + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to load GLB asset from file")); + } +} + +void FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString Path) +{ + FglTFRuntimeSkeletonConfig skeletonConfig = FglTFRuntimeSkeletonConfig(); + USkeleton* Skeleton = gltfAsset->LoadSkeleton(0, skeletonConfig); + UTransientObjectSaverLibrary::SaveTransientSkeleton(Skeleton,TEXT("/Game/ReadyPlayerMe/TestSkeleton")); +} diff --git a/Source/RpmNextGenEditor/Private/EditorCache.cpp b/Source/RpmNextGenEditor/Private/EditorCache.cpp new file mode 100644 index 0000000..0e5227e --- /dev/null +++ b/Source/RpmNextGenEditor/Private/EditorCache.cpp @@ -0,0 +1,74 @@ +#include "EditorCache.h" +#include "Misc/ConfigCacheIni.h" + +#define RPM_CACHE_SECTION TEXT("ReadyPlayerMeCache") + +void EditorCache::SetString( const FString& Key, const FString& Value) +{ + GConfig->SetString(RPM_CACHE_SECTION, *Key, *Value, GEditorPerProjectIni); + GConfig->Flush(false, GEditorPerProjectIni); +} + +void EditorCache::SetInt(const FString& Key, int32 Value) +{ + GConfig->SetInt(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni); + GConfig->Flush(false, GEditorPerProjectIni); +} + +void EditorCache::SetFloat(const FString& Key, float Value) +{ + GConfig->SetFloat(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni); + GConfig->Flush(false, GEditorPerProjectIni); +} + +void EditorCache::SetBool(const FString& Key, bool Value) +{ + GConfig->SetBool(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni); + GConfig->Flush(false, GEditorPerProjectIni); +} + +FString EditorCache::GetString(const FString& Key, const FString& DefaultValue) +{ + FString Value; + if (GConfig->GetString(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni)) + { + return Value; + } + return DefaultValue; +} + +int32 EditorCache::GetInt(const FString& Key, int32 DefaultValue) +{ + int32 Value; + if (GConfig->GetInt(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni)) + { + return Value; + } + return DefaultValue; +} + +float EditorCache::GetFloat(const FString& Key, float DefaultValue) +{ + float Value; + if (GConfig->GetFloat(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni)) + { + return Value; + } + return DefaultValue; +} + +bool EditorCache::GetBool(const FString& Key, bool DefaultValue) +{ + bool Value; + if (GConfig->GetBool(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni)) + { + return Value; + } + return DefaultValue; +} + +void EditorCache::RemoveKey(const FString& Key) +{ + GConfig->RemoveKey(RPM_CACHE_SECTION, *Key, GEditorPerProjectIni); + GConfig->Flush(false, GEditorPerProjectIni); +} \ No newline at end of file diff --git a/Source/RpmNextGenEditor/Private/LoginWindowCommands.cpp b/Source/RpmNextGenEditor/Private/LoginWindowCommands.cpp new file mode 100644 index 0000000..3b5f569 --- /dev/null +++ b/Source/RpmNextGenEditor/Private/LoginWindowCommands.cpp @@ -0,0 +1,10 @@ +#include "LoginWindowCommands.h" + +#define LOCTEXT_NAMESPACE "FRpmNextGenEditorModule" + +void FLoginWindowCommands::RegisterCommands() +{ + UI_COMMAND(OpenPluginWindow, "Rpm Developer window", "Bring up RPM Developer window", EUserInterfaceActionType::Button, FInputChord()); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/RpmNextGenEditor/Private/LoginWindowStyle.cpp b/Source/RpmNextGenEditor/Private/LoginWindowStyle.cpp new file mode 100644 index 0000000..6fc2e8b --- /dev/null +++ b/Source/RpmNextGenEditor/Private/LoginWindowStyle.cpp @@ -0,0 +1,58 @@ +#include "LoginWindowStyle.h" +#include "Styling/SlateStyleRegistry.h" +#include "Framework/Application/SlateApplication.h" +#include "Slate/SlateGameResources.h" +#include "Interfaces/IPluginManager.h" +#include "Styling/SlateStyleMacros.h" + +#define RootToContentDir Style->RootToContentDir + +TSharedPtr FLoginWindowStyle::StyleInstance = nullptr; + +void FLoginWindowStyle::Initialize() +{ + if (!StyleInstance.IsValid()) + { + StyleInstance = Create(); + FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); + } +} + +void FLoginWindowStyle::Shutdown() +{ + FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); + ensure(StyleInstance.IsUnique()); + StyleInstance.Reset(); +} + +FName FLoginWindowStyle::GetStyleSetName() +{ + static FName StyleSetName(TEXT("LoginWindowStyle")); + return StyleSetName; +} + +const FVector2D Icon16x16(16.0f, 16.0f); +const FVector2D Icon20x20(20.0f, 20.0f); + +TSharedRef< FSlateStyleSet > FLoginWindowStyle::Create() +{ + TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("LoginWindowStyle")); + Style->SetContentRoot(IPluginManager::Get().FindPlugin("RpmNextGen")->GetBaseDir() / TEXT("Resources")); + + Style->Set("RpmNextGenEditor.OpenPluginWindow", new IMAGE_BRUSH_SVG(TEXT("PlaceholderButtonIcon"), Icon20x20)); + + return Style; +} + +void FLoginWindowStyle::ReloadTextures() +{ + if (FSlateApplication::IsInitialized()) + { + FSlateApplication::Get().GetRenderer()->ReloadTextureResources(); + } +} + +const ISlateStyle& FLoginWindowStyle::Get() +{ + return *StyleInstance; +} diff --git a/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp new file mode 100644 index 0000000..a2d4af2 --- /dev/null +++ b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp @@ -0,0 +1,102 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "RpmNextGenEditor.h" +#include "LevelEditor.h" +#include "LoginWindowCommands.h" +#include "SRpmDeveloperLoginWidget.h" +#include "Widgets/Docking/SDockTab.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/Text/STextBlock.h" +#include "ToolMenus.h" + +static const FName TestWindowTabName("LoginWindow"); + +#define LOCTEXT_NAMESPACE "RpmNextGenEditorModule" + +void FRpmNextGenEditorModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module + + FLoginWindowStyle::Initialize(); + FLoginWindowStyle::ReloadTextures(); + + FLoginWindowCommands::Register(); + + PluginCommands = MakeShareable(new FUICommandList); + + PluginCommands->MapAction( + FLoginWindowCommands::Get().OpenPluginWindow, + FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::PluginButtonClicked), + FCanExecuteAction()); + + UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FRpmNextGenEditorModule::RegisterMenus)); + + FGlobalTabmanager::Get()->RegisterNomadTabSpawner(TestWindowTabName, FOnSpawnTab::CreateRaw(this, &FRpmNextGenEditorModule::OnSpawnPluginTab)) + .SetDisplayName(LOCTEXT("FRpmDevLoginWindow", "RPM Dev Login")) + .SetMenuType(ETabSpawnerMenuType::Hidden); +} + +void FRpmNextGenEditorModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. + + UToolMenus::UnRegisterStartupCallback(this); + + UToolMenus::UnregisterOwner(this); + + FLoginWindowStyle::Shutdown(); + + FLoginWindowCommands::Unregister(); + + FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(TestWindowTabName); +} + +TSharedRef FRpmNextGenEditorModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) +{ + FText WidgetText = FText::Format( + LOCTEXT("WindowWidgetText", "Add code to {0} in {1} to override this window's contents"), + FText::FromString(TEXT("FRpmNextGenEditorModule::OnSpawnPluginTab")), + FText::FromString(TEXT("RpmNextGenEditor.cpp")) + ); + + return SNew(SDockTab) + .TabRole(NomadTab) + [ + SNew(SRpmDeveloperLoginWidget) + ]; +} + +void FRpmNextGenEditorModule::PluginButtonClicked() +{ + FGlobalTabmanager::Get()->TryInvokeTab(TestWindowTabName); +} + +void FRpmNextGenEditorModule::RegisterMenus() +{ + // Owner will be used for cleanup in call to UToolMenus::UnregisterOwner + FToolMenuOwnerScoped OwnerScoped(this); + + { + UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window"); + { + FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout"); + Section.AddMenuEntryWithCommandList(FLoginWindowCommands::Get().OpenPluginWindow, PluginCommands); + } + } + + { + UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar"); + { + FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings"); + { + FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FLoginWindowCommands::Get().OpenPluginWindow)); + Entry.SetCommandList(PluginCommands); + } + } + } +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FRpmNextGenEditorModule, RpmNextGenEditor) \ No newline at end of file diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp new file mode 100644 index 0000000..4dd79cc --- /dev/null +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -0,0 +1,531 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "SRpmDeveloperLoginWidget.h" +#include "Auth/DevAuthTokenCache.h" +#include "EditorCache.h" +#include "HttpModule.h" +#include "IImageWrapper.h" +#include "RpmSettings.h" +#include "SlateOptMacros.h" +#include "Api/Assets/Models/AssetListRequest.h" +#include "Api/Assets/Models/AssetListResponse.h" +#include "Auth/DeveloperAccountApi.h" +#include "Auth/DeveloperTokenAuthStrategy.h" +#include "Interfaces/IHttpResponse.h" +#include "Widgets/Input/SEditableTextBox.h" +#include "IImageWrapperModule.h" +#include "Auth/DeveloperLoginRequest.h" + +BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION + +void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) +{ + FDeveloperAuth AuthData = DevAuthTokenCache::GetAuthData(); + bIsLoggedIn = AuthData.IsValid(); + Settings = GetDefault(); + UserName = AuthData.Name; + ChildSlot + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(STextBlock) + .Text(FText::FromString("Sign in with your Ready Player Me Studio account")) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(STextBlock) + .Text(FText::FromString("Email:")) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SAssignNew(EmailTextBox, SEditableTextBox) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(STextBlock) + .Text(FText::FromString("Password:")) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SAssignNew(PasswordTextBox, SEditableTextBox) + .IsPassword(true) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(SButton) + .Text(FText::FromString("Login")) + .OnClicked(this, &SRpmDeveloperLoginWidget::OnLoginClicked) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(SButton) + .Text(FText::FromString("Use Demo Account")) + .OnClicked(this, &SRpmDeveloperLoginWidget::OnUseDemoAccountClicked) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .FillWidth(1.0) + [ + SNew(STextBlock) + .Text(this, &SRpmDeveloperLoginWidget::GetWelcomeText) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .HAlign(HAlign_Right) + [ + SNew(SButton) + .Text(FText::FromString("Logout")) + .OnClicked(this, &SRpmDeveloperLoginWidget::OnLogoutClicked) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) + ] + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(STextBlock) + .Text(FText::FromString("Project Settings")) + .Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 16)) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(STextBlock) + .Text(FText::FromString("Select the Ready Player Me application to link to project")) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) + ] + + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(SComboBox>) + .OptionsSource(&ComboBoxItems) + .OnSelectionChanged(this, &SRpmDeveloperLoginWidget::OnComboBoxSelectionChanged) + .OnGenerateWidget_Lambda([](TSharedPtr Item) { + return SNew(STextBlock).Text(FText::FromString(*Item)); + }) + [ + SNew(STextBlock) + .Text(this, &SRpmDeveloperLoginWidget::GetSelectedComboBoxItemText) + ] + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(STextBlock) + .Text(FText::FromString("Character Styles")) + .Font(FSlateFontInfo(FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 16)) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) + ] + + SVerticalBox::Slot() + .Padding(10) + .AutoHeight() + [ + SNew(STextBlock) + .Text(FText::FromString("Here you can import your character styles from Studio")) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + + SAssignNew(ContentBox, SVerticalBox) + .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) + ] + ]; + + EmailTextBox->SetText(FText::FromString(EditorCache::GetString(CacheKeyEmail))); + Initialize(); +} + +void SRpmDeveloperLoginWidget::Initialize() +{ + if(bIsInitialized) return; + + if (!AssetApi.IsValid()) + { + AssetApi = MakeUnique(); + AssetApi->OnListAssetsResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleBaseModelListResponse); + + } + if(!DeveloperApi.IsValid()) + { + DeveloperTokenAuthStrategy* AuthStrategy = new DeveloperTokenAuthStrategy(); + DeveloperApi = MakeUnique(AuthStrategy); + + DeveloperApi->SetAuthenticationStrategy(AuthStrategy); + DeveloperApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); + DeveloperApi->OnApplicationListResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); + } + bIsInitialized = true; + if(bIsLoggedIn) + { + GetOrgList(); + } +} + +void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) +{ + TSharedPtr ImageWidget; + const FVector2D ImageSize(100.0f, 100.0f); + + ContentBox->AddSlot() + .AutoHeight() + .Padding(5) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(5) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Left) + [ + SAssignNew(ImageWidget, SImage).// The image will be set later after downloading + DesiredSizeOverride(ImageSize) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(5, 5) + [ + SNew(SBox) + .WidthOverride(100.0f) // Match button width to image width + [ + SNew(SButton) + .Text(FText::FromString("Load Style")) + .OnClicked_Lambda([this, StyleAsset]() -> FReply + { + OnLoadStyleClicked(StyleAsset.Id); + return FReply::Handled(); + }) + ] + ] + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Top) + .Padding(10, 10, 0, 0) // Padding from the left side of the Image & Button stack + [ + SNew(STextBlock) + .Text(FText::FromString(FString::Printf(TEXT("ID: %s"), *StyleAsset.Id))) + ] + ]; + + DownloadImage(StyleAsset.IconUrl, [ImageWidget](UTexture2D* Texture) + { + if (ImageWidget.IsValid()) + { + SetImageFromTexture(Texture, ImageWidget); + } + }); +} + +void SRpmDeveloperLoginWidget::DownloadImage(const FString& Url, TFunction Callback) +{ + TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->OnProcessRequestComplete().BindLambda([Callback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) + { + if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) + { + // Create a texture from the image data + UTexture2D* Texture = CreateTextureFromImageData(Response->GetContent()); + if (Texture) + { + Callback(Texture); + } + } + }); + + HttpRequest->SetURL(Url); + HttpRequest->SetVerb(TEXT("GET")); + HttpRequest->ProcessRequest(); +} + +void SRpmDeveloperLoginWidget::SetImageFromTexture(UTexture2D* Texture, const TSharedPtr& ImageWidget) +{ + const FVector2D DesiredSize(100.0f, 100.0f); + if (Texture) + { + FSlateBrush* Brush = new FSlateBrush(); + Brush->SetResourceObject(Texture); + Brush->ImageSize = DesiredSize; + ImageWidget->SetImage(Brush); + } +} + +UTexture2D* SRpmDeveloperLoginWidget::CreateTextureFromImageData(const TArray& ImageData) +{ + IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); + EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); + + if (ImageFormat != EImageFormat::Invalid) + { + TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); + if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) + { + TArray UncompressedBGRA ; + if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) + { + UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight()); + if (Texture) + { + void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); + Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); + + Texture->UpdateResource(); + return Texture; + } + UE_LOG(LogTemp, Error, TEXT("Failed to create texture: Texture is null.")); + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to get raw image data.")); + } + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to set compressed data or ImageWrapper is invalid.")); + } + } + else + { + UE_LOG(LogTemp, Error, TEXT("Invalid image format detected.")); + } + + return nullptr; +} + + +void SRpmDeveloperLoginWidget::OnLoadStyleClicked(const FString& StyleId) +{ + UE_LOG(LogTemp, Warning, TEXT("Clicked on style ID %s"), *StyleId); + AssetLoader = FEditorAssetLoader(); + const FString Url = FString::Printf(TEXT("%s"), *CharacterStyleAssets[StyleId].GlbUrl); + UE_LOG(LogTemp, Warning, TEXT("Trying to load GLB from URL %s"), *Url); + AssetLoader.LoadGLBFromURL(Url); +} + +EVisibility SRpmDeveloperLoginWidget::GetLoginViewVisibility() const +{ + return bIsLoggedIn ? EVisibility::Collapsed : EVisibility::Visible; +} + +EVisibility SRpmDeveloperLoginWidget::GetLoggedInViewVisibility() const +{ + return bIsLoggedIn ? EVisibility::Visible : EVisibility::Collapsed; +} + +FText SRpmDeveloperLoginWidget::GetWelcomeText() const +{ + return FText::Format(FText::FromString("Welcome {0}"), FText::FromString(UserName)); +} + +FReply SRpmDeveloperLoginWidget::OnLoginClicked() +{ + FString Email = EmailTextBox->GetText().ToString(); + FString Password = PasswordTextBox->GetText().ToString(); + EditorCache::SetString(CacheKeyEmail, Email); + Email = Email.TrimStartAndEnd(); + Password = Password.TrimStartAndEnd(); + + FDeveloperLoginRequest LoginRequest = FDeveloperLoginRequest(Email, Password); + FString url = FString::Printf(TEXT("%s/login"), *Settings->ApiBaseAuthUrl); + TSharedRef Request = FHttpModule::Get().CreateRequest(); + Request->OnProcessRequestComplete().BindSP(this, &SRpmDeveloperLoginWidget::HandleLoginResponse); + Request->SetURL(url); + Request->SetVerb(TEXT("POST")); + Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + Request->SetContentAsString(LoginRequest.ToJsonString()); + Request->ProcessRequest(); + + return FReply::Handled(); +} + +void SRpmDeveloperLoginWidget::GetOrgList() +{ + FOrganizationListRequest OrgRequest; + DeveloperApi->ListOrganizationsAsync(OrgRequest); +} + +void SRpmDeveloperLoginWidget::HandleLoginResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) +{ + if (bWasSuccessful && Response.IsValid() && Response->GetResponseCode() == 200) + { + TSharedPtr ResponseObj; + const TSharedRef> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString()); + + + if (FJsonSerializer::Deserialize(Reader, ResponseObj)) + { + SetLoggedInState(true); + FDeveloperLoginResponse ResponseStruct; + FDeveloperLoginResponse::FromJsonObject(ResponseObj, ResponseStruct); + UserName = ResponseStruct.Data.Name; + DevAuthTokenCache::SetAuthData(ResponseStruct); + FString json = ResponseStruct.ToJsonString(); + + GetOrgList(); + return; + } + UE_LOG(LogTemp, Error, TEXT("Failed to deserialize login response")); + } + else + { + UE_LOG(LogTemp, Error, TEXT("Login request failed. %s"), *Response->GetContentAsString()); + DevAuthTokenCache::ClearAuthData(); + } +} + +void SRpmDeveloperLoginWidget::HandleOrganizationListResponse(const FOrganizationListResponse& Response, + bool bWasSuccessful) +{ + + if (bWasSuccessful) + { + if(Response.Data.Num() == 0) + { + UE_LOG(LogTemp, Error, TEXT("No organizations found")); + return; + } + + FApplicationListRequest Request; + Request.Params.Add("organizationId", Response.Data[0].Id); + DeveloperApi->ListApplicationsAsync(Request); + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to list organizations")); + } +} + + +void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationListResponse& Response, + bool bWasSuccessful) +{ + if (bWasSuccessful) + { + UE_LOG(LogTemp, Warning, TEXT("Applications:")); + for (const FApplication& App : Response.Data) + { + UE_LOG(LogTemp, Warning, TEXT(" - %s"), *App.Name); + } + TArray Items; + for (const FApplication& App : Response.Data) + { + Items.Add(App.Name); + } + PopulateComboBoxItems(Items); + + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to list applications")); + } + LoadBaseModelList(); +} + + +void SRpmDeveloperLoginWidget::PopulateComboBoxItems(const TArray& Items) +{ + ComboBoxItems.Empty(); + for (const FString& Item : Items) + { + ComboBoxItems.Add(MakeShared(Item)); + } + SelectedComboBoxItem = ComboBoxItems.Num() > 0 ? ComboBoxItems[0] : nullptr; +} + +FText SRpmDeveloperLoginWidget::GetSelectedComboBoxItemText() const +{ + return SelectedComboBoxItem.IsValid() ? FText::FromString(*SelectedComboBoxItem) : FText::FromString("Select an option"); +} + + + +void SRpmDeveloperLoginWidget::OnComboBoxSelectionChanged(TSharedPtr NewValue, ESelectInfo::Type SelectInfo) +{ + SelectedComboBoxItem = NewValue; +} + +FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() +{ + UserName = "DemoUser"; + //TODO: Implement demo account login + UE_LOG(LogTemp, Error, TEXT("Demo account login not implemented")); + return FReply::Handled(); +} + +FReply SRpmDeveloperLoginWidget::OnLogoutClicked() +{ + DevAuthTokenCache::ClearAuthData(); + SetLoggedInState(false); + return FReply::Handled(); +} + +void SRpmDeveloperLoginWidget::LoadBaseModelList() +{ + FAssetListRequest Request = FAssetListRequest(); + FAssetListQueryParams Params = FAssetListQueryParams(); + Params.ApplicationId = Settings->ApplicationId; + Params.Type = "baseModel"; + Request.Params = Params; + AssetApi->SetAuthenticationStrategy(new DeveloperTokenAuthStrategy()); + AssetApi->ListAssetsAsync(Request); +} + +void SRpmDeveloperLoginWidget::HandleBaseModelListResponse(const FAssetListResponse& Response, bool bWasSuccessful) +{ + CharacterStyleAssets.Empty(); + for (FAsset Asset : Response.Data) + { + CharacterStyleAssets.Add(Asset.Id, Asset); + AddCharacterStyle(Asset); + } +} + + +void SRpmDeveloperLoginWidget::SetLoggedInState(const bool IsLoggedIn) +{ + this->bIsLoggedIn = IsLoggedIn; + + // Force the UI to refresh + Invalidate(EInvalidateWidget::Layout); +} + +END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h b/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h new file mode 100644 index 0000000..8c1bc98 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h @@ -0,0 +1,75 @@ +#pragma once + +#include "CoreMinimal.h" +#include "DeveloperLoginResponse.h" +#include "DevAuthTokenCache.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FDeveloperAuth +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "token")) + FString Token; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "refreshToken")) + FString RefreshToken; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "name")) + FString Name; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "isDemo")) + bool IsDemo; + + FDeveloperAuth() = default; + + FDeveloperAuth(FDeveloperLoginResponseBody Data, bool bIsDemo) + { + Token = Data.Token; + RefreshToken = Data.RefreshToken; + Name = Data.Name; + IsDemo = bIsDemo; + } + + FString ToJsonString() const + { + FString OutputString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); + Writer->WriteObjectStart(); + Writer->WriteValue(TEXT("token"), Token); + Writer->WriteValue(TEXT("refreshToken"), RefreshToken); + Writer->WriteValue(TEXT("name"), Name); + Writer->WriteValue(TEXT("isDemo"), IsDemo); + Writer->WriteObjectEnd(); + Writer->Close(); + return OutputString; + } + + static bool FromJsonString(const FString& JsonString, FDeveloperAuth& OutStruct) + { + return FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutStruct, 0, 0); + } + + bool IsValid() const + { + return !Token.IsEmpty(); + } +}; + + +class RPMNEXTGENEDITOR_API DevAuthTokenCache +{ +public: + static FDeveloperAuth GetAuthData(); + static void SetAuthData(const FDeveloperLoginResponse& Token); + static void ClearAuthData(); +private: + static constexpr const TCHAR* CacheKeyName = TEXT("Name"); + static constexpr const TCHAR* CacheKeyToken = TEXT("Token"); + static constexpr const TCHAR* CacheKeyRefreshToken = TEXT("RefreshToken"); + static constexpr const TCHAR* CacheKeyIsDemo = TEXT("IsDemo"); + + static FDeveloperAuth AuthData; + static bool bIsInitialized; + static void Initialize(); +}; diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperAccountApi.h b/Source/RpmNextGenEditor/Public/Auth/DeveloperAccountApi.h new file mode 100644 index 0000000..272760f --- /dev/null +++ b/Source/RpmNextGenEditor/Public/Auth/DeveloperAccountApi.h @@ -0,0 +1,85 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Auth/ApiRequest.h" +#include "Api/Common/Models/ApiResponse.h" +#include "Api/Common/WebApiWithAuth.h" +#include "DeveloperAccountApi.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FApplicationListRequest : public FApiRequest +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API") + TMap Params; +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FOrganizationListRequest : public FApiRequest +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API") + TMap Params; +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FApplication +{ + GENERATED_BODY() + UPROPERTY(meta = (JsonName = "id")) + FString Id; + UPROPERTY(meta=(Jsonname = "name" )) + FString Name; +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FApplicationListResponse : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API", meta = (JsonName = "data")) + TArray Data; +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FOrganization +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (JsonName = "id")) + FString Id; + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (JsonName = "name")) + FString Name; +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FOrganizationListResponse : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API", meta = ( JsonName = "data")) + TArray Data; +}; + +DECLARE_DELEGATE_TwoParams(FOnApplicationListResponse, const FApplicationListResponse&, bool); +DECLARE_DELEGATE_TwoParams(FOnOrganizationListResponse, const FOrganizationListResponse&, bool); + + +class RPMNEXTGENEDITOR_API FDeveloperAccountApi : public FWebApiWithAuth +{ +public: + FDeveloperAccountApi(IAuthenticationStrategy* InAuthenticationStrategy); + void ListApplicationsAsync(const FApplicationListRequest& Request); + void ListOrganizationsAsync(const FOrganizationListRequest& Request); + + FOnApplicationListResponse OnApplicationListResponse; + FOnOrganizationListResponse OnOrganizationResponse; +private: + static FString BuildQueryString(const TMap& Params); + void HandleOrgListResponse(FString Data, bool bWasSuccessful); + void HandleAppListResponse(FString Data, bool bWasSuccessful); + + FString ApiBaseUrl; +}; diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperLoginRequest.h b/Source/RpmNextGenEditor/Public/Auth/DeveloperLoginRequest.h new file mode 100644 index 0000000..369453c --- /dev/null +++ b/Source/RpmNextGenEditor/Public/Auth/DeveloperLoginRequest.h @@ -0,0 +1,58 @@ +#pragma once + +#include "CoreMinimal.h" +#include "JsonObjectConverter.h" +#include "DeveloperLoginRequest.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FDeveloperLoginRequestBody +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FString LoginId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") + FString Password; + + FString ToJsonString() const + { + FString OutputString; + FJsonObjectConverter::UStructToJsonObjectString(*this, OutputString); + return OutputString; + } + + static bool FromJsonString(const FString& JsonString, FDeveloperLoginRequestBody& OutStruct) + { + return FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutStruct, 0, 0); + } +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FDeveloperLoginRequest +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Request") + FDeveloperLoginRequestBody Data; + + FString ToJsonString() const + { + FString OutputString; + FJsonObjectConverter::UStructToJsonObjectString(*this, OutputString); + return OutputString; + } + + static bool FromJsonString(const FString& JsonString, FDeveloperLoginRequest& OutStruct) + { + return FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutStruct, 0, 0); + } + + FDeveloperLoginRequest() = default; + explicit FDeveloperLoginRequest(const FDeveloperLoginRequestBody& DeveloperLoginRequestBody); + FDeveloperLoginRequest(const FString& Email, const FString& String) + { + Data.LoginId = Email; + Data.Password = String; + } +}; diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperLoginResponse.h b/Source/RpmNextGenEditor/Public/Auth/DeveloperLoginResponse.h new file mode 100644 index 0000000..6af03f5 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/Auth/DeveloperLoginResponse.h @@ -0,0 +1,48 @@ +#pragma once + +#include "CoreMinimal.h" +#include "JsonObjectConverter.h" +#include "Api/Common/Models/ApiResponse.h" +#include "DeveloperLoginResponse.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FDeveloperLoginResponseBody : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "token")) + FString Token; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "refreshToken")) + FString RefreshToken; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "name")) + FString Name; + +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FDeveloperLoginResponse : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "data")) + FDeveloperLoginResponseBody Data; + + FString ToJsonString() const + { + FString OutputString; + FJsonObjectConverter::UStructToJsonObjectString(*this, OutputString); + return OutputString; + } + + static bool FromJsonObject(const TSharedPtr& JsonObject, FDeveloperLoginResponse& OutObject) + { + if (JsonObject.IsValid()) + { + return FJsonObjectConverter::JsonObjectToUStruct(JsonObject.ToSharedRef(), StaticStruct(), &OutObject, 0, 0); + } + UE_LOG(LogTemp, Warning, TEXT("JsonObject Invalid")); + return false; + } +}; diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h b/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h new file mode 100644 index 0000000..6e7d640 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h @@ -0,0 +1,23 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Auth/ApiRequest.h" +#include "Api/Auth/IAuthenticationStrategy.h" +#include "Api/Common/WebApiWithAuth.h" +#include "Api/Auth/Models/RefreshTokenRequest.h" + +struct FRefreshTokenResponse; + + +class RPMNEXTGENEDITOR_API DeveloperTokenAuthStrategy : public IAuthenticationStrategy +{ +public: + DeveloperTokenAuthStrategy(); + virtual void AddAuthToRequest(FApiRequest& Request) override; + virtual void OnRefreshTokenResponse(FString Response, bool bWasSuccessful) override; + virtual void TryRefresh(FApiRequest& Request) override; +private: + void RefreshTokenAsync(const FRefreshTokenRequest& Request); + FOnWebApiResponse OnWebApiResponse; + FWebApiWithAuth AuthWebApi; +}; diff --git a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h new file mode 100644 index 0000000..f830e2c --- /dev/null +++ b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h @@ -0,0 +1,18 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Assets/AssetLoader.h" +#include "HAL/PlatformFilemanager.h" + +class RPMNEXTGENEDITOR_API FEditorAssetLoader : public FAssetLoader +{ +public: + void OnAssetDownloadComplete(bool bArg, FString String, UglTFRuntimeAsset* UglTFRuntimeAsset); + FEditorAssetLoader(); + virtual ~FEditorAssetLoader() override; + + void LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset); + + void SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString Path); + +}; diff --git a/Source/RpmNextGenEditor/Public/EditorCache.h b/Source/RpmNextGenEditor/Public/EditorCache.h new file mode 100644 index 0000000..ce52318 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/EditorCache.h @@ -0,0 +1,23 @@ +#pragma once + +#include "CoreMinimal.h" + +class RPMNEXTGENEDITOR_API EditorCache +{ +public: + // Set methods + static void SetString(const FString& Key, const FString& Value); + static void SetInt(const FString& Key, int32 Value); + static void SetFloat(const FString& Key, float Value); + static void SetBool(const FString& Key, bool Value); + + // Get methods + static FString GetString(const FString& Key, const FString& DefaultValue = TEXT("")); + static int32 GetInt(const FString& Key, int32 DefaultValue = 0); + static float GetFloat(const FString& Key, float DefaultValue = 0.0f); + static bool GetBool(const FString& Key, bool DefaultValue = false); + + // Remove key + static void RemoveKey(const FString& Key); + +}; diff --git a/Source/RpmNextGenEditor/Public/LoginWindowCommands.h b/Source/RpmNextGenEditor/Public/LoginWindowCommands.h new file mode 100644 index 0000000..cb92740 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/LoginWindowCommands.h @@ -0,0 +1,20 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Framework/Commands/Commands.h" +#include "LoginWindowStyle.h" + +class RPMNEXTGENEDITOR_API FLoginWindowCommands: public TCommands +{ +public: + + FLoginWindowCommands() + : TCommands(TEXT("RpmNextGenEditor"), NSLOCTEXT("Contexts", "RpmNextGenEditor", "RpmNextGen Plugin"), NAME_None, FLoginWindowStyle::GetStyleSetName()) + { + } + + // TCommands<> interface + virtual void RegisterCommands() override; + + TSharedPtr< FUICommandInfo > OpenPluginWindow; +}; diff --git a/Source/RpmNextGenEditor/Public/LoginWindowStyle.h b/Source/RpmNextGenEditor/Public/LoginWindowStyle.h new file mode 100644 index 0000000..75812ef --- /dev/null +++ b/Source/RpmNextGenEditor/Public/LoginWindowStyle.h @@ -0,0 +1,28 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Styling/SlateStyle.h" + +class RPMNEXTGENEDITOR_API FLoginWindowStyle +{ +public: + + static void Initialize(); + + static void Shutdown(); + + /** reloads textures used by slate renderer */ + static void ReloadTextures(); + + /** @return The Slate style set for the Shooter game */ + static const ISlateStyle& Get(); + + static FName GetStyleSetName(); + +private: + + static TSharedRef< class FSlateStyleSet > Create(); + + static TSharedPtr< class FSlateStyleSet > StyleInstance; + +}; diff --git a/Source/RpmNextGenEditor/Public/RpmNextGenEditor.h b/Source/RpmNextGenEditor/Public/RpmNextGenEditor.h new file mode 100644 index 0000000..0f2cd2e --- /dev/null +++ b/Source/RpmNextGenEditor/Public/RpmNextGenEditor.h @@ -0,0 +1,26 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class RPMNEXTGENEDITOR_API FRpmNextGenEditorModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; + + /** This function will be bound to Command (by default it will bring up plugin window) */ + void PluginButtonClicked(); + +private: + + void RegisterMenus(); + + TSharedRef OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs); + TSharedPtr PluginCommands; + +}; diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h new file mode 100644 index 0000000..5af737f --- /dev/null +++ b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h @@ -0,0 +1,79 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "EditorAssetLoader.h" +#include "Api/Assets/AssetApi.h" +#include "Api/Assets/Models/AssetListResponse.h" +#include "Auth/DeveloperAccountApi.h" +#include "Interfaces/IHttpRequest.h" +#include "Widgets/SCompoundWidget.h" +#include "Containers/Map.h" + +class URpmSettings; +class UDeveloperAuthApi; +class SEditableTextBox; +/** + * + */ +class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SRpmDeveloperLoginWidget) + {} + SLATE_END_ARGS() + + /** Constructs this widget with InArgs */ + void Construct(const FArguments& InArgs); + +private: + FReply OnLoginClicked(); + void GetOrgList(); + FReply OnUseDemoAccountClicked(); + FReply OnLogoutClicked(); + + + void LoadBaseModelList(); + + void HandleLoginResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); + void HandleOrganizationListResponse(const FOrganizationListResponse& Response, bool bWasSuccessful); + + void HandleApplicationListResponse(const FApplicationListResponse& Response, bool bWasSuccessful); + void HandleBaseModelListResponse(const FAssetListResponse& Response, bool bWasSuccessful); + void OnLoadStyleClicked(const FString& StyleId); + EVisibility GetLoginViewVisibility() const; + EVisibility GetLoggedInViewVisibility() const; + + FText GetWelcomeText() const; + + void SetLoggedInState(const bool IsLoggedIn); + void PopulateComboBoxItems(const TArray& Items); + + const URpmSettings* Settings; + TSharedPtr EmailTextBox; + TSharedPtr PasswordTextBox; + + TArray> ComboBoxItems; + TSharedPtr SelectedComboBoxItem; + + void OnComboBoxSelectionChanged(TSharedPtr NewValue, ESelectInfo::Type SelectInfo); + FText GetSelectedComboBoxItemText() const; + + bool bIsLoggedIn = false; + FString UserName; + TUniquePtr AssetApi; + TUniquePtr DeveloperApi; + static constexpr const TCHAR* CacheKeyEmail = TEXT("Email"); + + TSharedPtr ContentBox; + + TMap CharacterStyleAssets; + void AddCharacterStyle(const FAsset& StyleAsset); + void DownloadImage(const FString& Url, TFunction Callback); + UTexture2D* CreateTextureFromImageData(const TArray& ImageData); + static void SetImageFromTexture(UTexture2D* Texture, const TSharedPtr& ImageWidget); + bool bIsInitialized = false; + FEditorAssetLoader AssetLoader; + void Initialize(); +}; diff --git a/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs b/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs new file mode 100644 index 0000000..36bea51 --- /dev/null +++ b/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs @@ -0,0 +1,62 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class RpmNextGenEditor : ModuleRules +{ + public RpmNextGenEditor(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "CoreUObject", + "Engine", + "HTTP", + "JsonUtilities", + "RpmNextGen", + "ImageWrapper", + "EditorStyle", + "glTFRuntime", + "TransientObjectSaver" + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "Projects", + "InputCore", + "EditorFramework", + "UnrealEd", + "ToolMenus", + "CoreUObject", + "Slate", + "SlateCore", + "Json" + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + } + ); + } +} From d3ba1374640bbe1528fda4a87220fdae19ae20dc Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 7 Aug 2024 10:41:49 +0300 Subject: [PATCH 02/74] chore: error fixing --- .../RpmNextGen/Private/Api/Assets/AssetApi.cpp | 4 ++-- .../Private/Api/Auth/ApiKeyAuthStrategy.cpp | 4 ++-- .../Private/Api/Characters/CharacterApi.cpp | 6 +++--- .../Private/Api/Common/WebApiWithAuth.cpp | 2 +- Source/RpmNextGen/Private/RpmActor.cpp | 5 ++--- .../Private/Samples/AssetButtonWidget.cpp | 2 +- .../Samples/CharacterCustomizationWidget.cpp | 10 +++++----- ...SettingsData.cpp => RpmDeveloperSettings.cpp} | 4 ++-- .../RpmNextGen/Private/Settings/RpmSettings.cpp | 16 ---------------- Source/RpmNextGen/Public/RpmActor.h | 4 ++-- ...{RpmSettingsData.h => RpmDeveloperSettings.h} | 6 +++--- Source/RpmNextGen/Public/Settings/RpmSettings.h | 16 ---------------- .../Private/Auth/DeveloperAccountApi.cpp | 4 ++-- .../Private/SRpmDeveloperLoginWidget.cpp | 4 ++-- .../Public/SRpmDeveloperLoginWidget.h | 4 ++-- 15 files changed, 29 insertions(+), 62 deletions(-) rename Source/RpmNextGen/Private/Settings/{RpmSettingsData.cpp => RpmDeveloperSettings.cpp} (74%) delete mode 100644 Source/RpmNextGen/Private/Settings/RpmSettings.cpp rename Source/RpmNextGen/Public/Settings/{RpmSettingsData.h => RpmDeveloperSettings.h} (82%) delete mode 100644 Source/RpmNextGen/Public/Settings/RpmSettings.h diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp index c375dc9..f731285 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp @@ -1,11 +1,11 @@ #include "Api/Assets/AssetApi.h" -#include "RpmSettings.h" +#include "Settings/RpmDeveloperSettings.h" #include "Api/Assets/Models/AssetListRequest.h" #include "Api/Assets/Models/AssetListResponse.h" FAssetApi::FAssetApi() { - URpmSettings* Settings = GetMutableDefault(); + URpmDeveloperSettings* Settings = GetMutableDefault(); ApiBaseUrl = Settings->ApiBaseUrl; OnApiResponse.BindRaw(this, &FAssetApi::HandleListAssetResponse); } diff --git a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp index ba0e57f..5e5df37 100644 --- a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp +++ b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp @@ -1,5 +1,5 @@ #include "Api/Auth/ApiKeyAuthStrategy.h" -#include "RpmSettings.h" +#include "Settings/RpmDeveloperSettings.h" class URpmSettings; @@ -9,7 +9,7 @@ FApiKeyAuthStrategy::FApiKeyAuthStrategy() void FApiKeyAuthStrategy::AddAuthToRequest(FApiRequest& Request) { - URpmSettings *Settings = GetMutableDefault(); + URpmDeveloperSettings *Settings = GetMutableDefault(); Request.Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); UE_LOG(LogTemp, Log, TEXT("Added API key to request %s"), *Settings->ApiKey); UE_LOG(LogTemp, Log, TEXT("Is OnAuthComplete bound = %d"), OnAuthComplete.IsBound()); diff --git a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp index d700d3c..cda52ba 100644 --- a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp +++ b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp @@ -1,15 +1,15 @@ #include "Api/Characters/CharacterApi.h" #include "HttpModule.h" -#include "RpmSettings.h" #include "Api/Characters/Models/CharacterFindByIdRequest.h" #include "Api/Characters/Models/CharacterPreviewRequest.h" #include "Api/Characters/Models/CharacterUpdateRequest.h" #include "GenericPlatform/GenericPlatformHttp.h" #include "Interfaces/IHttpResponse.h" +#include "Settings/RpmDeveloperSettings.h" FCharacterApi::FCharacterApi() { - URpmSettings *Settings = GetMutableDefault(); + URpmDeveloperSettings *Settings = GetMutableDefault(); BaseUrl = FString::Printf(TEXT("%s/v1/characters"), *Settings->ApiBaseUrl) ; Http = &FHttpModule::Get(); } @@ -59,7 +59,7 @@ void FCharacterApi::DispatchRaw(const FApiRequest& Data, const ECharacterRespons Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); //TODO move to auth strategy - URpmSettings *Settings = GetMutableDefault(); + URpmDeveloperSettings *Settings = GetMutableDefault(); Request->SetHeader(TEXT("X-API-KEY"), Settings->ApiKey); UE_LOG(LogTemp, Warning, TEXT("Making request to Url: %s | Verb: %s with data %s"), *Data.Url, *Data.GetVerb(), *Data.Payload); for (const auto& Header : Data.Headers) diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp index d207bde..da80ab5 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -1,6 +1,6 @@ #include "Api/Common/WebApiWithAuth.h" -FWebApiWithAuth::FWebApiWithAuth() : AuthenticationStrategy(nullptr), ApiRequestData(nullptr) +FWebApiWithAuth::FWebApiWithAuth() : ApiRequestData(nullptr), AuthenticationStrategy(nullptr) { FWebApi(); } diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index 6540ff6..033d8fe 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -4,19 +4,18 @@ #include "glTFRuntimeAsset.h" #include "glTFRuntimeFunctionLibrary.h" #include "HttpModule.h" -#include "RpmSettings.h" #include "Api/Characters/CharacterApi.h" #include "Api/Characters/Models/RpmCharacter.h" #include "Interfaces/IHttpResponse.h" +#include "Settings/RpmDeveloperSettings.h" -class URpmSettings; class IHttpRequest; ARpmActor::ARpmActor() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; - RpmSettings = GetMutableDefault(); + RpmSettings = GetMutableDefault(); CharacterApi = MakeShared(); CharacterApi->OnCharacterCreateResponse.BindUObject(this, &ARpmActor::HandleCharacterCreateResponse); CharacterApi->OnCharacterUpdateResponse.BindUObject(this, &ARpmActor::HandleCharacterUpdateResponse); diff --git a/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp index 4167b09..1bc5507 100644 --- a/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp @@ -1,6 +1,6 @@ // Fill out your copyright notice in the Description page of Project Settings. -#include "AssetButtonWidget.h" +#include "Samples/AssetButtonWidget.h" #include "HttpModule.h" #include "IImageWrapper.h" #include "IImageWrapperModule.h" diff --git a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp index 5c4e440..f63395e 100644 --- a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp @@ -1,7 +1,7 @@ -#include "CharacterCustomizationWidget.h" -#include "AssetButtonWidget.h" +#include "Samples/CharacterCustomizationWidget.h" +#include "Samples/AssetButtonWidget.h" #include "HttpModule.h" -#include "RpmSettings.h" +#include "Settings/RpmDeveloperSettings.h" #include "Components/HorizontalBox.h" #include "Components/Image.h" #include "Api/Assets/Models/Asset.h" @@ -13,7 +13,7 @@ void UCharacterCustomizationWidget::NativeConstruct() { Super::NativeConstruct(); - URpmSettings* Settings = GetMutableDefault(); + URpmDeveloperSettings* Settings = GetMutableDefault(); ApplicationID = Settings->ApplicationId; InitializeCustomizationOptions(); } @@ -22,7 +22,7 @@ void UCharacterCustomizationWidget::InitializeCustomizationOptions() { UE_LOG(LogTemp, Warning, TEXT("Initialize customization options called. Application ID: %s"), *ApplicationID); - URpmSettings* Settings = GetMutableDefault(); + URpmDeveloperSettings* Settings = GetMutableDefault(); FString ApiBaseUrl = Settings->ApiBaseUrl; UE_LOG(LogTemp, Warning, TEXT("Asset API is valid")); diff --git a/Source/RpmNextGen/Private/Settings/RpmSettingsData.cpp b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp similarity index 74% rename from Source/RpmNextGen/Private/Settings/RpmSettingsData.cpp rename to Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp index a2a1405..f6fe683 100644 --- a/Source/RpmNextGen/Private/Settings/RpmSettingsData.cpp +++ b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp @@ -1,9 +1,9 @@ // Fill out your copyright notice in the Description page of Project Settings. -#include "RpmSettingsData.h" +#include "Settings/RpmDeveloperSettings.h" -URpmSettingsData::URpmSettingsData() +URpmDeveloperSettings::URpmDeveloperSettings() { ApiBaseUrl = TEXT("https://api.readyplayer.me"); ApiBaseAuthUrl = TEXT("https://readyplayer.me/api/auth"); diff --git a/Source/RpmNextGen/Private/Settings/RpmSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmSettings.cpp deleted file mode 100644 index e6e389a..0000000 --- a/Source/RpmNextGen/Private/Settings/RpmSettings.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "RpmSettings.h" - -URpmSettingsData* RpmSettings::SettingsInstance = nullptr; - -URpmSettingsData* RpmSettings::GetRpmSettings() -{ - // Check if the settings instance is already initialized - if (!SettingsInstance) - { - // Initialize the settings instance if it is not already - SettingsInstance = GetMutableDefault(); - } - - // Return the settings instance - return SettingsInstance; -} \ No newline at end of file diff --git a/Source/RpmNextGen/Public/RpmActor.h b/Source/RpmNextGen/Public/RpmActor.h index e230758..fd2b1bc 100644 --- a/Source/RpmNextGen/Public/RpmActor.h +++ b/Source/RpmNextGen/Public/RpmActor.h @@ -14,7 +14,7 @@ class IHttpResponse; class IHttpRequest; class FCharacterApi; struct FRpmCharacter; -class URpmSettings; +class URpmDeveloperSettings; class UglTFRuntimeAsset; /** @@ -68,7 +68,7 @@ class RPMNEXTGEN_API ARpmActor : public AActor private: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category="Ready Player Me") USceneComponent* AssetRoot; - URpmSettings* RpmSettings; + URpmDeveloperSettings* RpmSettings; FglTFRuntimeSkeletalMeshAsync OnSkeletalMeshCallback; TSharedPtr CharacterApi; }; diff --git a/Source/RpmNextGen/Public/Settings/RpmSettingsData.h b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h similarity index 82% rename from Source/RpmNextGen/Public/Settings/RpmSettingsData.h rename to Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h index 17933b0..972cf24 100644 --- a/Source/RpmNextGen/Public/Settings/RpmSettingsData.h +++ b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h @@ -4,18 +4,18 @@ #include "CoreMinimal.h" #include "Engine/DeveloperSettings.h" -#include "RpmSettingsData.generated.h" +#include "RpmDeveloperSettings.generated.h" /** * */ UCLASS(config = Game, defaultconfig, meta = (DisplayName = "Ready Player Me")) -class RPMNEXTGEN_API URpmSettingsData : public UDeveloperSettings +class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings { GENERATED_BODY() public: - URpmSettingsData(); + URpmDeveloperSettings(); UPROPERTY(EditAnywhere, Config, Category = "API") FString ApiBaseUrl; diff --git a/Source/RpmNextGen/Public/Settings/RpmSettings.h b/Source/RpmNextGen/Public/Settings/RpmSettings.h deleted file mode 100644 index 82ef9dd..0000000 --- a/Source/RpmNextGen/Public/Settings/RpmSettings.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - - -#include "CoreMinimal.h" -#include "RpmSettingsData.h" // Include your settings header - -class RpmSettings -{ -public: - // Static method to get the settings instance - static URpmSettingsData* GetRpmSettings(); - -private: - // Static instance of the settings - static URpmSettingsData* SettingsInstance; -}; diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp index 166bd50..49dc3b1 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp @@ -1,10 +1,10 @@ #include "Auth/DeveloperAccountApi.h" #include "JsonObjectConverter.h" -#include "RpmSettings.h" +#include "Settings/RpmDeveloperSettings.h" FDeveloperAccountApi::FDeveloperAccountApi(IAuthenticationStrategy* InAuthenticationStrategy) : FWebApiWithAuth(InAuthenticationStrategy) { - if (URpmSettings* Settings = GetMutableDefault()) + if (URpmDeveloperSettings* Settings = GetMutableDefault()) { ApiBaseUrl = Settings->ApiBaseUrl; } diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 4dd79cc..c27884a 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -6,7 +6,6 @@ #include "EditorCache.h" #include "HttpModule.h" #include "IImageWrapper.h" -#include "RpmSettings.h" #include "SlateOptMacros.h" #include "Api/Assets/Models/AssetListRequest.h" #include "Api/Assets/Models/AssetListResponse.h" @@ -16,6 +15,7 @@ #include "Widgets/Input/SEditableTextBox.h" #include "IImageWrapperModule.h" #include "Auth/DeveloperLoginRequest.h" +#include "Settings/RpmDeveloperSettings.h" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION @@ -23,7 +23,7 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) { FDeveloperAuth AuthData = DevAuthTokenCache::GetAuthData(); bIsLoggedIn = AuthData.IsValid(); - Settings = GetDefault(); + Settings = GetDefault(); UserName = AuthData.Name; ChildSlot [ diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h index 5af737f..078ff58 100644 --- a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h @@ -11,7 +11,7 @@ #include "Widgets/SCompoundWidget.h" #include "Containers/Map.h" -class URpmSettings; +class URpmDeveloperSettings; class UDeveloperAuthApi; class SEditableTextBox; /** @@ -50,7 +50,7 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget void SetLoggedInState(const bool IsLoggedIn); void PopulateComboBoxItems(const TArray& Items); - const URpmSettings* Settings; + const URpmDeveloperSettings* Settings; TSharedPtr EmailTextBox; TSharedPtr PasswordTextBox; From 5ba7caeef6a9dda0578450c39e883f8ea16e96d4 Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 7 Aug 2024 13:32:15 +0300 Subject: [PATCH 03/74] feat: refactor and fix auth refresh --- .../Private/Api/Auth/ApiKeyAuthStrategy.cpp | 7 +- .../RpmNextGen/Private/Api/Common/WebApi.cpp | 3 - .../Private/Api/Common/WebApiWithAuth.cpp | 36 +++++----- .../Public/Api/Auth/ApiKeyAuthStrategy.h | 2 +- Source/RpmNextGen/Public/Api/Auth/AuthApi.cpp | 35 +++++++++ Source/RpmNextGen/Public/Api/Auth/AuthApi.h | 21 ++++++ .../Public/Api/Auth/IAuthenticationStrategy.h | 3 +- .../Api/Auth/Models/RefreshTokenRequest.h | 2 +- Source/RpmNextGen/Public/Api/Common/WebApi.h | 2 +- .../Public/Api/Common/WebApiWithAuth.h | 4 +- .../Private/Auth/DevAuthTokenCache.cpp | 4 +- .../Private/Auth/DeveloperAuthApi.cpp | 32 +++++++++ .../Auth/DeveloperTokenAuthStrategy.cpp | 55 +++++--------- .../Private/SRpmDeveloperLoginWidget.cpp | 72 +++++++------------ .../Public/Auth/DevAuthTokenCache.h | 2 +- .../Public/Auth/DeveloperAuthApi.h | 21 ++++++ .../Public/Auth/DeveloperTokenAuthStrategy.h | 7 +- .../Public/SRpmDeveloperLoginWidget.h | 7 +- 18 files changed, 192 insertions(+), 123 deletions(-) create mode 100644 Source/RpmNextGen/Public/Api/Auth/AuthApi.cpp create mode 100644 Source/RpmNextGen/Public/Api/Auth/AuthApi.h create mode 100644 Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp create mode 100644 Source/RpmNextGenEditor/Public/Auth/DeveloperAuthApi.h diff --git a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp index 5e5df37..8d857cf 100644 --- a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp +++ b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp @@ -10,14 +10,11 @@ FApiKeyAuthStrategy::FApiKeyAuthStrategy() void FApiKeyAuthStrategy::AddAuthToRequest(FApiRequest& Request) { URpmDeveloperSettings *Settings = GetMutableDefault(); - Request.Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); - UE_LOG(LogTemp, Log, TEXT("Added API key to request %s"), *Settings->ApiKey); - UE_LOG(LogTemp, Log, TEXT("Is OnAuthComplete bound = %d"), OnAuthComplete.IsBound()); - + Request.Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); OnAuthComplete.ExecuteIfBound(true, false); } -void FApiKeyAuthStrategy::OnRefreshTokenResponse(FString Response, bool bWasSuccessful) +void FApiKeyAuthStrategy::OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) { } diff --git a/Source/RpmNextGen/Private/Api/Common/WebApi.cpp b/Source/RpmNextGen/Private/Api/Common/WebApi.cpp index 680e548..2783139 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApi.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApi.cpp @@ -27,7 +27,6 @@ void FWebApi::DispatchRaw(const FApiRequest& Data) for (const auto& Header : Data.Headers) { Request->SetHeader(Header.Key, Header.Value); - //UE_LOG(LogTemp, Log, TEXT("Header Key: %s | Value: %s"), *Header.Key, *Header.Value); } if (!Data.Payload.IsEmpty()) @@ -41,10 +40,8 @@ void FWebApi::DispatchRaw(const FApiRequest& Data) void FWebApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { - if (bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())) { - UE_LOG(LogTemp, Warning, TEXT("WebApi from URL %s Response Success: %s."), *Request->GetURL(), *Response->GetContentAsString()); OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), true); return; } diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp index da80ab5..5287202 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -1,5 +1,7 @@ #include "Api/Common/WebApiWithAuth.h" +#include "Interfaces/IHttpResponse.h" + FWebApiWithAuth::FWebApiWithAuth() : ApiRequestData(nullptr), AuthenticationStrategy(nullptr) { FWebApi(); @@ -13,17 +15,13 @@ FWebApiWithAuth::FWebApiWithAuth(IAuthenticationStrategy* InAuthenticationStrate void FWebApiWithAuth::SetAuthenticationStrategy(IAuthenticationStrategy* InAuthenticationStrategy) { AuthenticationStrategy = InAuthenticationStrategy; - UE_LOG( LogTemp, Log, TEXT("About to bind = %d"), AuthenticationStrategy->OnAuthComplete.IsBound()); - AuthenticationStrategy->OnAuthComplete.BindRaw(this, &FWebApiWithAuth::OnAuthComplete); - UE_LOG( LogTemp, Log, TEXT("Set auth strategy. OnAuthComplete Isbound = %d"), AuthenticationStrategy->OnAuthComplete.IsBound()); } void FWebApiWithAuth::OnAuthComplete(bool bWasSuccessful, bool bWasRefreshed) { if(bWasSuccessful) { - UE_LOG(LogTemp, Log, TEXT("Dispatching raw data")); DispatchRaw(*ApiRequestData); return; } @@ -33,31 +31,35 @@ void FWebApiWithAuth::OnAuthComplete(bool bWasSuccessful, bool bWasRefreshed) AuthenticationStrategy->TryRefresh(*ApiRequestData); return; } - UE_LOG(LogTemp, Log, TEXT("Auth failed")); + UE_LOG(LogTemp, Warning, TEXT("Auth failed")); OnApiResponse.ExecuteIfBound(TEXT("Auth failed"), false); } -// template -// void FWebApiWithAuth::DispatchWithAuth( -// const TFApiRequest& Data -// ) -// { -// TFApiRequestBody payload = TFApiRequestBody(Data.Payload); -// FString PayloadString = ConvertToJsonString(payload.Data); -// DispatchRawWithAuth(TFApiRequest{Data.Url, Data.Method, Data.Headers, PayloadString}); -// } - - void FWebApiWithAuth::DispatchRawWithAuth( FApiRequest& Data ) { if (AuthenticationStrategy == nullptr) { - UE_LOG(LogTemp, Log, TEXT("Auth strategy is null")); + UE_LOG(LogTemp, Warning, TEXT("Auth strategy is null")); DispatchRaw(Data); return; } this->ApiRequestData = &Data; AuthenticationStrategy->AddAuthToRequest(Data); } + +void FWebApiWithAuth::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) +{ + //FWebApi::OnProcessResponse(Request, Response, bWasSuccessful); + if (bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())) + { + OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), true); + return; + } + if(EHttpResponseCodes::Denied == Response->GetResponseCode()) + { + UE_LOG(LogTemp, Warning, TEXT("Forbidden response trying auth refresh")); + AuthenticationStrategy->TryRefresh(*ApiRequestData); + } +} diff --git a/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h b/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h index c5c0b72..b774283 100644 --- a/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h +++ b/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h @@ -9,7 +9,7 @@ class RPMNEXTGEN_API FApiKeyAuthStrategy : public IAuthenticationStrategy public: FApiKeyAuthStrategy(); virtual void AddAuthToRequest(FApiRequest& Request) override; - virtual void OnRefreshTokenResponse(FString Response, bool bWasSuccessful) override; + virtual void OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) override; virtual void TryRefresh(FApiRequest& Request) override; FOnAuthComplete OnAuthComplete; }; diff --git a/Source/RpmNextGen/Public/Api/Auth/AuthApi.cpp b/Source/RpmNextGen/Public/Api/Auth/AuthApi.cpp new file mode 100644 index 0000000..616356a --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Auth/AuthApi.cpp @@ -0,0 +1,35 @@ +#include "AuthApi.h" + +#include "Interfaces/IHttpResponse.h" +#include "Models/RefreshTokenRequest.h" +#include "Models/RefreshTokenResponse.h" +#include "Settings/RpmDeveloperSettings.h" + +FAuthApi::FAuthApi() +{ + URpmDeveloperSettings* Settings = GetMutableDefault(); + ApiUrl = FString::Printf(TEXT("%s/refresh"), *Settings->ApiBaseAuthUrl); +} + +void FAuthApi::RefreshToken(const FRefreshTokenRequest& Request) +{ + FApiRequest ApiRequest = FApiRequest(); + ApiRequest.Url = ApiUrl; + ApiRequest.Method = POST; + ApiRequest.Headers.Add(TEXT("Content-Type"), TEXT("application/json")); + ApiRequest.Payload = Request.ToJsonString(); + DispatchRaw(ApiRequest); +} + +void FAuthApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) +{ + FString data = Response->GetContentAsString(); + + FRefreshTokenResponse TokenResponse; + if (bWasSuccessful && !data.IsEmpty() && FJsonObjectConverter::JsonObjectStringToUStruct(data, &TokenResponse, 0, 0)) + { + OnRefreshTokenResponse.ExecuteIfBound(TokenResponse, true); + return; + } + OnRefreshTokenResponse.ExecuteIfBound(FRefreshTokenResponse(), false); +} diff --git a/Source/RpmNextGen/Public/Api/Auth/AuthApi.h b/Source/RpmNextGen/Public/Api/Auth/AuthApi.h new file mode 100644 index 0000000..8093000 --- /dev/null +++ b/Source/RpmNextGen/Public/Api/Auth/AuthApi.h @@ -0,0 +1,21 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Common/WebApi.h" + +struct FRefreshTokenResponse; +struct FRefreshTokenRequest; + +DECLARE_DELEGATE_TwoParams(FOnRefreshTokenResponse, const FRefreshTokenResponse&, bool); + +class RPMNEXTGEN_API FAuthApi : public FWebApi +{ +public: + FAuthApi(); + void RefreshToken(const FRefreshTokenRequest& Request); + FOnRefreshTokenResponse OnRefreshTokenResponse; + virtual void OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) override; + +private: + FString ApiUrl; +}; diff --git a/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h index 9bfe108..6cf6c53 100644 --- a/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h +++ b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h @@ -2,6 +2,7 @@ #include "CoreMinimal.h" #include "Api/Auth/ApiRequest.h" +#include "Models/RefreshTokenResponse.h" DECLARE_DELEGATE_TwoParams(FOnAuthComplete, bool, bool); @@ -11,6 +12,6 @@ class RPMNEXTGEN_API IAuthenticationStrategy virtual ~IAuthenticationStrategy() = default; virtual void AddAuthToRequest(FApiRequest& Request) = 0; virtual void TryRefresh(FApiRequest& Request) = 0; - virtual void OnRefreshTokenResponse(FString Response, bool bWasSuccessful) = 0; + virtual void OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) = 0; FOnAuthComplete OnAuthComplete; }; \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenRequest.h b/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenRequest.h index 4a65fb3..a6c08ce 100644 --- a/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenRequest.h +++ b/Source/RpmNextGen/Public/Api/Auth/Models/RefreshTokenRequest.h @@ -36,7 +36,7 @@ struct RPMNEXTGEN_API FRefreshTokenRequest UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") - FRefreshTokenRequestBody Payload; + FRefreshTokenRequestBody Data; FString ToJsonString() const { diff --git a/Source/RpmNextGen/Public/Api/Common/WebApi.h b/Source/RpmNextGen/Public/Api/Common/WebApi.h index 5745cad..9a61640 100644 --- a/Source/RpmNextGen/Public/Api/Common/WebApi.h +++ b/Source/RpmNextGen/Public/Api/Common/WebApi.h @@ -27,7 +27,7 @@ class RPMNEXTGEN_API FWebApi const FApiRequest& Data ); - void OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); + virtual void OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); FString BuildQueryString(const TMap& QueryParams); diff --git a/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h index eab8e1a..62f0163 100644 --- a/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h +++ b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h @@ -17,13 +17,15 @@ class RPMNEXTGEN_API FWebApiWithAuth : public FWebApi // void DispatchWithAuth( // const TFApiRequest& Data // ); - + void OnAuthComplete(bool bWasSuccessful, bool bWasRefreshed); void DispatchRawWithAuth(FApiRequest& Data); protected: FApiRequest* ApiRequestData; + virtual void OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) override; + private: IAuthenticationStrategy* AuthenticationStrategy; }; diff --git a/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp b/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp index 7110ccc..e348e6c 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp @@ -30,9 +30,9 @@ FDeveloperAuth DevAuthTokenCache::GetAuthData() return AuthData; } -void DevAuthTokenCache::SetAuthData(const FDeveloperLoginResponse& LoginResponse) +void DevAuthTokenCache::SetAuthData(const FDeveloperAuth& DevAuthData) { - AuthData = FDeveloperAuth(LoginResponse.Data, false); + AuthData = DevAuthData; EditorCache::SetString( CacheKeyName, AuthData.Name); EditorCache::SetString( CacheKeyToken, AuthData.Token); EditorCache::SetString( CacheKeyRefreshToken, AuthData.RefreshToken); diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp new file mode 100644 index 0000000..0e93af5 --- /dev/null +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp @@ -0,0 +1,32 @@ +#include "Auth/DeveloperAuthApi.h" +#include "Auth/DeveloperLoginRequest.h" +#include "Auth/DeveloperLoginResponse.h" +#include "Settings/RpmDeveloperSettings.h" + +FDeveloperAuthApi::FDeveloperAuthApi() +{ + URpmDeveloperSettings* Settings = GetMutableDefault(); + ApiUrl = FString::Printf(TEXT("%s/login"), *Settings->ApiBaseAuthUrl); + OnApiResponse.BindRaw(this, &FDeveloperAuthApi::HandleLoginResponse); +} + +void FDeveloperAuthApi::HandleLoginResponse(FString JsonData, bool bIsSuccessful) const +{ + FDeveloperLoginResponse Response; + if (bIsSuccessful && !JsonData.IsEmpty() && FJsonObjectConverter::JsonObjectStringToUStruct(JsonData, &Response, 0, 0)) + { + OnLoginResponse.ExecuteIfBound(Response, true); + return; + } + OnLoginResponse.ExecuteIfBound(Response, bIsSuccessful); +} + +void FDeveloperAuthApi::LoginWithEmail(FDeveloperLoginRequest Request) +{ + FApiRequest ApiRequest = FApiRequest(); + ApiRequest.Url = ApiUrl; + ApiRequest.Method = POST; + ApiRequest.Headers.Add(TEXT("Content-Type"), TEXT("application/json")); + ApiRequest.Payload = Request.ToJsonString(); + DispatchRaw(ApiRequest); +} diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp index 170e606..7340d13 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp @@ -1,13 +1,14 @@ #include "Auth/DeveloperTokenAuthStrategy.h" #include "Auth/DevAuthTokenCache.h" #include "Api/Auth/ApiRequest.h" +#include "Api/Auth/Models/RefreshTokenRequest.h" #include "Api/Auth/Models/RefreshTokenResponse.h" DeveloperTokenAuthStrategy::DeveloperTokenAuthStrategy() { - AuthWebApi = FWebApiWithAuth(); - AuthWebApi.OnApiResponse.BindRaw(this, &DeveloperTokenAuthStrategy::OnRefreshTokenResponse); - AuthWebApi.SetAuthenticationStrategy(this); + AuthApi = FAuthApi(); + AuthApi.OnRefreshTokenResponse.BindRaw(this, &DeveloperTokenAuthStrategy::OnRefreshTokenResponse); + } void DeveloperTokenAuthStrategy::AddAuthToRequest(FApiRequest& Request) @@ -18,51 +19,31 @@ void DeveloperTokenAuthStrategy::AddAuthToRequest(FApiRequest& Request) void DeveloperTokenAuthStrategy::TryRefresh(FApiRequest& Request) { - if (!Request.Headers.Contains(TEXT("Authorization"))) - { - return; - } - FRefreshTokenRequest RefreshRequest; - RefreshRequest.Payload.Token = DevAuthTokenCache::GetAuthData().Token; - RefreshRequest.Payload.RefreshToken = DevAuthTokenCache::GetAuthData().RefreshToken; + RefreshRequest.Data.Token = DevAuthTokenCache::GetAuthData().Token; + RefreshRequest.Data.RefreshToken = DevAuthTokenCache::GetAuthData().RefreshToken; RefreshTokenAsync(RefreshRequest); } -void DeveloperTokenAuthStrategy::OnRefreshTokenResponse(FString Response, bool bWasSuccessful) +void DeveloperTokenAuthStrategy::OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) { - if (!bWasSuccessful) - { - // Log or handle failure here if needed - OnAuthComplete.ExecuteIfBound(false, true); - return; - } - - FRefreshTokenResponse RefreshTokenResponse; - const bool bParsedSuccessfully = FJsonObjectConverter::JsonObjectStringToUStruct(Response, &RefreshTokenResponse, 0, 0); - - if (bParsedSuccessfully && !RefreshTokenResponse.Data.Token.IsEmpty()) + if (bWasSuccessful && !Response.Data.Token.IsEmpty()) { - DevAuthTokenCache::GetAuthData().Token = RefreshTokenResponse.Data.Token; - UE_LOG(LogTemp, Warning, TEXT("DeveloperTokenAuthStrategy Token refreshed successfully: %s"), *RefreshTokenResponse.Data.Token); + FDeveloperAuth DeveloperAuth = DevAuthTokenCache::GetAuthData(); + DeveloperAuth.Token = Response.Data.Token; + DeveloperAuth.RefreshToken = Response.Data.RefreshToken; + DevAuthTokenCache::SetAuthData(DeveloperAuth); + UE_LOG(LogTemp, Log, TEXT("Token refreshed successfully: %s"), *Response.Data.Token); OnAuthComplete.ExecuteIfBound(true, true); + return; } - else - { - UE_LOG(LogTemp, Warning, TEXT("Failed to refresh token")); - OnAuthComplete.ExecuteIfBound(false, true); - } + UE_LOG(LogTemp, Warning, TEXT("Failed to refresh token")); + OnAuthComplete.ExecuteIfBound(false, true); } void DeveloperTokenAuthStrategy::RefreshTokenAsync(const FRefreshTokenRequest& Request) -{ - FApiRequest ApiRequest = FApiRequest(); - ApiRequest.Url = TEXT("https://readyplayer.me/api/auth/refresh"); - ApiRequest.Method = POST; - ApiRequest.Headers.Add(TEXT("Content-Type"), TEXT("application/json")); - ApiRequest.Payload = Request.ToJsonString(); - - AuthWebApi.DispatchRawWithAuth(ApiRequest); +{ + AuthApi.RefreshToken(Request); } diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index c27884a..99a9b44 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -14,6 +14,7 @@ #include "Interfaces/IHttpResponse.h" #include "Widgets/Input/SEditableTextBox.h" #include "IImageWrapperModule.h" +#include "Auth/DeveloperAuthApi.h" #include "Auth/DeveloperLoginRequest.h" #include "Settings/RpmDeveloperSettings.h" @@ -174,6 +175,12 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) void SRpmDeveloperLoginWidget::Initialize() { if(bIsInitialized) return; + + if(!DeveloperAuthApi.IsValid()) + { + DeveloperAuthApi = MakeUnique(); + DeveloperAuthApi->OnLoginResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleLoginResponse); + } if (!AssetApi.IsValid()) { @@ -181,14 +188,14 @@ void SRpmDeveloperLoginWidget::Initialize() AssetApi->OnListAssetsResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleBaseModelListResponse); } - if(!DeveloperApi.IsValid()) + if(!DeveloperAccountApi.IsValid()) { DeveloperTokenAuthStrategy* AuthStrategy = new DeveloperTokenAuthStrategy(); - DeveloperApi = MakeUnique(AuthStrategy); + DeveloperAccountApi = MakeUnique(AuthStrategy); - DeveloperApi->SetAuthenticationStrategy(AuthStrategy); - DeveloperApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); - DeveloperApi->OnApplicationListResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); + DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); + DeveloperAccountApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); + DeveloperAccountApi->OnApplicationListResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); } bIsInitialized = true; if(bIsLoggedIn) @@ -334,10 +341,8 @@ UTexture2D* SRpmDeveloperLoginWidget::CreateTextureFromImageData(const TArrayApiBaseAuthUrl); - TSharedRef Request = FHttpModule::Get().CreateRequest(); - Request->OnProcessRequestComplete().BindSP(this, &SRpmDeveloperLoginWidget::HandleLoginResponse); - Request->SetURL(url); - Request->SetVerb(TEXT("POST")); - Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); - Request->SetContentAsString(LoginRequest.ToJsonString()); - Request->ProcessRequest(); - + DeveloperAuthApi->LoginWithEmail(LoginRequest); return FReply::Handled(); } void SRpmDeveloperLoginWidget::GetOrgList() { FOrganizationListRequest OrgRequest; - DeveloperApi->ListOrganizationsAsync(OrgRequest); + DeveloperAccountApi->ListOrganizationsAsync(OrgRequest); } -void SRpmDeveloperLoginWidget::HandleLoginResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) +void SRpmDeveloperLoginWidget::HandleLoginResponse(const FDeveloperLoginResponse& Response, bool bWasSuccessful) { - if (bWasSuccessful && Response.IsValid() && Response->GetResponseCode() == 200) - { - TSharedPtr ResponseObj; - const TSharedRef> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString()); - - - if (FJsonSerializer::Deserialize(Reader, ResponseObj)) - { - SetLoggedInState(true); - FDeveloperLoginResponse ResponseStruct; - FDeveloperLoginResponse::FromJsonObject(ResponseObj, ResponseStruct); - UserName = ResponseStruct.Data.Name; - DevAuthTokenCache::SetAuthData(ResponseStruct); - FString json = ResponseStruct.ToJsonString(); - - GetOrgList(); - return; - } - UE_LOG(LogTemp, Error, TEXT("Failed to deserialize login response")); - } - else + if(bWasSuccessful) { - UE_LOG(LogTemp, Error, TEXT("Login request failed. %s"), *Response->GetContentAsString()); - DevAuthTokenCache::ClearAuthData(); + UserName = Response.Data.Name; + FDeveloperAuth AuthData = FDeveloperAuth(Response.Data, false); + DevAuthTokenCache::SetAuthData(AuthData); + SetLoggedInState(true); + GetOrgList(); + return; } + UE_LOG(LogTemp, Error, TEXT("Login request failed"),); + DevAuthTokenCache::ClearAuthData(); } void SRpmDeveloperLoginWidget::HandleOrganizationListResponse(const FOrganizationListResponse& Response, @@ -426,7 +409,7 @@ void SRpmDeveloperLoginWidget::HandleOrganizationListResponse(const FOrganizatio FApplicationListRequest Request; Request.Params.Add("organizationId", Response.Data[0].Id); - DeveloperApi->ListApplicationsAsync(Request); + DeveloperAccountApi->ListApplicationsAsync(Request); } else { @@ -440,11 +423,6 @@ void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationL { if (bWasSuccessful) { - UE_LOG(LogTemp, Warning, TEXT("Applications:")); - for (const FApplication& App : Response.Data) - { - UE_LOG(LogTemp, Warning, TEXT(" - %s"), *App.Name); - } TArray Items; for (const FApplication& App : Response.Data) { diff --git a/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h b/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h index 8c1bc98..48531f7 100644 --- a/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h +++ b/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h @@ -61,7 +61,7 @@ class RPMNEXTGENEDITOR_API DevAuthTokenCache { public: static FDeveloperAuth GetAuthData(); - static void SetAuthData(const FDeveloperLoginResponse& Token); + static void SetAuthData(const FDeveloperAuth& DevAuthData); static void ClearAuthData(); private: static constexpr const TCHAR* CacheKeyName = TEXT("Name"); diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperAuthApi.h b/Source/RpmNextGenEditor/Public/Auth/DeveloperAuthApi.h new file mode 100644 index 0000000..e8084b3 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/Auth/DeveloperAuthApi.h @@ -0,0 +1,21 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Common/WebApi.h" + +struct FDeveloperLoginResponse; +struct FDeveloperLoginRequest; + +DECLARE_DELEGATE_TwoParams(FOnDeveloperLoginResponse, const FDeveloperLoginResponse&, bool); + +class RPMNEXTGENEDITOR_API FDeveloperAuthApi : public FWebApi +{ +public: + FDeveloperAuthApi(); + + void HandleLoginResponse(FString JsonData, bool bIsSuccessful) const; + void LoginWithEmail(FDeveloperLoginRequest Request); + FOnDeveloperLoginResponse OnLoginResponse; +private: + FString ApiUrl; +}; diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h b/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h index 6e7d640..9431fe4 100644 --- a/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h +++ b/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h @@ -2,9 +2,8 @@ #include "CoreMinimal.h" #include "Api/Auth/ApiRequest.h" +#include "Api/Auth/AuthApi.h" #include "Api/Auth/IAuthenticationStrategy.h" -#include "Api/Common/WebApiWithAuth.h" -#include "Api/Auth/Models/RefreshTokenRequest.h" struct FRefreshTokenResponse; @@ -14,10 +13,10 @@ class RPMNEXTGENEDITOR_API DeveloperTokenAuthStrategy : public IAuthenticationSt public: DeveloperTokenAuthStrategy(); virtual void AddAuthToRequest(FApiRequest& Request) override; - virtual void OnRefreshTokenResponse(FString Response, bool bWasSuccessful) override; + virtual void OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) override; virtual void TryRefresh(FApiRequest& Request) override; private: void RefreshTokenAsync(const FRefreshTokenRequest& Request); FOnWebApiResponse OnWebApiResponse; - FWebApiWithAuth AuthWebApi; + FAuthApi AuthApi; }; diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h index 078ff58..5c3970e 100644 --- a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h @@ -11,6 +11,8 @@ #include "Widgets/SCompoundWidget.h" #include "Containers/Map.h" +struct FDeveloperLoginResponse; +class FDeveloperAuthApi; class URpmDeveloperSettings; class UDeveloperAuthApi; class SEditableTextBox; @@ -36,7 +38,7 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget void LoadBaseModelList(); - void HandleLoginResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); + void HandleLoginResponse(const FDeveloperLoginResponse& Response, bool bWasSuccessful); void HandleOrganizationListResponse(const FOrganizationListResponse& Response, bool bWasSuccessful); void HandleApplicationListResponse(const FApplicationListResponse& Response, bool bWasSuccessful); @@ -63,7 +65,8 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget bool bIsLoggedIn = false; FString UserName; TUniquePtr AssetApi; - TUniquePtr DeveloperApi; + TUniquePtr DeveloperAccountApi; + TUniquePtr DeveloperAuthApi; static constexpr const TCHAR* CacheKeyEmail = TEXT("Email"); TSharedPtr ContentBox; From 94168fac9d0610607860a68ce80087884ad8c4be Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 8 Aug 2024 12:20:15 +0300 Subject: [PATCH 04/74] chore: refactoring refresh token --- .../Private/Api/Auth/ApiKeyAuthStrategy.cpp | 2 +- .../Private/Api/Common/WebApiWithAuth.cpp | 25 ++++++++++++++----- .../Public/Api/Auth/IAuthenticationStrategy.h | 4 ++- .../Public/Api/Common/WebApiWithAuth.h | 3 ++- .../Auth/DeveloperTokenAuthStrategy.cpp | 16 ++++++++---- .../Private/SRpmDeveloperLoginWidget.cpp | 2 ++ 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp index 8d857cf..a6de451 100644 --- a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp +++ b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp @@ -11,7 +11,7 @@ void FApiKeyAuthStrategy::AddAuthToRequest(FApiRequest& Request) { URpmDeveloperSettings *Settings = GetMutableDefault(); Request.Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); - OnAuthComplete.ExecuteIfBound(true, false); + OnAuthComplete.ExecuteIfBound(true); } void FApiKeyAuthStrategy::OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp index 5287202..24e87a7 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -16,22 +16,35 @@ void FWebApiWithAuth::SetAuthenticationStrategy(IAuthenticationStrategy* InAuthe { AuthenticationStrategy = InAuthenticationStrategy; AuthenticationStrategy->OnAuthComplete.BindRaw(this, &FWebApiWithAuth::OnAuthComplete); + AuthenticationStrategy->OnTokenRefreshed.BindRaw(this, &FWebApiWithAuth::OnAuthTokenRefreshed); } -void FWebApiWithAuth::OnAuthComplete(bool bWasSuccessful, bool bWasRefreshed) +void FWebApiWithAuth::OnAuthComplete(bool bWasSuccessful) { - if(bWasSuccessful) + if(bWasSuccessful && ApiRequestData != nullptr) { DispatchRaw(*ApiRequestData); return; } - if(!bWasRefreshed) + UE_LOG(LogTemp, Warning, TEXT("Auth failed")); + OnApiResponse.ExecuteIfBound(TEXT("Auth failed"), false); +} + +void FWebApiWithAuth::OnAuthTokenRefreshed(const FRefreshTokenResponseBody& Response, bool bWasSuccessful) +{ + if(bWasSuccessful) { - UE_LOG(LogTemp, Log, TEXT("Auth failed, trying to refresh")); - AuthenticationStrategy->TryRefresh(*ApiRequestData); + const FString Key = TEXT("Authorization"); + if (ApiRequestData->Headers.Contains(Key)) + { + ApiRequestData->Headers.Remove(Key); + } + ApiRequestData->Headers.Add(Key, FString::Printf(TEXT("Bearer %s"), *Response.Token)); + UE_LOG(LogTemp, Log, TEXT("Auth refreshed, running request")); + DispatchRaw(*ApiRequestData); return; } - UE_LOG(LogTemp, Warning, TEXT("Auth failed")); + OnApiResponse.ExecuteIfBound(TEXT("Auth failed"), false); } diff --git a/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h index 6cf6c53..434710d 100644 --- a/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h +++ b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h @@ -4,7 +4,8 @@ #include "Api/Auth/ApiRequest.h" #include "Models/RefreshTokenResponse.h" -DECLARE_DELEGATE_TwoParams(FOnAuthComplete, bool, bool); +DECLARE_DELEGATE_OneParam(FOnAuthComplete, bool); +DECLARE_DELEGATE_TwoParams(FOnTokenRefreshed, const FRefreshTokenResponseBody&, bool); class RPMNEXTGEN_API IAuthenticationStrategy { @@ -14,4 +15,5 @@ class RPMNEXTGEN_API IAuthenticationStrategy virtual void TryRefresh(FApiRequest& Request) = 0; virtual void OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) = 0; FOnAuthComplete OnAuthComplete; + FOnTokenRefreshed OnTokenRefreshed; }; \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h index 62f0163..3507301 100644 --- a/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h +++ b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h @@ -18,7 +18,8 @@ class RPMNEXTGEN_API FWebApiWithAuth : public FWebApi // const TFApiRequest& Data // ); - void OnAuthComplete(bool bWasSuccessful, bool bWasRefreshed); + void OnAuthComplete(bool bWasSuccessful); + void OnAuthTokenRefreshed(const FRefreshTokenResponseBody& Response, bool bWasSuccessful); void DispatchRawWithAuth(FApiRequest& Data); protected: diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp index 7340d13..904f51f 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp @@ -13,8 +13,14 @@ DeveloperTokenAuthStrategy::DeveloperTokenAuthStrategy() void DeveloperTokenAuthStrategy::AddAuthToRequest(FApiRequest& Request) { - Request.Headers.Add(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *DevAuthTokenCache::GetAuthData().Token)); - OnAuthComplete.ExecuteIfBound(true, false); + const FString Key = TEXT("Authorization"); + if (Request.Headers.Contains(Key)) + { + Request.Headers.Remove(Key); + } + Request.Headers.Add(Key, FString::Printf(TEXT("Bearer %s"), *DevAuthTokenCache::GetAuthData().Token)); + + OnAuthComplete.ExecuteIfBound(true); } void DeveloperTokenAuthStrategy::TryRefresh(FApiRequest& Request) @@ -34,12 +40,12 @@ void DeveloperTokenAuthStrategy::OnRefreshTokenResponse(const FRefreshTokenRespo DeveloperAuth.Token = Response.Data.Token; DeveloperAuth.RefreshToken = Response.Data.RefreshToken; DevAuthTokenCache::SetAuthData(DeveloperAuth); - UE_LOG(LogTemp, Log, TEXT("Token refreshed successfully: %s"), *Response.Data.Token); - OnAuthComplete.ExecuteIfBound(true, true); + UE_LOG(LogTemp, Log, TEXT("Token refreshed successfully: %s"), *DeveloperAuth.Token); + OnTokenRefreshed.ExecuteIfBound(Response.Data, true); return; } UE_LOG(LogTemp, Warning, TEXT("Failed to refresh token")); - OnAuthComplete.ExecuteIfBound(false, true); + OnTokenRefreshed.ExecuteIfBound(Response.Data, false); } diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 99a9b44..5c5b8e3 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -23,6 +23,8 @@ BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) { FDeveloperAuth AuthData = DevAuthTokenCache::GetAuthData(); + DevAuthTokenCache::SetAuthData(AuthData); + bIsLoggedIn = AuthData.IsValid(); Settings = GetDefault(); UserName = AuthData.Name; From 26efd269bad4ca1e487fd189f3430fd292188e9a Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Fri, 9 Aug 2024 12:27:59 +0300 Subject: [PATCH 05/74] feat: refactoring and added loader widget to help testing --- RpmNextGen.uplugin | 10 ++ .../Private/Api/Assets/AssetLoader.cpp | 8 +- .../Public/Api/Assets/AssetLoader.h | 4 +- .../Private/CharacterLoaderWidget.cpp | 128 +++++++++++++++++ .../Private/EditorAssetLoader.cpp | 15 +- .../Private/RpmNextGenEditor.cpp | 129 ++++++++++++++---- .../Private/SRpmDeveloperLoginWidget.cpp | 104 ++++++++------ .../Public/CharacterLoaderWidget.h | 40 ++++++ .../Public/EditorAssetLoader.h | 4 +- .../Public/LoaderWindowCommands.cpp | 9 ++ .../Public/LoaderWindowCommands.h | 18 +++ .../Public/RpmNextGenEditor.h | 4 +- .../RpmNextGenEditor.Build.cs | 4 +- 13 files changed, 396 insertions(+), 81 deletions(-) create mode 100644 Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp create mode 100644 Source/RpmNextGenEditor/Public/CharacterLoaderWidget.h create mode 100644 Source/RpmNextGenEditor/Public/LoaderWindowCommands.cpp create mode 100644 Source/RpmNextGenEditor/Public/LoaderWindowCommands.h diff --git a/RpmNextGen.uplugin b/RpmNextGen.uplugin index d336f5b..e993a9a 100644 --- a/RpmNextGen.uplugin +++ b/RpmNextGen.uplugin @@ -37,5 +37,15 @@ "Linux" ] } + ], + "Plugins": [ + { + "Name": "glTFRuntime", + "Enabled": true + }, + { + "Name": "TransientObjectSaver", + "Enabled": true + } ] } \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp index dd84148..775bd1c 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp @@ -47,7 +47,7 @@ void FAssetLoader::OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr if (bWasSuccessful && Response.IsValid()) { TArray Content = Response->GetContent(); - OnAssetDataReceived.ExecuteIfBound(true, Content); + OnAssetDataReceived.ExecuteIfBound(Content, true); FString FileName = FPaths::GetCleanFilename(Request->GetURL()); FString FilePath = DownloadDirectory / FileName; @@ -57,7 +57,7 @@ void FAssetLoader::OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr { UE_LOG(LogTemp, Log, TEXT("Downloaded GLB file to %s"), *FilePath); UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(Content, *GltfConfig); - OnAssetDownloaded.ExecuteIfBound(true, FilePath, gltfAsset); + OnAssetDownloaded.ExecuteIfBound(FilePath, gltfAsset, true); } else { @@ -69,8 +69,8 @@ void FAssetLoader::OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr { UE_LOG(LogTemp, Error, TEXT("Failed to download GLB file from URL")); } - OnAssetDataReceived.ExecuteIfBound(false, TArray()); - OnAssetDownloaded.ExecuteIfBound(false, FString(), nullptr); + OnAssetDataReceived.ExecuteIfBound(TArray(), false); + OnAssetDownloaded.ExecuteIfBound(FString(), nullptr, false); } diff --git a/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h index a862bcb..1c7ec89 100644 --- a/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h +++ b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h @@ -8,8 +8,8 @@ struct FglTFRuntimeConfig; class UglTFRuntimeAsset; -DECLARE_DELEGATE_TwoParams(FOnAssetDataReceived, bool, TArray); -DECLARE_DELEGATE_ThreeParams(FOnAssetDownloaded, bool, FString, UglTFRuntimeAsset*); +DECLARE_DELEGATE_TwoParams(FOnAssetDataReceived, TArray, bool); +DECLARE_DELEGATE_ThreeParams(FOnAssetDownloaded, FString, UglTFRuntimeAsset*, bool); class RPMNEXTGEN_API FAssetLoader : public FWebApi { diff --git a/Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp b/Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp new file mode 100644 index 0000000..6f6bd76 --- /dev/null +++ b/Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp @@ -0,0 +1,128 @@ +#include "CharacterLoaderWidget.h" + +#include "Widgets/Input/SEditableTextBox.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Text/STextBlock.h" +#include "EditorStyleSet.h" +#include "glTFRuntimeFunctionLibrary.h" +#include "Api/Assets/AssetLoader.h" +#include "PropertyCustomizationHelpers.h" +#include "AssetRegistry/AssetData.h" + +// Configuration section and key names +static const FString ConfigSection = TEXT("/Script/ReadyPlayerMe.CharacterLoaderSettings"); +static const FString PathKeyName = TEXT("LastFilePath"); + +void SCharacterLoaderWidget::Construct(const FArguments& InArgs) +{ + FString LastSavedPath; + GConfig->GetString(*ConfigSection, *PathKeyName, LastSavedPath, GGameIni); + PathText = FText::FromString(LastSavedPath); + AssetLoader = FEditorAssetLoader(); + ChildSlot + [ + SNew(SVerticalBox) + + + SVerticalBox::Slot() + .AutoHeight() + .Padding(5) + [ + SNew(STextBlock) + .Text(FText::FromString("Enter Path:")) + ] + + + SVerticalBox::Slot() + .AutoHeight() + .Padding(5) + [ + SAssignNew(PathTextBox, SEditableTextBox) + .Text(FText::FromString(LastSavedPath)) // Set the loaded path as the initial text + .OnTextChanged(this, &SCharacterLoaderWidget::OnPathTextChanged) + ] + + + SVerticalBox::Slot() + .AutoHeight() + .Padding(5) + [ + SNew(SButton) + .Text(FText::FromString("Load Glb")) + .OnClicked(this, &SCharacterLoaderWidget::OnButtonClick) + ] + // Add a text block for labeling + + SVerticalBox::Slot() + .AutoHeight() + .Padding(5) + [ + SNew(STextBlock) + .Text(FText::FromString("Select Skeleton:")) + ] + + // Add the object property entry box + + SVerticalBox::Slot() + .AutoHeight() + .Padding(5) + [ + SNew(SObjectPropertyEntryBox) + .AllowedClass(USkeleton::StaticClass()) // Only allow USkeleton assets + .OnObjectChanged(this, &SCharacterLoaderWidget::OnSkeletonSelected) // Handle the asset selection + .ObjectPath(this, &SCharacterLoaderWidget::GetCurrentSkeletonPath) // Optionally provide a current value + ] + ]; +} + +// This function will be called when the user selects a skeleton +void SCharacterLoaderWidget::OnSkeletonSelected(const FAssetData& AssetData) +{ + SelectedSkeleton = Cast(AssetData.GetAsset()); + if (SelectedSkeleton) + { + UE_LOG(LogTemp, Log, TEXT("Selected Skeleton: %s"), *SelectedSkeleton->GetName()); + } +} + +// Optional: Provide the current path of the selected skeleton +FString SCharacterLoaderWidget::GetCurrentSkeletonPath() const +{ + return SelectedSkeleton ? SelectedSkeleton->GetPathName() : FString(); +} + +void SCharacterLoaderWidget::OnPathTextChanged(const FText& NewText) +{ + PathText = NewText; + + // Save the path to the configuration file + GConfig->SetString(*ConfigSection, *PathKeyName, *PathText.ToString(), GGameIni); + GConfig->Flush(false, GGameIni); // Ensure the config is saved immediately +} + + + +FReply SCharacterLoaderWidget::OnButtonClick() +{ + FString Path = PathText.ToString(); + if(Path.IsEmpty()) + { + UE_LOG(LogTemp, Error, TEXT("Path is empty")); + return FReply::Handled(); + } + LoadAsset(Path); + + return FReply::Handled(); +} + +void SCharacterLoaderWidget::LoadAsset(const FString& Path) +{ + FglTFRuntimeConfig Config = FglTFRuntimeConfig(); + Config.SceneScale = 100.0f; + Config.TransformBaseType = EglTFRuntimeTransformBaseType::YForward; + UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromFilename(Path, true, Config); + if(SelectedSkeleton) + { + AssetLoader.SkeletonToCopy = SelectedSkeleton; + } + AssetLoader.LoadGltfAssetToWorld(gltfAsset); + AssetLoader.SaveAsUAsset(gltfAsset, TEXT("/Game/ReadyPlayerMe/TestSkeleton")); + //FglTFRuntimeSkeletonConfig skeletonConfig = FglTFRuntimeSkeletonConfig(); + //USkeleton* Skeleton = gltfAsset->LoadSkeleton(0, skeletonConfig); + //UTransientObjectSaverLibrary::SaveTransientSkeleton(Skeleton,TEXT("/Game/ReadyPlayerMe/TestSkeleton")); +} diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index eb67a8a..364764d 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -14,7 +14,7 @@ FEditorAssetLoader::~FEditorAssetLoader() { } -void FEditorAssetLoader::OnAssetDownloadComplete(bool bWasSuccessful, FString FilePath, UglTFRuntimeAsset* gltfAsset) +void FEditorAssetLoader::OnAssetDownloadComplete(FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful) { if(bWasSuccessful) { @@ -49,6 +49,10 @@ void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) { NewActor->SetFlags(RF_Transient); NewActor->Asset = gltfAsset; + if(SkeletonToCopy) + { + NewActor->SkeletalMeshConfig.SkeletonConfig.CopyRotationsFrom = SkeletonToCopy; + } NewActor->bAllowSkeletalAnimations = false; NewActor->bAllowNodeAnimations = false; NewActor->StaticMeshConfig.bGenerateStaticMeshDescription = true; @@ -80,6 +84,15 @@ void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) void FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString Path) { FglTFRuntimeSkeletonConfig skeletonConfig = FglTFRuntimeSkeletonConfig(); + if(SkeletonToCopy) + { + skeletonConfig.CopyRotationsFrom = SkeletonToCopy; + } USkeleton* Skeleton = gltfAsset->LoadSkeleton(0, skeletonConfig); UTransientObjectSaverLibrary::SaveTransientSkeleton(Skeleton,TEXT("/Game/ReadyPlayerMe/TestSkeleton")); + // FglTFRuntimeSkeletalMeshConfig meshConfig = FglTFRuntimeSkeletalMeshConfig(); + // USkeletalMesh* skeletalMesh = gltfAsset->LoadSkeletalMeshRecursive("", {}, meshConfig); + // const FTransientObjectSaverMaterialNameGenerator& MaterialNameGenerator = FTransientObjectSaverMaterialNameGenerator(); + // const FTransientObjectSaverTextureNameGenerator& TextureNameGenerator = FTransientObjectSaverTextureNameGenerator(); + // UTransientObjectSaverLibrary::SaveTransientSkeletalMesh(skeletalMesh, TEXT("/Game/ReadyPlayerMe/TestSkeletalMesh"), TEXT("/Game/ReadyPlayerMe/TestSkeleton"), TEXT("/Game/ReadyPlayerMe/TestPhysicsAsset"), MaterialNameGenerator, TextureNameGenerator); } diff --git a/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp index a2d4af2..9073e7f 100644 --- a/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp +++ b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp @@ -1,7 +1,10 @@ // Copyright Epic Games, Inc. All Rights Reserved. #include "RpmNextGenEditor.h" + +#include "CharacterLoaderWidget.h" #include "LevelEditor.h" +#include "LoaderWindowCommands.h" #include "LoginWindowCommands.h" #include "SRpmDeveloperLoginWidget.h" #include "Widgets/Docking/SDockTab.h" @@ -12,16 +15,16 @@ static const FName TestWindowTabName("LoginWindow"); #define LOCTEXT_NAMESPACE "RpmNextGenEditorModule" +static const FName NewWindowTabName("CustomEditorWindow"); void FRpmNextGenEditorModule::StartupModule() { - // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - FLoginWindowStyle::Initialize(); FLoginWindowStyle::ReloadTextures(); FLoginWindowCommands::Register(); - + FLoaderWindowCommands::Register(); // Don't forget to register the other command set + PluginCommands = MakeShareable(new FUICommandList); PluginCommands->MapAction( @@ -29,27 +32,76 @@ void FRpmNextGenEditorModule::StartupModule() FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::PluginButtonClicked), FCanExecuteAction()); + PluginCommands->MapAction( + FLoaderWindowCommands::Get().OpenPluginWindow, + FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::OpenLoaderWindow), + FCanExecuteAction()); + UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FRpmNextGenEditorModule::RegisterMenus)); - + FGlobalTabmanager::Get()->RegisterNomadTabSpawner(TestWindowTabName, FOnSpawnTab::CreateRaw(this, &FRpmNextGenEditorModule::OnSpawnPluginTab)) .SetDisplayName(LOCTEXT("FRpmDevLoginWindow", "RPM Dev Login")) .SetMenuType(ETabSpawnerMenuType::Hidden); + + FGlobalTabmanager::Get()->RegisterNomadTabSpawner(NewWindowTabName, FOnSpawnTab::CreateRaw(this, &FRpmNextGenEditorModule::OnSpawnLoaderWindow)) + .SetDisplayName(LOCTEXT("CustomEditorWindow", "Custom Editor Window")) + .SetMenuType(ETabSpawnerMenuType::Hidden); } -void FRpmNextGenEditorModule::ShutdownModule() +void FRpmNextGenEditorModule::RegisterMenus() { - // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, - // we call this function before unloading the module. + FToolMenuOwnerScoped OwnerScoped(this); - UToolMenus::UnRegisterStartupCallback(this); + // Create a new main menu entry called "ReadyPlayerMe" + UToolMenu* MainMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu"); + + // Add a new top-level menu "Ready Player Me" + FToolMenuSection& Section = MainMenu->AddSection("ReadyPlayerMeTopMenu", LOCTEXT("ReadyPlayerMeMenuSection", "Ready Player Me")); + + // Add a sub-menu for "Ready Player Me" + FToolMenuEntry& SubMenuEntry = Section.AddSubMenu( + "ReadyPlayerMe", + LOCTEXT("ReadyPlayerMeMenu", "Ready Player Me"), + LOCTEXT("ReadyPlayerMeMenu_ToolTip", "Open Ready Player Me tools"), + FNewToolMenuDelegate::CreateRaw(this, &FRpmNextGenEditorModule::FillReadyPlayerMeMenu), + false, // Don't open on hover + FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.User") // Optional icon for the top-level menu + ); +} +void FRpmNextGenEditorModule::FillReadyPlayerMeMenu(UToolMenu* Menu) +{ + FToolMenuSection& Section = Menu->AddSection("ReadyPlayerMeSubSection"); + + Section.AddMenuEntry( + "OpenLoginWindow", + LOCTEXT("OpenLoginWindow", "Developer Window"), + LOCTEXT("OpenLoginWindowToolTip", "Open the RPM Developer Window."), + FSlateIcon(), + FUIAction(FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::PluginButtonClicked)) + ); + + Section.AddMenuEntry( + "OpenLoaderWindow", + LOCTEXT("OpenLoaderWindow", "Glb Loader"), + LOCTEXT("OpenLoaderWindowToolTip", "Glb Loader Window."), + FSlateIcon(), + FUIAction(FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::OpenLoaderWindow)) + ); +} + +void FRpmNextGenEditorModule::ShutdownModule() +{ + UToolMenus::UnRegisterStartupCallback(this); UToolMenus::UnregisterOwner(this); FLoginWindowStyle::Shutdown(); FLoginWindowCommands::Unregister(); + FLoaderWindowCommands::Unregister(); // Unregister custom commands FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(TestWindowTabName); + FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(NewWindowTabName); } TSharedRef FRpmNextGenEditorModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) @@ -65,6 +117,16 @@ TSharedRef FRpmNextGenEditorModule::OnSpawnPluginTab(const FSpawnTabAr [ SNew(SRpmDeveloperLoginWidget) ]; + +} + +TSharedRef FRpmNextGenEditorModule::OnSpawnLoaderWindow(const FSpawnTabArgs& SpawnTabArgs) +{ + return SNew(SDockTab) + .TabRole(NomadTab) + [ + SNew(SCharacterLoaderWidget) + ]; } void FRpmNextGenEditorModule::PluginButtonClicked() @@ -72,29 +134,36 @@ void FRpmNextGenEditorModule::PluginButtonClicked() FGlobalTabmanager::Get()->TryInvokeTab(TestWindowTabName); } -void FRpmNextGenEditorModule::RegisterMenus() +// void FRpmNextGenEditorModule::RegisterMenus() +// { +// // Owner will be used for cleanup in call to UToolMenus::UnregisterOwner +// FToolMenuOwnerScoped OwnerScoped(this); +// +// // Create a new main menu entry +// UToolMenu* MainMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu"); +// +// // Add a new section to the main menu for ReadyPlayerMe +// FToolMenuSection& Section = MainMenu->AddSection("ReadyPlayerMe", LOCTEXT("ReadyPlayerMeMenuSection", "Ready Player Me")); +// +// // Add menu entries to your custom section +// Section.AddMenuEntryWithCommandList(FLoginWindowCommands::Get().OpenPluginWindow, PluginCommands); +// Section.AddMenuEntryWithCommandList(FLoaderWindowCommands::Get().OpenPluginWindow, PluginCommands); +// +// // Add the section to the toolbar as well, if you want it to appear in the toolbar +// UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar"); +// { +// FToolMenuSection& ToolbarSection = ToolbarMenu->FindOrAddSection("ReadyPlayerMe"); +// { +// FToolMenuEntry& Entry = ToolbarSection.AddEntry(FToolMenuEntry::InitToolBarButton(FLoginWindowCommands::Get().OpenPluginWindow)); +// Entry.SetCommandList(PluginCommands); +// } +// } +// } + + +void FRpmNextGenEditorModule::OpenLoaderWindow() { - // Owner will be used for cleanup in call to UToolMenus::UnregisterOwner - FToolMenuOwnerScoped OwnerScoped(this); - - { - UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window"); - { - FToolMenuSection& Section = Menu->FindOrAddSection("WindowLayout"); - Section.AddMenuEntryWithCommandList(FLoginWindowCommands::Get().OpenPluginWindow, PluginCommands); - } - } - - { - UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar"); - { - FToolMenuSection& Section = ToolbarMenu->FindOrAddSection("Settings"); - { - FToolMenuEntry& Entry = Section.AddEntry(FToolMenuEntry::InitToolBarButton(FLoginWindowCommands::Get().OpenPluginWindow)); - Entry.SetCommandList(PluginCommands); - } - } - } + FGlobalTabmanager::Get()->TryInvokeTab(NewWindowTabName); } #undef LOCTEXT_NAMESPACE diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 5c5b8e3..119de91 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -17,6 +17,7 @@ #include "Auth/DeveloperAuthApi.h" #include "Auth/DeveloperLoginRequest.h" #include "Settings/RpmDeveloperSettings.h" +#include "Widgets/Layout/SScrollBox.h" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION @@ -160,52 +161,31 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) SNew(STextBlock) .Text(FText::FromString("Here you can import your character styles from Studio")) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) - ] + ] + SVerticalBox::Slot() - .AutoHeight() + .Padding(10) + .FillHeight(1.0f) // Allows the scroll box to take up remaining space [ - - SAssignNew(ContentBox, SVerticalBox) + SNew(SScrollBox) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) - ] + + SScrollBox::Slot() + [ + SAssignNew(ContentBox, SVerticalBox) + ] + ] + // + SVerticalBox::Slot() + // .AutoHeight() + // [ + // + // SAssignNew(ContentBox, SVerticalBox) + // .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) + // ] ]; EmailTextBox->SetText(FText::FromString(EditorCache::GetString(CacheKeyEmail))); Initialize(); } -void SRpmDeveloperLoginWidget::Initialize() -{ - if(bIsInitialized) return; - - if(!DeveloperAuthApi.IsValid()) - { - DeveloperAuthApi = MakeUnique(); - DeveloperAuthApi->OnLoginResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleLoginResponse); - } - - if (!AssetApi.IsValid()) - { - AssetApi = MakeUnique(); - AssetApi->OnListAssetsResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleBaseModelListResponse); - - } - if(!DeveloperAccountApi.IsValid()) - { - DeveloperTokenAuthStrategy* AuthStrategy = new DeveloperTokenAuthStrategy(); - DeveloperAccountApi = MakeUnique(AuthStrategy); - - DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); - DeveloperAccountApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); - DeveloperAccountApi->OnApplicationListResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); - } - bIsInitialized = true; - if(bIsLoggedIn) - { - GetOrgList(); - } -} - void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) { TSharedPtr ImageWidget; @@ -245,13 +225,17 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) ] ] ] - + SHorizontalBox::Slot() + + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Top) .Padding(10, 10, 0, 0) // Padding from the left side of the Image & Button stack [ - SNew(STextBlock) + SNew(SEditableText) .Text(FText::FromString(FString::Printf(TEXT("ID: %s"), *StyleAsset.Id))) + .IsReadOnly(true) // Prevents the text from being editable + .IsCaretMovedWhenGainFocus(false) // Caret won't appear when focused, keeping it look like plain text + .SelectAllTextWhenFocused(false) // Prevents selecting all text when focused + .MinDesiredWidth(100.0f) // Minimum width for the text box ] ]; @@ -264,6 +248,38 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) }); } +void SRpmDeveloperLoginWidget::Initialize() +{ + if(bIsInitialized) return; + + if(!DeveloperAuthApi.IsValid()) + { + DeveloperAuthApi = MakeUnique(); + DeveloperAuthApi->OnLoginResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleLoginResponse); + } + + if (!AssetApi.IsValid()) + { + AssetApi = MakeUnique(); + AssetApi->OnListAssetsResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleBaseModelListResponse); + + } + if(!DeveloperAccountApi.IsValid()) + { + DeveloperTokenAuthStrategy* AuthStrategy = new DeveloperTokenAuthStrategy(); + DeveloperAccountApi = MakeUnique(AuthStrategy); + + DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); + DeveloperAccountApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); + DeveloperAccountApi->OnApplicationListResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); + } + bIsInitialized = true; + if(bIsLoggedIn) + { + GetOrgList(); + } +} + void SRpmDeveloperLoginWidget::DownloadImage(const FString& Url, TFunction Callback) { TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); @@ -344,8 +360,8 @@ UTexture2D* SRpmDeveloperLoginWidget::CreateTextureFromImageData(const TArrayClearChildren(); + } DevAuthTokenCache::ClearAuthData(); SetLoggedInState(false); return FReply::Handled(); @@ -495,6 +516,7 @@ void SRpmDeveloperLoginWidget::HandleBaseModelListResponse(const FAssetListRespo for (FAsset Asset : Response.Data) { CharacterStyleAssets.Add(Asset.Id, Asset); + UE_LOG(LogTemp, Error, TEXT("Asset ID: %s"), *Asset.Id); AddCharacterStyle(Asset); } } diff --git a/Source/RpmNextGenEditor/Public/CharacterLoaderWidget.h b/Source/RpmNextGenEditor/Public/CharacterLoaderWidget.h new file mode 100644 index 0000000..b4a61ce --- /dev/null +++ b/Source/RpmNextGenEditor/Public/CharacterLoaderWidget.h @@ -0,0 +1,40 @@ +#pragma once + +#include "CoreMinimal.h" +#include "EditorAssetLoader.h" +#include "Api/Assets/AssetLoader.h" +#include "Widgets/SCompoundWidget.h" +#include "Widgets/DeclarativeSyntaxSupport.h" + +class FEditorAssetLoader; +class FAssetLoader; + +class SCharacterLoaderWidget : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SCharacterLoaderWidget) {} + SLATE_END_ARGS() + + void OnSkeletonSelected(const FAssetData& AssetData); + /** Constructs this widget with InArgs */ + void Construct(const FArguments& InArgs); + +private: + /** Callback for when the button is clicked */ + FReply OnButtonClick(); + + /** Stores the text input by the user */ + FText PathText; + + /** Callback for when the text in the input field changes */ + void OnPathTextChanged(const FText& NewText); + + /** Function that gets called when the button is pressed */ + void LoadAsset(const FString& Path); + FEditorAssetLoader AssetLoader; + TSharedPtr PathTextBox; + + // Store the selected skeleton + USkeleton* SelectedSkeleton; + FString GetCurrentSkeletonPath() const; +}; diff --git a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h index f830e2c..ac2e63d 100644 --- a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h +++ b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h @@ -1,18 +1,20 @@ #pragma once #include "CoreMinimal.h" +#include "TransientObjectSaverLibrary.h" #include "Api/Assets/AssetLoader.h" #include "HAL/PlatformFilemanager.h" class RPMNEXTGENEDITOR_API FEditorAssetLoader : public FAssetLoader { public: - void OnAssetDownloadComplete(bool bArg, FString String, UglTFRuntimeAsset* UglTFRuntimeAsset); + void OnAssetDownloadComplete(FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful); FEditorAssetLoader(); virtual ~FEditorAssetLoader() override; void LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset); void SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString Path); + USkeleton* SkeletonToCopy; }; diff --git a/Source/RpmNextGenEditor/Public/LoaderWindowCommands.cpp b/Source/RpmNextGenEditor/Public/LoaderWindowCommands.cpp new file mode 100644 index 0000000..657ce9d --- /dev/null +++ b/Source/RpmNextGenEditor/Public/LoaderWindowCommands.cpp @@ -0,0 +1,9 @@ +#include "LoaderWindowCommands.h" + +#define LOCTEXT_NAMESPACE "FRpmNextGenEditorModule" + +void FLoaderWindowCommands::RegisterCommands() +{ + UI_COMMAND(OpenPluginWindow, "Open Loader Window", "Open the custom loader window", EUserInterfaceActionType::Button, FInputGesture()); +} +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/RpmNextGenEditor/Public/LoaderWindowCommands.h b/Source/RpmNextGenEditor/Public/LoaderWindowCommands.h new file mode 100644 index 0000000..1a69094 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/LoaderWindowCommands.h @@ -0,0 +1,18 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Framework/Commands/Commands.h" + +class FLoaderWindowCommands : public TCommands +{ +public: + FLoaderWindowCommands() + : TCommands(TEXT("LoaderWindow"), NSLOCTEXT("Contexts", "LoaderWindow", "Loader Window Plugin"), NAME_None, FEditorStyle::GetStyleSetName()) + { + } + + virtual void RegisterCommands() override; + +public: + TSharedPtr OpenPluginWindow; +}; diff --git a/Source/RpmNextGenEditor/Public/RpmNextGenEditor.h b/Source/RpmNextGenEditor/Public/RpmNextGenEditor.h index 0f2cd2e..96034cf 100644 --- a/Source/RpmNextGenEditor/Public/RpmNextGenEditor.h +++ b/Source/RpmNextGenEditor/Public/RpmNextGenEditor.h @@ -19,7 +19,9 @@ class RPMNEXTGENEDITOR_API FRpmNextGenEditorModule : public IModuleInterface private: void RegisterMenus(); - + void FillReadyPlayerMeMenu(UToolMenu* Menu); + void OpenLoaderWindow(); + TSharedRef OnSpawnLoaderWindow(const FSpawnTabArgs& SpawnTabArgs); TSharedRef OnSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs); TSharedPtr PluginCommands; diff --git a/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs b/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs index 36bea51..9235a3f 100644 --- a/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs +++ b/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs @@ -32,7 +32,9 @@ public RpmNextGenEditor(ReadOnlyTargetRules Target) : base(Target) "ImageWrapper", "EditorStyle", "glTFRuntime", - "TransientObjectSaver" + "TransientObjectSaver", + "UnrealEd", + "PropertyEditor", } ); From 54d2b8a9b4c297b45c31d7de025a039efb14678c Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Fri, 9 Aug 2024 13:23:17 +0300 Subject: [PATCH 06/74] feat: added configurable settings --- Source/RpmNextGen/Private/RpmActor.cpp | 39 +++++++++++++++++++++----- Source/RpmNextGen/Public/RpmActor.h | 13 +++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index 033d8fe..c631a62 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -4,6 +4,7 @@ #include "glTFRuntimeAsset.h" #include "glTFRuntimeFunctionLibrary.h" #include "HttpModule.h" +#include "Animation/AnimInstance.h" #include "Api/Characters/CharacterApi.h" #include "Api/Characters/Models/RpmCharacter.h" #include "Interfaces/IHttpResponse.h" @@ -54,7 +55,7 @@ void ARpmActor::CreateCharacter(FString AppId) //TODO update to fetch first app id FCharacterCreateRequest CharacterCreateRequest = FCharacterCreateRequest(); CharacterCreateRequest.Data.Assets = TMap(); - CharacterCreateRequest.Data.Assets.Add("baseModel", "663a61055134180105bcdf0d"); + CharacterCreateRequest.Data.Assets.Add("baseModel", BaseModelId); CharacterCreateRequest.Data.ApplicationId = AppId; CharacterApi->CreateAsync(CharacterCreateRequest); @@ -62,6 +63,14 @@ void ARpmActor::CreateCharacter(FString AppId) USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* SkeletalMesh, const FString& Name) { + if(AddedMeshComponents.Num() > 0) + { + for (auto AddedMeshComponent : AddedMeshComponents) + { + AddedMeshComponent->DestroyComponent(); + } + AddedMeshComponents.Empty(); + } USkeletalMeshComponent* NewSkeletalMeshComponent = NewObject(this, *Name); NewSkeletalMeshComponent->SetupAttachment(RootComponent); NewSkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); @@ -69,14 +78,30 @@ USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* Sk NewSkeletalMeshComponent->RegisterComponent(); AddInstanceComponent(NewSkeletalMeshComponent); AddedMeshComponents.Add(NewSkeletalMeshComponent); + + if (AddedMeshComponents.Num() == 1 && AddedMeshComponents.IsValidIndex(0)) + { + // Get the SkeletalMeshComponent at index 0 + USkeletalMeshComponent* MeshComponent = AddedMeshComponents[0]; + + // Check if the MeshComponent is valid + if (MeshComponent && TargetAnimBP) + { + // Set the animation blueprint class + MeshComponent->SetAnimInstanceClass(TargetAnimBP); + } + } + else if(AddedMeshComponents.Num() > 1 && AddedMeshComponents.IsValidIndex(0)) + { + NewSkeletalMeshComponent->SetMasterPoseComponent(AddedMeshComponents[0]); + } + return NewSkeletalMeshComponent; } void ARpmActor::LoadglTFAsset(UglTFRuntimeAsset* Asset, const FString& AssetName) { - - FglTFRuntimeSkeletalMeshConfig* SkeletalMeshConfig = new FglTFRuntimeSkeletalMeshConfig(); - Asset->LoadSkeletalMeshRecursiveAsync("", {}, OnSkeletalMeshCallback, *SkeletalMeshConfig ); + Asset->LoadSkeletalMeshRecursiveAsync("", {}, OnSkeletalMeshCallback, SkeletalMeshConfig ); } void ARpmActor::HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh) @@ -91,9 +116,9 @@ void ARpmActor::OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedP if (bIsSuccessful) { UE_LOG(LogTemp, Warning, TEXT("Request success from url %s "), *HttpRequest->GetURL()); - FglTFRuntimeConfig Config; - Config.TransformBaseType = EglTFRuntimeTransformBaseType::YForward; - UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(HttpResponse->GetContent(),Config); + + glTFRuntimeConfig.TransformBaseType = EglTFRuntimeTransformBaseType::YForward; + UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(HttpResponse->GetContent(), glTFRuntimeConfig); LoadglTFAsset(gltfAsset, "Asset"); } else diff --git a/Source/RpmNextGen/Public/RpmActor.h b/Source/RpmNextGen/Public/RpmActor.h index fd2b1bc..dab6a4b 100644 --- a/Source/RpmNextGen/Public/RpmActor.h +++ b/Source/RpmNextGen/Public/RpmActor.h @@ -50,6 +50,19 @@ class RPMNEXTGEN_API ARpmActor : public AActor UFUNCTION(BlueprintCallable, Category = "Ready Player Me") void CreateCharacter(FString AppId); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Ready Player Me") + FglTFRuntimeSkeletalMeshConfig SkeletalMeshConfig; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Ready Player Me") + FglTFRuntimeConfig glTFRuntimeConfig; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Ready Player Me") + FString BaseModelId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") + TSubclassOf TargetAnimBP; + protected: FRpmCharacter Character; From 9dd9e0d8fe5e1272cfebaa0b359a313ce3fb67af Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 12 Aug 2024 09:55:27 +0300 Subject: [PATCH 07/74] feat: WIP demo login --- .../Private/Settings/RpmDeveloperSettings.cpp | 6 +++++ .../Public/Settings/RpmDeveloperSettings.h | 2 ++ .../Private/SRpmDeveloperLoginWidget.cpp | 25 ++++++++++++++++--- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp index f6fe683..4dfa9ff 100644 --- a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp +++ b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp @@ -11,3 +11,9 @@ URpmDeveloperSettings::URpmDeveloperSettings() ApiKey = TEXT(""); ApiProxyUrl = TEXT(""); } + +void URpmDeveloperSettings::SetupGuestUser() +{ + ApplicationId = TEXT("665e05a50c62c921e5a6ab84"); + ApiProxyUrl = TEXT("https://api.readyplayer.me/demo"); +} diff --git a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h index 972cf24..1ea122b 100644 --- a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h +++ b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h @@ -31,5 +31,7 @@ class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, Config, Category = "API") FString ApiProxyUrl; + + void SetupGuestUser(); }; diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 5c5b8e3..9a1896e 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -14,6 +14,7 @@ #include "Interfaces/IHttpResponse.h" #include "Widgets/Input/SEditableTextBox.h" #include "IImageWrapperModule.h" +#include "Api/Auth/ApiKeyAuthStrategy.h" #include "Auth/DeveloperAuthApi.h" #include "Auth/DeveloperLoginRequest.h" #include "Settings/RpmDeveloperSettings.h" @@ -192,10 +193,21 @@ void SRpmDeveloperLoginWidget::Initialize() } if(!DeveloperAccountApi.IsValid()) { - DeveloperTokenAuthStrategy* AuthStrategy = new DeveloperTokenAuthStrategy(); - DeveloperAccountApi = MakeUnique(AuthStrategy); - - DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); + const FDeveloperAuth DevAuthData = DevAuthTokenCache::GetAuthData(); + + if(DevAuthData.IsDemo) + { + DeveloperTokenAuthStrategy* AuthStrategy = new DeveloperTokenAuthStrategy(); + DeveloperAccountApi = MakeUnique(AuthStrategy); + DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); + } + else + { + FApiKeyAuthStrategy* AuthStrategy = new FApiKeyAuthStrategy(); + DeveloperAccountApi = MakeUnique(AuthStrategy); + DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); + } + DeveloperAccountApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); DeveloperAccountApi->OnApplicationListResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); } @@ -467,6 +479,11 @@ FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() { UserName = "DemoUser"; //TODO: Implement demo account login + FDeveloperAuth AuthData = FDeveloperAuth(); + AuthData.Name = "Guest user"; + AuthData.IsDemo = true; + + DevAuthTokenCache::SetAuthData(AuthData); UE_LOG(LogTemp, Error, TEXT("Demo account login not implemented")); return FReply::Handled(); } From 11a36607ccb7a2a20b99281e2718ab3696322c9a Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 12 Aug 2024 11:02:44 +0300 Subject: [PATCH 08/74] feat: wip demo login --- .../Private/Api/Assets/AssetApi.cpp | 2 +- .../Private/Api/Characters/CharacterApi.cpp | 2 +- .../Samples/CharacterCustomizationWidget.cpp | 2 +- .../Private/Settings/RpmDeveloperSettings.cpp | 5 +++++ .../Public/Settings/RpmDeveloperSettings.h | 1 + .../Private/Auth/DeveloperAccountApi.cpp | 2 +- .../Private/SRpmDeveloperLoginWidget.cpp | 22 +++++++------------ 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp index f731285..870bbf6 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp @@ -6,7 +6,7 @@ FAssetApi::FAssetApi() { URpmDeveloperSettings* Settings = GetMutableDefault(); - ApiBaseUrl = Settings->ApiBaseUrl; + ApiBaseUrl = Settings->GetApiBaseUrl(); OnApiResponse.BindRaw(this, &FAssetApi::HandleListAssetResponse); } diff --git a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp index cda52ba..82c5cb3 100644 --- a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp +++ b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp @@ -10,7 +10,7 @@ FCharacterApi::FCharacterApi() { URpmDeveloperSettings *Settings = GetMutableDefault(); - BaseUrl = FString::Printf(TEXT("%s/v1/characters"), *Settings->ApiBaseUrl) ; + BaseUrl = FString::Printf(TEXT("%s/v1/characters"), *Settings->GetApiBaseUrl()) ; Http = &FHttpModule::Get(); } diff --git a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp index f63395e..a9c9850 100644 --- a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp @@ -23,7 +23,7 @@ void UCharacterCustomizationWidget::InitializeCustomizationOptions() UE_LOG(LogTemp, Warning, TEXT("Initialize customization options called. Application ID: %s"), *ApplicationID); URpmDeveloperSettings* Settings = GetMutableDefault(); - FString ApiBaseUrl = Settings->ApiBaseUrl; + FString ApiBaseUrl = Settings->GetApiBaseUrl(); UE_LOG(LogTemp, Warning, TEXT("Asset API is valid")); diff --git a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp index 4dfa9ff..071e1ca 100644 --- a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp +++ b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp @@ -17,3 +17,8 @@ void URpmDeveloperSettings::SetupGuestUser() ApplicationId = TEXT("665e05a50c62c921e5a6ab84"); ApiProxyUrl = TEXT("https://api.readyplayer.me/demo"); } + +FString URpmDeveloperSettings::GetApiBaseUrl() +{ + return ApiProxyUrl.IsEmpty() ? ApiBaseUrl : ApiProxyUrl; +} diff --git a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h index 1ea122b..733b30f 100644 --- a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h +++ b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h @@ -34,4 +34,5 @@ class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings void SetupGuestUser(); + FString GetApiBaseUrl(); }; diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp index 49dc3b1..ba09692 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp @@ -6,7 +6,7 @@ FDeveloperAccountApi::FDeveloperAccountApi(IAuthenticationStrategy* InAuthentica { if (URpmDeveloperSettings* Settings = GetMutableDefault()) { - ApiBaseUrl = Settings->ApiBaseUrl; + ApiBaseUrl = Settings->GetApiBaseUrl(); } } diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 9a1896e..1a9dc6b 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -194,20 +194,13 @@ void SRpmDeveloperLoginWidget::Initialize() if(!DeveloperAccountApi.IsValid()) { const FDeveloperAuth DevAuthData = DevAuthTokenCache::GetAuthData(); - - if(DevAuthData.IsDemo) - { - DeveloperTokenAuthStrategy* AuthStrategy = new DeveloperTokenAuthStrategy(); - DeveloperAccountApi = MakeUnique(AuthStrategy); - DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); - } - else + DeveloperAccountApi = MakeUnique(nullptr); + if(!DevAuthData.IsDemo) { FApiKeyAuthStrategy* AuthStrategy = new FApiKeyAuthStrategy(); - DeveloperAccountApi = MakeUnique(AuthStrategy); DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); } - + DeveloperAccountApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); DeveloperAccountApi->OnApplicationListResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); } @@ -477,14 +470,15 @@ void SRpmDeveloperLoginWidget::OnComboBoxSelectionChanged(TSharedPtr Ne FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() { - UserName = "DemoUser"; - //TODO: Implement demo account login + FDeveloperAuth AuthData = FDeveloperAuth(); AuthData.Name = "Guest user"; AuthData.IsDemo = true; - + UserName = AuthData.Name; DevAuthTokenCache::SetAuthData(AuthData); - UE_LOG(LogTemp, Error, TEXT("Demo account login not implemented")); + SetLoggedInState(true); + GetOrgList(); + UE_LOG(LogTemp, Error, TEXT("Logging in as demo user.")); return FReply::Handled(); } From 8763c8100620f154ee57d6853521b65cb359ee0d Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Mon, 12 Aug 2024 14:46:11 +0300 Subject: [PATCH 09/74] feat: link application to first app and save over the application settings --- .../Private/SRpmDeveloperLoginWidget.cpp | 31 ++++++++++++++++--- .../Public/SRpmDeveloperLoginWidget.h | 3 +- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 119de91..3456b0b 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -441,12 +441,22 @@ void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationL { if (bWasSuccessful) { + UserApplications = Response.Data; + FString Active; TArray Items; - for (const FApplication& App : Response.Data) + for (const FApplication& App : UserApplications) { Items.Add(App.Name); + if(App.Id == Settings->ApplicationId) + { + Active = App.Name; + } + } + if(Active.IsEmpty() && Items.Num() > 0) + { + OnComboBoxSelectionChanged(MakeShared(Items[0]), ESelectInfo::Direct); } - PopulateComboBoxItems(Items); + PopulateComboBoxItems(Items, Active); } else @@ -457,19 +467,20 @@ void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationL } -void SRpmDeveloperLoginWidget::PopulateComboBoxItems(const TArray& Items) +void SRpmDeveloperLoginWidget::PopulateComboBoxItems(const TArray& Items, const FString ActiveItem) { ComboBoxItems.Empty(); for (const FString& Item : Items) { ComboBoxItems.Add(MakeShared(Item)); } - SelectedComboBoxItem = ComboBoxItems.Num() > 0 ? ComboBoxItems[0] : nullptr; + SelectedComboBoxItem = MakeShared(ActiveItem); } + FText SRpmDeveloperLoginWidget::GetSelectedComboBoxItemText() const { - return SelectedComboBoxItem.IsValid() ? FText::FromString(*SelectedComboBoxItem) : FText::FromString("Select an option"); + return SelectedComboBoxItem.IsValid() && *SelectedComboBoxItem != "" ? FText::FromString(*SelectedComboBoxItem) : FText::FromString("Select an option"); } @@ -477,6 +488,16 @@ FText SRpmDeveloperLoginWidget::GetSelectedComboBoxItemText() const void SRpmDeveloperLoginWidget::OnComboBoxSelectionChanged(TSharedPtr NewValue, ESelectInfo::Type SelectInfo) { SelectedComboBoxItem = NewValue; + FApplication* application = UserApplications.FindByPredicate([&](FApplication item) +{ + return item.Name == *NewValue; +}); + if(application) + { + URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + RpmSettings->ApplicationId = application->Id; + RpmSettings->SaveConfig(); + } } FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h index 5c3970e..6177b0f 100644 --- a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h @@ -50,7 +50,7 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget FText GetWelcomeText() const; void SetLoggedInState(const bool IsLoggedIn); - void PopulateComboBoxItems(const TArray& Items); + void PopulateComboBoxItems(const TArray& Items, FString CurrentItem); const URpmDeveloperSettings* Settings; TSharedPtr EmailTextBox; @@ -58,6 +58,7 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget TArray> ComboBoxItems; TSharedPtr SelectedComboBoxItem; + TArray UserApplications; void OnComboBoxSelectionChanged(TSharedPtr NewValue, ESelectInfo::Type SelectInfo); FText GetSelectedComboBoxItemText() const; From 885132c04b83fa60f9daaa8aee7ecc7a97d4e9d4 Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 13 Aug 2024 08:17:50 +0300 Subject: [PATCH 10/74] feat: fixed demo account login and small cleanup --- .../Private/Api/Assets/AssetApi.cpp | 9 ++- .../Private/Api/Common/WebApiWithAuth.cpp | 20 +++-- .../Samples/CharacterCustomizationWidget.cpp | 1 + .../Private/Settings/RpmDeveloperSettings.cpp | 15 ++++ .../Public/Settings/RpmDeveloperSettings.h | 8 +- .../Private/Auth/DeveloperAccountApi.cpp | 6 ++ .../Auth/DeveloperTokenAuthStrategy.cpp | 3 +- .../Private/SRpmDeveloperLoginWidget.cpp | 75 ++++++++----------- .../Public/SRpmDeveloperLoginWidget.h | 64 +++++++--------- 9 files changed, 102 insertions(+), 99 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp index 870bbf6..e46b9a2 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp @@ -5,21 +5,22 @@ FAssetApi::FAssetApi() { - URpmDeveloperSettings* Settings = GetMutableDefault(); - ApiBaseUrl = Settings->GetApiBaseUrl(); OnApiResponse.BindRaw(this, &FAssetApi::HandleListAssetResponse); } void FAssetApi::ListAssetsAsync(const FAssetListRequest& Request) { + // TODO find better way to get settings (or move to editor only code) + URpmDeveloperSettings* Settings = GetMutableDefault(); + ApiBaseUrl = Settings->GetApiBaseUrl(); + FString QueryString = Request.BuildQueryString(); const FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets%s"), *ApiBaseUrl, *QueryString); FApiRequest ApiRequest = FApiRequest(); ApiRequest.Url = Url; ApiRequest.Method = GET; - UE_LOG(LogTemp, Warning, TEXT("Try request from url %s"), *Url); - + DispatchRawWithAuth(ApiRequest); } diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp index 24e87a7..b64924c 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -14,7 +14,16 @@ FWebApiWithAuth::FWebApiWithAuth(IAuthenticationStrategy* InAuthenticationStrate void FWebApiWithAuth::SetAuthenticationStrategy(IAuthenticationStrategy* InAuthenticationStrategy) { + if(AuthenticationStrategy != nullptr) + { + AuthenticationStrategy->OnAuthComplete.Unbind(); + AuthenticationStrategy->OnTokenRefreshed.Unbind(); + } AuthenticationStrategy = InAuthenticationStrategy; + if(AuthenticationStrategy == nullptr) + { + return; + } AuthenticationStrategy->OnAuthComplete.BindRaw(this, &FWebApiWithAuth::OnAuthComplete); AuthenticationStrategy->OnTokenRefreshed.BindRaw(this, &FWebApiWithAuth::OnAuthTokenRefreshed); } @@ -26,7 +35,6 @@ void FWebApiWithAuth::OnAuthComplete(bool bWasSuccessful) DispatchRaw(*ApiRequestData); return; } - UE_LOG(LogTemp, Warning, TEXT("Auth failed")); OnApiResponse.ExecuteIfBound(TEXT("Auth failed"), false); } @@ -48,17 +56,14 @@ void FWebApiWithAuth::OnAuthTokenRefreshed(const FRefreshTokenResponseBody& Resp OnApiResponse.ExecuteIfBound(TEXT("Auth failed"), false); } -void FWebApiWithAuth::DispatchRawWithAuth( - FApiRequest& Data -) +void FWebApiWithAuth::DispatchRawWithAuth(FApiRequest& Data) { + this->ApiRequestData = &Data; if (AuthenticationStrategy == nullptr) { - UE_LOG(LogTemp, Warning, TEXT("Auth strategy is null")); DispatchRaw(Data); return; } - this->ApiRequestData = &Data; AuthenticationStrategy->AddAuthToRequest(Data); } @@ -70,9 +75,8 @@ void FWebApiWithAuth::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePt OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), true); return; } - if(EHttpResponseCodes::Denied == Response->GetResponseCode()) + if(EHttpResponseCodes::Denied == Response->GetResponseCode() && AuthenticationStrategy != nullptr) { - UE_LOG(LogTemp, Warning, TEXT("Forbidden response trying auth refresh")); AuthenticationStrategy->TryRefresh(*ApiRequestData); } } diff --git a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp index a9c9850..8653686 100644 --- a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp @@ -22,6 +22,7 @@ void UCharacterCustomizationWidget::InitializeCustomizationOptions() { UE_LOG(LogTemp, Warning, TEXT("Initialize customization options called. Application ID: %s"), *ApplicationID); + URpmDeveloperSettings* Settings = GetMutableDefault(); FString ApiBaseUrl = Settings->GetApiBaseUrl(); diff --git a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp index 071e1ca..0e53c75 100644 --- a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp +++ b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp @@ -16,6 +16,21 @@ void URpmDeveloperSettings::SetupGuestUser() { ApplicationId = TEXT("665e05a50c62c921e5a6ab84"); ApiProxyUrl = TEXT("https://api.readyplayer.me/demo"); + +} + +void URpmDeveloperSettings::Reset() +{ + if(ApplicationId == TEXT("665e05a50c62c921e5a6ab84")) + { + ApplicationId = TEXT(""); + + } + if(ApiProxyUrl == TEXT("https://api.readyplayer.me/demo")) + { + ApiProxyUrl = TEXT(""); + } + this->SaveConfig(); } FString URpmDeveloperSettings::GetApiBaseUrl() diff --git a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h index 733b30f..5091538 100644 --- a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h +++ b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h @@ -17,9 +17,6 @@ class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings public: URpmDeveloperSettings(); - UPROPERTY(EditAnywhere, Config, Category = "API") - FString ApiBaseUrl; - UPROPERTY(EditAnywhere, Config, Category = "API") FString ApiBaseAuthUrl; @@ -33,6 +30,9 @@ class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings FString ApiProxyUrl; void SetupGuestUser(); - + void Reset(); FString GetApiBaseUrl(); + +private: + FString ApiBaseUrl; }; diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp index ba09692..2a2a8b1 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp @@ -12,6 +12,9 @@ FDeveloperAccountApi::FDeveloperAccountApi(IAuthenticationStrategy* InAuthentica void FDeveloperAccountApi::ListApplicationsAsync(const FApplicationListRequest& Request) { + // TODO find better way to get settings (or move to editor only code) + URpmDeveloperSettings* Settings = GetMutableDefault(); + ApiBaseUrl = Settings->GetApiBaseUrl(); const FString QueryString = BuildQueryString(Request.Params); const FString Url = FString::Printf(TEXT("%s/v1/applications%s"), *ApiBaseUrl, *QueryString); FApiRequest ApiRequest; @@ -22,6 +25,9 @@ void FDeveloperAccountApi::ListApplicationsAsync(const FApplicationListRequest& void FDeveloperAccountApi::ListOrganizationsAsync(const FOrganizationListRequest& Request) { + // TODO find better way to get settings (or move to editor only code) + URpmDeveloperSettings* Settings = GetMutableDefault(); + ApiBaseUrl = Settings->GetApiBaseUrl(); const FString QueryString = BuildQueryString(Request.Params); const FString Url = FString::Printf(TEXT("%s/v1/organizations%s"), *ApiBaseUrl, *QueryString); FApiRequest ApiRequest; diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp index 904f51f..1825e49 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp @@ -40,11 +40,10 @@ void DeveloperTokenAuthStrategy::OnRefreshTokenResponse(const FRefreshTokenRespo DeveloperAuth.Token = Response.Data.Token; DeveloperAuth.RefreshToken = Response.Data.RefreshToken; DevAuthTokenCache::SetAuthData(DeveloperAuth); - UE_LOG(LogTemp, Log, TEXT("Token refreshed successfully: %s"), *DeveloperAuth.Token); OnTokenRefreshed.ExecuteIfBound(Response.Data, true); return; } - UE_LOG(LogTemp, Warning, TEXT("Failed to refresh token")); + UE_LOG(LogTemp, Error, TEXT("Failed to refresh token")); OnTokenRefreshed.ExecuteIfBound(Response.Data, false); } diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 10751fe..15fa10f 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -28,7 +28,7 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) DevAuthTokenCache::SetAuthData(AuthData); bIsLoggedIn = AuthData.IsValid(); - Settings = GetDefault(); + Settings = GetMutableDefault(); UserName = AuthData.Name; ChildSlot [ @@ -209,8 +209,7 @@ void SRpmDeveloperLoginWidget::Initialize() DeveloperAccountApi = MakeUnique(nullptr); if(!DevAuthData.IsDemo) { - FApiKeyAuthStrategy* AuthStrategy = new FApiKeyAuthStrategy(); - DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); + DeveloperAccountApi->SetAuthenticationStrategy(new DeveloperTokenAuthStrategy()); } DeveloperAccountApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); @@ -221,7 +220,7 @@ void SRpmDeveloperLoginWidget::Initialize() { GetOrgList(); } -} +} void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) { @@ -285,38 +284,6 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) }); } -void SRpmDeveloperLoginWidget::Initialize() -{ - if(bIsInitialized) return; - - if(!DeveloperAuthApi.IsValid()) - { - DeveloperAuthApi = MakeUnique(); - DeveloperAuthApi->OnLoginResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleLoginResponse); - } - - if (!AssetApi.IsValid()) - { - AssetApi = MakeUnique(); - AssetApi->OnListAssetsResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleBaseModelListResponse); - - } - if(!DeveloperAccountApi.IsValid()) - { - DeveloperTokenAuthStrategy* AuthStrategy = new DeveloperTokenAuthStrategy(); - DeveloperAccountApi = MakeUnique(AuthStrategy); - - DeveloperAccountApi->SetAuthenticationStrategy(AuthStrategy); - DeveloperAccountApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); - DeveloperAccountApi->OnApplicationListResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); - } - bIsInitialized = true; - if(bIsLoggedIn) - { - GetOrgList(); - } -} - void SRpmDeveloperLoginWidget::DownloadImage(const FString& Url, TFunction Callback) { TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); @@ -423,7 +390,8 @@ FReply SRpmDeveloperLoginWidget::OnLoginClicked() EditorCache::SetString(CacheKeyEmail, Email); Email = Email.TrimStartAndEnd(); Password = Password.TrimStartAndEnd(); - + DeveloperAccountApi->SetAuthenticationStrategy(new DeveloperTokenAuthStrategy()); + AssetApi->SetAuthenticationStrategy(new DeveloperTokenAuthStrategy()); FDeveloperLoginRequest LoginRequest = FDeveloperLoginRequest(Email, Password); DeveloperAuthApi->LoginWithEmail(LoginRequest); return FReply::Handled(); @@ -446,7 +414,7 @@ void SRpmDeveloperLoginWidget::HandleLoginResponse(const FDeveloperLoginResponse GetOrgList(); return; } - UE_LOG(LogTemp, Error, TEXT("Login request failed"),); + UE_LOG(LogTemp, Error, TEXT("Login request failed")); DevAuthTokenCache::ClearAuthData(); } @@ -517,26 +485,39 @@ void SRpmDeveloperLoginWidget::OnComboBoxSelectionChanged(TSharedPtr Ne } FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() -{ - +{ + // TODO find a better way to get the latest settings + Settings = GetMutableDefault(); + Settings->SetupGuestUser(); FDeveloperAuth AuthData = FDeveloperAuth(); - AuthData.Name = "Guest user"; + AuthData.Name = DemoUserName; AuthData.IsDemo = true; UserName = AuthData.Name; DevAuthTokenCache::SetAuthData(AuthData); SetLoggedInState(true); + + // Unset the authentication strategy for the APIs + DeveloperAccountApi->SetAuthenticationStrategy(nullptr); + AssetApi->SetAuthenticationStrategy(nullptr); + GetOrgList(); - UE_LOG(LogTemp, Error, TEXT("Logging in as demo user.")); return FReply::Handled(); } FReply SRpmDeveloperLoginWidget::OnLogoutClicked() { + // TODO find a better way to get the latest settings + + Settings = GetMutableDefault(); + Settings->Reset(); + // Clear the content box to remove all child widgets if (ContentBox.IsValid()) { ContentBox->ClearChildren(); } + ComboBoxItems.Empty(); + DevAuthTokenCache::ClearAuthData(); SetLoggedInState(false); return FReply::Handled(); @@ -544,12 +525,17 @@ FReply SRpmDeveloperLoginWidget::OnLogoutClicked() void SRpmDeveloperLoginWidget::LoadBaseModelList() { + URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + if(RpmSettings->ApplicationId.IsEmpty()) + { + UE_LOG(LogTemp, Error, TEXT("Application ID is empty, unable to load base models.")); + return; + } FAssetListRequest Request = FAssetListRequest(); FAssetListQueryParams Params = FAssetListQueryParams(); - Params.ApplicationId = Settings->ApplicationId; + Params.ApplicationId = RpmSettings->ApplicationId; Params.Type = "baseModel"; Request.Params = Params; - AssetApi->SetAuthenticationStrategy(new DeveloperTokenAuthStrategy()); AssetApi->ListAssetsAsync(Request); } @@ -559,7 +545,6 @@ void SRpmDeveloperLoginWidget::HandleBaseModelListResponse(const FAssetListRespo for (FAsset Asset : Response.Data) { CharacterStyleAssets.Add(Asset.Id, Asset); - UE_LOG(LogTemp, Error, TEXT("Asset ID: %s"), *Asset.Id); AddCharacterStyle(Asset); } } diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h index 5c3970e..3952120 100644 --- a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h @@ -7,7 +7,6 @@ #include "Api/Assets/AssetApi.h" #include "Api/Assets/Models/AssetListResponse.h" #include "Auth/DeveloperAccountApi.h" -#include "Interfaces/IHttpRequest.h" #include "Widgets/SCompoundWidget.h" #include "Containers/Map.h" @@ -25,58 +24,51 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget SLATE_BEGIN_ARGS(SRpmDeveloperLoginWidget) {} SLATE_END_ARGS() - - /** Constructs this widget with InArgs */ + void Construct(const FArguments& InArgs); private: + + TSharedPtr ContentBox; + TSharedPtr EmailTextBox; + TSharedPtr PasswordTextBox; + TSharedPtr SelectedComboBoxItem; + TArray> ComboBoxItems; + TMap CharacterStyleAssets; + + EVisibility GetLoginViewVisibility() const; + EVisibility GetLoggedInViewVisibility() const; + + FEditorAssetLoader AssetLoader; + TUniquePtr AssetApi; + TUniquePtr DeveloperAccountApi; + TUniquePtr DeveloperAuthApi; + static constexpr const TCHAR* CacheKeyEmail = TEXT("Email"); + bool bIsLoggedIn = false; + bool bIsInitialized = false; + FString UserName; + URpmDeveloperSettings* Settings; + FText GetWelcomeText() const; + FString DemoUserName = TEXT("Guest user"); + FText GetSelectedComboBoxItemText() const; + FReply OnLoginClicked(); - void GetOrgList(); FReply OnUseDemoAccountClicked(); FReply OnLogoutClicked(); - + static void SetImageFromTexture(UTexture2D* Texture, const TSharedPtr& ImageWidget); + void Initialize(); + void GetOrgList(); void LoadBaseModelList(); - void HandleLoginResponse(const FDeveloperLoginResponse& Response, bool bWasSuccessful); void HandleOrganizationListResponse(const FOrganizationListResponse& Response, bool bWasSuccessful); - void HandleApplicationListResponse(const FApplicationListResponse& Response, bool bWasSuccessful); void HandleBaseModelListResponse(const FAssetListResponse& Response, bool bWasSuccessful); void OnLoadStyleClicked(const FString& StyleId); - EVisibility GetLoginViewVisibility() const; - EVisibility GetLoggedInViewVisibility() const; - - FText GetWelcomeText() const; - void SetLoggedInState(const bool IsLoggedIn); void PopulateComboBoxItems(const TArray& Items); - - const URpmDeveloperSettings* Settings; - TSharedPtr EmailTextBox; - TSharedPtr PasswordTextBox; - - TArray> ComboBoxItems; - TSharedPtr SelectedComboBoxItem; - void OnComboBoxSelectionChanged(TSharedPtr NewValue, ESelectInfo::Type SelectInfo); - FText GetSelectedComboBoxItemText() const; - - bool bIsLoggedIn = false; - FString UserName; - TUniquePtr AssetApi; - TUniquePtr DeveloperAccountApi; - TUniquePtr DeveloperAuthApi; - static constexpr const TCHAR* CacheKeyEmail = TEXT("Email"); - - TSharedPtr ContentBox; - - TMap CharacterStyleAssets; void AddCharacterStyle(const FAsset& StyleAsset); void DownloadImage(const FString& Url, TFunction Callback); UTexture2D* CreateTextureFromImageData(const TArray& ImageData); - static void SetImageFromTexture(UTexture2D* Texture, const TSharedPtr& ImageWidget); - bool bIsInitialized = false; - FEditorAssetLoader AssetLoader; - void Initialize(); }; From 6454f55a82cb56a576b009985d37367dac1b2591 Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 13 Aug 2024 09:43:50 +0300 Subject: [PATCH 11/74] fix: fixed a dangling pointer issue causing crash on refresh --- .../RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp | 6 +++--- .../RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp | 6 +++--- .../Private/Settings/RpmDeveloperSettings.cpp | 2 +- Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h | 4 ++-- .../Public/Api/Auth/IAuthenticationStrategy.h | 4 ++-- Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h | 2 +- .../Private/Auth/DeveloperTokenAuthStrategy.cpp | 10 +++++----- .../Private/SRpmDeveloperLoginWidget.cpp | 10 +++++++--- .../RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h | 2 +- .../Public/Auth/DeveloperTokenAuthStrategy.h | 4 ++-- 10 files changed, 27 insertions(+), 23 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp index a6de451..a8e0a1f 100644 --- a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp +++ b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp @@ -7,10 +7,10 @@ FApiKeyAuthStrategy::FApiKeyAuthStrategy() { } -void FApiKeyAuthStrategy::AddAuthToRequest(FApiRequest& Request) +void FApiKeyAuthStrategy::AddAuthToRequest(TSharedPtr Request) { URpmDeveloperSettings *Settings = GetMutableDefault(); - Request.Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); + Request->Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); OnAuthComplete.ExecuteIfBound(true); } @@ -18,7 +18,7 @@ void FApiKeyAuthStrategy::OnRefreshTokenResponse(const FRefreshTokenResponse& Re { } -void FApiKeyAuthStrategy::TryRefresh(FApiRequest& Request) +void FApiKeyAuthStrategy::TryRefresh(TSharedPtr Request) { UE_LOG(LogTemp, Log, TEXT("Trying refresh")); } diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp index b64924c..cbc3ef5 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -58,13 +58,13 @@ void FWebApiWithAuth::OnAuthTokenRefreshed(const FRefreshTokenResponseBody& Resp void FWebApiWithAuth::DispatchRawWithAuth(FApiRequest& Data) { - this->ApiRequestData = &Data; + this->ApiRequestData = MakeShared(Data); if (AuthenticationStrategy == nullptr) { DispatchRaw(Data); return; } - AuthenticationStrategy->AddAuthToRequest(Data); + AuthenticationStrategy->AddAuthToRequest(this->ApiRequestData); } void FWebApiWithAuth::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) @@ -77,6 +77,6 @@ void FWebApiWithAuth::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePt } if(EHttpResponseCodes::Denied == Response->GetResponseCode() && AuthenticationStrategy != nullptr) { - AuthenticationStrategy->TryRefresh(*ApiRequestData); + AuthenticationStrategy->TryRefresh(ApiRequestData); } } diff --git a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp index 0e53c75..b2d0cb8 100644 --- a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp +++ b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp @@ -16,7 +16,7 @@ void URpmDeveloperSettings::SetupGuestUser() { ApplicationId = TEXT("665e05a50c62c921e5a6ab84"); ApiProxyUrl = TEXT("https://api.readyplayer.me/demo"); - + this->SaveConfig(); } void URpmDeveloperSettings::Reset() diff --git a/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h b/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h index b774283..047a325 100644 --- a/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h +++ b/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h @@ -8,8 +8,8 @@ class RPMNEXTGEN_API FApiKeyAuthStrategy : public IAuthenticationStrategy { public: FApiKeyAuthStrategy(); - virtual void AddAuthToRequest(FApiRequest& Request) override; + virtual void AddAuthToRequest(TSharedPtr Request) override; virtual void OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) override; - virtual void TryRefresh(FApiRequest& Request) override; + virtual void TryRefresh(TSharedPtr Request) override; FOnAuthComplete OnAuthComplete; }; diff --git a/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h index 434710d..e8a190f 100644 --- a/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h +++ b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h @@ -11,8 +11,8 @@ class RPMNEXTGEN_API IAuthenticationStrategy { public: virtual ~IAuthenticationStrategy() = default; - virtual void AddAuthToRequest(FApiRequest& Request) = 0; - virtual void TryRefresh(FApiRequest& Request) = 0; + virtual void AddAuthToRequest(TSharedPtr Request) = 0; + virtual void TryRefresh(TSharedPtr Request) = 0; virtual void OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) = 0; FOnAuthComplete OnAuthComplete; FOnTokenRefreshed OnTokenRefreshed; diff --git a/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h index 3507301..58137c1 100644 --- a/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h +++ b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h @@ -23,7 +23,7 @@ class RPMNEXTGEN_API FWebApiWithAuth : public FWebApi void DispatchRawWithAuth(FApiRequest& Data); protected: - FApiRequest* ApiRequestData; + TSharedPtr ApiRequestData; virtual void OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) override; diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp index 1825e49..9a3d838 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp @@ -11,19 +11,19 @@ DeveloperTokenAuthStrategy::DeveloperTokenAuthStrategy() } -void DeveloperTokenAuthStrategy::AddAuthToRequest(FApiRequest& Request) +void DeveloperTokenAuthStrategy::AddAuthToRequest(TSharedPtr Request) { const FString Key = TEXT("Authorization"); - if (Request.Headers.Contains(Key)) + if (Request->Headers.Contains(Key)) { - Request.Headers.Remove(Key); + Request->Headers.Remove(Key); } - Request.Headers.Add(Key, FString::Printf(TEXT("Bearer %s"), *DevAuthTokenCache::GetAuthData().Token)); + Request->Headers.Add(Key, FString::Printf(TEXT("Bearer %s"), *DevAuthTokenCache::GetAuthData().Token)); OnAuthComplete.ExecuteIfBound(true); } -void DeveloperTokenAuthStrategy::TryRefresh(FApiRequest& Request) +void DeveloperTokenAuthStrategy::TryRefresh(TSharedPtr Request) { FRefreshTokenRequest RefreshRequest; RefreshRequest.Data.Token = DevAuthTokenCache::GetAuthData().Token; diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 15fa10f..313afff 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -14,7 +14,6 @@ #include "Interfaces/IHttpResponse.h" #include "Widgets/Input/SEditableTextBox.h" #include "IImageWrapperModule.h" -#include "Api/Auth/ApiKeyAuthStrategy.h" #include "Auth/DeveloperAuthApi.h" #include "Auth/DeveloperLoginRequest.h" #include "Settings/RpmDeveloperSettings.h" @@ -190,22 +189,27 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) void SRpmDeveloperLoginWidget::Initialize() { if(bIsInitialized) return; - + const FDeveloperAuth DevAuthData = DevAuthTokenCache::GetAuthData(); if(!DeveloperAuthApi.IsValid()) { DeveloperAuthApi = MakeUnique(); + DeveloperAuthApi->OnLoginResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleLoginResponse); } if (!AssetApi.IsValid()) { AssetApi = MakeUnique(); + if(!DevAuthData.IsDemo) + { + AssetApi->SetAuthenticationStrategy(new DeveloperTokenAuthStrategy()); + } AssetApi->OnListAssetsResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleBaseModelListResponse); } if(!DeveloperAccountApi.IsValid()) { - const FDeveloperAuth DevAuthData = DevAuthTokenCache::GetAuthData(); + DeveloperAccountApi = MakeUnique(nullptr); if(!DevAuthData.IsDemo) { diff --git a/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h b/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h index 48531f7..288f95a 100644 --- a/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h +++ b/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h @@ -52,7 +52,7 @@ struct RPMNEXTGENEDITOR_API FDeveloperAuth bool IsValid() const { - return !Token.IsEmpty(); + return IsDemo || !Token.IsEmpty(); } }; diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h b/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h index 9431fe4..da3bf06 100644 --- a/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h +++ b/Source/RpmNextGenEditor/Public/Auth/DeveloperTokenAuthStrategy.h @@ -12,9 +12,9 @@ class RPMNEXTGENEDITOR_API DeveloperTokenAuthStrategy : public IAuthenticationSt { public: DeveloperTokenAuthStrategy(); - virtual void AddAuthToRequest(FApiRequest& Request) override; + virtual void AddAuthToRequest(TSharedPtr Request) override; virtual void OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) override; - virtual void TryRefresh(FApiRequest& Request) override; + virtual void TryRefresh(TSharedPtr Request) override; private: void RefreshTokenAsync(const FRefreshTokenRequest& Request); FOnWebApiResponse OnWebApiResponse; From b4f0e6c5a46f46fbecc172ffffddfae9219c5987 Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 13 Aug 2024 09:52:53 +0300 Subject: [PATCH 12/74] chore: added PR template and codeowners --- .github/CODEOWNERS | 5 +++ .github/pull_request_template.md | 55 ++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/pull_request_template.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..a53e890 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @readyplayerme/onboarding-integrations team members will be requested for +# review when someone opens a pull request. +* @readyplayerme/onboarding-integrations \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b169510 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,55 @@ + + + + + +## [TICKETID](https://ready-player-me.atlassian.net/browse/TICKETID) + +## Description + +- Briefly describe what this change will do + + + + +## Changes + +#### Added + +- List your additions and new features here. + +#### Updated + +- List your updates and changes here. + +#### Removed + +- List what is removed here. + + + +## How to Test + +- Add steps to locally test these changes + + + +## Checklist + +- [ ] Tests written or updated for the changes. +- [ ] Documentation is updated. +- [ ] Changelog is updated. +- [ ] QA Testing is updated. + +## QA Testing + +- Mention any useful test cases for this feature and add to QA Testing documentation. + +## Details + +- If more details are necessary to support the description with images and videos add them here. + + + + + From 6e40f4842fc17b9b1cf98f17e4b35d83e271bd4e Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 13 Aug 2024 11:02:00 +0300 Subject: [PATCH 13/74] chore: rename and added constants --- .../Private/Settings/RpmDeveloperSettings.cpp | 11 +++++------ .../RpmNextGen/Public/Settings/RpmDeveloperSettings.h | 4 +++- .../Private/SRpmDeveloperLoginWidget.cpp | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp index b2d0cb8..8bb317f 100644 --- a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp +++ b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp @@ -12,21 +12,20 @@ URpmDeveloperSettings::URpmDeveloperSettings() ApiProxyUrl = TEXT(""); } -void URpmDeveloperSettings::SetupGuestUser() +void URpmDeveloperSettings::SetupDemoAccount() { - ApplicationId = TEXT("665e05a50c62c921e5a6ab84"); - ApiProxyUrl = TEXT("https://api.readyplayer.me/demo"); + ApplicationId = DemoAppId; + ApiProxyUrl = DemoProxyUrl; this->SaveConfig(); } void URpmDeveloperSettings::Reset() { - if(ApplicationId == TEXT("665e05a50c62c921e5a6ab84")) + if(ApplicationId == DemoAppId) { ApplicationId = TEXT(""); - } - if(ApiProxyUrl == TEXT("https://api.readyplayer.me/demo")) + if(ApiProxyUrl == DemoProxyUrl) { ApiProxyUrl = TEXT(""); } diff --git a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h index 5091538..667bcde 100644 --- a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h +++ b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h @@ -29,10 +29,12 @@ class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings UPROPERTY(EditAnywhere, Config, Category = "API") FString ApiProxyUrl; - void SetupGuestUser(); + void SetupDemoAccount(); void Reset(); FString GetApiBaseUrl(); private: FString ApiBaseUrl; + const FString DemoAppId = TEXT("665e05a50c62c921e5a6ab84"); + const FString DemoProxyUrl = TEXT("https://api.readyplayer.me/demo"); }; diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 313afff..3d1526e 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -492,7 +492,7 @@ FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() { // TODO find a better way to get the latest settings Settings = GetMutableDefault(); - Settings->SetupGuestUser(); + Settings->SetupDemoAccount(); FDeveloperAuth AuthData = FDeveloperAuth(); AuthData.Name = DemoUserName; AuthData.IsDemo = true; From 274ce7fdafdb356b26c994ad5d30334b76d1a904 Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 13 Aug 2024 11:10:22 +0300 Subject: [PATCH 14/74] chore: update codeowners --- .github/CODEOWNERS | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a53e890..aa61f69 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,3 @@ # These owners will be the default owners for everything in # the repo. Unless a later match takes precedence, -# @readyplayerme/onboarding-integrations team members will be requested for -# review when someone opens a pull request. -* @readyplayerme/onboarding-integrations \ No newline at end of file +* @HarrisonHough \ No newline at end of file From faceb7e6462c9a07c3d75deb1713c3708a8e8eee Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 13 Aug 2024 11:47:15 +0300 Subject: [PATCH 15/74] chore: small cleanup of rpmactor to work better with app id --- Source/RpmNextGen/Private/RpmActor.cpp | 28 ++++++-------------------- Source/RpmNextGen/Public/RpmActor.h | 6 +++--- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index c631a62..c1befd5 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -16,12 +16,12 @@ ARpmActor::ARpmActor() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; - RpmSettings = GetMutableDefault(); + URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + AppId = RpmSettings->ApplicationId; CharacterApi = MakeShared(); CharacterApi->OnCharacterCreateResponse.BindUObject(this, &ARpmActor::HandleCharacterCreateResponse); CharacterApi->OnCharacterUpdateResponse.BindUObject(this, &ARpmActor::HandleCharacterUpdateResponse); CharacterApi->OnCharacterFindResponse.BindUObject(this, &ARpmActor::HandleCharacterFindResponse); - AssetRoot = CreateDefaultSubobject(TEXT("AssetRoot")); RootComponent = AssetRoot; BaseSkeletalMeshComponent = CreateDefaultSubobject(TEXT("BaseSkeletalMesh")); @@ -33,26 +33,22 @@ ARpmActor::ARpmActor() void ARpmActor::HandleCharacterCreateResponse(FCharacterCreateResponse CharacterCreateResponse, bool bWasSuccessful) { - UE_LOG(LogTemp, Warning, TEXT("HandleCharacterCreateResponse")); Character = CharacterCreateResponse.Data; LoadCharacter(Character); } void ARpmActor::HandleCharacterUpdateResponse(FCharacterUpdateResponse CharacterUpdateResponse, bool bWasSuccessful) { - UE_LOG(LogTemp, Warning, TEXT("HandleCharacterUpdateResponse")); Character = CharacterUpdateResponse.Data; } void ARpmActor::HandleCharacterFindResponse(FCharacterFindByIdResponse CharacterFindByIdResponse, bool bWasSuccessful) { - UE_LOG(LogTemp, Warning, TEXT("HandleCharacterFindResponse")); Character = CharacterFindByIdResponse.Data; } -void ARpmActor::CreateCharacter(FString AppId) +void ARpmActor::CreateCharacter() { - //TODO update to fetch first app id FCharacterCreateRequest CharacterCreateRequest = FCharacterCreateRequest(); CharacterCreateRequest.Data.Assets = TMap(); CharacterCreateRequest.Data.Assets.Add("baseModel", BaseModelId); @@ -99,7 +95,7 @@ USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* Sk return NewSkeletalMeshComponent; } -void ARpmActor::LoadglTFAsset(UglTFRuntimeAsset* Asset, const FString& AssetName) +void ARpmActor::LoadglTFAsset(UglTFRuntimeAsset* Asset) { Asset->LoadSkeletalMeshRecursiveAsync("", {}, OnSkeletalMeshCallback, SkeletalMeshConfig ); } @@ -114,16 +110,10 @@ void ARpmActor::HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh) void ARpmActor::OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedPtr HttpResponse, bool bIsSuccessful) { if (bIsSuccessful) - { - UE_LOG(LogTemp, Warning, TEXT("Request success from url %s "), *HttpRequest->GetURL()); - + { glTFRuntimeConfig.TransformBaseType = EglTFRuntimeTransformBaseType::YForward; UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(HttpResponse->GetContent(), glTFRuntimeConfig); - LoadglTFAsset(gltfAsset, "Asset"); - } - else - { - // Handle error + LoadglTFAsset(gltfAsset); } } @@ -139,10 +129,6 @@ void ARpmActor::LoadAsset(FAsset AssetData) FCharacterPreviewRequest PreviewRequest; PreviewRequest.Id = Character.Id; PreviewRequest.Params.Assets = PreviewAssetMap; - for (auto PreviewAsset : PreviewAssetMap) - { - UE_LOG(LogTemp, Warning, TEXT("PreviewAssetMap: %s | Value %s"), *PreviewAsset.Key, *PreviewAsset.Value); - } const FString& Url = CharacterApi->GeneratePreviewUrl(PreviewRequest); LoadCharacterUrl(Url); } @@ -150,8 +136,6 @@ void ARpmActor::LoadAsset(FAsset AssetData) void ARpmActor::LoadCharacterUrl(const FString Url) { TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - UE_LOG(LogTemp, Warning, TEXT("Requesting from url %s"), *Url); - HttpRequest->SetURL(Url); HttpRequest->SetVerb("GET"); HttpRequest->SetHeader("Content-Type", "application/json"); diff --git a/Source/RpmNextGen/Public/RpmActor.h b/Source/RpmNextGen/Public/RpmActor.h index dab6a4b..6399c73 100644 --- a/Source/RpmNextGen/Public/RpmActor.h +++ b/Source/RpmNextGen/Public/RpmActor.h @@ -32,7 +32,7 @@ class RPMNEXTGEN_API ARpmActor : public AActor USkeletalMeshComponent* CreateSkeletalMeshComponent(USkeletalMesh* SkeletalMesh, const FString& Name); UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - void LoadglTFAsset(UglTFRuntimeAsset *Asset, const FString& AssetName); + void LoadglTFAsset(UglTFRuntimeAsset *Asset); void OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedPtr HttpResponse, bool bIsSuccessful); @@ -49,7 +49,7 @@ class RPMNEXTGEN_API ARpmActor : public AActor USkeletalMeshComponent* BaseSkeletalMeshComponent; UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - void CreateCharacter(FString AppId); + void CreateCharacter(); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Ready Player Me") FglTFRuntimeSkeletalMeshConfig SkeletalMeshConfig; @@ -81,7 +81,7 @@ class RPMNEXTGEN_API ARpmActor : public AActor private: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category="Ready Player Me") USceneComponent* AssetRoot; - URpmDeveloperSettings* RpmSettings; FglTFRuntimeSkeletalMeshAsync OnSkeletalMeshCallback; TSharedPtr CharacterApi; + FString AppId; }; From 782359d7dd9ede2b3e4312701e05199c6fdde8b6 Mon Sep 17 00:00:00 2001 From: Harrison Date: Tue, 13 Aug 2024 12:04:12 +0300 Subject: [PATCH 16/74] chore: cleanup and added todos --- Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp | 1 + Source/RpmNextGen/Private/RpmActor.cpp | 1 + .../Private/Samples/CharacterCustomizationWidget.cpp | 8 ++------ .../Public/Samples/CharacterCustomizationWidget.h | 1 - 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp index 775bd1c..ae20a3f 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp @@ -34,6 +34,7 @@ FAssetLoader::~FAssetLoader() void FAssetLoader::LoadGLBFromURL(const FString& URL) { + // TODO replace this with use of WebApi class TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); HttpRequest->OnProcessRequestComplete().BindRaw(this, &FAssetLoader::OnDownloadComplete); HttpRequest->SetURL(URL); diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index c1befd5..353aa46 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -135,6 +135,7 @@ void ARpmActor::LoadAsset(FAsset AssetData) void ARpmActor::LoadCharacterUrl(const FString Url) { + // TODO replace this with use of WebApi class TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); HttpRequest->SetURL(Url); HttpRequest->SetVerb("GET"); diff --git a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp index 8653686..da3b9d8 100644 --- a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp @@ -20,14 +20,9 @@ void UCharacterCustomizationWidget::NativeConstruct() void UCharacterCustomizationWidget::InitializeCustomizationOptions() { - UE_LOG(LogTemp, Warning, TEXT("Initialize customization options called. Application ID: %s"), *ApplicationID); - - URpmDeveloperSettings* Settings = GetMutableDefault(); FString ApiBaseUrl = Settings->GetApiBaseUrl(); - - UE_LOG(LogTemp, Warning, TEXT("Asset API is valid")); - + // Setup request and parameters FAssetListRequest Request = FAssetListRequest(); FAssetListQueryParams Params; @@ -36,6 +31,7 @@ void UCharacterCustomizationWidget::InitializeCustomizationOptions() Request.Params = Params; FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets%s"), *ApiBaseUrl, *Request.BuildQueryString()); + // TODO replace this with use of AssetApi class TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); HttpRequest->SetURL(Url); HttpRequest->SetVerb("GET"); diff --git a/Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h b/Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h index 768ec6e..cd76b18 100644 --- a/Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h +++ b/Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h @@ -63,5 +63,4 @@ class RPMNEXTGEN_API UCharacterCustomizationWidget : public UUserWidget FString ApplicationID; TArray AssetDataArray; - TSharedPtr CharacterApi; }; From bff40c47e90ffc64bedafcddfd1e44fd2556d3e5 Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Tue, 13 Aug 2024 16:16:18 +0300 Subject: [PATCH 17/74] feat: show application from the beginning --- .../Private/SRpmDeveloperLoginWidget.cpp | 256 +++++++++--------- .../Public/SRpmDeveloperLoginWidget.h | 1 + 2 files changed, 133 insertions(+), 124 deletions(-) diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 1a8de5b..66960a6 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -29,51 +29,52 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) bIsLoggedIn = AuthData.IsValid(); Settings = GetMutableDefault(); UserName = AuthData.Name; + ChildSlot [ SNew(SVerticalBox) + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(STextBlock) .Text(FText::FromString("Sign in with your Ready Player Me Studio account")) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(STextBlock) .Text(FText::FromString("Email:")) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SAssignNew(EmailTextBox, SEditableTextBox) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(STextBlock) .Text(FText::FromString("Password:")) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SAssignNew(PasswordTextBox, SEditableTextBox) .IsPassword(true) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(SButton) .Text(FText::FromString("Login")) @@ -81,8 +82,8 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(SButton) .Text(FText::FromString("Use Demo Account")) @@ -90,8 +91,8 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoginViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(SHorizontalBox) + SHorizontalBox::Slot() @@ -102,8 +103,8 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) ] + SHorizontalBox::Slot() - .AutoWidth() - .HAlign(HAlign_Right) + .AutoWidth() + .HAlign(HAlign_Right) [ SNew(SButton) .Text(FText::FromString("Logout")) @@ -112,8 +113,8 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) ] ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(STextBlock) .Text(FText::FromString("Project Settings")) @@ -121,33 +122,34 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(STextBlock) .Text(FText::FromString("Select the Ready Player Me application to link to project")) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) ] - + + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(SComboBox>) .OptionsSource(&ComboBoxItems) .OnSelectionChanged(this, &SRpmDeveloperLoginWidget::OnComboBoxSelectionChanged) - .OnGenerateWidget_Lambda([](TSharedPtr Item) { - return SNew(STextBlock).Text(FText::FromString(*Item)); - }) + .OnGenerateWidget_Lambda([](TSharedPtr Item) + { + return SNew(STextBlock).Text(FText::FromString(*Item)); + }) [ - SNew(STextBlock) - .Text(this, &SRpmDeveloperLoginWidget::GetSelectedComboBoxItemText) + SAssignNew(SelectedApplicationTextBlock, STextBlock).Text( + this, &SRpmDeveloperLoginWidget::GetSelectedComboBoxItemText) ] .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(STextBlock) .Text(FText::FromString("Character Styles")) @@ -155,16 +157,16 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .AutoHeight() + .Padding(10) + .AutoHeight() [ SNew(STextBlock) .Text(FText::FromString("Here you can import your character styles from Studio")) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) ] + SVerticalBox::Slot() - .Padding(10) - .FillHeight(1.0f) // Allows the scroll box to take up remaining space + .Padding(10) + .FillHeight(1.0f) // Allows the scroll box to take up remaining space [ SNew(SScrollBox) .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) @@ -181,46 +183,49 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) // .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) // ] ]; - + EmailTextBox->SetText(FText::FromString(EditorCache::GetString(CacheKeyEmail))); Initialize(); } void SRpmDeveloperLoginWidget::Initialize() { - if(bIsInitialized) return; + if (bIsInitialized) + { + return; + } const FDeveloperAuth DevAuthData = DevAuthTokenCache::GetAuthData(); - if(!DeveloperAuthApi.IsValid()) + if (!DeveloperAuthApi.IsValid()) { DeveloperAuthApi = MakeUnique(); - + DeveloperAuthApi->OnLoginResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleLoginResponse); } - + if (!AssetApi.IsValid()) { AssetApi = MakeUnique(); - if(!DevAuthData.IsDemo) + if (!DevAuthData.IsDemo) { AssetApi->SetAuthenticationStrategy(new DeveloperTokenAuthStrategy()); } AssetApi->OnListAssetsResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleBaseModelListResponse); - } - if(!DeveloperAccountApi.IsValid()) + if (!DeveloperAccountApi.IsValid()) { - DeveloperAccountApi = MakeUnique(nullptr); - if(!DevAuthData.IsDemo) + if (!DevAuthData.IsDemo) { DeveloperAccountApi->SetAuthenticationStrategy(new DeveloperTokenAuthStrategy()); } - DeveloperAccountApi->OnOrganizationResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); - DeveloperAccountApi->OnApplicationListResponse.BindRaw(this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); + DeveloperAccountApi->OnOrganizationResponse.BindRaw( + this, &SRpmDeveloperLoginWidget::HandleOrganizationListResponse); + DeveloperAccountApi->OnApplicationListResponse.BindRaw( + this, &SRpmDeveloperLoginWidget::HandleApplicationListResponse); } bIsInitialized = true; - if(bIsLoggedIn) + if (bIsLoggedIn) { GetOrgList(); } @@ -232,77 +237,78 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) const FVector2D ImageSize(100.0f, 100.0f); ContentBox->AddSlot() - .AutoHeight() - .Padding(5) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(5) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .HAlign(HAlign_Left) - [ - SAssignNew(ImageWidget, SImage).// The image will be set later after downloading - DesiredSizeOverride(ImageSize) - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(5, 5) - [ - SNew(SBox) - .WidthOverride(100.0f) // Match button width to image width - [ - SNew(SButton) + .AutoHeight() + .Padding(5) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(5) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Left) + [ + SAssignNew(ImageWidget, SImage). // The image will be set later after downloading + DesiredSizeOverride(ImageSize) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(5, 5) + [ + SNew(SBox) + .WidthOverride(100.0f) // Match button width to image width + [ + SNew(SButton) .Text(FText::FromString("Load Style")) .OnClicked_Lambda([this, StyleAsset]() -> FReply - { - OnLoadStyleClicked(StyleAsset.Id); - return FReply::Handled(); - }) - ] - ] - ] - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Top) - .Padding(10, 10, 0, 0) // Padding from the left side of the Image & Button stack - [ - SNew(SEditableText) + { + OnLoadStyleClicked(StyleAsset.Id); + return FReply::Handled(); + }) + ] + ] + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Top) + .Padding(10, 10, 0, 0) // Padding from the left side of the Image & Button stack + [ + SNew(SEditableText) .Text(FText::FromString(FString::Printf(TEXT("ID: %s"), *StyleAsset.Id))) .IsReadOnly(true) // Prevents the text from being editable .IsCaretMovedWhenGainFocus(false) // Caret won't appear when focused, keeping it look like plain text .SelectAllTextWhenFocused(false) // Prevents selecting all text when focused .MinDesiredWidth(100.0f) // Minimum width for the text box - ] - ]; + ] + ]; DownloadImage(StyleAsset.IconUrl, [ImageWidget](UTexture2D* Texture) { - if (ImageWidget.IsValid()) - { - SetImageFromTexture(Texture, ImageWidget); - } + if (ImageWidget.IsValid()) + { + SetImageFromTexture(Texture, ImageWidget); + } }); } void SRpmDeveloperLoginWidget::DownloadImage(const FString& Url, TFunction Callback) { TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->OnProcessRequestComplete().BindLambda([Callback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) - { - if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) + HttpRequest->OnProcessRequestComplete().BindLambda( + [Callback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { - // Create a texture from the image data - UTexture2D* Texture = CreateTextureFromImageData(Response->GetContent()); - if (Texture) + if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) { - Callback(Texture); + // Create a texture from the image data + UTexture2D* Texture = CreateTextureFromImageData(Response->GetContent()); + if (Texture) + { + Callback(Texture); + } } - } - }); + }); HttpRequest->SetURL(Url); HttpRequest->SetVerb(TEXT("GET")); @@ -323,7 +329,8 @@ void SRpmDeveloperLoginWidget::SetImageFromTexture(UTexture2D* Texture, const TS UTexture2D* SRpmDeveloperLoginWidget::CreateTextureFromImageData(const TArray& ImageData) { - IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); + IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked( + FName("ImageWrapper")); EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); if (ImageFormat != EImageFormat::Invalid) @@ -331,7 +338,7 @@ UTexture2D* SRpmDeveloperLoginWidget::CreateTextureFromImageData(const TArray ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) { - TArray UncompressedBGRA ; + TArray UncompressedBGRA; if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) { UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight()); @@ -409,7 +416,7 @@ void SRpmDeveloperLoginWidget::GetOrgList() void SRpmDeveloperLoginWidget::HandleLoginResponse(const FDeveloperLoginResponse& Response, bool bWasSuccessful) { - if(bWasSuccessful) + if (bWasSuccessful) { UserName = Response.Data.Name; FDeveloperAuth AuthData = FDeveloperAuth(Response.Data, false); @@ -423,17 +430,16 @@ void SRpmDeveloperLoginWidget::HandleLoginResponse(const FDeveloperLoginResponse } void SRpmDeveloperLoginWidget::HandleOrganizationListResponse(const FOrganizationListResponse& Response, - bool bWasSuccessful) + bool bWasSuccessful) { - if (bWasSuccessful) - { - if(Response.Data.Num() == 0) + { + if (Response.Data.Num() == 0) { UE_LOG(LogTemp, Error, TEXT("No organizations found")); return; } - + FApplicationListRequest Request; Request.Params.Add("organizationId", Response.Data[0].Id); DeveloperAccountApi->ListApplicationsAsync(Request); @@ -446,7 +452,7 @@ void SRpmDeveloperLoginWidget::HandleOrganizationListResponse(const FOrganizatio void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationListResponse& Response, - bool bWasSuccessful) + bool bWasSuccessful) { if (bWasSuccessful) { @@ -456,17 +462,18 @@ void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationL for (const FApplication& App : UserApplications) { Items.Add(App.Name); - if(App.Id == Settings->ApplicationId) + if (App.Id == Settings->ApplicationId) { Active = App.Name; } } - if(Active.IsEmpty() && Items.Num() > 0) + if (Active.IsEmpty() && Items.Num() > 0) { - OnComboBoxSelectionChanged(MakeShared(Items[0]), ESelectInfo::Direct); + const auto NewActiveItem = MakeShared(Items[0]); + OnComboBoxSelectionChanged(NewActiveItem, ESelectInfo::Direct); + SelectedApplicationTextBlock->SetText(FText::FromString(*NewActiveItem)); } PopulateComboBoxItems(Items, Active); - } else { @@ -489,19 +496,20 @@ void SRpmDeveloperLoginWidget::PopulateComboBoxItems(const TArray& Item FText SRpmDeveloperLoginWidget::GetSelectedComboBoxItemText() const { - return SelectedComboBoxItem.IsValid() && *SelectedComboBoxItem != "" ? FText::FromString(*SelectedComboBoxItem) : FText::FromString("Select an option"); + return SelectedComboBoxItem.IsValid() && !SelectedComboBoxItem->IsEmpty() + ? FText::FromString(*SelectedComboBoxItem) + : FText::FromString("Select an option"); } - void SRpmDeveloperLoginWidget::OnComboBoxSelectionChanged(TSharedPtr NewValue, ESelectInfo::Type SelectInfo) { SelectedComboBoxItem = NewValue; FApplication* application = UserApplications.FindByPredicate([&](FApplication item) -{ - return item.Name == *NewValue; -}); - if(application) + { + return item.Name == *NewValue; + }); + if (application) { URpmDeveloperSettings* RpmSettings = GetMutableDefault(); RpmSettings->ApplicationId = application->Id; @@ -524,7 +532,7 @@ FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() // Unset the authentication strategy for the APIs DeveloperAccountApi->SetAuthenticationStrategy(nullptr); AssetApi->SetAuthenticationStrategy(nullptr); - + GetOrgList(); return FReply::Handled(); } @@ -535,14 +543,14 @@ FReply SRpmDeveloperLoginWidget::OnLogoutClicked() Settings = GetMutableDefault(); Settings->Reset(); - + // Clear the content box to remove all child widgets if (ContentBox.IsValid()) { ContentBox->ClearChildren(); } ComboBoxItems.Empty(); - + DevAuthTokenCache::ClearAuthData(); SetLoggedInState(false); return FReply::Handled(); @@ -551,7 +559,7 @@ FReply SRpmDeveloperLoginWidget::OnLogoutClicked() void SRpmDeveloperLoginWidget::LoadBaseModelList() { URpmDeveloperSettings* RpmSettings = GetMutableDefault(); - if(RpmSettings->ApplicationId.IsEmpty()) + if (RpmSettings->ApplicationId.IsEmpty()) { UE_LOG(LogTemp, Error, TEXT("Application ID is empty, unable to load base models.")); return; diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h index e6817e1..d44ec3d 100644 --- a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h @@ -33,6 +33,7 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget TSharedPtr ContentBox; TSharedPtr EmailTextBox; TSharedPtr PasswordTextBox; + TSharedPtr SelectedApplicationTextBlock; TSharedPtr SelectedComboBoxItem; TArray> ComboBoxItems; TMap CharacterStyleAssets; From 753cfa4cdbd5ef921d78b36511404f553a7697f9 Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 14 Aug 2024 10:19:04 +0300 Subject: [PATCH 18/74] feat: refactor to use Image loader class --- .../Private/Samples/AssetButtonWidget.cpp | 112 ------------- .../Private/Samples/RpmAssetButtonWidget.cpp | 34 ++++ .../Private/Samples/RpmAssetCard.cpp | 19 +++ ...pp => RpmCharacterCustomizationWidget.cpp} | 26 +-- Source/RpmNextGen/Public/RpmImageLoader.cpp | 158 ++++++++++++++++++ Source/RpmNextGen/Public/RpmImageLoader.h | 22 +++ ...tButtonWidget.h => RpmAssetButtonWidget.h} | 7 +- .../Public/Samples/RpmAssetCardWidget.h | 22 +++ ...et.h => RpmCharacterCustomizationWidget.h} | 4 +- Source/RpmNextGen/RpmNextGen.Build.cs | 10 +- .../Private/SRpmDeveloperLoginWidget.cpp | 91 +--------- .../Public/SRpmDeveloperLoginWidget.h | 3 - .../RpmNextGenEditor.Build.cs | 9 +- 13 files changed, 285 insertions(+), 232 deletions(-) delete mode 100644 Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp create mode 100644 Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp create mode 100644 Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp rename Source/RpmNextGen/Private/Samples/{CharacterCustomizationWidget.cpp => RpmCharacterCustomizationWidget.cpp} (78%) create mode 100644 Source/RpmNextGen/Public/RpmImageLoader.cpp create mode 100644 Source/RpmNextGen/Public/RpmImageLoader.h rename Source/RpmNextGen/Public/Samples/{AssetButtonWidget.h => RpmAssetButtonWidget.h} (75%) create mode 100644 Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h rename Source/RpmNextGen/Public/Samples/{CharacterCustomizationWidget.h => RpmCharacterCustomizationWidget.h} (94%) diff --git a/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp deleted file mode 100644 index 1bc5507..0000000 --- a/Source/RpmNextGen/Private/Samples/AssetButtonWidget.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#include "Samples/AssetButtonWidget.h" -#include "HttpModule.h" -#include "IImageWrapper.h" -#include "IImageWrapperModule.h" -#include "Async/Async.h" -#include "Components/Button.h" -#include "Components/Image.h" -#include "Interfaces/IHttpRequest.h" -#include "Interfaces/IHttpResponse.h" - -void UAssetButtonWidget::NativeConstruct() -{ - Super::NativeConstruct(); - if (AssetButton) - { - AssetButton->OnClicked.AddDynamic(this, &UAssetButtonWidget::HandleButtonClicked); - } -} - -void UAssetButtonWidget::InitializeButton(const FAsset& InAssetData, const FVector2D& InImageSize) -{ - AssetData = InAssetData; - - if (AssetImage) - { - // Set the image size - AssetImage->SetDesiredSizeOverride(InImageSize); - - // Set the image from URL - SetImageFromURL(AssetImage, AssetData.IconUrl); - } -} - -void UAssetButtonWidget::HandleButtonClicked() -{ - OnAssetButtonClicked.Broadcast(AssetData); -} - -void UAssetButtonWidget::SetImageFromURL(UImage* Image, const FString& URL) -{ - if (!Image) - { - UE_LOG(LogTemp, Error, TEXT("SetImageFromURL: Image is null")); - return; - } - - TWeakObjectPtr WeakImagePtr(Image); - - TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->OnProcessRequestComplete().BindUObject(this, &UAssetButtonWidget::OnImageDownloaded, WeakImagePtr); - HttpRequest->SetURL(URL); - HttpRequest->SetVerb("GET"); - HttpRequest->ProcessRequest(); -} - -void UAssetButtonWidget::OnImageDownloaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TWeakObjectPtr Image) -{ - if (!Image.IsValid()) - { - UE_LOG(LogTemp, Error, TEXT("OnImageDownloaded: Image is null or no longer valid")); - return; - } - - if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) - { - TArray ImageData = Response->GetContent(); - IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); - EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); - if (ImageFormat != EImageFormat::Invalid) - { - TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); - - if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) - { - TArray UncompressedBGRA; - if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) - { - UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); - if (Texture) - { - void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); - FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); - Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); - - Texture->UpdateResource(); - - // Set the brush from the texture - FSlateBrush Brush; - Brush.SetResourceObject(Texture); - Brush.ImageSize = Image->Brush.ImageSize; // Use the same size as the Image - AsyncTask(ENamedThreads::GameThread, [Image, Brush]() { - if (Image.IsValid()) - { - Image->SetBrush(Brush); - } - }); - } - } - } - } - else - { - UE_LOG(LogTemp, Error, TEXT("Unsupported image format: %s"), *Request->GetURL()); - } - } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to download image from URL: %s"), *Request->GetURL()); - } -} \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp new file mode 100644 index 0000000..6db0e12 --- /dev/null +++ b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp @@ -0,0 +1,34 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "Samples/RpmAssetButtonWidget.h" +#include "RpmImageLoader.h" +#include "Components/Button.h" +#include "Components/Image.h" + +void URpmAssetButtonWidget::NativeConstruct() +{ + Super::NativeConstruct(); + if (AssetButton) + { + AssetButton->OnClicked.AddDynamic(this, &URpmAssetButtonWidget::HandleButtonClicked); + } +} + +void URpmAssetButtonWidget::InitializeButton(const FAsset& InAssetData, const FVector2D& InImageSize) +{ + AssetData = InAssetData; + + if (AssetImage) + { + // Set the image size + AssetImage->SetDesiredSizeOverride(InImageSize); + + FRpmImageLoader ImageLoader; + ImageLoader.LoadImageFromURL(AssetImage, AssetData.IconUrl); + } +} + +void URpmAssetButtonWidget::HandleButtonClicked() +{ + OnAssetButtonClicked.Broadcast(AssetData); +} \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp new file mode 100644 index 0000000..455cc96 --- /dev/null +++ b/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp @@ -0,0 +1,19 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "Api/Assets/Models/Asset.h" +#include "Samples/RpmAssetCardWidget.h" + +void URpmAssetCardWidget::NativeConstruct() +{ + Super::NativeConstruct(); +} + +void URpmAssetCardWidget::Initialize(FAsset Asset) +{ + // // Set the asset data + // AssetData = Asset; + // + // // Load the image + // LoadImage(AssetData.IconUrl); +} + diff --git a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp similarity index 78% rename from Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp rename to Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp index da3b9d8..c8f8980 100644 --- a/Source/RpmNextGen/Private/Samples/CharacterCustomizationWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp @@ -1,5 +1,5 @@ -#include "Samples/CharacterCustomizationWidget.h" -#include "Samples/AssetButtonWidget.h" +#include "Samples/RpmCharacterCustomizationWidget.h" +#include "Samples/RpmAssetButtonWidget.h" #include "HttpModule.h" #include "Settings/RpmDeveloperSettings.h" #include "Components/HorizontalBox.h" @@ -10,7 +10,7 @@ #include "Api/Characters/CharacterApi.h" #include "Interfaces/IHttpResponse.h" -void UCharacterCustomizationWidget::NativeConstruct() +void URpmCharacterCustomizationWidget::NativeConstruct() { Super::NativeConstruct(); URpmDeveloperSettings* Settings = GetMutableDefault(); @@ -18,7 +18,7 @@ void UCharacterCustomizationWidget::NativeConstruct() InitializeCustomizationOptions(); } -void UCharacterCustomizationWidget::InitializeCustomizationOptions() +void URpmCharacterCustomizationWidget::InitializeCustomizationOptions() { URpmDeveloperSettings* Settings = GetMutableDefault(); FString ApiBaseUrl = Settings->GetApiBaseUrl(); @@ -60,7 +60,7 @@ void UCharacterCustomizationWidget::InitializeCustomizationOptions() UE_LOG(LogTemp, Warning, TEXT("Made request")); } -void UCharacterCustomizationWidget::OnAssetsFetched(const FAssetListResponse& AssetListResponse, bool bWasSuccessful) +void URpmCharacterCustomizationWidget::OnAssetsFetched(const FAssetListResponse& AssetListResponse, bool bWasSuccessful) { UE_LOG(LogTemp, Warning, TEXT("OnAssetsFetched called. Success: %s"), bWasSuccessful ? TEXT("true") : TEXT("false")); @@ -77,7 +77,7 @@ void UCharacterCustomizationWidget::OnAssetsFetched(const FAssetListResponse& As OnAssetsFetchedDelegate.Broadcast(bWasSuccessful, AssetDataArray); } -void UCharacterCustomizationWidget::PopulateBoxWithFilter(UPanelWidget* ParentBox, const FString& AssetType) +void URpmCharacterCustomizationWidget::PopulateBoxWithFilter(UPanelWidget* ParentBox, const FString& AssetType) { if (!ParentBox) { @@ -94,7 +94,7 @@ void UCharacterCustomizationWidget::PopulateBoxWithFilter(UPanelWidget* ParentBo } } -void UCharacterCustomizationWidget::PopulateBox(UPanelWidget* ParentBox) +void URpmCharacterCustomizationWidget::PopulateBox(UPanelWidget* ParentBox) { if (!ParentBox) { @@ -108,7 +108,7 @@ void UCharacterCustomizationWidget::PopulateBox(UPanelWidget* ParentBox) } } -void UCharacterCustomizationWidget::ClearBox(UPanelWidget* ParentBox) +void URpmCharacterCustomizationWidget::ClearBox(UPanelWidget* ParentBox) { if (!ParentBox) { @@ -119,14 +119,14 @@ void UCharacterCustomizationWidget::ClearBox(UPanelWidget* ParentBox) ParentBox->ClearChildren(); } -void UCharacterCustomizationWidget::UpdateCustomizationOptions(UPanelWidget* ParentBox) +void URpmCharacterCustomizationWidget::UpdateCustomizationOptions(UPanelWidget* ParentBox) { ClearBox(ParentBox); InitializeCustomizationOptions(); PopulateBox(ParentBox); } -void UCharacterCustomizationWidget::CreateAssetWidget(const FAsset& AssetData, UPanelWidget* ParentBox) +void URpmCharacterCustomizationWidget::CreateAssetWidget(const FAsset& AssetData, UPanelWidget* ParentBox) { if (!ParentBox) { @@ -140,16 +140,16 @@ void UCharacterCustomizationWidget::CreateAssetWidget(const FAsset& AssetData, U return; } - UAssetButtonWidget* AssetButton = CreateWidget(this, AssetButtonWidgetClass); + URpmAssetButtonWidget* AssetButton = CreateWidget(this, AssetButtonWidgetClass); if (AssetButton) { AssetButton->InitializeButton(AssetData, ImageSize); - AssetButton->OnAssetButtonClicked.AddDynamic(this, &UCharacterCustomizationWidget::OnAssetButtonClicked); + AssetButton->OnAssetButtonClicked.AddDynamic(this, &URpmCharacterCustomizationWidget::OnAssetButtonClicked); ParentBox->AddChild(AssetButton); } } -void UCharacterCustomizationWidget::OnAssetButtonClicked(const FAsset& AssetData) +void URpmCharacterCustomizationWidget::OnAssetButtonClicked(const FAsset& AssetData) { FString GlbUrl = AssetData.GlbUrl; UE_LOG(LogTemp, Log, TEXT("Asset Button Clicked: %s"), *GlbUrl); diff --git a/Source/RpmNextGen/Public/RpmImageLoader.cpp b/Source/RpmNextGen/Public/RpmImageLoader.cpp new file mode 100644 index 0000000..1123d1c --- /dev/null +++ b/Source/RpmNextGen/Public/RpmImageLoader.cpp @@ -0,0 +1,158 @@ +#include "RpmImageLoader.h" +#include "HttpModule.h" +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" +#include "IImageWrapper.h" +#include "IImageWrapperModule.h" +#include "Async/Async.h" +#include "Modules/ModuleManager.h" +#include "Engine/Texture2D.h" +#include "Components/Image.h" + +void FRpmImageLoader::LoadImageFromURL(UImage* Image, const FString& URL) +{ + if (!Image) + { + UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: Image is null")); + return; + } + + TWeakObjectPtr WeakImagePtr(Image); + + TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->OnProcessRequestComplete().BindRaw(this, &FRpmImageLoader::OnImageDownloadedForUImage, WeakImagePtr); + HttpRequest->SetURL(URL); + HttpRequest->SetVerb("GET"); + HttpRequest->ProcessRequest(); +} + +void FRpmImageLoader::LoadImageFromURL(TSharedPtr ImageWidget, const FString& URL) +{ + if (!ImageWidget.IsValid()) + { + UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: SImage widget is invalid")); + return; + } + + TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->OnProcessRequestComplete().BindRaw(this, &FRpmImageLoader::OnImageDownloadedForSImage, ImageWidget); + HttpRequest->SetURL(URL); + HttpRequest->SetVerb("GET"); + HttpRequest->ProcessRequest(); +} + +void FRpmImageLoader::OnImageDownloadedForUImage(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TWeakObjectPtr Image) +{ + if (!Image.IsValid()) + { + UE_LOG(LogTemp, Error, TEXT("OnImageDownloaded: Image is null or no longer valid")); + return; + } + + if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) + { + TArray ImageData = Response->GetContent(); + IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); + EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); + if (ImageFormat != EImageFormat::Invalid) + { + TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); + + if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) + { + TArray UncompressedBGRA; + if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) + { + UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); + if (Texture) + { + void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); + Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); + + Texture->UpdateResource(); + + FSlateBrush Brush; + Brush.SetResourceObject(Texture); + Brush.ImageSize = Image->Brush.ImageSize; + AsyncTask(ENamedThreads::GameThread, [Image, Brush]() { + if (Image.IsValid()) + { + Image->SetBrush(Brush); + } + }); + } + } + } + } + else + { + UE_LOG(LogTemp, Error, TEXT("Unsupported image format: %s"), *Request->GetURL()); + } + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to download image from URL: %s"), *Request->GetURL()); + } +} + +void FRpmImageLoader::OnImageDownloadedForSImage(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TSharedPtr ImageWidget) +{ + if (!ImageWidget.IsValid()) + { + UE_LOG(LogTemp, Error, TEXT("OnImageDownloadedForSImage: SImage widget is invalid")); + return; + } + + UTexture2D* Texture = nullptr; + if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) + { + Texture = CreateTextureFromImageData(Response->GetContent()); + } + + if (Texture) + { + FSlateBrush* Brush = new FSlateBrush(); + Brush->SetResourceObject(Texture); + Brush->ImageSize = FVector2D(100.0f, 100.0f); // You can adjust the size as needed + + AsyncTask(ENamedThreads::GameThread, [ImageWidget, Brush]() { + ImageWidget->SetImage(Brush); + }); + } + else + { + UE_LOG(LogTemp, Error, TEXT("Failed to download or create texture from URL: %s"), *Request->GetURL()); + } +} + +UTexture2D* FRpmImageLoader::CreateTextureFromImageData(const TArray& ImageData) +{ + IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); + EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); + + if (ImageFormat != EImageFormat::Invalid) + { + TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); + if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) + { + TArray UncompressedBGRA; + if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) + { + UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); + if (Texture) + { + void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); + Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); + + Texture->UpdateResource(); + return Texture; + } + } + } + } + + return nullptr; +} + diff --git a/Source/RpmNextGen/Public/RpmImageLoader.h b/Source/RpmNextGen/Public/RpmImageLoader.h new file mode 100644 index 0000000..1dbbf54 --- /dev/null +++ b/Source/RpmNextGen/Public/RpmImageLoader.h @@ -0,0 +1,22 @@ +#pragma once + +#pragma once + +#include "CoreMinimal.h" +#include "Interfaces/IHttpRequest.h" +#include "Engine/Texture2D.h" +#include "Components/Image.h" + +class RPMNEXTGEN_API FRpmImageLoader +{ +public: + FRpmImageLoader() = default; + + void LoadImageFromURL(UImage* Image, const FString& URL); + void LoadImageFromURL(TSharedPtr ImageWidget, const FString& URL); + +private: + void OnImageDownloadedForUImage(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TWeakObjectPtr Image); + void OnImageDownloadedForSImage(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TSharedPtr ImageWidget); + UTexture2D* CreateTextureFromImageData(const TArray& ImageData); +}; \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Samples/AssetButtonWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h similarity index 75% rename from Source/RpmNextGen/Public/Samples/AssetButtonWidget.h rename to Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h index f231e46..1fdef9b 100644 --- a/Source/RpmNextGen/Public/Samples/AssetButtonWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h @@ -6,7 +6,7 @@ #include "Api/Assets/Models/Asset.h" #include "Blueprint/UserWidget.h" #include "Interfaces/IHttpRequest.h" -#include "AssetButtonWidget.generated.h" +#include "RpmAssetButtonWidget.generated.h" class UImage; class UButton; @@ -17,7 +17,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAssetButtonClicked, const FAsset& * */ UCLASS() -class RPMNEXTGEN_API UAssetButtonWidget : public UUserWidget +class RPMNEXTGEN_API URpmAssetButtonWidget : public UUserWidget { GENERATED_BODY() @@ -42,7 +42,4 @@ class RPMNEXTGEN_API UAssetButtonWidget : public UUserWidget UFUNCTION() void HandleButtonClicked(); - - void SetImageFromURL(UImage* Image, const FString& URL); - void OnImageDownloaded(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TWeakObjectPtr Image); }; diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h new file mode 100644 index 0000000..5f7ca37 --- /dev/null +++ b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h @@ -0,0 +1,22 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "RpmAssetCardWidget.generated.h" + +DECLARE_DELEGATE_OneParam(FOnImageLoaded, UTexture2D*); + +UCLASS() +class RPMNEXTGEN_API URpmAssetCardWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + virtual void NativeConstruct() override; + + void Initialize(FAsset Asset); + + void LoadImage(const FString& URL); +}; diff --git a/Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h b/Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h similarity index 94% rename from Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h rename to Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h index cd76b18..c31d2fb 100644 --- a/Source/RpmNextGen/Public/Samples/CharacterCustomizationWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h @@ -6,7 +6,7 @@ #include "Api/Assets/Models/Asset.h" #include "Blueprint/UserWidget.h" #include "Api/Characters/CharacterApi.h" -#include "CharacterCustomizationWidget.generated.h" +#include "RpmCharacterCustomizationWidget.generated.h" class FCharacterApi; struct FAssetListResponse; @@ -21,7 +21,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAssetSelected, const FAsset&, Ass * */ UCLASS() -class RPMNEXTGEN_API UCharacterCustomizationWidget : public UUserWidget +class RPMNEXTGEN_API URpmCharacterCustomizationWidget : public UUserWidget { GENERATED_BODY() diff --git a/Source/RpmNextGen/RpmNextGen.Build.cs b/Source/RpmNextGen/RpmNextGen.Build.cs index 0b5af8d..07e7a1c 100644 --- a/Source/RpmNextGen/RpmNextGen.Build.cs +++ b/Source/RpmNextGen/RpmNextGen.Build.cs @@ -30,8 +30,8 @@ public RpmNextGen(ReadOnlyTargetRules Target) : base(Target) "DeveloperSettings", "glTFRuntime", "UnrealEd", - "UMG", - "ImageWrapper" + "Slate", + "SlateCore", } ); @@ -40,12 +40,12 @@ public RpmNextGen(ReadOnlyTargetRules Target) : base(Target) new string[] { "CoreUObject", - "Engine", - "Slate", - "SlateCore", + "Engine", "Json", "JsonUtilities", "HTTP", + "UMG", + "ImageWrapper", } ); diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 3d1526e..aeeb590 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -4,16 +4,13 @@ #include "SRpmDeveloperLoginWidget.h" #include "Auth/DevAuthTokenCache.h" #include "EditorCache.h" -#include "HttpModule.h" -#include "IImageWrapper.h" #include "SlateOptMacros.h" #include "Api/Assets/Models/AssetListRequest.h" #include "Api/Assets/Models/AssetListResponse.h" #include "Auth/DeveloperAccountApi.h" #include "Auth/DeveloperTokenAuthStrategy.h" -#include "Interfaces/IHttpResponse.h" #include "Widgets/Input/SEditableTextBox.h" -#include "IImageWrapperModule.h" +#include "RpmImageLoader.h" #include "Auth/DeveloperAuthApi.h" #include "Auth/DeveloperLoginRequest.h" #include "Settings/RpmDeveloperSettings.h" @@ -279,92 +276,10 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) ] ]; - DownloadImage(StyleAsset.IconUrl, [ImageWidget](UTexture2D* Texture) - { - if (ImageWidget.IsValid()) - { - SetImageFromTexture(Texture, ImageWidget); - } - }); -} - -void SRpmDeveloperLoginWidget::DownloadImage(const FString& Url, TFunction Callback) -{ - TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->OnProcessRequestComplete().BindLambda([Callback, this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) - { - if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) - { - // Create a texture from the image data - UTexture2D* Texture = CreateTextureFromImageData(Response->GetContent()); - if (Texture) - { - Callback(Texture); - } - } - }); - - HttpRequest->SetURL(Url); - HttpRequest->SetVerb(TEXT("GET")); - HttpRequest->ProcessRequest(); -} - -void SRpmDeveloperLoginWidget::SetImageFromTexture(UTexture2D* Texture, const TSharedPtr& ImageWidget) -{ - const FVector2D DesiredSize(100.0f, 100.0f); - if (Texture) - { - FSlateBrush* Brush = new FSlateBrush(); - Brush->SetResourceObject(Texture); - Brush->ImageSize = DesiredSize; - ImageWidget->SetImage(Brush); - } -} - -UTexture2D* SRpmDeveloperLoginWidget::CreateTextureFromImageData(const TArray& ImageData) -{ - IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); - EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); - - if (ImageFormat != EImageFormat::Invalid) - { - TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); - if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) - { - TArray UncompressedBGRA ; - if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) - { - UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight()); - if (Texture) - { - void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); - FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); - Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); - - Texture->UpdateResource(); - return Texture; - } - UE_LOG(LogTemp, Error, TEXT("Failed to create texture: Texture is null.")); - } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to get raw image data.")); - } - } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to set compressed data or ImageWrapper is invalid.")); - } - } - else - { - UE_LOG(LogTemp, Error, TEXT("Invalid image format detected.")); - } - - return nullptr; + FRpmImageLoader ImageLoader; + ImageLoader.LoadImageFromURL(ImageWidget, StyleAsset.IconUrl); } - void SRpmDeveloperLoginWidget::OnLoadStyleClicked(const FString& StyleId) { AssetLoader = FEditorAssetLoader(); diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h index 3952120..499dd46 100644 --- a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h @@ -56,7 +56,6 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget FReply OnUseDemoAccountClicked(); FReply OnLogoutClicked(); - static void SetImageFromTexture(UTexture2D* Texture, const TSharedPtr& ImageWidget); void Initialize(); void GetOrgList(); void LoadBaseModelList(); @@ -69,6 +68,4 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget void PopulateComboBoxItems(const TArray& Items); void OnComboBoxSelectionChanged(TSharedPtr NewValue, ESelectInfo::Type SelectInfo); void AddCharacterStyle(const FAsset& StyleAsset); - void DownloadImage(const FString& Url, TFunction Callback); - UTexture2D* CreateTextureFromImageData(const TArray& ImageData); }; diff --git a/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs b/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs index 9235a3f..1fe1b7b 100644 --- a/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs +++ b/Source/RpmNextGenEditor/RpmNextGenEditor.Build.cs @@ -29,12 +29,13 @@ public RpmNextGenEditor(ReadOnlyTargetRules Target) : base(Target) "HTTP", "JsonUtilities", "RpmNextGen", - "ImageWrapper", "EditorStyle", "glTFRuntime", "TransientObjectSaver", "UnrealEd", "PropertyEditor", + "Slate", + "SlateCore", } ); @@ -48,9 +49,9 @@ public RpmNextGenEditor(ReadOnlyTargetRules Target) : base(Target) "UnrealEd", "ToolMenus", "CoreUObject", - "Slate", - "SlateCore", - "Json" + "Json", + "UMG", + "ImageWrapper", } ); From 4076399b25df62700b86f9d93bd67b272b545251 Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 14 Aug 2024 10:24:08 +0300 Subject: [PATCH 19/74] fix: remove uasset generation --- Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 364764d..75a86f1 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -19,7 +19,7 @@ void FEditorAssetLoader::OnAssetDownloadComplete(FString FilePath, UglTFRuntimeA if(bWasSuccessful) { LoadGltfAssetToWorld(gltfAsset); - SaveAsUAsset(gltfAsset, FilePath); + //SaveAsUAsset(gltfAsset, FilePath); } } @@ -71,7 +71,6 @@ void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) { UE_LOG(LogTemp, Error, TEXT("Failed to spawn AglTFRuntimeAssetActor in the editor world")); } - //return; } From 781848d1b3b9e96b2415670ff9c7f3bca66bc2c7 Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Wed, 14 Aug 2024 11:02:34 +0300 Subject: [PATCH 20/74] feat: add error message, when api key is missing --- .../Private/Api/Characters/CharacterApi.cpp | 77 ++++++++++++------- .../Public/Api/Characters/CharacterApi.h | 16 ++-- 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp index 82c5cb3..632cecb 100644 --- a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp +++ b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp @@ -7,10 +7,12 @@ #include "Interfaces/IHttpResponse.h" #include "Settings/RpmDeveloperSettings.h" +DEFINE_LOG_CATEGORY(ReadyPlayerMe); + FCharacterApi::FCharacterApi() { - URpmDeveloperSettings *Settings = GetMutableDefault(); - BaseUrl = FString::Printf(TEXT("%s/v1/characters"), *Settings->GetApiBaseUrl()) ; + URpmDeveloperSettings* Settings = GetMutableDefault(); + BaseUrl = FString::Printf(TEXT("%s/v1/characters"), *Settings->GetApiBaseUrl()); Http = &FHttpModule::Get(); } @@ -30,7 +32,7 @@ void FCharacterApi::CreateAsync(const FCharacterCreateRequest& Request) void FCharacterApi::UpdateAsync(const FCharacterUpdateRequest& Request) { FApiRequest ApiRequest; - ApiRequest.Url = FString::Printf(TEXT("%s/%s"), *BaseUrl, *Request.Id ); + ApiRequest.Url = FString::Printf(TEXT("%s/%s"), *BaseUrl, *Request.Id); ApiRequest.Method = PATCH; ApiRequest.Payload = ConvertToJsonString(Request); DispatchRaw(ApiRequest, Update); @@ -39,7 +41,7 @@ void FCharacterApi::UpdateAsync(const FCharacterUpdateRequest& Request) void FCharacterApi::FindByIdAsync(const FCharacterFindByIdRequest& Request) { FApiRequest ApiRequest; - ApiRequest.Url = FString::Printf(TEXT("%s/%s"), *BaseUrl, *Request.Id ); + ApiRequest.Url = FString::Printf(TEXT("%s/%s"), *BaseUrl, *Request.Id); ApiRequest.Method = GET; DispatchRaw(ApiRequest, FindById); } @@ -59,62 +61,78 @@ void FCharacterApi::DispatchRaw(const FApiRequest& Data, const ECharacterRespons Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); //TODO move to auth strategy - URpmDeveloperSettings *Settings = GetMutableDefault(); + URpmDeveloperSettings* Settings = GetMutableDefault(); Request->SetHeader(TEXT("X-API-KEY"), Settings->ApiKey); - UE_LOG(LogTemp, Warning, TEXT("Making request to Url: %s | Verb: %s with data %s"), *Data.Url, *Data.GetVerb(), *Data.Payload); + UE_LOG(LogTemp, Warning, TEXT("Making request to Url: %s | Verb: %s with data %s"), *Data.Url, *Data.GetVerb(), + *Data.Payload); for (const auto& Header : Data.Headers) { - if(!Request->GetHeader(Header.Key).IsEmpty()) + if (!Request->GetHeader(Header.Key).IsEmpty()) + { continue; + } Request->SetHeader(Header.Key, Header.Value); - } if (!Data.Payload.IsEmpty()) { Request->SetContentAsString(Data.Payload); } - Request->OnProcessRequestComplete().BindLambda([this, ResponseType](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) - { - UE_LOG(LogTemp, Warning, TEXT("RunCallback")) - OnProcessResponse(Request, Response, bWasSuccessful, ResponseType); - }); + Request->OnProcessRequestComplete().BindLambda( + [this, ResponseType](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) + { + UE_LOG(LogTemp, Warning, TEXT("RunCallback")) + OnProcessResponse(Request, Response, bWasSuccessful, ResponseType); + }); Request->ProcessRequest(); } -void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, const ECharacterResponseType& ResponseType) +void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, + const ECharacterResponseType& ResponseType) { bool success = bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()); FCharacterCreateResponse CharacterCreateResponse; FCharacterUpdateResponse CharacterUpdateResponse; FCharacterFindByIdResponse CharacterFindByIdResponse; - UE_LOG(LogTemp, Warning, TEXT("Character API Response: %s. Response Code: %d"), *Response->GetContentAsString(), Response->GetResponseCode()); + + if (Response->GetResponseCode() == 401) + { + UE_LOG(ReadyPlayerMe, Error, + TEXT( + "The request to the character API failed with a 401 response code. Please ensure that your API Key or proxy is correctly configured." + )); + return; + } + UE_LOG(LogTemp, Warning, TEXT("Character API Response: %s. Response Code: %d"), *Response->GetContentAsString(), + Response->GetResponseCode()); switch (ResponseType) { case Create: - success = success && FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &CharacterCreateResponse, 0, 0); + success = success && FJsonObjectConverter::JsonObjectStringToUStruct( + Response->GetContentAsString(), &CharacterCreateResponse, 0, 0); OnCharacterCreateResponse.ExecuteIfBound(CharacterCreateResponse, success); break; case Update: - success = success && FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &CharacterUpdateResponse, 0, 0); + success = success && FJsonObjectConverter::JsonObjectStringToUStruct( + Response->GetContentAsString(), &CharacterUpdateResponse, 0, 0); OnCharacterUpdateResponse.ExecuteIfBound(CharacterUpdateResponse, success); break; case FindById: - success = success && FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &CharacterFindByIdResponse, 0, 0); + success = success && FJsonObjectConverter::JsonObjectStringToUStruct( + Response->GetContentAsString(), &CharacterFindByIdResponse, 0, 0); OnCharacterFindResponse.ExecuteIfBound(CharacterFindByIdResponse, success); break; - } -// if (bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())) -// { -// UE_LOG(LogTemp, Warning, TEXT("WebApi Response Success: %s. Response Code: %d"), *Response->GetContentAsString(), Response->GetResponseCode()); -// OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), true); -// return; -// } -// FString ErrorMessage = Response.IsValid() ? Response->GetContentAsString() : TEXT("Request failed"); -// UE_LOG(LogTemp, Warning, TEXT("WebApi request failed: %s"), *ErrorMessage); -// OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), false); + // if (bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())) + // { + // UE_LOG(LogTemp, Warning, TEXT("WebApi Response Success: %s. Response Code: %d"), *Response->GetContentAsString(), Response->GetResponseCode()); + // OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), true); + // return; + // } + // FString ErrorMessage = Response.IsValid() ? Response->GetContentAsString() : TEXT("Request failed"); + // UE_LOG(LogTemp, Warning, TEXT("WebApi request failed: %s"), *ErrorMessage); + // OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), false); } FString FCharacterApi::BuildQueryString(const TMap& QueryParams) @@ -125,7 +143,8 @@ FString FCharacterApi::BuildQueryString(const TMap& QueryParam QueryString.Append(TEXT("?")); for (const auto& Param : QueryParams) { - QueryString.Append(FString::Printf(TEXT("%s=%s&"), *FGenericPlatformHttp::UrlEncode(Param.Key), *FGenericPlatformHttp::UrlEncode(Param.Value))); + QueryString.Append(FString::Printf(TEXT("%s=%s&"), *FGenericPlatformHttp::UrlEncode(Param.Key), + *FGenericPlatformHttp::UrlEncode(Param.Value))); } QueryString.RemoveFromEnd(TEXT("&")); } diff --git a/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h b/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h index 2ef6830..47a00ab 100644 --- a/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h +++ b/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h @@ -11,8 +11,11 @@ #include "Models/CharacterUpdateRequest.h" #include "Models/CharacterUpdateResponse.h" +DECLARE_LOG_CATEGORY_EXTERN(ReadyPlayerMe, Log, All); + UENUM(BlueprintType) -enum ECharacterResponseType { +enum ECharacterResponseType +{ Create, Update, FindById @@ -22,7 +25,7 @@ DECLARE_DELEGATE_TwoParams(FOnCharacterCreateResponse, FCharacterCreateResponse, DECLARE_DELEGATE_TwoParams(FOnCharacterUpdatResponse, FCharacterUpdateResponse, bool); DECLARE_DELEGATE_TwoParams(FOnCharacterFindResponse, FCharacterFindByIdResponse, bool); -class RPMNEXTGEN_API FCharacterApi : public TSharedFromThis +class RPMNEXTGEN_API FCharacterApi : public TSharedFromThis { public: FCharacterApi(); @@ -37,19 +40,20 @@ class RPMNEXTGEN_API FCharacterApi : public TSharedFromThis& QueryParams); - + template FString ConvertToJsonString(const T& Data); FHttpModule* Http; + private: FString BaseUrl; }; From 321c7557e3af16c24345a8a5af0dc84e200c8b9e Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Wed, 14 Aug 2024 11:17:13 +0300 Subject: [PATCH 21/74] chore: clear unnecessary messages --- .../Private/Api/Characters/CharacterApi.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp index 632cecb..f343748 100644 --- a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp +++ b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp @@ -63,8 +63,7 @@ void FCharacterApi::DispatchRaw(const FApiRequest& Data, const ECharacterRespons //TODO move to auth strategy URpmDeveloperSettings* Settings = GetMutableDefault(); Request->SetHeader(TEXT("X-API-KEY"), Settings->ApiKey); - UE_LOG(LogTemp, Warning, TEXT("Making request to Url: %s | Verb: %s with data %s"), *Data.Url, *Data.GetVerb(), - *Data.Payload); + for (const auto& Header : Data.Headers) { if (!Request->GetHeader(Header.Key).IsEmpty()) @@ -103,8 +102,6 @@ void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr )); return; } - UE_LOG(LogTemp, Warning, TEXT("Character API Response: %s. Response Code: %d"), *Response->GetContentAsString(), - Response->GetResponseCode()); switch (ResponseType) { @@ -124,15 +121,6 @@ void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr OnCharacterFindResponse.ExecuteIfBound(CharacterFindByIdResponse, success); break; } - // if (bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())) - // { - // UE_LOG(LogTemp, Warning, TEXT("WebApi Response Success: %s. Response Code: %d"), *Response->GetContentAsString(), Response->GetResponseCode()); - // OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), true); - // return; - // } - // FString ErrorMessage = Response.IsValid() ? Response->GetContentAsString() : TEXT("Request failed"); - // UE_LOG(LogTemp, Warning, TEXT("WebApi request failed: %s"), *ErrorMessage); - // OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), false); } FString FCharacterApi::BuildQueryString(const TMap& QueryParams) From 53d34c809609f6688f30c57be2e74f118e948645 Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 14 Aug 2024 15:08:23 +0300 Subject: [PATCH 22/74] feat: wip UI example elements --- .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 0 -> 41203 bytes .../Blueprints/WBP_RpmAssetButton.uasset | Bin 0 -> 27883 bytes .../Blueprints/WBP_RpmAssetCard.uasset | Bin 0 -> 31131 bytes .../Blueprints/WBP_RpmCategoryPanel.uasset | Bin 0 -> 65253 bytes .../Blueprints/WPB_RpmCategoryButton.uasset | Bin 0 -> 29917 bytes .../BasicUI/Icons/T-rpm-baseModel.uasset | Bin 0 -> 6430 bytes .../Samples/BasicUI/Icons/T-rpm-bottom.uasset | Bin 0 -> 6444 bytes .../Samples/BasicUI/Icons/T-rpm-custom.uasset | Bin 0 -> 5795 bytes .../BasicUI/Icons/T-rpm-facial-hair.uasset | Bin 0 -> 6456 bytes .../BasicUI/Icons/T-rpm-glasses.uasset | Bin 0 -> 5983 bytes .../Samples/BasicUI/Icons/T-rpm-hair.uasset | Bin 0 -> 6826 bytes .../Samples/BasicUI/Icons/T-rpm-shoes.uasset | Bin 0 -> 6066 bytes .../Samples/BasicUI/Icons/T-rpm-top.uasset | Bin 0 -> 5583 bytes Content/Samples/BasicUI/RpmBasicUI.umap | Bin 0 -> 60280 bytes .../Private/Samples/RpmAssetButtonWidget.cpp | 24 +++++ .../Private/Samples/RpmAssetCard.cpp | 21 +++-- .../Private/Samples/RpmCategoryButton.cpp | 85 ++++++++++++++++++ .../Public/Samples/RpmAssetButtonWidget.h | 11 +++ .../Public/Samples/RpmAssetCardWidget.h | 20 ++++- .../Public/Samples/RpmCategoryButton.h | 64 +++++++++++++ 20 files changed, 216 insertions(+), 9 deletions(-) create mode 100644 Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset create mode 100644 Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset create mode 100644 Content/Samples/BasicUI/Blueprints/WBP_RpmAssetCard.uasset create mode 100644 Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset create mode 100644 Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset create mode 100644 Content/Samples/BasicUI/Icons/T-rpm-baseModel.uasset create mode 100644 Content/Samples/BasicUI/Icons/T-rpm-bottom.uasset create mode 100644 Content/Samples/BasicUI/Icons/T-rpm-custom.uasset create mode 100644 Content/Samples/BasicUI/Icons/T-rpm-facial-hair.uasset create mode 100644 Content/Samples/BasicUI/Icons/T-rpm-glasses.uasset create mode 100644 Content/Samples/BasicUI/Icons/T-rpm-hair.uasset create mode 100644 Content/Samples/BasicUI/Icons/T-rpm-shoes.uasset create mode 100644 Content/Samples/BasicUI/Icons/T-rpm-top.uasset create mode 100644 Content/Samples/BasicUI/RpmBasicUI.umap create mode 100644 Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp create mode 100644 Source/RpmNextGen/Public/Samples/RpmCategoryButton.h diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset new file mode 100644 index 0000000000000000000000000000000000000000..85e9657ff5446d744a4a489ed158aeb0c80df901 GIT binary patch literal 41203 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 z z860hhGQ`Ct#KgoVCP$@3C&s15B_&2jM;qc25(m?hI@E+X14(Xi7Vs%TNfF~?rW2*wG=JLEYHV=vxg6Dv=Kr}d7#b^#Cxfdz-SaVcx&F^dh8YPZy{ut9I zEvzlmT3aU8gpo$^Z_l~{UFwshaOloLo(ja#O&dF?R|{Z`%(j#vYXHuF2Fu~ZNkEoi zUs{$Ifp1Du%hGzyD!AsiER0znlCQz4TcQ4QWLf0if-g!1{M$>dY9czlt3CJjx`Q)xkDC^zM`TArVjvf znN-AB>2_l|o{+01Sr5Dh(qi>c^Lt^!+vI#Fh% zs7oqII)efM=So864FVuD-0Y%+mK>}O=Fo&b$iunhL%JRv(-Tx3C%NGD>b0h(�OL z;80qs9;hvP-X2srK)~S8@r&!+@-lhPL5|U?SlkN$@LHOmWb+Ept`@B6LbY}7*5ig9 z%Hat#t`X-zmnILkSciuhe)SO}@}&*p(p?#yql^teN(KB|(>rwp2oXRjeQAUenIixZ a8NNeTz?#wYplSvpOwD(M*nMNab^i<3SV@}z literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset new file mode 100644 index 0000000000000000000000000000000000000000..c3b0b376f057ae90a6fa7e56d0f39f04e1d2af11 GIT binary patch literal 27883 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetCard.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetCard.uasset new file mode 100644 index 0000000000000000000000000000000000000000..f6b08df09d182e194a1bf532979a0c37f9c37fa5 GIT binary patch literal 31131 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset new file mode 100644 index 0000000000000000000000000000000000000000..a4a54da9b18cc8b135ed4e6fa0c08351fb148312 GIT binary patch literal 65253 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset b/Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset new file mode 100644 index 0000000000000000000000000000000000000000..45e3d6bda5240a2eccc2a1d9a109a442d998662b GIT binary patch literal 29917 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Icons/T-rpm-baseModel.uasset b/Content/Samples/BasicUI/Icons/T-rpm-baseModel.uasset new file mode 100644 index 0000000000000000000000000000000000000000..2536a4f7f033caea789d8b3f92e13640406d3506 GIT binary patch literal 6430 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 bceI9dUrpohPv5e8>Wt%#S8p7L{~7rYMzJD* literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Icons/T-rpm-bottom.uasset b/Content/Samples/BasicUI/Icons/T-rpm-bottom.uasset new file mode 100644 index 0000000000000000000000000000000000000000..cfde8a412ddcd6b993f7acac8e7abd557bbfa8dd GIT binary patch literal 6444 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Icons/T-rpm-facial-hair.uasset b/Content/Samples/BasicUI/Icons/T-rpm-facial-hair.uasset new file mode 100644 index 0000000000000000000000000000000000000000..bb1e8e8c3d5afe118f2c87d4fb9a9b9d52e238e8 GIT binary patch literal 6456 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

H!iz8)jeEN{!I&4Zdes=Kj~pX; Llfvror}h5^LBEK4 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Icons/T-rpm-hair.uasset b/Content/Samples/BasicUI/Icons/T-rpm-hair.uasset new file mode 100644 index 0000000000000000000000000000000000000000..f2daba780da611684c84f9306a916e673091630a GIT binary patch literal 6826 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 AKL7v# literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Icons/T-rpm-shoes.uasset b/Content/Samples/BasicUI/Icons/T-rpm-shoes.uasset new file mode 100644 index 0000000000000000000000000000000000000000..5d7def300c145ca93e0c08ad63c062b7a2f8a34b GIT binary patch literal 6066 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Icons/T-rpm-top.uasset b/Content/Samples/BasicUI/Icons/T-rpm-top.uasset new file mode 100644 index 0000000000000000000000000000000000000000..b6acb8e42fec151348601157c1538fea4e9fdc41 GIT binary patch literal 5583 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/RpmBasicUI.umap b/Content/Samples/BasicUI/RpmBasicUI.umap new file mode 100644 index 0000000000000000000000000000000000000000..94a77615f073efe04391b0f4b5a6eee914eb4d48 GIT binary patch literal 60280 zcmeHQ2Ygi3(m$(+ih>P65hWBskdj_NA<6D01V{)ZRK>8_y-5}}yI~6uq=|r75f!n5 zAc~3wMFGWvy^Fo~`s}>{KEC;%d*<%lmTUmud*A!Lo8Qg3cV^DaoH=vm%sF@O?!#yI zS^3BA-Mc66FGQRDg!qO^9PQ{_b^aem?ivpz_S^5NHh^={wpM-c3khtJ5_IrP)kKP(jY%$?S!XDfmo zTY9Lqp`>z}`)c<&pKZAKgTn|mdeYkyhL)rsIdj6yE$4r^rQslgEk5S>$qPriZhili z4!6AiYpm{LVUNTcLF?B zl1KH}lAXBNO7eqW>oERLjW~qXh;OIs$Iz zm=d7WW&iM{xO?8FVd$>`6;8jqAvmDSG2LD32)cdVipD@ttM4BT7@H<8+xEw&=(_@E z(C04>xm}|8wr-_|Fl2l}xmk{TTHYZdNU znixK|dY9zybq9PwzptUusD8){$G06&-9VWuuIoAFAgQk3?`SNiikd&zC`vYb;+F05 z`+YO4K9A2Yei(A72Lq-+2h}%_^O166Dr*L}1vnUVIP1zCegOOLzUfnltd^FtdmYst zjktK+XqQM^vVIsiRTp?Xz8N-h5tv=<{;}QH?ZaR<8t>GK+?vNF_S<4R;TQu~(V#iq zHEySUrZSZ{@7l^gIvV)O8NLd)OLLVvf|}p$@Q97y-ueivqe~o1P1I(}Rt3Znom%%e zTB4%NU+QQOZ9l7gMAy`?9l+glI`x4cR2PJTzH!j+i$mVF$_5uYrfNEWnnll==r)nssTF|Xzl)B2w{LaJspBgax# z5RDksUz5$_ zz8-w@)ZMyti73_lwWfZtetz*hI8U{`-W>?Ir)#ioN%`&RQk?ELQW}}0COk64_jA@< zCfz|Ni5{xrfSCgqN>`$Ip!rE5R(Z@=G5PhBE2Xg_aP^LcF=GQ_)$7xCO4*Dob9miB zO&8;momrRVsqX@vi~YV(Ly3#*?RGjmz-fUZpPxLy?X4AWoua*oxukli+eI{D?sFw= z5ErXUyyUblt%L>;#TkD`A~@ol+$o73VEI4&BsPAl6s8LY8}qTGEJjkLfBfZ zVyfmDtw;^E9l!6l2IITX6Ve*| zZf{V=t?dVP8iVm-K=q(Uo-0S_S;2!(j;K`G*r16+kGSLm>Bh1b?bAZ;=?;=Z-l7k) zfar1G*zSiD+iFP>hCg#yyG63`5?)@{NRc^!0V00C^ssxR2b5|-2RV%s9`Hu-yr&U; zs>^)-dNM76&P!i;r}U#TUyzJ!^~g}^uh9I{HNQBz>xR3fEqt!V5*am}n#lk3rsq?@ zWu({B$o*-h*b1u?kFn(6^;$sua>Ew~$j177Q|lf6sTANSD%VzaI$vk$^B7_A@Kzhn zlU+sTcDjR&(CpXcSG1CCHei9NfS>yNTffLQk0cHK6pqlbay%XW+r_6zJ2Ixy@9+j{ zG(XJ*?$?$@TfeW8F9$~G3A3%|&MWvfT~LbY{XEf9cV zUcO_d$%N;Khi$k_3@SpKG>Es2wBCUqN$*Gl*G5 zbllkSW+|h>P1C-+rcn;kwIi;1Rk{_X3W~UX2<~2b{o^voRs?CHcgoh#P=(N7@t6{u z7<|Rl_L6_4!&@sIyFpA|`@+3|Db>n-o<{NZ@#|7$2pJR5Y+8*Y_56O#>wYp{kFk5Xuk8n z?W?RZ`d5wCNC)p&2gM@s_-0SH-e#I&Em%(Zkmje%g)$hANApKi82|bCmmDZ7)Kg=` z*|)Es55ZPP{YZbX&NtELs~6M#r*1)S5wP4BaLeJg+1g=|f{pf}=PB3d_TdvUzahFY z4K9jbF5&vQ=aVp)mF{YN{>BPQ+hCRUN1X5i0=c!$LG!6cbV}LrIo#LkaRdS)IAi8A z+&R@<;9-n2!yDAZAzF!_?nJK}2r^ZT+oP2^>NRojL8q?7kT#Gs_mam4G%@h3o425| zVlV-5Y~8nCOEqM2cIz^&Rw}8_@#2|d?_Y&^AUt-iUbeX-I?WoY*2Lz{WmV8qId8Gw z(NI^0+2ZbxKc9XuAt)!IiHExP_nlWQg_S8w3py$^r;j{VJW_e){!q)N1>AnkRp<+O zU85Z?cPJoMKbrm!JXoQM+|sejHNTU_lcQD>Hp|MsvaY+PCIr_E=de{nZ@C9b+lZ+i zDd*Y!{She*h{GdCZMa!aj_Ey5XBR*?a25KIzaZ3zFGl!pK)-^3Mz5m~n;- zgTxDOJL@sVY>i$=z1yh|D8`ohrbCX@Labfi>nO}q_V9H3#*YhkBXSW)&9`#u^tJ8r zIJ($w^dx)6yrq{7LOKzH891=aXHbhPJfr?VJHup#lVrT~bz6l}fwI(B?e@_0B*tCf z`V!5hT2ZKh1651Ju9cU~mW{BEt}3bZ`e;@-um6EJNscbceOxx5ljccpu*6&A6I-he z-wDsLOVr5fDb!Qtj$oa5p5JlnNN zl7Rz4TGR(4L3JGF5KTOMV%d9$Np^3oo7Uo+Ew;~PH)AjrxQwM< zKxk(j+7;u%9*r3|u%ga4!z=EzJ|)!theOTnHwP{|Oyc)W8I4nkTa z$Jy+&FT4pZ#@S?8$r~o_IvTojs_W8csKp13uY^y>nO?<|OKyJ+R+EjF3xhQao}G@C z${TC>jyi}h4h1P^mg(n}-%WZPMpGb4gEevX4F6g2m6)B8AhtQCfdhBZhM=Hk zT1p=mzCGTF8HZ_PG$rpv!Wa-||I|@#hA`GZbRYKdD$Ml;B1ma*J=LLYQgZC+S|Kg- zD2M8(bwIC_I%ZOij}0H#UHr6roxo@;)#@q4NFThQ%f?Nz@e-sq-{yWf=M;FbP8W_3 zb2r^P1W`v$oJLRHJlDNaXMxQHy$NFF`bC9^otRKb3-UBg^J4s~L{5zVv3NRMTM|&b zqc#|=JY)0yj~O`H<}|Ng&VM9hi|iO;9BH)e-5pcW!MqP(Y+t7SUjD4kPTwF1$9_%x zvFxdrq@HeXSqN4cStG|opwcIuLF9El`wRGykrCayZaP^oLWhHj9BEcac0f_sXGFgP9>WeP4e-S z*Z%L-xIECLM?+WDM5lt#Q|i6@8DbMc6Xuhi_+ReWJrC(5gkii8oK+B`?;_=g#>}LW@5dNZXHJArg0^;6(UhTnTAfGe*vj5|4vFE0e2DMv2 zS(cjA2em!sPjs!0H}}#7*B*a7qMJ`?$t}yprhm* zr}-;=$VlW2U*sn#qH@hCSD}sA$|&+UC}8b4{=+`#bwC2*rGWK)sBRvgV)X1IXTcRH zV0pyYr*G~Lb!gK_L)6k}{6jTy)|~2RppHURx}8(S@KL*7K+vN=giRZA7)(L>LmDfs z9E19_F?ac8*XTU;PzdA5xqeCc(S6MbV-3Ic{`Y#rVR+a|J{dOgggxt4IJL1rdg1&T z{Sf#Vpl|S5d(5uatLuwxP77!MHe#{fhVWj+^}%lENe(_A?ey?=RQt`2tE3>_RVci8 z=99<_rHb^(!timx}bHP|6Yia^oP|SYn z8=C>gc1J-y>?f)^T=+D`SB0}qt9OXqThE_?c_JLO#W~ddr zOT3gRBNSGRDZwtOeWn<6O6Ic|QWaAh!;c@rZO-0)-3zcpg|8+keNzU@t8a7vh0z44 zX-zn9^*gKK?=pK&v>15m&(M*uH$T+DIxOY$kLCUZ@7@`9^S7Ic3HdT74uitv-XHDVFsFoZHhPFeJ%Jo-xYj7NSEtH- z(F&ful@bWC0ddl~>(7_X3Hps`W6;C?9@0EPe~6aL9Pv`cHS(FA+HEwq<*K6H4?>Ab z2hGYsnMy@wPGf%iXanXLoq(!p9<^woS%@MsC6%7V7neX2Gv61!?K1@)8HQ1Gk_)IQ zKW!S>D;}nbHVP|VRGGnGCc?Myd_d%imGL5vK!CoVVeOn%%=TksVO85Pk^a4*kA ztHNO3YR}vK@n>8?F3QR&?gm7s?rSn6m@@y2bfZJw@0&Xek#mgKkEdVM=f#?@UZW*7 zIWLXp@zXZFgMl`d;uziD*twMp<6pl`w}~;So`casee;x}vD-<)eqGx8CY_8n6ajc^ z;u9ZXfXZiZ`bJIb<=v&z>CYsBez#LT+6#N-*bDD`13^3dAi%wK`)!!q#`*jn7Y&dg z^^QLF#NDGeTA+tS8F^W&b1#1q+D{zm#~yp-QD4{fq1yra-i0cR8Ek;8Tucux7$lB= zDynvlg&d{-1JATsmjO(%_}u!+5yITjP)K+T2e(_>_l6{V^y0bWS~AR z&J%rLz*sp228=5#uTmc#5LZo1UxN|SR8`ZGKkKg7-bzcFybAehP(BjddB?(9L}318 zf}xI_!^M~H%(fu}GMXMGYhzT$|54I!F#MWvYFlb?{-P2@Mg|x8JZ>5X;-zuB79h$n zJUZVLw`}P#03n14#*`L|>`6bRV6d5B`ktS-^^B6wFjE^C8b%I}-6i_AT5%Ba3w^EH zZ+rI%&?fSO1gYYtgU+snLl@GXakY=0oP@pq(sy^Z#=s1lLOs`2dmO#5Zs><0R!EQV zO6d`vSWtgh8_0-!fRri@^A7$AV?ci%A==%)_H?ulo8of8K;z=A+9?mghH5=nRi!_t zy6QLY?Q$zxf8JA76}=}d4m&8#hLC7fw9nL^ThS*FhRR(s+Sw7qH)V~3%?+wz%E`#( z8}<1nAg;ft&m$N+1_3|Nbg6DrR$TG+FA@5fmi{_L&#c2v#yoE@3Oi?&RlGF#CkMRD zz(!2?qVu#Pe?Tvr(Q+CR?v7>iA{r2$D0R8>^1DolAczWTqW49ePD?TK)^}Qr&K=>d zrd^>%F{1aoCGe*hOE0e*buH!$yGtJut$cS+0-?Is^PsBX>0-;nSF}Z6+9S7#j=Zq< zI!sG;Z>V03eRPoz(b$eHKb_yC8!VGB2gU8c4Ws)_l3S>8dN=H`tG^pE2`;ZktrtBr zUxI(~PKogUbnm(F&*;v-?)kcE3~s|;434Sd&DTC%hyIN3539>xcnCFzr(^9_WmQk! zWD5QJinY&~aq?lDZgf$6%9}Hg?W=my3L>y*;&ISk`jnW`ciCl7R`$P1p&sv~31|m{k{H~g&5c>5NgA}wZKEvN=pQammCdM)jc*DB&%!tuGJ(QAAFz>26%&d zljrQ$?KwEkXmi4!D$bu;y#T%v9!cWi-`|-cGkN1vW~NYXOZjsJj2ot>kvD$kaw~XO z&?7NZnmY6tR~2aWZ9PgZDOD81k6yS$rrYLhTx>0WZ!#=GLslP?_YV7O6Gkfoj7}2m z=AGXiK{W=GDn48KgM2^`F(ses+ud0%4&u(f&p!UqC|J2drr6@5Q^jYnUUUSQz5k?V zV3RQ1P$*UGx_R72*pSjnzw*JhU(cBiKGA`q&-3GV$~-1K3zPkN1SXD#5~}i!q8qkg zJVphPs9o^s&s}#c!d(;<`!nM#PRlTPaZbH=7X$JzEOp9I}qB)2w6x?qxY)03cs{e~q;7tdd! z?FWAB6K21PM^~Dp+_EI;*e9Bk%RbQ@-5E*B4JzMhj$c)h{9H-WX-U%6B}wN^lJ4Fl z+k0PexBT2fsN!n$8l5|HVNq1I~bPJNCJ10rHvhmPg)9Qh;B{nfOVC=*2=dp3p0Dh-8LB}}()_HtwwjcY@g^I^LM0b81eoV)6F6Q$| zap-;^xaxMfR^~PGLG(fCGZX@^`SAf3iVcH|(hMiI245yH%Q}&Oyl#T5-iqkaeI)C9 zlYDIP@EY7K>zltp7TOh0*3pYq1F6^>8=BkID@qpIbMW6Ft6!8X?yq6{NY;1MU!R!z zY>slKgGugVbm9n@Wa*H^|JfIbWsDzwYl5W`3#_9-k8R48ie=6@>Y|x~<$x@ntZz)R z@Q?s&alG0KSuR!iHIpoW%slDVILz>Pvc5LSg57vu2R?s+;*xi+!EUeo@cAfN$i>zB z;-dEfx{Sw7vcghhubOMuS0-7oA#!%qfekq)`R<}vzVRT<$@ z7wctn|C^Kb50fl>g21UNpj{6u=2I_ePD(7E*v9z$7bX~N2;YM^$l9q`^u1JFG*c)w zB~?6GpPOVM_VT&~HuNjry)J7;h(b3f>lc%(1F6onrh4zI%2y^S%dAnnc0tw?N*-kK zItyY3tI9JjH*-nuI-abTl^k$De__1?Iq0uD>BMnQlCqQ;WSw}jely9!CO`IXS=Jo2 z_^VkVtGDQ1va&cHu2r%=xw=JSByqB`5TjtjV_NGn&bdx6Tk=1itQVqeh&>Fp;U*Oi zSKZhWDLQRDS-(cf!ah37x=zKYF}MDGvbeuSsQ&u(_I)GkQ&YPJ5X(k1iVpObMNKXR z8zrdOf2yjai)X`+O|me)&QujJZ=SAfShy)H252+SFedUUo~)luvOqmkF@l)3s$DC$ zg!$}^tdC5x&@SW~IAB9|zx7)ssM&w!_Qq>hv@Gmb!zCc=8O43n{pzBbqB&U~n%ae& z?E^ZwPKn}u+yl)BQE0A*+j#AI-y{n*+(9Smpj~)4io^9c%EH_NS*dD#1^1CG@HHRf zl_AmyXjh1}51T2HyN+i=vn<#U`MeS*8Wr0wla$5vP%WOU_e}kTN5Z_{fqaA4MAeTd zq0JP{$$Hl$3+kfG}o@TO|sA~?9Xt! zzE}KDd@Kn;MADqBw@k8n5k2qgpj}re5$zw}H?rO|$wIqolyc}Vp0;lNn`B`R2{9hB zUZ)es#3%H!x&O`C@C{SDdJ{9=dx8y@LMR=nPwHiJ|63vppHnrvdDR&|la$Lmk-gH=Tv3c|lz?Q#2>*HIpptZ}5H_Y{=`B)3-MxM4_9L^{Po0s&Kx6 z&)*!Sg5?V>kj3(dZOmHl6%!0L#B)LR!*kk0x)2|+L+ZB)|Fawr#FO=$NfvUpWmExm z&|e&T>%OL&B#!1}AyvZjHyrqU7Vfdn{7*W2s(U;q`-@J@(~RGx@UQ{)k$2Jw9QIgu z(Al4^;l}vjB0g+{wRkr=ae(f4I`N5b^cC*$nHhXh9^g2@7rH|yTtmla=xj~b_^j-) zbm9|FFg-qu)qzgXv5vTg-cQmA{?}0nT~<*EzPLu&gHGrPJ`WQPJi!Zfz?bh2P;kJ& z1N6W_AC%AuILJYL;GqNHz(W>vZbv6R&jovQr}IIgNA86j8S^aS4*D2#6XN(`bYdJK zMt~pcE7z7hyV5;)r_jjAI1C((&^ z;~IUuj!y77nNE~_=|ny3(_c^L4Rmg$(+sKEpYc{VeIuU!S`1zf2g6j^$>x{fdJ$By?^v4pL^-A$;x*+ zB$xcs|o@rQLzH|dk;(T3Gl)mr? zy%R8IFfPmeZu+LG#)1I77A6_Nq3GkbF-X~b7%s_|7BF~{Qc-HoUvUzCd6BBiL5goT zqc7QW8JK{K7bo?Wp@Xm<4PPmux$t(rzlOd=Ab_foCX%m~VfcmpZXmtgWd5M8`5PS( z2%>ztlt4yvB5}QvvxrGRm9^L2_#|TdUb$BDBL?BsXfAyNkNom4df}45x@#LqD~&jO z$&tiFR@1+gQaTQ>@Wn(94P1%*9yh!^C?glW6HVV+g!okCE|ed!zWg7EqZawMjvN$M z= z&=Aqqh+wnW{Z!q()uFZGGgdg2R3|2p0$|yfVU=F9p-I@u@<^i3r(b9$&4EzGNg{+6R*c6 z8@+6yV6Afs_h(lzn|Al6Qwv>&#n~(75;cz|_O(Q`7RsAgOAFurvO z;a+PR%RSO`n~zj&E+Pwtkqj}^IY6(U_9bp>CCu^ykMm2p>8gc^EL%vuqxz^IPdA$H$$Q@=8 zFAME?>ps@>80RcNkDDQq-c`>2E|Lb_5uv*)PQ1z%7M|C6t&t{YK@06^CnJ-493u`o zTWHT&ueYzpbtPHGNqw)+0kLs05x!?q`g0CgOS(5m85Y_xk8T0mW-RTh3?(ejYBm{Z zbj`+7f6;GHkR3u|GG*x1WbYb*kq{!@HB^iH$6TwcFxRutZh10R=bR#Pjbr8;$+dS< zT-nPoo^s?kE26QLNh7a_ycy#U|FQPSk>fIlAbA8Sl;cq!n;5$mxvob$n)X+o%@bwJ zk#lc2Yd%@EW^X-=^GmE_ut$M4ad=I`^M)DQ)r9Y>w9xwtvu;?QXn)~sB5@D#EN-U2 zK0@p|crK0V$lm1<+|;`IfM)uFH5^aujV&_DHt(_AIv2^s_(nucRc#3OK1YX^?TWC!nRj>0R%{NRru1lf%(2gpmC-2> zotel#j_Aqm?(r-;RE`kr`|G>U zScmJ;%tbvsNRFhCvA>7b#Mg+|gcy0qY0PctnxIauYD3c=$1RJVtMqIsdhgRhGfFbH z=DEr)GeA8H#4cEtoRKog!s&FCLH9*8ljYIORY=joDrd7if@I5?4Dn}RQt~*DH9r>^ zKs?c>;ZcexH6Uj1cNj$=eb;?ZQnHZo;ZNU{Kt6`uKmOBqB>>G+c``8lsBMF(96&A9 zvu7QeXjkHtq%`5a)4js%6Y;(1)TFp_{4n$DDxYFub&7ql!HQ4pjKL5@yCF$QvlhIK$JT|rVU z<1vs&eUIL@lI(2h^8?A>?c@oC((~c>=`=QS39IL8xpL){C-XYYemoC1J(6SnGMz>b z#!?2IVZVfwOgabBsrzN@&RQbBv?kSZj{Z&A8tuYg({188XbeTofXd!2WV@EsJ&m5o z(63ieUn8~`sk%A`{BNm$alT~E@?tZcQt~b*Nrc|BDAMcuyu--L_5HIl@-65zS!O@w z1jjCSksM!~q3Ct=vB$e^Tu;yYx>DQpnih&{$ykNG5^HokyTBR&-hlN(F?pI@`XW~P z`pN>HTSy)Re?bJ#pg+Vgi(F|zV$(gyGgnL6Uy@Wyc;_QBPQ{OW$dH@YwY{Q3FGXhY z91Du6*IblSA+sEw6i@bGv$t62NlDA~LC-@nb~8$)9v-@4f3Oft=gBcRym!DW4?ESu zGp~?Q-$M11(S-LAoaCeWUhl}H>hZIgBB`gYJ?kFU{7l+GNu_h3I898VPn>llpVRMv zNtf5%$nUyIT&B9uQuo>N{xo5x%~7~qMVqJY)07-YfDBz0OGq6G-fR5!Lk}{0> zr_qRD7|TdEWTYE1Fxp~Uly1mNH{_Y?Kr(blmy)3?o=gE&BOJ7&hp47QbuCiG9J)8# z!OV?YWQK($gtc!@!lW1}8M4LDgss8c!uHNEY>{U4=)SghhUBbUkv)p|tNU%( zXAqGY#yyJMT8Clw*Zn9%dQWnG6t9=jyXIcTte``}{hVp|Leu?>ono%b`lT8AB|EOL z*Q~cK(`1+Ueb4g3cH7&L5ZN*si?q3g`i44>)HAz?qyGHl$m zpJ$nTx+y=;lCe`CoBCXcXwTLQ`#Dz7Y|(gro^A9(*hjG9iDvt7FEr)R*coAYE$f-Ezl8hV4CB7va|zjo zH#aRIUM$Eod3ej(m2IlYmfKsOjve<{7dP)+ImY<<*LoM8_^?;R8~5gZW*eDdd*AnF zZhwyi>Jpl{nIl0rKR^z z&FG(*Mr#@&w#~y^X?XL0r+g0yFVf(|2la7uQSS-ibrBqHy@>r!9OTPxzd;Iy9D=pel}u2nZ+`Ioweh*oVD(vSSnH>)Wa-hku7U`Bv$bUvaG ztPWld!I7%e((g?FPwwbFkd|?Qc*WLWZM}y58W)l#{K)o>jW1mewVr-As8cPx(+Yn-CA|#v}FXG{d z7O1~b6d!Y-YGI5jkhU3h=)g4&eyj8y{Z{X9kGg@6;9wg{KcM>?B{a^zoW(^y7vGp) zc+SCHUY~o*+%>hc`A5U^cL@Dpt30l{nGHZ6Dbv5^17r-mSP_lX>&E_1kFqu90~~ki zh5o`Ru0aQ8@J{{hx-gLu%R#h}P8{sp{C26~a)0W0LxfQqaQ1_w^nxzX32%U+_5Ue) zK^N$R$s4WvPtgmyKquAb|1o-@51|uugWdj9>_w_be@B1*i~4aBWYQ5cdU$r=0RkeP zM!v!g9mYt4F@q8mCyzpc#ouTYQxnLem47T#9C!p@rtZaZb&+t+1D6(0W1Ua0{Nm-o zrxvWga>=|$?x_x_&}B?xc(GI-$aG?U#c8!d#u)A)g)kcqpl%8tJ*ipfIkbMPQl(*5 zk$+9mjD*O^Ull!^2>JThQ-*X>$XJ2+q5>STm@zsc4(36H;Yi=?%-d$@&Mv|8H{?f` z4=6@4tv#l6MR6z*vR%LQA(aV~6XK3U)o4+X&*3L&%@wn#qA}m1{4|K6CqKv#pAxmb5kDmEFD(1;+bI9l3TNvMUQ(P9nwB= ze?)DyYDv;#|2kD6#^=9Uo{mrE$RpaVn$9dgyl0zUtwb*$xo?}<*yd}UPETDp&$?Iv zm29a4I>d;%MrLIA&}nh1u;=P1L+A(OlHAP`6ISEOMm2o7%9lirF9pNHX(3omgy1R( zVJ!fudz>nC#M(=AI73IXG@y@7Sw(oD!OD#3{3t4>Kwb>f4inG7$DboB@DL%e==_-C zDmqMTMkieZd>;%SogaxOGe%wy;@ui{Ib&jLjOkW~R9NOSk8sus2!FdOt}&Xi7C&R+ z8AY(b@gD#JQ7l?RmL zpT~>*HAuq&6eX;0KKiJefo33GTf`a#;jvSs?q7(aG;W_)SL~fe5WaG6YiPZJ^Y3GuN)vcia9IBwL7*#B?ba`RgY9M+**-#)19zRLNTtO6)m zEP{F4va=qVI%`+{xtA{NedL0z8{@$aSB90IfW%k-*+iKG>p#V8z9fU}h7^GziGxMM zR60ccXG8=r!41BI38)AT&?~yrLBudL=I!oBZ5Z;2Hhj@dSO4go^?C1jupF*H^tu#Q zHM$Mhse%OKNYn-sM}w9xA~=9B9x=n9?``u}&OPSX*Q{3!y8D~^DpKm>!G34%R7SSp zc7;i#s?k9v4l$P)->Gtxry97V+QHP&d0YEV(+)ZFQQIBWrIiI=owzg}tTi{B=p)); ziSCOJlI;stAzmp(aPUHgv*GhQcF&n~;K++#8hziyg;z>ELeNVObZGbI>sQ;ae)F0S zPrPz_UocD5cfjqt5c3kXGhv9~vCN&DFvMI(lnFx&el7y5S{%{YjZy+dgnNjq z6~@fnL>!Jo5x{0{qzLv%xW|%Jm5VQ`csu{BRR>>EJL}*~)NjfSfJZz2DouU;hSN6< zTY6~avge%F4~Yls#e#@FqHUJHzin{wyVhN_^1|Z&#dlY<9l!ppPFVy>s%;%EOIf=A ziye!X3>nw=unT5qqHWQzeKdz)EI=*eNZ8v3MeIdPr(V0N{f?WD*jBh?!R1TqZkpYQ zD#>)}_MLP3{W2)CVD)*O&lxfeHRQZ_u)DYdl})$pMQ$|LNVb2F;FS5i8rRe1 za04;9@5wxX#_V*zd;gZ_#@R3bc+-SUSA}-PgWYC`jc(w0h2eHQy(bOCYTxE7^9AW6 z8B^s0-0&SsPRc1koG?J=({$5h64~Maef{9c>xMu7YS}_h-t^_E9apmvpay58=$f-X zz3_o8-``$zf7XVFzHPJQ)p)RfXMn~g=}zZQ3+}%1?xM@4rXBy+>{d3kp}Fx9FQ<$L z`*#Fr9vYZ@qI2_P+qQexU#D;B>L|GW?Q>QfSaq2nZHtCE!Au>-*e@Z#n1JR_(SN)? zMSdM;%4n@d^J`wGrr)=ztkY6_)l)QQFvaO{1Oh3IzEH{xhc}oK^rbj`-aybFat7%) zBoupguf576<^CTrOnN*)I2s-uU*f^yu73-u^0>nCltM?q?MxX{k}^_nbJ!fCwP48a zO&RN;ucwt(F&eMAl!Zjh3Yr@N{p7*s;rnDtX$6W4@`#uE0}(Ie1D?$Ah+dJyYSFNt z!Au>73sn-J+~a5@C$jpyLBG%Aq01Q4jhx|iduvm4N@=?Wy;88w5lrdjo8b-gN(qvq z&2YPFwIF@4>OXV3y-Ky8_XvyBeakmBf20{GVK)DlV#~gAs=ANeWj!t-rp435VYZcz zJhs_3{OmVgz2>*I?K4<7B(U!x1mC$V_oe)U7ZfizU{kI0siMqyu}Jsrai&5h-Mk$?VE^$B-kV*gB-$-GD` z&QolbsgfUqT%_*BQgxBo%lT<)?8x{?)#Z=Ajc29^S(SC}K#JGr(o(2&xdV=B55;hY z*F^!y;~`8irCLjIIw+dC`nR$99knUGnwpdbw>N-UuR7%RxKe6DUT2WL1>50q2OImh z{Q1FQa=tfn>6lY&FcFlwXp3J}Z$G5IL?vb{gu6@q--n>@ll$^Ek-Y@La zc76KS`IBy}YQJRY*5fXq1=Rf$K7DRZC}-h0_RHKwwqq_i_Op8x$C&nXC&cklDlbyR ztespaF%bjH6;VQ!Z#d`Y*Vb2#E?&I6=M6pkRvvre_#IO|xqVs5`Gem3X36->*G~A< z+i}y5(~7oMZMl8vZM&Y{I_b)5>Xx5sU#gWI(4}3M={uIqdw<0CaitGFbM)#ny8YN^ zOO#gFV8oG7fp&j>d9M4bGe=zi+V0hFe&FBnm`-neZ~%@X5BwMkepm-bjDg@lX1Bwq zhygp8&ZFq;P3NI>o=7Kr{V+QF(buYuq;KXX~MFRG+o2ts-2d zWE=kd{BwPWunS+WkZfdw2!499LPjSP`fum}8LeEx!FTb-7r*Nt>Rfu&jrsj^UcToN zmJcT8Yly`O-EW~22OQo?izzuR(30Hi()?IKRr(#?K#k8|ProHYzrTUJ71iqL#DVP` zT8?Y-!(?$0@_i7ZngVh<`qMd}PoEUo3n??|G;a!)W|aQaBH1BPv@WA0$>9w8{QXn< z44`kj&<}W5A*isb6K^XhMy=dO&;kRB9rap~pZ?A8`KJyjkR0knZ`DV=h%z~;k~MmX zmCm8-u+n;(mZDA9{EaC!kk>cGU6T@YJEy{Aq`c zUmuB|Kg2{^pkD(vf5`g^!I6x0IVzGtxVjl`HiP*Z)aLuvvihvY=m$*ceCiPr{7{%9 zt|An~{X=>~&2^0HLY>bn8}Tw<6JPyc+4%W2J_tj!d_>5LFbLN*$xwW`jx6UhC%uf& z7kW`0sW0nDrJ*lh?`wTcd|6l4GREF)UsE0E7op>0%D#Z(`tQ)!#FurHCG3k+9fo~P zx=4Ldzfjpa`g&jMOZ?eBhF)?I@tt1kwr?eGB76Y#Sw~ycHQl2f`oTW95BwMM3_IxK zF1~NH3hS%t7`jt^!#DUkg73c2m)~{eK*Ztq4>n44$wIjVem_OTOgL%0F}gC}A6j(U zkAY*JE4b?5quRW2{C#=*D+KQpVfi8ttaHLY5f>5NfHy0_KW6-utQZd6@>d=?zQ!P- zdcu#_qjL^akvFDhPeQi1;p2YX>1}(JJ^$7xj?H_~Gk$7x!;u@x11pY-sf`7}dS9TS zj-CdI)e6Jw4elwyG2Lto(3YwSN%ujZ z)hP1k<(oB&7IMELjIr-Ygh<#k$Q={*3~IKxXZ%Yy2j6bjd*oSp5Bz$^oKs7pd#0_@ zQ!crwXGXYd>w+!sltRTOdT~Rh-Y3@HwqVa*pgB%SkLd=i#u8c@$QE0A({<0j^5h9` z4!!dl`<8ZnH!g~{bQ`vGYr3FqI-fscoCBLs+rETMs4b8!*7nNQg~zT-T~Y8rpP8Rj z9rVD_(c0oo0(rneDoU%!H7fRSz;;U12l#E-&I-vPa;Fk2euv!3g31zWsl8%oRYqoN zZdy)mc2;&;VP<+(L0(pRhBYrUD>Kzzkd>EbtIEjAbEKx{(GQnubY`ShJF?QUbF%YX zsWoYNndvzdZu$w&##*1ZDvSQo)3UOw(sFBZQ*)ge&YGO`OfAcimzPV2|OD*k9R74XNF`e@Z~e-YyC8>%Cak;^2_iO~0k@8cA^AkuQ$u z@obu#lrqe6&!VXK@S*+oj8RIyR8WLVb2w&8=`<1l6S^aQ*0@}e#SvJB}_) zj#`jgl$M&6n`N_S=Va#P*$eX0G77V^(rwmEa@s6=ReE}EMn-yJfju=nJ=11O&&#x@ z(|vA^EjJ@CGq-3|$l>z4ouyi!ZnWm24?xg%Ox4)*9_dR~p2_zp?dh4;g1o%E zoWfjNMs7v{jjzn~ymT6UIayihsRafj_IxfG!S`G;oXaRUeZNfP5rqe`NR22aOmxrf zkaSr(b*4SLDBGTmJF5=K&yy`d0>MtVFKBjkO>n7)a0ne`Zl|!Y25yubLcH+48Qs5^$Rx) zzHLi%qR?I{MgPEQtj`mw*MbzjtR7#;)$%-Yx8k-+l}4)9(s6Il$uS zpP#>M%DRR=S@ro<2^b}OIXzYv9WqHFV$3e`I~Pq>$k;iNefED1`mgJu)2@8Sa_V>U zw>^30$`1eNW6%HYmlw|cBqmyR~0;_I7~J6PK7ZVi!Xr8PGkuo^7Hva7A)$519TQo#mG>A9*SFZ zE`2#yI8hOFS0!`i-G~iL7*j%PI=dy-_qzO-RIS*a6Jp3K36a#THzh=h`T4IUM$)Io z5-TKzaI@kmBsw-#^XsN$e$y0EC5j@B%=tqeS2d$MXt9EeC)Gv21cZ%Dhz&vVa{>;_;pS!0LzuVJ_-|lHee8dVzi~Er$;O%<- zxN#tVK$Hb}PH?MA@rvEgCgylu!Ocw|3%4m`%oK#_`wNAPj!Bm&xVThZ+?{}MArmfA z7rge39gjm4MJ#4;Ld>Xym=h9WtO+m{F**T8-|GM}RySLMI`|!9{fMp8B_T#7M{(qw zn6OUIgqZgfCeC#8R3ZD~i;9SyIaLk)mlZsw=&JlPuEYGG$d{>-XW13Z<_dKYj}VNL zBE+UvQiQY!O^y(QEGdpivyvigoh%`uNesuQ35bJx<$vj&GzYrLIR4AhyC+n`{+Hev zJC>;^Z!A5H~-DaQs;A(<{Z#?VrX_%`TsDzTc_F@GectD>X2~yB;=#2 zX7u!Vj)IFN>Y|4#qmv7ZI6eV}HVqPDG7@6a6JRVNB>_f%c^=4U-RQ0O1a&O*BdPik zTgR3FW1%0+iC?FwM6P$jI)^8~&`W3uG4Co&lL=NsVxk?kX^D|T3*S)4xPHX>Tb?4~ zT{vF+^Ol|Q?i`OeMIX0l@zcV|71vzwXG2Emy2~H_;JURtEl9X<@W*>Np_OlPjq~|E Zt_m#}q?aiIu|F5VEkgZ=&+fDG{{X1Rr-lFk literal 0 HcmV?d00001 diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp index 6db0e12..17509d2 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp @@ -2,6 +2,7 @@ #include "Samples/RpmAssetButtonWidget.h" #include "RpmImageLoader.h" +#include "Components/Border.h" #include "Components/Button.h" #include "Components/Image.h" @@ -26,9 +27,32 @@ void URpmAssetButtonWidget::InitializeButton(const FAsset& InAssetData, const FV FRpmImageLoader ImageLoader; ImageLoader.LoadImageFromURL(AssetImage, AssetData.IconUrl); } + + if (SelectionBorder) + { + SelectionBorder->SetBrushColor(SelectedColor); + SelectionBorder->SetVisibility(ESlateVisibility::Hidden); + } } void URpmAssetButtonWidget::HandleButtonClicked() { + const bool bIsSelected = SelectionBorder->Visibility == ESlateVisibility::Visible; + SetSelected(!bIsSelected); OnAssetButtonClicked.Broadcast(AssetData); +} + +void URpmAssetButtonWidget::SetSelected(bool bIsSelected) +{ + if (SelectionBorder) + { + if (bIsSelected) + { + SelectionBorder->SetVisibility(ESlateVisibility::Visible); + } + else + { + SelectionBorder->SetVisibility(ESlateVisibility::Hidden); + } + } } \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp index 455cc96..afe05b5 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp @@ -1,19 +1,28 @@ // Fill out your copyright notice in the Description page of Project Settings. +#include "RpmImageLoader.h" #include "Api/Assets/Models/Asset.h" +#include "Components/TextBlock.h" #include "Samples/RpmAssetCardWidget.h" void URpmAssetCardWidget::NativeConstruct() { Super::NativeConstruct(); + } -void URpmAssetCardWidget::Initialize(FAsset Asset) +void URpmAssetCardWidget::Initialize(const FAsset& Asset) { - // // Set the asset data - // AssetData = Asset; - // - // // Load the image - // LoadImage(AssetData.IconUrl); + AssetData = Asset; + AssetCategoryText->SetText(FText::FromString(AssetData.Type)); + AssetNameText->SetText(FText::FromString(AssetData.Name)); + AssetIdText->SetText(FText::FromString(AssetData.Id)); + LoadImage(AssetData.IconUrl); +} + +void URpmAssetCardWidget::LoadImage(const FString& URL) +{ + FRpmImageLoader ImageLoader; + ImageLoader.LoadImageFromURL(AssetImage, URL); } diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp new file mode 100644 index 0000000..ea7640b --- /dev/null +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp @@ -0,0 +1,85 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Samples/RpmCategoryButton.h" +#include "Components/Border.h" +#include "Components/Button.h" +#include "Components/Image.h" + + +void URpmCategoryButton::NativeConstruct() +{ + Super::NativeConstruct(); + + if (CategoryButton) + { + CategoryButton->OnClicked.AddDynamic(this, &URpmCategoryButton::HandleButtonClicked); + } + + if (SelectionBorder) + { + SelectionBorder->SetBrushColor(FLinearColor::Transparent); + } + if (CategoryImageTexture) + { + CategoryImage->SetBrushFromTexture(CategoryImageTexture); + } +} + +void URpmCategoryButton::Initialize(FString Category, UTexture2D* Image) +{ + AssetCategoryName = Category; + + if (CategoryImage) + { + CategoryImageTexture = Image; + CategoryImage->SetBrushFromTexture(CategoryImageTexture); + } +} + +void URpmCategoryButton::SetSelected(bool bInIsSelected) +{ + bIsSelected = bInIsSelected; + + if (SelectionBorder) + { + const FLinearColor NewColor = bInIsSelected ? SelectedColor : FLinearColor::Transparent; + SelectionBorder->SetBrushColor(NewColor); + } +} + +void URpmCategoryButton::HandleButtonClicked() +{ + SetSelected(!bIsSelected); + + OnCategoryClicked.Broadcast(AssetCategoryName); +} + +#if WITH_EDITOR +void URpmCategoryButton::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + + FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None; + + if (PropertyName == GET_MEMBER_NAME_CHECKED(URpmCategoryButton, CategoryImageTexture)) + { + // Apply the texture to the CategoryImage widget + if (CategoryImage && CategoryImageTexture) + { + CategoryImage->SetBrushFromTexture(CategoryImageTexture); + SynchronizeProperties(); + } + + } +} +#endif + +void URpmCategoryButton::SynchronizeProperties() +{ + Super::SynchronizeProperties(); + if (CategoryImage && CategoryImageTexture) + { + CategoryImage->SetBrushFromTexture(CategoryImageTexture); + } +} diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h index 1fdef9b..4e37187 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h @@ -8,6 +8,7 @@ #include "Interfaces/IHttpRequest.h" #include "RpmAssetButtonWidget.generated.h" +class UBorder; class UImage; class UButton; @@ -28,10 +29,20 @@ class RPMNEXTGEN_API URpmAssetButtonWidget : public UUserWidget UFUNCTION(BlueprintCallable, Category = "Setup") void InitializeButton(const FAsset& InAssetData, const FVector2D& InImageSize); + UFUNCTION(BlueprintCallable, Category = "Category Button") + void SetSelected(bool bIsSelected); + protected: virtual void NativeConstruct() override; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Button" ) + FLinearColor SelectedColor = FLinearColor::Yellow; private: + + UPROPERTY(meta = (BindWidget)) + UBorder* SelectionBorder; + UPROPERTY(meta = (BindWidget)) UButton* AssetButton; diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h index 5f7ca37..07a4be4 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h @@ -6,7 +6,8 @@ #include "Blueprint/UserWidget.h" #include "RpmAssetCardWidget.generated.h" -DECLARE_DELEGATE_OneParam(FOnImageLoaded, UTexture2D*); +class UImage; +class UTextBlock; UCLASS() class RPMNEXTGEN_API URpmAssetCardWidget : public UUserWidget @@ -16,7 +17,20 @@ class RPMNEXTGEN_API URpmAssetCardWidget : public UUserWidget public: virtual void NativeConstruct() override; - void Initialize(FAsset Asset); - + void Initialize(const FAsset& Asset); void LoadImage(const FString& URL); + + UPROPERTY(meta = (BindWidget)) + UTextBlock* AssetCategoryText; + + UPROPERTY(meta = (BindWidget)) + UImage* AssetImage; + + UPROPERTY(meta = (BindWidget)) + UTextBlock* AssetNameText; + + UPROPERTY(meta = (BindWidget)) + UTextBlock* AssetIdText; +private: + FAsset AssetData; }; diff --git a/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h b/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h new file mode 100644 index 0000000..caf58da --- /dev/null +++ b/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h @@ -0,0 +1,64 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "RpmCategoryButton.generated.h" + +class UImage; +class UBorder; +class UButton; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCategoryClicked, const FString&, CategoryName); + +/** + * + */ +UCLASS() +class RPMNEXTGEN_API URpmCategoryButton : public UUserWidget +{ + GENERATED_BODY() + +public: + virtual void NativeConstruct() override; + + UPROPERTY(meta = (BindWidget)) + UBorder* SelectionBorder; + + UPROPERTY(meta = (BindWidget)) + UImage* CategoryImage; + + UPROPERTY(meta = (BindWidget)) + UButton* CategoryButton; + + UPROPERTY(BlueprintAssignable, Category = "Events") + FOnCategoryClicked OnCategoryClicked; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Button" ) + FLinearColor SelectedColor = FLinearColor::Yellow; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Button") + UTexture2D* CategoryImageTexture; + + UFUNCTION(BlueprintCallable, Category = "Category Button") + void Initialize(FString Category, UTexture2D* Image); + + UFUNCTION(BlueprintCallable, Category = "Category Button") + void SetSelected(bool bInIsSelected); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Button" ) + FString AssetCategoryName; + +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif + + virtual void SynchronizeProperties() override; + +private: + UFUNCTION() + void HandleButtonClicked(); + + bool bIsSelected; +}; From 3fde6f101390615922dc6bfcb105c810186e7041 Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Thu, 15 Aug 2024 10:10:11 +0300 Subject: [PATCH 23/74] feat: add image to the root --- .../Private/SRpmDeveloperLoginWidget.cpp | 15 +++++++++++++++ .../Public/SRpmDeveloperLoginWidget.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 66960a6..c2ecbf6 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -14,11 +14,14 @@ #include "Interfaces/IHttpResponse.h" #include "Widgets/Input/SEditableTextBox.h" #include "IImageWrapperModule.h" +#include "Api/Characters/CharacterApi.h" #include "Auth/DeveloperAuthApi.h" #include "Auth/DeveloperLoginRequest.h" #include "Settings/RpmDeveloperSettings.h" #include "Widgets/Layout/SScrollBox.h" +DEFINE_LOG_CATEGORY(ReadyPlayerMe); + BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) @@ -231,6 +234,15 @@ void SRpmDeveloperLoginWidget::Initialize() } } +SRpmDeveloperLoginWidget::~SRpmDeveloperLoginWidget() +{ + for (const auto Texture : CharacterStyleTextures) + { + Texture->RemoveFromRoot(); + } +} + + void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) { TSharedPtr ImageWidget; @@ -303,8 +315,11 @@ void SRpmDeveloperLoginWidget::DownloadImage(const FString& Url, TFunctionGetContent()); + if (Texture) { + Texture->AddToRoot(); + CharacterStyleTextures.Add(Texture); Callback(Texture); } } diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h index d44ec3d..e9c3f34 100644 --- a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h @@ -28,6 +28,7 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget SLATE_END_ARGS() void Construct(const FArguments& InArgs); + virtual ~SRpmDeveloperLoginWidget() override; private: TSharedPtr ContentBox; @@ -36,6 +37,7 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget TSharedPtr SelectedApplicationTextBlock; TSharedPtr SelectedComboBoxItem; TArray> ComboBoxItems; + TArray CharacterStyleTextures; TMap CharacterStyleAssets; EVisibility GetLoginViewVisibility() const; From a81a6b247cf395a6a3c597f074721e2201c1f0d5 Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Thu, 15 Aug 2024 10:14:37 +0300 Subject: [PATCH 24/74] feat: remove category --- Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index c2ecbf6..94db103 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -20,8 +20,6 @@ #include "Settings/RpmDeveloperSettings.h" #include "Widgets/Layout/SScrollBox.h" -DEFINE_LOG_CATEGORY(ReadyPlayerMe); - BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) From 39687e18abed4650cf64e5b2c3470c6209a1fdb7 Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Thu, 15 Aug 2024 10:15:05 +0300 Subject: [PATCH 25/74] chore: remove unnecessary import --- Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 94db103..8672099 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -14,7 +14,6 @@ #include "Interfaces/IHttpResponse.h" #include "Widgets/Input/SEditableTextBox.h" #include "IImageWrapperModule.h" -#include "Api/Characters/CharacterApi.h" #include "Auth/DeveloperAuthApi.h" #include "Auth/DeveloperLoginRequest.h" #include "Settings/RpmDeveloperSettings.h" From f8ec268693836b67c95648d7bae8359356818995 Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 15 Aug 2024 11:31:06 +0300 Subject: [PATCH 26/74] feat: WIP UI elements --- .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 41203 -> 51095 bytes .../Blueprints/WBP_RpmAssetButton.uasset | Bin 27883 -> 29837 bytes .../Blueprints/WBP_RpmCategoryPanel.uasset | Bin 65253 -> 91750 bytes Content/Samples/BasicUI/RpmBasicUI.umap | Bin 60280 -> 66101 bytes .../Private/Samples/RpmAssetButtonWidget.cpp | 29 +++----- .../Private/Samples/RpmAssetCard.cpp | 2 +- .../Private/Samples/RpmAssetPanel.cpp | 68 ++++++++++++++++++ .../Samples/RpmBasicUISampleWidget.cpp | 5 ++ .../Private/Samples/RpmCategoryButton.cpp | 18 ++--- .../Samples/RpmCategoryPanelWidget.cpp | 13 ++++ .../RpmCharacterCustomizationWidget.cpp | 6 +- .../Public/Samples/RpmAssetButtonWidget.h | 37 +++++----- .../Public/Samples/RpmAssetCardWidget.h | 2 +- .../RpmNextGen/Public/Samples/RpmAssetPanel.h | 52 ++++++++++++++ .../Public/Samples/RpmBasicUISampleWidget.h | 17 +++++ .../Public/Samples/RpmCategoryButton.h | 12 ++-- .../Public/Samples/RpmCategoryPanelWidget.h | 24 +++++++ .../Samples/RpmCharacterCustomizationWidget.h | 5 +- 18 files changed, 227 insertions(+), 63 deletions(-) create mode 100644 Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp create mode 100644 Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp create mode 100644 Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp create mode 100644 Source/RpmNextGen/Public/Samples/RpmAssetPanel.h create mode 100644 Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h create mode 100644 Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset index 85e9657ff5446d744a4a489ed158aeb0c80df901..dec164444a96a429d28220382aac701ec1519c52 100644 GIT binary patch literal 51095 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 delta 11873 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 K>hR2tdH)Mf*X&dP diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset index c3b0b376f057ae90a6fa7e56d0f39f04e1d2af11..456d40abdb82e8e13dbb2ecf7f72c26e2313266a 100644 GIT binary patch delta 8407 zcmeHM3s6+o89rA;TtrzB7IsB z$u7+^6{5P96mRQnx6z!uFX>Fo&doIy=AqC6z63As8zoMvH|6D*qki>cEcoNT#zFlE zZ=NN)+$iaLIP3Fu*jF~m*Ax1SjX9=8CS$II9hvp=g*e*uTvUZ=a-jsRu<`(07Db(Irvhz1dCPl5X;rlKDmH%++Av+HUgR z>L%}P%HzA5cuO}GZto_qz@8_q-!ZuEkQHyUCyFh`+dwfWEVS-yA+|g=)}BZGZ%Zuf zroy6b@`^j-vHb^5NoPcgK~d_2$BbbS57EEe36FVU6UF$ugt*2Dkxjz30k&kN6CN{~ z?d!h@kJW8m-AMZBaO6uWo27;)f)q8pm((hW!%2f?;~Foyg5q{CoPJgUjY0X}3D+j&^Q3999(NX2S}T)_@5fUOihoGhBCR(Po3m~%$SOu@$$ z1_fkhX5t7)`b6Z0RtVe;iWj&5fY9W$RtS0ks(_Ue*^(*F8RjQfY?OMCxLI&M5#e%h zwx2-(X$Kq&Bj=JI2v;bsyUu3aV6`)9qDaNOQm%l+#4L}33Xw_%d#DDIrY(i}|6fIT zpvs_FA@UKLNXiIWUs){L@xaCoK(h%JA92hE@pt!Jy!Bu)|vBui+GCN*4=QB{A-#hxO2qP#1pL zmAa<%>fui!mFsh+j`*lrKwte`{p7y(!Ty#hi=)D_o;Ce>*tDISb_acVxrKZ3nNMAG z{ge&OP1~C82xzOil^c3=sDQrwN6Ov$EWX+0_VVC6+iK08jScFCD*{q!j~~(AzRrIW z-JJj16#~jyy6{6|gMiM2FX5|BZ)`dLk$@^o1hh*1Xn&LHT+6Bp&+GW}o1<%geU z^woXbJVQX5M=b)Xn{sHuY7H5^6h^BKuGC#T`S61~=gtY}v8VQdfM%ra+I>?%jc%1B z{`MY?9sOv+&kFxPeaLqiHW$AR-)A=G>P_p8TWLEZ2t5})qcPb}KoH~4QxHwWKd9(ZbnYkN`;a`<)@_26Vq=Hh?U)i-4E4*T)a zR=@8^f#Regeb~E`3olKG&RN($D)gW4)yCh{CzX7g*2tfA53JgIJ+KN7(|n582fo%j zj(l*yU-Tc2zYSFO84!$6cAV99%q_Jtj-K0@v}8Bp>Eu%2$4{fZ<7r7RUeMCa$n6YF z>?2b+DZ3>aaOsf`K2&I5&$~S0>p)s^=5YaRTR8HGr1XbzQXg|A^s+GFd zXxW5AD94+YaMb}BB^3ybrWH0Ib!WV`&A4@d#x=Y#LB7>!TF%Vi9l1W6ueCdlnF>L99$9SQKx3?d#F^ItfkkvuKZ+H7JGEjAwUzmTxmgRIlZ8`Mhp!mCgI=SpNLqqsQ4(m$qRb2Lh*1t7 zMteunlF>)dHVVL(=7z~;(q=lYo}aIjo_d6sJ@z|Uohav`@u|cxE(Y&NRmuj_vC+g@ zZ`a2pOcKbahDo?3H3Z|NKyIPP8fU}uBjXukzCMuq1z~vOU+V)%KUuN(cLaW_k5Rr9 z&%k#d5IfIkqohvs3N2eB<}ak92i$fq8Qj*K+_BsITAlK@lrG%%avYOImu}}6S*v64 z#QEMm5kRw2!4oN|9eW~+dcx3Y@JzyW3j(+qV(^Sh)5)aMnCvrNoz@m&lW|<%}VpC{G!$!Z$qeUyc;Fdc84>^LrI~i9imHG?M?+X%a2j%McxBt;q4_ zm_lo5#+hDBqct;YS1+u|@NorGWATkeV>vh{IUOFE7u4?fB;DnCL3VlXzrN4zjmQOy zi8y&5*c*{3Va~x$-X!)$uoxrwFV3g(;Hv>%P7m26t3<9O3x8T*W^!^I|V*&K@L z@K%$*qFri)d!La6S~Rm*bP@!&^!6v&20Ga8v13X!IuH;dCaFME@sI-FQS6ivbxoxZ z>pVu=L*Z#vsvS^Y+Fn3V4X!TmRO%fV?C8oPjKGk26m=52YigJ?Cs2|Ok1@Y8kQu@h zNw=AeX1NdJ1HH|QN6jH5m0ULmj~h<)z`t9V*p2}gB~d_!LoH*;bDC}Ox6|8(?-cPG Oi@)4f$JzBk@BarOT^`^7 delta 7032 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 literal 65253 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 diff --git a/Content/Samples/BasicUI/RpmBasicUI.umap b/Content/Samples/BasicUI/RpmBasicUI.umap index 94a77615f073efe04391b0f4b5a6eee914eb4d48..6a0e9c7e3c905a425c1174fd020aa94ec8b4e3b2 100644 GIT binary patch literal 66101 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

KK`IJUF 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 Zt_m#}q?aiIu|F5VEkgZ=&+fDG{{X1Rr-lFk diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp index 17509d2..4872d04 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp @@ -12,6 +12,7 @@ void URpmAssetButtonWidget::NativeConstruct() if (AssetButton) { AssetButton->OnClicked.AddDynamic(this, &URpmAssetButtonWidget::HandleButtonClicked); + DefaultColor = AssetButton->BackgroundColor; } } @@ -21,38 +22,26 @@ void URpmAssetButtonWidget::InitializeButton(const FAsset& InAssetData, const FV if (AssetImage) { - // Set the image size AssetImage->SetDesiredSizeOverride(InImageSize); FRpmImageLoader ImageLoader; ImageLoader.LoadImageFromURL(AssetImage, AssetData.IconUrl); } - - if (SelectionBorder) - { - SelectionBorder->SetBrushColor(SelectedColor); - SelectionBorder->SetVisibility(ESlateVisibility::Hidden); - } } void URpmAssetButtonWidget::HandleButtonClicked() { - const bool bIsSelected = SelectionBorder->Visibility == ESlateVisibility::Visible; SetSelected(!bIsSelected); - OnAssetButtonClicked.Broadcast(AssetData); + OnAssetButtonClicked.Broadcast(this); } -void URpmAssetButtonWidget::SetSelected(bool bIsSelected) +void URpmAssetButtonWidget::SetSelected(const bool bInIsSelected) { - if (SelectionBorder) + bIsSelected = bInIsSelected; + + if (AssetButton) { - if (bIsSelected) - { - SelectionBorder->SetVisibility(ESlateVisibility::Visible); - } - else - { - SelectionBorder->SetVisibility(ESlateVisibility::Hidden); - } + const FLinearColor NewColor = bInIsSelected ? SelectedColor : DefaultColor; + AssetButton->SetBackgroundColor(NewColor); } -} \ No newline at end of file +} diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp index afe05b5..3e37ede 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp @@ -11,7 +11,7 @@ void URpmAssetCardWidget::NativeConstruct() } -void URpmAssetCardWidget::Initialize(const FAsset& Asset) +void URpmAssetCardWidget::InitializeCard(const FAsset& Asset) { AssetData = Asset; AssetCategoryText->SetText(FText::FromString(AssetData.Type)); diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp new file mode 100644 index 0000000..12b7fca --- /dev/null +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp @@ -0,0 +1,68 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Samples/RpmAssetPanel.h" + +#include "Components/PanelWidget.h" +#include "Samples/RpmAssetButtonWidget.h" + +void URpmAssetPanel::LoadAssetsAndCreateButtons(TArray Assets) +{ + for (auto Asset : Assets) + { + CreateButton(Asset); + } +} + +void URpmAssetPanel::ClearAllButtons() +{ + if(!AssetButtons.IsEmpty()) + { + AssetButtons.Empty(); + } + SelectedAssetButton = nullptr; +} + +void URpmAssetPanel::UpdateSelectedButton(TSubclassOf AssetButton) +{ + if(SelectedAssetButton) + { + SelectedAssetButton.GetDefaultObject()->SetSelected(false); + } + SelectedAssetButton = AssetButton; +} + +void URpmAssetPanel::CreateButton(const FAsset& AssetData) +{ + if (AssetButtonBlueprint) + { + // Get the current world context + UWorld* World = GetWorld(); // or whatever provides your current world context + + if (World) + { + // Create the widget instance + URpmAssetButtonWidget* AssetButtonInstance = CreateWidget(World, AssetButtonBlueprint); + + if (AssetButtonInstance) + { + // Do something with the instance, e.g., add it to a parent widget or initialize it + AssetButtonInstance->InitializeButton(AssetData, ImageSize); // Pass any required parameters here + + // If you have a UMG parent widget: + if(ButtonContainer) + { + ButtonContainer->AddChild(AssetButtonInstance); + } + + AssetButtons.Add(AssetButtonInstance->GetClass()); + AssetButtonInstance->OnAssetButtonClicked.AddDynamic(this, &URpmAssetPanel::OnAssetButtonClicked); + } + } + } +} + +void URpmAssetPanel::OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton) +{ + OnAssetSelected.Broadcast(AssetButton->GetAssetData()); +} \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp new file mode 100644 index 0000000..b1820de --- /dev/null +++ b/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp @@ -0,0 +1,5 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Samples/RpmBasicUISampleWidget.h" + diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp index ea7640b..fe0e84c 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp @@ -2,7 +2,6 @@ #include "Samples/RpmCategoryButton.h" -#include "Components/Border.h" #include "Components/Button.h" #include "Components/Image.h" @@ -14,11 +13,7 @@ void URpmCategoryButton::NativeConstruct() if (CategoryButton) { CategoryButton->OnClicked.AddDynamic(this, &URpmCategoryButton::HandleButtonClicked); - } - - if (SelectionBorder) - { - SelectionBorder->SetBrushColor(FLinearColor::Transparent); + DefaultColor = CategoryButton->BackgroundColor; } if (CategoryImageTexture) { @@ -26,7 +21,7 @@ void URpmCategoryButton::NativeConstruct() } } -void URpmCategoryButton::Initialize(FString Category, UTexture2D* Image) +void URpmCategoryButton::InitializeButton(FString Category, UTexture2D* Image) { AssetCategoryName = Category; @@ -40,11 +35,10 @@ void URpmCategoryButton::Initialize(FString Category, UTexture2D* Image) void URpmCategoryButton::SetSelected(bool bInIsSelected) { bIsSelected = bInIsSelected; - - if (SelectionBorder) + if (CategoryButton) { - const FLinearColor NewColor = bInIsSelected ? SelectedColor : FLinearColor::Transparent; - SelectionBorder->SetBrushColor(NewColor); + const FLinearColor NewColor = bInIsSelected ? SelectedColor : DefaultColor; + CategoryButton->SetBackgroundColor(NewColor); } } @@ -52,7 +46,7 @@ void URpmCategoryButton::HandleButtonClicked() { SetSelected(!bIsSelected); - OnCategoryClicked.Broadcast(AssetCategoryName); + OnCategoryClicked.Broadcast(this); } #if WITH_EDITOR diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp new file mode 100644 index 0000000..8acdbc7 --- /dev/null +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp @@ -0,0 +1,13 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "Samples/RpmCategoryPanelWidget.h" +#include "Samples/RpmCategoryButton.h" + +void URpmCategoryPanelWidget::UpdateSelectedButton(TSubclassOf CategoryButton) +{ + if(SelectedCategoryButton->IsValidLowLevel()) + { + SelectedCategoryButton.GetDefaultObject()->SetSelected(false); + } + SelectedCategoryButton = CategoryButton; +} diff --git a/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp index c8f8980..cd69baa 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp @@ -9,6 +9,7 @@ #include "Api/Assets/Models/AssetListResponse.h" #include "Api/Characters/CharacterApi.h" #include "Interfaces/IHttpResponse.h" +#include "Samples/RpmAssetPanel.h" void URpmCharacterCustomizationWidget::NativeConstruct() { @@ -149,9 +150,10 @@ void URpmCharacterCustomizationWidget::CreateAssetWidget(const FAsset& AssetData } } -void URpmCharacterCustomizationWidget::OnAssetButtonClicked(const FAsset& AssetData) +void URpmCharacterCustomizationWidget::OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton) { - FString GlbUrl = AssetData.GlbUrl; + const FAsset& AssetData = AssetButton->GetAssetData(); + const FString GlbUrl = AssetData.GlbUrl; UE_LOG(LogTemp, Log, TEXT("Asset Button Clicked: %s"), *GlbUrl); OnAssetButtonSelected.Broadcast(AssetData); } diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h index 4e37187..357ae92 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h @@ -12,7 +12,7 @@ class UBorder; class UImage; class UButton; -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAssetButtonClicked, const FAsset&, AssetData); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAssetButtonClicked, const URpmAssetButtonWidget*, AssetData); /** * @@ -23,34 +23,35 @@ class RPMNEXTGEN_API URpmAssetButtonWidget : public UUserWidget GENERATED_BODY() public: + UPROPERTY(meta = (BindWidget)) + UButton* AssetButton; + + UPROPERTY(meta = (BindWidget)) + UImage* AssetImage; + UPROPERTY(BlueprintAssignable, Category = "Events") FOnAssetButtonClicked OnAssetButtonClicked; - UFUNCTION(BlueprintCallable, Category = "Setup") - void InitializeButton(const FAsset& InAssetData, const FVector2D& InImageSize); + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Button" ) + FLinearColor SelectedColor = FLinearColor::Yellow; + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Button") + virtual void InitializeButton(const FAsset& InAssetData, const FVector2D& InImageSize); - UFUNCTION(BlueprintCallable, Category = "Category Button") - void SetSelected(bool bIsSelected); + UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Button") + virtual void SetSelected(const bool bInIsSelected); + FAsset GetAssetData() const { return AssetData; } protected: virtual void NativeConstruct() override; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Button" ) - FLinearColor SelectedColor = FLinearColor::Yellow; private: - - UPROPERTY(meta = (BindWidget)) - UBorder* SelectionBorder; - - UPROPERTY(meta = (BindWidget)) - UButton* AssetButton; - - UPROPERTY(meta = (BindWidget)) - UImage* AssetImage; + FLinearColor DefaultColor; FAsset AssetData; UFUNCTION() - void HandleButtonClicked(); + virtual void HandleButtonClicked(); + + bool bIsSelected; }; diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h index 07a4be4..6289f16 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h @@ -17,7 +17,7 @@ class RPMNEXTGEN_API URpmAssetCardWidget : public UUserWidget public: virtual void NativeConstruct() override; - void Initialize(const FAsset& Asset); + virtual void InitializeCard(const FAsset& Asset); void LoadImage(const FString& URL); UPROPERTY(meta = (BindWidget)) diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h b/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h new file mode 100644 index 0000000..1a8bd5f --- /dev/null +++ b/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h @@ -0,0 +1,52 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "RpmAssetPanel.generated.h" + +struct FAsset; +class URpmAssetButtonWidget; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAssetSelected, const FAsset&, AssetData); + +/** + * + */ +UCLASS() +class RPMNEXTGEN_API URpmAssetPanel : public UUserWidget +{ + GENERATED_BODY() +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Panel" ) + TSubclassOf AssetButtonBlueprint; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Panel" ) + UPanelWidget* ButtonContainer; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Panel") + TSubclassOf SelectedAssetButton; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Button" ) + FVector2D ImageSize; + + FOnAssetSelected OnAssetSelected; + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") + void LoadAssetsAndCreateButtons(TArray Assets); + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") + void ClearAllButtons(); + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") + void UpdateSelectedButton(TSubclassOf AssetButton); + + UFUNCTION() + void OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButtonWidget); + + void CreateButton(const FAsset& AssetData); + +private: + TArray> AssetButtons; +}; diff --git a/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h b/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h new file mode 100644 index 0000000..0b757a8 --- /dev/null +++ b/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h @@ -0,0 +1,17 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "RpmBasicUISampleWidget.generated.h" + +/** + * + */ +UCLASS() +class RPMNEXTGEN_API URpmBasicUISampleWidget : public UUserWidget +{ + GENERATED_BODY() +public: +}; diff --git a/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h b/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h index caf58da..43cdf6a 100644 --- a/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h +++ b/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h @@ -10,7 +10,7 @@ class UImage; class UBorder; class UButton; -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCategoryClicked, const FString&, CategoryName); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCategoryClicked, URpmCategoryButton*, CategoryButton); /** * @@ -22,9 +22,6 @@ class RPMNEXTGEN_API URpmCategoryButton : public UUserWidget public: virtual void NativeConstruct() override; - - UPROPERTY(meta = (BindWidget)) - UBorder* SelectionBorder; UPROPERTY(meta = (BindWidget)) UImage* CategoryImage; @@ -42,10 +39,10 @@ class RPMNEXTGEN_API URpmCategoryButton : public UUserWidget UTexture2D* CategoryImageTexture; UFUNCTION(BlueprintCallable, Category = "Category Button") - void Initialize(FString Category, UTexture2D* Image); + virtual void InitializeButton(FString Category, UTexture2D* Image); UFUNCTION(BlueprintCallable, Category = "Category Button") - void SetSelected(bool bInIsSelected); + virtual void SetSelected(bool bInIsSelected); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Button" ) FString AssetCategoryName; @@ -58,7 +55,8 @@ class RPMNEXTGEN_API URpmCategoryButton : public UUserWidget private: UFUNCTION() - void HandleButtonClicked(); + virtual void HandleButtonClicked(); bool bIsSelected; + FLinearColor DefaultColor; }; diff --git a/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h b/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h new file mode 100644 index 0000000..e6e7b57 --- /dev/null +++ b/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h @@ -0,0 +1,24 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "RpmCategoryPanelWidget.generated.h" + +class URpmCategoryButton; +/** + * + */ +UCLASS() +class RPMNEXTGEN_API URpmCategoryPanelWidget : public UUserWidget +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Panel") + TSubclassOf SelectedCategoryButton; + + UFUNCTION(BlueprintCallable, Category = "Category Panel") + void UpdateSelectedButton(TSubclassOf CategoryButton); +}; diff --git a/Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h b/Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h index c31d2fb..da71322 100644 --- a/Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h @@ -3,11 +3,13 @@ #pragma once #include "CoreMinimal.h" +#include "RpmAssetPanel.h" #include "Api/Assets/Models/Asset.h" #include "Blueprint/UserWidget.h" #include "Api/Characters/CharacterApi.h" #include "RpmCharacterCustomizationWidget.generated.h" +class URpmAssetButtonWidget; class FCharacterApi; struct FAssetListResponse; class FApiKeyAuthStrategy; @@ -15,7 +17,6 @@ class UImage; class UVerticalBox; DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAssetsFetchedDelegate, bool, bWasSuccessful, const TArray&, AssetDataArray); -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAssetSelected, const FAsset&, AssetData); /** * @@ -59,7 +60,7 @@ class RPMNEXTGEN_API URpmCharacterCustomizationWidget : public UUserWidget void CreateAssetWidget(const FAsset& AssetData, UPanelWidget* ParentBox); UFUNCTION() - void OnAssetButtonClicked(const FAsset& AssetData); + void OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton); FString ApplicationID; TArray AssetDataArray; From 4f971f15aea258eb8b62c9f1193179d36c6cfcc6 Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 15 Aug 2024 16:07:43 +0300 Subject: [PATCH 27/74] feat: WIP UI example --- .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 51095 -> 72446 bytes .../Blueprints/WBP_RpmAssetButton.uasset | Bin 29837 -> 27371 bytes .../Blueprints/WBP_RpmAssetPanel.uasset | Bin 0 -> 24397 bytes .../Blueprints/WBP_RpmCategoryPanel.uasset | Bin 91750 -> 42951 bytes .../Blueprints/WPB_RpmCategoryButton.uasset | Bin 29917 -> 27537 bytes Content/Samples/BasicUI/RpmBasicUI.umap | Bin 66101 -> 0 bytes Content/Samples/BasicUI/RpmBasicUILevel.umap | Bin 0 -> 70708 bytes .../Private/Api/Assets/AssetApi.cpp | 4 ++ .../Private/Samples/RpmAssetButtonWidget.cpp | 1 - .../Private/Samples/RpmAssetPanel.cpp | 57 ++++++++++++++++-- .../Samples/RpmBasicUISampleWidget.cpp | 4 ++ .../Samples/RpmCategoryPanelWidget.cpp | 31 +++++++++- .../Api/Assets/Models/AssetListRequest.h | 11 ++++ .../Public/Samples/RpmAssetButtonWidget.h | 1 - .../RpmNextGen/Public/Samples/RpmAssetPanel.h | 20 ++++-- .../Public/Samples/RpmBasicUISampleWidget.h | 5 +- .../Public/Samples/RpmCategoryPanelWidget.h | 18 +++++- 17 files changed, 134 insertions(+), 18 deletions(-) create mode 100644 Content/Samples/BasicUI/Blueprints/WBP_RpmAssetPanel.uasset delete mode 100644 Content/Samples/BasicUI/RpmBasicUI.umap create mode 100644 Content/Samples/BasicUI/RpmBasicUILevel.umap diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset index dec164444a96a429d28220382aac701ec1519c52..1e553f8a1ad2fad9c2c4601dbed569cc29a4aaa3 100644 GIT binary patch literal 72446 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 X5gqU!P$@JAxkGB%Jl}u(-mw5kx!@ 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 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset index 456d40abdb82e8e13dbb2ecf7f72c26e2313266a..dafa237421b4d4b23bd32562ecba04e3cdd93582 100644 GIT binary patch literal 27371 zcmeHQ31C#k`JW|$KmZ|#T!OqHHzXk;ghMRkPJkSc1VlyH?7mGFHv7Wv+k_x@1rfDY zMQueCDHd8$JP=y(0IdgBMN74c|B6^k1w2p@kSqW1H*aS5CD|mKfc)Ekov=Iaee=zH z=X^8s=IyZS=YtV13>zeBgz={+lu)qF2AO zY^!xB!9Gj);EDEmxAfk=XwT;L6V4_@5^S7)X3vteCsOx!zVmLc(yMDff~DNB=7Wr! z=lgEj-07yOuJ-+X3HD{_6Q3PQS$(YSzWDXYJ2GMi609(jv6htQYHpP>#%`g{8y3c* zDIbxMlrUo0=%mC^$%&(rQWBC9l2YR1N2eyFWTd7i4;uk|o;uTar%sIZpuEK;j4h)J zYJ<(boG}l5&h}@l|Kg{1hNUgd-8_E7#?qWUmna*u_OXa@{{ zqcC=VukjXYlnohDls;m}6xF3#y=u&e*kQb!v9Hz-s;8VP@s!XaW)v>80gU}?wgHph zWUy*T?;rrKk#gudZc2Gst~yW8RNZj}*0OS!>WxdWdY!hS?6?${Pc8R2-MTk!T1x&* z!c6vhRXxS0>zX^ZE{Nsm%a6CEMw4BxX-<2Is(abX{VuzYRK$`!9&1&;M=Mu7dKJrl z;vdcq1T7{^tul>d)t#D~Rey8Ik?sJex~yLB6m_o8=~2D8R^3^l7CLQn*c&~ab1&lv zT3MOuCRU1zU24A5?N;q>U(^^EPp3$t2#$B(=n9tIAbX zd!d%^^OUFsb5z$9)!}7#Ja+w6P=;TznLekT?TP47)YS|vsPgJ+*;Lizg;JL4ZHCmS zR9IE6vUX8-?T6nKYnqG2wzVH_j~3|0uj7v4PppdIKr&YutKL3;1Kg`PuR`^BoOU(C z=eBXJcRc>f>skTPTl~{Zp_X$gpz92)e_B9#ZD|*{Ypm@pg{BJ zPIn3G9CgLp2n4Byeb^h_pL+qon*Q+&E|so|Gzb|*dBw9;o1SmgOWE7W4=sUHrDtg#=X}kr zTV2U6atzdD)$iRp2Sn4|R`Mcyfeub~^~?P`h^D(soD@j5Thb0fwdvry2I7kGAAQ#| zSjm_%Bn5AgJ^ARlzZw*LWqyTE{@dE|@YeJK7ZK!^frQ-jlcQ6Pp_c?H8#5+Fo5v#D zgU_@BLIp{naE-EqKlI*$9>-zijH*X<+f=r3{eefhzR25jr_C(+)h{1jj|T}{le-m^CRx3DO@H*Vc2~4`2W(DK9Xg9!vGNGqDaYy|e`3eZ6?H}w z%~8vWRZp&!n7FO?)7wDM0OecBcsg5jXyI7&K?4}D>7s3a90!}`Xm+1VrGbRUPwwjF zeQve#rmHeltxVOaC%@Hc3q#YO<5%~xZ(C1*&~Rwkm1p&x`5oi~jBEGZ&EbzqK)&Xk z&O8}ksxTxlyzGzX*0>-dA8U_=z0w}`6WvaZ1IZsfeVBuNdbv->IKX>)yRy4-dJ%TM z)i%dkqE6DtF`e^C49yKTz2#PN-A;PdN(7R8tKE*!lQ?_vTxd~f=c*%nZ-WTNt>G@+ z{{F~ykd>S_5RlsL?VAriJ*w5krtbb-EJT>%EGgAl+v9^=P}dYq(~Vxh+NnE7L+J(9 z3e`TK(UMqQyYBW*%k7 zQqV7OX*!!6eed0%j~g$``=4t+fqDuwhi(d3iy!{zVTe|sd;Au=YL}-k=!hu0I{Myg zcugL3{aC};17|=>FI8z^dGaqyaKn4#FM9fNQ2{rmz8$yVy)wkcn&2rkHKCE|74Ffwg_fTj$qVV~MVoXTb9V-v@g>DT?6?#;a%^7t2 zsWAj<_AG}zyRTw22In|qUJ#fQ8DaI%M+?%>$7OM<=243bQ?ZlTF+YO}WSJN(R*YYs zjqWMIfe9EJxgx411eZnJOVA`sw9dZ~dXptZIhibJ#)XgF88-OzNnwsnRveDiywKh2z& z!2Yo7SM9+zAK47t8IM?Iz@z*@uGh4b8zF}wcZZ*)--j?TF#(ZkCa=i6%dfp0b#heQ znr79l?6W0X?}m1SHPwkgu}edY2KBc-&&%%$1iRD2O=b zZ+h*}4JzEY=8RhC@67#=Yyx+N!%-i{tZCw9H?4$_MPAh-@Y)|)SOGt(t6p||cg&rl zo7A!qY5we)Rdc{pZH4FEu>04rTP=945x3&ab=^w|3&~?->?o}zD#dlW$dLuZyDu6z0q-knuObAzF9~m12zW;buRKJ&xgp}s4-s!shJ5S? zb?|dh_66Zp1;LXvqzzyXUl2UWvntB|MR@Ystd}1ME>IXEl3U!gru-gpjb{%{P1LNv zU0K&m3j=R3T8AzWEj;Ctbn(0feHl6A`W1bU7G)WEQ@(@AWu`?B)q>LMlHC+425vA~ zr%iHUNS!B|g≫trI3%@D0sG>uGUqn;P4O&}srgV8!? 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 literal 29837 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 F{||^>KSuxn diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetPanel.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetPanel.uasset new file mode 100644 index 0000000000000000000000000000000000000000..5f245122bd71cf62e642ef56199457ec58c21053 GIT binary patch literal 24397 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset index 83e4c410cc6d7e2020adf98411e2483883b7f10a..34e93ebf18ddec92311760b336ad25aac11969dd 100644 GIT binary patch literal 42951 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 literal 91750 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 diff --git a/Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset b/Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset index 45e3d6bda5240a2eccc2a1d9a109a442d998662b..fc086588e10ad84fff6c8372dfaf9749a191f2e8 100644 GIT binary patch literal 27537 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 literal 29917 zcmeHQ31AdO)~*5M2!{qa1VuU=hBJXAata|gfslk;D6WLbOiwZ}nF%vJ34`3L$f7Q~ zc(010F02Q5fbOmuE@H&uKcd3hzcHD%Na8N`>LyEI>}5j0p#EPwO~@+Rj=NA z_1>#juez(dxBq;|gU9`T|LoR`MYUq=8_JQ|)9;W#3jjz31!HJC1bxXym$-7c=7qQQ5+&j9ox^uFI)X#@KE2 zd&|UF4CNz6q>f5UxN>w-(ulN#)KQtKnW-sblE$Xcc|y|Iq;$aZbP4@!dog2|QU04Q zj9t-(F~Y$zFDK0O>ot_IufBdQDejr#oK>5mC%#@XX#ZQ5lkNWYv((&M?s@!^o`3$} zuYd<0FaVBPv-|o?GIgb+p+k!^t{hsRI#jbq9e!op2wuqw|m9F7@wYq_d<*sBA(-%WyIDQ>sf zm+y8}scy~3vY-Ck4!~5iWp26K<#k$F)*DC0T}VY`)Yw&%PD0S^E+_LJ?6SWXiqjls zkEcML=e4_4Pp(W%ha9Ie@FOQgv#n-m)@>ns0YHRV$kpH!!D1qskJy z$4)S8`hcsh>W12k`Bi48Rkdb2(^ZeX+^M?RoqsOPyA*Yk&7MMw+g_z*LqwpUTJy~$ z8a6HIw4--85YF4wm&zL$fok)x)ldCwJmeFQc$U|0 zWjosSF79b8E%bRbwX#HYdmxl$T8E+a8Ws7fRMsi_SD(P3%3Ll7i|c6J*BKb-Bp~*S z+C06g9WNv$Rx%F+RTo2x_Ly$O0ln+lE z^;tAQq^mZw*P&&(%~ch-F00Bmf3~+8G_7P07lx0}$q^xw8#S67%J8drZV(CSvKuE93E1RQQw0yHx!QM$( zyA)=WG1=v|FK{_Evm?bprht~r|NhN$K{Uf@CcCi~YT#u1z}$~PG{aeLC!g|yDSa=5 znE}4*0j`*|>o6QfqndH!s23a~d*+F{zv~pdl>vcI|HlK9V4oR<4kDQ9_IfJNL$2)U zF{wu|E<)6e8<*;u&)PYM)OG?uHT6K@5@mZ&^xcFp#>>e3RJZE1sBHbZPoLoOBKy+p z7GuvRyt?m3c*qQWJd;MN><$`um3;8!yH%@Qx0pu{74#wJ(=6b)aWm`=2Ok~Y*I)b{ z>{XqwTG+yUwY}TYiOu0MYpk2^fwy7cnK%o`cgmVlvlfX(o|pDK~d|9g+>D?Dg9ZjsiTKfkQK+uPlA}$RfQg z$jxK#jhX_LWqWj|!2v({wC5Dq>r~ZO<8oU)ENyu1W>Cs8d$juY=wY2MZ}l$N%u#I` zi(j#FKS<<|F`3aU&NZIw=K3?qAs=OLz13vmYRsL!kO}Q+e<_&YXn&Tp4wJ)GLzr_$B%J;M+O~qQXStLkPklH zs@cIx-u!JGx=>&*uh3Y>qk|m~SAol==_7-6QePemp%@d))daxGJ<%sFh~sfAQuTU8!Ee@%F6A;xYIO{h9&Jr|XyKzl_raTVFC*p_|mwj!7;a#gXDrFU-ug<>-Qa?BC= zWCR}8yZ^dGbf&~G!tWn>=yKR#2|0L|J0YDFk8pH=e{SsKlGbb(0Xgf6EOM(Vn>+Z{ zlZeqM%uy)#QPRlUAo@}C@Q2;_;j!-~Q2p7&Q0(aJUN2&jkJsaiV9=#I>#uh$Oo!)? z)oCubTC8i99nT&<5rU9)Vh&m{>Bejfa9JFT@Yv`T(HEfmvWnXoP_iWRg6knYSyMbM zi;cSOkYa?UGB_`CNl(to{sKmlO5TmaB>T6P>mfV+l$+&h>s=pk2p%c2tM;wtF`+Iw z4)OkdeWSp=l&TQwSGUD3gVUGA*_EVSR{CxKG+4PlP_>%*woaIVun@MNE_GY>-6A(pX(#HbZP?o-S&Gr zLkfIe)nTuD%yb>>FW^r5%t*Z+{m^@L;lzx);asOCz$cdShRk#0%uCT`nyQ)8&6=6* zS-N=*L@BhcPW{#deh{uv?89;#?vJ23an5DibNSC4y7S{6l6kULc8B{B9y5UYfnkrT z&~T&Bp3VFGxk|96TO~ON1Z-2g->{NdHn~xNivG!U*8@6Wq!O;Jzj}cZ6`B2;sB{;k*&TEsYTFh6v$q zj1cbT2;pvx5UwHuxUWbbw=@B_LewFf8zG#u-B9*y#8(+1K4*k*t_b0(B7~b40#5QG zZ5?*+3jrr(@C#A5kKmSsfRi+&4xo<(A>bs>Qm0=KoIEzl`2>}V<_w-{l{jfnc|W=; zhJ@=aElq?rfLFFP(n6>Ziq?bEBy{9?C|bC(Co$j}6?_A7^y@JFkY3Ky^OpQ=juu1~ ziq>=aEuo^rhN5-M&@bq#TA)S0?h(honJx}mkTpjO`U*vBVnGXt2+&ZpjvD%f7#zM= z>MH{Wls@>J0rtM7fN9(l+{IHAU&AACMNtG-P;Yet`sNc`*nmjh&_EE%_UY)@g$bf$JuLkh^%qyp%Rt^c;U7Uoq^0g&~eY(K=$F z1v^CC0~RKz4!kBw6IDG zMJq{8@>Cyb3e~Ug4gCV1UkYR}i&Y{9c-ug$IoptS6N=VBDm-bRw_4D`xRy3qA_I9* z*HqRNtpf%+@VylRQ3uwB$zkl;zcF1ePDWBb zNgUsMj%Y#s@JU!*;~4JtVsQ-F!%d+aKg^}bvB*ao9HakegJZzr@|c8$eF3f${jf&w zLO~IcW0h_@38+;=^-a|jx zUh)dwB_6#$C@mm}Q>X_rpIdsXZ0HcggEdZCF3o&q{qa z#1>31_+sV>KYMv%&AP2C>t1`$&%XTUlEk{CC1qdx+2L=t-Roz)OI9!0|NW4EkB{G0 zXFB{+_aj+H3+O{cP#N?=u!fpT2p$ zpY3?IuDrIwfO+NwKil-j_`+vT`PuHi<9C*(8)+p!qxsp1qor^A*{wZ5x8vWx+3IJ< z+r4vS#?D|5R)2F{a^0f44<~<~@yI79PrdRJbG`o$BsVwtn)jX>?x=Y+>JjJbz6rM` zt^0V>=|y|q@%HZ-SG<00ZL5FWv~5oxKWkmLe9O@IS3fz_#m|=cS<1{i)*npR;AdSX zuB0KhCV$9YC=0P>{G+))pt=10@ta<( z>p=6>2Q*(L4?WTMz$2&TV!pbwcJ=WY?;M#v|7hLT-9K@5US+o91Mj62mfZIC#$C_- zGRrdS!i=QP9{Be3qCdUUePGYa)lGNTUbJr2kj}5H8uU=B|I*)u{(s7NA32scSOzt` z<%uO!=o^^@morBH8@6b0GZag$knQPKXXl}G3v(&~Py+q%v_f%)g&fNB&9u)_Q{^pp zD$y5<*sPK1rrnmbLCnXOp($R?#V(}k`ujNe5;3r)3v0ak^ zi`PMexqu9NIJ0Tzq1#6Lh4jlAoRHl0%C|&?7bv9AKvTGO&_Mh3A!5&Ft4QeWwxwyY ze4%gL$TW=j;5#K^<#vwX9+0?3_xfY`#QNj^Wktdji9lsNQ-nPQYQ&aLXcn#bg$3T%Ooy3F2-hk`(r)cBiB~4@ zI@*{=n<(;}4j;c>k!f~#RDOjd)1Jz=SIj-+_qXO38cgh0)C9}>5#k)j_}3wDeu+5B zqMg((rm`vYH;q-&JsumIM|ZGL>SejCjPf$Na%Q746Ddf?Go8+=2o9xQg0(TN+1gg3 z?b-6sR^TaN7Qt2H5-EpsMJtbF;3R4$lD#g0U;QxZOx`>h-l8naNdFlR(mDve{ zlQ5ZB9|19$5ZHqpotd^f3p^Rb zfkynpD&}#0R}n`hx*n(ZJY2n$Rge$eDor6Q%|s4dO16px`&vwPRz*^QmCYlF_5x}! z$p~E7*bpjdCrX2q&aB504{)oz*FoIIGRcL`Fd z1@#%j7P2fFjV_Wq`i{Ozt5{6Xax_U>3kPeW=}vNKBFCj4m#};qJ#&dOU=MA37}31~ z@EXB-*ww^|%DsV?jc4!zHkQtL(+H1;_ys-_&8#{d9U>+qjMSCPO>`|*Z`CrNByB34 zj~$IHRm#aEyr7BbV_em*ixSkLMIMx?@kq+os7>Rj4AMB;en@_#olPcbLfR&}X0FSn zagFrHP4}UnXg`wVFp~a_qVbnVp5;o)$5PoCHjX8c6^vyGRGUagnRJ{%M+sDuL3L>a zn?P9Ph(FuDO1_O=F{rhsYo$h-?sHes=S-4d(3=?P&1TSP&{og3PttcxCR(^f0b4KABoER*Tvs=>= zl(qT9x0iZ`zcJKj8}V(UUYF51;t4r3OFf?H>?yq|D3lZSYNF$1Gq}vL7BJB=R+mwD zh~5H1hr``Yd333$Ia@B}yrWAqT}YvVwCy2H27O4l`pX0#Qo5l%k(_4{vDKHhjMvY$ zAF}-vves1M9hMCK!566OLE>o0Ih z3;zEa*-L+3!sXR4vJVy02jgt4Me4`<1BBF?mbTQ%D2h0=ZRF4MKC5d@JFI0DbLVa!q~9VlOMs^CRlPsi!?HtWDB{c zav7(Rj8jSG&`>JrB8&6{$tO@ghR#QkF7&cQ(igO1VzCj?lYSOj<*Rt!_FghA3&QO!UkNc60_v3X*;`~Z+K8l}T%dtq< z(V}jQfE_E&M+!Qi02(?i*$V4!SthBBq;V+AbV_I|@sFgj2)(?|x<(HYIPGyncM+W%WoE?I6oo|2vyMye zQKS?S4SkdpaLq#YTlOK*(AVZDT-T@{iCjM5Ps&?LJE+mbfaKscrB;5@X!AA`O5V_0 zbMlr}`IEfio#4UFYy`ok^_Fr38OLpK1WB7u3V4_#!{}>nRMqzkNro{hsU{i5sHB<{ z7T`;=EBdR+Pkv z9NV%*dT5eG9EGgRYpN1EyOBb+%+%}n9d{f`W}gt{MIvvMnMH|gkS)%O8$pVKYZ^4b zUIwIbdKT*M(&PosjAXrLX|iPXG64#MK*A1sq=9-yulN!NvcU{-j{Dk3vO$)9N|$oz z0Q==jM1iabC;+#+Wh=?IfThNGy8)7XVhTGocmil)gU9bBQvmqMLb#aZpZ{iA$Dva#6pfEgjb^{%z^k> zJ{Kq*rUVr2DH|aUWwa$B@HAw|^m6idueZ(HKI!gHlmEKr+GjpDYCh~}g^Otz%pmmP z2_dlb75W>IBEkuCKSP{@bi#p{RMML$${S^PzZa4eu#_jGU4#Cs^g5Ozaybr}d>MyC zFMBLyEZ`~7Q^etb1}OSTkpAMJt;idrfQXSA*QDwdfhh87an9drP*h>}C{B9sltQ^x zyn<4pH=rst^m0V1(UdY(p;znCyDsCR=#`V@ipyqGs_agWqPdhZuiar)Y~o#^^a4K3 z7f08*^%Sv9#a+ufP4L|^_11s2T9z{Yffy+;puq%$1an?{^@bhy?TnwarrSp=cP4I8 zLX}PC(E1<#4igL*oSJ%z8Y6a5#JD1*z5<029B|YU4&exX;S%^wdH(dQoVPcoZTZ9c z$DZ6X@6k|Y*BKDh@53-r6S5De3tM48+3>K%eRyQd13mh*`8fTdNeQS6r1Zr3tCXITBN0 z%I-Hz>t26t+Ec&ZIU}{(#+yQworV99$`s0oC~J{Xf?xmM>fe5;x+ZP-?VOt)+Og}Q zP-Xwg{^L9itXV?((iF~MU_C^xToGt~qyuZtx{|WhQKl(3zHspQD+WKmLrN1;kq!vq zTiIJ(H~qScC-we`BUctxb~c47JBxuOl_``FQP!e?1%6-o+hwC>>=>Q#?5P>^f3sj* z`%q>7$pZ`g)^6MEUV9-i>Gjw^#a^kkYt9bV<8Hz(t@ z`-7vR=xH{GqEQH=RMFQ6NEifTQ5E_$fkwraxy+O+NDMcSs&^QPFfhzU(B~gyE)ldy zC`Ll<2?L&`LZAx4AYpw^FL$Ny2G9X2deILlP=RSCWCc)!tUz-xEiYOlzQe0wmB7<> z{4qR$eM-OhAw!}%C__l3T%g;2J@mBCS;O8_ZQN6iRQKkjnaIQ3O-1S--UI?!!%~=9k#?xy? z^o09k`Q9>mIxhH@=lI||bK|f}RZnkH_Ctyz>dfRITeV0AyhpI62+W}e*^@MfG`{jw z%r<&LldG1#4?=G|_R)A$JoprXa6MSQTI4q+XsN2uAg)sR6QzpFsZ_XXcx%0hi+}3@ zO}sR4xnjA&sr>s0m1bXX&8t+>Hz@4%wGTejX^HHmBbTDTPM)um-D;()8tmBIu1Zd% z!cLzga9b*T+~SmMo8ohM6(@c1Loq8DW@0@(+(iy!cn!TgeYnM~;#oFiO(9+|cMVhQTCDCkshlv=L=P`)rYT)@ID#&7$dDN+1-aR|S<+3zLE}F@ z+CrbpQK~3LAlirsEG`dyHo|UEyj51RYDfU%!{NeZB%k7{q0jLsRAU~-2ZqJ$m!eR8#;z@3z&YdYrMEqRyv}9MD8e zm#9>97Jb$g!w0fX86(}|HhXj>3cZ04Cq{t)S0{H07{MO;oC7by%%hRYNc&~-IdSSW zT}0zNSj;qjsRsf#xgWLK?C|Q7kfK~esL*H4>vrN|8YgD_+JpfdGai`GpC=a=PRKZj zK0dDl`SW`Hjr5g&5#2#?Wn*RX<2UATtxW#Z`Jc{A^TX-64ty6C*y^Ler5sPksT z=iY$1e@1B8C`v`4+!hi;$IX7TZ3f2`ycmQS7)f5w=MgL}5y*D^bwK8UAc2hHW9QbHN4-~S1)F?flN>3B5w#7`HexyDeOHhsL8nUIFEc` zYf@Oga7oa}S1Fw%4bZcIHin3EHdGwU7P;JygD{LI+e%d zaH9a~Bl1QWRys(sK~MdZo`Y{DL9T}Hr%V^%5|135wr0=-std$XoX7(@VQlF(X$kS7 zvXO2eQK@N3TEJ3<6*E#p=EB}U{mbsYJ>EXnFZU5k#g{3U3^7UiKoZt?AUdp`kQ(w} zs%Ol`*8}VD;_oGovRbk-_)8>d7dSSufKii=LQ0K90n!-NNYYj$A_>eF{Spv7H{2f( l7CQ!QMA!ic@@?q{%^3LyH6(CQKVftrHLfwX-%cMk{a?yfZ(RTY diff --git a/Content/Samples/BasicUI/RpmBasicUI.umap b/Content/Samples/BasicUI/RpmBasicUI.umap deleted file mode 100644 index 6a0e9c7e3c905a425c1174fd020aa94ec8b4e3b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66101 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

KK`IJUF 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%0Ayg()4^_UXY7?#Ua&1Y% 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 literal 0 HcmV?d00001 diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp index e46b9a2..590ffd0 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp @@ -17,6 +17,7 @@ void FAssetApi::ListAssetsAsync(const FAssetListRequest& Request) FString QueryString = Request.BuildQueryString(); const FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets%s"), *ApiBaseUrl, *QueryString); + UE_LOG(LogTemp, Warning, TEXT("Requesting assets from %s"), *Url); FApiRequest ApiRequest = FApiRequest(); ApiRequest.Url = Url; ApiRequest.Method = GET; @@ -31,9 +32,12 @@ void FAssetApi::HandleListAssetResponse(FString Response, bool bWasSuccessful) FAssetListResponse AssetListResponse = FAssetListResponse(); if(FJsonObjectConverter::JsonObjectStringToUStruct(Response, &AssetListResponse, 0, 0)) { + UE_LOG(LogTemp, Warning, TEXT("Assets received: %d"), AssetListResponse.Data.Num()); + OnListAssetsResponse.ExecuteIfBound(AssetListResponse, true); return; } + UE_LOG(LogTemp, Warning, TEXT("Assets received: %d. But can't convert"), AssetListResponse.Data.Num()); } else { diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp index 4872d04..2249cf4 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp @@ -2,7 +2,6 @@ #include "Samples/RpmAssetButtonWidget.h" #include "RpmImageLoader.h" -#include "Components/Border.h" #include "Components/Button.h" #include "Components/Image.h" diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp index 12b7fca..4aae0b3 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp @@ -3,13 +3,46 @@ #include "Samples/RpmAssetPanel.h" +#include "Api/Assets/AssetApi.h" +#include "Api/Assets/Models/AssetListRequest.h" +#include "Api/Auth/ApiKeyAuthStrategy.h" #include "Components/PanelWidget.h" #include "Samples/RpmAssetButtonWidget.h" +#include "Settings/RpmDeveloperSettings.h" + +void URpmAssetPanel::NativeConstruct() +{ + Super::NativeConstruct(); + URpmDeveloperSettings *Settings = GetMutableDefault(); + AssetApi = MakeShared(); + // TODO - add smarter setting of auth strategy + if(Settings->ApiProxyUrl.IsEmpty() && !Settings->ApiKey.IsEmpty()) + { + AssetApi->SetAuthenticationStrategy(new FApiKeyAuthStrategy()); + UE_LOG(LogTemp, Warning, TEXT("Adding ApiKeyAuthStrategy")); + } + + AssetApi->OnListAssetsResponse.BindUObject(this, &URpmAssetPanel::OnAssetListResponse); +} + +void URpmAssetPanel::OnAssetListResponse(const FAssetListResponse& AssetListResponse, bool bWasSuccessful) +{ + if(bWasSuccessful && AssetListResponse.Data.Num() > 0) + { + UE_LOG(LogTemp, Warning, TEXT("Asset Fetch Success.")); + + LoadAssetsAndCreateButtons(AssetListResponse.Data); + + return; + } + UE_LOG(LogTemp, Error, TEXT("Failed to fetch assets")); +} void URpmAssetPanel::LoadAssetsAndCreateButtons(TArray Assets) { for (auto Asset : Assets) { + UE_LOG(LogTemp, Warning, TEXT("Creating button.")); CreateButton(Asset); } } @@ -23,11 +56,13 @@ void URpmAssetPanel::ClearAllButtons() SelectedAssetButton = nullptr; } -void URpmAssetPanel::UpdateSelectedButton(TSubclassOf AssetButton) +void URpmAssetPanel::UpdateSelectedButton(URpmAssetButtonWidget* AssetButton) { if(SelectedAssetButton) { - SelectedAssetButton.GetDefaultObject()->SetSelected(false); + SelectedAssetButton->SetSelected(false); + UE_LOG(LogTemp, Warning, TEXT("Setting old button to false")); + } SelectedAssetButton = AssetButton; } @@ -42,7 +77,7 @@ void URpmAssetPanel::CreateButton(const FAsset& AssetData) if (World) { // Create the widget instance - URpmAssetButtonWidget* AssetButtonInstance = CreateWidget(World, AssetButtonBlueprint); + URpmAssetButtonWidget* AssetButtonInstance = CreateWidget(World, AssetButtonBlueprint->GetClass()); if (AssetButtonInstance) { @@ -54,7 +89,7 @@ void URpmAssetPanel::CreateButton(const FAsset& AssetData) { ButtonContainer->AddChild(AssetButtonInstance); } - + UE_LOG(LogTemp, Warning, TEXT("Button created and added.")); AssetButtons.Add(AssetButtonInstance->GetClass()); AssetButtonInstance->OnAssetButtonClicked.AddDynamic(this, &URpmAssetPanel::OnAssetButtonClicked); } @@ -62,7 +97,19 @@ void URpmAssetPanel::CreateButton(const FAsset& AssetData) } } + void URpmAssetPanel::OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton) { + UpdateSelectedButton(const_cast(AssetButton)); OnAssetSelected.Broadcast(AssetButton->GetAssetData()); -} \ No newline at end of file +} + +void URpmAssetPanel::FetchAssets(const FString& AssetType) +{ + URpmDeveloperSettings *Settings = GetMutableDefault(); + FAssetListQueryParams QueryParams; + QueryParams.Type = AssetType; + QueryParams.ApplicationId = Settings->ApplicationId; + const FAssetListRequest AssetListRequest = FAssetListRequest(QueryParams); + AssetApi->ListAssetsAsync(AssetListRequest); +} diff --git a/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp index b1820de..61df158 100644 --- a/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp @@ -3,3 +3,7 @@ #include "Samples/RpmBasicUISampleWidget.h" +void URpmBasicUISampleWidget::NativeConstruct() +{ + Super::NativeConstruct(); +} \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp index 8acdbc7..33f801b 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp @@ -1,13 +1,38 @@ // Fill out your copyright notice in the Description page of Project Settings. #include "Samples/RpmCategoryPanelWidget.h" + #include "Samples/RpmCategoryButton.h" -void URpmCategoryPanelWidget::UpdateSelectedButton(TSubclassOf CategoryButton) +void URpmCategoryPanelWidget::OnCategoryButtonClicked(URpmCategoryButton* CategoryButton) +{ + UpdateSelectedButton(CategoryButton); + OnCategorySelected.Broadcast(CategoryButton->AssetCategoryName); +} + +void URpmCategoryPanelWidget::NativeConstruct() +{ + Super::NativeConstruct(); + if(CategoryButtons.Num() > 0) + { + for(auto CategoryButton : CategoryButtons) + { + if(CategoryButton == nullptr) + { + continue; + } + URpmCategoryButton* Button = CreateWidget(this, CategoryButton->GetClass()); + Button->InitializeButton(Button->AssetCategoryName, Button->CategoryImageTexture); + Button->OnCategoryClicked.AddDynamic(this, &URpmCategoryPanelWidget::OnCategoryButtonClicked); + } + } +} + +void URpmCategoryPanelWidget::UpdateSelectedButton(URpmCategoryButton* CategoryButton) { - if(SelectedCategoryButton->IsValidLowLevel()) + if(SelectedCategoryButton) { - SelectedCategoryButton.GetDefaultObject()->SetSelected(false); + SelectedCategoryButton->SetSelected(false); } SelectedCategoryButton = CategoryButton; } diff --git a/Source/RpmNextGen/Public/Api/Assets/Models/AssetListRequest.h b/Source/RpmNextGen/Public/Api/Assets/Models/AssetListRequest.h index 4f66f21..982517c 100644 --- a/Source/RpmNextGen/Public/Api/Assets/Models/AssetListRequest.h +++ b/Source/RpmNextGen/Public/Api/Assets/Models/AssetListRequest.h @@ -27,6 +27,17 @@ struct RPMNEXTGEN_API FAssetListRequest UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me") FAssetListQueryParams Params; + // Default constructor + FAssetListRequest() + { + } + + // Constructor that accepts FAssetListQueryParams + FAssetListRequest(const FAssetListQueryParams& InParams) + : Params(InParams) + { + } + FString BuildQueryString() const; }; diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h index 357ae92..1b6bda5 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h @@ -5,7 +5,6 @@ #include "CoreMinimal.h" #include "Api/Assets/Models/Asset.h" #include "Blueprint/UserWidget.h" -#include "Interfaces/IHttpRequest.h" #include "RpmAssetButtonWidget.generated.h" class UBorder; diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h b/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h index 1a8bd5f..ca8368c 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h @@ -3,9 +3,11 @@ #pragma once #include "CoreMinimal.h" +#include "Api/Assets/Models/AssetListResponse.h" #include "Blueprint/UserWidget.h" #include "RpmAssetPanel.generated.h" +class FAssetApi; struct FAsset; class URpmAssetButtonWidget; @@ -19,18 +21,20 @@ class RPMNEXTGEN_API URpmAssetPanel : public UUserWidget { GENERATED_BODY() public: + virtual void NativeConstruct() override; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Panel" ) - TSubclassOf AssetButtonBlueprint; + URpmAssetButtonWidget* AssetButtonBlueprint; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Panel" ) + UPROPERTY(meta = (BindWidget)) UPanelWidget* ButtonContainer; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Panel") - TSubclassOf SelectedAssetButton; + URpmAssetButtonWidget* SelectedAssetButton; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Button" ) FVector2D ImageSize; + UPROPERTY(BlueprintAssignable, Category = "Events" ) FOnAssetSelected OnAssetSelected; UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") @@ -40,13 +44,19 @@ class RPMNEXTGEN_API URpmAssetPanel : public UUserWidget void ClearAllButtons(); UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") - void UpdateSelectedButton(TSubclassOf AssetButton); + void UpdateSelectedButton(URpmAssetButtonWidget* AssetButton); UFUNCTION() void OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButtonWidget); - void CreateButton(const FAsset& AssetData); + UFUNCTION() + void OnAssetListResponse(const FAssetListResponse& AssetListResponse, bool bWasSuccessful); + UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") + void FetchAssets(const FString& AssetType); + + void CreateButton(const FAsset& AssetData); private: TArray> AssetButtons; + TSharedPtr AssetApi; }; diff --git a/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h b/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h index 0b757a8..1f5043b 100644 --- a/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h @@ -6,6 +6,7 @@ #include "Blueprint/UserWidget.h" #include "RpmBasicUISampleWidget.generated.h" +class FAssetApi; /** * */ @@ -13,5 +14,7 @@ UCLASS() class RPMNEXTGEN_API URpmBasicUISampleWidget : public UUserWidget { GENERATED_BODY() -public: +public: + + virtual void NativeConstruct() override; }; diff --git a/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h b/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h index e6e7b57..cbf5e4c 100644 --- a/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h @@ -7,6 +7,9 @@ #include "RpmCategoryPanelWidget.generated.h" class URpmCategoryButton; + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCategorySelected, const FString&, CategoryName); + /** * */ @@ -16,9 +19,20 @@ class RPMNEXTGEN_API URpmCategoryPanelWidget : public UUserWidget GENERATED_BODY() public: + virtual void NativeConstruct() override; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Panel") - TSubclassOf SelectedCategoryButton; + URpmCategoryButton* SelectedCategoryButton; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Panel") + TArray CategoryButtons; + + UPROPERTY(BlueprintAssignable, Category = "Events") + FOnCategorySelected OnCategorySelected; + UFUNCTION(BlueprintCallable, Category = "Category Panel") - void UpdateSelectedButton(TSubclassOf CategoryButton); + virtual void UpdateSelectedButton(URpmCategoryButton* CategoryButton); + + UFUNCTION() + virtual void OnCategoryButtonClicked(URpmCategoryButton* CategoryButton); }; From dc4d71407cd752fff8b9dd1ed6e9ec1120c38373 Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Fri, 16 Aug 2024 09:36:44 +0300 Subject: [PATCH 28/74] chore: remove images on log out --- .../RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp | 6 ++++++ Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h | 1 + 2 files changed, 7 insertions(+) diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 8672099..b166121 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -232,6 +232,11 @@ void SRpmDeveloperLoginWidget::Initialize() } SRpmDeveloperLoginWidget::~SRpmDeveloperLoginWidget() +{ + ClearLoadedCharacterModelImages(); +} + +void SRpmDeveloperLoginWidget::ClearLoadedCharacterModelImages() { for (const auto Texture : CharacterStyleTextures) { @@ -563,6 +568,7 @@ FReply SRpmDeveloperLoginWidget::OnLogoutClicked() } ComboBoxItems.Empty(); + ClearLoadedCharacterModelImages(); DevAuthTokenCache::ClearAuthData(); SetLoggedInState(false); return FReply::Handled(); diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h index e9c3f34..4be48e4 100644 --- a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h @@ -64,6 +64,7 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget static void SetImageFromTexture(UTexture2D* Texture, const TSharedPtr& ImageWidget); void Initialize(); void GetOrgList(); + void ClearLoadedCharacterModelImages(); void LoadBaseModelList(); void HandleLoginResponse(const FDeveloperLoginResponse& Response, bool bWasSuccessful); void HandleOrganizationListResponse(const FOrganizationListResponse& Response, bool bWasSuccessful); From 27c6039804013b52a6e86b8189b53e81f4cbb0c1 Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Fri, 16 Aug 2024 10:03:05 +0300 Subject: [PATCH 29/74] feat: wip fixes --- .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 72446 -> 96877 bytes .../Private/Api/Auth/ApiKeyAuthStrategy.cpp | 3 +- .../Private/Api/Common/WebApiWithAuth.cpp | 4 ++ .../Private/Samples/RpmAssetPanel.cpp | 11 +++--- .../Private/Samples/RpmCategoryButton.cpp | 7 ++-- .../Samples/RpmCategoryPanelWidget.cpp | 36 ++++++++++-------- .../RpmNextGen/Public/Samples/RpmAssetPanel.h | 7 ++-- .../Public/Samples/RpmCategoryButton.h | 5 +-- .../Public/Samples/RpmCategoryPanelWidget.h | 6 +-- 9 files changed, 44 insertions(+), 35 deletions(-) diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset index 1e553f8a1ad2fad9c2c4601dbed569cc29a4aaa3..ca8c05143a62cf49afafc031630952c3e643e36c 100644 GIT binary patch literal 96877 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 literal 72446 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 X5gqU!P$@JAxkGB%Jl}u( Request) { URpmDeveloperSettings *Settings = GetMutableDefault(); - Request->Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); + Request->Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); + UE_LOG(LogTemp, Warning, TEXT("Adding ApiKey to request, onauthcomplete bound: %d"), OnAuthComplete.IsBound()); OnAuthComplete.ExecuteIfBound(true); } diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp index cbc3ef5..40e86bd 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -32,6 +32,7 @@ void FWebApiWithAuth::OnAuthComplete(bool bWasSuccessful) { if(bWasSuccessful && ApiRequestData != nullptr) { + UE_LOG(LogTemp, Warning, TEXT("Auth complete, running request to %s"), *ApiRequestData->Url); DispatchRaw(*ApiRequestData); return; } @@ -62,8 +63,11 @@ void FWebApiWithAuth::DispatchRawWithAuth(FApiRequest& Data) if (AuthenticationStrategy == nullptr) { DispatchRaw(Data); + UE_LOG(LogTemp, Warning, TEXT("Auth Strategy is null, running request")); return; } + UE_LOG(LogTemp, Warning, TEXT("Auth Strategy is NOT null, running request")); + AuthenticationStrategy->AddAuthToRequest(this->ApiRequestData); } diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp index 4aae0b3..2806ce5 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp @@ -31,14 +31,14 @@ void URpmAssetPanel::OnAssetListResponse(const FAssetListResponse& AssetListResp { UE_LOG(LogTemp, Warning, TEXT("Asset Fetch Success.")); - LoadAssetsAndCreateButtons(AssetListResponse.Data); + CreateButtonsFromAssets(AssetListResponse.Data); return; } UE_LOG(LogTemp, Error, TEXT("Failed to fetch assets")); } -void URpmAssetPanel::LoadAssetsAndCreateButtons(TArray Assets) +void URpmAssetPanel::CreateButtonsFromAssets(TArray Assets) { for (auto Asset : Assets) { @@ -58,10 +58,9 @@ void URpmAssetPanel::ClearAllButtons() void URpmAssetPanel::UpdateSelectedButton(URpmAssetButtonWidget* AssetButton) { - if(SelectedAssetButton) + if(SelectedAssetButton && SelectedAssetButton != AssetButton) { SelectedAssetButton->SetSelected(false); - UE_LOG(LogTemp, Warning, TEXT("Setting old button to false")); } SelectedAssetButton = AssetButton; @@ -104,12 +103,12 @@ void URpmAssetPanel::OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButt OnAssetSelected.Broadcast(AssetButton->GetAssetData()); } -void URpmAssetPanel::FetchAssets(const FString& AssetType) +void URpmAssetPanel::LoadAssetsOfType(const FString& AssetType) { URpmDeveloperSettings *Settings = GetMutableDefault(); FAssetListQueryParams QueryParams; QueryParams.Type = AssetType; QueryParams.ApplicationId = Settings->ApplicationId; - const FAssetListRequest AssetListRequest = FAssetListRequest(QueryParams); + FAssetListRequest AssetListRequest = FAssetListRequest(QueryParams); AssetApi->ListAssetsAsync(AssetListRequest); } diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp index fe0e84c..b2a06d8 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp @@ -32,19 +32,18 @@ void URpmCategoryButton::InitializeButton(FString Category, UTexture2D* Image) } } -void URpmCategoryButton::SetSelected(bool bInIsSelected) +void URpmCategoryButton::SetSelected(bool bIsSelected) { - bIsSelected = bInIsSelected; if (CategoryButton) { - const FLinearColor NewColor = bInIsSelected ? SelectedColor : DefaultColor; + const FLinearColor NewColor = bIsSelected ? SelectedColor : DefaultColor; CategoryButton->SetBackgroundColor(NewColor); } } void URpmCategoryButton::HandleButtonClicked() { - SetSelected(!bIsSelected); + SetSelected(true); OnCategoryClicked.Broadcast(this); } diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp index 33f801b..eec762f 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp @@ -2,37 +2,43 @@ #include "Samples/RpmCategoryPanelWidget.h" +#include "Blueprint/WidgetTree.h" #include "Samples/RpmCategoryButton.h" -void URpmCategoryPanelWidget::OnCategoryButtonClicked(URpmCategoryButton* CategoryButton) +void URpmCategoryPanelWidget::NativeConstruct() { - UpdateSelectedButton(CategoryButton); - OnCategorySelected.Broadcast(CategoryButton->AssetCategoryName); + Super::NativeConstruct(); + InitializeCategoryButtons(); } -void URpmCategoryPanelWidget::NativeConstruct() +void URpmCategoryPanelWidget::InitializeCategoryButtons() { - Super::NativeConstruct(); - if(CategoryButtons.Num() > 0) + TArray Widgets; + WidgetTree->GetAllWidgets(Widgets); + + for (UWidget* Widget : Widgets) { - for(auto CategoryButton : CategoryButtons) + if (URpmCategoryButton* CategoryButton = Cast(Widget)) { - if(CategoryButton == nullptr) - { - continue; - } - URpmCategoryButton* Button = CreateWidget(this, CategoryButton->GetClass()); - Button->InitializeButton(Button->AssetCategoryName, Button->CategoryImageTexture); - Button->OnCategoryClicked.AddDynamic(this, &URpmCategoryPanelWidget::OnCategoryButtonClicked); + CategoryButton->InitializeButton(CategoryButton->AssetCategoryName, CategoryButton->CategoryImageTexture); + CategoryButton->OnCategoryClicked.AddDynamic(this, &URpmCategoryPanelWidget::OnCategoryButtonClicked); } } } void URpmCategoryPanelWidget::UpdateSelectedButton(URpmCategoryButton* CategoryButton) { - if(SelectedCategoryButton) + if(SelectedCategoryButton && SelectedCategoryButton != CategoryButton) { SelectedCategoryButton->SetSelected(false); + } SelectedCategoryButton = CategoryButton; } + +void URpmCategoryPanelWidget::OnCategoryButtonClicked(URpmCategoryButton* CategoryButton) +{ + UpdateSelectedButton(CategoryButton); + OnCategorySelected.Broadcast(CategoryButton->AssetCategoryName); +} + diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h b/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h index ca8368c..4edb63c 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h @@ -22,8 +22,9 @@ class RPMNEXTGEN_API URpmAssetPanel : public UUserWidget GENERATED_BODY() public: virtual void NativeConstruct() override; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Panel" ) - URpmAssetButtonWidget* AssetButtonBlueprint; + TSubclassOf AssetButtonBlueprint; UPROPERTY(meta = (BindWidget)) UPanelWidget* ButtonContainer; @@ -38,7 +39,7 @@ class RPMNEXTGEN_API URpmAssetPanel : public UUserWidget FOnAssetSelected OnAssetSelected; UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") - void LoadAssetsAndCreateButtons(TArray Assets); + void CreateButtonsFromAssets(TArray Assets); UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") void ClearAllButtons(); @@ -53,7 +54,7 @@ class RPMNEXTGEN_API URpmAssetPanel : public UUserWidget void OnAssetListResponse(const FAssetListResponse& AssetListResponse, bool bWasSuccessful); UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") - void FetchAssets(const FString& AssetType); + void LoadAssetsOfType(const FString& AssetType); void CreateButton(const FAsset& AssetData); private: diff --git a/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h b/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h index 43cdf6a..0348b35 100644 --- a/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h +++ b/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h @@ -42,7 +42,7 @@ class RPMNEXTGEN_API URpmCategoryButton : public UUserWidget virtual void InitializeButton(FString Category, UTexture2D* Image); UFUNCTION(BlueprintCallable, Category = "Category Button") - virtual void SetSelected(bool bInIsSelected); + virtual void SetSelected(bool bIsSelected); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Button" ) FString AssetCategoryName; @@ -56,7 +56,6 @@ class RPMNEXTGEN_API URpmCategoryButton : public UUserWidget private: UFUNCTION() virtual void HandleButtonClicked(); - - bool bIsSelected; + FLinearColor DefaultColor; }; diff --git a/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h b/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h index cbf5e4c..4d48ab8 100644 --- a/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h @@ -24,9 +24,6 @@ class RPMNEXTGEN_API URpmCategoryPanelWidget : public UUserWidget UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Panel") URpmCategoryButton* SelectedCategoryButton; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Panel") - TArray CategoryButtons; - UPROPERTY(BlueprintAssignable, Category = "Events") FOnCategorySelected OnCategorySelected; @@ -35,4 +32,7 @@ class RPMNEXTGEN_API URpmCategoryPanelWidget : public UUserWidget UFUNCTION() virtual void OnCategoryButtonClicked(URpmCategoryButton* CategoryButton); + +private: + void InitializeCategoryButtons(); }; From e895c2838db9a047d7527b29d0e984ae4a615c57 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 16 Aug 2024 15:00:59 +0300 Subject: [PATCH 30/74] feat: first version of basic UI Sample --- .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 96877 -> 125777 bytes .../Blueprints/WBP_RpmAssetButton.uasset | Bin 27371 -> 27389 bytes .../Blueprints/WBP_RpmAssetCard.uasset | Bin 31131 -> 32076 bytes .../Blueprints/WBP_RpmAssetPanel.uasset | Bin 24397 -> 25853 bytes .../Blueprints/WBP_RpmCategoryPanel.uasset | Bin 42951 -> 42854 bytes .../Blueprints/WPB_RpmCategoryButton.uasset | Bin 27537 -> 28727 bytes Content/Samples/BasicUI/RpmBasicUILevel.umap | Bin 70708 -> 72570 bytes .../Private/Samples/RpmAssetButtonWidget.cpp | 3 +- .../Private/Samples/RpmAssetCard.cpp | 7 ++- .../Private/Samples/RpmAssetPanel.cpp | 40 +++++++++++------- .../Private/Samples/RpmCategoryButton.cpp | 1 - .../RpmCharacterCustomizationWidget.cpp | 7 --- .../Public/Api/Auth/ApiKeyAuthStrategy.h | 1 - .../Public/Samples/RpmAssetButtonWidget.h | 2 + .../Public/Samples/RpmAssetCardWidget.h | 3 ++ .../RpmNextGen/Public/Samples/RpmAssetPanel.h | 11 +++-- 16 files changed, 45 insertions(+), 30 deletions(-) diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset index ca8c05143a62cf49afafc031630952c3e643e36c..af1eb7680e9106d8209747cf58ab57582cb4afb1 100644 GIT binary patch literal 125777 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 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset index dafa237421b4d4b23bd32562ecba04e3cdd93582..700d7759a13316b2ccba078d83aa405b9b182356 100644 GIT binary patch delta 7613 zcmeHLeQZ=!7Qe4W+QF_&+v#*>_$pG%P)ZdFD+0C@+78pwZ`;xqv_t!7r`8#mPP?)S z!-u;Hx`?@c;75_jZj2v6%`AT{%SWOin}8pyWQ`gX*<=%17Lr9>ROp`b-aU`k_RTOQ z)XgS)k~inxdw%zvbI(2R-20~glyv!&wEIxXp(4g^VvJSzeKIh{9)Ifnh3@JU#vITo zyoIr2(2$`0+9bx_fk)0{Y{$O+!8w;ZD)v0X^A^1|`#{F;SA3RasVRN&cnP2K~O;;KnYQz47FcFh3LjLb3@2Mc!`D+Ag_`y)mo0IlO7y+BkC}46pF_ z$^jV7p85H1{?+(SMafF9Pxb^<6PD%OC0tJUWMTrKEBt{6JY8&aU-3zPS7I%HDRJQx z)KV3YySqWl-o9@JFj4^-Q1yR#EjN5V@pg%4Pv}W$kaQ_6{gE8RF0q&7&;=p1*00Y+q zs<`eLaSz9c+Y-x)(ZQ`TD*SDXxSawQinhmSxIG4iX92e(3T~T_A@dH!sq3fY(gSJC z$U!~s{-Fw$9?jL`x(sn5{(9g8hCp>tkMqTd^BdsEnMY{7(nD&nWh=kWun7QQ*fY&C+^ zx-`gHfZ+Rsr5CmAQI)W>ZXG1afx3-g+e!s2)B0;M6SW)^ZAY1=CD&rMZqPx(qWys$ zl4Qs7Zv@-Mzb?JNIZ_ zmRil$?dm8~;(4>$$!g;3t@YL|&`XC0XIH|5lqD;(Z%NNI6!)lN+>Tx8W-FEq@}6S- zt{#%)_ys_WV5`?@ZmzSLt-WX*Xf8ZUu>o{Zg&ekJS|3$M7%pI^vJ}SVx5Fy3`7Zlp zo^Ll>9}^Hf3kiPM%ac^70=C*+;)?KDGVpw=P)*Lg!w+@ltj^0`@ud4E=+bdb-PnLoY}zD?dN3U8QsW}1DS zsBq1K@5U+zl*$WJZ1xpuhmjP^HBuc)OQ)(G<*NsqSVWO#s*EwGXt)IbrQkP69UWpf zNZsUU-qnnHv9xG$jB7E(?>?j@5JN|3O95l*^TPAb=QpS#_4I}|Mbs_EmD3j$){6lW zh^qu+@bRk6B1=~%PZ7;V!OeWVH-VpUCErHgqTv5Ip?#ZSb^;DC?7H-q~k%NPu8(>Omg!L zZi@?DK^oyn0u1UsxkrH+0O8U&Si-A2(^rxXMz?qq%$WcWy}!e^hp~A=D-}6H`mYrezPD^z>|QLoelPgeGK(p)*=%L$R~KI*`UM!O zP`>EV9#F{HMcAIZg>aeRl-{gD4A4d=i#~0G=t5z>RW!2%Cl#4$e-2}}2??e!(nuYc zQb=arRb`hN`O%df_9Asi+fteZ2~h5oT)eYg=XT0_ozOI-67hA3Kjps})JE zCV@!6oYUoP+*Fw<-Nkb&?=WF~KyqQ6Kw6kGgYU0&+oQ&_^7`UwQ%z|vjuuZo&}#QA z6jE((K7@g51op7VY+iTL4 z`w^W6>Nx)D@%M`1;b&3~!QoU==sv+41sDGuGL`@X6`r8CSJwV3BcJoMDvkfdv;F3=

!93yhn$$QEAJY0D*3%Iy}t#TN-LJNl_t Yr#;)5Gi%5c%o5zkgke*TZO`8OAL!)n6#xJL delta 7657 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 literal 31131 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 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetPanel.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetPanel.uasset index 5f245122bd71cf62e642ef56199457ec58c21053..48df5a7a89e9772eef16925152559db8927e40af 100644 GIT binary patch delta 5262 zcmcIndu)_N5Z|xR(n67Id$zsf(W4v%Dj-Ef5TxaLlpd6}S{~PyzI&8|(n2k$sFa5o zBY&W5ObmYzF^vgEh^7*Q8bLG>2p||^{KFVYL=<00d_+ZYe*4W{zuv=MqHOZr%T4pC^O}Y?ch$h`8#3xQ64B}6Y z7UB$j{#+sE*pEGb^~#a5{rSf}I#Ds|())8WuQl7ommY1|lDEw{b1(4Fvs+deRbx-l zjNe;Da06n;t1|+7J+1pZm(w*E`fmC>%}_--f2PKvi&S zKunmHa}-!#hd0pP-LyPNSntk)O0@no;qDNm-3F>&7$Zetv{?+Ev31di;7ujDELABq4*$2}Y;t|Ar= z>(`-%;m2bE!*JW;#O)dcr{WL8je`Io2JVSCaNiJa=fB_{VHuGg(HtL8=yZ&B2rUXj z+c_9+jj~JY9}L(Qr-N(b#09N56@MDghtRr`bygtr+8KS4ajG{&!D)SuvFuyIJs1Vo zqmAhO!rpa80cx-Hfx1RG?YuRD8rR4=z9O9Ft++pFoFF+0Hi~|`6ek1<(oyF3c;vz* zGv)bocP45JiZsL%hMMxC;sN3W7FQ23#Vv~mh%t)Un-fj(+e=t%m*p9@%&q3AfEF>r z>=abW&pr9_i?n3&!7ks-NOhuaqdIu#gEpPn1+^h}pWE^LOT$yq^jFhh@qRhT~HF zmHCm)vLHQQ5?mh1Om&W;;YJpvh@ql}`5`TOi3$e4H4O39u(}W|@4Ce>j>mfX&;UQE zI3eDM4#&1cgZo=UPSj^F@4@e8>zmxT+MBHXI^m=^C_I6l|K9XC^rqQ#qSg8Zy> zUP>YNs-(-e)=|Vg?bX0FxI8waSQdEZ8H;2=u|wu8Nv8hAGXc-D)l*7tUMrdqFWNT*#ffnTB@v0u8u$@P7g>F@v^00QUIf!CbOM<*2@YJV+*sx|?oyKum6fL~VZ$OXLdwRO5bm(Vt~_<$izuNUJM){F|`x0(q%A%W1wzNZSmk zrMB24!Yj8{kI9WF9jKO8?MjP3wiebF$`Rc3+1f<75aF;YSFSs?4{EI_$d!7&^w&%` zlI5#)S+fm6e|}Y~Vv1U?8e9(jAskFU3Vpx+Q~ynWJwoiCWV#{5YGy(q=6)zR@HA#{ zaPL`zqm-A$uN=XRsT}tmigLzu9qo`)xKn467%jY_*HH?L8&O@Pgrd4=IPHZ)uBmk{ zuzKn@#lp>0Um}yaFQzb4&9q9sQ#(4Zk$Vvy;dU?8LV2@xOlCwW#^qYe3E^8kd7QrG z)GJ~l&uw5jNA6vnm8aLHj@cX*>A@r1AB&uKsC1+eE!Ee<10;3Hv2~*yWnuGe+(*E& zD^hlpeLUmP^CzxUZ{K$zxbws^mQH0Fiv78>Qwwz@r?D_195(q%-NZEQi?(Dw82+fU zH@=J3Ey7=Cc>&Y?3h`)o-m;lsr{L N(+668cUS&_e*gmY)oK6$ delta 4241 zcmcImeQZ-z6u)n5lg$mf+I4LiTi2CYDjRO#2Mpa^_rV6E8*EOtZsV0v$Fy{Fb`ua8 z(?pF~kmbfi6I?Li4^4<4X^5DR5rY4qQTsoCC?O&$!oZIZmEbw=om<~%A!y_#?eE-k ze)rt-&OPtkcm1^T<&1LRxcN*CV;PJw|4<|Zz}WcIS1UJ|K$y;B%mKJRm$5z@V+zP- zJLur|UNK|umyed`%nr32spwd9DPDf=vsKpFt)`Nuqy1xLdmUA8ARqmVi&mw5vCYX? z^A?yI#tj&_e{bxB@?Bc&Sh|vC$pL$Fq&^gm20I3KgjoGcl^wBbnU*wBlyz3KcGiXm z2O?V<2g6}@u<~k=$j|PM?aMx%X3mDS?foM|;jnLLC-Yyp%EYGJ!{aJ#IY4*?whfca z2@zD1jl3b!aY6)@WXYQaf5W8Xgb1oGk253jSzXCEA%aS>rcs}{2Lu^OG(s~MxNdg*qi`mFtYHl2)EzH-}CbPe+FVC z1yODy&kN7)=@dK}w5*e>z}tNro~)C{F910X(WE*Vl);u8v<4Hw1p;I89vIXQp!XOEifb!*B z9?(4QwL9@nVlNN{)AYKQFvcbWTCS#Tg=N3N(f(mL-aSOm0(_NZYoC}hd&RWP=^_oo zDv*_u#nM?P8Q$rWi7ETl;1%PRbUY17|0O{er+p@I-ccWvsYb*guoQ6lkKpcm_~9H6 z7wxFWIc5$c!YdK$4eR(k{4znts}3I<&j@qS7dy4+RAzb++|44M>k>y?w#-ygyXFy^C2jgUZ2B-6s|hio+K@iD_^7VL1<$07QbI{9p*eGj$@SG) zYiQak;_J8DAETKJYwknD>rDz5XX~pKpE%oK7hku`hxwG*l7VqG!qKbZSc6B|gx0L3 z-#;GuO-qeoc*R*U$w(XFjT%aplPIMz1X??zGz5>Y@>Y96Oq6=C8uGlGxZ#e4&BCh_fvm+{GlPx?^@;VnOHTsWi<|oyZ6* zQ#Oc+0Je;E0dFoRHwaSy_D%W+MLbYoE|SdUBy^+NQ6|-!NvJ2R6-rN`E2({8Gvq`; z_^-}~dkVct&M_Grz0znQtQFt%RQMLrOkTtmNy8T78;wSXB6UNNXmBtR8o7m=$=-^J zf9D2z)qU2aK=B&M7Vq{Jh`^S@d@ch4>>#;RnEGtv9-2KtC_~2w)dt=v{$O-)ER?w2 zk~UiioCckqe23u8EARBAmXv|l){|5&T=fKG!`JRJH~9<@K17I%aGcYD$xg0an35!w z9}uY!-v;~Qo0iDd=?AORgHPQs7K%jIjs%Cdv*w$~@}}qlvZrnxSsx%w^jXICo#3Qg zMwpZ{Oi7M*Kr_MYJQ=LqLAy{_Prg#P!tK56|FC%CT<`TY_e-bfQ+{eBIYt*5;Mi_5 z((x1^>im_q!|X4Roxs9@pA>R%DFEVUMib5PMY9NJzo`nfIOL|0@{~*E2a9p($(Oy;e*+E7WoG~Y diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset index 34e93ebf18ddec92311760b336ad25aac11969dd..d82c902888f9f2605c5e562666cb8b6bac312d1d 100644 GIT binary patch delta 9472 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 delta 9622 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 Q7-W-Q@#eUl-JI3-UxZP-*#H0l diff --git a/Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset b/Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset index fc086588e10ad84fff6c8372dfaf9749a191f2e8..484bdbdf3d1f23351f5ad5e63a99cf612c40c350 100644 GIT binary patch literal 28727 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 S-*_m5)U?D}e=GgT!2bpJpe#xN delta 7197 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 V;D0jsBmbo;j|lA_IlJ$Q{|94xQM>>E literal 70708 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%0Ayg()4^_UXY7?#Ua&1Y% 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 diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp index 2249cf4..26c67c2 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp @@ -4,6 +4,7 @@ #include "RpmImageLoader.h" #include "Components/Button.h" #include "Components/Image.h" +#include "Components/SizeBox.h" void URpmAssetButtonWidget::NativeConstruct() { @@ -16,7 +17,7 @@ void URpmAssetButtonWidget::NativeConstruct() } void URpmAssetButtonWidget::InitializeButton(const FAsset& InAssetData, const FVector2D& InImageSize) -{ +{ AssetData = InAssetData; if (AssetImage) diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp index 3e37ede..1248b19 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp @@ -8,14 +8,19 @@ void URpmAssetCardWidget::NativeConstruct() { Super::NativeConstruct(); - + this->SetVisibility(ESlateVisibility::Hidden); } void URpmAssetCardWidget::InitializeCard(const FAsset& Asset) { + this->SetVisibility(ESlateVisibility::Visible); AssetData = Asset; AssetCategoryText->SetText(FText::FromString(AssetData.Type)); AssetNameText->SetText(FText::FromString(AssetData.Name)); + + AssetNameText->SetVisibility( AssetData.Name.IsEmpty() ? ESlateVisibility::Hidden : ESlateVisibility::Visible ); + + AssetIdText->SetText(FText::FromString(AssetData.Id)); LoadImage(AssetData.IconUrl); } diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp index 2806ce5..7745aee 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp @@ -7,6 +7,7 @@ #include "Api/Assets/Models/AssetListRequest.h" #include "Api/Auth/ApiKeyAuthStrategy.h" #include "Components/PanelWidget.h" +#include "Components/SizeBox.h" #include "Samples/RpmAssetButtonWidget.h" #include "Settings/RpmDeveloperSettings.h" @@ -23,14 +24,15 @@ void URpmAssetPanel::NativeConstruct() } AssetApi->OnListAssetsResponse.BindUObject(this, &URpmAssetPanel::OnAssetListResponse); + + ButtonSize = FVector2D(200, 200); + ImageSize = FVector2D(200, 200); } void URpmAssetPanel::OnAssetListResponse(const FAssetListResponse& AssetListResponse, bool bWasSuccessful) { if(bWasSuccessful && AssetListResponse.Data.Num() > 0) { - UE_LOG(LogTemp, Warning, TEXT("Asset Fetch Success.")); - CreateButtonsFromAssets(AssetListResponse.Data); return; @@ -42,9 +44,10 @@ void URpmAssetPanel::CreateButtonsFromAssets(TArray Assets) { for (auto Asset : Assets) { - UE_LOG(LogTemp, Warning, TEXT("Creating button.")); CreateButton(Asset); + return; } + UE_LOG(LogTemp, Warning, TEXT("No assets found") ); } void URpmAssetPanel::ClearAllButtons() @@ -70,33 +73,40 @@ void URpmAssetPanel::CreateButton(const FAsset& AssetData) { if (AssetButtonBlueprint) { - // Get the current world context - UWorld* World = GetWorld(); // or whatever provides your current world context + UWorld* World = GetWorld(); if (World) { - // Create the widget instance - URpmAssetButtonWidget* AssetButtonInstance = CreateWidget(World, AssetButtonBlueprint->GetClass()); + URpmAssetButtonWidget* AssetButtonInstance = CreateWidget(World, AssetButtonBlueprint); if (AssetButtonInstance) { - // Do something with the instance, e.g., add it to a parent widget or initialize it - AssetButtonInstance->InitializeButton(AssetData, ImageSize); // Pass any required parameters here - - // If you have a UMG parent widget: - if(ButtonContainer) + USizeBox* ButtonSizeBox = NewObject(this); + if (ButtonSizeBox && ButtonContainer) { - ButtonContainer->AddChild(AssetButtonInstance); + ButtonSizeBox->SetWidthOverride(ButtonSize.X); + ButtonSizeBox->SetHeightOverride(ButtonSize.Y); + + ButtonSizeBox->AddChild(AssetButtonInstance); + ButtonContainer->AddChild(ButtonSizeBox); } - UE_LOG(LogTemp, Warning, TEXT("Button created and added.")); - AssetButtons.Add(AssetButtonInstance->GetClass()); + + AssetButtonInstance->InitializeButton(AssetData, ImageSize); + + AssetButtons.Add(AssetButtonBlueprint); + AssetButtonInstance->OnAssetButtonClicked.AddDynamic(this, &URpmAssetPanel::OnAssetButtonClicked); } } } + else + { + UE_LOG(LogTemp, Error, TEXT("AssetButtonBlueprint is not set!")); + } } + void URpmAssetPanel::OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton) { UpdateSelectedButton(const_cast(AssetButton)); diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp index b2a06d8..32e375a 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp @@ -57,7 +57,6 @@ void URpmCategoryButton::PostEditChangeProperty(FPropertyChangedEvent& PropertyC if (PropertyName == GET_MEMBER_NAME_CHECKED(URpmCategoryButton, CategoryImageTexture)) { - // Apply the texture to the CategoryImage widget if (CategoryImage && CategoryImageTexture) { CategoryImage->SetBrushFromTexture(CategoryImageTexture); diff --git a/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp index cd69baa..119e089 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp @@ -57,24 +57,18 @@ void URpmCharacterCustomizationWidget::InitializeCustomizationOptions() OnAssetsFetched(FAssetListResponse(), false); // Call with empty response on failure }); HttpRequest->ProcessRequest(); - - UE_LOG(LogTemp, Warning, TEXT("Made request")); } void URpmCharacterCustomizationWidget::OnAssetsFetched(const FAssetListResponse& AssetListResponse, bool bWasSuccessful) { - UE_LOG(LogTemp, Warning, TEXT("OnAssetsFetched called. Success: %s"), bWasSuccessful ? TEXT("true") : TEXT("false")); - if (AssetListResponse.Data.Num() > 0) { - UE_LOG(LogTemp, Warning, TEXT("Number of assets fetched: %d"), AssetListResponse.Data.Num()); AssetDataArray = AssetListResponse.Data; } else { UE_LOG(LogTemp, Warning, TEXT("No assets fetched")); } - // Broadcast the delegate to notify Blueprints OnAssetsFetchedDelegate.Broadcast(bWasSuccessful, AssetDataArray); } @@ -154,6 +148,5 @@ void URpmCharacterCustomizationWidget::OnAssetButtonClicked(const URpmAssetButto { const FAsset& AssetData = AssetButton->GetAssetData(); const FString GlbUrl = AssetData.GlbUrl; - UE_LOG(LogTemp, Log, TEXT("Asset Button Clicked: %s"), *GlbUrl); OnAssetButtonSelected.Broadcast(AssetData); } diff --git a/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h b/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h index 047a325..273693d 100644 --- a/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h +++ b/Source/RpmNextGen/Public/Api/Auth/ApiKeyAuthStrategy.h @@ -11,5 +11,4 @@ class RPMNEXTGEN_API FApiKeyAuthStrategy : public IAuthenticationStrategy virtual void AddAuthToRequest(TSharedPtr Request) override; virtual void OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) override; virtual void TryRefresh(TSharedPtr Request) override; - FOnAuthComplete OnAuthComplete; }; diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h index 1b6bda5..715f93e 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetButtonWidget.h @@ -7,6 +7,7 @@ #include "Blueprint/UserWidget.h" #include "RpmAssetButtonWidget.generated.h" +class USizeBox; class UBorder; class UImage; class UButton; @@ -22,6 +23,7 @@ class RPMNEXTGEN_API URpmAssetButtonWidget : public UUserWidget GENERATED_BODY() public: + UPROPERTY(meta = (BindWidget)) UButton* AssetButton; diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h index 6289f16..af15698 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h @@ -17,7 +17,10 @@ class RPMNEXTGEN_API URpmAssetCardWidget : public UUserWidget public: virtual void NativeConstruct() override; + UFUNCTION(BlueprintCallable, Category = "Asset Card") virtual void InitializeCard(const FAsset& Asset); + + UFUNCTION(BlueprintCallable, Category = "Asset Card") void LoadImage(const FString& URL); UPROPERTY(meta = (BindWidget)) diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h b/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h index 4edb63c..4369ca2 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h @@ -32,19 +32,22 @@ class RPMNEXTGEN_API URpmAssetPanel : public UUserWidget UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Panel") URpmAssetButtonWidget* SelectedAssetButton; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Button" ) + FVector2D ButtonSize; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Asset Button" ) FVector2D ImageSize; UPROPERTY(BlueprintAssignable, Category = "Events" ) FOnAssetSelected OnAssetSelected; - UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") + UFUNCTION(BlueprintCallable, Category = "Asset Panel") void CreateButtonsFromAssets(TArray Assets); - UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") + UFUNCTION(BlueprintCallable, Category = "Asset Panel") void ClearAllButtons(); - UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") + UFUNCTION(BlueprintCallable, Category = "Asset Panel") void UpdateSelectedButton(URpmAssetButtonWidget* AssetButton); UFUNCTION() @@ -53,7 +56,7 @@ class RPMNEXTGEN_API URpmAssetPanel : public UUserWidget UFUNCTION() void OnAssetListResponse(const FAssetListResponse& AssetListResponse, bool bWasSuccessful); - UFUNCTION(BlueprintCallable, Category = "Ready Player Me|Asset Panel") + UFUNCTION(BlueprintCallable, Category = "Asset Panel") void LoadAssetsOfType(const FString& AssetType); void CreateButton(const FAsset& AssetData); From b081c3d449b69a25e54e408bac5123c04e6dae50 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 16 Aug 2024 15:08:13 +0300 Subject: [PATCH 31/74] chore: cleanup --- Content/Samples/BasicUI/RpmBasicUILevel.umap | Bin 72570 -> 71753 bytes .../Private/Api/Assets/AssetApi.cpp | 7 +- .../Private/Api/Auth/ApiKeyAuthStrategy.cpp | 2 - .../Private/Api/Common/WebApiWithAuth.cpp | 5 - .../Samples/RpmBasicUISampleWidget.cpp | 9 -- .../RpmCharacterCustomizationWidget.cpp | 152 ------------------ .../Public/Samples/RpmBasicUISampleWidget.h | 20 --- .../Samples/RpmCharacterCustomizationWidget.h | 67 -------- 8 files changed, 2 insertions(+), 260 deletions(-) delete mode 100644 Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp delete mode 100644 Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp delete mode 100644 Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h delete mode 100644 Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h diff --git a/Content/Samples/BasicUI/RpmBasicUILevel.umap b/Content/Samples/BasicUI/RpmBasicUILevel.umap index e6707d715f2159c2fd7d98672b4e1b3acd8cfb50..e71989c46e02d2baf1cb1162af54619c5a86fd17 100644 GIT binary patch delta 3255 zcmbtU3s6+o89wJM)U7FIi_60V5ZGnkP~;_x0t!N+lURHp*mivYA`$|)D2UPo zA^Yo4lmrNqPTM4gWX0ARg^96_kJK@piJeS|Ni!K=9Uo1rW0NWU&$+wHt|6Jpba(E( z-~ImgD=z6OdD*>l-N}c?NkN5ndMNjFq~QiI*zDPpoFY&wq+P{vh1vH7(TN-2|Wvy za3#yaqDG)@nGM!tOYFpmaCjbJ%xiEr+s@v6O$bMFB=*to1bj4CVsqaRuo+?bVYvJE zG#DKYfWd_-xQbNtTYgk|p2Sv<3WITk&%7;Q(jtkSf7?&74=LLbKT44=v5q6cpe8?J z_JIJo6~-~I)|z|?Zs#j!zv~vAJLW42R-l0rFYOOx%g&QtF;CZJli1Psz0{)x2KL~r z&#MH31uAy*f|ue(_X{Kzb?G5W4ULOcY|CXYJ-*n$iY7(s8A~LV`YEhiVrTX*@L>Z- zp$g&()vWQFP;eAVEb_X5HxXWy;mRV3X>SO2Wsw>#7pYjb{F+)UvD#bSP*KGZk40jA zL%1q@TX?}ceR_KpH#9%yjq zqM^M^V%Bi}>bh9AU%@sHxvEzb2Sjd~oSIT#{`~)M5+>NgsETYRm1GW1996I(JZ%}mSzj|`Sw_vBeINY{1MINTv(vH9y_TaytX`_*dvXVbdiXp ze=e8i3m@G7o6nU|o3m*NEf7>qQz%{RM#_*?q?FW?YO(?Elq~Uar+EHXv{1M^`+P1f z5*E5<11%Qlh2{lxsX&v>DRh}YHBCyew#=a=;%#$F3N01skF`Y5<aH$0w z*{Y%C@a%3EHt|XNl-rLI*6q-M9WMZr{i(3JkD3**wxR$w?p#P$3-031V!B44oL#YWtw3G7 zR>1mQ8VGEU#zSvnYciC#heCWit{rAsC77%2p|l#b9m#>Y7-<6{9i(=oVB=SJ=y=mR z65(b?EVT=dsjavKyXVu5SnFlF3Atog)0qppP7R#tOoL&3M(wGhHO{7F2$F?$(3l)hw!oPk(yyljDsFV2CU9s@NxyA$D1cW{!Ca8>SDN332Wi?3I zh;}AWt#ex{!^8npFACYUg*hqE(@Jh@{}b|%Pv!Hgeb_COq@_6Gy)enO;R zA||)>cc)+?X5SPn?i8$;!X<^7Xkkvjp6a1@pAjDESI?a}0l8{3@igkYLz;>H;gIp* z==+c#89jQerS`x^#M%9cyt5f*;%VIUl+2>7zJ0KGXX%-e1^cP(wI*3+NXz2w?es7# z#Nd1}UST0dJ`D>VW3|qCkTh^E(c&HFbOsi0R-Q>%2p(?x3PP;jO#CHAPdoXH92H_c ztnO84##vcKp6Nu!eD>*hoNmR#s+zRAwdF1K*Y=ljKRm97xqa>s@JWBzFRY?ek6hm8nNrYU;rxS|Asn*PQms1{XZXkF)rYkQQ)wFXiu=UD z$HU~9T+gp?xN|TpW&>GAHi-jxT1#%YC->MxanO1wH0hfk!c61mQOtY+5Ws)C6U1|i z^Kn_~y`4xzR2!k(Q znIGiY7_pr=m@qI7Mg+-4d)hpKY{Ou5&^JaD<-fhW!7~j=oyx0u__*Yp=Qx@EMmej; zAq8?^aPG4TV1!=}jm=6+(Wa#sOs>k|yij;>B?LbI{aXmhSh6|6Ki9etpzjXq1CoOQt77Q0&SGorN@kz@60LOc!9smFU delta 3919 zcmb_fdr(y86~Eub#|oCL$g9D}1s4#3g#~umg&^<6RRkmkjV30`?p1bOc3~fg@>md2 z8zV+eY*a+WWHRYErs|G1j@8Oo+p$e5)i%b~#AX~HoisJnw5d%x_I%$y0L3wXl%2iz zywC6a&UY_x@#uvAoSbkpi}3CNP?0*RYHQK0PiBSaItTukbBmMn@IGmXY>FY==%0u9 z;~wy2@%M6Q-p)KNtj&}0pHk7xPdbFRYN(ta&8KTRBx8G&Xw+m(bhliQ?Op%PAa)gO3 zDC&L+!`Brszc>Q!pc;A9r)v16$5_xWie-G|aV930$oRqIf%vyjeeMKP|6ZcuH=JNW zw$d5FZ%p(~oqRG7Y%2|id!>?Kwa<0>^k`RD5GF|Q`}HJm=KXWbyc*B54tSO%`bNKqhhe0?H{nT^O z?B3+lNv~MxnYr>%1T=06cTelw9bI87Cr^ zYvoF?Q!VGjPbJ>17~RgrK9bA(azp$zV4q<+JZKf>`1>yBin%42SIlaeIU~w7+(L}c zf;Wuu@M%LhQ9_mbhY-^E8c{J@n^iK!=p}E z$ziqw7;(iOH#ygf1kTpxLbhWQOmikcze@tSHYq%CL_;U)m$pbC-&sWFv&_`?6u9Di zmgF-nZF3kb+Z+m+t`btfEd8!@QplwHt~gS}u%%YX*%+H8qxyTAYDpk{UGpTPw3aMbymmB1yl1W$(bSztA#+R6~b36`pAg zCeJf(erp0*3g%57IIwXZdBImn0*!46qz1KAxX@Mvj`jpdZP$|J%ui=I++IyyWZE2e z3R!{mlgUb2JH)^>;HMt(zr?zJ>~5 zqGy&poi(HuzUnL`1{i8g1;fTjVq_kQP-M1mHbz2zmmIfZ?NZ=I4t6Of(hZoPpeuD^ zDN1IR@oiTeS<9sSRtYTHG>cep)~j%=+j|;p-9^O8{8zg(05&H;+GZnZU}r48dj@HQ z&o;Z@CtJK{sdLL*;=t%sxVohX3|r+;wsi(^(P+=kt-qNNq~y{ez3=zAN>0JWaVhxC z2`Y0fcBm7Boi^tOjI#sV=<}_rMHS7qY9+2 zXCodJ3G`1=aTG7jd`f=^-Ih42y9UleJYfvM=e^;O|C$u?c83uyM}seg zU17=D$n5vQkd3dKu^f0&DCwRcc2^c-7W?CT$TMDtda`q~8LwlxQFG~!aH9@v*}p$E zGVI@ydK`r60OuoHQgcd%P|YFNxFt1idRE`fBz0iRBaEm6xcvLk;EsoWTo$1TU>WVf zx!`7RSb%A|!!$Q#I)`h)tA=6p%ifdfyPr~u*ZQm^Bd`Leo_mskG8-A4n6%*lYJoIcl}X#l=tmP!|R9a`rV$G2=B2>_dRw# z5FpN`UIFP!`7dJ4be(L47EGiul&qc01J6<=D9GCY0k%IRzh_@F-)V)joR$Cm;B?wdRcn{gAy?}ea059$xubL9h?vI-N^bN(QDFQFokyjF($N!QTdEBU#y%N}V z@X_0@VUh}t9yl>sAy=y83Z=(#usAX$3IBdvs<*EZoxx@Lda=QxUoSlG)H_WEhfrB- z77b3JT3jvKMXN!C<0l7`5-?$z&2BLY#Wt%`Yf**@+`q`kn6{D-Lrf3&*R-@P) ztizl&qCFS|R0We&aMp@!ponWyU z^cG)+L(p4|w1QR8H|b3leXT_l>|(vmDGD~L=)gbw&%w^iC7?U2f%F0iT(}qxAAgWE zEgf-Hi%yr_D%9vLE-@HFFUG;;KZn8Hj~9AYo%)1Kq5mlamo|$A=(%i!{?o2e7X7b` zu>R-0i874wvG;-lQ7FUD(yxbK0v}!oW0JprVdjn4OUlut!PhS za=pz?XAqpW2GmUs9G=x)XR=s?T2bzs%%eNoK|%spISz)dt%hr-rBHHC{|Mr#gS}ab z5yU>QML2$g-DGtN<)&J@-oE~E_ydE3kxESTZIUkDyCyGf`ir~aXx;=M=kw&r7^buO ziE!EktcfA8*PpRQ6ZJ5XK*W2>{0rrbW At^fc4 diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp index 590ffd0..54e5bf3 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp @@ -17,7 +17,6 @@ void FAssetApi::ListAssetsAsync(const FAssetListRequest& Request) FString QueryString = Request.BuildQueryString(); const FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets%s"), *ApiBaseUrl, *QueryString); - UE_LOG(LogTemp, Warning, TEXT("Requesting assets from %s"), *Url); FApiRequest ApiRequest = FApiRequest(); ApiRequest.Url = Url; ApiRequest.Method = GET; @@ -32,16 +31,14 @@ void FAssetApi::HandleListAssetResponse(FString Response, bool bWasSuccessful) FAssetListResponse AssetListResponse = FAssetListResponse(); if(FJsonObjectConverter::JsonObjectStringToUStruct(Response, &AssetListResponse, 0, 0)) { - UE_LOG(LogTemp, Warning, TEXT("Assets received: %d"), AssetListResponse.Data.Num()); - OnListAssetsResponse.ExecuteIfBound(AssetListResponse, true); return; } - UE_LOG(LogTemp, Warning, TEXT("Assets received: %d. But can't convert"), AssetListResponse.Data.Num()); + UE_LOG(LogTemp, Error, TEXT("Failed to parse API response")); } else { - UE_LOG(LogTemp, Warning, TEXT("API Response was unsuccessful or failed to parse")); + UE_LOG(LogTemp, Error, TEXT("API Response was unsuccessful")); } OnListAssetsResponse.ExecuteIfBound(FAssetListResponse(), false); } diff --git a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp index d48b538..5d954cf 100644 --- a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp +++ b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp @@ -11,7 +11,6 @@ void FApiKeyAuthStrategy::AddAuthToRequest(TSharedPtr Request) { URpmDeveloperSettings *Settings = GetMutableDefault(); Request->Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); - UE_LOG(LogTemp, Warning, TEXT("Adding ApiKey to request, onauthcomplete bound: %d"), OnAuthComplete.IsBound()); OnAuthComplete.ExecuteIfBound(true); } @@ -21,7 +20,6 @@ void FApiKeyAuthStrategy::OnRefreshTokenResponse(const FRefreshTokenResponse& Re void FApiKeyAuthStrategy::TryRefresh(TSharedPtr Request) { - UE_LOG(LogTemp, Log, TEXT("Trying refresh")); } diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp index 40e86bd..3ec1c95 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -32,7 +32,6 @@ void FWebApiWithAuth::OnAuthComplete(bool bWasSuccessful) { if(bWasSuccessful && ApiRequestData != nullptr) { - UE_LOG(LogTemp, Warning, TEXT("Auth complete, running request to %s"), *ApiRequestData->Url); DispatchRaw(*ApiRequestData); return; } @@ -49,7 +48,6 @@ void FWebApiWithAuth::OnAuthTokenRefreshed(const FRefreshTokenResponseBody& Resp ApiRequestData->Headers.Remove(Key); } ApiRequestData->Headers.Add(Key, FString::Printf(TEXT("Bearer %s"), *Response.Token)); - UE_LOG(LogTemp, Log, TEXT("Auth refreshed, running request")); DispatchRaw(*ApiRequestData); return; } @@ -63,17 +61,14 @@ void FWebApiWithAuth::DispatchRawWithAuth(FApiRequest& Data) if (AuthenticationStrategy == nullptr) { DispatchRaw(Data); - UE_LOG(LogTemp, Warning, TEXT("Auth Strategy is null, running request")); return; } - UE_LOG(LogTemp, Warning, TEXT("Auth Strategy is NOT null, running request")); AuthenticationStrategy->AddAuthToRequest(this->ApiRequestData); } void FWebApiWithAuth::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { - //FWebApi::OnProcessResponse(Request, Response, bWasSuccessful); if (bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode())) { OnApiResponse.ExecuteIfBound(Response->GetContentAsString(), true); diff --git a/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp deleted file mode 100644 index 61df158..0000000 --- a/Source/RpmNextGen/Private/Samples/RpmBasicUISampleWidget.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "Samples/RpmBasicUISampleWidget.h" - -void URpmBasicUISampleWidget::NativeConstruct() -{ - Super::NativeConstruct(); -} \ No newline at end of file diff --git a/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp deleted file mode 100644 index 119e089..0000000 --- a/Source/RpmNextGen/Private/Samples/RpmCharacterCustomizationWidget.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "Samples/RpmCharacterCustomizationWidget.h" -#include "Samples/RpmAssetButtonWidget.h" -#include "HttpModule.h" -#include "Settings/RpmDeveloperSettings.h" -#include "Components/HorizontalBox.h" -#include "Components/Image.h" -#include "Api/Assets/Models/Asset.h" -#include "Api/Assets/Models/AssetListRequest.h" -#include "Api/Assets/Models/AssetListResponse.h" -#include "Api/Characters/CharacterApi.h" -#include "Interfaces/IHttpResponse.h" -#include "Samples/RpmAssetPanel.h" - -void URpmCharacterCustomizationWidget::NativeConstruct() -{ - Super::NativeConstruct(); - URpmDeveloperSettings* Settings = GetMutableDefault(); - ApplicationID = Settings->ApplicationId; - InitializeCustomizationOptions(); -} - -void URpmCharacterCustomizationWidget::InitializeCustomizationOptions() -{ - URpmDeveloperSettings* Settings = GetMutableDefault(); - FString ApiBaseUrl = Settings->GetApiBaseUrl(); - - // Setup request and parameters - FAssetListRequest Request = FAssetListRequest(); - FAssetListQueryParams Params; - Params.ApplicationId = ApplicationID; - Params.ExcludeTypes = "baseModel"; - Request.Params = Params; - FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets%s"), *ApiBaseUrl, *Request.BuildQueryString()); - - // TODO replace this with use of AssetApi class - TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->SetURL(Url); - HttpRequest->SetVerb("GET"); - HttpRequest->SetHeader("Content-Type", "application/json"); - HttpRequest->SetHeader("X-API-KEY", Settings->ApiKey); - HttpRequest->OnProcessRequestComplete().BindLambda([this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) - { - if (bWasSuccessful) - { - // Parse the response to fill Assets - FAssetListResponse AssetListResponse = FAssetListResponse(); - if (FJsonObjectConverter::JsonObjectStringToUStruct(Response->GetContentAsString(), &AssetListResponse, 0, 0)) - { - OnAssetsFetched(AssetListResponse, true); - return; - } - } - else - { - // Handle error - } - OnAssetsFetched(FAssetListResponse(), false); // Call with empty response on failure - }); - HttpRequest->ProcessRequest(); -} - -void URpmCharacterCustomizationWidget::OnAssetsFetched(const FAssetListResponse& AssetListResponse, bool bWasSuccessful) -{ - if (AssetListResponse.Data.Num() > 0) - { - AssetDataArray = AssetListResponse.Data; - } - else - { - UE_LOG(LogTemp, Warning, TEXT("No assets fetched")); - } - OnAssetsFetchedDelegate.Broadcast(bWasSuccessful, AssetDataArray); -} - -void URpmCharacterCustomizationWidget::PopulateBoxWithFilter(UPanelWidget* ParentBox, const FString& AssetType) -{ - if (!ParentBox) - { - UE_LOG(LogTemp, Error, TEXT("ParentBox is null")); - return; - } - - for (const FAsset& AssetData : AssetDataArray) - { - if (AssetData.Type == AssetType) - { - CreateAssetWidget(AssetData, ParentBox); - } - } -} - -void URpmCharacterCustomizationWidget::PopulateBox(UPanelWidget* ParentBox) -{ - if (!ParentBox) - { - UE_LOG(LogTemp, Error, TEXT("ParentBox is null")); - return; - } - - for (const FAsset& AssetData : AssetDataArray) - { - CreateAssetWidget(AssetData, ParentBox); - } -} - -void URpmCharacterCustomizationWidget::ClearBox(UPanelWidget* ParentBox) -{ - if (!ParentBox) - { - UE_LOG(LogTemp, Error, TEXT("ParentBox is null")); - return; - } - - ParentBox->ClearChildren(); -} - -void URpmCharacterCustomizationWidget::UpdateCustomizationOptions(UPanelWidget* ParentBox) -{ - ClearBox(ParentBox); - InitializeCustomizationOptions(); - PopulateBox(ParentBox); -} - -void URpmCharacterCustomizationWidget::CreateAssetWidget(const FAsset& AssetData, UPanelWidget* ParentBox) -{ - if (!ParentBox) - { - UE_LOG(LogTemp, Error, TEXT("ParentBox is null")); - return; - } - - if (!AssetButtonWidgetClass) - { - UE_LOG(LogTemp, Error, TEXT("AssetButtonWidgetClass is null")); - return; - } - - URpmAssetButtonWidget* AssetButton = CreateWidget(this, AssetButtonWidgetClass); - if (AssetButton) - { - AssetButton->InitializeButton(AssetData, ImageSize); - AssetButton->OnAssetButtonClicked.AddDynamic(this, &URpmCharacterCustomizationWidget::OnAssetButtonClicked); - ParentBox->AddChild(AssetButton); - } -} - -void URpmCharacterCustomizationWidget::OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton) -{ - const FAsset& AssetData = AssetButton->GetAssetData(); - const FString GlbUrl = AssetData.GlbUrl; - OnAssetButtonSelected.Broadcast(AssetData); -} diff --git a/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h b/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h deleted file mode 100644 index 1f5043b..0000000 --- a/Source/RpmNextGen/Public/Samples/RpmBasicUISampleWidget.h +++ /dev/null @@ -1,20 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "Blueprint/UserWidget.h" -#include "RpmBasicUISampleWidget.generated.h" - -class FAssetApi; -/** - * - */ -UCLASS() -class RPMNEXTGEN_API URpmBasicUISampleWidget : public UUserWidget -{ - GENERATED_BODY() -public: - - virtual void NativeConstruct() override; -}; diff --git a/Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h b/Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h deleted file mode 100644 index da71322..0000000 --- a/Source/RpmNextGen/Public/Samples/RpmCharacterCustomizationWidget.h +++ /dev/null @@ -1,67 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "RpmAssetPanel.h" -#include "Api/Assets/Models/Asset.h" -#include "Blueprint/UserWidget.h" -#include "Api/Characters/CharacterApi.h" -#include "RpmCharacterCustomizationWidget.generated.h" - -class URpmAssetButtonWidget; -class FCharacterApi; -struct FAssetListResponse; -class FApiKeyAuthStrategy; -class UImage; -class UVerticalBox; - -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnAssetsFetchedDelegate, bool, bWasSuccessful, const TArray&, AssetDataArray); - -/** - * - */ -UCLASS() -class RPMNEXTGEN_API URpmCharacterCustomizationWidget : public UUserWidget -{ - GENERATED_BODY() - -public: - virtual void NativeConstruct() override; - - UFUNCTION(BlueprintCallable, Category = "Customization") - void PopulateBox(UPanelWidget* ParentBox); - - UFUNCTION(BlueprintCallable, Category = "Customization") - void ClearBox(UPanelWidget* ParentBox); - - UFUNCTION(BlueprintCallable, Category = "Customization") - void UpdateCustomizationOptions(UPanelWidget* ParentBox); - - UFUNCTION(BlueprintCallable, Category = "Customization") - void PopulateBoxWithFilter(UPanelWidget* ParentBox, const FString& AssetType); - - UPROPERTY(BlueprintAssignable, Category = "Customization") - FOnAssetsFetchedDelegate OnAssetsFetchedDelegate; - - UPROPERTY(BlueprintAssignable, Category = "Customization") - FOnAssetSelected OnAssetButtonSelected; - - // Property to define the size of the images - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Customization", meta = (ClampMin = "0.0", UIMin = "0.0", ToolTip = "Recommended to keep this square (e.g., 200x200)")) - FVector2D ImageSize = FVector2D(200.0f, 200.0f); // Default size of 200x200 - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Customization") - TSubclassOf AssetButtonWidgetClass; - -private: - void InitializeCustomizationOptions(); - void OnAssetsFetched(const FAssetListResponse& AssetListResponse, bool bWasSuccessful); - void CreateAssetWidget(const FAsset& AssetData, UPanelWidget* ParentBox); - - UFUNCTION() - void OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton); - - FString ApplicationID; - TArray AssetDataArray; -}; From 3b62e2b91b36e81741b7860de579cbe21518dc54 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 16 Aug 2024 15:34:44 +0300 Subject: [PATCH 32/74] chore: more cleanup and renaming --- .../BasicUI/Blueprints/WBP_AssetPanel.uasset | Bin 0 -> 23950 bytes .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 125777 -> 124107 bytes .../Blueprints/WBP_CategoryButton.uasset | Bin 0 -> 28213 bytes .../Blueprints/WBP_RpmAssetButton.uasset | Bin 27389 -> 27389 bytes .../Blueprints/WBP_RpmAssetCard.uasset | Bin 32076 -> 32076 bytes .../Blueprints/WBP_RpmAssetPanel.uasset | Bin 25853 -> 0 bytes .../Blueprints/WBP_RpmCategoryPanel.uasset | Bin 42854 -> 41965 bytes .../Blueprints/WPB_RpmCategoryButton.uasset | Bin 28727 -> 0 bytes Content/Samples/BasicUI/RpmBasicUILevel.umap | Bin 71753 -> 71753 bytes .../Private/Api/Common/WebApiWithAuth.cpp | 13 ++++------- ...pmAssetCard.cpp => RpmAssetCardWidget.cpp} | 7 +++--- ...AssetPanel.cpp => RpmAssetPanelWidget.cpp} | 22 +++++++++--------- ...Button.cpp => RpmCategoryButtonWidget.cpp} | 18 +++++++------- .../Samples/RpmCategoryPanelWidget.cpp | 8 +++---- .../Public/Api/Auth/IAuthenticationStrategy.h | 5 ++-- .../Public/Samples/RpmAssetCardWidget.h | 1 + ...{RpmAssetPanel.h => RpmAssetPanelWidget.h} | 4 ++-- ...goryButton.h => RpmCategoryButtonWidget.h} | 6 ++--- .../Public/Samples/RpmCategoryPanelWidget.h | 8 +++---- 19 files changed, 44 insertions(+), 48 deletions(-) create mode 100644 Content/Samples/BasicUI/Blueprints/WBP_AssetPanel.uasset create mode 100644 Content/Samples/BasicUI/Blueprints/WBP_CategoryButton.uasset delete mode 100644 Content/Samples/BasicUI/Blueprints/WBP_RpmAssetPanel.uasset delete mode 100644 Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset rename Source/RpmNextGen/Private/Samples/{RpmAssetCard.cpp => RpmAssetCardWidget.cpp} (99%) rename Source/RpmNextGen/Private/Samples/{RpmAssetPanel.cpp => RpmAssetPanelWidget.cpp} (80%) rename Source/RpmNextGen/Private/Samples/{RpmCategoryButton.cpp => RpmCategoryButtonWidget.cpp} (69%) rename Source/RpmNextGen/Public/Samples/{RpmAssetPanel.h => RpmAssetPanelWidget.h} (94%) rename Source/RpmNextGen/Public/Samples/{RpmCategoryButton.h => RpmCategoryButtonWidget.h} (89%) diff --git a/Content/Samples/BasicUI/Blueprints/WBP_AssetPanel.uasset b/Content/Samples/BasicUI/Blueprints/WBP_AssetPanel.uasset new file mode 100644 index 0000000000000000000000000000000000000000..2fe6816fe244d6ec48aa2bce6a54e9c06900b1d5 GIT binary patch literal 23950 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 ORN*#{nd$*LiTz(Xbp;Us literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset index af1eb7680e9106d8209747cf58ab57582cb4afb1..8f3a34b461460f93168aeb47c90e537e6b30cbc7 100644 GIT binary patch literal 124107 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 Zn5j2N8O|RTWgvnzx}2HvX8MP5{{y1**wO$1 literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetButton.uasset index 700d7759a13316b2ccba078d83aa405b9b182356..cd7dc7b3359fa971eb1353835fc58c8a919c0a64 100644 GIT binary patch delta 31 pcmV+)0O0@q)dBt00k9?k5a{jG{SQ!GON62B$3Lh%cC$7C$7u;u561ui delta 31 pcmV+)0O0@q)dBt00k9?k5Q(6pbS?d8PMDRGGdiy;uCq1)$7%D24t)Rs diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetCard.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetCard.uasset index e9da32ae58b8540e300d7f07682a2981ec5d430c..e634b5f009d598f1a318aa866be47599a4c5b397 100644 GIT binary patch delta 45 zcmV+|0Mh@=`T@-P0k9?k5Eb=h`*ygeO@Rdy5q@;=9kVt8L0Xd&TNSf&TS5x6&SicV D$rloi delta 47 zcmX@}i}B1a#tmwW0%5O9i`n##`_A$TRrjgol-X>?=oqzmLbM$hBg15YXo=0|QtQM4 D!Fmxh diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetPanel.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmAssetPanel.uasset deleted file mode 100644 index 48df5a7a89e9772eef16925152559db8927e40af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25853 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 Vl5{He|42X#hxi2COaJl7{{rAI=#l^c diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset index d82c902888f9f2605c5e562666cb8b6bac312d1d..f6162ada5fe66130d2a66f9b83007e3ac548223a 100644 GIT binary patch literal 41965 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 P!GY{*Q{6qR>4N_O($88+ literal 42854 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 MrB!eD$4+zq2hr%me*gdg diff --git a/Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset b/Content/Samples/BasicUI/Blueprints/WPB_RpmCategoryButton.uasset deleted file mode 100644 index 484bdbdf3d1f23351f5ad5e63a99cf612c40c350..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28727 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 S-*_m5)U?D}e=GgT!2bpJpe#xN diff --git a/Content/Samples/BasicUI/RpmBasicUILevel.umap b/Content/Samples/BasicUI/RpmBasicUILevel.umap index e71989c46e02d2baf1cb1162af54619c5a86fd17..946bf3a67a6f3e7ff84216dcae252c9e25277793 100644 GIT binary patch delta 93 zcmV-j0HXiNums7l1hAd~5U6O@b2(X&MwBfXk*}F$j$J0>O1Z}+l{3HYy^|riIFs;360>9J<}Lu1EtCKN diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp index 3ec1c95..d1d5e28 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -14,18 +14,13 @@ FWebApiWithAuth::FWebApiWithAuth(IAuthenticationStrategy* InAuthenticationStrate void FWebApiWithAuth::SetAuthenticationStrategy(IAuthenticationStrategy* InAuthenticationStrategy) { - if(AuthenticationStrategy != nullptr) - { - AuthenticationStrategy->OnAuthComplete.Unbind(); - AuthenticationStrategy->OnTokenRefreshed.Unbind(); - } AuthenticationStrategy = InAuthenticationStrategy; - if(AuthenticationStrategy == nullptr) + + if (AuthenticationStrategy != nullptr) { - return; + AuthenticationStrategy->OnAuthComplete.BindRaw(this, &FWebApiWithAuth::OnAuthComplete); + AuthenticationStrategy->OnTokenRefreshed.BindRaw(this, &FWebApiWithAuth::OnAuthTokenRefreshed); } - AuthenticationStrategy->OnAuthComplete.BindRaw(this, &FWebApiWithAuth::OnAuthComplete); - AuthenticationStrategy->OnTokenRefreshed.BindRaw(this, &FWebApiWithAuth::OnAuthTokenRefreshed); } void FWebApiWithAuth::OnAuthComplete(bool bWasSuccessful) diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetCardWidget.cpp similarity index 99% rename from Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp rename to Source/RpmNextGen/Private/Samples/RpmAssetCardWidget.cpp index 1248b19..90b83eb 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetCard.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetCardWidget.cpp @@ -1,9 +1,9 @@ // Fill out your copyright notice in the Description page of Project Settings. +#include "Samples/RpmAssetCardWidget.h" #include "RpmImageLoader.h" #include "Api/Assets/Models/Asset.h" #include "Components/TextBlock.h" -#include "Samples/RpmAssetCardWidget.h" void URpmAssetCardWidget::NativeConstruct() { @@ -15,13 +15,12 @@ void URpmAssetCardWidget::InitializeCard(const FAsset& Asset) { this->SetVisibility(ESlateVisibility::Visible); AssetData = Asset; + AssetCategoryText->SetText(FText::FromString(AssetData.Type)); AssetNameText->SetText(FText::FromString(AssetData.Name)); - AssetNameText->SetVisibility( AssetData.Name.IsEmpty() ? ESlateVisibility::Hidden : ESlateVisibility::Visible ); - - AssetIdText->SetText(FText::FromString(AssetData.Id)); + LoadImage(AssetData.IconUrl); } diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp similarity index 80% rename from Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp rename to Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp index 7745aee..6dcfad4 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanel.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp @@ -1,7 +1,7 @@ // Fill out your copyright notice in the Description page of Project Settings. -#include "Samples/RpmAssetPanel.h" +#include "Samples/RpmAssetPanelWidget.h" #include "Api/Assets/AssetApi.h" #include "Api/Assets/Models/AssetListRequest.h" @@ -11,7 +11,7 @@ #include "Samples/RpmAssetButtonWidget.h" #include "Settings/RpmDeveloperSettings.h" -void URpmAssetPanel::NativeConstruct() +void URpmAssetPanelWidget::NativeConstruct() { Super::NativeConstruct(); URpmDeveloperSettings *Settings = GetMutableDefault(); @@ -23,13 +23,13 @@ void URpmAssetPanel::NativeConstruct() UE_LOG(LogTemp, Warning, TEXT("Adding ApiKeyAuthStrategy")); } - AssetApi->OnListAssetsResponse.BindUObject(this, &URpmAssetPanel::OnAssetListResponse); + AssetApi->OnListAssetsResponse.BindUObject(this, &URpmAssetPanelWidget::OnAssetListResponse); ButtonSize = FVector2D(200, 200); ImageSize = FVector2D(200, 200); } -void URpmAssetPanel::OnAssetListResponse(const FAssetListResponse& AssetListResponse, bool bWasSuccessful) +void URpmAssetPanelWidget::OnAssetListResponse(const FAssetListResponse& AssetListResponse, bool bWasSuccessful) { if(bWasSuccessful && AssetListResponse.Data.Num() > 0) { @@ -40,7 +40,7 @@ void URpmAssetPanel::OnAssetListResponse(const FAssetListResponse& AssetListResp UE_LOG(LogTemp, Error, TEXT("Failed to fetch assets")); } -void URpmAssetPanel::CreateButtonsFromAssets(TArray Assets) +void URpmAssetPanelWidget::CreateButtonsFromAssets(TArray Assets) { for (auto Asset : Assets) { @@ -50,7 +50,7 @@ void URpmAssetPanel::CreateButtonsFromAssets(TArray Assets) UE_LOG(LogTemp, Warning, TEXT("No assets found") ); } -void URpmAssetPanel::ClearAllButtons() +void URpmAssetPanelWidget::ClearAllButtons() { if(!AssetButtons.IsEmpty()) { @@ -59,7 +59,7 @@ void URpmAssetPanel::ClearAllButtons() SelectedAssetButton = nullptr; } -void URpmAssetPanel::UpdateSelectedButton(URpmAssetButtonWidget* AssetButton) +void URpmAssetPanelWidget::UpdateSelectedButton(URpmAssetButtonWidget* AssetButton) { if(SelectedAssetButton && SelectedAssetButton != AssetButton) { @@ -69,7 +69,7 @@ void URpmAssetPanel::UpdateSelectedButton(URpmAssetButtonWidget* AssetButton) SelectedAssetButton = AssetButton; } -void URpmAssetPanel::CreateButton(const FAsset& AssetData) +void URpmAssetPanelWidget::CreateButton(const FAsset& AssetData) { if (AssetButtonBlueprint) { @@ -95,7 +95,7 @@ void URpmAssetPanel::CreateButton(const FAsset& AssetData) AssetButtons.Add(AssetButtonBlueprint); - AssetButtonInstance->OnAssetButtonClicked.AddDynamic(this, &URpmAssetPanel::OnAssetButtonClicked); + AssetButtonInstance->OnAssetButtonClicked.AddDynamic(this, &URpmAssetPanelWidget::OnAssetButtonClicked); } } } @@ -107,13 +107,13 @@ void URpmAssetPanel::CreateButton(const FAsset& AssetData) -void URpmAssetPanel::OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton) +void URpmAssetPanelWidget::OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton) { UpdateSelectedButton(const_cast(AssetButton)); OnAssetSelected.Broadcast(AssetButton->GetAssetData()); } -void URpmAssetPanel::LoadAssetsOfType(const FString& AssetType) +void URpmAssetPanelWidget::LoadAssetsOfType(const FString& AssetType) { URpmDeveloperSettings *Settings = GetMutableDefault(); FAssetListQueryParams QueryParams; diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp similarity index 69% rename from Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp rename to Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp index 32e375a..4c54785 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryButton.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp @@ -1,18 +1,18 @@ // Fill out your copyright notice in the Description page of Project Settings. -#include "Samples/RpmCategoryButton.h" +#include "Samples/RpmCategoryButtonWidget.h" #include "Components/Button.h" #include "Components/Image.h" -void URpmCategoryButton::NativeConstruct() +void URpmCategoryButtonWidget::NativeConstruct() { Super::NativeConstruct(); if (CategoryButton) { - CategoryButton->OnClicked.AddDynamic(this, &URpmCategoryButton::HandleButtonClicked); + CategoryButton->OnClicked.AddDynamic(this, &URpmCategoryButtonWidget::HandleButtonClicked); DefaultColor = CategoryButton->BackgroundColor; } if (CategoryImageTexture) @@ -21,7 +21,7 @@ void URpmCategoryButton::NativeConstruct() } } -void URpmCategoryButton::InitializeButton(FString Category, UTexture2D* Image) +void URpmCategoryButtonWidget::InitializeButton(FString Category, UTexture2D* Image) { AssetCategoryName = Category; @@ -32,7 +32,7 @@ void URpmCategoryButton::InitializeButton(FString Category, UTexture2D* Image) } } -void URpmCategoryButton::SetSelected(bool bIsSelected) +void URpmCategoryButtonWidget::SetSelected(bool bIsSelected) { if (CategoryButton) { @@ -41,7 +41,7 @@ void URpmCategoryButton::SetSelected(bool bIsSelected) } } -void URpmCategoryButton::HandleButtonClicked() +void URpmCategoryButtonWidget::HandleButtonClicked() { SetSelected(true); @@ -49,13 +49,13 @@ void URpmCategoryButton::HandleButtonClicked() } #if WITH_EDITOR -void URpmCategoryButton::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) +void URpmCategoryButtonWidget::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None; - if (PropertyName == GET_MEMBER_NAME_CHECKED(URpmCategoryButton, CategoryImageTexture)) + if (PropertyName == GET_MEMBER_NAME_CHECKED(URpmCategoryButtonWidget, CategoryImageTexture)) { if (CategoryImage && CategoryImageTexture) { @@ -67,7 +67,7 @@ void URpmCategoryButton::PostEditChangeProperty(FPropertyChangedEvent& PropertyC } #endif -void URpmCategoryButton::SynchronizeProperties() +void URpmCategoryButtonWidget::SynchronizeProperties() { Super::SynchronizeProperties(); if (CategoryImage && CategoryImageTexture) diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp index eec762f..9e54b55 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp @@ -3,7 +3,7 @@ #include "Samples/RpmCategoryPanelWidget.h" #include "Blueprint/WidgetTree.h" -#include "Samples/RpmCategoryButton.h" +#include "Samples/RpmCategoryButtonWidget.h" void URpmCategoryPanelWidget::NativeConstruct() { @@ -18,7 +18,7 @@ void URpmCategoryPanelWidget::InitializeCategoryButtons() for (UWidget* Widget : Widgets) { - if (URpmCategoryButton* CategoryButton = Cast(Widget)) + if (URpmCategoryButtonWidget* CategoryButton = Cast(Widget)) { CategoryButton->InitializeButton(CategoryButton->AssetCategoryName, CategoryButton->CategoryImageTexture); CategoryButton->OnCategoryClicked.AddDynamic(this, &URpmCategoryPanelWidget::OnCategoryButtonClicked); @@ -26,7 +26,7 @@ void URpmCategoryPanelWidget::InitializeCategoryButtons() } } -void URpmCategoryPanelWidget::UpdateSelectedButton(URpmCategoryButton* CategoryButton) +void URpmCategoryPanelWidget::UpdateSelectedButton(URpmCategoryButtonWidget* CategoryButton) { if(SelectedCategoryButton && SelectedCategoryButton != CategoryButton) { @@ -36,7 +36,7 @@ void URpmCategoryPanelWidget::UpdateSelectedButton(URpmCategoryButton* CategoryB SelectedCategoryButton = CategoryButton; } -void URpmCategoryPanelWidget::OnCategoryButtonClicked(URpmCategoryButton* CategoryButton) +void URpmCategoryPanelWidget::OnCategoryButtonClicked(URpmCategoryButtonWidget* CategoryButton) { UpdateSelectedButton(CategoryButton); OnCategorySelected.Broadcast(CategoryButton->AssetCategoryName); diff --git a/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h index e8a190f..651827b 100644 --- a/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h +++ b/Source/RpmNextGen/Public/Api/Auth/IAuthenticationStrategy.h @@ -10,10 +10,11 @@ DECLARE_DELEGATE_TwoParams(FOnTokenRefreshed, const FRefreshTokenResponseBody&, class RPMNEXTGEN_API IAuthenticationStrategy { public: + FOnAuthComplete OnAuthComplete; + FOnTokenRefreshed OnTokenRefreshed; + virtual ~IAuthenticationStrategy() = default; virtual void AddAuthToRequest(TSharedPtr Request) = 0; virtual void TryRefresh(TSharedPtr Request) = 0; virtual void OnRefreshTokenResponse(const FRefreshTokenResponse& Response, bool bWasSuccessful) = 0; - FOnAuthComplete OnAuthComplete; - FOnTokenRefreshed OnTokenRefreshed; }; \ No newline at end of file diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h index af15698..afcd456 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h @@ -34,6 +34,7 @@ class RPMNEXTGEN_API URpmAssetCardWidget : public UUserWidget UPROPERTY(meta = (BindWidget)) UTextBlock* AssetIdText; + private: FAsset AssetData; }; diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h b/Source/RpmNextGen/Public/Samples/RpmAssetPanelWidget.h similarity index 94% rename from Source/RpmNextGen/Public/Samples/RpmAssetPanel.h rename to Source/RpmNextGen/Public/Samples/RpmAssetPanelWidget.h index 4369ca2..0b50b3a 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetPanel.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetPanelWidget.h @@ -5,7 +5,7 @@ #include "CoreMinimal.h" #include "Api/Assets/Models/AssetListResponse.h" #include "Blueprint/UserWidget.h" -#include "RpmAssetPanel.generated.h" +#include "RpmAssetPanelWidget.generated.h" class FAssetApi; struct FAsset; @@ -17,7 +17,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAssetSelected, const FAsset&, Ass * */ UCLASS() -class RPMNEXTGEN_API URpmAssetPanel : public UUserWidget +class RPMNEXTGEN_API URpmAssetPanelWidget : public UUserWidget { GENERATED_BODY() public: diff --git a/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h b/Source/RpmNextGen/Public/Samples/RpmCategoryButtonWidget.h similarity index 89% rename from Source/RpmNextGen/Public/Samples/RpmCategoryButton.h rename to Source/RpmNextGen/Public/Samples/RpmCategoryButtonWidget.h index 0348b35..3e22e0f 100644 --- a/Source/RpmNextGen/Public/Samples/RpmCategoryButton.h +++ b/Source/RpmNextGen/Public/Samples/RpmCategoryButtonWidget.h @@ -4,19 +4,19 @@ #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" -#include "RpmCategoryButton.generated.h" +#include "RpmCategoryButtonWidget.generated.h" class UImage; class UBorder; class UButton; -DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCategoryClicked, URpmCategoryButton*, CategoryButton); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCategoryClicked, URpmCategoryButtonWidget*, CategoryButton); /** * */ UCLASS() -class RPMNEXTGEN_API URpmCategoryButton : public UUserWidget +class RPMNEXTGEN_API URpmCategoryButtonWidget : public UUserWidget { GENERATED_BODY() diff --git a/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h b/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h index 4d48ab8..feae846 100644 --- a/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmCategoryPanelWidget.h @@ -6,7 +6,7 @@ #include "Blueprint/UserWidget.h" #include "RpmCategoryPanelWidget.generated.h" -class URpmCategoryButton; +class URpmCategoryButtonWidget; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCategorySelected, const FString&, CategoryName); @@ -22,16 +22,16 @@ class RPMNEXTGEN_API URpmCategoryPanelWidget : public UUserWidget virtual void NativeConstruct() override; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Category Panel") - URpmCategoryButton* SelectedCategoryButton; + URpmCategoryButtonWidget* SelectedCategoryButton; UPROPERTY(BlueprintAssignable, Category = "Events") FOnCategorySelected OnCategorySelected; UFUNCTION(BlueprintCallable, Category = "Category Panel") - virtual void UpdateSelectedButton(URpmCategoryButton* CategoryButton); + virtual void UpdateSelectedButton(URpmCategoryButtonWidget* CategoryButton); UFUNCTION() - virtual void OnCategoryButtonClicked(URpmCategoryButton* CategoryButton); + virtual void OnCategoryButtonClicked(URpmCategoryButtonWidget* CategoryButton); private: void InitializeCategoryButtons(); From 23a8242efb0aba079e8b4621b51d3967c7f920b4 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 16 Aug 2024 15:40:20 +0300 Subject: [PATCH 33/74] chore: small cleanup --- Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp | 8 +------- .../Private/Samples/RpmCategoryButtonWidget.cpp | 3 +++ .../RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp | 1 - 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp index 6dcfad4..c4586c3 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp @@ -64,7 +64,6 @@ void URpmAssetPanelWidget::UpdateSelectedButton(URpmAssetButtonWidget* AssetButt if(SelectedAssetButton && SelectedAssetButton != AssetButton) { SelectedAssetButton->SetSelected(false); - } SelectedAssetButton = AssetButton; } @@ -77,24 +76,19 @@ void URpmAssetPanelWidget::CreateButton(const FAsset& AssetData) if (World) { - URpmAssetButtonWidget* AssetButtonInstance = CreateWidget(World, AssetButtonBlueprint); - - if (AssetButtonInstance) + if (URpmAssetButtonWidget* AssetButtonInstance = CreateWidget(World, AssetButtonBlueprint)) { USizeBox* ButtonSizeBox = NewObject(this); if (ButtonSizeBox && ButtonContainer) { ButtonSizeBox->SetWidthOverride(ButtonSize.X); ButtonSizeBox->SetHeightOverride(ButtonSize.Y); - ButtonSizeBox->AddChild(AssetButtonInstance); ButtonContainer->AddChild(ButtonSizeBox); } AssetButtonInstance->InitializeButton(AssetData, ImageSize); - AssetButtons.Add(AssetButtonBlueprint); - AssetButtonInstance->OnAssetButtonClicked.AddDynamic(this, &URpmAssetPanelWidget::OnAssetButtonClicked); } } diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp index 4c54785..966dc0a 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp @@ -49,12 +49,14 @@ void URpmCategoryButtonWidget::HandleButtonClicked() } #if WITH_EDITOR +// This function is called when a property is changed in the editor, it enables the CategoryImageTexture to be updated in the editor void URpmCategoryButtonWidget::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None; + // If the property that was changed is the CategoryImageTexture apply the new texture to the CategoryImage if (PropertyName == GET_MEMBER_NAME_CHECKED(URpmCategoryButtonWidget, CategoryImageTexture)) { if (CategoryImage && CategoryImageTexture) @@ -67,6 +69,7 @@ void URpmCategoryButtonWidget::PostEditChangeProperty(FPropertyChangedEvent& Pro } #endif +// This is also required to update the CategoryImageTexture in the editor void URpmCategoryButtonWidget::SynchronizeProperties() { Super::SynchronizeProperties(); diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp index 9e54b55..5acaf1d 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp @@ -31,7 +31,6 @@ void URpmCategoryPanelWidget::UpdateSelectedButton(URpmCategoryButtonWidget* Cat if(SelectedCategoryButton && SelectedCategoryButton != CategoryButton) { SelectedCategoryButton->SetSelected(false); - } SelectedCategoryButton = CategoryButton; } From 8f980a23622aa4caaa22ea689e75997614252207 Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Wed, 21 Aug 2024 08:10:04 +0300 Subject: [PATCH 34/74] chore: refactor imageloader --- Source/RpmNextGen/Private/RpmImageLoader.cpp | 113 +++++++++++++ .../Private/Samples/RpmAssetButtonWidget.cpp | 2 +- .../Private/Samples/RpmAssetCardWidget.cpp | 2 +- Source/RpmNextGen/Public/RpmImageLoader.cpp | 158 ------------------ Source/RpmNextGen/Public/RpmImageLoader.h | 9 +- .../Private/SRpmDeveloperLoginWidget.cpp | 2 +- 6 files changed, 120 insertions(+), 166 deletions(-) create mode 100644 Source/RpmNextGen/Private/RpmImageLoader.cpp delete mode 100644 Source/RpmNextGen/Public/RpmImageLoader.cpp diff --git a/Source/RpmNextGen/Private/RpmImageLoader.cpp b/Source/RpmNextGen/Private/RpmImageLoader.cpp new file mode 100644 index 0000000..0906583 --- /dev/null +++ b/Source/RpmNextGen/Private/RpmImageLoader.cpp @@ -0,0 +1,113 @@ +#include "RpmImageLoader.h" +#include "HttpModule.h" +#include "Interfaces/IHttpRequest.h" +#include "Interfaces/IHttpResponse.h" +#include "IImageWrapper.h" +#include "IImageWrapperModule.h" +#include "Async/Async.h" +#include "Modules/ModuleManager.h" +#include "Engine/Texture2D.h" +#include "Components/Image.h" + +void FRpmImageLoader::LoadUImageFromURL(UImage* Image, const FString& URL) +{ + if (!Image) + { + UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: Image is null")); + return; + } + + TWeakObjectPtr WeakImagePtr(Image); + DownloadImage(URL, [WeakImagePtr](UTexture2D* Texture) { + if (WeakImagePtr.IsValid() && Texture) + { + FSlateBrush Brush; + Brush.SetResourceObject(Texture); + Brush.ImageSize = WeakImagePtr->Brush.ImageSize; + WeakImagePtr->SetBrush(Brush); + } + }); +} + +void FRpmImageLoader::LoadSImageFromURL(TSharedPtr ImageWidget, const FString& URL) +{ + if (!ImageWidget.IsValid()) + { + UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: SImage widget is invalid")); + return; + } + + DownloadImage(URL, [ImageWidget](UTexture2D* Texture) { + if (ImageWidget.IsValid() && Texture) + { + FSlateBrush* Brush = new FSlateBrush(); + Brush->SetResourceObject(Texture); + Brush->ImageSize = FVector2D(100.0f, 100.0f); // Adjust size as needed + ImageWidget->SetImage(Brush); + } + }); +} + +void FRpmImageLoader::DownloadImage(const FString& URL, TFunction OnImageDownloaded) +{ + TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->OnProcessRequestComplete().BindRaw(this, &FRpmImageLoader::OnImageDownloadComplete, OnImageDownloaded); + HttpRequest->SetURL(URL); + HttpRequest->SetVerb("GET"); + HttpRequest->ProcessRequest(); +} + +void FRpmImageLoader::OnImageDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TFunction OnImageDownloaded) +{ + UTexture2D* Texture = nullptr; + if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) + { + Texture = CreateTextureFromImageData(Response->GetContent()); + } + + if (!Texture) + { + UE_LOG(LogTemp, Error, TEXT("Failed to download or create texture from URL: %s"), *Request->GetURL()); + } + + AsyncTask(ENamedThreads::GameThread, [OnImageDownloaded, Texture]() { + OnImageDownloaded(Texture); + }); +} + +UTexture2D* FRpmImageLoader::CreateTextureFromImageData(const TArray& ImageData) +{ + IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); + EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); + + if (ImageFormat == EImageFormat::Invalid) + { + return nullptr; + } + + TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); + if (!ImageWrapper.IsValid() || !ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) + { + return nullptr; + } + + TArray UncompressedBGRA; + if (!ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) + { + return nullptr; + } + + UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); + if (!Texture) + { + return nullptr; + } + + void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); + Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); + Texture->UpdateResource(); + + return Texture; +} + diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp index 26c67c2..b168236 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetButtonWidget.cpp @@ -25,7 +25,7 @@ void URpmAssetButtonWidget::InitializeButton(const FAsset& InAssetData, const FV AssetImage->SetDesiredSizeOverride(InImageSize); FRpmImageLoader ImageLoader; - ImageLoader.LoadImageFromURL(AssetImage, AssetData.IconUrl); + ImageLoader.LoadUImageFromURL(AssetImage, AssetData.IconUrl); } } diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetCardWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetCardWidget.cpp index 90b83eb..10da885 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetCardWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetCardWidget.cpp @@ -27,6 +27,6 @@ void URpmAssetCardWidget::InitializeCard(const FAsset& Asset) void URpmAssetCardWidget::LoadImage(const FString& URL) { FRpmImageLoader ImageLoader; - ImageLoader.LoadImageFromURL(AssetImage, URL); + ImageLoader.LoadUImageFromURL(AssetImage, URL); } diff --git a/Source/RpmNextGen/Public/RpmImageLoader.cpp b/Source/RpmNextGen/Public/RpmImageLoader.cpp deleted file mode 100644 index 1123d1c..0000000 --- a/Source/RpmNextGen/Public/RpmImageLoader.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include "RpmImageLoader.h" -#include "HttpModule.h" -#include "Interfaces/IHttpRequest.h" -#include "Interfaces/IHttpResponse.h" -#include "IImageWrapper.h" -#include "IImageWrapperModule.h" -#include "Async/Async.h" -#include "Modules/ModuleManager.h" -#include "Engine/Texture2D.h" -#include "Components/Image.h" - -void FRpmImageLoader::LoadImageFromURL(UImage* Image, const FString& URL) -{ - if (!Image) - { - UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: Image is null")); - return; - } - - TWeakObjectPtr WeakImagePtr(Image); - - TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->OnProcessRequestComplete().BindRaw(this, &FRpmImageLoader::OnImageDownloadedForUImage, WeakImagePtr); - HttpRequest->SetURL(URL); - HttpRequest->SetVerb("GET"); - HttpRequest->ProcessRequest(); -} - -void FRpmImageLoader::LoadImageFromURL(TSharedPtr ImageWidget, const FString& URL) -{ - if (!ImageWidget.IsValid()) - { - UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: SImage widget is invalid")); - return; - } - - TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->OnProcessRequestComplete().BindRaw(this, &FRpmImageLoader::OnImageDownloadedForSImage, ImageWidget); - HttpRequest->SetURL(URL); - HttpRequest->SetVerb("GET"); - HttpRequest->ProcessRequest(); -} - -void FRpmImageLoader::OnImageDownloadedForUImage(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TWeakObjectPtr Image) -{ - if (!Image.IsValid()) - { - UE_LOG(LogTemp, Error, TEXT("OnImageDownloaded: Image is null or no longer valid")); - return; - } - - if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) - { - TArray ImageData = Response->GetContent(); - IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); - EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); - if (ImageFormat != EImageFormat::Invalid) - { - TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); - - if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) - { - TArray UncompressedBGRA; - if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) - { - UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); - if (Texture) - { - void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); - FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); - Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); - - Texture->UpdateResource(); - - FSlateBrush Brush; - Brush.SetResourceObject(Texture); - Brush.ImageSize = Image->Brush.ImageSize; - AsyncTask(ENamedThreads::GameThread, [Image, Brush]() { - if (Image.IsValid()) - { - Image->SetBrush(Brush); - } - }); - } - } - } - } - else - { - UE_LOG(LogTemp, Error, TEXT("Unsupported image format: %s"), *Request->GetURL()); - } - } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to download image from URL: %s"), *Request->GetURL()); - } -} - -void FRpmImageLoader::OnImageDownloadedForSImage(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TSharedPtr ImageWidget) -{ - if (!ImageWidget.IsValid()) - { - UE_LOG(LogTemp, Error, TEXT("OnImageDownloadedForSImage: SImage widget is invalid")); - return; - } - - UTexture2D* Texture = nullptr; - if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) - { - Texture = CreateTextureFromImageData(Response->GetContent()); - } - - if (Texture) - { - FSlateBrush* Brush = new FSlateBrush(); - Brush->SetResourceObject(Texture); - Brush->ImageSize = FVector2D(100.0f, 100.0f); // You can adjust the size as needed - - AsyncTask(ENamedThreads::GameThread, [ImageWidget, Brush]() { - ImageWidget->SetImage(Brush); - }); - } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to download or create texture from URL: %s"), *Request->GetURL()); - } -} - -UTexture2D* FRpmImageLoader::CreateTextureFromImageData(const TArray& ImageData) -{ - IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); - EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); - - if (ImageFormat != EImageFormat::Invalid) - { - TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); - if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) - { - TArray UncompressedBGRA; - if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) - { - UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); - if (Texture) - { - void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); - FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); - Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); - - Texture->UpdateResource(); - return Texture; - } - } - } - } - - return nullptr; -} - diff --git a/Source/RpmNextGen/Public/RpmImageLoader.h b/Source/RpmNextGen/Public/RpmImageLoader.h index 1dbbf54..a16be65 100644 --- a/Source/RpmNextGen/Public/RpmImageLoader.h +++ b/Source/RpmNextGen/Public/RpmImageLoader.h @@ -11,12 +11,11 @@ class RPMNEXTGEN_API FRpmImageLoader { public: FRpmImageLoader() = default; - - void LoadImageFromURL(UImage* Image, const FString& URL); - void LoadImageFromURL(TSharedPtr ImageWidget, const FString& URL); + void LoadUImageFromURL(UImage* Image, const FString& URL); + void LoadSImageFromURL(TSharedPtr ImageWidget, const FString& URL); private: - void OnImageDownloadedForUImage(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TWeakObjectPtr Image); - void OnImageDownloadedForSImage(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TSharedPtr ImageWidget); + void DownloadImage(const FString& URL, TFunction OnImageDownloaded); + void OnImageDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TFunction OnImageDownloaded); UTexture2D* CreateTextureFromImageData(const TArray& ImageData); }; \ No newline at end of file diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index aeeb590..ce93580 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -277,7 +277,7 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) ]; FRpmImageLoader ImageLoader; - ImageLoader.LoadImageFromURL(ImageWidget, StyleAsset.IconUrl); + ImageLoader.LoadSImageFromURL(ImageWidget, StyleAsset.IconUrl); } void SRpmDeveloperLoginWidget::OnLoadStyleClicked(const FString& StyleId) From f12df6bec7a2968e95d69cbc4e8f51ed2fc5b1d7 Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 21 Aug 2024 11:16:46 +0300 Subject: [PATCH 35/74] feat: update README --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d91149..9fda6c5 100644 --- a/README.md +++ b/README.md @@ -1 +1,16 @@ -# rpm-unreal-next-gen-sdk \ No newline at end of file +# Ready Player Me NextGen SDK for Unreal Engine + +This is the Unreal Engine SDK for Ready Player Me's NextGen avatars. It allows you to easily integrate our avatars into your Unreal Engine project. + +### Requirements +- Unreal Engine 5.0 or later +- Visual Studio 2022 +- glTFRuntime plugin available here: https://github.com/rdeioris/glTFRuntime +- TransientObjectSaver plugin available here: https://github.com/rdeioris/TransientObjectSaver + +### Installation +1. Download the latest release from the [Releases](https://github.com/readyplayerme/rpm-unreal-next-gen-sdk/releases) page. +2. Extract the contents of the zip file into your project's Plugins directory. If the Plugins directory does not exist, create it. +3. Open your project's .sln file in your IDE and build. +4. Run your project and launch the Unreal Editor. +5. In the Editor, go to Edit -> Plugins and enable the Ready Player Me NextGen SDK plugin. \ No newline at end of file From 1ef16487b6d732e70d3a055ead1484ebf4a6a239 Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Thu, 22 Aug 2024 10:21:59 +0300 Subject: [PATCH 36/74] feat: add initial loading --- Source/RpmNextGen/Private/RpmActor.cpp | 25 +++-- .../Public/Api/Assets/AssetLoader.h | 5 +- .../Private/EditorAssetLoader.cpp | 97 +++++++++++++------ .../Private/SRpmDeveloperLoginWidget.cpp | 2 +- .../Private/UEditorAssetNamer.cpp | 21 ++++ .../Public/EditorAssetLoader.h | 8 +- .../Public/UEditorAssetNamer.h | 27 ++++++ 7 files changed, 138 insertions(+), 47 deletions(-) create mode 100644 Source/RpmNextGenEditor/Private/UEditorAssetNamer.cpp create mode 100644 Source/RpmNextGenEditor/Public/UEditorAssetNamer.h diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index 353aa46..a014fc8 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -59,7 +59,7 @@ void ARpmActor::CreateCharacter() USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* SkeletalMesh, const FString& Name) { - if(AddedMeshComponents.Num() > 0) + if (AddedMeshComponents.Num() > 0) { for (auto AddedMeshComponent : AddedMeshComponents) { @@ -70,11 +70,11 @@ USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* Sk USkeletalMeshComponent* NewSkeletalMeshComponent = NewObject(this, *Name); NewSkeletalMeshComponent->SetupAttachment(RootComponent); NewSkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); - NewSkeletalMeshComponent->SkeletalMesh->SetSkeleton(BaseSkeletalMeshComponent->SkeletalMesh->GetSkeleton()); + //NewSkeletalMeshComponent->SkeletalMesh->SetSkeleton(BaseSkeletalMeshComponent->SkeletalMesh->GetSkeleton()); NewSkeletalMeshComponent->RegisterComponent(); AddInstanceComponent(NewSkeletalMeshComponent); AddedMeshComponents.Add(NewSkeletalMeshComponent); - + if (AddedMeshComponents.Num() == 1 && AddedMeshComponents.IsValidIndex(0)) { // Get the SkeletalMeshComponent at index 0 @@ -87,7 +87,7 @@ USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* Sk MeshComponent->SetAnimInstanceClass(TargetAnimBP); } } - else if(AddedMeshComponents.Num() > 1 && AddedMeshComponents.IsValidIndex(0)) + else if (AddedMeshComponents.Num() > 1 && AddedMeshComponents.IsValidIndex(0)) { NewSkeletalMeshComponent->SetMasterPoseComponent(AddedMeshComponents[0]); } @@ -97,7 +97,10 @@ USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* Sk void ARpmActor::LoadglTFAsset(UglTFRuntimeAsset* Asset) { - Asset->LoadSkeletalMeshRecursiveAsync("", {}, OnSkeletalMeshCallback, SkeletalMeshConfig ); + /*Asset->LoadSkeletalMeshRecursiveAsync("", {}, OnSkeletalMeshCallback, SkeletalMeshConfig);*/ + + auto mesh = Asset->LoadSkeletalMeshRecursive("", {}, SkeletalMeshConfig); + HandleSkeletalMeshLoaded(mesh); } void ARpmActor::HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh) @@ -107,24 +110,26 @@ void ARpmActor::HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh) CreateSkeletalMeshComponent(SkeletalMesh, "Asset"); } -void ARpmActor::OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedPtr HttpResponse, bool bIsSuccessful) +void ARpmActor::OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedPtr HttpResponse, + bool bIsSuccessful) { if (bIsSuccessful) - { + { glTFRuntimeConfig.TransformBaseType = EglTFRuntimeTransformBaseType::YForward; - UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(HttpResponse->GetContent(), glTFRuntimeConfig); + UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData( + HttpResponse->GetContent(), glTFRuntimeConfig); LoadglTFAsset(gltfAsset); } } void ARpmActor::LoadAsset(FAsset AssetData) { - if(Character.Id.IsEmpty()) + if (Character.Id.IsEmpty()) { UE_LOG(LogTemp, Warning, TEXT("Character Id is empty")); return; } - + PreviewAssetMap.Add(AssetData.Type, AssetData.Id); FCharacterPreviewRequest PreviewRequest; PreviewRequest.Id = Character.Id; diff --git a/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h index 1c7ec89..73a887d 100644 --- a/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h +++ b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h @@ -16,12 +16,13 @@ class RPMNEXTGEN_API FAssetLoader : public FWebApi public: FAssetLoader(); FAssetLoader(FglTFRuntimeConfig* Config); - virtual ~FAssetLoader(); + virtual ~FAssetLoader() override; void LoadGLBFromURL(const FString& URL); - + FOnAssetDataReceived OnAssetDataReceived; FOnAssetDownloaded OnAssetDownloaded; + protected: FglTFRuntimeConfig* GltfConfig; void virtual OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 75a86f1..658b83b 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -1,32 +1,89 @@ #include "EditorAssetLoader.h" +#include "AssetToolsModule.h" #include "glTFRuntimeAssetActor.h" #include "glTFRuntimeFunctionLibrary.h" +#include "RpmActor.h" #include "TransientObjectSaverLibrary.h" +#include "UEditorAssetNamer.h" #include "Animation/SkeletalMeshActor.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "Factories/PhysicsAssetFactory.h" FEditorAssetLoader::FEditorAssetLoader() { - OnAssetDownloaded.BindRaw(this, &FEditorAssetLoader::OnAssetDownloadComplete); } FEditorAssetLoader::~FEditorAssetLoader() { } -void FEditorAssetLoader::OnAssetDownloadComplete(FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful) +void FEditorAssetLoader::OnAssetDownloadComplete(FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful, + FString LoadedAssetId) { - if(bWasSuccessful) + if (bWasSuccessful) { - LoadGltfAssetToWorld(gltfAsset); - //SaveAsUAsset(gltfAsset, FilePath); + //LoadGltfAssetToWorld(gltfAsset); + gltfAsset->AddToRoot(); + SaveAsUAsset(gltfAsset, LoadedAssetId); + gltfAsset->RemoveFromRoot(); } } +void FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString LoadedAssetId) +{ + const FglTFRuntimeSkeletonConfig SkeletonConfig = FglTFRuntimeSkeletonConfig(); + USkeleton* Skeleton = gltfAsset->LoadSkeleton(0, SkeletonConfig); + + FglTFRuntimeSkeletalMeshConfig meshConfig = FglTFRuntimeSkeletalMeshConfig(); + meshConfig.Skeleton = Skeleton; + + USkeletalMesh* skeletalMesh = gltfAsset->LoadSkeletalMeshRecursive(TEXT(""), {}, meshConfig); + skeletalMesh->AddToRoot(); + skeletalMesh->SetSkeleton(Skeleton); + Skeleton->SetPreviewMesh(skeletalMesh); + + const FString CoreAssetPath = TEXT("/Game/ReadyPlayerMe/") + LoadedAssetId + "/"; + const FString SkeletonAssetPath = CoreAssetPath + LoadedAssetId + TEXT("_Skeleton"); + const FString SkeletalMeshAssetPath = CoreAssetPath + LoadedAssetId + TEXT("_SkeletalMesh"); + + const auto NameGenerator = NewObject(); + NameGenerator->SetPath(CoreAssetPath); + + FTransientObjectSaverMaterialNameGenerator MaterialNameGeneratorDelegate; + MaterialNameGeneratorDelegate.BindUFunction(NameGenerator, FName("GenerateMaterialName")); + FTransientObjectSaverTextureNameGenerator TextureNameGeneratorDelegate; + TextureNameGeneratorDelegate.BindUFunction(NameGenerator, FName("GenerateTextureName")); + + UTransientObjectSaverLibrary::SaveTransientSkeletalMesh(skeletalMesh, SkeletalMeshAssetPath, + SkeletonAssetPath, + TEXT(""), + MaterialNameGeneratorDelegate, + TextureNameGeneratorDelegate); + + UE_LOG(LogTemp, Log, TEXT("Character model saved: %s"), *LoadedAssetId); +} + +void FEditorAssetLoader::LoadGLBFromURLWithId(const FString& URL, FString LoadedAssetId) +{ + OnAssetDownloaded.BindLambda( + [LoadedAssetId, this](FString FilePath, UglTFRuntimeAsset* gltfAsset, + bool bWasSuccessful) + { + if (!gltfAsset) + { + UE_LOG(LogTemp, Log, TEXT("No gltf asset")); + return; + } + OnAssetDownloadComplete(FilePath, gltfAsset, bWasSuccessful, LoadedAssetId); + }); + LoadGLBFromURL(URL); +} + void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) { - if (!GEditor) + if (!GEditor) { UE_LOG(LogTemp, Error, TEXT("GEditor is not available.")); return; @@ -44,18 +101,15 @@ void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) { FTransform Transform = FTransform::Identity; // Spawn an actor of type AglTFRuntimeAssetActor in the editor world - AglTFRuntimeAssetActor* NewActor = EditorWorld->SpawnActorDeferred(AglTFRuntimeAssetActor::StaticClass(), Transform); + ARpmActor* NewActor = EditorWorld->SpawnActorDeferred(ARpmActor::StaticClass(), Transform); if (NewActor) { NewActor->SetFlags(RF_Transient); - NewActor->Asset = gltfAsset; - if(SkeletonToCopy) + + if (SkeletonToCopy) { NewActor->SkeletalMeshConfig.SkeletonConfig.CopyRotationsFrom = SkeletonToCopy; } - NewActor->bAllowSkeletalAnimations = false; - NewActor->bAllowNodeAnimations = false; - NewActor->StaticMeshConfig.bGenerateStaticMeshDescription = true; NewActor->FinishSpawning(Transform); NewActor->DispatchBeginPlay(); GEditor->SelectNone(true, true, true); @@ -65,33 +119,16 @@ void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) GEditor->SelectActor(NewActor, true, true); GEditor->EditorUpdateComponents(); + NewActor->LoadglTFAsset(gltfAsset); UE_LOG(LogTemp, Log, TEXT("Successfully loaded GLB asset into the editor world")); } else { UE_LOG(LogTemp, Error, TEXT("Failed to spawn AglTFRuntimeAssetActor in the editor world")); } - - } else { UE_LOG(LogTemp, Error, TEXT("Failed to load GLB asset from file")); } } - -void FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString Path) -{ - FglTFRuntimeSkeletonConfig skeletonConfig = FglTFRuntimeSkeletonConfig(); - if(SkeletonToCopy) - { - skeletonConfig.CopyRotationsFrom = SkeletonToCopy; - } - USkeleton* Skeleton = gltfAsset->LoadSkeleton(0, skeletonConfig); - UTransientObjectSaverLibrary::SaveTransientSkeleton(Skeleton,TEXT("/Game/ReadyPlayerMe/TestSkeleton")); - // FglTFRuntimeSkeletalMeshConfig meshConfig = FglTFRuntimeSkeletalMeshConfig(); - // USkeletalMesh* skeletalMesh = gltfAsset->LoadSkeletalMeshRecursive("", {}, meshConfig); - // const FTransientObjectSaverMaterialNameGenerator& MaterialNameGenerator = FTransientObjectSaverMaterialNameGenerator(); - // const FTransientObjectSaverTextureNameGenerator& TextureNameGenerator = FTransientObjectSaverTextureNameGenerator(); - // UTransientObjectSaverLibrary::SaveTransientSkeletalMesh(skeletalMesh, TEXT("/Game/ReadyPlayerMe/TestSkeletalMesh"), TEXT("/Game/ReadyPlayerMe/TestSkeleton"), TEXT("/Game/ReadyPlayerMe/TestPhysicsAsset"), MaterialNameGenerator, TextureNameGenerator); -} diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index 22e06c1..dde06fe 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -303,7 +303,7 @@ void SRpmDeveloperLoginWidget::OnLoadStyleClicked(const FString& StyleId) { AssetLoader = FEditorAssetLoader(); UE_LOG(LogTemp, Error, TEXT("Loading model from glb url %s"), *CharacterStyleAssets[StyleId].GlbUrl); - AssetLoader.LoadGLBFromURL(CharacterStyleAssets[StyleId].GlbUrl); + AssetLoader.LoadGLBFromURLWithId(CharacterStyleAssets[StyleId].GlbUrl, *StyleId); } EVisibility SRpmDeveloperLoginWidget::GetLoginViewVisibility() const diff --git a/Source/RpmNextGenEditor/Private/UEditorAssetNamer.cpp b/Source/RpmNextGenEditor/Private/UEditorAssetNamer.cpp new file mode 100644 index 0000000..c2dfe0c --- /dev/null +++ b/Source/RpmNextGenEditor/Private/UEditorAssetNamer.cpp @@ -0,0 +1,21 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "UEditorAssetNamer.h" + +void UUEditorAssetNamer::SetPath(FString NewPath) +{ + this->path = NewPath; +} + +FString UUEditorAssetNamer::GenerateMaterialName(UMaterialInterface* Material, const int32 MaterialIndex, + const FString& SlotName) +{ + return path + TEXT("Material_") + FString::FromInt(MaterialIndex); +} + +FString UUEditorAssetNamer::GenerateTextureName(UTexture* Texture, UMaterialInterface* Material, + const FString& MaterialPath, const FString& ParamName) +{ + return path + Material->GetName() + ParamName; +} diff --git a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h index ac2e63d..2554932 100644 --- a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h +++ b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h @@ -8,13 +8,13 @@ class RPMNEXTGENEDITOR_API FEditorAssetLoader : public FAssetLoader { public: - void OnAssetDownloadComplete(FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful); + void OnAssetDownloadComplete(FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful, + FString LoadedAssetId); FEditorAssetLoader(); virtual ~FEditorAssetLoader() override; - - void LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset); + void LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset); + void LoadGLBFromURLWithId(const FString& URL, const FString AssetId); void SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString Path); USkeleton* SkeletonToCopy; - }; diff --git a/Source/RpmNextGenEditor/Public/UEditorAssetNamer.h b/Source/RpmNextGenEditor/Public/UEditorAssetNamer.h new file mode 100644 index 0000000..cb49ccd --- /dev/null +++ b/Source/RpmNextGenEditor/Public/UEditorAssetNamer.h @@ -0,0 +1,27 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UObject/Object.h" +#include "UEditorAssetNamer.generated.h" + +/** + * + */ +UCLASS() +class RPMNEXTGENEDITOR_API UUEditorAssetNamer : public UObject +{ + GENERATED_BODY() + +public: + void SetPath(FString path); + UFUNCTION() + FString GenerateMaterialName(UMaterialInterface* Material, int32 MaterialIndex, const FString& SlotName); + UFUNCTION() + FString GenerateTextureName(UTexture* Texture, UMaterialInterface* Material, const FString& MaterialPath, + const FString& ParamName); + +private: + FString path; +}; From 05ec3f42d21b416f8a421cd1d87ac418b9918080 Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 22 Aug 2024 11:20:47 +0300 Subject: [PATCH 37/74] chore: folder reorg --- .../Private/RpmNextGenEditor.cpp | 34 ++--------------- .../{ => UI}/CharacterLoaderWidget.cpp | 2 +- .../Private/{ => UI}/LoginWindowCommands.cpp | 2 +- .../Private/{ => UI}/LoginWindowStyle.cpp | 2 +- .../{ => UI}/SRpmDeveloperLoginWidget.cpp | 38 +++++++++---------- .../Public/{ => UI}/CharacterLoaderWidget.h | 0 .../Public/{ => UI}/LoaderWindowCommands.cpp | 0 .../Public/{ => UI}/LoaderWindowCommands.h | 0 .../Public/{ => UI}/LoginWindowCommands.h | 0 .../Public/{ => UI}/LoginWindowStyle.h | 0 .../{ => UI}/SRpmDeveloperLoginWidget.h | 1 - 11 files changed, 26 insertions(+), 53 deletions(-) rename Source/RpmNextGenEditor/Private/{ => UI}/CharacterLoaderWidget.cpp (98%) rename Source/RpmNextGenEditor/Private/{ => UI}/LoginWindowCommands.cpp (87%) rename Source/RpmNextGenEditor/Private/{ => UI}/LoginWindowStyle.cpp (97%) rename Source/RpmNextGenEditor/Private/{ => UI}/SRpmDeveloperLoginWidget.cpp (95%) rename Source/RpmNextGenEditor/Public/{ => UI}/CharacterLoaderWidget.h (100%) rename Source/RpmNextGenEditor/Public/{ => UI}/LoaderWindowCommands.cpp (100%) rename Source/RpmNextGenEditor/Public/{ => UI}/LoaderWindowCommands.h (100%) rename Source/RpmNextGenEditor/Public/{ => UI}/LoginWindowCommands.h (100%) rename Source/RpmNextGenEditor/Public/{ => UI}/LoginWindowStyle.h (100%) rename Source/RpmNextGenEditor/Public/{ => UI}/SRpmDeveloperLoginWidget.h (98%) diff --git a/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp index 9073e7f..2f0fce8 100644 --- a/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp +++ b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp @@ -2,11 +2,11 @@ #include "RpmNextGenEditor.h" -#include "CharacterLoaderWidget.h" +#include "UI/CharacterLoaderWidget.h" #include "LevelEditor.h" -#include "LoaderWindowCommands.h" -#include "LoginWindowCommands.h" -#include "SRpmDeveloperLoginWidget.h" +#include "UI/LoaderWindowCommands.h" +#include "UI/LoginWindowCommands.h" +#include "UI/SRpmDeveloperLoginWidget.h" #include "Widgets/Docking/SDockTab.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Text/STextBlock.h" @@ -134,32 +134,6 @@ void FRpmNextGenEditorModule::PluginButtonClicked() FGlobalTabmanager::Get()->TryInvokeTab(TestWindowTabName); } -// void FRpmNextGenEditorModule::RegisterMenus() -// { -// // Owner will be used for cleanup in call to UToolMenus::UnregisterOwner -// FToolMenuOwnerScoped OwnerScoped(this); -// -// // Create a new main menu entry -// UToolMenu* MainMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu"); -// -// // Add a new section to the main menu for ReadyPlayerMe -// FToolMenuSection& Section = MainMenu->AddSection("ReadyPlayerMe", LOCTEXT("ReadyPlayerMeMenuSection", "Ready Player Me")); -// -// // Add menu entries to your custom section -// Section.AddMenuEntryWithCommandList(FLoginWindowCommands::Get().OpenPluginWindow, PluginCommands); -// Section.AddMenuEntryWithCommandList(FLoaderWindowCommands::Get().OpenPluginWindow, PluginCommands); -// -// // Add the section to the toolbar as well, if you want it to appear in the toolbar -// UToolMenu* ToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.LevelEditorToolBar"); -// { -// FToolMenuSection& ToolbarSection = ToolbarMenu->FindOrAddSection("ReadyPlayerMe"); -// { -// FToolMenuEntry& Entry = ToolbarSection.AddEntry(FToolMenuEntry::InitToolBarButton(FLoginWindowCommands::Get().OpenPluginWindow)); -// Entry.SetCommandList(PluginCommands); -// } -// } -// } - void FRpmNextGenEditorModule::OpenLoaderWindow() { diff --git a/Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp b/Source/RpmNextGenEditor/Private/UI/CharacterLoaderWidget.cpp similarity index 98% rename from Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp rename to Source/RpmNextGenEditor/Private/UI/CharacterLoaderWidget.cpp index 6f6bd76..3afbef9 100644 --- a/Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp +++ b/Source/RpmNextGenEditor/Private/UI/CharacterLoaderWidget.cpp @@ -1,4 +1,4 @@ -#include "CharacterLoaderWidget.h" +#include "UI/CharacterLoaderWidget.h" #include "Widgets/Input/SEditableTextBox.h" #include "Widgets/Input/SButton.h" diff --git a/Source/RpmNextGenEditor/Private/LoginWindowCommands.cpp b/Source/RpmNextGenEditor/Private/UI/LoginWindowCommands.cpp similarity index 87% rename from Source/RpmNextGenEditor/Private/LoginWindowCommands.cpp rename to Source/RpmNextGenEditor/Private/UI/LoginWindowCommands.cpp index 3b5f569..b182c6e 100644 --- a/Source/RpmNextGenEditor/Private/LoginWindowCommands.cpp +++ b/Source/RpmNextGenEditor/Private/UI/LoginWindowCommands.cpp @@ -1,4 +1,4 @@ -#include "LoginWindowCommands.h" +#include "UI/LoginWindowCommands.h" #define LOCTEXT_NAMESPACE "FRpmNextGenEditorModule" diff --git a/Source/RpmNextGenEditor/Private/LoginWindowStyle.cpp b/Source/RpmNextGenEditor/Private/UI/LoginWindowStyle.cpp similarity index 97% rename from Source/RpmNextGenEditor/Private/LoginWindowStyle.cpp rename to Source/RpmNextGenEditor/Private/UI/LoginWindowStyle.cpp index 6fc2e8b..1d86263 100644 --- a/Source/RpmNextGenEditor/Private/LoginWindowStyle.cpp +++ b/Source/RpmNextGenEditor/Private/UI/LoginWindowStyle.cpp @@ -1,4 +1,4 @@ -#include "LoginWindowStyle.h" +#include "UI/LoginWindowStyle.h" #include "Styling/SlateStyleRegistry.h" #include "Framework/Application/SlateApplication.h" #include "Slate/SlateGameResources.h" diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp similarity index 95% rename from Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp rename to Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp index 22e06c1..385a8db 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp @@ -1,7 +1,7 @@ // Fill out your copyright notice in the Description page of Project Settings. -#include "SRpmDeveloperLoginWidget.h" +#include "UI/SRpmDeveloperLoginWidget.h" #include "Auth/DevAuthTokenCache.h" #include "EditorCache.h" #include "SlateOptMacros.h" @@ -24,7 +24,6 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) DevAuthTokenCache::SetAuthData(AuthData); bIsLoggedIn = AuthData.IsValid(); - Settings = GetMutableDefault(); UserName = AuthData.Name; ChildSlot @@ -171,14 +170,7 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) [ SAssignNew(ContentBox, SVerticalBox) ] - ] - // + SVerticalBox::Slot() - // .AutoHeight() - // [ - // - // SAssignNew(ContentBox, SVerticalBox) - // .Visibility(this, &SRpmDeveloperLoginWidget::GetLoggedInViewVisibility) - // ] + ] ]; EmailTextBox->SetText(FText::FromString(EditorCache::GetString(CacheKeyEmail))); @@ -224,8 +216,15 @@ void SRpmDeveloperLoginWidget::Initialize() bIsInitialized = true; if (bIsLoggedIn) { + UE_LOG(LogTemp, Warning, TEXT("Logged in getting org list")); GetOrgList(); } + else + { + UE_LOG(LogTemp, Warning, TEXT("Not logged in, logging out")); + + OnLogoutClicked(); + } } SRpmDeveloperLoginWidget::~SRpmDeveloperLoginWidget() @@ -241,7 +240,6 @@ void SRpmDeveloperLoginWidget::ClearLoadedCharacterModelImages() } } - void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) { TSharedPtr ImageWidget; @@ -366,6 +364,7 @@ void SRpmDeveloperLoginWidget::HandleOrganizationListResponse(const FOrganizatio UE_LOG(LogTemp, Error, TEXT("No organizations found")); return; } + UE_LOG(LogTemp, Warning, TEXT("Orgs found")); FApplicationListRequest Request; Request.Params.Add("organizationId", Response.Data[0].Id); @@ -383,13 +382,16 @@ void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationL { if (bWasSuccessful) { + UE_LOG(LogTemp, Warning, TEXT("HandleApplicationListResponse")); + + URpmDeveloperSettings* RpmSettings = GetMutableDefault(); UserApplications = Response.Data; FString Active; TArray Items; for (const FApplication& App : UserApplications) { Items.Add(App.Name); - if (App.Id == Settings->ApplicationId) + if (App.Id == RpmSettings->ApplicationId) { Active = App.Name; } @@ -405,6 +407,7 @@ void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationL else { UE_LOG(LogTemp, Error, TEXT("Failed to list applications")); + } LoadBaseModelList(); } @@ -446,9 +449,8 @@ void SRpmDeveloperLoginWidget::OnComboBoxSelectionChanged(TSharedPtr Ne FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() { - // TODO find a better way to get the latest settings - Settings = GetMutableDefault(); - Settings->SetupDemoAccount(); + URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + RpmSettings->SetupDemoAccount(); FDeveloperAuth AuthData = FDeveloperAuth(); AuthData.Name = DemoUserName; AuthData.IsDemo = true; @@ -466,10 +468,8 @@ FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() FReply SRpmDeveloperLoginWidget::OnLogoutClicked() { - // TODO find a better way to get the latest settings - - Settings = GetMutableDefault(); - Settings->Reset(); + URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + RpmSettings->Reset(); // Clear the content box to remove all child widgets if (ContentBox.IsValid()) diff --git a/Source/RpmNextGenEditor/Public/CharacterLoaderWidget.h b/Source/RpmNextGenEditor/Public/UI/CharacterLoaderWidget.h similarity index 100% rename from Source/RpmNextGenEditor/Public/CharacterLoaderWidget.h rename to Source/RpmNextGenEditor/Public/UI/CharacterLoaderWidget.h diff --git a/Source/RpmNextGenEditor/Public/LoaderWindowCommands.cpp b/Source/RpmNextGenEditor/Public/UI/LoaderWindowCommands.cpp similarity index 100% rename from Source/RpmNextGenEditor/Public/LoaderWindowCommands.cpp rename to Source/RpmNextGenEditor/Public/UI/LoaderWindowCommands.cpp diff --git a/Source/RpmNextGenEditor/Public/LoaderWindowCommands.h b/Source/RpmNextGenEditor/Public/UI/LoaderWindowCommands.h similarity index 100% rename from Source/RpmNextGenEditor/Public/LoaderWindowCommands.h rename to Source/RpmNextGenEditor/Public/UI/LoaderWindowCommands.h diff --git a/Source/RpmNextGenEditor/Public/LoginWindowCommands.h b/Source/RpmNextGenEditor/Public/UI/LoginWindowCommands.h similarity index 100% rename from Source/RpmNextGenEditor/Public/LoginWindowCommands.h rename to Source/RpmNextGenEditor/Public/UI/LoginWindowCommands.h diff --git a/Source/RpmNextGenEditor/Public/LoginWindowStyle.h b/Source/RpmNextGenEditor/Public/UI/LoginWindowStyle.h similarity index 100% rename from Source/RpmNextGenEditor/Public/LoginWindowStyle.h rename to Source/RpmNextGenEditor/Public/UI/LoginWindowStyle.h diff --git a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h similarity index 98% rename from Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h rename to Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h index da22dff..733fe26 100644 --- a/Source/RpmNextGenEditor/Public/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h @@ -52,7 +52,6 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget bool bIsInitialized = false; FString UserName; TArray UserApplications; - URpmDeveloperSettings* Settings; FText GetWelcomeText() const; FString DemoUserName = TEXT("Guest user"); FText GetSelectedComboBoxItemText() const; From c835dbdf558464a6f69871705b885aa3eecef87c Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 22 Aug 2024 11:21:24 +0300 Subject: [PATCH 38/74] chore: update dev settings --- .../Public/Settings/RpmDeveloperSettings.h | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h index 667bcde..aebe2a2 100644 --- a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h +++ b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h @@ -13,28 +13,35 @@ UCLASS(config = Game, defaultconfig, meta = (DisplayName = "Ready Player Me")) class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings { GENERATED_BODY() + + UPROPERTY(VisibleAnywhere, Config, Category = "Auth Settings", meta = (ReadOnly = "true", ToolTip = "Base URL for requests.")) + FString ApiBaseUrl; public: URpmDeveloperSettings(); - UPROPERTY(EditAnywhere, Config, Category = "API") + UPROPERTY(VisibleAnywhere, Config, Category = "Auth Settings", meta = (ReadOnly = "true", ToolTip = "Base URL for authentication requests.")) FString ApiBaseAuthUrl; - - UPROPERTY(EditAnywhere, Config, Category = "API") + + UPROPERTY(EditAnywhere, Config, Category = "Auth Settings", meta = (ToolTip = "Application ID used for authentication.")) FString ApplicationId; - UPROPERTY(EditAnywhere, Config, Category = "API") + UPROPERTY(EditAnywhere, Config, Category = "Auth Settings", meta = (ToolTip = "API key used for authentication.")) FString ApiKey; - UPROPERTY(EditAnywhere, Config, Category = "API") + UPROPERTY(EditAnywhere, Config, Category = "Auth Settings", meta = (ToolTip = "Proxy URL for API requests. If empty, the base URL will be used.")) FString ApiProxyUrl; void SetupDemoAccount(); void Reset(); FString GetApiBaseUrl(); + bool IsValid() const + { + return !ApplicationId.IsEmpty() && (!ApiKey.IsEmpty() || !ApiProxyUrl.IsEmpty()); + } + private: - FString ApiBaseUrl; const FString DemoAppId = TEXT("665e05a50c62c921e5a6ab84"); const FString DemoProxyUrl = TEXT("https://api.readyplayer.me/demo"); }; From 2792eef1be6209b1adf12a62eda35828fca5a4ab Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 22 Aug 2024 12:02:06 +0300 Subject: [PATCH 39/74] feat: added invalid data checks --- Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp | 9 +++++++-- .../RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp | 8 +++++++- .../Private/Auth/DeveloperTokenAuthStrategy.cpp | 9 ++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp index 54e5bf3..b204b39 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp @@ -11,10 +11,15 @@ FAssetApi::FAssetApi() void FAssetApi::ListAssetsAsync(const FAssetListRequest& Request) { - // TODO find better way to get settings (or move to editor only code) URpmDeveloperSettings* Settings = GetMutableDefault(); ApiBaseUrl = Settings->GetApiBaseUrl(); - + + if(Settings->ApplicationId.IsEmpty()) + { + UE_LOG(LogTemp, Error, TEXT("Application ID is empty")); + OnListAssetsResponse.ExecuteIfBound(FAssetListResponse(), false); + return; + } FString QueryString = Request.BuildQueryString(); const FString Url = FString::Printf(TEXT("%s/v1/phoenix-assets%s"), *ApiBaseUrl, *QueryString); FApiRequest ApiRequest = FApiRequest(); diff --git a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp index 5d954cf..af50abf 100644 --- a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp +++ b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp @@ -9,7 +9,13 @@ FApiKeyAuthStrategy::FApiKeyAuthStrategy() void FApiKeyAuthStrategy::AddAuthToRequest(TSharedPtr Request) { - URpmDeveloperSettings *Settings = GetMutableDefault(); + URpmDeveloperSettings *Settings = GetMutableDefault(); + if(Settings->ApiKey.IsEmpty()) + { + UE_LOG(LogTemp, Error, TEXT("API Key is empty")); + OnAuthComplete.ExecuteIfBound(false); + return; + } Request->Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); OnAuthComplete.ExecuteIfBound(true); } diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp index 9a3d838..7193b1f 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp @@ -14,11 +14,18 @@ DeveloperTokenAuthStrategy::DeveloperTokenAuthStrategy() void DeveloperTokenAuthStrategy::AddAuthToRequest(TSharedPtr Request) { const FString Key = TEXT("Authorization"); + const FString Token = DevAuthTokenCache::GetAuthData().Token; + if(Token.IsEmpty()) + { + UE_LOG(LogTemp, Error, TEXT("Token is empty")); + OnAuthComplete.ExecuteIfBound(false); + return; + } if (Request->Headers.Contains(Key)) { Request->Headers.Remove(Key); } - Request->Headers.Add(Key, FString::Printf(TEXT("Bearer %s"), *DevAuthTokenCache::GetAuthData().Token)); + Request->Headers.Add(Key, FString::Printf(TEXT("Bearer %s"), *Token)); OnAuthComplete.ExecuteIfBound(true); } From 1db5ca57ac9114e927e0d844816475fcceb03baf Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 23 Aug 2024 09:59:59 +0300 Subject: [PATCH 40/74] chore: cleanup --- .../Private/Api/Assets/AssetLoader.cpp | 31 ++++++------------- .../Private/Auth/DevAuthTokenCache.cpp | 24 +++++++------- .../Private/EditorAssetLoader.cpp | 14 ++------- .../RpmNextGenEditor/Private/EditorCache.cpp | 18 +++++------ .../Private/UI/SRpmDeveloperLoginWidget.cpp | 4 +-- Source/RpmNextGenEditor/Public/EditorCache.h | 10 ++---- 6 files changed, 39 insertions(+), 62 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp index ae20a3f..824b09d 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp @@ -1,21 +1,16 @@ #include "Api/Assets/AssetLoader.h" #include "HttpModule.h" #include "glTFRuntime/Public/glTFRuntimeFunctionLibrary.h" -#include "Engine/World.h" #include "Interfaces/IHttpResponse.h" #include "Misc/FileHelper.h" #include "HAL/PlatformFilemanager.h" -#include "Engine/World.h" -#include "Engine/Engine.h" -#include "GameFramework/Actor.h" FAssetLoader::FAssetLoader() { GltfConfig = new FglTFRuntimeConfig(); GltfConfig->TransformBaseType = EglTFRuntimeTransformBaseType::YForward; - - // Set the download directory within the project DownloadDirectory = FPaths::ProjectContentDir() / TEXT("ReadyPlayerMe/"); + // Ensure the directory exists IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); if (!PlatformFile.DirectoryExists(*DownloadDirectory)) @@ -47,31 +42,25 @@ void FAssetLoader::OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr if (bWasSuccessful && Response.IsValid()) { - TArray Content = Response->GetContent(); + const TArray Content = Response->GetContent(); OnAssetDataReceived.ExecuteIfBound(Content, true); + + const FString FileName = FPaths::GetCleanFilename(Request->GetURL()); + const FString FilePath = DownloadDirectory / FileName; - FString FileName = FPaths::GetCleanFilename(Request->GetURL()); - FString FilePath = DownloadDirectory / FileName; - - // Write the file to disk if (FFileHelper::SaveArrayToFile(Response->GetContent(), *FilePath)) { UE_LOG(LogTemp, Log, TEXT("Downloaded GLB file to %s"), *FilePath); UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(Content, *GltfConfig); OnAssetDownloaded.ExecuteIfBound(FilePath, gltfAsset, true); - } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to save GLB file to %s"), *FilePath); + return; } + UE_LOG(LogTemp, Error, TEXT("Failed to save GLB file to %s"), *FilePath); + OnAssetDownloaded.ExecuteIfBound(FString(), nullptr, false); + return; } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to download GLB file from URL")); - } + UE_LOG(LogTemp, Error, TEXT("Failed to download GLB file from URL")); OnAssetDataReceived.ExecuteIfBound(TArray(), false); OnAssetDownloaded.ExecuteIfBound(FString(), nullptr, false); } - - diff --git a/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp b/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp index e348e6c..e8c2dcf 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp @@ -9,10 +9,10 @@ void DevAuthTokenCache::Initialize() { if (!bIsInitialized) { - AuthData.Name = EditorCache::GetString(CacheKeyName, FString()); - AuthData.Token = EditorCache::GetString(CacheKeyToken, FString()); - AuthData.RefreshToken = EditorCache::GetString(CacheKeyRefreshToken, FString()); - AuthData.IsDemo = EditorCache::GetBool(CacheKeyIsDemo, false); + AuthData.Name = FEditorCache::GetString(CacheKeyName, FString()); + AuthData.Token = FEditorCache::GetString(CacheKeyToken, FString()); + AuthData.RefreshToken = FEditorCache::GetString(CacheKeyRefreshToken, FString()); + AuthData.IsDemo = FEditorCache::GetBool(CacheKeyIsDemo, false); if (!AuthData.IsValid()) { @@ -33,17 +33,17 @@ FDeveloperAuth DevAuthTokenCache::GetAuthData() void DevAuthTokenCache::SetAuthData(const FDeveloperAuth& DevAuthData) { AuthData = DevAuthData; - EditorCache::SetString( CacheKeyName, AuthData.Name); - EditorCache::SetString( CacheKeyToken, AuthData.Token); - EditorCache::SetString( CacheKeyRefreshToken, AuthData.RefreshToken); - EditorCache::SetBool( CacheKeyIsDemo, AuthData.IsDemo); + FEditorCache::SetString( CacheKeyName, AuthData.Name); + FEditorCache::SetString( CacheKeyToken, AuthData.Token); + FEditorCache::SetString( CacheKeyRefreshToken, AuthData.RefreshToken); + FEditorCache::SetBool( CacheKeyIsDemo, AuthData.IsDemo); } void DevAuthTokenCache::ClearAuthData() { AuthData = FDeveloperAuth(); - EditorCache::RemoveKey(CacheKeyName); - EditorCache::RemoveKey(CacheKeyToken); - EditorCache::RemoveKey(CacheKeyRefreshToken); - EditorCache::RemoveKey(CacheKeyIsDemo); + FEditorCache::RemoveKey(CacheKeyName); + FEditorCache::RemoveKey(CacheKeyToken); + FEditorCache::RemoveKey(CacheKeyRefreshToken); + FEditorCache::RemoveKey(CacheKeyIsDemo); } diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 75a86f1..55a34c5 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -66,13 +66,10 @@ void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) GEditor->EditorUpdateComponents(); UE_LOG(LogTemp, Log, TEXT("Successfully loaded GLB asset into the editor world")); + return; } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to spawn AglTFRuntimeAssetActor in the editor world")); - } - - + + UE_LOG(LogTemp, Error, TEXT("Failed to spawn AglTFRuntimeAssetActor in the editor world")); } else { @@ -89,9 +86,4 @@ void FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString Path } USkeleton* Skeleton = gltfAsset->LoadSkeleton(0, skeletonConfig); UTransientObjectSaverLibrary::SaveTransientSkeleton(Skeleton,TEXT("/Game/ReadyPlayerMe/TestSkeleton")); - // FglTFRuntimeSkeletalMeshConfig meshConfig = FglTFRuntimeSkeletalMeshConfig(); - // USkeletalMesh* skeletalMesh = gltfAsset->LoadSkeletalMeshRecursive("", {}, meshConfig); - // const FTransientObjectSaverMaterialNameGenerator& MaterialNameGenerator = FTransientObjectSaverMaterialNameGenerator(); - // const FTransientObjectSaverTextureNameGenerator& TextureNameGenerator = FTransientObjectSaverTextureNameGenerator(); - // UTransientObjectSaverLibrary::SaveTransientSkeletalMesh(skeletalMesh, TEXT("/Game/ReadyPlayerMe/TestSkeletalMesh"), TEXT("/Game/ReadyPlayerMe/TestSkeleton"), TEXT("/Game/ReadyPlayerMe/TestPhysicsAsset"), MaterialNameGenerator, TextureNameGenerator); } diff --git a/Source/RpmNextGenEditor/Private/EditorCache.cpp b/Source/RpmNextGenEditor/Private/EditorCache.cpp index 0e5227e..e7f8f93 100644 --- a/Source/RpmNextGenEditor/Private/EditorCache.cpp +++ b/Source/RpmNextGenEditor/Private/EditorCache.cpp @@ -3,31 +3,31 @@ #define RPM_CACHE_SECTION TEXT("ReadyPlayerMeCache") -void EditorCache::SetString( const FString& Key, const FString& Value) +void FEditorCache::SetString( const FString& Key, const FString& Value) { GConfig->SetString(RPM_CACHE_SECTION, *Key, *Value, GEditorPerProjectIni); GConfig->Flush(false, GEditorPerProjectIni); } -void EditorCache::SetInt(const FString& Key, int32 Value) +void FEditorCache::SetInt(const FString& Key, int32 Value) { GConfig->SetInt(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni); GConfig->Flush(false, GEditorPerProjectIni); } -void EditorCache::SetFloat(const FString& Key, float Value) +void FEditorCache::SetFloat(const FString& Key, float Value) { GConfig->SetFloat(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni); GConfig->Flush(false, GEditorPerProjectIni); } -void EditorCache::SetBool(const FString& Key, bool Value) +void FEditorCache::SetBool(const FString& Key, bool Value) { GConfig->SetBool(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni); GConfig->Flush(false, GEditorPerProjectIni); } -FString EditorCache::GetString(const FString& Key, const FString& DefaultValue) +FString FEditorCache::GetString(const FString& Key, const FString& DefaultValue) { FString Value; if (GConfig->GetString(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni)) @@ -37,7 +37,7 @@ FString EditorCache::GetString(const FString& Key, const FString& DefaultValue) return DefaultValue; } -int32 EditorCache::GetInt(const FString& Key, int32 DefaultValue) +int32 FEditorCache::GetInt(const FString& Key, int32 DefaultValue) { int32 Value; if (GConfig->GetInt(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni)) @@ -47,7 +47,7 @@ int32 EditorCache::GetInt(const FString& Key, int32 DefaultValue) return DefaultValue; } -float EditorCache::GetFloat(const FString& Key, float DefaultValue) +float FEditorCache::GetFloat(const FString& Key, float DefaultValue) { float Value; if (GConfig->GetFloat(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni)) @@ -57,7 +57,7 @@ float EditorCache::GetFloat(const FString& Key, float DefaultValue) return DefaultValue; } -bool EditorCache::GetBool(const FString& Key, bool DefaultValue) +bool FEditorCache::GetBool(const FString& Key, bool DefaultValue) { bool Value; if (GConfig->GetBool(RPM_CACHE_SECTION, *Key, Value, GEditorPerProjectIni)) @@ -67,7 +67,7 @@ bool EditorCache::GetBool(const FString& Key, bool DefaultValue) return DefaultValue; } -void EditorCache::RemoveKey(const FString& Key) +void FEditorCache::RemoveKey(const FString& Key) { GConfig->RemoveKey(RPM_CACHE_SECTION, *Key, GEditorPerProjectIni); GConfig->Flush(false, GEditorPerProjectIni); diff --git a/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp index 385a8db..d06359e 100644 --- a/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp @@ -173,7 +173,7 @@ void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) ] ]; - EmailTextBox->SetText(FText::FromString(EditorCache::GetString(CacheKeyEmail))); + EmailTextBox->SetText(FText::FromString(FEditorCache::GetString(CacheKeyEmail))); Initialize(); } @@ -323,7 +323,7 @@ FReply SRpmDeveloperLoginWidget::OnLoginClicked() { FString Email = EmailTextBox->GetText().ToString(); FString Password = PasswordTextBox->GetText().ToString(); - EditorCache::SetString(CacheKeyEmail, Email); + FEditorCache::SetString(CacheKeyEmail, Email); Email = Email.TrimStartAndEnd(); Password = Password.TrimStartAndEnd(); DeveloperAccountApi->SetAuthenticationStrategy(new DeveloperTokenAuthStrategy()); diff --git a/Source/RpmNextGenEditor/Public/EditorCache.h b/Source/RpmNextGenEditor/Public/EditorCache.h index ce52318..b1ae63e 100644 --- a/Source/RpmNextGenEditor/Public/EditorCache.h +++ b/Source/RpmNextGenEditor/Public/EditorCache.h @@ -2,22 +2,18 @@ #include "CoreMinimal.h" -class RPMNEXTGENEDITOR_API EditorCache +class RPMNEXTGENEDITOR_API FEditorCache { public: - // Set methods static void SetString(const FString& Key, const FString& Value); static void SetInt(const FString& Key, int32 Value); static void SetFloat(const FString& Key, float Value); static void SetBool(const FString& Key, bool Value); - - // Get methods + static FString GetString(const FString& Key, const FString& DefaultValue = TEXT("")); static int32 GetInt(const FString& Key, int32 DefaultValue = 0); static float GetFloat(const FString& Key, float DefaultValue = 0.0f); static bool GetBool(const FString& Key, bool DefaultValue = false); - - // Remove key - static void RemoveKey(const FString& Key); + static void RemoveKey(const FString& Key); }; From a9c1d3164a0349dfacfd9fa9eab3dd8c0faad3b6 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 23 Aug 2024 11:03:13 +0300 Subject: [PATCH 41/74] chore: refactoring --- Content/Samples/BasicUI/RpmBasicUILevel.umap | Bin 71753 -> 71753 bytes RpmNextGen.uplugin | 4 +- .../Private/Auth/DevAuthTokenCache.cpp | 13 +-- .../Private/Auth/DeveloperAuthApi.cpp | 5 +- .../Auth/DeveloperTokenAuthStrategy.cpp | 10 +-- .../DeveloperAccountApi.cpp | 6 +- .../Private/RpmNextGenEditor.cpp | 37 ++++---- .../Private/UI/CharacterLoaderWidget.cpp | 11 +-- .../Private/UI/SRpmDeveloperLoginWidget.cpp | 61 ++++++------- .../Public/Auth/DevAuthTokenCache.h | 57 +----------- .../Public/Auth/DeveloperAccountApi.h | 85 ------------------ .../Public/Auth/Models/DeveloperAuth.h | 57 ++++++++++++ .../Auth/{ => Models}/DeveloperLoginRequest.h | 0 .../{ => Models}/DeveloperLoginResponse.h | 0 .../DeveloperAccounts/DeveloperAccountApi.h | 29 ++++++ .../Models/ApplicationListRequest.h | 14 +++ .../Models/ApplicationListResponse.h | 24 +++++ .../Models/OrganizationListRequest.h | 14 +++ .../Models/OrganizationListResponse.h | 26 ++++++ .../Public/EditorAssetLoader.h | 1 - .../Public/UI/SRpmDeveloperLoginWidget.h | 3 +- 21 files changed, 237 insertions(+), 220 deletions(-) rename Source/RpmNextGenEditor/Private/{Auth => DeveloperAccounts}/DeveloperAccountApi.cpp (90%) delete mode 100644 Source/RpmNextGenEditor/Public/Auth/DeveloperAccountApi.h create mode 100644 Source/RpmNextGenEditor/Public/Auth/Models/DeveloperAuth.h rename Source/RpmNextGenEditor/Public/Auth/{ => Models}/DeveloperLoginRequest.h (100%) rename Source/RpmNextGenEditor/Public/Auth/{ => Models}/DeveloperLoginResponse.h (100%) create mode 100644 Source/RpmNextGenEditor/Public/DeveloperAccounts/DeveloperAccountApi.h create mode 100644 Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/ApplicationListRequest.h create mode 100644 Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/ApplicationListResponse.h create mode 100644 Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/OrganizationListRequest.h create mode 100644 Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/OrganizationListResponse.h diff --git a/Content/Samples/BasicUI/RpmBasicUILevel.umap b/Content/Samples/BasicUI/RpmBasicUILevel.umap index 946bf3a67a6f3e7ff84216dcae252c9e25277793..170939926139bc054b1ad3a879e01a29d4067f87 100644 GIT binary patch delta 97 zcmV-n0G|KJums7l1hAd~5XHlQs4vpEONg#gtd$U7ZL_Wcvz`kwGc7PUE-*4KH8-|COnC1|(^W|p&lSb&n5K%mwqP=LcN3VT+bGNjq3zH$aIFs;360>9J F<}R^-E|vfQ diff --git a/RpmNextGen.uplugin b/RpmNextGen.uplugin index e993a9a..7fa4776 100644 --- a/RpmNextGen.uplugin +++ b/RpmNextGen.uplugin @@ -3,10 +3,10 @@ "Version": 1, "VersionName": "1.0", "FriendlyName": "RpmNextGen", - "Description": "", + "Description": "Ready Player Me Next Gen SDK", "Category": "Other", "CreatedBy": "Ready Player Me", - "CreatedByURL": "", + "CreatedByURL": "https://readyplayer.me", "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", diff --git a/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp b/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp index e8c2dcf..d30fba9 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DevAuthTokenCache.cpp @@ -1,11 +1,12 @@ #include "Auth/DevAuthTokenCache.h" #include "EditorCache.h" +#include "Auth/Models/DeveloperAuth.h" #include "Misc/ConfigCacheIni.h" -FDeveloperAuth DevAuthTokenCache::AuthData = FDeveloperAuth(); -bool DevAuthTokenCache::bIsInitialized = false; +FDeveloperAuth FDevAuthTokenCache::AuthData = FDeveloperAuth(); +bool FDevAuthTokenCache::bIsInitialized = false; -void DevAuthTokenCache::Initialize() +void FDevAuthTokenCache::Initialize() { if (!bIsInitialized) { @@ -24,13 +25,13 @@ void DevAuthTokenCache::Initialize() } } -FDeveloperAuth DevAuthTokenCache::GetAuthData() +FDeveloperAuth FDevAuthTokenCache::GetAuthData() { Initialize(); return AuthData; } -void DevAuthTokenCache::SetAuthData(const FDeveloperAuth& DevAuthData) +void FDevAuthTokenCache::SetAuthData(const FDeveloperAuth& DevAuthData) { AuthData = DevAuthData; FEditorCache::SetString( CacheKeyName, AuthData.Name); @@ -39,7 +40,7 @@ void DevAuthTokenCache::SetAuthData(const FDeveloperAuth& DevAuthData) FEditorCache::SetBool( CacheKeyIsDemo, AuthData.IsDemo); } -void DevAuthTokenCache::ClearAuthData() +void FDevAuthTokenCache::ClearAuthData() { AuthData = FDeveloperAuth(); FEditorCache::RemoveKey(CacheKeyName); diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp index 0e93af5..edce232 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp @@ -1,6 +1,7 @@ #include "Auth/DeveloperAuthApi.h" -#include "Auth/DeveloperLoginRequest.h" -#include "Auth/DeveloperLoginResponse.h" + +#include "Auth/Models/DeveloperLoginRequest.h" +#include "Auth/Models/DeveloperLoginResponse.h" #include "Settings/RpmDeveloperSettings.h" FDeveloperAuthApi::FDeveloperAuthApi() diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp index 7193b1f..5920821 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp @@ -14,7 +14,7 @@ DeveloperTokenAuthStrategy::DeveloperTokenAuthStrategy() void DeveloperTokenAuthStrategy::AddAuthToRequest(TSharedPtr Request) { const FString Key = TEXT("Authorization"); - const FString Token = DevAuthTokenCache::GetAuthData().Token; + const FString Token = FDevAuthTokenCache::GetAuthData().Token; if(Token.IsEmpty()) { UE_LOG(LogTemp, Error, TEXT("Token is empty")); @@ -33,8 +33,8 @@ void DeveloperTokenAuthStrategy::AddAuthToRequest(TSharedPtr Reques void DeveloperTokenAuthStrategy::TryRefresh(TSharedPtr Request) { FRefreshTokenRequest RefreshRequest; - RefreshRequest.Data.Token = DevAuthTokenCache::GetAuthData().Token; - RefreshRequest.Data.RefreshToken = DevAuthTokenCache::GetAuthData().RefreshToken; + RefreshRequest.Data.Token = FDevAuthTokenCache::GetAuthData().Token; + RefreshRequest.Data.RefreshToken = FDevAuthTokenCache::GetAuthData().RefreshToken; RefreshTokenAsync(RefreshRequest); } @@ -43,10 +43,10 @@ void DeveloperTokenAuthStrategy::OnRefreshTokenResponse(const FRefreshTokenRespo { if (bWasSuccessful && !Response.Data.Token.IsEmpty()) { - FDeveloperAuth DeveloperAuth = DevAuthTokenCache::GetAuthData(); + FDeveloperAuth DeveloperAuth = FDevAuthTokenCache::GetAuthData(); DeveloperAuth.Token = Response.Data.Token; DeveloperAuth.RefreshToken = Response.Data.RefreshToken; - DevAuthTokenCache::SetAuthData(DeveloperAuth); + FDevAuthTokenCache::SetAuthData(DeveloperAuth); OnTokenRefreshed.ExecuteIfBound(Response.Data, true); return; } diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp b/Source/RpmNextGenEditor/Private/DeveloperAccounts/DeveloperAccountApi.cpp similarity index 90% rename from Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp rename to Source/RpmNextGenEditor/Private/DeveloperAccounts/DeveloperAccountApi.cpp index 2a2a8b1..1df33c1 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperAccountApi.cpp +++ b/Source/RpmNextGenEditor/Private/DeveloperAccounts/DeveloperAccountApi.cpp @@ -1,5 +1,9 @@ -#include "Auth/DeveloperAccountApi.h" +#include "DeveloperAccounts/DeveloperAccountApi.h" #include "JsonObjectConverter.h" +#include "DeveloperAccounts/Models/ApplicationListRequest.h" +#include "DeveloperAccounts/Models/ApplicationListResponse.h" +#include "DeveloperAccounts/Models/OrganizationListRequest.h" +#include "DeveloperAccounts/Models/OrganizationListResponse.h" #include "Settings/RpmDeveloperSettings.h" FDeveloperAccountApi::FDeveloperAccountApi(IAuthenticationStrategy* InAuthenticationStrategy) : FWebApiWithAuth(InAuthenticationStrategy) diff --git a/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp index 2f0fce8..fa252da 100644 --- a/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp +++ b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp @@ -32,20 +32,22 @@ void FRpmNextGenEditorModule::StartupModule() FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::PluginButtonClicked), FCanExecuteAction()); - PluginCommands->MapAction( - FLoaderWindowCommands::Get().OpenPluginWindow, - FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::OpenLoaderWindow), - FCanExecuteAction()); + // Don't show Loader window in the menu + // PluginCommands->MapAction( + // FLoaderWindowCommands::Get().OpenPluginWindow, + // FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::OpenLoaderWindow), + // FCanExecuteAction()); UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FRpmNextGenEditorModule::RegisterMenus)); FGlobalTabmanager::Get()->RegisterNomadTabSpawner(TestWindowTabName, FOnSpawnTab::CreateRaw(this, &FRpmNextGenEditorModule::OnSpawnPluginTab)) - .SetDisplayName(LOCTEXT("FRpmDevLoginWindow", "RPM Dev Login")) - .SetMenuType(ETabSpawnerMenuType::Hidden); - - FGlobalTabmanager::Get()->RegisterNomadTabSpawner(NewWindowTabName, FOnSpawnTab::CreateRaw(this, &FRpmNextGenEditorModule::OnSpawnLoaderWindow)) - .SetDisplayName(LOCTEXT("CustomEditorWindow", "Custom Editor Window")) + .SetDisplayName(LOCTEXT("DeveloperLoginWidget", "RPM Dev Login")) .SetMenuType(ETabSpawnerMenuType::Hidden); + + // Don't show Loader window in the menu + // FGlobalTabmanager::Get()->RegisterNomadTabSpawner(NewWindowTabName, FOnSpawnTab::CreateRaw(this, &FRpmNextGenEditorModule::OnSpawnLoaderWindow)) + // .SetDisplayName(LOCTEXT("CharacterLoaderWidget", "Avatar Loader")) + // .SetMenuType(ETabSpawnerMenuType::Hidden); } void FRpmNextGenEditorModule::RegisterMenus() @@ -80,14 +82,15 @@ void FRpmNextGenEditorModule::FillReadyPlayerMeMenu(UToolMenu* Menu) FSlateIcon(), FUIAction(FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::PluginButtonClicked)) ); - - Section.AddMenuEntry( - "OpenLoaderWindow", - LOCTEXT("OpenLoaderWindow", "Glb Loader"), - LOCTEXT("OpenLoaderWindowToolTip", "Glb Loader Window."), - FSlateIcon(), - FUIAction(FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::OpenLoaderWindow)) - ); + + // Don't show Loader window in the menu + // Section.AddMenuEntry( + // "OpenLoaderWindow", + // LOCTEXT("OpenLoaderWindow", "Glb Loader"), + // LOCTEXT("OpenLoaderWindowToolTip", "Avatar Loader Window."), + // FSlateIcon(), + // FUIAction(FExecuteAction::CreateRaw(this, &FRpmNextGenEditorModule::OpenLoaderWindow)) + // ); } void FRpmNextGenEditorModule::ShutdownModule() diff --git a/Source/RpmNextGenEditor/Private/UI/CharacterLoaderWidget.cpp b/Source/RpmNextGenEditor/Private/UI/CharacterLoaderWidget.cpp index 3afbef9..8f40d9b 100644 --- a/Source/RpmNextGenEditor/Private/UI/CharacterLoaderWidget.cpp +++ b/Source/RpmNextGenEditor/Private/UI/CharacterLoaderWidget.cpp @@ -48,7 +48,6 @@ void SCharacterLoaderWidget::Construct(const FArguments& InArgs) .Text(FText::FromString("Load Glb")) .OnClicked(this, &SCharacterLoaderWidget::OnButtonClick) ] - // Add a text block for labeling + SVerticalBox::Slot() .AutoHeight() .Padding(5) @@ -57,7 +56,6 @@ void SCharacterLoaderWidget::Construct(const FArguments& InArgs) .Text(FText::FromString("Select Skeleton:")) ] - // Add the object property entry box + SVerticalBox::Slot() .AutoHeight() .Padding(5) @@ -70,7 +68,7 @@ void SCharacterLoaderWidget::Construct(const FArguments& InArgs) ]; } -// This function will be called when the user selects a skeleton + void SCharacterLoaderWidget::OnSkeletonSelected(const FAssetData& AssetData) { SelectedSkeleton = Cast(AssetData.GetAsset()); @@ -80,7 +78,7 @@ void SCharacterLoaderWidget::OnSkeletonSelected(const FAssetData& AssetData) } } -// Optional: Provide the current path of the selected skeleton + FString SCharacterLoaderWidget::GetCurrentSkeletonPath() const { return SelectedSkeleton ? SelectedSkeleton->GetPathName() : FString(); @@ -95,8 +93,6 @@ void SCharacterLoaderWidget::OnPathTextChanged(const FText& NewText) GConfig->Flush(false, GGameIni); // Ensure the config is saved immediately } - - FReply SCharacterLoaderWidget::OnButtonClick() { FString Path = PathText.ToString(); @@ -122,7 +118,4 @@ void SCharacterLoaderWidget::LoadAsset(const FString& Path) } AssetLoader.LoadGltfAssetToWorld(gltfAsset); AssetLoader.SaveAsUAsset(gltfAsset, TEXT("/Game/ReadyPlayerMe/TestSkeleton")); - //FglTFRuntimeSkeletonConfig skeletonConfig = FglTFRuntimeSkeletonConfig(); - //USkeleton* Skeleton = gltfAsset->LoadSkeleton(0, skeletonConfig); - //UTransientObjectSaverLibrary::SaveTransientSkeleton(Skeleton,TEXT("/Game/ReadyPlayerMe/TestSkeleton")); } diff --git a/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp index d06359e..f6a0d86 100644 --- a/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp @@ -1,18 +1,20 @@ // Fill out your copyright notice in the Description page of Project Settings. - #include "UI/SRpmDeveloperLoginWidget.h" #include "Auth/DevAuthTokenCache.h" #include "EditorCache.h" #include "SlateOptMacros.h" #include "Api/Assets/Models/AssetListRequest.h" #include "Api/Assets/Models/AssetListResponse.h" -#include "Auth/DeveloperAccountApi.h" +#include "DeveloperAccounts/DeveloperAccountApi.h" #include "Auth/DeveloperTokenAuthStrategy.h" #include "Widgets/Input/SEditableTextBox.h" #include "RpmImageLoader.h" #include "Auth/DeveloperAuthApi.h" -#include "Auth/DeveloperLoginRequest.h" +#include "Auth/Models/DeveloperAuth.h" +#include "Auth/Models/DeveloperLoginRequest.h" +#include "DeveloperAccounts/Models/ApplicationListRequest.h" +#include "DeveloperAccounts/Models/ApplicationListResponse.h" #include "Settings/RpmDeveloperSettings.h" #include "Widgets/Layout/SScrollBox.h" @@ -20,8 +22,8 @@ BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SRpmDeveloperLoginWidget::Construct(const FArguments& InArgs) { - FDeveloperAuth AuthData = DevAuthTokenCache::GetAuthData(); - DevAuthTokenCache::SetAuthData(AuthData); + FDeveloperAuth AuthData = FDevAuthTokenCache::GetAuthData(); + FDevAuthTokenCache::SetAuthData(AuthData); bIsLoggedIn = AuthData.IsValid(); UserName = AuthData.Name; @@ -183,7 +185,7 @@ void SRpmDeveloperLoginWidget::Initialize() { return; } - const FDeveloperAuth DevAuthData = DevAuthTokenCache::GetAuthData(); + const FDeveloperAuth DevAuthData = FDevAuthTokenCache::GetAuthData(); if (!DeveloperAuthApi.IsValid()) { DeveloperAuthApi = MakeUnique(); @@ -216,15 +218,10 @@ void SRpmDeveloperLoginWidget::Initialize() bIsInitialized = true; if (bIsLoggedIn) { - UE_LOG(LogTemp, Warning, TEXT("Logged in getting org list")); GetOrgList(); + return; } - else - { - UE_LOG(LogTemp, Warning, TEXT("Not logged in, logging out")); - - OnLogoutClicked(); - } + OnLogoutClicked(); } SRpmDeveloperLoginWidget::~SRpmDeveloperLoginWidget() @@ -259,7 +256,7 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) .AutoHeight() .HAlign(HAlign_Left) [ - SAssignNew(ImageWidget, SImage). // The image will be set later after downloading + SAssignNew(ImageWidget, SImage). DesiredSizeOverride(ImageSize) ] + SVerticalBox::Slot() @@ -267,7 +264,7 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) .Padding(5, 5) [ SNew(SBox) - .WidthOverride(100.0f) // Match button width to image width + .WidthOverride(100.0f) [ SNew(SButton) .Text(FText::FromString("Load Style")) @@ -282,14 +279,14 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Top) - .Padding(10, 10, 0, 0) // Padding from the left side of the Image & Button stack + .Padding(10, 10, 0, 0) [ SNew(SEditableText) .Text(FText::FromString(FString::Printf(TEXT("ID: %s"), *StyleAsset.Id))) - .IsReadOnly(true) // Prevents the text from being editable - .IsCaretMovedWhenGainFocus(false) // Caret won't appear when focused, keeping it look like plain text - .SelectAllTextWhenFocused(false) // Prevents selecting all text when focused - .MinDesiredWidth(100.0f) // Minimum width for the text box + .IsReadOnly(true) + .IsCaretMovedWhenGainFocus(false) + .SelectAllTextWhenFocused(false) + .MinDesiredWidth(100.0f) ] ]; @@ -300,7 +297,6 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) void SRpmDeveloperLoginWidget::OnLoadStyleClicked(const FString& StyleId) { AssetLoader = FEditorAssetLoader(); - UE_LOG(LogTemp, Error, TEXT("Loading model from glb url %s"), *CharacterStyleAssets[StyleId].GlbUrl); AssetLoader.LoadGLBFromURL(CharacterStyleAssets[StyleId].GlbUrl); } @@ -345,13 +341,13 @@ void SRpmDeveloperLoginWidget::HandleLoginResponse(const FDeveloperLoginResponse { UserName = Response.Data.Name; FDeveloperAuth AuthData = FDeveloperAuth(Response.Data, false); - DevAuthTokenCache::SetAuthData(AuthData); + FDevAuthTokenCache::SetAuthData(AuthData); SetLoggedInState(true); GetOrgList(); return; } UE_LOG(LogTemp, Error, TEXT("Login request failed")); - DevAuthTokenCache::ClearAuthData(); + FDevAuthTokenCache::ClearAuthData(); } void SRpmDeveloperLoginWidget::HandleOrganizationListResponse(const FOrganizationListResponse& Response, @@ -364,26 +360,20 @@ void SRpmDeveloperLoginWidget::HandleOrganizationListResponse(const FOrganizatio UE_LOG(LogTemp, Error, TEXT("No organizations found")); return; } - UE_LOG(LogTemp, Warning, TEXT("Orgs found")); - FApplicationListRequest Request; Request.Params.Add("organizationId", Response.Data[0].Id); DeveloperAccountApi->ListApplicationsAsync(Request); + return; } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to list organizations")); - } + + UE_LOG(LogTemp, Error, TEXT("Failed to list organizations")); } -void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationListResponse& Response, - bool bWasSuccessful) +void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationListResponse& Response, bool bWasSuccessful) { if (bWasSuccessful) { - UE_LOG(LogTemp, Warning, TEXT("HandleApplicationListResponse")); - URpmDeveloperSettings* RpmSettings = GetMutableDefault(); UserApplications = Response.Data; FString Active; @@ -407,7 +397,6 @@ void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationL else { UE_LOG(LogTemp, Error, TEXT("Failed to list applications")); - } LoadBaseModelList(); } @@ -455,7 +444,7 @@ FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() AuthData.Name = DemoUserName; AuthData.IsDemo = true; UserName = AuthData.Name; - DevAuthTokenCache::SetAuthData(AuthData); + FDevAuthTokenCache::SetAuthData(AuthData); SetLoggedInState(true); // Unset the authentication strategy for the APIs @@ -479,7 +468,7 @@ FReply SRpmDeveloperLoginWidget::OnLogoutClicked() ComboBoxItems.Empty(); ClearLoadedCharacterModelImages(); - DevAuthTokenCache::ClearAuthData(); + FDevAuthTokenCache::ClearAuthData(); SetLoggedInState(false); return FReply::Handled(); } diff --git a/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h b/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h index 288f95a..316d9a6 100644 --- a/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h +++ b/Source/RpmNextGenEditor/Public/Auth/DevAuthTokenCache.h @@ -1,63 +1,10 @@ #pragma once #include "CoreMinimal.h" -#include "DeveloperLoginResponse.h" -#include "DevAuthTokenCache.generated.h" - -USTRUCT(BlueprintType) -struct RPMNEXTGENEDITOR_API FDeveloperAuth -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "token")) - FString Token; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "refreshToken")) - FString RefreshToken; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "name")) - FString Name; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "isDemo")) - bool IsDemo; - - FDeveloperAuth() = default; - - FDeveloperAuth(FDeveloperLoginResponseBody Data, bool bIsDemo) - { - Token = Data.Token; - RefreshToken = Data.RefreshToken; - Name = Data.Name; - IsDemo = bIsDemo; - } - - FString ToJsonString() const - { - FString OutputString; - TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); - Writer->WriteObjectStart(); - Writer->WriteValue(TEXT("token"), Token); - Writer->WriteValue(TEXT("refreshToken"), RefreshToken); - Writer->WriteValue(TEXT("name"), Name); - Writer->WriteValue(TEXT("isDemo"), IsDemo); - Writer->WriteObjectEnd(); - Writer->Close(); - return OutputString; - } - - static bool FromJsonString(const FString& JsonString, FDeveloperAuth& OutStruct) - { - return FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutStruct, 0, 0); - } - - bool IsValid() const - { - return IsDemo || !Token.IsEmpty(); - } -}; +struct FDeveloperAuth; -class RPMNEXTGENEDITOR_API DevAuthTokenCache +class RPMNEXTGENEDITOR_API FDevAuthTokenCache { public: static FDeveloperAuth GetAuthData(); diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperAccountApi.h b/Source/RpmNextGenEditor/Public/Auth/DeveloperAccountApi.h deleted file mode 100644 index 272760f..0000000 --- a/Source/RpmNextGenEditor/Public/Auth/DeveloperAccountApi.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include "CoreMinimal.h" -#include "Api/Auth/ApiRequest.h" -#include "Api/Common/Models/ApiResponse.h" -#include "Api/Common/WebApiWithAuth.h" -#include "DeveloperAccountApi.generated.h" - -USTRUCT(BlueprintType) -struct RPMNEXTGENEDITOR_API FApplicationListRequest : public FApiRequest -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API") - TMap Params; -}; - -USTRUCT(BlueprintType) -struct RPMNEXTGENEDITOR_API FOrganizationListRequest : public FApiRequest -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API") - TMap Params; -}; - -USTRUCT(BlueprintType) -struct RPMNEXTGENEDITOR_API FApplication -{ - GENERATED_BODY() - UPROPERTY(meta = (JsonName = "id")) - FString Id; - UPROPERTY(meta=(Jsonname = "name" )) - FString Name; -}; - -USTRUCT(BlueprintType) -struct RPMNEXTGENEDITOR_API FApplicationListResponse : public FApiResponse -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API", meta = (JsonName = "data")) - TArray Data; -}; - -USTRUCT(BlueprintType) -struct RPMNEXTGENEDITOR_API FOrganization -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (JsonName = "id")) - FString Id; - UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (JsonName = "name")) - FString Name; -}; - -USTRUCT(BlueprintType) -struct RPMNEXTGENEDITOR_API FOrganizationListResponse : public FApiResponse -{ - GENERATED_BODY() - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API", meta = ( JsonName = "data")) - TArray Data; -}; - -DECLARE_DELEGATE_TwoParams(FOnApplicationListResponse, const FApplicationListResponse&, bool); -DECLARE_DELEGATE_TwoParams(FOnOrganizationListResponse, const FOrganizationListResponse&, bool); - - -class RPMNEXTGENEDITOR_API FDeveloperAccountApi : public FWebApiWithAuth -{ -public: - FDeveloperAccountApi(IAuthenticationStrategy* InAuthenticationStrategy); - void ListApplicationsAsync(const FApplicationListRequest& Request); - void ListOrganizationsAsync(const FOrganizationListRequest& Request); - - FOnApplicationListResponse OnApplicationListResponse; - FOnOrganizationListResponse OnOrganizationResponse; -private: - static FString BuildQueryString(const TMap& Params); - void HandleOrgListResponse(FString Data, bool bWasSuccessful); - void HandleAppListResponse(FString Data, bool bWasSuccessful); - - FString ApiBaseUrl; -}; diff --git a/Source/RpmNextGenEditor/Public/Auth/Models/DeveloperAuth.h b/Source/RpmNextGenEditor/Public/Auth/Models/DeveloperAuth.h new file mode 100644 index 0000000..aa7a092 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/Auth/Models/DeveloperAuth.h @@ -0,0 +1,57 @@ +#pragma once + +#include "CoreMinimal.h" +#include "DeveloperLoginResponse.h" +#include "DeveloperAuth.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FDeveloperAuth +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "token")) + FString Token; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "refreshToken")) + FString RefreshToken; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "name")) + FString Name; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ready Player Me", meta = (JsonName = "isDemo")) + bool IsDemo; + + FDeveloperAuth() = default; + + FDeveloperAuth(FDeveloperLoginResponseBody Data, bool bIsDemo) + { + Token = Data.Token; + RefreshToken = Data.RefreshToken; + Name = Data.Name; + IsDemo = bIsDemo; + } + + FString ToJsonString() const + { + FString OutputString; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&OutputString); + Writer->WriteObjectStart(); + Writer->WriteValue(TEXT("token"), Token); + Writer->WriteValue(TEXT("refreshToken"), RefreshToken); + Writer->WriteValue(TEXT("name"), Name); + Writer->WriteValue(TEXT("isDemo"), IsDemo); + Writer->WriteObjectEnd(); + Writer->Close(); + return OutputString; + } + + static bool FromJsonString(const FString& JsonString, FDeveloperAuth& OutStruct) + { + return FJsonObjectConverter::JsonObjectStringToUStruct(JsonString, &OutStruct, 0, 0); + } + + bool IsValid() const + { + return IsDemo || !Token.IsEmpty(); + } +}; diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperLoginRequest.h b/Source/RpmNextGenEditor/Public/Auth/Models/DeveloperLoginRequest.h similarity index 100% rename from Source/RpmNextGenEditor/Public/Auth/DeveloperLoginRequest.h rename to Source/RpmNextGenEditor/Public/Auth/Models/DeveloperLoginRequest.h diff --git a/Source/RpmNextGenEditor/Public/Auth/DeveloperLoginResponse.h b/Source/RpmNextGenEditor/Public/Auth/Models/DeveloperLoginResponse.h similarity index 100% rename from Source/RpmNextGenEditor/Public/Auth/DeveloperLoginResponse.h rename to Source/RpmNextGenEditor/Public/Auth/Models/DeveloperLoginResponse.h diff --git a/Source/RpmNextGenEditor/Public/DeveloperAccounts/DeveloperAccountApi.h b/Source/RpmNextGenEditor/Public/DeveloperAccounts/DeveloperAccountApi.h new file mode 100644 index 0000000..21565be --- /dev/null +++ b/Source/RpmNextGenEditor/Public/DeveloperAccounts/DeveloperAccountApi.h @@ -0,0 +1,29 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Common/WebApiWithAuth.h" + +struct FOrganizationListResponse; +struct FOrganizationListRequest; +struct FApplicationListResponse; +struct FApplicationListRequest; + +DECLARE_DELEGATE_TwoParams(FOnApplicationListResponse, const FApplicationListResponse&, bool); +DECLARE_DELEGATE_TwoParams(FOnOrganizationListResponse, const FOrganizationListResponse&, bool); + +class RPMNEXTGENEDITOR_API FDeveloperAccountApi : public FWebApiWithAuth +{ +public: + FDeveloperAccountApi(IAuthenticationStrategy* InAuthenticationStrategy); + void ListApplicationsAsync(const FApplicationListRequest& Request); + void ListOrganizationsAsync(const FOrganizationListRequest& Request); + + FOnApplicationListResponse OnApplicationListResponse; + FOnOrganizationListResponse OnOrganizationResponse; +private: + static FString BuildQueryString(const TMap& Params); + void HandleOrgListResponse(FString Data, bool bWasSuccessful); + void HandleAppListResponse(FString Data, bool bWasSuccessful); + + FString ApiBaseUrl; +}; diff --git a/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/ApplicationListRequest.h b/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/ApplicationListRequest.h new file mode 100644 index 0000000..12b49a8 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/ApplicationListRequest.h @@ -0,0 +1,14 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Auth/ApiRequest.h" +#include "ApplicationListRequest.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FApplicationListRequest : public FApiRequest +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API") + TMap Params; +}; diff --git a/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/ApplicationListResponse.h b/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/ApplicationListResponse.h new file mode 100644 index 0000000..96cf4e2 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/ApplicationListResponse.h @@ -0,0 +1,24 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Common/Models/ApiResponse.h" +#include "ApplicationListResponse.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FApplication +{ + GENERATED_BODY() + UPROPERTY(meta = (JsonName = "id")) + FString Id; + UPROPERTY(meta=(Jsonname = "name" )) + FString Name; +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FApplicationListResponse : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API", meta = (JsonName = "data")) + TArray Data; +}; diff --git a/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/OrganizationListRequest.h b/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/OrganizationListRequest.h new file mode 100644 index 0000000..0a83cd0 --- /dev/null +++ b/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/OrganizationListRequest.h @@ -0,0 +1,14 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Auth/ApiRequest.h" +#include "OrganizationListRequest.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FOrganizationListRequest : public FApiRequest +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API") + TMap Params; +}; diff --git a/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/OrganizationListResponse.h b/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/OrganizationListResponse.h new file mode 100644 index 0000000..8c3355e --- /dev/null +++ b/Source/RpmNextGenEditor/Public/DeveloperAccounts/Models/OrganizationListResponse.h @@ -0,0 +1,26 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Api/Common/Models/ApiResponse.h" +#include "Api/Common/WebApiWithAuth.h" +#include "OrganizationListResponse.generated.h" + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FOrganization +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (JsonName = "id")) + FString Id; + UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (JsonName = "name")) + FString Name; +}; + +USTRUCT(BlueprintType) +struct RPMNEXTGENEDITOR_API FOrganizationListResponse : public FApiResponse +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "API", meta = ( JsonName = "data")) + TArray Data; +}; diff --git a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h index ac2e63d..84bf10b 100644 --- a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h +++ b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h @@ -1,7 +1,6 @@ #pragma once #include "CoreMinimal.h" -#include "TransientObjectSaverLibrary.h" #include "Api/Assets/AssetLoader.h" #include "HAL/PlatformFilemanager.h" diff --git a/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h index 733fe26..1b84abd 100644 --- a/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h @@ -6,7 +6,7 @@ #include "EditorAssetLoader.h" #include "Api/Assets/AssetApi.h" #include "Api/Assets/Models/AssetListResponse.h" -#include "Auth/DeveloperAccountApi.h" +#include "DeveloperAccounts/DeveloperAccountApi.h" #include "Widgets/SCompoundWidget.h" #include "Containers/Map.h" @@ -15,6 +15,7 @@ class FDeveloperAuthApi; class URpmDeveloperSettings; class UDeveloperAuthApi; class SEditableTextBox; + /** * */ From ee5f2e49301b2441a272594bedb6401fee0ac1cd Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Fri, 23 Aug 2024 11:16:12 +0300 Subject: [PATCH 42/74] chore: add asset to the scene and fix crash issues --- Source/RpmNextGen/Private/RpmActor.cpp | 2 - Source/RpmNextGen/Private/RpmImageLoader.cpp | 173 +++++++++--------- Source/RpmNextGen/Public/RpmActor.h | 33 ++-- Source/RpmNextGen/Public/RpmImageLoader.h | 4 +- .../Private/CharacterLoaderWidget.cpp | 29 ++- .../Private/EditorAssetLoader.cpp | 76 ++++---- .../Private/EditorAssetNamer.cpp | 25 +++ .../Private/SRpmDeveloperLoginWidget.cpp | 7 +- .../Private/UEditorAssetNamer.cpp | 21 --- .../Public/EditorAssetLoader.h | 8 +- ...UEditorAssetNamer.h => EditorAssetNamer.h} | 13 +- 11 files changed, 208 insertions(+), 183 deletions(-) create mode 100644 Source/RpmNextGenEditor/Private/EditorAssetNamer.cpp delete mode 100644 Source/RpmNextGenEditor/Private/UEditorAssetNamer.cpp rename Source/RpmNextGenEditor/Public/{UEditorAssetNamer.h => EditorAssetNamer.h} (51%) diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index a014fc8..50df671 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -97,8 +97,6 @@ USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* Sk void ARpmActor::LoadglTFAsset(UglTFRuntimeAsset* Asset) { - /*Asset->LoadSkeletalMeshRecursiveAsync("", {}, OnSkeletalMeshCallback, SkeletalMeshConfig);*/ - auto mesh = Asset->LoadSkeletalMeshRecursive("", {}, SkeletalMeshConfig); HandleSkeletalMeshLoaded(mesh); } diff --git a/Source/RpmNextGen/Private/RpmImageLoader.cpp b/Source/RpmNextGen/Private/RpmImageLoader.cpp index 0906583..6c551e7 100644 --- a/Source/RpmNextGen/Private/RpmImageLoader.cpp +++ b/Source/RpmNextGen/Private/RpmImageLoader.cpp @@ -11,103 +11,108 @@ void FRpmImageLoader::LoadUImageFromURL(UImage* Image, const FString& URL) { - if (!Image) - { - UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: Image is null")); - return; - } - - TWeakObjectPtr WeakImagePtr(Image); - DownloadImage(URL, [WeakImagePtr](UTexture2D* Texture) { - if (WeakImagePtr.IsValid() && Texture) - { - FSlateBrush Brush; - Brush.SetResourceObject(Texture); - Brush.ImageSize = WeakImagePtr->Brush.ImageSize; - WeakImagePtr->SetBrush(Brush); - } - }); + if (!Image) + { + UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: Image is null")); + return; + } + + TWeakObjectPtr WeakImagePtr(Image); + DownloadImage(URL, [WeakImagePtr](UTexture2D* Texture) + { + if (WeakImagePtr.IsValid() && Texture) + { + FSlateBrush Brush; + Brush.SetResourceObject(Texture); + Brush.ImageSize = WeakImagePtr->Brush.ImageSize; + WeakImagePtr->SetBrush(Brush); + } + }); } -void FRpmImageLoader::LoadSImageFromURL(TSharedPtr ImageWidget, const FString& URL) +void FRpmImageLoader::LoadSImageFromURL(TSharedPtr ImageWidget, const FString& URL, + TFunction OnImageUpdated) { - if (!ImageWidget.IsValid()) - { - UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: SImage widget is invalid")); - return; - } - - DownloadImage(URL, [ImageWidget](UTexture2D* Texture) { - if (ImageWidget.IsValid() && Texture) - { - FSlateBrush* Brush = new FSlateBrush(); - Brush->SetResourceObject(Texture); - Brush->ImageSize = FVector2D(100.0f, 100.0f); // Adjust size as needed - ImageWidget->SetImage(Brush); - } - }); + if (!ImageWidget.IsValid()) + { + UE_LOG(LogTemp, Error, TEXT("LoadImageFromURL: SImage widget is invalid")); + return; + } + + DownloadImage(URL, [ImageWidget, OnImageUpdated](UTexture2D* Texture) + { + if (ImageWidget.IsValid() && Texture) + { + FSlateBrush* Brush = new FSlateBrush(); + Brush->SetResourceObject(Texture); + Brush->ImageSize = FVector2D(100.0f, 100.0f); // Adjust size as needed + ImageWidget->SetImage(Brush); + OnImageUpdated(Texture); + } + }); } void FRpmImageLoader::DownloadImage(const FString& URL, TFunction OnImageDownloaded) { - TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->OnProcessRequestComplete().BindRaw(this, &FRpmImageLoader::OnImageDownloadComplete, OnImageDownloaded); - HttpRequest->SetURL(URL); - HttpRequest->SetVerb("GET"); - HttpRequest->ProcessRequest(); + TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->OnProcessRequestComplete().BindRaw(this, &FRpmImageLoader::OnImageDownloadComplete, OnImageDownloaded); + HttpRequest->SetURL(URL); + HttpRequest->SetVerb("GET"); + HttpRequest->ProcessRequest(); } void FRpmImageLoader::OnImageDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TFunction OnImageDownloaded) { - UTexture2D* Texture = nullptr; - if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) - { - Texture = CreateTextureFromImageData(Response->GetContent()); - } - - if (!Texture) - { - UE_LOG(LogTemp, Error, TEXT("Failed to download or create texture from URL: %s"), *Request->GetURL()); - } - - AsyncTask(ENamedThreads::GameThread, [OnImageDownloaded, Texture]() { - OnImageDownloaded(Texture); - }); + UTexture2D* Texture = nullptr; + if (bWasSuccessful && Response.IsValid() && Response->GetContentLength() > 0) + { + Texture = CreateTextureFromImageData(Response->GetContent()); + } + + if (!Texture) + { + UE_LOG(LogTemp, Error, TEXT("Failed to download or create texture from URL: %s"), *Request->GetURL()); + } + + AsyncTask(ENamedThreads::GameThread, [OnImageDownloaded, Texture]() + { + OnImageDownloaded(Texture); + }); } UTexture2D* FRpmImageLoader::CreateTextureFromImageData(const TArray& ImageData) { - IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked(FName("ImageWrapper")); - EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); - - if (ImageFormat == EImageFormat::Invalid) - { - return nullptr; - } - - TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); - if (!ImageWrapper.IsValid() || !ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) - { - return nullptr; - } - - TArray UncompressedBGRA; - if (!ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) - { - return nullptr; - } - - UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); - if (!Texture) - { - return nullptr; - } - - void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); - FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); - Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); - Texture->UpdateResource(); - - return Texture; + IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked( + FName("ImageWrapper")); + EImageFormat ImageFormat = ImageWrapperModule.DetectImageFormat(ImageData.GetData(), ImageData.Num()); + + if (ImageFormat == EImageFormat::Invalid) + { + return nullptr; + } + + TSharedPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(ImageFormat); + if (!ImageWrapper.IsValid() || !ImageWrapper->SetCompressed(ImageData.GetData(), ImageData.Num())) + { + return nullptr; + } + + TArray UncompressedBGRA; + if (!ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA)) + { + return nullptr; + } + + UTexture2D* Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8); + if (!Texture) + { + return nullptr; + } + + void* TextureData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(TextureData, UncompressedBGRA.GetData(), UncompressedBGRA.Num()); + Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); + Texture->UpdateResource(); + + return Texture; } - diff --git a/Source/RpmNextGen/Public/RpmActor.h b/Source/RpmNextGen/Public/RpmActor.h index 6399c73..fbd15e7 100644 --- a/Source/RpmNextGen/Public/RpmActor.h +++ b/Source/RpmNextGen/Public/RpmActor.h @@ -24,60 +24,63 @@ UCLASS() class RPMNEXTGEN_API ARpmActor : public AActor { GENERATED_BODY() -public: +public: ARpmActor(); UFUNCTION(BlueprintCallable, Category = "Ready Player Me") USkeletalMeshComponent* CreateSkeletalMeshComponent(USkeletalMesh* SkeletalMesh, const FString& Name); UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - void LoadglTFAsset(UglTFRuntimeAsset *Asset); + void LoadglTFAsset(UglTFRuntimeAsset* Asset); void OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedPtr HttpResponse, bool bIsSuccessful); - + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") void LoadAsset(FAsset AssetData); - + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") void LoadCharacterUrl(FString Url); - + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") void LoadCharacter(FRpmCharacter CharacterData); - - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Ready Player Me"); + + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Ready Player Me") + ; USkeletalMeshComponent* BaseSkeletalMeshComponent; - + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") void CreateCharacter(); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Ready Player Me") FglTFRuntimeSkeletalMeshConfig SkeletalMeshConfig; - + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Ready Player Me") FglTFRuntimeConfig glTFRuntimeConfig; - + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Ready Player Me") FString BaseModelId; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") TSubclassOf TargetAnimBP; - + + UFUNCTION() + virtual void HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh); + protected: FRpmCharacter Character; - - TArray AddedMeshComponents; + TArray AddedMeshComponents; UFUNCTION() virtual void HandleCharacterCreateResponse(FCharacterCreateResponse CharacterCreateResponse, bool bWasSuccessful); UFUNCTION() virtual void HandleCharacterUpdateResponse(FCharacterUpdateResponse CharacterUpdateResponse, bool bWasSuccessful); UFUNCTION() virtual void HandleCharacterFindResponse(FCharacterFindByIdResponse CharacterFindByIdResponse, bool bWasSuccessful); - UFUNCTION() - virtual void HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh); + TMap PreviewAssetMap; + private: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category="Ready Player Me") USceneComponent* AssetRoot; diff --git a/Source/RpmNextGen/Public/RpmImageLoader.h b/Source/RpmNextGen/Public/RpmImageLoader.h index a16be65..43d4619 100644 --- a/Source/RpmNextGen/Public/RpmImageLoader.h +++ b/Source/RpmNextGen/Public/RpmImageLoader.h @@ -12,10 +12,10 @@ class RPMNEXTGEN_API FRpmImageLoader public: FRpmImageLoader() = default; void LoadUImageFromURL(UImage* Image, const FString& URL); - void LoadSImageFromURL(TSharedPtr ImageWidget, const FString& URL); + void LoadSImageFromURL(TSharedPtr ImageWidget, const FString& URL, TFunction OnImageUpdated); private: void DownloadImage(const FString& URL, TFunction OnImageDownloaded); void OnImageDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, TFunction OnImageDownloaded); UTexture2D* CreateTextureFromImageData(const TArray& ImageData); -}; \ No newline at end of file +}; diff --git a/Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp b/Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp index 6f6bd76..0e09ecf 100644 --- a/Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp +++ b/Source/RpmNextGenEditor/Private/CharacterLoaderWidget.cpp @@ -24,16 +24,16 @@ void SCharacterLoaderWidget::Construct(const FArguments& InArgs) SNew(SVerticalBox) + SVerticalBox::Slot() - .AutoHeight() - .Padding(5) + .AutoHeight() + .Padding(5) [ SNew(STextBlock) .Text(FText::FromString("Enter Path:")) ] + SVerticalBox::Slot() - .AutoHeight() - .Padding(5) + .AutoHeight() + .Padding(5) [ SAssignNew(PathTextBox, SEditableTextBox) .Text(FText::FromString(LastSavedPath)) // Set the loaded path as the initial text @@ -41,8 +41,8 @@ void SCharacterLoaderWidget::Construct(const FArguments& InArgs) ] + SVerticalBox::Slot() - .AutoHeight() - .Padding(5) + .AutoHeight() + .Padding(5) [ SNew(SButton) .Text(FText::FromString("Load Glb")) @@ -50,8 +50,8 @@ void SCharacterLoaderWidget::Construct(const FArguments& InArgs) ] // Add a text block for labeling + SVerticalBox::Slot() - .AutoHeight() - .Padding(5) + .AutoHeight() + .Padding(5) [ SNew(STextBlock) .Text(FText::FromString("Select Skeleton:")) @@ -59,8 +59,8 @@ void SCharacterLoaderWidget::Construct(const FArguments& InArgs) // Add the object property entry box + SVerticalBox::Slot() - .AutoHeight() - .Padding(5) + .AutoHeight() + .Padding(5) [ SNew(SObjectPropertyEntryBox) .AllowedClass(USkeleton::StaticClass()) // Only allow USkeleton assets @@ -96,11 +96,10 @@ void SCharacterLoaderWidget::OnPathTextChanged(const FText& NewText) } - FReply SCharacterLoaderWidget::OnButtonClick() { FString Path = PathText.ToString(); - if(Path.IsEmpty()) + if (Path.IsEmpty()) { UE_LOG(LogTemp, Error, TEXT("Path is empty")); return FReply::Handled(); @@ -113,14 +112,14 @@ FReply SCharacterLoaderWidget::OnButtonClick() void SCharacterLoaderWidget::LoadAsset(const FString& Path) { FglTFRuntimeConfig Config = FglTFRuntimeConfig(); - Config.SceneScale = 100.0f; + Config.SceneScale = 100.0f; Config.TransformBaseType = EglTFRuntimeTransformBaseType::YForward; UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromFilename(Path, true, Config); - if(SelectedSkeleton) + if (SelectedSkeleton) { AssetLoader.SkeletonToCopy = SelectedSkeleton; } - AssetLoader.LoadGltfAssetToWorld(gltfAsset); + AssetLoader.LoadAssetToWorldAsURpmActor(gltfAsset); AssetLoader.SaveAsUAsset(gltfAsset, TEXT("/Game/ReadyPlayerMe/TestSkeleton")); //FglTFRuntimeSkeletonConfig skeletonConfig = FglTFRuntimeSkeletonConfig(); //USkeleton* Skeleton = gltfAsset->LoadSkeleton(0, skeletonConfig); diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 658b83b..7834ca4 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -1,14 +1,10 @@ #include "EditorAssetLoader.h" -#include "AssetToolsModule.h" -#include "glTFRuntimeAssetActor.h" #include "glTFRuntimeFunctionLibrary.h" #include "RpmActor.h" #include "TransientObjectSaverLibrary.h" -#include "UEditorAssetNamer.h" -#include "Animation/SkeletalMeshActor.h" -#include "AssetRegistry/AssetRegistryModule.h" -#include "Factories/PhysicsAssetFactory.h" +#include "EditorAssetNamer.h" +#include "glTFRuntimeAssetActor.h" FEditorAssetLoader::FEditorAssetLoader() { @@ -23,45 +19,40 @@ void FEditorAssetLoader::OnAssetDownloadComplete(FString FilePath, UglTFRuntimeA { if (bWasSuccessful) { - //LoadGltfAssetToWorld(gltfAsset); gltfAsset->AddToRoot(); - SaveAsUAsset(gltfAsset, LoadedAssetId); + auto SkeletalMesh = SaveAsUAsset(gltfAsset, LoadedAssetId); + LoadAssetToWorldAsURpmActor(SkeletalMesh); gltfAsset->RemoveFromRoot(); } } -void FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString LoadedAssetId) +USkeletalMesh* FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* GltfAsset, const FString& LoadedAssetId) const { const FglTFRuntimeSkeletonConfig SkeletonConfig = FglTFRuntimeSkeletonConfig(); - USkeleton* Skeleton = gltfAsset->LoadSkeleton(0, SkeletonConfig); + USkeleton* Skeleton = GltfAsset->LoadSkeleton(0, SkeletonConfig); FglTFRuntimeSkeletalMeshConfig meshConfig = FglTFRuntimeSkeletalMeshConfig(); meshConfig.Skeleton = Skeleton; - USkeletalMesh* skeletalMesh = gltfAsset->LoadSkeletalMeshRecursive(TEXT(""), {}, meshConfig); - skeletalMesh->AddToRoot(); + USkeletalMesh* skeletalMesh = GltfAsset->LoadSkeletalMeshRecursive(TEXT(""), {}, meshConfig); skeletalMesh->SetSkeleton(Skeleton); Skeleton->SetPreviewMesh(skeletalMesh); - const FString CoreAssetPath = TEXT("/Game/ReadyPlayerMe/") + LoadedAssetId + "/"; - const FString SkeletonAssetPath = CoreAssetPath + LoadedAssetId + TEXT("_Skeleton"); - const FString SkeletalMeshAssetPath = CoreAssetPath + LoadedAssetId + TEXT("_SkeletalMesh"); + const FString CoreAssetPath = FString::Printf(TEXT("/Game/ReadyPlayerMe/%s/"), *LoadedAssetId); + const FString SkeletonAssetPath = FString::Printf(TEXT("%s%s_Skeleton"), *CoreAssetPath, *LoadedAssetId); + const FString SkeletalMeshAssetPath = FString::Printf(TEXT("%s%s_SkeletalMesh"), *CoreAssetPath, *LoadedAssetId); - const auto NameGenerator = NewObject(); + const auto NameGenerator = NewObject(); NameGenerator->SetPath(CoreAssetPath); - FTransientObjectSaverMaterialNameGenerator MaterialNameGeneratorDelegate; - MaterialNameGeneratorDelegate.BindUFunction(NameGenerator, FName("GenerateMaterialName")); - FTransientObjectSaverTextureNameGenerator TextureNameGeneratorDelegate; - TextureNameGeneratorDelegate.BindUFunction(NameGenerator, FName("GenerateTextureName")); - UTransientObjectSaverLibrary::SaveTransientSkeletalMesh(skeletalMesh, SkeletalMeshAssetPath, SkeletonAssetPath, TEXT(""), - MaterialNameGeneratorDelegate, - TextureNameGeneratorDelegate); + NameGenerator->MaterialNameGeneratorDelegate, + NameGenerator->TextureNameGeneratorDelegate); UE_LOG(LogTemp, Log, TEXT("Character model saved: %s"), *LoadedAssetId); + return skeletalMesh; } void FEditorAssetLoader::LoadGLBFromURLWithId(const FString& URL, FString LoadedAssetId) @@ -80,8 +71,17 @@ void FEditorAssetLoader::LoadGLBFromURLWithId(const FString& URL, FString Loaded LoadGLBFromURL(URL); } +void FEditorAssetLoader::LoadAssetToWorldAsURpmActor(UglTFRuntimeAsset* gltfAsset) +{ + this->LoadAssetToWorld(nullptr, gltfAsset); +} -void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) +void FEditorAssetLoader::LoadAssetToWorldAsURpmActor(USkeletalMesh* SkeletalMesh) +{ + this->LoadAssetToWorld(SkeletalMesh, nullptr); +} + +void FEditorAssetLoader::LoadAssetToWorld(USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset) { if (!GEditor) { @@ -97,38 +97,40 @@ void FEditorAssetLoader::LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset) return; } - if (gltfAsset) + if (SkeletalMesh) { FTransform Transform = FTransform::Identity; - // Spawn an actor of type AglTFRuntimeAssetActor in the editor world + + ARpmActor* NewActor = EditorWorld->SpawnActorDeferred(ARpmActor::StaticClass(), Transform); + if (NewActor) { NewActor->SetFlags(RF_Transient); - if (SkeletonToCopy) - { - NewActor->SkeletalMeshConfig.SkeletonConfig.CopyRotationsFrom = SkeletonToCopy; - } NewActor->FinishSpawning(Transform); NewActor->DispatchBeginPlay(); + if (SkeletalMesh) + { + NewActor->HandleSkeletalMeshLoaded(SkeletalMesh); + } + GEditor->SelectNone(true, true, true); GEditor->SelectActor(NewActor, true, true, false, true); // Register the actor in the editor world and update the editor GEditor->SelectActor(NewActor, true, true); GEditor->EditorUpdateComponents(); - - NewActor->LoadglTFAsset(gltfAsset); + if (gltfAsset) + { + NewActor->LoadglTFAsset(gltfAsset); + } UE_LOG(LogTemp, Log, TEXT("Successfully loaded GLB asset into the editor world")); } else { - UE_LOG(LogTemp, Error, TEXT("Failed to spawn AglTFRuntimeAssetActor in the editor world")); + UE_LOG(LogTemp, Error, TEXT("Failed to spawn ARpmActor in the editor world")); } } - else - { - UE_LOG(LogTemp, Error, TEXT("Failed to load GLB asset from file")); - } + UE_LOG(LogTemp, Error, TEXT("Failed to load GLB asset from file")); } diff --git a/Source/RpmNextGenEditor/Private/EditorAssetNamer.cpp b/Source/RpmNextGenEditor/Private/EditorAssetNamer.cpp new file mode 100644 index 0000000..60e534f --- /dev/null +++ b/Source/RpmNextGenEditor/Private/EditorAssetNamer.cpp @@ -0,0 +1,25 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "EditorAssetNamer.h" + +UEditorAssetNamer::UEditorAssetNamer() +{ + MaterialNameGeneratorDelegate.BindUFunction(this, "GenerateMaterialName"); + TextureNameGeneratorDelegate.BindUFunction(this, "GenerateTextureName"); +} + +void UEditorAssetNamer::SetPath(FString NewPath) +{ + this->path = NewPath; +} + +FString UEditorAssetNamer::GenerateMaterialName(UMaterialInterface* Material, const int32 MaterialIndex, const FString& SlotName) const +{ + return FString::Printf(TEXT("%sMaterial_%d"), *path, MaterialIndex); +} + +FString UEditorAssetNamer::GenerateTextureName(UTexture* Texture, UMaterialInterface* Material, const FString& MaterialPath, const FString& ParamName) const +{ + return FString::Printf(TEXT("%s%s%s"), *path, *Material->GetName(), *ParamName); +} diff --git a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp index dde06fe..fe8ddb1 100644 --- a/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/SRpmDeveloperLoginWidget.cpp @@ -239,6 +239,7 @@ void SRpmDeveloperLoginWidget::ClearLoadedCharacterModelImages() { Texture->RemoveFromRoot(); } + CharacterStyleTextures.Empty(); } @@ -296,7 +297,11 @@ void SRpmDeveloperLoginWidget::AddCharacterStyle(const FAsset& StyleAsset) ]; FRpmImageLoader ImageLoader; - ImageLoader.LoadSImageFromURL(ImageWidget, StyleAsset.IconUrl); + ImageLoader.LoadSImageFromURL(ImageWidget, StyleAsset.IconUrl, [this](UTexture2D* texture) + { + texture->AddToRoot(); + CharacterStyleTextures.Add(texture); + }); } void SRpmDeveloperLoginWidget::OnLoadStyleClicked(const FString& StyleId) diff --git a/Source/RpmNextGenEditor/Private/UEditorAssetNamer.cpp b/Source/RpmNextGenEditor/Private/UEditorAssetNamer.cpp deleted file mode 100644 index c2dfe0c..0000000 --- a/Source/RpmNextGenEditor/Private/UEditorAssetNamer.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "UEditorAssetNamer.h" - -void UUEditorAssetNamer::SetPath(FString NewPath) -{ - this->path = NewPath; -} - -FString UUEditorAssetNamer::GenerateMaterialName(UMaterialInterface* Material, const int32 MaterialIndex, - const FString& SlotName) -{ - return path + TEXT("Material_") + FString::FromInt(MaterialIndex); -} - -FString UUEditorAssetNamer::GenerateTextureName(UTexture* Texture, UMaterialInterface* Material, - const FString& MaterialPath, const FString& ParamName) -{ - return path + Material->GetName() + ParamName; -} diff --git a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h index 2554932..e9f96d2 100644 --- a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h +++ b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h @@ -13,8 +13,12 @@ class RPMNEXTGENEDITOR_API FEditorAssetLoader : public FAssetLoader FEditorAssetLoader(); virtual ~FEditorAssetLoader() override; - void LoadGltfAssetToWorld(UglTFRuntimeAsset* gltfAsset); + void LoadAssetToWorldAsURpmActor(UglTFRuntimeAsset* gltfAsset); + void LoadAssetToWorldAsURpmActor(USkeletalMesh* gltfAsset); void LoadGLBFromURLWithId(const FString& URL, const FString AssetId); - void SaveAsUAsset(UglTFRuntimeAsset* gltfAsset, FString Path); + USkeletalMesh* SaveAsUAsset(UglTFRuntimeAsset* GltfAsset, const FString& LoadedAssetId) const; USkeleton* SkeletonToCopy; + +private: + void LoadAssetToWorld(USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset); }; diff --git a/Source/RpmNextGenEditor/Public/UEditorAssetNamer.h b/Source/RpmNextGenEditor/Public/EditorAssetNamer.h similarity index 51% rename from Source/RpmNextGenEditor/Public/UEditorAssetNamer.h rename to Source/RpmNextGenEditor/Public/EditorAssetNamer.h index cb49ccd..80d5871 100644 --- a/Source/RpmNextGenEditor/Public/UEditorAssetNamer.h +++ b/Source/RpmNextGenEditor/Public/EditorAssetNamer.h @@ -3,24 +3,29 @@ #pragma once #include "CoreMinimal.h" +#include "TransientObjectSaverLibrary.h" #include "UObject/Object.h" -#include "UEditorAssetNamer.generated.h" +#include "EditorAssetNamer.generated.h" /** * */ UCLASS() -class RPMNEXTGENEDITOR_API UUEditorAssetNamer : public UObject +class RPMNEXTGENEDITOR_API UEditorAssetNamer : public UObject { GENERATED_BODY() public: + UEditorAssetNamer(); void SetPath(FString path); UFUNCTION() - FString GenerateMaterialName(UMaterialInterface* Material, int32 MaterialIndex, const FString& SlotName); + FString GenerateMaterialName(UMaterialInterface* Material, int32 MaterialIndex, const FString& SlotName) const; UFUNCTION() FString GenerateTextureName(UTexture* Texture, UMaterialInterface* Material, const FString& MaterialPath, - const FString& ParamName); + const FString& ParamName) const; + + FTransientObjectSaverMaterialNameGenerator MaterialNameGeneratorDelegate; + FTransientObjectSaverTextureNameGenerator TextureNameGeneratorDelegate; private: FString path; From 21167b7ed3126b1b5358683d02ca44ab800fcaf3 Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Fri, 23 Aug 2024 11:51:14 +0300 Subject: [PATCH 43/74] chore: rename AssetRenamer --- .../{EditorAssetNamer.cpp => AssetNameGenerator.cpp} | 10 +++++----- Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp | 4 ++-- .../{EditorAssetNamer.h => AssetNameGenerator.h} | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) rename Source/RpmNextGenEditor/Private/{EditorAssetNamer.cpp => AssetNameGenerator.cpp} (50%) rename Source/RpmNextGenEditor/Public/{EditorAssetNamer.h => AssetNameGenerator.h} (85%) diff --git a/Source/RpmNextGenEditor/Private/EditorAssetNamer.cpp b/Source/RpmNextGenEditor/Private/AssetNameGenerator.cpp similarity index 50% rename from Source/RpmNextGenEditor/Private/EditorAssetNamer.cpp rename to Source/RpmNextGenEditor/Private/AssetNameGenerator.cpp index 60e534f..782b376 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetNamer.cpp +++ b/Source/RpmNextGenEditor/Private/AssetNameGenerator.cpp @@ -1,25 +1,25 @@ // Fill out your copyright notice in the Description page of Project Settings. -#include "EditorAssetNamer.h" +#include "AssetNameGenerator.h" -UEditorAssetNamer::UEditorAssetNamer() +UAssetNameGenerator::UAssetNameGenerator() { MaterialNameGeneratorDelegate.BindUFunction(this, "GenerateMaterialName"); TextureNameGeneratorDelegate.BindUFunction(this, "GenerateTextureName"); } -void UEditorAssetNamer::SetPath(FString NewPath) +void UAssetNameGenerator::SetPath(FString NewPath) { this->path = NewPath; } -FString UEditorAssetNamer::GenerateMaterialName(UMaterialInterface* Material, const int32 MaterialIndex, const FString& SlotName) const +FString UAssetNameGenerator::GenerateMaterialName(UMaterialInterface* Material, const int32 MaterialIndex, const FString& SlotName) const { return FString::Printf(TEXT("%sMaterial_%d"), *path, MaterialIndex); } -FString UEditorAssetNamer::GenerateTextureName(UTexture* Texture, UMaterialInterface* Material, const FString& MaterialPath, const FString& ParamName) const +FString UAssetNameGenerator::GenerateTextureName(UTexture* Texture, UMaterialInterface* Material, const FString& MaterialPath, const FString& ParamName) const { return FString::Printf(TEXT("%s%s%s"), *path, *Material->GetName(), *ParamName); } diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 7834ca4..3af6b26 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -3,7 +3,7 @@ #include "glTFRuntimeFunctionLibrary.h" #include "RpmActor.h" #include "TransientObjectSaverLibrary.h" -#include "EditorAssetNamer.h" +#include "AssetNameGenerator.h" #include "glTFRuntimeAssetActor.h" FEditorAssetLoader::FEditorAssetLoader() @@ -42,7 +42,7 @@ USkeletalMesh* FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* GltfAsset, co const FString SkeletonAssetPath = FString::Printf(TEXT("%s%s_Skeleton"), *CoreAssetPath, *LoadedAssetId); const FString SkeletalMeshAssetPath = FString::Printf(TEXT("%s%s_SkeletalMesh"), *CoreAssetPath, *LoadedAssetId); - const auto NameGenerator = NewObject(); + const auto NameGenerator = NewObject(); NameGenerator->SetPath(CoreAssetPath); UTransientObjectSaverLibrary::SaveTransientSkeletalMesh(skeletalMesh, SkeletalMeshAssetPath, diff --git a/Source/RpmNextGenEditor/Public/EditorAssetNamer.h b/Source/RpmNextGenEditor/Public/AssetNameGenerator.h similarity index 85% rename from Source/RpmNextGenEditor/Public/EditorAssetNamer.h rename to Source/RpmNextGenEditor/Public/AssetNameGenerator.h index 80d5871..346cd99 100644 --- a/Source/RpmNextGenEditor/Public/EditorAssetNamer.h +++ b/Source/RpmNextGenEditor/Public/AssetNameGenerator.h @@ -5,18 +5,18 @@ #include "CoreMinimal.h" #include "TransientObjectSaverLibrary.h" #include "UObject/Object.h" -#include "EditorAssetNamer.generated.h" +#include "AssetNameGenerator.generated.h" /** * */ UCLASS() -class RPMNEXTGENEDITOR_API UEditorAssetNamer : public UObject +class RPMNEXTGENEDITOR_API UAssetNameGenerator : public UObject { GENERATED_BODY() public: - UEditorAssetNamer(); + UAssetNameGenerator(); void SetPath(FString path); UFUNCTION() FString GenerateMaterialName(UMaterialInterface* Material, int32 MaterialIndex, const FString& SlotName) const; From ea80ed92ef1e789d577f9e5b141879c72b50a298 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 23 Aug 2024 12:48:33 +0300 Subject: [PATCH 44/74] chore: fix issue with asset button loading --- Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp | 3 +-- Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp index c4586c3..7a880e3 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp @@ -34,7 +34,7 @@ void URpmAssetPanelWidget::OnAssetListResponse(const FAssetListResponse& AssetLi if(bWasSuccessful && AssetListResponse.Data.Num() > 0) { CreateButtonsFromAssets(AssetListResponse.Data); - + return; } UE_LOG(LogTemp, Error, TEXT("Failed to fetch assets")); @@ -45,7 +45,6 @@ void URpmAssetPanelWidget::CreateButtonsFromAssets(TArray Assets) for (auto Asset : Assets) { CreateButton(Asset); - return; } UE_LOG(LogTemp, Warning, TEXT("No assets found") ); } diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 438fddd..daaae27 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -45,11 +45,7 @@ USkeletalMesh* FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* GltfAsset, co const auto NameGenerator = NewObject(); NameGenerator->SetPath(CoreAssetPath); - UTransientObjectSaverLibrary::SaveTransientSkeletalMesh(skeletalMesh, SkeletalMeshAssetPath, - SkeletonAssetPath, - TEXT(""), - NameGenerator->MaterialNameGeneratorDelegate, - NameGenerator->TextureNameGeneratorDelegate); + UTransientObjectSaverLibrary::SaveTransientSkeletalMesh(skeletalMesh, SkeletalMeshAssetPath, SkeletonAssetPath, TEXT(""), NameGenerator->MaterialNameGeneratorDelegate, NameGenerator->TextureNameGeneratorDelegate); UE_LOG(LogTemp, Log, TEXT("Character model saved: %s"), *LoadedAssetId); return skeletalMesh; From 9e8fc9ccdf7bdf418ca1d7ba7874a17a1096581c Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Fri, 23 Aug 2024 13:27:26 +0300 Subject: [PATCH 45/74] feat: add back skeleton copy and assign assetId --- .../Private/EditorAssetLoader.cpp | 21 ++++++++++++------- .../Public/EditorAssetLoader.h | 6 +++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 3af6b26..5b58956 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -1,6 +1,5 @@ #include "EditorAssetLoader.h" -#include "glTFRuntimeFunctionLibrary.h" #include "RpmActor.h" #include "TransientObjectSaverLibrary.h" #include "AssetNameGenerator.h" @@ -8,6 +7,7 @@ FEditorAssetLoader::FEditorAssetLoader() { + SkeletonToCopy = nullptr; } FEditorAssetLoader::~FEditorAssetLoader() @@ -21,7 +21,7 @@ void FEditorAssetLoader::OnAssetDownloadComplete(FString FilePath, UglTFRuntimeA { gltfAsset->AddToRoot(); auto SkeletalMesh = SaveAsUAsset(gltfAsset, LoadedAssetId); - LoadAssetToWorldAsURpmActor(SkeletalMesh); + LoadAssetToWorldAsURpmActor(SkeletalMesh, LoadedAssetId); gltfAsset->RemoveFromRoot(); } } @@ -71,17 +71,17 @@ void FEditorAssetLoader::LoadGLBFromURLWithId(const FString& URL, FString Loaded LoadGLBFromURL(URL); } -void FEditorAssetLoader::LoadAssetToWorldAsURpmActor(UglTFRuntimeAsset* gltfAsset) +void FEditorAssetLoader::LoadAssetToWorldAsURpmActor(UglTFRuntimeAsset* gltfAsset, FString AssetId) { - this->LoadAssetToWorld(nullptr, gltfAsset); + this->LoadAssetToWorld(AssetId, nullptr, gltfAsset); } -void FEditorAssetLoader::LoadAssetToWorldAsURpmActor(USkeletalMesh* SkeletalMesh) +void FEditorAssetLoader::LoadAssetToWorldAsURpmActor(USkeletalMesh* SkeletalMesh, FString AssetId) { - this->LoadAssetToWorld(SkeletalMesh, nullptr); + this->LoadAssetToWorld(AssetId, SkeletalMesh, nullptr); } -void FEditorAssetLoader::LoadAssetToWorld(USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset) +void FEditorAssetLoader::LoadAssetToWorld(FString ActorId, USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset) { if (!GEditor) { @@ -106,10 +106,17 @@ void FEditorAssetLoader::LoadAssetToWorld(USkeletalMesh* SkeletalMesh, UglTFRunt if (NewActor) { + NewActor->BaseModelId = ActorId; NewActor->SetFlags(RF_Transient); NewActor->FinishSpawning(Transform); NewActor->DispatchBeginPlay(); + + if (SkeletonToCopy) + { + NewActor->SkeletalMeshConfig.SkeletonConfig.CopyRotationsFrom = SkeletonToCopy; + } + if (SkeletalMesh) { NewActor->HandleSkeletalMeshLoaded(SkeletalMesh); diff --git a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h index e9f96d2..9c34c28 100644 --- a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h +++ b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h @@ -13,12 +13,12 @@ class RPMNEXTGENEDITOR_API FEditorAssetLoader : public FAssetLoader FEditorAssetLoader(); virtual ~FEditorAssetLoader() override; - void LoadAssetToWorldAsURpmActor(UglTFRuntimeAsset* gltfAsset); - void LoadAssetToWorldAsURpmActor(USkeletalMesh* gltfAsset); + void LoadAssetToWorldAsURpmActor(UglTFRuntimeAsset* gltfAsset, FString AssetId = ""); + void LoadAssetToWorldAsURpmActor(USkeletalMesh* SkeletalMesh, FString AssetId = ""); void LoadGLBFromURLWithId(const FString& URL, const FString AssetId); USkeletalMesh* SaveAsUAsset(UglTFRuntimeAsset* GltfAsset, const FString& LoadedAssetId) const; USkeleton* SkeletonToCopy; private: - void LoadAssetToWorld(USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset); + void LoadAssetToWorld(FString actorId, USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset); }; From 7b05b45882dbe926e0100ee8e21ce70768957217 Mon Sep 17 00:00:00 2001 From: Raigo Kovask Date: Fri, 23 Aug 2024 13:35:28 +0300 Subject: [PATCH 46/74] chore: refactor prop names --- Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp | 4 ++-- Source/RpmNextGenEditor/Public/EditorAssetLoader.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 5b58956..dc54702 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -81,7 +81,7 @@ void FEditorAssetLoader::LoadAssetToWorldAsURpmActor(USkeletalMesh* SkeletalMesh this->LoadAssetToWorld(AssetId, SkeletalMesh, nullptr); } -void FEditorAssetLoader::LoadAssetToWorld(FString ActorId, USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset) +void FEditorAssetLoader::LoadAssetToWorld(FString AssetId, USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset) { if (!GEditor) { @@ -106,7 +106,7 @@ void FEditorAssetLoader::LoadAssetToWorld(FString ActorId, USkeletalMesh* Skelet if (NewActor) { - NewActor->BaseModelId = ActorId; + NewActor->BaseModelId = AssetId; NewActor->SetFlags(RF_Transient); NewActor->FinishSpawning(Transform); diff --git a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h index 9c34c28..8c010ed 100644 --- a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h +++ b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h @@ -20,5 +20,5 @@ class RPMNEXTGENEDITOR_API FEditorAssetLoader : public FAssetLoader USkeleton* SkeletonToCopy; private: - void LoadAssetToWorld(FString actorId, USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset); + void LoadAssetToWorld(FString AssetId, USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset); }; From 757dfe9e3180a6b9bde671fd347c7511f7835c8b Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 23 Aug 2024 13:58:29 +0300 Subject: [PATCH 47/74] chore: cleanup and fixed includes --- .../{Public => Private}/Api/Auth/AuthApi.cpp | 7 +- .../Private/Api/Characters/CharacterApi.cpp | 141 ++++++++++++------ .../RpmNextGen/Private/Api/Common/WebApi.cpp | 8 - Source/RpmNextGen/Public/Api/Common/WebApi.h | 7 +- .../Public/Api/Common/WebApiWithAuth.h | 5 - .../Private/UI/SRpmDeveloperLoginWidget.cpp | 2 + 6 files changed, 103 insertions(+), 67 deletions(-) rename Source/RpmNextGen/{Public => Private}/Api/Auth/AuthApi.cpp (88%) diff --git a/Source/RpmNextGen/Public/Api/Auth/AuthApi.cpp b/Source/RpmNextGen/Private/Api/Auth/AuthApi.cpp similarity index 88% rename from Source/RpmNextGen/Public/Api/Auth/AuthApi.cpp rename to Source/RpmNextGen/Private/Api/Auth/AuthApi.cpp index 616356a..e038845 100644 --- a/Source/RpmNextGen/Public/Api/Auth/AuthApi.cpp +++ b/Source/RpmNextGen/Private/Api/Auth/AuthApi.cpp @@ -1,8 +1,7 @@ -#include "AuthApi.h" - +#include "Api/Auth/AuthApi.h" #include "Interfaces/IHttpResponse.h" -#include "Models/RefreshTokenRequest.h" -#include "Models/RefreshTokenResponse.h" +#include "Api/Auth/Models/RefreshTokenRequest.h" +#include "Api/Auth/Models/RefreshTokenResponse.h" #include "Settings/RpmDeveloperSettings.h" FAuthApi::FAuthApi() diff --git a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp index f343748..161e1cc 100644 --- a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp +++ b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp @@ -1,5 +1,6 @@ #include "Api/Characters/CharacterApi.h" #include "HttpModule.h" +#include "Api/Auth/ApiKeyAuthStrategy.h" #include "Api/Characters/Models/CharacterFindByIdRequest.h" #include "Api/Characters/Models/CharacterPreviewRequest.h" #include "Api/Characters/Models/CharacterUpdateRequest.h" @@ -14,6 +15,11 @@ FCharacterApi::FCharacterApi() URpmDeveloperSettings* Settings = GetMutableDefault(); BaseUrl = FString::Printf(TEXT("%s/v1/characters"), *Settings->GetApiBaseUrl()); Http = &FHttpModule::Get(); + SetAuthenticationStrategy(nullptr); + if(!Settings->ApiKey.IsEmpty() && Settings->ApiProxyUrl.IsEmpty()) + { + SetAuthenticationStrategy(new FApiKeyAuthStrategy()); + } } FCharacterApi::~FCharacterApi() @@ -26,7 +32,8 @@ void FCharacterApi::CreateAsync(const FCharacterCreateRequest& Request) ApiRequest.Url = FString::Printf(TEXT("%s"), *BaseUrl); ApiRequest.Method = POST; ApiRequest.Payload = ConvertToJsonString(Request); - DispatchRaw(ApiRequest, Create); + ApiRequest.Headers.Add(TEXT("Content-Type"), TEXT("application/json")); + DispatchRawWithAuth(ApiRequest); } void FCharacterApi::UpdateAsync(const FCharacterUpdateRequest& Request) @@ -35,7 +42,8 @@ void FCharacterApi::UpdateAsync(const FCharacterUpdateRequest& Request) ApiRequest.Url = FString::Printf(TEXT("%s/%s"), *BaseUrl, *Request.Id); ApiRequest.Method = PATCH; ApiRequest.Payload = ConvertToJsonString(Request); - DispatchRaw(ApiRequest, Update); + ApiRequest.Headers.Add(TEXT("Content-Type"), TEXT("application/json")); + DispatchRawWithAuth(ApiRequest); } void FCharacterApi::FindByIdAsync(const FCharacterFindByIdRequest& Request) @@ -43,7 +51,8 @@ void FCharacterApi::FindByIdAsync(const FCharacterFindByIdRequest& Request) FApiRequest ApiRequest; ApiRequest.Url = FString::Printf(TEXT("%s/%s"), *BaseUrl, *Request.Id); ApiRequest.Method = GET; - DispatchRaw(ApiRequest, FindById); + ApiRequest.Headers.Add(TEXT("Content-Type"), TEXT("application/json")); + DispatchRawWithAuth(ApiRequest); } FString FCharacterApi::GeneratePreviewUrl(const FCharacterPreviewRequest& Request) @@ -53,47 +62,49 @@ FString FCharacterApi::GeneratePreviewUrl(const FCharacterPreviewRequest& Reques return url; } -void FCharacterApi::DispatchRaw(const FApiRequest& Data, const ECharacterResponseType& ResponseType) +void FCharacterApi::Dispatch(FApiRequest Data, const ECharacterResponseType& ResponseType) { - TSharedRef Request = Http->CreateRequest(); - Request->SetURL(Data.Url); - Request->SetVerb(Data.GetVerb()); - Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); - - //TODO move to auth strategy - URpmDeveloperSettings* Settings = GetMutableDefault(); - Request->SetHeader(TEXT("X-API-KEY"), Settings->ApiKey); - - for (const auto& Header : Data.Headers) - { - if (!Request->GetHeader(Header.Key).IsEmpty()) - { - continue; - } - Request->SetHeader(Header.Key, Header.Value); - } - if (!Data.Payload.IsEmpty()) - { - Request->SetContentAsString(Data.Payload); - } - Request->OnProcessRequestComplete().BindLambda( - [this, ResponseType](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) - { - UE_LOG(LogTemp, Warning, TEXT("RunCallback")) - OnProcessResponse(Request, Response, bWasSuccessful, ResponseType); - }); - Request->ProcessRequest(); + DispatchRawWithAuth(Data); + + // TSharedRef Request = Http->CreateRequest(); + // Request->SetURL(Data.Url); + // Request->SetVerb(Data.GetVerb()); + // Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); + // + // //TODO move to auth strategy + // URpmDeveloperSettings* Settings = GetMutableDefault(); + // Request->SetHeader(TEXT("X-API-KEY"), Settings->ApiKey); + // + // for (const auto& Header : Data.Headers) + // { + // if (!Request->GetHeader(Header.Key).IsEmpty()) + // { + // continue; + // } + // Request->SetHeader(Header.Key, Header.Value); + // } + // + // if (!Data.Payload.IsEmpty()) + // { + // Request->SetContentAsString(Data.Payload); + // } + // Request->OnProcessRequestComplete().BindLambda( + // [this, ResponseType](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) + // { + // UE_LOG(LogTemp, Warning, TEXT("RunCallback")) + // OnProcessResponse(Request, Response, bWasSuccessful, ResponseType); + // }); + // Request->ProcessRequest(); } -void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, - const ECharacterResponseType& ResponseType) +void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { - bool success = bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()); + FWebApiWithAuth::OnProcessResponse(Request, Response, bWasSuccessful); FCharacterCreateResponse CharacterCreateResponse; FCharacterUpdateResponse CharacterUpdateResponse; FCharacterFindByIdResponse CharacterFindByIdResponse; - + bool success = bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()); if (Response->GetResponseCode() == 401) { UE_LOG(ReadyPlayerMe, Error, @@ -102,27 +113,69 @@ void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr )); return; } - - switch (ResponseType) + + const FString Verb = Request->GetVerb(); + if (Verb == "POST") { - case Create: success = success && FJsonObjectConverter::JsonObjectStringToUStruct( Response->GetContentAsString(), &CharacterCreateResponse, 0, 0); OnCharacterCreateResponse.ExecuteIfBound(CharacterCreateResponse, success); - break; - case Update: + } + else if (Verb == "PATCH") + { success = success && FJsonObjectConverter::JsonObjectStringToUStruct( Response->GetContentAsString(), &CharacterUpdateResponse, 0, 0); OnCharacterUpdateResponse.ExecuteIfBound(CharacterUpdateResponse, success); - break; - case FindById: + } + else if (Verb == "GET") + { success = success && FJsonObjectConverter::JsonObjectStringToUStruct( Response->GetContentAsString(), &CharacterFindByIdResponse, 0, 0); OnCharacterFindResponse.ExecuteIfBound(CharacterFindByIdResponse, success); - break; + } + else + { + UE_LOG(LogTemp, Warning, TEXT("Unhandled verb")); } } +// void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, +// const ECharacterResponseType& ResponseType) +// { +// bool success = bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()); +// FCharacterCreateResponse CharacterCreateResponse; +// FCharacterUpdateResponse CharacterUpdateResponse; +// FCharacterFindByIdResponse CharacterFindByIdResponse; +// +// if (Response->GetResponseCode() == 401) +// { +// UE_LOG(ReadyPlayerMe, Error, +// TEXT( +// "The request to the character API failed with a 401 response code. Please ensure that your API Key or proxy is correctly configured." +// )); +// return; +// } +// +// switch (ResponseType) +// { +// case Create: +// success = success && FJsonObjectConverter::JsonObjectStringToUStruct( +// Response->GetContentAsString(), &CharacterCreateResponse, 0, 0); +// OnCharacterCreateResponse.ExecuteIfBound(CharacterCreateResponse, success); +// break; +// case Update: +// success = success && FJsonObjectConverter::JsonObjectStringToUStruct( +// Response->GetContentAsString(), &CharacterUpdateResponse, 0, 0); +// OnCharacterUpdateResponse.ExecuteIfBound(CharacterUpdateResponse, success); +// break; +// case FindById: +// success = success && FJsonObjectConverter::JsonObjectStringToUStruct( +// Response->GetContentAsString(), &CharacterFindByIdResponse, 0, 0); +// OnCharacterFindResponse.ExecuteIfBound(CharacterFindByIdResponse, success); +// break; +// } +// } + FString FCharacterApi::BuildQueryString(const TMap& QueryParams) { FString QueryString; diff --git a/Source/RpmNextGen/Private/Api/Common/WebApi.cpp b/Source/RpmNextGen/Private/Api/Common/WebApi.cpp index 2783139..8d53855 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApi.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApi.cpp @@ -10,14 +10,6 @@ FWebApi::FWebApi() FWebApi::~FWebApi() {} -// template -// void FWebApi::Dispatch(const FApiRequest& Data) -// { -// TFApiRequestBody payload = TFApiRequestBody(Data.Payload); -// FString PayloadString = ConvertToJsonString(payload.Data); -// DispatchRaw(FApiRequest{Data.Url, Data.Method, Data.Headers, PayloadString}); -// } - void FWebApi::DispatchRaw(const FApiRequest& Data) { TSharedRef Request = Http->CreateRequest(); diff --git a/Source/RpmNextGen/Public/Api/Common/WebApi.h b/Source/RpmNextGen/Public/Api/Common/WebApi.h index 9a61640..f611615 100644 --- a/Source/RpmNextGen/Public/Api/Common/WebApi.h +++ b/Source/RpmNextGen/Public/Api/Common/WebApi.h @@ -16,13 +16,8 @@ class RPMNEXTGEN_API FWebApi virtual ~FWebApi(); FOnWebApiResponse OnApiResponse; -protected: - // template - // void Dispatch( - // const FApiRequest& Data - // ); - +protected: void DispatchRaw( const FApiRequest& Data ); diff --git a/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h index 58137c1..dd52dca 100644 --- a/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h +++ b/Source/RpmNextGen/Public/Api/Common/WebApiWithAuth.h @@ -12,11 +12,6 @@ class RPMNEXTGEN_API FWebApiWithAuth : public FWebApi FWebApiWithAuth(IAuthenticationStrategy* InAuthenticationStrategy); void SetAuthenticationStrategy(IAuthenticationStrategy* InAuthenticationStrategy); - - // template - // void DispatchWithAuth( - // const TFApiRequest& Data - // ); void OnAuthComplete(bool bWasSuccessful); void OnAuthTokenRefreshed(const FRefreshTokenResponseBody& Response, bool bWasSuccessful); diff --git a/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp index 505d816..6b93425 100644 --- a/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp @@ -15,6 +15,8 @@ #include "Auth/Models/DeveloperLoginRequest.h" #include "DeveloperAccounts/Models/ApplicationListRequest.h" #include "DeveloperAccounts/Models/ApplicationListResponse.h" +#include "DeveloperAccounts/Models/OrganizationListRequest.h" +#include "DeveloperAccounts/Models/OrganizationListResponse.h" #include "Settings/RpmDeveloperSettings.h" #include "Widgets/Layout/SScrollBox.h" From f6814dd4d2cb8fc8dd002a22f7443fd0c816f8fb Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 23 Aug 2024 13:59:03 +0300 Subject: [PATCH 48/74] feat: updated character API to use WebApiWithAuth --- Source/RpmNextGen/Public/Api/Characters/CharacterApi.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h b/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h index 47a00ab..966925f 100644 --- a/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h +++ b/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h @@ -3,6 +3,7 @@ #include "CoreMinimal.h" #include "JsonObjectConverter.h" #include "Api/Common/WebApi.h" +#include "Api/Common/WebApiWithAuth.h" #include "Models/CharacterCreateRequest.h" #include "Models/CharacterCreateResponse.h" #include "Models/CharacterFindByIdRequest.h" @@ -25,11 +26,11 @@ DECLARE_DELEGATE_TwoParams(FOnCharacterCreateResponse, FCharacterCreateResponse, DECLARE_DELEGATE_TwoParams(FOnCharacterUpdatResponse, FCharacterUpdateResponse, bool); DECLARE_DELEGATE_TwoParams(FOnCharacterFindResponse, FCharacterFindByIdResponse, bool); -class RPMNEXTGEN_API FCharacterApi : public TSharedFromThis +class RPMNEXTGEN_API FCharacterApi : public TSharedFromThis, public FWebApiWithAuth { public: FCharacterApi(); - virtual ~FCharacterApi(); + virtual ~FCharacterApi() override; FOnWebApiResponse OnApiResponse; void CreateAsync(const FCharacterCreateRequest& Request); @@ -42,10 +43,9 @@ class RPMNEXTGEN_API FCharacterApi : public TSharedFromThis& QueryParams); From 9de76a03f817c2781d6be663496658d858d1f7df Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 23 Aug 2024 14:07:19 +0300 Subject: [PATCH 49/74] chore: cleanup --- .../Private/Api/Characters/CharacterApi.cpp | 113 ++---------------- .../Private/Api/Common/WebApiWithAuth.cpp | 1 - .../Private/Samples/RpmAssetPanelWidget.cpp | 1 - .../Samples/RpmCategoryButtonWidget.cpp | 2 - .../Samples/RpmCategoryPanelWidget.cpp | 1 - .../Private/Settings/RpmDeveloperSettings.cpp | 1 - .../Public/Api/Characters/CharacterApi.h | 18 +-- .../Private/AssetNameGenerator.cpp | 1 - .../Private/Auth/DeveloperAuthApi.cpp | 1 - .../Auth/DeveloperTokenAuthStrategy.cpp | 1 + .../Private/EditorAssetLoader.cpp | 2 - .../Private/RpmNextGenEditor.cpp | 1 - 12 files changed, 14 insertions(+), 129 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp index 161e1cc..114a011 100644 --- a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp +++ b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp @@ -4,12 +4,9 @@ #include "Api/Characters/Models/CharacterFindByIdRequest.h" #include "Api/Characters/Models/CharacterPreviewRequest.h" #include "Api/Characters/Models/CharacterUpdateRequest.h" -#include "GenericPlatform/GenericPlatformHttp.h" #include "Interfaces/IHttpResponse.h" #include "Settings/RpmDeveloperSettings.h" -DEFINE_LOG_CATEGORY(ReadyPlayerMe); - FCharacterApi::FCharacterApi() { URpmDeveloperSettings* Settings = GetMutableDefault(); @@ -62,52 +59,13 @@ FString FCharacterApi::GeneratePreviewUrl(const FCharacterPreviewRequest& Reques return url; } -void FCharacterApi::Dispatch(FApiRequest Data, const ECharacterResponseType& ResponseType) -{ - - DispatchRawWithAuth(Data); - - // TSharedRef Request = Http->CreateRequest(); - // Request->SetURL(Data.Url); - // Request->SetVerb(Data.GetVerb()); - // Request->SetHeader(TEXT("Content-Type"), TEXT("application/json")); - // - // //TODO move to auth strategy - // URpmDeveloperSettings* Settings = GetMutableDefault(); - // Request->SetHeader(TEXT("X-API-KEY"), Settings->ApiKey); - // - // for (const auto& Header : Data.Headers) - // { - // if (!Request->GetHeader(Header.Key).IsEmpty()) - // { - // continue; - // } - // Request->SetHeader(Header.Key, Header.Value); - // } - // - // if (!Data.Payload.IsEmpty()) - // { - // Request->SetContentAsString(Data.Payload); - // } - // Request->OnProcessRequestComplete().BindLambda( - // [this, ResponseType](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) - // { - // UE_LOG(LogTemp, Warning, TEXT("RunCallback")) - // OnProcessResponse(Request, Response, bWasSuccessful, ResponseType); - // }); - // Request->ProcessRequest(); -} - void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { FWebApiWithAuth::OnProcessResponse(Request, Response, bWasSuccessful); - FCharacterCreateResponse CharacterCreateResponse; - FCharacterUpdateResponse CharacterUpdateResponse; - FCharacterFindByIdResponse CharacterFindByIdResponse; - bool success = bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()); + bool bSuccess = bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()); if (Response->GetResponseCode() == 401) { - UE_LOG(ReadyPlayerMe, Error, + UE_LOG(LogTemp, Error, TEXT( "The request to the character API failed with a 401 response code. Please ensure that your API Key or proxy is correctly configured." )); @@ -117,21 +75,24 @@ void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr const FString Verb = Request->GetVerb(); if (Verb == "POST") { - success = success && FJsonObjectConverter::JsonObjectStringToUStruct( + FCharacterCreateResponse CharacterCreateResponse; + bSuccess = bSuccess && FJsonObjectConverter::JsonObjectStringToUStruct( Response->GetContentAsString(), &CharacterCreateResponse, 0, 0); - OnCharacterCreateResponse.ExecuteIfBound(CharacterCreateResponse, success); + OnCharacterCreateResponse.ExecuteIfBound(CharacterCreateResponse, bSuccess); } else if (Verb == "PATCH") { - success = success && FJsonObjectConverter::JsonObjectStringToUStruct( + FCharacterUpdateResponse CharacterUpdateResponse; + bSuccess = bSuccess && FJsonObjectConverter::JsonObjectStringToUStruct( Response->GetContentAsString(), &CharacterUpdateResponse, 0, 0); - OnCharacterUpdateResponse.ExecuteIfBound(CharacterUpdateResponse, success); + OnCharacterUpdateResponse.ExecuteIfBound(CharacterUpdateResponse, bSuccess); } else if (Verb == "GET") { - success = success && FJsonObjectConverter::JsonObjectStringToUStruct( + FCharacterFindByIdResponse CharacterFindByIdResponse; + bSuccess = bSuccess && FJsonObjectConverter::JsonObjectStringToUStruct( Response->GetContentAsString(), &CharacterFindByIdResponse, 0, 0); - OnCharacterFindResponse.ExecuteIfBound(CharacterFindByIdResponse, success); + OnCharacterFindResponse.ExecuteIfBound(CharacterFindByIdResponse, bSuccess); } else { @@ -139,55 +100,3 @@ void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr } } -// void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, -// const ECharacterResponseType& ResponseType) -// { -// bool success = bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()); -// FCharacterCreateResponse CharacterCreateResponse; -// FCharacterUpdateResponse CharacterUpdateResponse; -// FCharacterFindByIdResponse CharacterFindByIdResponse; -// -// if (Response->GetResponseCode() == 401) -// { -// UE_LOG(ReadyPlayerMe, Error, -// TEXT( -// "The request to the character API failed with a 401 response code. Please ensure that your API Key or proxy is correctly configured." -// )); -// return; -// } -// -// switch (ResponseType) -// { -// case Create: -// success = success && FJsonObjectConverter::JsonObjectStringToUStruct( -// Response->GetContentAsString(), &CharacterCreateResponse, 0, 0); -// OnCharacterCreateResponse.ExecuteIfBound(CharacterCreateResponse, success); -// break; -// case Update: -// success = success && FJsonObjectConverter::JsonObjectStringToUStruct( -// Response->GetContentAsString(), &CharacterUpdateResponse, 0, 0); -// OnCharacterUpdateResponse.ExecuteIfBound(CharacterUpdateResponse, success); -// break; -// case FindById: -// success = success && FJsonObjectConverter::JsonObjectStringToUStruct( -// Response->GetContentAsString(), &CharacterFindByIdResponse, 0, 0); -// OnCharacterFindResponse.ExecuteIfBound(CharacterFindByIdResponse, success); -// break; -// } -// } - -FString FCharacterApi::BuildQueryString(const TMap& QueryParams) -{ - FString QueryString; - if (QueryParams.Num() > 0) - { - QueryString.Append(TEXT("?")); - for (const auto& Param : QueryParams) - { - QueryString.Append(FString::Printf(TEXT("%s=%s&"), *FGenericPlatformHttp::UrlEncode(Param.Key), - *FGenericPlatformHttp::UrlEncode(Param.Value))); - } - QueryString.RemoveFromEnd(TEXT("&")); - } - return QueryString; -} diff --git a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp index d1d5e28..5877a38 100644 --- a/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp +++ b/Source/RpmNextGen/Private/Api/Common/WebApiWithAuth.cpp @@ -1,5 +1,4 @@ #include "Api/Common/WebApiWithAuth.h" - #include "Interfaces/IHttpResponse.h" FWebApiWithAuth::FWebApiWithAuth() : ApiRequestData(nullptr), AuthenticationStrategy(nullptr) diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp index 7a880e3..968f509 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp @@ -2,7 +2,6 @@ #include "Samples/RpmAssetPanelWidget.h" - #include "Api/Assets/AssetApi.h" #include "Api/Assets/Models/AssetListRequest.h" #include "Api/Auth/ApiKeyAuthStrategy.h" diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp index 966dc0a..119bd8c 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryButtonWidget.cpp @@ -1,11 +1,9 @@ // Fill out your copyright notice in the Description page of Project Settings. - #include "Samples/RpmCategoryButtonWidget.h" #include "Components/Button.h" #include "Components/Image.h" - void URpmCategoryButtonWidget::NativeConstruct() { Super::NativeConstruct(); diff --git a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp index 5acaf1d..b25abcd 100644 --- a/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmCategoryPanelWidget.cpp @@ -1,7 +1,6 @@ // Fill out your copyright notice in the Description page of Project Settings. #include "Samples/RpmCategoryPanelWidget.h" - #include "Blueprint/WidgetTree.h" #include "Samples/RpmCategoryButtonWidget.h" diff --git a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp index 8bb317f..67b5ae0 100644 --- a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp +++ b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp @@ -1,6 +1,5 @@ // Fill out your copyright notice in the Description page of Project Settings. - #include "Settings/RpmDeveloperSettings.h" URpmDeveloperSettings::URpmDeveloperSettings() diff --git a/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h b/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h index 966925f..2dd5d0c 100644 --- a/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h +++ b/Source/RpmNextGen/Public/Api/Characters/CharacterApi.h @@ -12,16 +12,6 @@ #include "Models/CharacterUpdateRequest.h" #include "Models/CharacterUpdateResponse.h" -DECLARE_LOG_CATEGORY_EXTERN(ReadyPlayerMe, Log, All); - -UENUM(BlueprintType) -enum ECharacterResponseType -{ - Create, - Update, - FindById -}; - DECLARE_DELEGATE_TwoParams(FOnCharacterCreateResponse, FCharacterCreateResponse, bool); DECLARE_DELEGATE_TwoParams(FOnCharacterUpdatResponse, FCharacterUpdateResponse, bool); DECLARE_DELEGATE_TwoParams(FOnCharacterFindResponse, FCharacterFindByIdResponse, bool); @@ -43,12 +33,8 @@ class RPMNEXTGEN_API FCharacterApi : public TSharedFromThis& QueryParams); - + virtual void OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) override; + template FString ConvertToJsonString(const T& Data); diff --git a/Source/RpmNextGenEditor/Private/AssetNameGenerator.cpp b/Source/RpmNextGenEditor/Private/AssetNameGenerator.cpp index 782b376..bbb8e00 100644 --- a/Source/RpmNextGenEditor/Private/AssetNameGenerator.cpp +++ b/Source/RpmNextGenEditor/Private/AssetNameGenerator.cpp @@ -1,6 +1,5 @@ // Fill out your copyright notice in the Description page of Project Settings. - #include "AssetNameGenerator.h" UAssetNameGenerator::UAssetNameGenerator() diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp index edce232..c3fa1c7 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp @@ -1,5 +1,4 @@ #include "Auth/DeveloperAuthApi.h" - #include "Auth/Models/DeveloperLoginRequest.h" #include "Auth/Models/DeveloperLoginResponse.h" #include "Settings/RpmDeveloperSettings.h" diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp index 5920821..07a85f3 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp @@ -3,6 +3,7 @@ #include "Api/Auth/ApiRequest.h" #include "Api/Auth/Models/RefreshTokenRequest.h" #include "Api/Auth/Models/RefreshTokenResponse.h" +#include "Auth/Models/DeveloperAuth.h" DeveloperTokenAuthStrategy::DeveloperTokenAuthStrategy() { diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index daaae27..9433001 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -1,6 +1,4 @@ #include "EditorAssetLoader.h" - -#include "glTFRuntimeFunctionLibrary.h" #include "RpmActor.h" #include "TransientObjectSaverLibrary.h" #include "AssetNameGenerator.h" diff --git a/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp index fa252da..1bf9fad 100644 --- a/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp +++ b/Source/RpmNextGenEditor/Private/RpmNextGenEditor.cpp @@ -3,7 +3,6 @@ #include "RpmNextGenEditor.h" #include "UI/CharacterLoaderWidget.h" -#include "LevelEditor.h" #include "UI/LoaderWindowCommands.h" #include "UI/LoginWindowCommands.h" #include "UI/SRpmDeveloperLoginWidget.h" From 3360a5b901f437194e05837aa35c218ee2a81558 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 26 Aug 2024 11:39:34 +0300 Subject: [PATCH 50/74] chore: update log --- Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 9433001..899e3bc 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -123,7 +123,7 @@ void FEditorAssetLoader::LoadAssetToWorld(USkeletalMesh* SkeletalMesh, UglTFRunt return; } - UE_LOG(LogTemp, Error, TEXT("Failed to spawn AglTFRuntimeAssetActor in the editor world")); + UE_LOG(LogTemp, Error, TEXT("Failed to spawn ARpmActor in the editor world")); } UE_LOG(LogTemp, Error, TEXT("Failed to load GLB asset from file")); } From 10e9729690df19ef009ec66fef8645e9382f752c Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 26 Aug 2024 12:12:22 +0300 Subject: [PATCH 51/74] feat: create base class based on gltfRuntimeAssetActor --- Source/RpmNextGen/Private/RpmActorBase.cpp | 310 +++++++++++++++++++++ Source/RpmNextGen/Public/RpmActorBase.h | 72 +++++ 2 files changed, 382 insertions(+) create mode 100644 Source/RpmNextGen/Private/RpmActorBase.cpp create mode 100644 Source/RpmNextGen/Public/RpmActorBase.h diff --git a/Source/RpmNextGen/Private/RpmActorBase.cpp b/Source/RpmNextGen/Private/RpmActorBase.cpp new file mode 100644 index 0000000..50b92a3 --- /dev/null +++ b/Source/RpmNextGen/Private/RpmActorBase.cpp @@ -0,0 +1,310 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "RpmActorBase.h" +#include "Components/InstancedStaticMeshComponent.h" +#include "Components/SkeletalMeshComponent.h" +#include "Engine/StaticMeshSocket.h" +#include "Animation/AnimSequence.h" +#include "glTFRuntimeSkeletalMeshComponent.h" + +// Sets default values +ARpmActorBase::ARpmActorBase() +{ + // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. + PrimaryActorTick.bCanEverTick = true; + AssetRoot = CreateDefaultSubobject(TEXT("AssetRoot")); + RootComponent = AssetRoot; + RootNodeIndex = INDEX_NONE; + bStaticMeshesAsSkeletalOnMorphTargets = true; +} + +// Called when the game starts or when spawned +void ARpmActorBase::BeginPlay() +{ + Super::BeginPlay(); + + if (!Asset) + { + return; + } + + double LoadingStartTime = FPlatformTime::Seconds(); + + if (RootNodeIndex > INDEX_NONE) + { + FglTFRuntimeNode Node; + if (!Asset->GetNode(RootNodeIndex, Node)) + { + return; + } + AssetRoot = nullptr; + ProcessNode(nullptr, NAME_None, Node); + } + else + { + TArray Scenes = Asset->GetScenes(); + for (FglTFRuntimeScene& Scene : Scenes) + { + USceneComponent* SceneComponent = NewObject(this, *FString::Printf(TEXT("Scene %d"), Scene.Index)); + SceneComponent->SetupAttachment(RootComponent); + SceneComponent->RegisterComponent(); + AddInstanceComponent(SceneComponent); + for (int32 NodeIndex : Scene.RootNodesIndices) + { + FglTFRuntimeNode Node; + if (!Asset->GetNode(NodeIndex, Node)) + { + return; + } + ProcessNode(SceneComponent, NAME_None, Node); + } + } + } + + for (TPair& Pair : SocketMapping) + { + for (USkeletalMeshComponent* SkeletalMeshComponent : DiscoveredSkeletalMeshComponents) + { + if (SkeletalMeshComponent->DoesSocketExist(Pair.Value)) + { + Pair.Key->AttachToComponent(SkeletalMeshComponent, FAttachmentTransformRules::KeepRelativeTransform, Pair.Value); + Pair.Key->SetRelativeTransform(FTransform::Identity); + break; + } + } + } + + UE_LOG(LogGLTFRuntime, Log, TEXT("Asset loaded in %f seconds"), FPlatformTime::Seconds() - LoadingStartTime); +} + +void ARpmActorBase::ProcessNode(USceneComponent* NodeParentComponent, const FName SocketName, FglTFRuntimeNode& Node) +{ + // special case for bones/joints + if (Asset->NodeIsBone(Node.Index)) + { + for (int32 ChildIndex : Node.ChildrenIndices) + { + FglTFRuntimeNode Child; + if (!Asset->GetNode(ChildIndex, Child)) + { + return; + } + ProcessNode(NodeParentComponent, *Child.Name, Child); + } + return; + } + + USceneComponent* NewComponent = nullptr; + if (Node.MeshIndex < 0) + { + NewComponent = NewObject(this, GetSafeNodeName(Node)); + if (!NodeParentComponent) + { + SetRootComponent(NewComponent); + } + else + { + NewComponent->SetupAttachment(NodeParentComponent); + } + NewComponent->RegisterComponent(); + NewComponent->SetRelativeTransform(Node.Transform); + AddInstanceComponent(NewComponent); + } + else + { + if (Node.SkinIndex < 0 && !(bStaticMeshesAsSkeletalOnMorphTargets && Asset->MeshHasMorphTargets(Node.MeshIndex))) + { + UStaticMeshComponent* StaticMeshComponent = nullptr; + TArray GPUInstancingTransforms; + if (Asset->GetNodeGPUInstancingTransforms(Node.Index, GPUInstancingTransforms)) + { + UInstancedStaticMeshComponent* InstancedStaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); + for (const FTransform& GPUInstanceTransform : GPUInstancingTransforms) + { + InstancedStaticMeshComponent->AddInstance(GPUInstanceTransform); + } + StaticMeshComponent = InstancedStaticMeshComponent; + } + else + { + StaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); + } + + if (!NodeParentComponent) + { + SetRootComponent(StaticMeshComponent); + } + else + { + StaticMeshComponent->SetupAttachment(NodeParentComponent); + } + StaticMeshComponent->RegisterComponent(); + StaticMeshComponent->SetRelativeTransform(Node.Transform); + AddInstanceComponent(StaticMeshComponent); + if (StaticMeshConfig.Outer == nullptr) + { + StaticMeshConfig.Outer = StaticMeshComponent; + } + + TArray MeshIndices; + MeshIndices.Add(Node.MeshIndex); + + TArray LODNodeIndices; + if (Asset->GetNodeExtensionIndices(Node.Index, "MSFT_lod", "ids", LODNodeIndices)) + { + for (const int32 LODNodeIndex : LODNodeIndices) + { + FglTFRuntimeNode LODNode; + // stop the chain at the first invalid node/mesh + if (!Asset->GetNode(LODNodeIndex, LODNode)) + { + break; + } + if (LODNode.MeshIndex <= INDEX_NONE) + { + break; + } + MeshIndices.Add(LODNode.MeshIndex); + } + } + + if (MeshIndices.Num() > 1) + { + TArray ScreenCoverages; + if (Asset->GetNodeExtrasNumbers(Node.Index, "MSFT_screencoverage", ScreenCoverages)) + { + for (int32 SCIndex = 0; SCIndex < ScreenCoverages.Num(); SCIndex++) + { + StaticMeshConfig.LODScreenSize.Add(SCIndex, ScreenCoverages[SCIndex]); + } + } + } + + UStaticMesh* StaticMesh = Asset->LoadStaticMeshLODs(MeshIndices, StaticMeshConfig); + if (StaticMesh && !StaticMeshConfig.ExportOriginalPivotToSocket.IsEmpty()) + { + UStaticMeshSocket* DeltaSocket = StaticMesh->FindSocket(FName(StaticMeshConfig.ExportOriginalPivotToSocket)); + if (DeltaSocket) + { + FTransform NewTransform = StaticMeshComponent->GetRelativeTransform(); + FVector DeltaLocation = -DeltaSocket->RelativeLocation * NewTransform.GetScale3D(); + DeltaLocation = NewTransform.GetRotation().RotateVector(DeltaLocation); + NewTransform.AddToTranslation(DeltaLocation); + StaticMeshComponent->SetRelativeTransform(NewTransform); + } + } + StaticMeshComponent->SetStaticMesh(StaticMesh); + ReceiveOnStaticMeshComponentCreated(StaticMeshComponent, Node); + NewComponent = StaticMeshComponent; + } + else + { + USkeletalMeshComponent* SkeletalMeshComponent = nullptr; + if (!SkeletalMeshConfig.bPerPolyCollision) + { + SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); + } + else + { + SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); + SkeletalMeshComponent->bEnablePerPolyCollision = true; + SkeletalMeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + } + if (!NodeParentComponent) + { + SetRootComponent(SkeletalMeshComponent); + } + else + { + SkeletalMeshComponent->SetupAttachment(NodeParentComponent); + } + SkeletalMeshComponent->RegisterComponent(); + SkeletalMeshComponent->SetRelativeTransform(Node.Transform); + AddInstanceComponent(SkeletalMeshComponent); + if (SkeletalMeshConfig.Outer == nullptr) + { + SkeletalMeshConfig.Outer = SkeletalMeshComponent; + } + USkeletalMesh* SkeletalMesh = Asset->LoadSkeletalMesh(Node.MeshIndex, Node.SkinIndex, SkeletalMeshConfig); + SkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); + DiscoveredSkeletalMeshComponents.Add(SkeletalMeshComponent); + ReceiveOnSkeletalMeshComponentCreated(SkeletalMeshComponent, Node); + NewComponent = SkeletalMeshComponent; + } + } + + if (!NewComponent) + { + return; + } + else + { + NewComponent->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeName:%s"), *Node.Name)); + NewComponent->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeIndex:%d"), Node.Index)); + + if (SocketName != NAME_None) + { + SocketMapping.Add(NewComponent, SocketName); + } + } + + + TArray EmitterIndices; + if (Asset->GetNodeExtensionIndices(Node.Index, "MSFT_audio_emitter", "emitters", EmitterIndices)) + { + // check for audio emitters + for (const int32 EmitterIndex : EmitterIndices) + { + FglTFRuntimeAudioEmitter AudioEmitter; + if (Asset->LoadAudioEmitter(EmitterIndex, AudioEmitter)) + { + UAudioComponent* AudioComponent = NewObject(this, *AudioEmitter.Name); + AudioComponent->SetupAttachment(NewComponent); + AudioComponent->RegisterComponent(); + AudioComponent->SetRelativeTransform(Node.Transform); + AddInstanceComponent(AudioComponent); + Asset->LoadEmitterIntoAudioComponent(AudioEmitter, AudioComponent); + AudioComponent->Play(); + } + } + } + + OnNodeProcessed.Broadcast(Node, NewComponent); + + for (int32 ChildIndex : Node.ChildrenIndices) + { + FglTFRuntimeNode Child; + if (!Asset->GetNode(ChildIndex, Child)) + { + return; + } + ProcessNode(NewComponent, NAME_None, Child); + } +} + +// Called every frame +void ARpmActorBase::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); +} + +void ARpmActorBase::ReceiveOnStaticMeshComponentCreated_Implementation(UStaticMeshComponent* StaticMeshComponent, const FglTFRuntimeNode& Node) +{ + +} + +void ARpmActorBase::ReceiveOnSkeletalMeshComponentCreated_Implementation(USkeletalMeshComponent* SkeletalMeshComponent, const FglTFRuntimeNode& Node) +{ + +} + +void ARpmActorBase::PostUnregisterAllComponents() +{ + if (Asset) + { + Asset->ClearCache(); + Asset = nullptr; + } + Super::PostUnregisterAllComponents(); +} \ No newline at end of file diff --git a/Source/RpmNextGen/Public/RpmActorBase.h b/Source/RpmNextGen/Public/RpmActorBase.h new file mode 100644 index 0000000..ff11950 --- /dev/null +++ b/Source/RpmNextGen/Public/RpmActorBase.h @@ -0,0 +1,72 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "glTFRuntimeAsset.h" +#include "RpmActorBase.generated.h" + +UCLASS() +class RPMNEXTGEN_API ARpmActorBase : public AActor +{ + GENERATED_BODY() + +public: + // Sets default values for this actor's properties + ARpmActorBase(); + +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + + + virtual void ProcessNode(USceneComponent* NodeParentComponent, const FName SocketName, FglTFRuntimeNode& Node); + + template + FName GetSafeNodeName(const FglTFRuntimeNode& Node) + { + return MakeUniqueObjectName(this, T::StaticClass(), *Node.Name); + } + + TMap SocketMapping; + TArray DiscoveredSkeletalMeshComponents; + +public: + // Called every frame + virtual void Tick(float DeltaTime) override; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + UglTFRuntimeAsset* Asset; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + FglTFRuntimeStaticMeshConfig StaticMeshConfig; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + FglTFRuntimeSkeletalMeshConfig SkeletalMeshConfig; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + FglTFRuntimeSkeletalAnimationConfig SkeletalAnimationConfig; + + UFUNCTION(BlueprintNativeEvent, Category = "Ready Player Me|glTFRuntime", meta = (DisplayName = "On StaticMeshComponent Created")) + void ReceiveOnStaticMeshComponentCreated(UStaticMeshComponent* StaticMeshComponent, const FglTFRuntimeNode& Node); + + UFUNCTION(BlueprintNativeEvent, Category = "Ready Player Me|glTFRuntime", meta = (DisplayName = "On SkeletalMeshComponent Created")) + void ReceiveOnSkeletalMeshComponentCreated(USkeletalMeshComponent* SkeletalMeshComponent, const FglTFRuntimeNode& Node); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + int32 RootNodeIndex; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + bool bStaticMeshesAsSkeletalOnMorphTargets; + + DECLARE_MULTICAST_DELEGATE_TwoParams(FglTFRuntimeAssetActorNodeProcessed, const FglTFRuntimeNode&, USceneComponent*); + FglTFRuntimeAssetActorNodeProcessed OnNodeProcessed; + + virtual void PostUnregisterAllComponents() override; + +private: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category="Ready Player Me|glTFRuntime") + USceneComponent* AssetRoot; + +}; From d92e246eed59a034083ec08419154485bdbc3cd0 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 26 Aug 2024 14:12:52 +0300 Subject: [PATCH 52/74] chore refactor and simplified rpmactorbase --- .../Private/Api/Assets/AssetLoader.cpp | 10 +- Source/RpmNextGen/Private/RpmActorBase.cpp | 296 ++++++++---------- .../Public/Api/Assets/AssetLoader.h | 4 +- Source/RpmNextGen/Public/RpmActor.h | 3 +- Source/RpmNextGen/Public/RpmActorBase.h | 14 +- .../Private/EditorAssetLoader.cpp | 2 +- 6 files changed, 143 insertions(+), 186 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp index 824b09d..698129f 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp @@ -43,7 +43,7 @@ void FAssetLoader::OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr if (bWasSuccessful && Response.IsValid()) { const TArray Content = Response->GetContent(); - OnAssetDataReceived.ExecuteIfBound(Content, true); + OnRequestDataReceived.ExecuteIfBound(Content, true); const FString FileName = FPaths::GetCleanFilename(Request->GetURL()); const FString FilePath = DownloadDirectory / FileName; @@ -52,15 +52,15 @@ void FAssetLoader::OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr { UE_LOG(LogTemp, Log, TEXT("Downloaded GLB file to %s"), *FilePath); UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(Content, *GltfConfig); - OnAssetDownloaded.ExecuteIfBound(FilePath, gltfAsset, true); + OnGLtfAssetLoaded.ExecuteIfBound(FilePath, gltfAsset, true); return; } UE_LOG(LogTemp, Error, TEXT("Failed to save GLB file to %s"), *FilePath); - OnAssetDownloaded.ExecuteIfBound(FString(), nullptr, false); + OnGLtfAssetLoaded.ExecuteIfBound(FString(), nullptr, false); return; } UE_LOG(LogTemp, Error, TEXT("Failed to download GLB file from URL")); - OnAssetDataReceived.ExecuteIfBound(TArray(), false); - OnAssetDownloaded.ExecuteIfBound(FString(), nullptr, false); + OnRequestDataReceived.ExecuteIfBound(TArray(), false); + OnGLtfAssetLoaded.ExecuteIfBound(FString(), nullptr, false); } diff --git a/Source/RpmNextGen/Private/RpmActorBase.cpp b/Source/RpmNextGen/Private/RpmActorBase.cpp index 50b92a3..e283a7b 100644 --- a/Source/RpmNextGen/Private/RpmActorBase.cpp +++ b/Source/RpmNextGen/Private/RpmActorBase.cpp @@ -4,7 +4,6 @@ #include "RpmActorBase.h" #include "Components/InstancedStaticMeshComponent.h" #include "Components/SkeletalMeshComponent.h" -#include "Engine/StaticMeshSocket.h" #include "Animation/AnimSequence.h" #include "glTFRuntimeSkeletalMeshComponent.h" @@ -28,6 +27,23 @@ void ARpmActorBase::BeginPlay() { return; } + + SetupAsset(); +} + +void ARpmActorBase::LoadGltfAsset(UglTFRuntimeAsset* GltfAsset) +{ + Asset = GltfAsset; + SetupAsset(); +} + +void ARpmActorBase::SetupAsset() +{ + if (!Asset) + { + UE_LOG(LogGLTFRuntime, Warning, TEXT("No asset to setup")); + return; + } double LoadingStartTime = FPlatformTime::Seconds(); @@ -78,200 +94,132 @@ void ARpmActorBase::BeginPlay() UE_LOG(LogGLTFRuntime, Log, TEXT("Asset loaded in %f seconds"), FPlatformTime::Seconds() - LoadingStartTime); } + void ARpmActorBase::ProcessNode(USceneComponent* NodeParentComponent, const FName SocketName, FglTFRuntimeNode& Node) { - // special case for bones/joints if (Asset->NodeIsBone(Node.Index)) { - for (int32 ChildIndex : Node.ChildrenIndices) - { - FglTFRuntimeNode Child; - if (!Asset->GetNode(ChildIndex, Child)) - { - return; - } - ProcessNode(NodeParentComponent, *Child.Name, Child); - } + ProcessBoneNode(NodeParentComponent, Node); return; } - USceneComponent* NewComponent = nullptr; - if (Node.MeshIndex < 0) + USceneComponent* NewComponent = CreateNewComponent(NodeParentComponent, Node); + + if (!NewComponent) { - NewComponent = NewObject(this, GetSafeNodeName(Node)); - if (!NodeParentComponent) - { - SetRootComponent(NewComponent); - } - else - { - NewComponent->SetupAttachment(NodeParentComponent); - } - NewComponent->RegisterComponent(); - NewComponent->SetRelativeTransform(Node.Transform); - AddInstanceComponent(NewComponent); + return; } - else + + SetupComponentTags(NewComponent, Node, SocketName); + ProcessChildNodes(NewComponent, Node); +} + +void ARpmActorBase::ProcessBoneNode(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node) +{ + for (int32 ChildIndex : Node.ChildrenIndices) { - if (Node.SkinIndex < 0 && !(bStaticMeshesAsSkeletalOnMorphTargets && Asset->MeshHasMorphTargets(Node.MeshIndex))) + FglTFRuntimeNode Child; + if (!Asset->GetNode(ChildIndex, Child)) { - UStaticMeshComponent* StaticMeshComponent = nullptr; - TArray GPUInstancingTransforms; - if (Asset->GetNodeGPUInstancingTransforms(Node.Index, GPUInstancingTransforms)) - { - UInstancedStaticMeshComponent* InstancedStaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); - for (const FTransform& GPUInstanceTransform : GPUInstancingTransforms) - { - InstancedStaticMeshComponent->AddInstance(GPUInstanceTransform); - } - StaticMeshComponent = InstancedStaticMeshComponent; - } - else - { - StaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); - } + return; + } + ProcessNode(NodeParentComponent, *Child.Name, Child); + } +} - if (!NodeParentComponent) - { - SetRootComponent(StaticMeshComponent); - } - else - { - StaticMeshComponent->SetupAttachment(NodeParentComponent); - } - StaticMeshComponent->RegisterComponent(); - StaticMeshComponent->SetRelativeTransform(Node.Transform); - AddInstanceComponent(StaticMeshComponent); - if (StaticMeshConfig.Outer == nullptr) - { - StaticMeshConfig.Outer = StaticMeshComponent; - } +USceneComponent* ARpmActorBase::CreateNewComponent(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node) +{ + USceneComponent* NewComponent = nullptr; - TArray MeshIndices; - MeshIndices.Add(Node.MeshIndex); + // Check if the node should be a skeletal mesh component + if (Node.SkinIndex >= 0 || (bStaticMeshesAsSkeletalOnMorphTargets && Asset->MeshHasMorphTargets(Node.MeshIndex))) + { + // Create a skeletal mesh component + USkeletalMeshComponent* SkeletalMeshComponent = nullptr; - TArray LODNodeIndices; - if (Asset->GetNodeExtensionIndices(Node.Index, "MSFT_lod", "ids", LODNodeIndices)) - { - for (const int32 LODNodeIndex : LODNodeIndices) - { - FglTFRuntimeNode LODNode; - // stop the chain at the first invalid node/mesh - if (!Asset->GetNode(LODNodeIndex, LODNode)) - { - break; - } - if (LODNode.MeshIndex <= INDEX_NONE) - { - break; - } - MeshIndices.Add(LODNode.MeshIndex); - } - } + if (SkeletalMeshConfig.bPerPolyCollision) + { + SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); + SkeletalMeshComponent->bEnablePerPolyCollision = true; + SkeletalMeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + } + else + { + SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); + } - if (MeshIndices.Num() > 1) - { - TArray ScreenCoverages; - if (Asset->GetNodeExtrasNumbers(Node.Index, "MSFT_screencoverage", ScreenCoverages)) - { - for (int32 SCIndex = 0; SCIndex < ScreenCoverages.Num(); SCIndex++) - { - StaticMeshConfig.LODScreenSize.Add(SCIndex, ScreenCoverages[SCIndex]); - } - } - } + // Load and set the skeletal mesh + USkeletalMesh* SkeletalMesh = Asset->LoadSkeletalMesh(Node.MeshIndex, Node.SkinIndex, SkeletalMeshConfig); + SkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); - UStaticMesh* StaticMesh = Asset->LoadStaticMeshLODs(MeshIndices, StaticMeshConfig); - if (StaticMesh && !StaticMeshConfig.ExportOriginalPivotToSocket.IsEmpty()) - { - UStaticMeshSocket* DeltaSocket = StaticMesh->FindSocket(FName(StaticMeshConfig.ExportOriginalPivotToSocket)); - if (DeltaSocket) - { - FTransform NewTransform = StaticMeshComponent->GetRelativeTransform(); - FVector DeltaLocation = -DeltaSocket->RelativeLocation * NewTransform.GetScale3D(); - DeltaLocation = NewTransform.GetRotation().RotateVector(DeltaLocation); - NewTransform.AddToTranslation(DeltaLocation); - StaticMeshComponent->SetRelativeTransform(NewTransform); - } - } - StaticMeshComponent->SetStaticMesh(StaticMesh); - ReceiveOnStaticMeshComponentCreated(StaticMeshComponent, Node); - NewComponent = StaticMeshComponent; - } - else - { - USkeletalMeshComponent* SkeletalMeshComponent = nullptr; - if (!SkeletalMeshConfig.bPerPolyCollision) - { - SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); - } - else - { - SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); - SkeletalMeshComponent->bEnablePerPolyCollision = true; - SkeletalMeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); - } - if (!NodeParentComponent) - { - SetRootComponent(SkeletalMeshComponent); - } - else - { - SkeletalMeshComponent->SetupAttachment(NodeParentComponent); - } - SkeletalMeshComponent->RegisterComponent(); - SkeletalMeshComponent->SetRelativeTransform(Node.Transform); - AddInstanceComponent(SkeletalMeshComponent); - if (SkeletalMeshConfig.Outer == nullptr) - { - SkeletalMeshConfig.Outer = SkeletalMeshComponent; - } - USkeletalMesh* SkeletalMesh = Asset->LoadSkeletalMesh(Node.MeshIndex, Node.SkinIndex, SkeletalMeshConfig); - SkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); - DiscoveredSkeletalMeshComponents.Add(SkeletalMeshComponent); - ReceiveOnSkeletalMeshComponentCreated(SkeletalMeshComponent, Node); - NewComponent = SkeletalMeshComponent; - } - } + // Attach and register the component + SkeletalMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent); + SkeletalMeshComponent->RegisterComponent(); + SkeletalMeshComponent->SetRelativeTransform(Node.Transform); - if (!NewComponent) - { - return; - } - else - { - NewComponent->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeName:%s"), *Node.Name)); - NewComponent->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeIndex:%d"), Node.Index)); + // Add the component to the list of discovered skeletal mesh components + DiscoveredSkeletalMeshComponents.Add(SkeletalMeshComponent); - if (SocketName != NAME_None) - { - SocketMapping.Add(NewComponent, SocketName); - } - } + NewComponent = SkeletalMeshComponent; + + // Custom event handling for when a skeletal mesh component is created + ReceiveOnSkeletalMeshComponentCreated(SkeletalMeshComponent, Node); + } + else + { + // Create a static mesh component + UStaticMeshComponent* StaticMeshComponent = nullptr; + TArray GPUInstancingTransforms; + + if (Asset->GetNodeGPUInstancingTransforms(Node.Index, GPUInstancingTransforms)) + { + UInstancedStaticMeshComponent* InstancedStaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); + for (const FTransform& GPUInstanceTransform : GPUInstancingTransforms) + { + InstancedStaticMeshComponent->AddInstance(GPUInstanceTransform); + } + StaticMeshComponent = InstancedStaticMeshComponent; + } + else + { + StaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); + } + + // Load and set the static mesh + UStaticMesh* StaticMesh = Asset->LoadStaticMeshLODs({Node.MeshIndex}, StaticMeshConfig); + StaticMeshComponent->SetStaticMesh(StaticMesh); + + // Attach and register the component + StaticMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent); + StaticMeshComponent->RegisterComponent(); + StaticMeshComponent->SetRelativeTransform(Node.Transform); + + NewComponent = StaticMeshComponent; + + // Custom event handling for when a static mesh component is created + ReceiveOnStaticMeshComponentCreated(StaticMeshComponent, Node); + } + // Add the component to the actor's list of instance components + AddInstanceComponent(NewComponent); - TArray EmitterIndices; - if (Asset->GetNodeExtensionIndices(Node.Index, "MSFT_audio_emitter", "emitters", EmitterIndices)) + return NewComponent; +} + + +void ARpmActorBase::SetupComponentTags(USceneComponent* Component, FglTFRuntimeNode& Node, const FName SocketName) +{ + Component->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeName:%s"), *Node.Name)); + Component->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeIndex:%d"), Node.Index)); + + if (SocketName != NAME_None) { - // check for audio emitters - for (const int32 EmitterIndex : EmitterIndices) - { - FglTFRuntimeAudioEmitter AudioEmitter; - if (Asset->LoadAudioEmitter(EmitterIndex, AudioEmitter)) - { - UAudioComponent* AudioComponent = NewObject(this, *AudioEmitter.Name); - AudioComponent->SetupAttachment(NewComponent); - AudioComponent->RegisterComponent(); - AudioComponent->SetRelativeTransform(Node.Transform); - AddInstanceComponent(AudioComponent); - Asset->LoadEmitterIntoAudioComponent(AudioEmitter, AudioComponent); - AudioComponent->Play(); - } - } + SocketMapping.Add(Component, SocketName); } - - OnNodeProcessed.Broadcast(Node, NewComponent); +} +void ARpmActorBase::ProcessChildNodes(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node) +{ for (int32 ChildIndex : Node.ChildrenIndices) { FglTFRuntimeNode Child; @@ -279,7 +227,7 @@ void ARpmActorBase::ProcessNode(USceneComponent* NodeParentComponent, const FNam { return; } - ProcessNode(NewComponent, NAME_None, Child); + ProcessNode(NodeParentComponent, NAME_None, Child); } } @@ -307,4 +255,4 @@ void ARpmActorBase::PostUnregisterAllComponents() Asset = nullptr; } Super::PostUnregisterAllComponents(); -} \ No newline at end of file +} diff --git a/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h index 73a887d..773713e 100644 --- a/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h +++ b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h @@ -20,8 +20,8 @@ class RPMNEXTGEN_API FAssetLoader : public FWebApi void LoadGLBFromURL(const FString& URL); - FOnAssetDataReceived OnAssetDataReceived; - FOnAssetDownloaded OnAssetDownloaded; + FOnAssetDataReceived OnRequestDataReceived; + FOnAssetDownloaded OnGLtfAssetLoaded; protected: FglTFRuntimeConfig* GltfConfig; diff --git a/Source/RpmNextGen/Public/RpmActor.h b/Source/RpmNextGen/Public/RpmActor.h index fbd15e7..7295130 100644 --- a/Source/RpmNextGen/Public/RpmActor.h +++ b/Source/RpmNextGen/Public/RpmActor.h @@ -45,8 +45,7 @@ class RPMNEXTGEN_API ARpmActor : public AActor UFUNCTION(BlueprintCallable, Category = "Ready Player Me") void LoadCharacter(FRpmCharacter CharacterData); - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Ready Player Me") - ; + UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Ready Player Me"); USkeletalMeshComponent* BaseSkeletalMeshComponent; UFUNCTION(BlueprintCallable, Category = "Ready Player Me") diff --git a/Source/RpmNextGen/Public/RpmActorBase.h b/Source/RpmNextGen/Public/RpmActorBase.h index ff11950..60276e6 100644 --- a/Source/RpmNextGen/Public/RpmActorBase.h +++ b/Source/RpmNextGen/Public/RpmActorBase.h @@ -19,8 +19,7 @@ class RPMNEXTGEN_API ARpmActorBase : public AActor protected: // Called when the game starts or when spawned virtual void BeginPlay() override; - - + virtual void ProcessNode(USceneComponent* NodeParentComponent, const FName SocketName, FglTFRuntimeNode& Node); template @@ -29,7 +28,9 @@ class RPMNEXTGEN_API ARpmActorBase : public AActor return MakeUniqueObjectName(this, T::StaticClass(), *Node.Name); } + UPROPERTY() TMap SocketMapping; + UPROPERTY() TArray DiscoveredSkeletalMeshComponents; public: @@ -65,8 +66,17 @@ class RPMNEXTGEN_API ARpmActorBase : public AActor virtual void PostUnregisterAllComponents() override; + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + virtual void LoadGltfAsset(UglTFRuntimeAsset* GltfAsset); + + virtual void SetupAsset(); + private: UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category="Ready Player Me|glTFRuntime") USceneComponent* AssetRoot; + void ProcessBoneNode(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node); + USceneComponent* CreateNewComponent(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node); + void SetupComponentTags(USceneComponent* Component, FglTFRuntimeNode& Node, const FName SocketName); + void ProcessChildNodes(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node); }; diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 4380c83..529a339 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -52,7 +52,7 @@ USkeletalMesh* FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* GltfAsset, co void FEditorAssetLoader::LoadGLBFromURLWithId(const FString& URL, FString LoadedAssetId) { - OnAssetDownloaded.BindLambda( + OnGLtfAssetLoaded.BindLambda( [LoadedAssetId, this](FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful) { From 0c0596763778ce86d0353593a61d33ec032639a7 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 26 Aug 2024 14:13:02 +0300 Subject: [PATCH 53/74] feat: added loader components --- .../Private/RpmAssetLoaderComponent.cpp | 49 +++++++++++ .../Private/RpmPreviewLoaderComponent.cpp | 88 +++++++++++++++++++ .../Public/RpmAssetLoaderComponent.h | 35 ++++++++ .../Public/RpmPreviewLoaderComponent.h | 51 +++++++++++ 4 files changed, 223 insertions(+) create mode 100644 Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp create mode 100644 Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp create mode 100644 Source/RpmNextGen/Public/RpmAssetLoaderComponent.h create mode 100644 Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h diff --git a/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp b/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp new file mode 100644 index 0000000..94ac2ff --- /dev/null +++ b/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp @@ -0,0 +1,49 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "RpmAssetLoaderComponent.h" +#include "Api/Assets/AssetLoader.h" + +class URpmDeveloperSettings; + +// Sets default values for this component's properties +URpmAssetLoaderComponent::URpmAssetLoaderComponent() +{ + // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features + // off to improve performance if you don't need them. + PrimaryComponentTick.bCanEverTick = false; + AssetLoader = MakeShared(); +} + +// Called when the game starts +void URpmAssetLoaderComponent::BeginPlay() +{ + Super::BeginPlay(); +} + +void URpmAssetLoaderComponent::LoadCharacterFromUrl(const FString Url) +{ + AssetLoader->OnGLtfAssetLoaded.BindLambda( + [this](FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful) + { + if (!gltfAsset || !bWasSuccessful) + { + UE_LOG(LogTemp, Log, TEXT("Failed to load gltf asset")); + return; + } + OnGltfAssetLoaded.Broadcast(gltfAsset); + }); + + AssetLoader->LoadGLBFromURL(Url); +} + + + +// Called every frame +void URpmAssetLoaderComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +{ + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); + + // ... +} + diff --git a/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp new file mode 100644 index 0000000..ca02dfc --- /dev/null +++ b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp @@ -0,0 +1,88 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "RpmPreviewLoaderComponent.h" +#include "Api/Assets/Models/Asset.h" +#include "Api/Auth/ApiKeyAuthStrategy.h" +#include "Api/Characters/CharacterApi.h" +#include "Api/Characters/Models/CharacterCreateResponse.h" +#include "Api/Characters/Models/CharacterFindByIdResponse.h" +#include "Api/Characters/Models/CharacterUpdateResponse.h" +#include "Settings/RpmDeveloperSettings.h" + +class URpmDeveloperSettings; + +// Sets default values for this component's properties +URpmPreviewLoaderComponent::URpmPreviewLoaderComponent() +{ + PrimaryComponentTick.bCanEverTick = false; + URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + AppId = RpmSettings->ApplicationId; + CharacterApi = MakeShared(); + // TODO - add smarter setting of auth strategy + if(RpmSettings->ApiProxyUrl.IsEmpty() && !RpmSettings->ApiKey.IsEmpty()) + { + CharacterApi->SetAuthenticationStrategy(new FApiKeyAuthStrategy()); + UE_LOG(LogTemp, Warning, TEXT("Adding ApiKeyAuthStrategy")); + } + CharacterApi->OnCharacterCreateResponse.BindUObject(this, &URpmPreviewLoaderComponent::HandleCharacterCreateResponse); + CharacterApi->OnCharacterUpdateResponse.BindUObject(this, &URpmPreviewLoaderComponent::HandleCharacterUpdateResponse); + CharacterApi->OnCharacterFindResponse.BindUObject(this, &URpmPreviewLoaderComponent::HandleCharacterFindResponse); + PreviewAssetMap = TMap(); +} + +void URpmPreviewLoaderComponent::CreateCharacter() +{ + if(BaseModelId.IsEmpty()) + { + UE_LOG(LogTemp, Error, TEXT("BaseModelId is empty on %s"), *GetOwner()->GetName()); + return; + } + FCharacterCreateRequest CharacterCreateRequest = FCharacterCreateRequest(); + CharacterCreateRequest.Data.Assets = TMap(); + CharacterCreateRequest.Data.Assets.Add("baseModel", BaseModelId); + CharacterCreateRequest.Data.ApplicationId = AppId; + + CharacterApi->CreateAsync(CharacterCreateRequest); +} + +void URpmPreviewLoaderComponent::HandleCharacterCreateResponse(FCharacterCreateResponse CharacterCreateResponse, + bool bWasSuccessful) +{ + Character = CharacterCreateResponse.Data; + LoadCharacter(Character); +} + +void URpmPreviewLoaderComponent::HandleCharacterUpdateResponse(FCharacterUpdateResponse CharacterUpdateResponse, + bool bWasSuccessful) +{ + Character = CharacterUpdateResponse.Data; +} + +void URpmPreviewLoaderComponent::HandleCharacterFindResponse(FCharacterFindByIdResponse CharacterFindByIdResponse, + bool bWasSuccessful) +{ + Character = CharacterFindByIdResponse.Data; +} + +void URpmPreviewLoaderComponent::LoadCharacter(FRpmCharacter CharacterData) +{ + LoadCharacterFromUrl(CharacterData.GlbUrl); +} + +void URpmPreviewLoaderComponent::LoadAssetPreview(FAsset AssetData) +{ + if (Character.Id.IsEmpty()) + { + UE_LOG(LogTemp, Warning, TEXT("Character Id is empty")); + return; + } + + PreviewAssetMap.Add(AssetData.Type, AssetData.Id); + FCharacterPreviewRequest PreviewRequest; + PreviewRequest.Id = Character.Id; + PreviewRequest.Params.Assets = PreviewAssetMap; + const FString& Url = CharacterApi->GeneratePreviewUrl(PreviewRequest); + LoadCharacterFromUrl(Url); +} + diff --git a/Source/RpmNextGen/Public/RpmAssetLoaderComponent.h b/Source/RpmNextGen/Public/RpmAssetLoaderComponent.h new file mode 100644 index 0000000..6e24670 --- /dev/null +++ b/Source/RpmNextGen/Public/RpmAssetLoaderComponent.h @@ -0,0 +1,35 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "RpmAssetLoaderComponent.generated.h" + +class FAssetLoader; +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGltfAssetLoaded, UglTFRuntimeAsset*, Asset); + +UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) +class RPMNEXTGEN_API URpmAssetLoaderComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + // Sets default values for this component's properties + URpmAssetLoaderComponent(); + + virtual void LoadCharacterFromUrl(FString Url); + + UPROPERTY(BlueprintAssignable, Category = "Ready Player Me" ) + FOnGltfAssetLoaded OnGltfAssetLoaded; + +protected: + // Called when the game starts + virtual void BeginPlay() override; + +public: + // Called every frame + virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; +private: + TSharedPtr AssetLoader; +}; diff --git a/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h b/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h new file mode 100644 index 0000000..61efd14 --- /dev/null +++ b/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h @@ -0,0 +1,51 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "RpmAssetLoaderComponent.h" +#include "Api/Characters/Models/RpmCharacter.h" +#include "RpmPreviewLoaderComponent.generated.h" + +struct FCharacterCreateResponse; +struct FCharacterUpdateResponse; +struct FCharacterFindByIdResponse; +struct FAsset; + +/** + * + */ +UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) +class RPMNEXTGEN_API URpmPreviewLoaderComponent : public URpmAssetLoaderComponent +{ + GENERATED_BODY() + +public: + URpmPreviewLoaderComponent(); + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Ready Player Me") + FString BaseModelId; + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + virtual void CreateCharacter(); + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + virtual void LoadCharacter(FRpmCharacter CharacterData); + + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + virtual void LoadAssetPreview(FAsset AssetData); + + UFUNCTION() + virtual void HandleCharacterCreateResponse(FCharacterCreateResponse CharacterCreateResponse, bool bWasSuccessful); + UFUNCTION() + virtual void HandleCharacterUpdateResponse(FCharacterUpdateResponse CharacterUpdateResponse, bool bWasSuccessful); + UFUNCTION() + virtual void HandleCharacterFindResponse(FCharacterFindByIdResponse CharacterFindByIdResponse, bool bWasSuccessful); + +protected: + FString AppId; + FRpmCharacter Character; + TMap PreviewAssetMap; +private: + TSharedPtr CharacterApi; +}; From c51132f1c03921dd58df5e0968617ca5def99940 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 26 Aug 2024 14:35:20 +0300 Subject: [PATCH 54/74] feat: updated editor loader to use new RPMActor class --- .../Private/EditorAssetLoader.cpp | 36 +++++++------------ .../Public/EditorAssetLoader.h | 3 +- 2 files changed, 13 insertions(+), 26 deletions(-) diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 529a339..1dffdf2 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -3,6 +3,7 @@ #include "TransientObjectSaverLibrary.h" #include "AssetNameGenerator.h" #include "glTFRuntimeAssetActor.h" +#include "RpmActorBase.h" FEditorAssetLoader::FEditorAssetLoader() { @@ -19,8 +20,8 @@ void FEditorAssetLoader::OnAssetDownloadComplete(FString FilePath, UglTFRuntimeA if (bWasSuccessful) { gltfAsset->AddToRoot(); - auto SkeletalMesh = SaveAsUAsset(gltfAsset, LoadedAssetId); - LoadAssetToWorldAsURpmActor(SkeletalMesh, LoadedAssetId); + SaveAsUAsset(gltfAsset, LoadedAssetId); + LoadAssetToWorldAsURpmActor(gltfAsset, LoadedAssetId); gltfAsset->RemoveFromRoot(); } } @@ -68,23 +69,18 @@ void FEditorAssetLoader::LoadGLBFromURLWithId(const FString& URL, FString Loaded void FEditorAssetLoader::LoadAssetToWorldAsURpmActor(UglTFRuntimeAsset* gltfAsset, FString AssetId) { - this->LoadAssetToWorld(AssetId, nullptr, gltfAsset); + this->LoadAssetToWorld(AssetId, gltfAsset); } -void FEditorAssetLoader::LoadAssetToWorldAsURpmActor(USkeletalMesh* SkeletalMesh, FString AssetId) -{ - this->LoadAssetToWorld(AssetId, SkeletalMesh, nullptr); -} -void FEditorAssetLoader::LoadAssetToWorld(FString AssetId, USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset) +void FEditorAssetLoader::LoadAssetToWorld(FString AssetId, UglTFRuntimeAsset* gltfAsset) { if (!GEditor) { UE_LOG(LogTemp, Error, TEXT("GEditor is not available.")); return; } - - // Get the editor world context + UWorld* EditorWorld = GEditor->GetEditorWorldContext().World(); if (!EditorWorld) { @@ -92,31 +88,23 @@ void FEditorAssetLoader::LoadAssetToWorld(FString AssetId, USkeletalMesh* Skelet return; } - if (SkeletalMesh) + if (gltfAsset) { FTransform Transform = FTransform::Identity; - ARpmActor* NewActor = EditorWorld->SpawnActorDeferred(ARpmActor::StaticClass(), Transform); + ARpmActorBase* NewActor = EditorWorld->SpawnActorDeferred(ARpmActorBase::StaticClass(), Transform); if (NewActor) { - NewActor->BaseModelId = AssetId; NewActor->SetFlags(RF_Transient); - - NewActor->FinishSpawning(Transform); - NewActor->DispatchBeginPlay(); - + NewActor->Rename(*AssetId); if (SkeletonToCopy) { NewActor->SkeletalMeshConfig.SkeletonConfig.CopyRotationsFrom = SkeletonToCopy; } - - if (SkeletalMesh) - { - NewActor->HandleSkeletalMeshLoaded(SkeletalMesh); - } - + NewActor->FinishSpawning(Transform); + NewActor->DispatchBeginPlay(); GEditor->SelectNone(true, true, true); GEditor->SelectActor(NewActor, true, true, false, true); @@ -125,7 +113,7 @@ void FEditorAssetLoader::LoadAssetToWorld(FString AssetId, USkeletalMesh* Skelet GEditor->EditorUpdateComponents(); if (gltfAsset) { - NewActor->LoadglTFAsset(gltfAsset); + NewActor->LoadGltfAsset(gltfAsset); } UE_LOG(LogTemp, Log, TEXT("Successfully loaded GLB asset into the editor world")); return; diff --git a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h index 7751869..7bd3fdb 100644 --- a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h +++ b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h @@ -13,11 +13,10 @@ class RPMNEXTGENEDITOR_API FEditorAssetLoader : public FAssetLoader virtual ~FEditorAssetLoader() override; void LoadAssetToWorldAsURpmActor(UglTFRuntimeAsset* gltfAsset, FString AssetId = ""); - void LoadAssetToWorldAsURpmActor(USkeletalMesh* SkeletalMesh, FString AssetId = ""); void LoadGLBFromURLWithId(const FString& URL, const FString AssetId); USkeletalMesh* SaveAsUAsset(UglTFRuntimeAsset* GltfAsset, const FString& LoadedAssetId) const; USkeleton* SkeletonToCopy; private: - void LoadAssetToWorld(FString AssetId, USkeletalMesh* SkeletalMesh, UglTFRuntimeAsset* gltfAsset); + void LoadAssetToWorld(FString AssetId, UglTFRuntimeAsset* gltfAsset); }; From 21e514c8223744dd4ec3fe1da3d4063fd032e49a Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 26 Aug 2024 15:47:53 +0300 Subject: [PATCH 55/74] feat: basic asset loading based on UI selection --- .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 124107 -> 129657 bytes Content/Samples/LoaderDemo/BP_RpmActor.uasset | Bin 0 -> 2442 bytes .../Blueprints/BP_LoaderDemoUI.uasset | Bin 0 -> 2518 bytes .../Blueprints/BP_RpmPreviewActor.uasset | Bin 0 -> 52313 bytes .../Blueprints/WBP_LoaderDemoUI.uasset | Bin 0 -> 110065 bytes Content/Samples/LoaderDemo/LoaderSample.umap | Bin 0 -> 91328 bytes .../Private/Api/Assets/AssetLoader.cpp | 36 ++- Source/RpmNextGen/Private/RpmActor.cpp | 295 ++++++++++++------ Source/RpmNextGen/Private/RpmActorBase.cpp | 258 --------------- .../Private/RpmAssetLoaderComponent.cpp | 2 +- .../Private/Samples/RpmAssetPanelWidget.cpp | 5 + .../Public/Api/Assets/AssetLoader.h | 8 +- Source/RpmNextGen/Public/RpmActor.h | 107 +++---- Source/RpmNextGen/Public/RpmActorBase.h | 82 ----- .../Public/RpmPreviewLoaderComponent.h | 1 + .../Private/EditorAssetLoader.cpp | 12 +- .../Public/EditorAssetLoader.h | 2 +- 17 files changed, 289 insertions(+), 519 deletions(-) create mode 100644 Content/Samples/LoaderDemo/BP_RpmActor.uasset create mode 100644 Content/Samples/LoaderDemo/Blueprints/BP_LoaderDemoUI.uasset create mode 100644 Content/Samples/LoaderDemo/Blueprints/BP_RpmPreviewActor.uasset create mode 100644 Content/Samples/LoaderDemo/Blueprints/WBP_LoaderDemoUI.uasset create mode 100644 Content/Samples/LoaderDemo/LoaderSample.umap delete mode 100644 Source/RpmNextGen/Private/RpmActorBase.cpp delete mode 100644 Source/RpmNextGen/Public/RpmActorBase.h diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset index 8f3a34b461460f93168aeb47c90e537e6b30cbc7..2b4ec4f2b1dcc22ab60bffc646d03fa26076d4bb 100644 GIT binary patch literal 129657 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 jeY*h!Zb;b-b~k&KeL{^=A6*EvkotZP literal 124107 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/LoaderDemo/Blueprints/BP_LoaderDemoUI.uasset b/Content/Samples/LoaderDemo/Blueprints/BP_LoaderDemoUI.uasset new file mode 100644 index 0000000000000000000000000000000000000000..d54fc1362f30fd3bc40d10e8644183329891b9ee GIT binary patch literal 2518 zcmcH*TSyd9^qQ5aS!P8<8u_JV?kn9~Mag}sq^qqg_M_Xhd(+8%F*81jpfCar3M79L zidcevA}jh3g1|tcD1?lV{tEjL>_PZpR&M9)&TQlCh`Uq=?mhRObMATGduAps9=N#} zi9}AM6Oy%#ka@reX)r!@-JhK^k1S-r(}!yx+cm{NV=@WZ0N4=>P(Xz2fKda|g@6wk zj3$H0Tvk@Av)lA`z0PQ^u-Ht73Y*S&*lxF@Jh==O)&ma1lp*e&8<9f@2N=gFj4w|r z?>)$8ZNtlsG5gVpH+TBR#s`kK*N;@!*ITB#M$tpmgASla>14FvNLn7ON=jU|@)9TY zQHH0bJQEA)lQ{*zXkk3v zOebv&dKe#jmJ!%sz`^(_A7VU|4EY_jU#O!2Z7bss`6#b#3^HEIS*bs$ zHT!yKh+_i+uQj*0|1ew)WX@ACS(TE8Hl>P;srNIOGHt7eV?%=066C0>`Q&Np5y)ag zY0YLC&(%~%jP2j{VNxY|pxL~v5ah_+j)sr9z)@PL9-6*_ClqwIg}gX7{|qZ_@i9D4 zguedaJ+QOVQ%sLfaJv%^e4sOAs{T z?|`vZh=0IRPPh1DC;z-r=a^rNeA&L!qas*}08 wNW$Ov9RFyksEz*CoClL?fHwgLLLrQ#{+XiEFyWh$)`;;<$@loaCg6|mH$#y`xBvhE literal 0 HcmV?d00001 diff --git a/Content/Samples/LoaderDemo/Blueprints/BP_RpmPreviewActor.uasset b/Content/Samples/LoaderDemo/Blueprints/BP_RpmPreviewActor.uasset new file mode 100644 index 0000000000000000000000000000000000000000..33991f242aae64dbfe9e398d47bccf8da5e75637 GIT binary patch literal 52313 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 literal 0 HcmV?d00001 diff --git a/Content/Samples/LoaderDemo/Blueprints/WBP_LoaderDemoUI.uasset b/Content/Samples/LoaderDemo/Blueprints/WBP_LoaderDemoUI.uasset new file mode 100644 index 0000000000000000000000000000000000000000..397bba20e72b464cf92f74f6e7a6b4ec4380f663 GIT binary patch literal 110065 zcmeHQ2YeL8_ur#Sks=@<=!IUSB!rM)BgrKp)JQ^C!X?=xr!U9YoVsE7?RWeOuz@d^T==h9L$A$mpK{i7M{oA-O|TErUfa}V z)`@%FapbcbFQ58*Mn{4j5c%!dRh~_`@AN$5%&>dF?n4Ncv-pD7@@L#P_}YyJkW3KQ|tb9?X6S4KdtbBaW9^@+kz9eB0uUu1JI(@ z+W7+}yLwP;)TlXm6GqL}{kk`-kDf4QoJiNSuPz^vAe{<{u=u54$00KGAfo;TU@!u8 z*fvoA1c4Aopk`}hXV=!u)K^3b^uX9+Z%wUV509N5^j7F0k6sfTo8zz3YeT+3Bs_Lr zPEo0mGN*8i6`-wq>5dS=1MU@^2YYz?Sb&dc9%BzDR zk2m7gey$$30@9N$Kw)ibg}-c9JcmNw`l3*+{BSkh9w+!raY)JVF0rIr>3JbJ|I^M?hiwC<5fO9ke<3SY#F;u{Aq zEA5Hmc`ItYfeO6>CBoE~MB~|ymChnj*UF}N!^PzxUu~oi!h#0s6-8c>5N$^K@BV&? zvXuDB57nkUP&HlfppZ(10$zXKGDyjxbMF||83hZ&(YEwD{^uhUtf(%e=hp?wwbve= zb)0C!biK-3UO!XUD@uZtTBR2ss{3c_m0|7l8xGoMAXOEWTVi4@DSIFSj#qgjdX6tp z;R{r0ZS~vJAY{>;1$Dj(?b-JI=Im~U7T1R(dd*xt6h?2Yj_fijrjY3E&fV9&Q-eZ* zio9i!(o&0}l$t~^Dxam((q@vJmPXr`11S;wC`~P$6%b{LsmxVc)D&;;I#_>)tJ~UxaYo9ZWLZ6Z$Om~8?l62!ZLduzw^<>+tE8;VV(=z6F?q&iO2SX89fS%p=dqyd*jj`3KEWl>dGV9^5Df6%2p8f$TnU!BV)^MsHHAUV;A?6 zK;YeY%GbyW{!i2EwJ!J9Ekb2@mi)>4fp#2^7r_Yr9kpy+gEJ zqMGh4qtXA;pO$UJ@L|Tk^6Zat&|RMTfVak1uDWqn*&$?O!=NUTTQETT z=;ys|5)8o@ZFI*qm%n$tpkRewt{wi_Z~Y)(Iz7};<9+9RumQtwo_wBDriZE^WTkPw z;ydj--7i|UA`qnmK1b&;fqa=v_)waI!yI>zD8FXB=@V8zdj~rh;M2xSHf_!|3`6W;y!2{tE zTz>R_mVsdLkX!UiHA-0w?y4*29R~ivVe&`3CDiAd=jhZDkW)yW)Cz6MW#_($*$}$m zr5>Am$=n8ESG|}q6@(RrrOPF%c;~&{rw$=tl0ioJ3BntGWntF1;mI?{n8iNx&tCxh zrYe`NM-2KS+dX{?+6?2edB6E*!5C0{c`#HF)^bPB+ze-Nx;Gq&RgSRMdCykQp>wBu z>w|UDS6?#jmd@bfbiFd7jXi$N=U~8el5lT`>~Aon^~??DLz^(g5JZJ%RSFHQ(nQw zY`{w|o(Rwky@sSzbnXwcm$o-R!YrM#*R8jsTV$>xFPWr>cI2l=?27?K0u7Nm^7gw9 zfXMQoDCRHP?8+XminjOyqAlGHJHrQQrD;l?U#EGVH{x5S3&T-6@MNzWNo1^j<;2*+ zfS*{zmM==}yEk zKnvx6sY3@)D6HM}#|3^g8&AR^(fk45$B+s!Jbu^ukuK=Nps>iAKJA=sP&H*(c^a7g z#;u=$owF({Y1S9k*0;LxLUe}3W!COG;oMO)N4LW5T@+k*jgPPo=f!(5L@{Tk3! z zMInqp%E9RL;@~0(DT{=(y>A5fyu}Pfmxr~xm$wz8RY>>xwYg7RHwMG^Y`xY`!im-! z<67|k6HY;SN}L&t_$upRaJBXqytEFDHv4o}Lcrn5J{unG@ia(-I>x|yN^8lq&|c0Zyj z8zNakbCD8Xt#;dI?f-@GpoA7hekncYHnhSba(nvD&V;*SfO%$-wQb9XTtN5)MWhO0 zt>2)_#|tnm31p;4o4L65AU{|tJ<-d@rCy7MkjWCZ=-AYUR{;x~FvA3Yd$OmC$|>yk zABVR~RnKBDDHt}rBP`Th^7(?HG>|LC3mqUPcJn?h z<*@@jn1h)_C#OD_LE+H8W7FJ99^o_$k@F_SsHyG0ANFCCCDjdB(1clR-e|-Gp;|kG#3^tZC zwTZ`fZwo2mB%#r$QcL%)6z&?Q%$ZT3WgPsC+nk$VaBIjj*|@myLwF|9+yU6byb5ja zHs^GJkV+F%d*9HfQGvmAEa&9}S7?Wv{=i`%E4tJ~@-f|47V^?4{m90Bc87B3kW)p= zY`!nc4}&F7_iiIkXE#K8Q9eFhu=N&R!|kqG$*Wwq!??H3X0~) z>7bZ+md5%2rMX(SUsAWC718ptY!sC?eZa|C&|Nhn)7k_#9}I-*8D0dI&%{FW`5Svq zl5;U3NpdWp6(Q};2hSDyCEQynWd~p7Iv6&9=LA~lo4ZZ~AnG~|n3r=HOkn{nu1NhE z^m6ZXk%b_F)3ZJ-xB-n}*PPZdn2TEI;yJrp(xSM2Jm<=%u!hOyczM!T{?m4U_S0)& zx6O{Z7Cvs_9xz4<$n}Ydop!;CZAU`%RAOOe30e0%OfkC^xE_Uis1!Ds1$sow2(1$y zJ$tL#2dg%Vp-3s5KJ7~v*sr`6EnKjs7i3F4M~hvw_mw)RCCxKLU&%p#*HbR&yVaJc zt7pOe4HJJJ0W~p+T$j&}SOy7zwKm#8 za&~dMF28VsVW!BwzxLvo8IoliymJ9R#+u=*5W75!>X1A zZkb{(d+O4@uw*huXupl>l&lgqCPzi)rBEoX}CS%FyE67^F8G-ACCcc{cCO?k0}l1d)6V|=N#sH-eJBM9Oip5 zNxmOd8tmk`4)VeNz2qR@%are9n|w?g>n!x`9h-bi8|wu0<2{>vTp#Q98Rord100;iDK0AyFJ>o) zD{^qR!}TQrxhO;dJ_7EL4Yp|poMJHYHG~Viv%~d63*f>6 zo*5UjZeMo#jAl0&YltqaEt+vL5680O*dPWYLqoXQCBRkJOt`+F25@VZPy{|GCghOA zS#%-X(*n3)H|%hAl+hYbEr1Vk=mlIx-9!ML3%?{VsCR`;{*N3V;@USn86@@Lo%X?#fo!~lMRR~-+ zsQ9lu@HYaP9j+>E&1Qt|=W$TH#DU&R-+YZoa`qV_1 zn{Z*Bi|Jzju&7J(&}D}U;s+jfQz&wGZGK!S3Fs>B+H$z8bmcRF)E1|Kx+ZRf{oMq^ zxQumsPyrrpQo201d-Kp`hl|H$#?`KO%i-d2nQ`6TujO!Ye=)8~UVI?FIt_OH^_!)? zux!Hp)mJ&Cy$3fBU3R#*zwT4AF?LkT;o|y0OT$sxO*OLlYpZ%K$*RK{i#Qq#=2Cj7q*Or`?!^LuVvBDK8XgORghm5Po^p?W~ za==5zwW+A(aIqXRu9Yew;V2(jXE%s zLU2j*;QHCb!`-M}EF&`yA5e6Cpf?XLyn>6iBftm1GBV>zQTladb@Sl*(Si&6=8S8A zl8s0FEr*L`<6gzXEy0$<#j?S;E)KOEF18ztYhhi>;bObNxO%T>Ib1w`F|HR@HV>}v zO>#&gsPXbR#Nw=;fN0ujM;N#eGoD1U?GIHO)g{jGi3zrZfG?{A*uz~^r32SFsa+LR zHz&B>FyU&iP>edW`Ehv(j~!iUXSED2JG%BcyLoVZZ0fIUqU&e6*lzH?L(cl<$CaaS zp=QVi<66@KxN;RPD_zgD04`4gx_X||JUo2L)L%;o7q88Ohl}V!SlB|ifCB;d`68xi zx3v&1oEk*{u6ZJ+Xpc@)AmcYhODDR2sT82pgbNfVqmt98+BkpXLxSN9nnI!Rb|K`kI~*zs)34wfw7R1H#6s9Zh`j+D%&r=xJ$t{2jW20SvJ>P=3to^n7#^&cC(`D_igIf@n zwZE=tCR|%g`h{~2kI{v&3w=rJXc^T$Z2?>u%NQ4QnQv&^3{+adcsfPorx*UFmcUqiZ-_7>kF}HHof~bYZ`F zAG$`-wJ%-!(S?2I1Lzt}*BH9S(zQ2Tm|pjyt2bR}S0-KNfOZX_Yj?Uh-p3U0Yl`O)!Z1$Xoj`g~8i5V*d*C=MPBq-zje=o>r_rfUdY=m+F=)0Jb2XRCM$-94sw zF2zwEJOQ786VKomc*xJ-9r(%5;3Ig8XY>Pji8h0;;6L~Y-hv*EgU`r^`oT}|6?{e> z@EZLGexn}%LmY7AL%#rqIN&IcegX{m&<^0^`nm0(1NgY@paaj`cF=)mrX74hUreA2 zd__k@EmnVJeMxC5pfS)&`ZRD6LUn4FEqs$ znc@pf@qMNo6fpmd3(1bRF9?;RAF2K=tv<>Y>dppvFw#}uh&=j9; zicdAgr>Xcny3a7hi%jvEruZz1gC3kif0C{)6o)Li>B^xCxM$PF{l@J8e&7W@l-ZRo z=wb?8+<(Os=YHk(pdG*uy#Jvq8e9}3{7+B^YkojUH)u}kMpUAp)0(Y)C(A;87!o4<0sbblT*JqsQ$xZrHHQ=~?^b z=1nP_GAd)%oSFG0lMANgp%Pc;E?v5J?Vi%3M@rt9VPoC|zqq??|Vl zX1CkzKyUkjOM9lRKI5_ugK}uekE6tFO8Cx(&D7y7{)-@3`}>yZ`;r!;d`r*yB$;`NE4Yz5L3nul@J+_qM$M z!G|Ax{K==Eef`b1-+lkXk3aqV3+Mu!93*8Hy1Elxt=hC{-KIV0aydq6d+*ZS2Bxj<&@<;Bk!O>zI@UN6+zwK;S0faS;RyYGZIp7`yFD^5$#?bEgU+Vzpzaha`M^FO$_L$9X} z`Qf8yrkz|F33q;bK_qMLL+=lIhKIi1 zIO@|SuYdi}(*Bobc6{>4t#4*Mwov={6N`v^a^_cEuNpn@hzGyB>e5yx4R825|E=QR z-UA5m)4;|U_xw=a_EJ(eh=Bu|W)~#!E+=CBw{$X*4)-SfdX~N^b z9eC>t8*dx_{)>a=Up?~bEt#7J72jF&z<@paKK{#|A8r2EUOzpS`+WL;u0LX0(Ubun zy?t-*HcPs`*ZzgO#`ZhphlP*ccSK$Jq$fJxQn2acwl{CC8gN)q``s*TZST3H_J|R_ z-9U!c^T51@uU9_18+`piY0 zT_02hvroTw-rBUT`)xV7-3#B3`h4Q#i%Z@euC2bKc4U5G=AicLj=b%z%U8VcP{G!R zU)k&PF{gg9eE9M)PmRidZCKVHt>3t>e8po2-TLZn*WdH`#DQ7okG}SUz`MiuZFNK! zZE3{P%1Ggl?O#}b#{TdAGW)rvNsWHZ(f_+?-|<6l>$&FZW5=w$X3T^0kAJ4Noi^;0 zGY`7q&J7pX*s=K@g3Ez-Fa6onqM$@!`kadd~;(;$Db?x?`z>K?r@~K(PjBKfp8-6or9Qf zBfLs37d%HxX3Lih;E!61(&rI2l0SL&=afbPSmCBxv)MKUb4)a`$V*>Gj_CY39fC;z z4&N!t59v$m=);ip!dt~xlZE?;B>K)(g-?FR6f?tl^Z`=wWusp75Su=mzO7B)Getn- z__IUNFMFcZoT8EiMS1i^&rl706$>ng<}tqJMNpAMA4#UKjp`NA zl(2AahmR2RXwxT@>GMZ=sFFSe7Dmxn)Z*K4woHWMZ0VC40wwLWVRl~-GCJS*CXa(p zewXGB6O(T9SIHnY;yW|Lq94qeQ832%QpYecm6=H&5W`pR@IgKKRhov&VQMq8u7*EA zM&F&5ABFP~(`1jKr=z`L{&FqB1=?t0jBLdgGkvlY#X8X?S}4Di2FXfI&q~i6KOs$| z(I*q>gR8Rw{(3S0$oJyAE11RQ`*OsG86$UZ+tyl4B3#-Vw6*}({U-H{IR18tKKD_v z0__kjsOdDv3DU%)Mq8#;YD=}SfY#9rri|caS~Wq#RHCygbs#;}Qa(WIC~u_}X)v{w zD!aXHXs7C#MePa@W*6~8QfEMz58YJ?`M@@@-4v{dXiTA5V1)iEiJla#gzhzTRnSu< zamPjT63s{Dq!nar+bdJ7-*btx()z|jdZ<qi)uG$wc2Qwlo7-u zw4zezPp>#kO{17)33WrKFrwPQdMkWVK4EPQUULhl)7m@NW7lI#qP^@Q4^h@+^k<58 zIPE_zBMLG8NshBr97(x(EM-|sl-EVRGq=)3yE-yHS1TfkK9nd#?e?+<+WSWHV^j{) zO0r>j_M!D<*jJC}2WcyoiTVt!mpNdO&{wf}c2{*9oNHR2u}pb{?!bRwJ{h*yE{(CV ztuT0Mjxm4Dy=m4;gGbv-2ai82GgGMd!EG0<%gbJiPwA&plG89y7%;sU_6Q^?=#j8qwS#N5Ykrogkw$P z!V>Dii8T7VXkS3e$@WshW41v_3E^EybX5wkypHfV$>Ub-Nlqz`B4+vwd@k~5nujWu zDuXImr5eFc!#>-mvIZrmWUOlG2=YJor(PK=v4rGN7ZYI3!KgpR6#+Z1>5QQr_w>h|`D38-7#BcF9?zF|QfgOfBb; z02r#sON;mX8_qGz5oK)e;ekNs3|fXeD$g9noB$)di%x?`9b%rDM>zH*3=a%5B+1DL zS)6Jbxl?E_`Vb){F5z#XHlE+p7M!Y`tsS^iLv>UVe3T>1+~RVQM(CuA&OOK$^Y1^^ z!(z{GuHcu|!?N?641XuyryA{u+icAyk+x*eIFE6E5?QDTB9F(i{dUip~C$m@Bh?ZV!g5NCFn+l<|kgT92*cG6SxL@&Ep<{zcq>e(#Si@ zBkoV2k$nP<^^=50mM3Bb!e2w5$^OZs9C_4pnZlbH=STwPs9bZ%OTip9g>+obcv93e zd=2`CUNlR91=1-8Rv~exkDL>%MwNlh#oRcBH~_7exyRBfNFMnqShY-{nyct9g={p| zIe`sc49da7tr4|WQQSP&L0#~6R?;ZyBCbgJO3p?0BxX{n6dG+S2@6&yv5J9Jv@}Px z@cb=ywgOKPe>`q(?<$pf{$(TOU?-%LE?EZ!BKhk?y$DRV`?4srg+2BYKSQ4@*-gw9b(4!%6pIJ%`x60%LvYNb1oM zw9*idEpZ8Wq1@*@dO5`xyBfiMgNsh`Ne-ovErZQV7j`e5;+Umo5$zdd-O>o2OLq^^ zmq)RQbYZsY5%v*tRAVKjNJwt{N@)D90?)LZ^8i#N*FWaVd}`r=bf&MEY+oQw@(v^^ zLGMD=_fZ^3UN6smV^<^MTW%*SE;^Gbxttsa_r*lPSF8}E(_aSlLk>X-h|BOA@RUz| z0gnKy1@H=TMCv5!Gmo&?`#HkLwkE!>j1__XRZTnfi1*vr#)2d9dNfh2*<@0`p>Oi3 zcP5bbbRQb16` z)f+Qkq`?C$AUydBAIs!UtC(CGCkZJ852Y=fKpe)-Pabg@kcpIzRLEZr@fp$zPJ8H{ zNvS*DD)K&$y);H>eS^;gKt7zyB4`BVu#lO8a`0jTL7=m+mGE*g{{Uy;KY%;n2{aZn zktqtFb6Mo|V|cm5<(e_@0VCEdixoaeB`kumn#K}h)aj5&~-jpIUm=kP!HFazzVrffW--Dta@EdKWWqJYm+2 zT^1MVl@okJRW9=xv$0e&#tmaV87G*=s?&csqiCjYI`!a6(%}lyMnZM*hwwCVq$I$TNf9- ztL&tXrdyCwNA=sa(KxTQ#8Dl*hK`jSZ=*_Jj$hWO5*X>q8&v}GTH&Y!OHa8vv0p{@ z2Nne<*z1K9>W=DR-72S3hGk^8;5jW7GFU;+{0=~+s!K`%e%X(7dZv-jU&ZT}97Sf3 z?vxW=$p1=OmzU5bLX)xkV(jwr8Gjdfcuu5}TN*p=urF^t!RZpV1ob8#&Da;3NF2jR zgB4A=f`hd!?3lpg#VQuh+pyXLe+4UC*zL#21DWzUqJmF#IT?AZG#R7KA&zjxjy4!U zKnwrL6IFoXgrWiE(FQ9k1~k!VbEs+wTkWQf4Qvx&tLjLGaBkbMTWlN6sVSy(zaxI| zzHYosVh^cdS>ZOC=M%=;0RcyKv{kvJ|KJk7L&KPP4cq*tL~Wz+!OO&$20yQ1d~B8A z4b;)TX_!i<#Jr~wQuP@9Zto8>G*{RJoKb+D*h9mP+QyzstR0w2Rs`oUFy`jdC@#-W zMI6;=*Z|CVpz&BW!+HfQ0p>;-q$6nrgO4faxw*pk#5%X!Axfikm*8;2+~u>=iDtcK zUKw-VWsYcxJ>MkHyz(0d%T*0Lb{c2T`1AnIdf@#z7oAgaA}3sKPPAOTqjD_I1*GlV zS1VOXiTCd{*RoqbGta+D#JQh3A^q}An8O_Li6s-e_Beem=dGBjV}%#99>}VkFJZ2W zRY%O2<@UiD-%z=2!85{HVLOeZ3II5p5@Lci>rjWLV zb;)IEwWGSYZ;cikJ7CG{KU&q#n%Q`-afqrR8J>NF2V6%IE8p3H2Vl-`tYH{$jbKiP zbrf?MEAP$aj&Z~();hV~iM9ng?L1A@`*)I9_NncujNO$!-4WkdRxyW^C*H8X2j9AYq!e>) z>=R)}0G1T9Z`e?r)Q0VZXM^3P9KyBM5k6j(HYl*llzC_U3`cb~hAZn-jen z_PP0;_Ol(;=$s?#RegV_{$_8|zQ6el)H#kg#eEGA2smv*ire7uThv^}=3=ZJN37yYMT*(03a*u^);oN?lOM|JVKB=7Q? zcVzLlMv-`XV-Br`;%hT5`d_O~TFCsxUXMIM3@;NiKe-MGe-svSJn0MePjL#0S1S-h zId}&y?LtTRSn`<)gU=B`fzkep9M#8Mk!QD!+6)V0_9$K2#g1y_6*!|lyi>uPy+qZ) z{-d$J!K)(WwBCS~A@g{BsUytXACO$wDx9*CYe3jRhtHKyeE@rh6IR&a##tZu>l3Lb zFh@Y2z@xn^VJmrUkTvo0Mzsv9(V0RUv9ArU5ThwtDpv-iMaCOa*jY!*U~yrS$BQSI zc16OLvHxx!6RvDj>oSC#!UKS1KBa|i-!0lyX}PQXEzc2&aG z@oHkMpK`Uz-_Qyrd!zC_0KD-e-xtAqD0nYz37r_h+gx?xElz%0glmm0<Q3H2LlZPKH2(H#)+{eH=@ZVgF)Rg>e~FWEXm^#1#|7>c z^a(~c>?`2iM1B_s{c#xW+>fJc678TPo+{!43I9NPGkGAj0ciuNW%7A~dd^VK6UB2S z?d$qQkE2YcfQ=L7vqT*A0?QEuGh@LDUvfO)natq~ldN5~I#sj)b_z74sV7OncxuZL zv`>%rV)Z7T16Wa#m|?3W7htU=EG+@om>%pg$THAWE+bn4?xGdI4Y}rgj1MO+8OHd= zD}3k?#sMjj`7n0K`e08ajC%}r0+87L#cCCokerS0W1Os#d6Mzo+NTEI#Pu>&jjIP`I8GKZ#>)DW_Z_s9^X=?Ad(DSt#mj{u^DMV^x`Dmymgg^| zL(0-WMLSqmG0TXx0ETX`-c5*F6ZpfD6Up@@)^1}xi229Zu)8H;j1Oz@3^8EbyUFpz z%MV6PS(~AsSf^qbaABl^2gR_R`k6>SWBW5h$b;3ka^Fe5Iva`YG#~4V+#{^{c0Gsn zCeF9B=bZK&YhB~?9^SL$bz(hC6&{k+j^m6r_fqoSWKG}c<4mG?k)4Me&)Yx0H=S8k z%)YqSck1R}w>OQkFPUymjN1F1y7|}DO`K9-o#Wn|Xc{Bgt0}l^-iMCu1~t z7Vj%NmmJ2L+z#xN9B3kD7ds_K@*&gV2(nXhc1jNBmH46+OO7$B?HsL~%nJVLHYd~M z)3Bd|cY5V`XqFuIGm?*1*eBxgR_<-subO8@M|h{J=H~s1L>`IrbrR?WcKV9iqOmf>kKlcRo!;KPB zi|tFuHNIqNqp1~gj|)2%kZbGxpopU;Lwf)Ect>fnZpD|*mAI|HU&EIO5?&a=yjWEt#?i?fJY0{lz z1f;ULG{QKIYiLBGnW=G3w;{i&C1$30>y*a^=@rMmslekzVm)y>kBFVowN$-LpP6De zLvkYdyfKyztjNZXu5yP%)+V_M+2VDHDVbxa-Ry&~7|4TrV%l%o$_ zGwW(*>$QGgIg*M(!CE~O@#$gumj)WSOr7bs$Q#lF5m^fLirBER#pNMiZDj1683kkJ zg!PcbHjMs7t)|TgR@C|Rncf!=Vft9SrlqE5rDu+xkf!ZQ4^cLh)Ys|~QdF{_D6e#8FjV99lf-G-@Aq#zVB52M z(3|QqoUQIRtNY>VUZ?JHw^{``NZs2QIXdUunJn?r#EF_VG+E-wDzS^Yb7PNI&)2GZ z-0gNtfkO3sU0hBDxWQ7On|fZFs6Z@y)dAd8zF)60v{m;G>V7I0L`OhQH&_y)e9fbT zo+@#Oy7LWRoioBel|A5DLiXfS;y;2t=bv}p{@j~T~EO>3f|?39c>Q~oLJX|g4NCzDWmFqAoyp1+kPSWjdj@PHZ}H!-kI z5=l0Rb18w1Eb=6*1ukO3T4p<~rFF7e+?HDY8SFt>Kzi@+}Ma^=WF!feB73d#j?)N@lD{Ks>WxapVXvO=*6GRl(_ zN`}X^6O%jyF2G**za_Y#-**pKrm{4-0&cqEX46~36+u7{;yw0+vJUq>p{%2Sxo*_E zQx3p|*zgaL15D);$^lH7Sq>&B7Qj#U5@k4DB`i*q0p=EgiAacwaW);5TOe|P3SKut(@_nS6*Np0ruqm?N41x%pkY)G ztBeUzouM)$?5jZ*RNQ9(U#ZgK{H{N?ZQFLDN@S^wSTbw<2>_`??@y#_99^k&rO`E> zu5`MF(KVc|5p-ePfliF1D~qmu=o&@WzI5$J*Zy=JK-Xxx#?UpEuD$7k!`q9l-gH5) z5UeIyqfg1m!1}@CPnm+p-7dVM{i+rHHcvbI;Vq9|^!sxaO=b)WIdf60%ViQita&0d zLt_uUF87_lQKwBlFZIp+4jFsv`{u@CDH0(b%^I0NA8?kTjk;^^(on=BdR*eElz4oi zl67Qun6>c;yQ^aHDaWdmo79~(K!g37;tf>zb$50+9M&W5TyLntJ*T#U7J9?laVo## zZq2x;Y|u@I{y1grnDf4v(c_CZ&74AAiMe%xLKVl5t)vYJSvigvl8}`zk*QC}%E{!Y zGzTlOIxYefMX|ET8_@mk;^n?bd3ED?IB)d@=M?w3d-{e8?%nAAZR3^Az{7Wm*n~Ws zObkxQ!+nUs33+&SGl^qA#X@0Zutx!5)FWZHFW{~PFPk8VHLiE|>iXLi`K!lwJ@bT7 z-L_b{XnjT#h)dC7pJYb_zz}d>Mb~=#|2Yw&H9SWQgjt0tUc7K|1+x0+g2E4=YYG5d z-|iG&xHup10dLSx?}cqT(KKwbs?->3DJ>dM0qY=F1Q-JDtLSP(!>HTh8wNq&P9^$$ZV#^#uH#p`Po8eTd00CWyfIDo`S$Gg3Dq~jpIz(ka zqeMBb3l^Ol)sf<=Q7p8v^7R%;E-FC401xv_5-g>24-ykmx_~(IWKK?rkm?7d%v^Ixjek`0x;2Ger^ZYzX1vh4?2r z(sx9yfc13~^S1$+HKqSMl;8sN5xNlO;0FK}hp1#TLjdT#cwzg&+!stX68$hS+-npDG}Bx_ z@}Rw`^_U-U=>InL>|SNwuugmTx}Pk_Vp)Jq2*~Zqx#=zoM1r+ORu~inWbH0u@<(P3 zR|m;8jn)ezhk(rNUgB9bBp8VVYbdwkf4d-c&0>tXu2rj$O@yd=Fo5ppLN**%DL7yV zxUZti#;U>Iw523m0uQwcy9y$h@w`bH0YqIxM5;#rby@~99>i_%NJSn=iDtKh6&n2| zYUUc@@v~tcNFoqt*sn(k3TQV1w>dhmg4_(GYY<%<=tAJcsC`1u^%O}4QRap~kF$22aTe5pcZbHL-$$wQMRGK1EuOmX~mQ$oU( ztsF|^8c@OCisbQVF$Eq5qap>lYZ^bYspQ|59|=iLA*vFJfamW_a??a0DL-;8{K%tc z1hxXl%18QyQG!sf;76-Fk762+-YZlB*T|l0S%L~R*Sq&`7!x`*@B04NWIVrm^&Bj+ zA;8X=gJCXDP~bS#SWCLGCQ;(1wp!!Y9tL~b-dK6+={YyQc|?cM!0GRp)rU0_7VO2h zFPZ3Hk&Glq0QBD zv@uF2H25-x_TKl+^r_bt{J!ki`>rns4OZCIszM>saOb9_%_Nsi!?ZRB{#&j`_v=-( z5Lm4HD~o&qaa0o-OS~aEGpa@V^AuGRcN<)>n7|1!zy=%+!$^Gunsu7B6* zFFt?qamb#GwmelITbF;Zz3N8xMeu`2W^x-erojX5zpq2Zs>%Pl_RgELH=nSMdk<`5 zdo_&WR(oX!+mZGvmD>2ozx(e0@H?w#Ts^e?ug5-chHy}m$p&Zy9aei~2m34AE40^Y zo9tl!Bzpz=thUJxwj=EoWb&FV;|}VfznXo*jm2}W_x2I{oXKSJ|B=1g)D(Ml!v$@g zef+j#r{B8!SNFd;X6>nV_G&os-D z$p#e>yU|vA1=_5zzp}kTd#(1$4)#y7SD?>ouk2tu(q2I(S1o$)t55#O%)8*dhvs*9 z|JE%8+UZ|`33NcDZ4>;f4i}wweyi_)%fBSOti3+$lr!z@)d;HGYOm~IJJMcZX(8ul z{l~jk-RC)GOZJa@UD$sLn9`8F0v%S{WC!~z+bgu!YOm~I|0H__`mFZK4z?rhm00dN zYgf;i-}NiF=Bm=KzdZ5Z&r!3J$|Ux}5r)WH^w*`8syI)b^3`*~m93R3L);qkw>0xj zxa<{HrPvUhpq{JLohMylFLq6$#I3Y&b(~7%1~)j@+w^-zjcfBmlh0o9&`tNe>F&r) zKs{vJM8JMXUdOZ6rxx~_vhkgHp|4(kF~bg)NY>j^d9}J>iejMy?u|_87=26BJ;Ph; zp6)9Pc|-N?S!IXlX83`F!Q zJrwTPv7}me*HAhE>CNCudRf*TA&@^9JhYBV1vL79(K47xZe3{uR3LF9@am-6NOn zd(b>Pm|x_UKT#ff=y{r6-%)bSJ-n>m9nvdDNRAz<*Att(5%+SR-|sHd-F0ET0z3rY z1Ou0O>Ah@sU9CO;MAI4TcJpT()l_xjw|5q-et*iX*L*f?>ZkYbipmr5hY^DpY5Q+{ z;<%J=XIyg0k;RX`bJ+jvVE1tWx<~o5h#uyG$c{>wBxfYu^a6UY+=n;Li9?ZU!4cxw z-tG#$(p%?`xXXiqa3oY$jvT&9H~6{S8+HeR5qD*NMl8j`StVr6P%!_lx>F+ zV74Bq3kBSgdpW_NU-t%xdm$1S;)OTh_C@p>!4JU;_wanLKTK(X3h@@dU{f`zG7i~6 zS)soqfTY>jqRwCO0+?og_AK=Z>*+f5w!nvRfUg! z(|gvn;{*P~mR$WXGA3hbsj7)blYI5u_~GvZGJOe$ziU*r%!m`zbCtSBhrb7CL`pdP zEosW|_xc&r+I{)VklfqHesao~lRdj~6M&h~j-vSad!KO8xGRn>xS~3DNLGg-_u9d1 zU2*hJ$GjDKIem)(l~OR{kP!8oFd-&a?1PYA^dSQJ`0Kv?j+s2G-{kexn>*b9!Djep zN&6V-pyt2(+_PzICX$P})$PEhr0^2lb^-agjzV@}E**WKH{!7!& zf3?64b~VVRAjTs&C2E@nQF~Wqcr>M>{N9E-imZnmI4KZ+DsP07ji}>TOjqwi#jgG} z=Zk(NM@>C>ap=80wyt>(H6>GLq=Ok&%W4NBPb(S(VxnLhhOWXug}#CZ7P37mS3-gk zCPb^3Ck)4lbA}i%Wr(5HZsT*{RiI!pRBOXs7wWc7&fd20mO?CZhVeN z%^?y@1-LNoPN+re%jDTebI};KB@^dTRqAhHvTBK$jW$`o&mKSVq}LyswQ0fkL(iCa z!QE!AvdJ1vT(FufJJ^mlSvVy6MWIupMu(;OR8QV1XK|ak7K`Qw-LAL~oy=M{T(7q@H|i_Ij@j8BbbLd04AfZdm>q1#+cC^%nqtR5jn$6X!Tu?B3}4vz;H!%Y&l)^t>Is8t zPRo5N^DzQBk_4;G0-vm~Ki!VCqejS=@jwO#$Ck{aN*t~3Vu2I!GZY|4-A`3_o^@&e zqxl>rGzf8}AC_A=N=@Zj4b0(yvj?YQU^>}hJ zaCN$!!S~bClPq_6gf5}D9PQ9|#=o=q6=jVmo zdNiXoekfoC^}Dj)Z`^X%ZJtwyr?md^ZuiG_uroz&`6Hx@*d^|+Y?WfJJ(?MBAu#1p zpo55G0Y+VD@8xeD`a|LTFLI9Wb4ic&r2;0F@P35Eg(~E#noxz(Uynf}nQDwL7G^8a=PzCL7 zQXW(a*5IvM_DBM;$p}tpkxoZx3JEO|RvZ&rB(YLRrL7hzPt?gjqG2iYl#w}M;`p@e zy!7%#R<)NqsR`z2FI7cCa|9tv zXpSb+Fwkh9@|WLVZv9EQXV%U4@AK%D2NWK`tw&4P96^ka{Md79YR&w@o1dJu_USKv z9c2gG4s*nm1Fh2_B%3ogB*AE}^*s_h*p4(u!>Enc^P-^732oN*5kQ+22DgNRU>=5x z!32>C`3e^ zCACgXdMo6z6JP6fa+`bdE;-=RjCV>s(a*r5C2XD2D86-%c2iT+=I0*$*-h2mQVY+r zgKdX(V#*Z*9NijaozPzETO)R`9ci70QyZINoj`~6?E^d5U)?%^{=blQ>OjpwaA=*< zvpt@)yxiRM@e{JLvdMeOBXcz&$CIAr@#JNudj9s-$t}2%WGa``I=S06!FOuiKeXzu z-?mIXW6jNJm)!ei%%?ipI*q4hrQNmX6+ec4&so#?wGJ7V5Bby%wjI`qDOU_|bZeA# zLVK<6h}gk)q;xu#er(4kr0HL!#I)vc4=I~*Ww2l-CB05=7P)+sA5*ONlRiExKX?4ZjK95gN|EenqVKe}Dc0$*UT^+3^{Ef?H{AH2-OsBi zn8toPTEf;To#JaB-{W5&Z0R*)%|4xKSAF`y|_-xXd z$7x@2vyqWC8)ujXm$%t+(bzL*t-Z3%8QXU8*V@6FsM%mAb1klYNdS$~Y|-BR!zX-y zz;3hd{PeZeAN?M<-ww7TG@EH`XnaL`C-fV;SK&qfn|bCD51g=U@o^D5ST$Ej_o#Kk z5@KQ4?a{-&s(`!1M{C9EeU?A#%@b}_LuCF*ii!n$1eW+FC~DGaz$y}JR=;LY)U}fq zUbO0kOLLFjV`1s32er9~n++0L;fDei$FCT3O?3w?wczM;0-mzf$JE)uwnI^wYXYCc zpGGMv+G{;SX$RX8iVDd^z_F>OY{t(OrHwBy<*Vn$Z)PSPf^JjQvWO&>?1ZDtr9|p1 zRZWSy^D4oC>RFquB8jEEyUGzS|HrD7o76peh6LD3s(pBKu7X~q3%e_PVQ-nA-jbvL zpder3#H)w&B4@eRPv9{fJt1!uy);|tuJr}PE3;*FK7WO~vMx{_@dX23zb{fhrt!NO z@9ciwyEm+xnSa%{zkV8e@ulyXn`kiYj)*XkexKS_wd~g^>pW|w|9j!g^0A-T!L~!; zxHA<-N4G{P9NN2IyJg3fzEP5MT>I|BkGW>@Lw2wop>RxXLqi7I`@uDbKJ;YTu<5I6 zE?xi1u=gj~!B%sHbdQdVm{-oIcNgfv8a)!K@5m9ng-x%$($0l^Up8FpE!W*7babMh z#F1XWrnfjrK$nYu?i|0V@hmnq%)u-{W_YrAObt%huI{%!rySTqE?YRU3zTQ zxl{pfR(Cd)2`zHH%6PQ8vr&wGs+8&KqcX&szDE1#-L)Av^|`3+l*@X#$NkaamNOUy zF!0cyN%51%jUV&=oS{>${PK#!N407dwuAkZwHWQ~GV7jua-QyycT&w4wUfTM;TJpD zRUll2sQBX9eqWKZ;%;W08J;ZeSlXbT+_p`sMn))hIL%l%!3?v;QDNd~;wqY-B^lB-+sJAI2he)X#SS*#L`+O4{R!_q!%|q!3!ym; z*-y}6eJ9=y2BG8-OWR-tHgk@1ihIHYQ?9lYr=dp|A6yX;P87(5R1gFQaI7CPKt?B2 zTHgu>l~x#=3Sr{fQwH@;bbRw@ppz||2mBNgQkgJ8mb3khA$k;LvA%qdQcj3|`K50T z-8>{T^^*N=a9{P|qIn?N3Y&?_C|HTcs+je=3V#CGqAm23jJ*3*CLY6%RnLtd!yZv3 zS6FIf2fs|g<8J5Q+xePz@2kqbICskzCtS3k9;cNNhR9p=*TLtuF`^~3YMYv3)z-Xr z@DGDBGNzt3_=eGU?)yT-EQ3^^2zKACv4b&(P)NvSR0ix0+%@>^9f@52nww>YCyU3Z z2JC_LJ47g)Om>hCIxjoq+5d&d%*#LL&BMbL?Kb)BU{@nE1*>YnG}wP5L1aHg{2Df4 zj(U#XnoKy?kcdFQt;zvsAEEKe(Mc8*@0TOm1lh*9mFAW0npc+ZJ^JDCldm}H-Pe8? zIcJrbmn_@+P-Cs1hOmP%fgmncwyj;*{GYEFML4bB=|DRYbuH2mK*yo8ZrQu_;Wg9O zO?u(|Ls&! zZYR5T@6p=9m_W24x$Hird==GN!!AVu4k9sj7psBRS8Py6L$V7xA_x6=`cuz5Gx?;o zi*m-MTr%4Z_WxXV#c)HcYAM;BOm#O}b`SHsT+}yx^sKwydF93pj}IDUC%bm9RNBFq zKx)UIF1vVft|_t$IxhaadXJ24{brr{ORL>N--S=JgZ)33-CfY96k1AlpQE}PExXUX zUsH47JvZmvU42Q%jJI38ZYR5TuhH4Tm_QKuXWL!8p4SxF1s$GaUR!WM-G8T?_rNFL zPZ@m5Hapn=bJ^u1>n$a_OK5y-yzHL+!ZXufy(90;MW=XX-}~g{cCvc_^{@3aLUu4F zkh1^jvJ0j*MRq~Qdnb6m-*WfEvrhW(;opC~;H1%Zu)n43nme-x5sH~2oTxqNLTF1@ zbUDm6VFKkq0$73&ErA_m+XQTn*d}0&&o+VJBH=1TOaTj|Sa?L}O4pn)eF_jCTn$yy zzLEH6SxCB#{OGYGM|RxBJ(6B`#SW&MzE?|MiISgQt)iFs#7-wZ5UN-BBEirYw>z6Q zotB63<h>x~m=zneqj zYEu@UH>GVe&`6((U9S86WAGpVBS+55o;|a0W`SaY*zBckG#{~wz9m@UuB9C}!VQYb zgJIf}^Oe)rc`NAEU~m8*Q}qY2_gn7{E)P(CO7ZUPt`F7`@__g*ZP;6h8ocG@dN@pk z)w=~-sakyJRa|Yvy zX4x@f2TWqF@z%S`L*B6LMAdIml4)B_bmuZX5E1yo-etM~VfPH5CZ^smqYw1f(r2IX z#a-I@7Huf^ijT#5Ro%gWTgO*>z1Y_#u~?vJ7T>6)9dJQeQAp3_PxhL!^A~8<_ub?< zAnJC)9}(Z0R`QT1WX?^ss84K%Mc0khj)e45fHoG@mHB<;W24{b9t&ZYHIJ#*?hyIt zU)qzX|H&f$r;2w}v^$z*`p=i=KSh@56Q6&Q5I4U73wgv?;J`ljCZlh$&)!N{QHU0O z?phD|>tTb!uqDda;+Tr*Gufn>9=(=~8S;_NRtJ}h(y~ZUe!`xLkZwz3B#sg2!aBf^ zM9;RkYv=>qzFI$71G2Dm6ox)KO?JpzA!>%f4(T=Y4QWDC84AkJ!B_jpc8AKV>xJ67 z3#EYz=rGo9jGoFwj}Fp~{pjV@dSG;UNQYe{V?p+5Z#2t2D^Mth7vD-8u_4<%R;y8J z-HGlwdKKA=BEPr333dVhm|NL}7G-SJzTOdAdZpXl&PEkSwr%}B2u~{uLfV;_T7*26D$`*UkPvNojrPf^1Q+yvqe5y4%-lDL%>=4_s;%)4|ZNa^W zCWq>>)5G5BVY_#F*l-WTtfP4+kvlzX;Xt*(Z1HdHVfQEdywk(p>0x7GhTPBXo47QO zhmC~_xJBlLIy|367eWeMx#?LM`8k=M^o*?RJo=77TBav2eOzW*Ubbgk{)DU??QxM+ z(+*O1_#i55%`I0k+eCP92=KjF0W!eE{nO=PN-{hlhp##;ixU1WVN)l)RIsjaW33yhWZjrgkJP(n4!`fZ&;r}i{bv5 z1}3a}A(ggPoxsACs(;I2COl!ii)`veOGUsdZl)&7PqjL#b^(>NHg`8!soX&_Rf{iK zJE{~zk+srhV((&`s+)tor8f081A7m)sn#ib@1n8@e59Jk;d3Q8{+Ija5tS5CcW530 z54ctGQFB>OOal&0>`0d(_6#kd?uX52k5u^&WuW}+=qj$JeJZUr#keo5W?mCt*QE?` z4o9rwCzO9s&jg~Hgc&Ed$iy)sn~`y`D#86PUbEs%3>={{jZ}r>ZVxEP(dy3ajJ_-OFO|SG z!udcblehR^*Sq&`7!x`*@B04NWIVrm^_&M)7Oo&qJx`4Bcg&IT_`%uhi%^C@@l=tD|*u-|MzH2{CrjXUUiRhv#sXp zhH?60l@P~nrs*P;VYH=&8R}WXVE}}~28y+S#?h5`Ex=KT%l49Qg@T8uR4GkxI`uL2pG)- zru9>aL)6{4F`uoCD>Jgh#(IB}dDNiyugp2;?7e>~D0{nx@grBl!QM-Wg{mIzBsSjM zV9`n4Dbw95BPT>Df%0CU64|@L87&01a7>2)wxZuhM2y}Tw7j2ox`JEBQ!_nqLtugT zpbNbZHuj+l-ZU789Vs+wPr8tgk+bjU(%b}sH9$tcA#M~8jh~Y$; z5UWX3b71AM%?>N5F=3Tcs^&o2kt#;8L)w+#CZw2fH3!O00--%hNRok4j!eaL7L5F$ zP?M44-7N0fhm#9rFG5tc*m*M=Av18>Q0sH0_8oOJh({=qH}Pw3+<^qSUZtV?5PHg6biyZSn@>PAdLdm{XW2j(0*i2z ze%ME@VTj0L{J7$lJV3t)ATptjnOp3rwHSmCrfV@>rF121Ei6q-wst+$j}XVExdfaf zl57$SDZxy0(`tchwEeYAZI)W5Ijv4)bixr|lD1`Lv(z%nQp;uZj1VU; zT?hzQ1A!Hl-73zkArmebQa4>WbfM5}y0YnV(FMVSd_g+Ux7MCX*n(Xtv!fOuBUZdp g=0ah@vRrP!2hAAApcM1PDYD5J3r>>?B#(?1tS95Fc*kP*gnd z!W(ZvMNvT!MZ8b&zHhwsdAtwMhqu1nU9+>Zv&jVGgww1>gw6u zN6zSf^{;#O?3uQYrXA2m)4rxFj*fI*v*OoIzmIBIe$bn9Z|b*YNajF-{Wx;Rv9)6c z4Zfjl$5{gpS^1rdVDC13(yve9^&iZ7>5!|wd}lzfLkTwQk*OKGNABGB+cUL$=g;ha zd|QHbEjcK!zPNm*_X_W_Pu8FR{=ozrf7)B8j4V#>H0P8#n^t_WslGkImL7S`v@^$f z*1h*~m)l-@^2-iG3AX6fdET`-<39cRq*1r^y>H_~fc1Q}{K|*M9e+#Vemz$0SbUFr z0Kr~+aL-kfT;qxtck1!c@>?z(b^yV4Ccm-1!??4$Z=An<-I`HvgLD~u9b)W7I=>yXSC5m#w zT+=U3IaLzJ@EfNt#?6pwO?%J-4v~WlWe-}vn83KlkwwLW$Jf`5)#rqZbpPNocU`?t z4-Fm@aC`J%zFrqlx3XZ6gv$r*lQ-{r2i-QftSacO4-X#ep5?7^hrI!RSz{=y*A21) zCMIdCw*I;kU6@l94g`xDydJIS_MRmNGGvUqQuk>$9}%p84d-|~<$;M_eRh2y7}lPC zazW-{0y@j>uhKoGUVpwfRPS>)YV(iy*M415R9ETM{o!#{RlbH0$&nZEhr(`uSliwC zwN>405X!0D4MDxkd%CWTo>=+2pzrsF0^wkwzR{?D;B5CdT~OU9GFQC$`04G1xWS;i zv6L$6!EmEiy#8aaXjd>8n4K5!1%leQBM$Om+~i15T|L<#2{)miY!;8j$gUKv+9S|E^sJR1JsSRkfw=ASw>pbIVSMRVlPt;CEO0bmHXmqU~DJ^0lMD ztun{w3(U?ZYeLs(d%o-VRp$uU3k6l_gYAR3g#$vYG+?n z{_7D&dHL)>nb)IxO59;R=ym(F4d1NW1`SG%6G;vF9MSTScIZ+2_C8z`S-PmiU9WZc zq-Gd@hsODH z!gYaAeXSnUwc5Oz$wKrfggjrM!4uLhdnakCEDY4uQJ>^CR8l99Y5H}KHgnK%V}xjV z=E8~8J%pp3)O*-4*kmP@);EN8&lqn_ZI~K9UJnI)4WhH^4n1e<(Iy-lluN#S^fx%| z%DkX1n#cV={MH$JBzI9zq6ceCeU}2$ zh|>NwZ_O%UA`-&qtizzr_33^OY){+uOPe*Ilq(%n>_QI=Lb!&IHYk6d@GS<4 z@R{59J8A;Pp#hZ+m#klm{6*pYPk=)*ipv}8b?u--FMMAN1z`yVGaJ0K+$0KFoE*L( zt@i^HdmTdcDg{Oa{>;H0mk9j`cxi28$XgW>j_Q|-4!&1dQHdUQli^jtie4{T@N^B> z`#lA-!W9*{r4=;A2p@%;(x5(ze7c-UDn=)dB~?@yP0kH8pc=_lkytU#U*ro{=fE!t zbWcSE{1{C#LYn(2uOg|UV2)lzerZA_d5)mFzP7?V z`&W>m8;$UPyk|ZJy`-QzSaf)jHZs_8vk zN((B+=(9*55lUzax367-j>;{a=%%TKx-(x7i6Ts43l2a2Ao!q2Da;QBS>O)`8?~1% zsb45u;&|QdEBDsvJkUR0zUXY>oW|?qH{^UEts|t3s&Z`=wlQ9Bp!tS;itt=@(mTDk z^TwluvlgYIg@J~U5SfiZ>z4aj8cd*ae4wGG)=zebPJ8Lj5%2B`VS^1c9}W7ci$~Z1 zo^l(!J_@t61={{gG5-k^VPF#4Zy0msE0P9H@H8g|A!e_V>mL_RvMfv? zT$N}$xk!i@E}BrBuMNL!MrR>Sx!YeO+(x}N?S>cb2TV1jG~jF0-a6*yMB(Hogs9W1 z-3`8QBqo`8-_V;x06~Q#-9b-|zs9GR*V34+4bWVk^Ygcxx$YRZ-xI2G*Hc@_v5;He zr+)omLPDy`Mw}mX&o)f_x94wpvM2OU2;<$2Gz2J0Gf$qkvsEGbkk&@J-*S`v!5BKNDr4%q^k$f=Ru0`^k7tl zNuRE`@ECK2;t)~tdZLhi=-jsVegdhETCAOM$J&LEF3(*zE*P#2Ob!I@Fy=K10HC-)as!+K4C#^EpXS3M3j z=TR{LWfaCkmz@ti_61DPd9`kelYH7y3A;YU5XtkoLm@3Zd(H)Ts!~s(M_}8A4XJ=Y zYpZ>K7i`5SAO%-@efn5;ov!WQ{=}={bc{lZsc1?K>DrJ*w{Aig$CQM$uC?EMCBzWH z`nn7B8X=^N-Lz-AK6nkL@5uPP;)0Dw2*@~^f zMywokTeK%I9LiJUun1lp)2(*TN1x6@*Q)|q`;aGoOhNxq zq%Edc?U&uFR|3FuE&)c4m-HltVbeV--yQ;eGbT+A6gtzoLhILYWIxHt7j~EFRRJ2+ z+P3n4?1M&`fUazeialAMSdJh?$c;c(vCXJdjl z!tB*5Q}n^<7k`?I!E57I7^J=MR#hGRP=2G|UFWTmPKQfN0<+-Ag%sKiYx^9A5Tzgz z6Tbf4nR{R(1X4kv2;Kr6dOr>Y7Z`TS<6yzcRYMUJ$CL~iGB#jfYnS;>`W5Yrpcz9V z<;AZ$K;=}V;!cZ!?*Dyw|CA;Wh71w&i%z~N6b?2B)vJm8;A=g z77d>KKov!0{&2CsI-qT?IAk}hwm=k(tDH{8QR)uYYA@tmvk)DO0*d4)C=F?=PwAe3 zn7qI`|InOYB^TEBZ%uf%N0TT+hKwXD(%QIg>?ULyPNG03=lIs22l5T&;)c>8GNwrop>3^A6uw7%6IKfL>k2zPjiXC5o} zE?Lqa<5HOsW_?|IY7W2452}S!Qs*FTKn%;M86}R6EUN9=cEIBNr4+^389iK zC%t}E_OXzIAafdJCu$dRcLEn!YC z%b-z;#H^3rp{pK|vuEm_C(;7jW`{nOPVcNk1Ef_TKr8{pEz0%YILObb? z>#oLhTkQ+D!&>LYtDZxsT8Jm3e|O*f-XE}o!az;F?x%31QTx{&XY`dI#Li+I&pqSZ zTVNW6L0Zpwe2v0W@ALRG2f_e(3XRBq}wiv46p(tm1~O?qlR z_62A;r@hkYp2MMrVp@*KP6}+_cLO2^g2>T3zoGa5Ng~p@H@=)cO0qlI>#5Ph<$*$S z+aYcLyy_}+XK^TjMpS}`W3{USj~y;3h7@4gCw-~}fHKXZ!~b3M=91&68fO$l){6s@ z3Tf&5sP$a`@dFsdj4YxpnRbx=R`-asgLdYTKb{3P6mZQB1SxjO8#wkJxW4%F!_MEQ z?uH?ap@gEGb9g!mX$KwKW(SOZ3}SihoTbm!z~73ekoHn(pc*bmEGxyD3ZWfkJue?H zbCOXYXI9}(v@9DC@h2QdOfA*a!W3oEOCvHjEKXgA3$w zQ|R{S9K2{Fc8|CiHYqAHMp+f*k+dU=`^2oolt;h)l0Em{N> z$IjxlbEljRUB;A921_i8wUWBkjhF{xF~qQJtf5ubnQu+1LM*^IVw$7zjAc|vJLCH! zMCzAIje=gIKDq`mp-~W~!B9taXitfT`Yb({l0Ot{AE9@F8I-u^(0Uo^ZFIEu{hpgO zp)9?QQa{3F{XBl=ei4uuZs&C0a36Z4L|~~MiTPj58-^h*ff0qyzwiDL@a`hYF|6s< z`QED~2_#QttF)`vF3H6#h3JO_BWu@nKb)jm189@JE1D%b4hWQ*qGlNlpStnE$BZ)C zA2O#0R%}bzB>IZ#MeA{UFZT>7oy;Q1+}-%<|UF6hTmq#j+Or;P&>v!W#>`F^J=CsTuJ$^o)KpOn343 z?MGpfmTC4vYGS3kYDPI_+mMrCWuHFx`(BteB$iN{73HJpuYC?1wQdE-S@r%kr|pL& zaZG8zU+t~YE*;RdMu?`*=1gBmyWytqHejTg*Dr6Sr+qA8l*3m`@@c&*et<3!ND4E3 zab4TB44~}AB<=Aj%iEX$r)a^#Z**uIRYKbR|GvV9X<01Zf86Ks4#+C1C{pDIBg^!m z9gh4B_9M&9oqo<$#~cGU9uP|lO&@)!Si-552FI#7Z*TY#{UX6MHnfjwPA$S{q3tH5 zxOw8y?q7D<2?!i5aJ%I9f~qZ@AyiDJTw8YTvSS5tnO8Qf_o>3lD`CB++_F)2Wv``Y zqq9t9=5~%e_N9Cnrz$oV{kGxq`EU?&nysK1N-Ofzt%i58%)AHHU05R2Ac9it{5*GR z#SNImY?FG~7lywP4OA&_c4;B)!~5QT6s9w7HWG9)JEq-n<9nYY4^~RHOxXeoVJHiB zx(IJz{UH%qbqY5whb>8`CGGUccW<16(QeJMX{oPt?<$Z*`mNaS(dTGqU9o%(Ohv>t zdayiz9J!cQ3WLN&D_=8gHQI=TXra$du6oxoAM}Ua0TR+)4CTEChr@$W;=CO=_HHv2 zCLPiqnBDFvG}G*|wDEI0oesk#x9-y>K7H#Th)zju9ll*BmxyL)i{@261JPAMxwmSD zHu|LBU%=Er_J^c0nKvQ>xuc&)doN)*o6o;^)s>Q_oG_qB5hE@yJ$!(FX zus8Nuf=)`l_C!J2I`r;%oifmpNMsotczD@4XfhR2s7DEE#1z_oSG;l|My-*dcwxot zff7w-@uY*!>pizla+FYB#?1YB%u?CxNJ91caL=;^1=>tE(i~dnjqcTg5oa}X&!6)o zRx+Yhv}1p^_x?9veMUy#7%B(v=Uy(F#kqd-Xc#eXw}hM;)yT&15>P9<1K@q z`mLc8dXC>bKoTS2(~l~kOTe&MVe~+-<1`(Pj0?;i(DAqqbA>)>TNPXWD)*#)?cZ0d zC*>xG*bV7)O14<`ML-dB|91sFg@(kQ2+;HCO$X{aPQmK0%n+MKWgK8BA>`2Qd)C?& zqB-Fpk92M5BSDdamx(fMRgX(132lkInSqt4T`As0QF|@srmZgA^AKbzcT><57SVQe za6RGY57%SLmIzc;_o=lrP3jcP(7MC7^!#G@6wozzJ~kvQ_PWi?UihZ}bl86cMy^(* z7p8xI-?%<@B^9)HQbt&`uVYHhZrgm`Hbff?V|`&!fbd@`Y-7iXePOoYTH3Z>bk|D@ zVP5666iE5qUY{`9yT_z#zz8j;9o2Ggy>{=H`#pqlC^kGrZp}0}dg_b6gmx|N`B?A8 znP^o6%=@TWdp`PvE7V}EUrLimNIR<6niPaM6Y|Pvub5tSQB#VX4NllOxgHAcrV2Lix65UGgNPpFA#zJ<{^SzN$^6 z$H6i;Iauawjm0k8QD`uYW50*C7R9xl{7^v!6_o#?zG}GamR~bXRmRopejzF24mQ#cEcf7&;tDddDzrs=tm3{?PDK5t z-|aKh7aAP9b+7e0c*?q=Eh2Im#HlH((EP0m`fO3 z81Q+?D`+pC{QF`!Vuo8+INEKSdW+;NBSiW_%UF77JIoARG@+zOOF!-V1PpW&Od7ei zZffx-m;#Lwavg48fkzwA_R{ubD&KJO*Me z?W|P>XrM$!?M3hG-WQ|R(3#dbD@Ck{42UIX+V6_`fp8YN^wvuWz4f9kt~>YujOD0z zNwh(O>Q=)^#~N8ho|c*X%G(e-`t65A?O^}#@8JOCW~$cl!5dD6%CJi+meS-D-mIDa zFlH5%QsGyP%rBr2gGXO`?nU%#1WUY%vHEBK9mvoaVCyEKc5wTo{3!ATbL4wjGy{wj z`vjDi)<)l#b~5b1po0*KMt7r(C_>ux=k?zPH(?;~%S9e#N~-%Vee1_4c}z(?iE(_| z!6#tqFbJ8)4K?)0Q`S#~?HlD$ft%KCW~XmqVCG^GKWN?~#x96zLL?ZHoEKzwpB{x^ z@%&o9^NyO5V5TiobcWrG@m5kwx=|a`@10_pa*VDou0820OkM?YTTg2nxaTw=s%IGh z)7xU&rbjO8fZi;K&QW(dx8KbO9SZynb=t&7mjp0niG;2k_@~|IIt?ah+lA}L4?GPW zZC4l>9@qVI#Az5u(nas^&3O@~&sk6{`2GE7!H%unf9>sIddSd0V8Z!q^=^q+vi)-D4bPd&L}FZ} zP>Y7WG228kFOmn>-XHn?MF4Dt0BKaPR*s8om+Dq4Ox+Mu41*2FSKyK2AAC_vJ*QV!lPzByUH)(anjLO9rD=}|!78Q)Gn3yxZIEUVP)s8Gb`yDtlE0#e> zd!qLDlR>_WUhFWf&_*8VsX(X6T@G4T(dz`-=)=!kj#(wzpKUIEcN(-sQ0d0*AN9w5 zaBvKWFzdKrMK9P{Oi7~l$;xlVzH(H^c4A;pH;haB=E5s^XStjIAGN{9;Q#BT%uq{sVm@Cyo(8s>)7F4rs{4cybqh8 z$9SOU1n(k;c;_~Qce#>jE#ZCCgnX-2*^PvEl?~o<1>-W{U1x*G`YVGn>A05g)--`P zL856t6W-M}c;_luNa3v^y!8(89(0Je#Ub9q4)L}+#Cyac-ZqDLk2=JA%mLnYr00ze z@wPj}d(k1@OAhd0R~I|Pvm3uwy8=BPqvmmLhsR^3IlLWm1 zOf^UEPKWgFafo-XL%dB6@g8@G_k=^dj~(h|r$f909P;ht5O1(UycCCcZ#u+#+aca~ zhj{Ne#Cz8v-p3B{rZ~j=)FIwHhj?E(#9Qb9?;7$~4>-WPn(*FmfOi$)ec}-BbBB0e zI>5V<_&($S?+U`Z)*;?EHh3JDuy2IFzrG1P_Kgn{-jhw>v2Xm1@HX4v@%(YODg)oU zZ17k<_MyPLp$RvLyz&E zQ)Sl^-qTIsJ+I1cBfPI2;N41i&o+Vgf-1X-@OIeXvAw*k%Fqw^#6AwY{xIX6;Sldk zhj>dI;w^QEx6C0PuW_1d?^zD%t#F8Ujzc`|qvqseTWb!FZM-?W3mnRKp+mfj9OA9A z!DCr?PQ&+*yl#WXI^fvm7s5j-2Zy=6R=m3i?=>5GtoyfA8OC{%13b*Xm)hVlUyi9j z@75;pIHp4WfbZ?v!7&x+-PQy>j;WAmc&`b($*SxY!h7EV-pz#fK@)f!qd~s4P2h2i zwvzC6Hi5@68tAQS0*_-fTJkWge8WL(W~B5TlP1T(J`0`htny!t7mrLDy46BB=A2RWBz=5^;^ zzzglNjSxMXv2j>ZX|s^8uP2FZg{XaU9)TNfp}A6}(PR zmDjWqua7Lekgs809>weVmkFp2fE1WP#?=hBYuAS+Uf2`ooG8ZEIf_css|LA0WrvSg zcwI}v?W0Kd!=@%3TTHwp#QuMC?fSsP3;lHmRX`o|*NLj~r*GNQwnCfZ^}dN0d@rxx zpu=o*938#hk=N$_H^=Ke6R+cm*alSr?fOI!uX?XJF0tiA*066beb)qo4zX4QJMh}A zkVd|*Zko9?*RFR=yx`|~jR_qF745tanqi`fo8$GBi5Iqy9x~M%rxu0xexQIYxDeSG z9a?!MDB*CqT+wdJ9oGuHHYpjcMQ`Vjk_blm@=ytd@OQ(o`*B2wab-2^+1AWx|ZSQBNac7=HnK`<1XY0{(ZVaE&ezWKOC5lSLpr7BpP=!CkN ziv60%%nR+pS`SkRbU0U4@4!hb^&IbFmdLJMUz&6X8n37du#X(oUuT(kq1a3lb?gp1 zUay&WpeFX(V5ov4GZLVSSho2fA6 z*sB@Ku3gWYc%fbQsS4E3W@p{U{3p!j#$ZUtp@Yw)5`js*T`5f=R8Ox5>RueD8z28s;)Ioo-pI^^O z=6FmLyg6Q&>mP;?bYMQgGxihzO=qfl#``3H(1}<3x%_uk4jo{>a5tSO!(Q4hI)~9c zx}X!CP|sm>;(Zh3=#QuSK6K(h9l#Rliuf1L_>3g_5N*XX-UI%LPQY=1C)x#hasM!t zJwzvR3;103v2-3yC*B~(XO!?chhB8{rn3i~c$*&IcGI2C1Um8IfSz=M7Pk@iX!iqj zB3_2<_tS~?pk0vpY&wAl7}^J!aF1&;o$csEJ@5m6@CIMNA=}+l202+i41356IRQf% zc;g;$&;U==1rG2bdjg%1AGCpwde8%Kz#nx{2Yi78`5{vRov4pC03Y=rJM;%!#52$l z+L}OD@Bn?h*R(~!SdX9sdC*2~Gxz|8`lt&W&;tx@LAxLaWCLBu3mG6UXrLT4(61-Z zc|4tc>AaK55F2AJ1hGj5oyh59KF8dLcycJ6nRKEJ`_Ks*pb2_-#&tbCqa1CXXqMm zjH3(b1dl8_aUDV@>S53OJUY*(a~+*#*ah@_JDs30j85nh_ZW-kDEJ6^K2E_0&=tM} z$Jt;)$NB0ipX~pGCi=$sB%S3`8_==k!%ENTQXKh74Rn$jL3@LW586&pIzZiqLQ@RU zX(*?!H{!4Kpx@$TSLriJ-y}j1^yS^(S}>G37V1z)+KtGgC9tlM{vKGkQUMz#fv=e0h0{#IOz7 z8Su*RcBMfteMx+y__7W`3Z_LLB8w>$DuC@Y^GWWg9)^#0B&lcsf6r{>`*Enc@U8eA z1o{mRk5Lj5!?r{hajbq~JyFIY`qS{OprXibSP1;H$07I6rD6@~f3X3;+^ z1BN{Mv!=Y4pWl^sU+OT!r3T@p-{Lv=rHg{3QN5(AOo2i8av_Tt7Ik*6wwFC`Oje}IJljy0Q z@BnQf+-fb{Y-t-+_s`0(uc9-KbBDXKgTGp2x zRIQUlU+0qs8mP^+Bwa{5oywXk-_IhUS+fxxw^vy8T9r0~=zD}#8QNNHC^b>fC+${} zJ;EZn4e`pEKWo{(4V_gRCea8vNt>pb?WKU)V?%Q^$!GHP87ogq2{)OQ$d&6Tr{Xj-Qom9)@b4Yh2R7!xPco%BHw zK26i%(J+qsARy#p%B-io)jw07NVX-tUo3<@NQ4)2A48)mEc{c0R!*Ap5)D7K&PA&W zIa)`G9-T=g1p;@bu-nr$_F~htLTxtH3ebp&;6{9~ed!^JdPJf>=N-^LVUiy+!c5_{ zpyx>M#4G1si^#$D89U3kXq6&$f?kZY)#@wRYxE->$=>cy^*Xgui`k!nwk}%jqLolY zE`}YZV6p@rBhp5ea15=#n2ueKO*4AgMKPPC6zR_%v>R1KBLX81^90(;RxzKj*}Jl> z#lv;cYKo42Cu(MIXm5M)ooqL`LJG_ZLxn$p7nm+AGmq|S={`VS5k42*DbmZa zW$Z^I`eGg}o4J@r#M85OBK+;jSOaXE5lx!!SgYnfG>^qdkNw$p@Jzv$GLrfnu}5SM zi7h!o^Kgg@IR_6{S-`cEhC z=q0*#u%jKx&pjKF9W9wo9spx+uxU1!Mtzx~H4@cst<(~p$098$C%&gsyQ*pSl98|z z9G=^nwoZ;9%`D$=Ta(q)~{XT&Spm&)~%OT3FErD3mY_7#Jbr2WV< z4yRrjOnKYL-$1fMJ7}IQG}i`-`7NQ9w87R69r#6Lg2*BgPg>zUT8S@5O>&)qnI#s& z{;Fxa;nGs_lMOW9@V^K7tZHGWwKM}^1>>T8YU|h!&jjFLAEP#p(Ob#FvYV|ZVo^L3 zAjZI$i?ro9NA+2w)#Meh#&S`PR_cKN|F=dd9F4_~QYMfO^=LlQ2Wu$;>ElSdY@K;x z&P<{?HI3%UEc%~I^JWT-d(52qbf!~&dkB@J&}>>jf0uqM!q zf>lHhim+r%+Cx2KUZCF>q?HM*MJEj8X>grGC3W;C>tK9f_0oehfR&ZR9!ybMfmnUi zQb|48b`4!;Qq6$ih8YL-5RKFcsx@?Pjz&QXGt=qh@m%y7Drsd++c?i3a=bQ>JUztR z;vsI39i9RH4u71#(Of)6IFd~gKGsEhAWnF4k7NA`nO)idRgb-4B*JM~49as@IccVf z`aWLvBwqPINAhztQA4sLM+JX9D5eFhn~9XK8%d=cRp(P5quua|=B(BSvotQ+mT=OSJPOljB*~GNM{{i^-6I+*q!ZC= z5skn!f@jc~MSr;jO&1X@YNgV%i%OjE^-z9+HJ~h3SrJa<5Dj<0lj~u3ThwPPEQZ z1F~xkgc;kI9+Tnssr}ezTL_XFpa*z6c&EjjSf0{@};*$X8{Nx5_0wr_x;p{iTryLo@*# zL>BqNv!NeObi|L>59W;IVB&y2jf{JY*umsIqv!ci6gR*MT(mZE(l(wSx-{i!KvK>+7k!v0&J;dG_B6qCFa}=Fe@5vC% zKA45@*R+@7c^xCWfw1#bEm;@;xs^`G9%nx6yw^~YN~b?$T~8)&QcYfECf#F)FCwYL z=|M2Wh_QIs0cpNvJO)zK?3_w%8$vdaCM+O>tUQ;jA)lTQPh$oz66Jsv(20l}W5-2L zPWZ*zWwM}`PMp%Uh+SgzVca2xA4oFUt={9=WuYTU*k?DT>mo;#IM+OZyk>|zzs%Ag z{%cmFZ%StuQL@C2PtN$oT-S-NUHegRDuej<8GT5+|f=di1f4=8+x1 zJ0mNaN%oRP*EE7;k-frBGb#3jRc4Z}&Z7HVfj>mVqRE8k(nizSJU#J-ii!d&Vtw2v{&duO~li^jl?S<_yDBjqa6ke4($ z$JA41{kmvwbfOiu+2|M(YXS5PXLw|b;8&f{WX}aJ#WN<>z*srw36GU4yccHJRDxp< zD2x0RvQ&9=%@ngOBFs}9@oPzhX>C~~!kp}gSL{rlYDAJrrkR|hOI+nlE;C0F_(0M` z?EH=l)D%aOus>xxkiLaIPP8qYswl>eCSHLfzg#E0l}kIVMf|NA#j4W!&c;+lk*z+G zBWhXJi~SR7<&oth=BOl3f^}OZ`2oBs7THngNpDFrCgQXTNA2pRuwvr|i~@`cyiLI) zLwYy(w*bw(jbb&4d@u4^E{Y(Wj2DjT(H_6Zmxjo5fRlXt!lk(#(dnzOBCD#tA}bPk zdm@o!#3~(LshUlO@OohFti>NuF;oL*& zj_BFR6xk`q{DZ%yy*7`#T*9nrQH?yphD|%?fr!uBq8b<*(;d}dOU3MiouV4@;4(`i zGjT3@$IgkYJdUeqMnV3ofT-cEPwXAZd@=gQr)Wuv*tv@MkM^p}Ja0wzRONhSom>AC z^D}D*Q4_z@868X3Inof%p;!%K&KyFTM%*t~sD)zYOcjxT3Y8&;gM72x`@!s5M0E=2 z*+m}3NssVbKoQ@;Gvi>2lfu+9)?8!25#8ANGBN|2=ga6k4QFJA&`O)}xwl@`Ht^i@y|#{D6^6wu@a$tYlLN%SHCbeIl>Tdjg)1WS#^51-k-~eeGiv z#XK<=V&~HQ{+rA$p})-UnMdA@H1{0$Ks?;$V|tF{N@-R`t^n^2%UrVbvhuBE?6bN= zQ~@0{J>S^(W;|WYQo3lGCif=Z+d>Aci2AXRd<^o#czY{@dMuTo$T=W0ktO;O*@+A) zMO+V$gc)$QBYqs2M*77?s~snH*AjgqXS%ogM2sD#9>M!8W+_j1M3eOi-C+M7BP5;f zv9~BaI^LLrPtTxo&*MAx8}8F=P(pCjk~Xs>`+*X^~4?=%^--MET~v+_!|MsHRaM zbKH?PM_k$>M>JV0=GX_H3c#8J^R#@^V6h`wY=s!JnPh(jbj>BZlW%Im0%2#^DJrB9 z41bN?pnSSV#u4yzVU;fJ3`hLfKJ4NQvnI}TM2({sC)$Zm@;72QMlG!xch^P z8F=&i@{54d5&nZ#b7*V z(5Qv26o~PSv5PnhBN!#vPr%4WJ`TIS*i*o$hizdMy3!FpUVU>LA|BH!|M`yS@w>}* zo|Ad-+X8s^1FKQanq1(B1F!ERk|H-PGfFP)LPbN)FfCbw{(Wl#?t|uZ=F%>5q@jon z<&$+F5BU91_(N^T6EMQ=3OwZg9d%C033kgEOpL$^y%l(%|hr zyE-flW?ES%S;zoB$YU^!TbXRMGuhw=f6REy5APQ;jCmv*JW>oEnByfL>kVrPK%%vb z)i~=Jn0A`RKC{GWTDS8^i{>7TaE?!z+tnQ9)2NrCsiz1{$bM@|$B2rg^naa>vBG67 z{=cl{Xbq>DtSQoiuq$YtZ4>K%2}w0miH^75=k#IcJMZqghj>UMhNQ*pH^xw0LV@$Tp-1&DxJr)=_ghkEWbv)62-fN`BnScq33kSchqb z=V&Rv=C#BvVP4#WE#<_P6klsrPKbbIJDcMb>yePllyxJPi>&T+6HpJ6y8$5ng}BR%6F9 zG9ymx2k(de=j{hOC)|F!cq3AZyVjg7{yNIYGjN;YNzFFT^MZ449qo;i72 zX{oJ7JOZ~1Z)Zwc>{m05(Zg-Ss(NquVO4Hz8{QSQ^5dRtt~Ie^18;9hY7yUxe6fT@ z<~i(4Fbw=*hjtMfKIPAN+j~~r4EFt3C{!ITxTR!)Cgf?=0 zQqvJzWUS_FBbxT!%>ZmS&GC|JbmXmJNmys%6DW~;Ikv*|ZMF@x>c?sk7_v>#*wJOc zf(#6m7HD6CtrNT$A9iY$C%OzA&^if4E<)xz?0BFQ{b5frQkrO@W~Y$|FIj5FH*1aW zBlr?x#+RCnC#@_6euPGRt)m69G?Jl-)I&eSZ@DZr*ONRUnbaAsiH6r{+B*R=;$vj1 z?fnxl+4D3Vi6gU#zJ=}mH~XmA83tBh&I<6T<`Ki1Zf=xEdlaS`@m36BU$wV-r9i3*_{WxxqcWWMmB5X~bHJOetn^ zM8CL0b|8YoJ_UN@duu5&sEKsqinpe*o{8W{?Kkal1`64+62M49bcTFYOTAEZ4`cMU zh7Xc?WsHZ24?<5u8-PY)BtY4wn_hwbwO;728_}ozZ=_KV|8J!IRU-{ELF`E5c_DhF z!9&aO*VKI98X4pNp7qGyMD=?&+rt-f&G<9Ev)!m9nh9c8uHe9toY^zm?ajf5^UA0> z&x~)@Vawu($8H7N)c#x~Qwz>KQ`peK2;>OSO3_gz{5q8x(XV8WX&8RchJR7_2fD|RApX-&`lw2*LIDQs8-;K} z@~NTxmwy_2%drnubnd+HY%|996w2`76!H9EeGL>~Aln|!_6-QWvr9Z-!9{1{j`*nrR7 z$zSPeN1s5Jhrt;?WX7CFszO!WsHVVlF{ui9X?68%t)BZT_1^n3AwYPru;c&Br{6mneI39U4<Yl$MW!U6k!Lnb^nbda1yOYpoIHoEt_}x5lpwn_oE5_H?l?L@$UVU~>RX7l&I8+|^ zoje@;n{Oi`M1qqW9=A7AP)RM zU2d=;RGSo|<0dsZQPq!aHUA7U%i8Q16OKa@eSwtkXGhC+np zFcNd#s*vK2Zc(1SQsVJdJ0RU69*$1!wyJq ziK2*Y^Ds9|E?KdRsf~(k2ae=|ERYFyjn@BPkqfdwCQP2t&Hoj-APZzt9q@lfF7zQ} zf^5*+{}p|aD8k;+pZ}qL`~W=Z;F-Y~Jv=+`009zDBQet-l(`ZZbi@>PB;co_C{E!7 zWgqJm63cL}de-h!H*x1Yu)yHN!LF#;_@djX<)6JY{KTBKmoHzi?cU0ea$QDzf!}Tv z2NnhS-k@HEWtH1E##>Vx*0!m_7{z^|3e1WF=r{$>Rad^?BZ2bRs;-86)3UEDyfALz zv@Z+4idXpB#9$lcw;E7oUQub(MGTh-NQ z?U5yy7M&lju;akw5tXXYDz>J*60aJqM6>rUW>6F(m&WX2%1*5+h#Qyxk(GJKjQ#Sq ze)RjdZ**KdV(YiZ?~{G_5vOMFuL#vB>TK{=Ft;n!joF6As7f*8>qlCjbXA2sS!XK( zj4Xay7zTkKx5kf5BX~v^;zy=?(b=2M9&~o06XwyK&ICH~qt!j>#2CO4tNW*9f%9?q z{f}qQ|L2XP$1Vz8`(XINeJ(IJ@*j#A%WW73_@5F_0~on+eFJK?QDnL2n)MC6pPT-u zZ{?`5mp&6tTfQ#+0;_L;+Qb1TQ81@I;HQ*|*XEs_+BIAu)hb3dq9^)p19`lyuI4Hn;>jX| z>HVfvyw|204+=}QS4`Dv<5uI!1~=pkxcA?&skTtTJ@$^#J{aKzM8W3|LoxfVJ>P&ET$NExPM%tbmL78)8EluyBp5w#v>NeJ1l)w4 zQU~Tb)FKYDHYAa?+qY*A5`SVzCTwCS4sK(!6HDgP7d(0Lu?H4yDt)HU_!rwhW}R3t z---jm75hVBx4%mF6b5`=ca1LdT2?)?K2=5SEOo;&EmP0`W(4c?4+V&sbYT;}tHPK# zH$m~;tq_=9vnp_X@bGPeg69^j?RInei;EUb;PxD$2=eq}On}!?UmO{)a2m1iq6!_0 z@Xe1&pO4U=ExCD7n{IFI+WaYtfTl28bU^+7<@=t0Wt-yr{V8dqm$v=E4z>lA)FDiZ zlqOB=K?OBaXH0R8TM&g6{UAuI_KL{u-`5;)b>At+kGkdd%a>P;y#5rRqnvS~tIH>} z*c*pgCIcAf=5AFvibA~3XRAVEo*R7GiVhF`o_E&e-TP(TzA#T!PlaJlu13KK{vNpL}lD-HRZNBs3PHQ@BO9Q+Y zox6@+c+8~}MwFa$!V|x39`?U)~TBoyP@-ko;+|^|9uP2ueho#?}(SLXK_G}MMT=EXAzyIdyo>C5vZvylC5T7oiPJ^)$-Sj_qS^dh+SVt|(Z${r-lR z&ffl_9qjK2tazZox({3YV`|%BD@L7r%^g!7x#^9cA-j{doq6iC=Wc)KhWsu6+!FeE zsJ8}fv%*?w`hdAZEaNcSk^yYyMo{2I8ixD3H$9M%e9hg(cl>kJlb*-MRii#S+xy|RO(77qOg8QB zb=6p@J%)zTF-^(1_mR<~O}VF&6nXB$xb7*Nd{c|PPH@g+oBJ;uGxEGyC(iG-=jpY~ z1r0-_g2T@0?O=bW*Rk^yki9uWL_5m&z5dH-Z(cw0u6B2OcR#e~0z25>>2)x7etK)_ zqW0h2RC3p^yMO(0_zu6*wymyp_g(YoJL5Lo_OO0X=h63~ZB|$-c^z0W#4--EEg8T# zH^_6MD2hV7&Ql}8HrPM|tpUhjuD}e4nPl|W1xn1)E^tFb^ydJN3h3 zM&0JSBJ=z~br0FWUNqQRrvNbnQ7O0M!M$l9GV}R?v4JqX{5wOuu@iY>&W?U{{C7C#;uA!!BJnuUHT`GJ+^rEdoMLGdSd5Mqwf3cfL~Yad?k-XfJALsEV{mS z^9?J0bRSf_@tljVnKSjnnMgHTVSnfBmq7BY@ea6Xz#Z8mFFI>pe&OJC-3ipJlR-Q3 zpUYnI?EQ_ox3BG4?Jd3WQajk+G5bM&9L+gO$opN*KcBsE;^0w>m)&#u<@t|YZ3kN_8i_rO0+db(i2vPB@^q~Rx?w`kL(cI9K1 zpS0L3!;F{JVdn(xyYJlJKlQM_>0Hh zIr7x{?q3~uHtILkwz%W}TB;Ry{C`Wbw|M;T_w1UkRJ@(eV(N6# zw%wgBz2FnyYq^^z9ev^A+N^14n-vy}$r znLrkUTWkbR2Ef6MXalDP?D80(fw=I+&Z{?Xdtt}Oo8RfWyleM)7Xa?0&DP8!+H8dl zU<08tquq~dg;y$h~O(4~_*q2V} zF>mwd?p32MTs`xW=eEt-#-j%Ean=~qfpu%j4%XMMZ$Zel6#wZkQkAwwe$6*ym zLhb(x^I?{Dn}4W=a>lVEgLdd^I&_;dWy7dDhU~~})Aq1AcCf$GmnM*m*L~>y=F16l zvhT_%n^C%d<3_Z_N!zT`IND}~@p~npX^crcxc>T3!!Gm@tic{PHV5D=bE`-w7;RRZ zrYW|2pBVg%f|}nIwU{xUsmSwCC{T#G>dF_Fcykp`u_Y}Y<;V8l^V8@4b4G9c{p>5! z&zb!`GY5H|5fF_Gp0czwYtQo~=N|M_&$Ok9YwcirIm)3FZadgIJ%m9rGIXc($DpXI zT|{LE`#VQ7W<_gcibgcow*QcQW;wRcxD(M1YR#=^5*_R1#n^GXT|H}es2jYWjU(2| zyUPNblEamZQ`L2n!W17mEU)#368!YSO9EXz-jKV}r+X6Ieh-BOJ|C5Y6DswDDmTR# zogqlu>@8`jcKwE z0qE_ausfkluL{t&5gbtH3s5xLH$(y)})-rxZ zf{VVpKqR_w<)cO2)?G;w^8zGZl8qaEW;YdmmeM@Z;(6(g9SvLif4yk*gXiuZdtt%p zf0z}ZL=+#)cWN+gT?zmEjGB&;v(08=aNP~_*&aO?!()!VG$rJr;Zco-WqP(!5$<$K16?# zPUI-$5;Jwi6xX-~QCQIrg0$*Y@rfntvIXcUXPoHj@+pBXR#UTsy(H1a|LC?^m(-45 z7&B3fxs7UDcPh3HZq#tnHfuUPf$CXdZ-SUQqT2QszT?Y%g5{2N3j(4zY1`AkY~PlA z_M!Ppd(AoPr%x9joj~=hus1+V9sjAeF~8>8(4dqsO!*?OQIK6O?lV84m%)-+jC|j9`>@VP$6$$+Pyqx2it;5>WH#`?XQnP@p_E) zB|M1Ilo_BL?XZ4^%MSK;dz`sC?dGxTKDltwy$AoY3vIK) z*iJ!HEI}v8-L8CZ2zy%i7*a?$3c1-8*%cd7SymgET@q*r>3I#o5Pj8|98>K*okDHumlEe5aohi#8qg+ot*d zSU1oP)`$B66+6@USEy$U(H*ac=_6pXbV~ivzTqsluR*75pn3>;kvkkfxk7&e={tck zgkjz70fIXrq}RKH^clK@B0Vh9jhd@fR2|W9q^?te+%80m3TlE98JXv5iM& zHeO$M#qFb4AOBGJvlj|>Kz~+PEBX8X?DF{lZ`}@^dE8G zt|^6^D>mJ+^7h}K-hA5SSJqy9V!=v%Y`g9qyU*Hn!Gib3Y(Kf=p=S=iZfei(`hR4R zs@ddQ$KSp<%lqX&#$5mEp6lLtKe+3AMIc58cT(`WFkXibr&{JI?I3yv4E>AmrplnR z1Dz>!;xnueYbc$WbYgc43Wn`A_=CFJM>q0&p$8ZEYrKAaaEZGqYnoS%X>A(GVg^RBMqEdQ`Q68czVA-R+p*lTWR#mIlxkW;czfv3+e4nZ~ z1=W>1l6;4Rj8FwJs^-BpTp?Hu>INl`Hdo!WQ;6(g7b-aSkzmM33}kC8VQSXvA_a(% zkH0v5u|i;-VsMKCL9X}=4Y!2-Cu15G4|GPFDe6YP{THiF9L!&&CLfS6?F@B&UtPKB zO1H-Dt9WK4Zu+yT(lzQDbGcSQxB`#Za;4~)3JmB=H>G+o&7vE5aF-*XJR}Q zlH#?LE008NI%0%}sWB1|lv{Hy?)=wsvtP)_A9KTp_XkVv`S(-%fC?RKp*H?O_F!5R zUsN&60lDzH8ya>doj8oqK(m;A0lES;UZ4|4FFNz+6>Ra^3cY^q_vk^qcU>NI`$N@% zV4eK64WObsPsa@%a8!f)_fHTumN2_k_lwuF>18XLSVIX^k z6EJ#mfrVt3cA)AO$eZVLheCR2a1rIh3WM}-b|5%ou=rN6LABZph1rKNopB|^XrU1; zM1+yp#>{R?UC{NO;{ZUnet34+WPK#$E`XNL8iajyY2eX>Vgy z)Z{ zPGM4BVp3K?USZS#BK)R~?3LM~n6KI?*>6v4Sh?%?OR^D$;^1qxc#{9H(!&A$!sp?N zgRhVlCp)IqkF0*Ve{i4N8&>6v{PeNOBJMN~bJX3tgQeqZwuy{cN{nTej;~6mMvb=1 z5C7uTi}oG0r2Cd5k1QQHExLT+2m_H`!jkuZEgW&ELgWf8OIb95NOd#ZtOoNvVsi5{ zYMY;tOC?wJm^`1_MuI#nK*ql)feE_sVcBqV9pj#6EODQ&X-}E&iLN}D_Z`T?tAk1i zmyalZQ3~R^CLW3|*Adr+Oi5m&dZ3t@o`hFVScH7;=e0#ZU4ob>CJzMA-o9 zvyAzcdwNDYQc{yL6X~w9%2S=_(RwQPbdb7o94$OL0(!MfiObcqwp!h=3h)^3e!P+)CZfd>gB@{9VH+xp`&H!VwYXnJ4zj3?gZ(`# zC~ho&@^`!a9aa})uY00v)(+pK8BGSqWiuLc!gYaAeJy=Ru;t%DK1IsrT)2I!`<*&; zgkG@msQJHaJz#Bzz5AYW#nrfEFUU4`nU%?>3NfZ<(y1VB&mb_3+cT)y;+_ev+!%hV zW502WvbOwm=e!e(tv!S6gg9XL)H8ee!x3W@mDs`1I@;d4c<+9YU`o8vlSKl@)ierQ ztm*r%efH%ik9}k0T~`)t>NsG-604?-gaZN_`osaf2VgcJC@`+Ha7b~bMPZAjy?o!? zt~V!MnzN<K1ErlszUbQy9HDF_Y3%z z=ad)cl@yeXtVl^s%uLG2OixQs%1up9%gIVhPRYwkO-oHI$Vtn}%CAUC%W@|sXJw{l z=yaweR=U%Y(lgStJc-pwS*ghxWnN0BG}Z+C6>0RBoRpSck(61TnV4CXQdOOioT{g} zv$9gF(=t4IT2*FBRaI(bMS5mpN>XNFN@`YGZhmflL2f~6UQ!aRM)LAgvoi7u68T6{ zH+*F{U5P{?9{Fl261OR-smX~c;!hQ6zbPHC#qj(wQ|U^~Q}k-Vo)Z+nipWk!Ag&`V z4_6e~q4!kJ3|EhlOILMk9nX<@s&&S-_QAs^sz$JEJg3j$)<&c<-z8+rr^wYjnwqE^ z*Bmb(MNL&KV%u#s)b4E*>d|y#GSr;RLW(pp)A9?_Gg7m%3UacNQgYMNlJoOY$!OCG zDw2~kQ&N(1a|#lZlT-8ale1C_lIb}!BR?}GD>bw5qz1Pq=&dTzL$%{|PlNbOTgAlW z-pP7mMM_3~K2gi-o$Sd^%}J$8DlLlXlABkUPM4JAoC5l})2xE@)QpO}abwHM$LAD} zEy%Bkwz|xyN2_J>Gp+^6sd+hBSy>smnfWQ1DLFL0Qj@ciY4l~Jr6nik7=+mJ`N{?S z%vXl<)dHsxGDMjMm?TC@F^D7bd1x|yY}oEK+* zqIFg*<|qUnf^8IhHdC@*a})wQo;NK7#C*g^O!WqZl&dORiE2RwiIH=zs*$a())rsJ zq^?po>_5{qWm2Spx-RA;keo69c{m+UO$pmFEjIcv>FZ4^ge& zDrss)s~1mrbuWKlNKrB_A1sLq+^5IWGqPdVsakC3TVztoSbK>4A0x3!mn)=C43?Ig zI`zyInw{~g4p?;Eu-7iydc}e5T)sis*%wT|xxRl|UA8|Cp}0TxWf8=9^r^e8>T0N6 z%kEYfP{7zZk!|*W4f?O^p^kUG;nv*b-cQd zb@-j)O_l?w%63(aCrcfBeRS|Nr=c<;Ic6k4Ig_yV8 zszz)x6wjE{Ed^F_>mVG!5lihPSzuF=uEgWUo}nV|whFE)MlL0);57v@8jJ5>o39GD zS_?HTJNs7!WLD=Ol}HEDD7HtnqGpYyH!^mOLeSNfDf8;#RYr`tLTWmz;>&w&wkv+2 zHe#Q+h19LjDSlhk)zA@bRAlcPw-pbC0*i{oU2M&Z0Frn7q_^gdv7c5x3?8X?rlZv4&Z2U8z;4xBXLbSK2aZ2Q;%k8Nh9=A^4xF!AKmK>sbZ>z#E8O2k% zY>Vh-96Zt!)zIghpe!~0uT&Lm4W^$eT(2tdEW3#b+^=rz5K-3=VjHK3NM1Rj2-P@3 zM406W5p%sGM0k@J#9s-BgL~zF;hj*2$%6mu!n>bD3pMS3;hj=M;aI>S`Sk zE-fKWiKdN;Tap>KBqL6VOMC#z_#t)_S|w;I zkQ*46TBkTAnifC6I#~7PR#n$DJw*bn6{^q%X;=O^@e4=hwvT5jup^G&vK0l+!XYz< zgZB}|r!E0t=Yaco2MFU}hjNH~jQ@R+)5@+q=ePQllAAAi HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->OnProcessRequestComplete().BindRaw(this, &FAssetLoader::OnDownloadComplete); + HttpRequest->OnProcessRequestComplete().BindRaw(this, &FAssetLoader::OnLoadComplete); HttpRequest->SetURL(URL); HttpRequest->SetVerb(TEXT("GET")); HttpRequest->ProcessRequest(); } -void FAssetLoader::OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) +void FAssetLoader::OnLoadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { - + TArray Content = TArray(); + UglTFRuntimeAsset* gltfAsset = nullptr; + bool AssetFileSaved = false; if (bWasSuccessful && Response.IsValid()) { - const TArray Content = Response->GetContent(); - OnRequestDataReceived.ExecuteIfBound(Content, true); + Content = Response->GetContent(); const FString FileName = FPaths::GetCleanFilename(Request->GetURL()); const FString FilePath = DownloadDirectory / FileName; - - if (FFileHelper::SaveArrayToFile(Response->GetContent(), *FilePath)) + + if(bSaveToDisk && FFileHelper::SaveArrayToFile(Response->GetContent(), *FilePath)) { UE_LOG(LogTemp, Log, TEXT("Downloaded GLB file to %s"), *FilePath); - UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(Content, *GltfConfig); - OnGLtfAssetLoaded.ExecuteIfBound(FilePath, gltfAsset, true); - return; + AssetFileSaved = true; + } + if(OnGLtfAssetLoaded.IsBound()) + { + gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData(Content, *GltfConfig); } - - UE_LOG(LogTemp, Error, TEXT("Failed to save GLB file to %s"), *FilePath); - OnGLtfAssetLoaded.ExecuteIfBound(FString(), nullptr, false); + OnRequestDataReceived.ExecuteIfBound(Content, Content.Num() > 0); + OnGLtfAssetLoaded.ExecuteIfBound(gltfAsset, gltfAsset != nullptr); + OnAssetSaved.ExecuteIfBound(FilePath, AssetFileSaved); return; } - UE_LOG(LogTemp, Error, TEXT("Failed to download GLB file from URL")); - OnRequestDataReceived.ExecuteIfBound(TArray(), false); - OnGLtfAssetLoaded.ExecuteIfBound(FString(), nullptr, false); + UE_LOG(LogTemp, Error, TEXT("Failed to load GLB from URL")); + OnRequestDataReceived.ExecuteIfBound(Content, Content.Num() > 0); + OnGLtfAssetLoaded.ExecuteIfBound(gltfAsset, gltfAsset != nullptr); + OnAssetSaved.ExecuteIfBound("", AssetFileSaved); } diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index 50df671..6f16056 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -1,153 +1,258 @@ // Fill out your copyright notice in the Description page of Project Settings. -#include "RpmActor.h" -#include "glTFRuntimeAsset.h" -#include "glTFRuntimeFunctionLibrary.h" -#include "HttpModule.h" -#include "Animation/AnimInstance.h" -#include "Api/Characters/CharacterApi.h" -#include "Api/Characters/Models/RpmCharacter.h" -#include "Interfaces/IHttpResponse.h" -#include "Settings/RpmDeveloperSettings.h" -class IHttpRequest; +#include "RpmActor.h" +#include "Components/InstancedStaticMeshComponent.h" +#include "Components/SkeletalMeshComponent.h" +#include "Animation/AnimSequence.h" +#include "glTFRuntimeSkeletalMeshComponent.h" +// Sets default values ARpmActor::ARpmActor() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; - URpmDeveloperSettings* RpmSettings = GetMutableDefault(); - AppId = RpmSettings->ApplicationId; - CharacterApi = MakeShared(); - CharacterApi->OnCharacterCreateResponse.BindUObject(this, &ARpmActor::HandleCharacterCreateResponse); - CharacterApi->OnCharacterUpdateResponse.BindUObject(this, &ARpmActor::HandleCharacterUpdateResponse); - CharacterApi->OnCharacterFindResponse.BindUObject(this, &ARpmActor::HandleCharacterFindResponse); AssetRoot = CreateDefaultSubobject(TEXT("AssetRoot")); RootComponent = AssetRoot; - BaseSkeletalMeshComponent = CreateDefaultSubobject(TEXT("BaseSkeletalMesh")); - BaseSkeletalMeshComponent->SetupAttachment(AssetRoot); - AddedMeshComponents = TArray(); - PreviewAssetMap = TMap(); - OnSkeletalMeshCallback.BindDynamic(this, &ARpmActor::HandleSkeletalMeshLoaded); + RootNodeIndex = INDEX_NONE; + bStaticMeshesAsSkeletalOnMorphTargets = true; } -void ARpmActor::HandleCharacterCreateResponse(FCharacterCreateResponse CharacterCreateResponse, bool bWasSuccessful) +// Called when the game starts or when spawned +void ARpmActor::BeginPlay() { - Character = CharacterCreateResponse.Data; - LoadCharacter(Character); -} + Super::BeginPlay(); -void ARpmActor::HandleCharacterUpdateResponse(FCharacterUpdateResponse CharacterUpdateResponse, bool bWasSuccessful) -{ - Character = CharacterUpdateResponse.Data; + if (!Asset) + { + return; + } + + SetupAsset(); } -void ARpmActor::HandleCharacterFindResponse(FCharacterFindByIdResponse CharacterFindByIdResponse, bool bWasSuccessful) +void ARpmActor::LoadGltfAsset(UglTFRuntimeAsset* GltfAsset) { - Character = CharacterFindByIdResponse.Data; + Asset = GltfAsset; + SetupAsset(); } -void ARpmActor::CreateCharacter() +void ARpmActor::SetupAsset() { - FCharacterCreateRequest CharacterCreateRequest = FCharacterCreateRequest(); - CharacterCreateRequest.Data.Assets = TMap(); - CharacterCreateRequest.Data.Assets.Add("baseModel", BaseModelId); - CharacterCreateRequest.Data.ApplicationId = AppId; + if (!Asset) + { + UE_LOG(LogGLTFRuntime, Warning, TEXT("No asset to setup")); + return; + } - CharacterApi->CreateAsync(CharacterCreateRequest); -} + double LoadingStartTime = FPlatformTime::Seconds(); -USkeletalMeshComponent* ARpmActor::CreateSkeletalMeshComponent(USkeletalMesh* SkeletalMesh, const FString& Name) -{ - if (AddedMeshComponents.Num() > 0) + if (RootNodeIndex > INDEX_NONE) { - for (auto AddedMeshComponent : AddedMeshComponents) + FglTFRuntimeNode Node; + if (!Asset->GetNode(RootNodeIndex, Node)) { - AddedMeshComponent->DestroyComponent(); + return; } - AddedMeshComponents.Empty(); + AssetRoot = nullptr; + ProcessNode(nullptr, NAME_None, Node); } - USkeletalMeshComponent* NewSkeletalMeshComponent = NewObject(this, *Name); - NewSkeletalMeshComponent->SetupAttachment(RootComponent); - NewSkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); - //NewSkeletalMeshComponent->SkeletalMesh->SetSkeleton(BaseSkeletalMeshComponent->SkeletalMesh->GetSkeleton()); - NewSkeletalMeshComponent->RegisterComponent(); - AddInstanceComponent(NewSkeletalMeshComponent); - AddedMeshComponents.Add(NewSkeletalMeshComponent); - - if (AddedMeshComponents.Num() == 1 && AddedMeshComponents.IsValidIndex(0)) + else { - // Get the SkeletalMeshComponent at index 0 - USkeletalMeshComponent* MeshComponent = AddedMeshComponents[0]; + TArray Scenes = Asset->GetScenes(); + for (FglTFRuntimeScene& Scene : Scenes) + { + USceneComponent* SceneComponent = NewObject(this, *FString::Printf(TEXT("Scene %d"), Scene.Index)); + SceneComponent->SetupAttachment(RootComponent); + SceneComponent->RegisterComponent(); + AddInstanceComponent(SceneComponent); + for (int32 NodeIndex : Scene.RootNodesIndices) + { + FglTFRuntimeNode Node; + if (!Asset->GetNode(NodeIndex, Node)) + { + return; + } + ProcessNode(SceneComponent, NAME_None, Node); + } + } + } - // Check if the MeshComponent is valid - if (MeshComponent && TargetAnimBP) + for (TPair& Pair : SocketMapping) + { + for (USkeletalMeshComponent* SkeletalMeshComponent : DiscoveredSkeletalMeshComponents) { - // Set the animation blueprint class - MeshComponent->SetAnimInstanceClass(TargetAnimBP); + if (SkeletalMeshComponent->DoesSocketExist(Pair.Value)) + { + Pair.Key->AttachToComponent(SkeletalMeshComponent, FAttachmentTransformRules::KeepRelativeTransform, Pair.Value); + Pair.Key->SetRelativeTransform(FTransform::Identity); + break; + } } } - else if (AddedMeshComponents.Num() > 1 && AddedMeshComponents.IsValidIndex(0)) + + UE_LOG(LogGLTFRuntime, Log, TEXT("Asset loaded in %f seconds"), FPlatformTime::Seconds() - LoadingStartTime); +} + + +void ARpmActor::ProcessNode(USceneComponent* NodeParentComponent, const FName SocketName, FglTFRuntimeNode& Node) +{ + if (Asset->NodeIsBone(Node.Index)) + { + ProcessBoneNode(NodeParentComponent, Node); + return; + } + + USceneComponent* NewComponent = CreateNewComponent(NodeParentComponent, Node); + + if (!NewComponent) { - NewSkeletalMeshComponent->SetMasterPoseComponent(AddedMeshComponents[0]); + return; } - return NewSkeletalMeshComponent; + SetupComponentTags(NewComponent, Node, SocketName); + ProcessChildNodes(NewComponent, Node); } -void ARpmActor::LoadglTFAsset(UglTFRuntimeAsset* Asset) +void ARpmActor::ProcessBoneNode(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node) { - auto mesh = Asset->LoadSkeletalMeshRecursive("", {}, SkeletalMeshConfig); - HandleSkeletalMeshLoaded(mesh); + for (int32 ChildIndex : Node.ChildrenIndices) + { + FglTFRuntimeNode Child; + if (!Asset->GetNode(ChildIndex, Child)) + { + return; + } + ProcessNode(NodeParentComponent, *Child.Name, Child); + } } -void ARpmActor::HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh) +USceneComponent* ARpmActor::CreateNewComponent(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node) { - //hide starter mesh - BaseSkeletalMeshComponent->SetVisibility(false); - CreateSkeletalMeshComponent(SkeletalMesh, "Asset"); + USceneComponent* NewComponent = nullptr; + + // Check if the node should be a skeletal mesh component + if (Node.SkinIndex >= 0 || (bStaticMeshesAsSkeletalOnMorphTargets && Asset->MeshHasMorphTargets(Node.MeshIndex))) + { + // Create a skeletal mesh component + USkeletalMeshComponent* SkeletalMeshComponent = nullptr; + + if (SkeletalMeshConfig.bPerPolyCollision) + { + SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); + SkeletalMeshComponent->bEnablePerPolyCollision = true; + SkeletalMeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + } + else + { + SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); + } + + // Load and set the skeletal mesh + USkeletalMesh* SkeletalMesh = Asset->LoadSkeletalMesh(Node.MeshIndex, Node.SkinIndex, SkeletalMeshConfig); + SkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); + + // Attach and register the component + SkeletalMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent); + SkeletalMeshComponent->RegisterComponent(); + SkeletalMeshComponent->SetRelativeTransform(Node.Transform); + + // Add the component to the list of discovered skeletal mesh components + DiscoveredSkeletalMeshComponents.Add(SkeletalMeshComponent); + + NewComponent = SkeletalMeshComponent; + + // Custom event handling for when a skeletal mesh component is created + ReceiveOnSkeletalMeshComponentCreated(SkeletalMeshComponent, Node); + } + else + { + // Create a static mesh component + UStaticMeshComponent* StaticMeshComponent = nullptr; + TArray GPUInstancingTransforms; + + if (Asset->GetNodeGPUInstancingTransforms(Node.Index, GPUInstancingTransforms)) + { + UInstancedStaticMeshComponent* InstancedStaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); + for (const FTransform& GPUInstanceTransform : GPUInstancingTransforms) + { + InstancedStaticMeshComponent->AddInstance(GPUInstanceTransform); + } + StaticMeshComponent = InstancedStaticMeshComponent; + } + else + { + StaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); + } + + // Load and set the static mesh + UStaticMesh* StaticMesh = Asset->LoadStaticMeshLODs({Node.MeshIndex}, StaticMeshConfig); + StaticMeshComponent->SetStaticMesh(StaticMesh); + + // Attach and register the component + StaticMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent); + StaticMeshComponent->RegisterComponent(); + StaticMeshComponent->SetRelativeTransform(Node.Transform); + + NewComponent = StaticMeshComponent; + + // Custom event handling for when a static mesh component is created + ReceiveOnStaticMeshComponentCreated(StaticMeshComponent, Node); + } + + // Add the component to the actor's list of instance components + AddInstanceComponent(NewComponent); + + return NewComponent; } -void ARpmActor::OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedPtr HttpResponse, - bool bIsSuccessful) + +void ARpmActor::SetupComponentTags(USceneComponent* Component, FglTFRuntimeNode& Node, const FName SocketName) { - if (bIsSuccessful) + Component->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeName:%s"), *Node.Name)); + Component->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeIndex:%d"), Node.Index)); + + if (SocketName != NAME_None) { - glTFRuntimeConfig.TransformBaseType = EglTFRuntimeTransformBaseType::YForward; - UglTFRuntimeAsset* gltfAsset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromData( - HttpResponse->GetContent(), glTFRuntimeConfig); - LoadglTFAsset(gltfAsset); + SocketMapping.Add(Component, SocketName); } } -void ARpmActor::LoadAsset(FAsset AssetData) +void ARpmActor::ProcessChildNodes(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node) { - if (Character.Id.IsEmpty()) + for (int32 ChildIndex : Node.ChildrenIndices) { - UE_LOG(LogTemp, Warning, TEXT("Character Id is empty")); - return; + FglTFRuntimeNode Child; + if (!Asset->GetNode(ChildIndex, Child)) + { + return; + } + ProcessNode(NodeParentComponent, NAME_None, Child); } +} - PreviewAssetMap.Add(AssetData.Type, AssetData.Id); - FCharacterPreviewRequest PreviewRequest; - PreviewRequest.Id = Character.Id; - PreviewRequest.Params.Assets = PreviewAssetMap; - const FString& Url = CharacterApi->GeneratePreviewUrl(PreviewRequest); - LoadCharacterUrl(Url); +// Called every frame +void ARpmActor::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); } -void ARpmActor::LoadCharacterUrl(const FString Url) +void ARpmActor::ReceiveOnStaticMeshComponentCreated_Implementation(UStaticMeshComponent* StaticMeshComponent, const FglTFRuntimeNode& Node) { - // TODO replace this with use of WebApi class - TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->SetURL(Url); - HttpRequest->SetVerb("GET"); - HttpRequest->SetHeader("Content-Type", "application/json"); - HttpRequest->OnProcessRequestComplete().BindUObject(this, &ARpmActor::OnAssetDataLoaded); - HttpRequest->ProcessRequest(); + } -void ARpmActor::LoadCharacter(FRpmCharacter CharacterData) +void ARpmActor::ReceiveOnSkeletalMeshComponentCreated_Implementation(USkeletalMeshComponent* SkeletalMeshComponent, const FglTFRuntimeNode& Node) { - LoadCharacterUrl(CharacterData.GlbUrl); + +} + +void ARpmActor::PostUnregisterAllComponents() +{ + if (Asset) + { + Asset->ClearCache(); + Asset = nullptr; + } + Super::PostUnregisterAllComponents(); } diff --git a/Source/RpmNextGen/Private/RpmActorBase.cpp b/Source/RpmNextGen/Private/RpmActorBase.cpp deleted file mode 100644 index e283a7b..0000000 --- a/Source/RpmNextGen/Private/RpmActorBase.cpp +++ /dev/null @@ -1,258 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - -#include "RpmActorBase.h" -#include "Components/InstancedStaticMeshComponent.h" -#include "Components/SkeletalMeshComponent.h" -#include "Animation/AnimSequence.h" -#include "glTFRuntimeSkeletalMeshComponent.h" - -// Sets default values -ARpmActorBase::ARpmActorBase() -{ - // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. - PrimaryActorTick.bCanEverTick = true; - AssetRoot = CreateDefaultSubobject(TEXT("AssetRoot")); - RootComponent = AssetRoot; - RootNodeIndex = INDEX_NONE; - bStaticMeshesAsSkeletalOnMorphTargets = true; -} - -// Called when the game starts or when spawned -void ARpmActorBase::BeginPlay() -{ - Super::BeginPlay(); - - if (!Asset) - { - return; - } - - SetupAsset(); -} - -void ARpmActorBase::LoadGltfAsset(UglTFRuntimeAsset* GltfAsset) -{ - Asset = GltfAsset; - SetupAsset(); -} - -void ARpmActorBase::SetupAsset() -{ - if (!Asset) - { - UE_LOG(LogGLTFRuntime, Warning, TEXT("No asset to setup")); - return; - } - - double LoadingStartTime = FPlatformTime::Seconds(); - - if (RootNodeIndex > INDEX_NONE) - { - FglTFRuntimeNode Node; - if (!Asset->GetNode(RootNodeIndex, Node)) - { - return; - } - AssetRoot = nullptr; - ProcessNode(nullptr, NAME_None, Node); - } - else - { - TArray Scenes = Asset->GetScenes(); - for (FglTFRuntimeScene& Scene : Scenes) - { - USceneComponent* SceneComponent = NewObject(this, *FString::Printf(TEXT("Scene %d"), Scene.Index)); - SceneComponent->SetupAttachment(RootComponent); - SceneComponent->RegisterComponent(); - AddInstanceComponent(SceneComponent); - for (int32 NodeIndex : Scene.RootNodesIndices) - { - FglTFRuntimeNode Node; - if (!Asset->GetNode(NodeIndex, Node)) - { - return; - } - ProcessNode(SceneComponent, NAME_None, Node); - } - } - } - - for (TPair& Pair : SocketMapping) - { - for (USkeletalMeshComponent* SkeletalMeshComponent : DiscoveredSkeletalMeshComponents) - { - if (SkeletalMeshComponent->DoesSocketExist(Pair.Value)) - { - Pair.Key->AttachToComponent(SkeletalMeshComponent, FAttachmentTransformRules::KeepRelativeTransform, Pair.Value); - Pair.Key->SetRelativeTransform(FTransform::Identity); - break; - } - } - } - - UE_LOG(LogGLTFRuntime, Log, TEXT("Asset loaded in %f seconds"), FPlatformTime::Seconds() - LoadingStartTime); -} - - -void ARpmActorBase::ProcessNode(USceneComponent* NodeParentComponent, const FName SocketName, FglTFRuntimeNode& Node) -{ - if (Asset->NodeIsBone(Node.Index)) - { - ProcessBoneNode(NodeParentComponent, Node); - return; - } - - USceneComponent* NewComponent = CreateNewComponent(NodeParentComponent, Node); - - if (!NewComponent) - { - return; - } - - SetupComponentTags(NewComponent, Node, SocketName); - ProcessChildNodes(NewComponent, Node); -} - -void ARpmActorBase::ProcessBoneNode(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node) -{ - for (int32 ChildIndex : Node.ChildrenIndices) - { - FglTFRuntimeNode Child; - if (!Asset->GetNode(ChildIndex, Child)) - { - return; - } - ProcessNode(NodeParentComponent, *Child.Name, Child); - } -} - -USceneComponent* ARpmActorBase::CreateNewComponent(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node) -{ - USceneComponent* NewComponent = nullptr; - - // Check if the node should be a skeletal mesh component - if (Node.SkinIndex >= 0 || (bStaticMeshesAsSkeletalOnMorphTargets && Asset->MeshHasMorphTargets(Node.MeshIndex))) - { - // Create a skeletal mesh component - USkeletalMeshComponent* SkeletalMeshComponent = nullptr; - - if (SkeletalMeshConfig.bPerPolyCollision) - { - SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); - SkeletalMeshComponent->bEnablePerPolyCollision = true; - SkeletalMeshComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); - } - else - { - SkeletalMeshComponent = NewObject(this, GetSafeNodeName(Node)); - } - - // Load and set the skeletal mesh - USkeletalMesh* SkeletalMesh = Asset->LoadSkeletalMesh(Node.MeshIndex, Node.SkinIndex, SkeletalMeshConfig); - SkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); - - // Attach and register the component - SkeletalMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent); - SkeletalMeshComponent->RegisterComponent(); - SkeletalMeshComponent->SetRelativeTransform(Node.Transform); - - // Add the component to the list of discovered skeletal mesh components - DiscoveredSkeletalMeshComponents.Add(SkeletalMeshComponent); - - NewComponent = SkeletalMeshComponent; - - // Custom event handling for when a skeletal mesh component is created - ReceiveOnSkeletalMeshComponentCreated(SkeletalMeshComponent, Node); - } - else - { - // Create a static mesh component - UStaticMeshComponent* StaticMeshComponent = nullptr; - TArray GPUInstancingTransforms; - - if (Asset->GetNodeGPUInstancingTransforms(Node.Index, GPUInstancingTransforms)) - { - UInstancedStaticMeshComponent* InstancedStaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); - for (const FTransform& GPUInstanceTransform : GPUInstancingTransforms) - { - InstancedStaticMeshComponent->AddInstance(GPUInstanceTransform); - } - StaticMeshComponent = InstancedStaticMeshComponent; - } - else - { - StaticMeshComponent = NewObject(this, GetSafeNodeName(Node)); - } - - // Load and set the static mesh - UStaticMesh* StaticMesh = Asset->LoadStaticMeshLODs({Node.MeshIndex}, StaticMeshConfig); - StaticMeshComponent->SetStaticMesh(StaticMesh); - - // Attach and register the component - StaticMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent); - StaticMeshComponent->RegisterComponent(); - StaticMeshComponent->SetRelativeTransform(Node.Transform); - - NewComponent = StaticMeshComponent; - - // Custom event handling for when a static mesh component is created - ReceiveOnStaticMeshComponentCreated(StaticMeshComponent, Node); - } - - // Add the component to the actor's list of instance components - AddInstanceComponent(NewComponent); - - return NewComponent; -} - - -void ARpmActorBase::SetupComponentTags(USceneComponent* Component, FglTFRuntimeNode& Node, const FName SocketName) -{ - Component->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeName:%s"), *Node.Name)); - Component->ComponentTags.Add(*FString::Printf(TEXT("glTFRuntime:NodeIndex:%d"), Node.Index)); - - if (SocketName != NAME_None) - { - SocketMapping.Add(Component, SocketName); - } -} - -void ARpmActorBase::ProcessChildNodes(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node) -{ - for (int32 ChildIndex : Node.ChildrenIndices) - { - FglTFRuntimeNode Child; - if (!Asset->GetNode(ChildIndex, Child)) - { - return; - } - ProcessNode(NodeParentComponent, NAME_None, Child); - } -} - -// Called every frame -void ARpmActorBase::Tick(float DeltaTime) -{ - Super::Tick(DeltaTime); -} - -void ARpmActorBase::ReceiveOnStaticMeshComponentCreated_Implementation(UStaticMeshComponent* StaticMeshComponent, const FglTFRuntimeNode& Node) -{ - -} - -void ARpmActorBase::ReceiveOnSkeletalMeshComponentCreated_Implementation(USkeletalMeshComponent* SkeletalMeshComponent, const FglTFRuntimeNode& Node) -{ - -} - -void ARpmActorBase::PostUnregisterAllComponents() -{ - if (Asset) - { - Asset->ClearCache(); - Asset = nullptr; - } - Super::PostUnregisterAllComponents(); -} diff --git a/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp b/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp index 94ac2ff..476c47e 100644 --- a/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp +++ b/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp @@ -24,7 +24,7 @@ void URpmAssetLoaderComponent::BeginPlay() void URpmAssetLoaderComponent::LoadCharacterFromUrl(const FString Url) { AssetLoader->OnGLtfAssetLoaded.BindLambda( - [this](FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful) + [this](UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful) { if (!gltfAsset || !bWasSuccessful) { diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp index 968f509..05015bd 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp @@ -107,6 +107,11 @@ void URpmAssetPanelWidget::OnAssetButtonClicked(const URpmAssetButtonWidget* Ass void URpmAssetPanelWidget::LoadAssetsOfType(const FString& AssetType) { + if (!AssetApi.IsValid()) + { + UE_LOG(LogTemp, Error, TEXT("AssetApi is null or invalid")); + return; + } URpmDeveloperSettings *Settings = GetMutableDefault(); FAssetListQueryParams QueryParams; QueryParams.Type = AssetType; diff --git a/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h index 773713e..75cf1c3 100644 --- a/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h +++ b/Source/RpmNextGen/Public/Api/Assets/AssetLoader.h @@ -9,7 +9,9 @@ struct FglTFRuntimeConfig; class UglTFRuntimeAsset; DECLARE_DELEGATE_TwoParams(FOnAssetDataReceived, TArray, bool); -DECLARE_DELEGATE_ThreeParams(FOnAssetDownloaded, FString, UglTFRuntimeAsset*, bool); +DECLARE_DELEGATE_TwoParams(FOnAssetSaved, FString, bool); +DECLARE_DELEGATE_TwoParams(FOnAssetDownloaded, UglTFRuntimeAsset*, bool); + class RPMNEXTGEN_API FAssetLoader : public FWebApi { @@ -22,9 +24,11 @@ class RPMNEXTGEN_API FAssetLoader : public FWebApi FOnAssetDataReceived OnRequestDataReceived; FOnAssetDownloaded OnGLtfAssetLoaded; + FOnAssetSaved OnAssetSaved; + bool bSaveToDisk = false; protected: FglTFRuntimeConfig* GltfConfig; - void virtual OnDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); + void virtual OnLoadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); FString DownloadDirectory; }; diff --git a/Source/RpmNextGen/Public/RpmActor.h b/Source/RpmNextGen/Public/RpmActor.h index 7295130..8348e9d 100644 --- a/Source/RpmNextGen/Public/RpmActor.h +++ b/Source/RpmNextGen/Public/RpmActor.h @@ -3,87 +3,80 @@ #pragma once #include "CoreMinimal.h" -#include "glTFRuntimeParser.h" -#include "Api/Assets/Models/Asset.h" -#include "Api/Characters/Models/CharacterCreateResponse.h" -#include "Api/Characters/Models/CharacterFindByIdResponse.h" -#include "Api/Characters/Models/CharacterUpdateResponse.h" +#include "GameFramework/Actor.h" +#include "glTFRuntimeAsset.h" #include "RpmActor.generated.h" -class IHttpResponse; -class IHttpRequest; -class FCharacterApi; -struct FRpmCharacter; -class URpmDeveloperSettings; -class UglTFRuntimeAsset; - -/** - * - */ UCLASS() class RPMNEXTGEN_API ARpmActor : public AActor { GENERATED_BODY() - -public: + +public: + // Sets default values for this actor's properties ARpmActor(); - UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - USkeletalMeshComponent* CreateSkeletalMeshComponent(USkeletalMesh* SkeletalMesh, const FString& Name); - - UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - void LoadglTFAsset(UglTFRuntimeAsset* Asset); - - void OnAssetDataLoaded(TSharedPtr HttpRequest, TSharedPtr HttpResponse, bool bIsSuccessful); +protected: + // Called when the game starts or when spawned + virtual void BeginPlay() override; + + virtual void ProcessNode(USceneComponent* NodeParentComponent, const FName SocketName, FglTFRuntimeNode& Node); - UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - void LoadAsset(FAsset AssetData); + template + FName GetSafeNodeName(const FglTFRuntimeNode& Node) + { + return MakeUniqueObjectName(this, T::StaticClass(), *Node.Name); + } - UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - void LoadCharacterUrl(FString Url); + UPROPERTY() + TMap SocketMapping; + UPROPERTY() + TArray DiscoveredSkeletalMeshComponents; - UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - void LoadCharacter(FRpmCharacter CharacterData); +public: + // Called every frame + virtual void Tick(float DeltaTime) override; - UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="Ready Player Me"); - USkeletalMeshComponent* BaseSkeletalMeshComponent; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + UglTFRuntimeAsset* Asset; - UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - void CreateCharacter(); + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + FglTFRuntimeStaticMeshConfig StaticMeshConfig; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Ready Player Me") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") FglTFRuntimeSkeletalMeshConfig SkeletalMeshConfig; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Ready Player Me") - FglTFRuntimeConfig glTFRuntimeConfig; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + FglTFRuntimeSkeletalAnimationConfig SkeletalAnimationConfig; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Ready Player Me") - FString BaseModelId; + UFUNCTION(BlueprintNativeEvent, Category = "Ready Player Me|glTFRuntime", meta = (DisplayName = "On StaticMeshComponent Created")) + void ReceiveOnStaticMeshComponentCreated(UStaticMeshComponent* StaticMeshComponent, const FglTFRuntimeNode& Node); - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Animation") - TSubclassOf TargetAnimBP; + UFUNCTION(BlueprintNativeEvent, Category = "Ready Player Me|glTFRuntime", meta = (DisplayName = "On SkeletalMeshComponent Created")) + void ReceiveOnSkeletalMeshComponentCreated(USkeletalMeshComponent* SkeletalMeshComponent, const FglTFRuntimeNode& Node); - UFUNCTION() - virtual void HandleSkeletalMeshLoaded(USkeletalMesh* SkeletalMesh); + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + int32 RootNodeIndex; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") + bool bStaticMeshesAsSkeletalOnMorphTargets; -protected: - FRpmCharacter Character; + DECLARE_MULTICAST_DELEGATE_TwoParams(FglTFRuntimeAssetActorNodeProcessed, const FglTFRuntimeNode&, USceneComponent*); + FglTFRuntimeAssetActorNodeProcessed OnNodeProcessed; + virtual void PostUnregisterAllComponents() override; - TArray AddedMeshComponents; - UFUNCTION() - virtual void HandleCharacterCreateResponse(FCharacterCreateResponse CharacterCreateResponse, bool bWasSuccessful); - UFUNCTION() - virtual void HandleCharacterUpdateResponse(FCharacterUpdateResponse CharacterUpdateResponse, bool bWasSuccessful); - UFUNCTION() - virtual void HandleCharacterFindResponse(FCharacterFindByIdResponse CharacterFindByIdResponse, bool bWasSuccessful); + UFUNCTION(BlueprintCallable, Category = "Ready Player Me") + virtual void LoadGltfAsset(UglTFRuntimeAsset* GltfAsset); - TMap PreviewAssetMap; + virtual void SetupAsset(); private: - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category="Ready Player Me") + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category="Ready Player Me|glTFRuntime") USceneComponent* AssetRoot; - FglTFRuntimeSkeletalMeshAsync OnSkeletalMeshCallback; - TSharedPtr CharacterApi; - FString AppId; + + void ProcessBoneNode(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node); + USceneComponent* CreateNewComponent(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node); + void SetupComponentTags(USceneComponent* Component, FglTFRuntimeNode& Node, const FName SocketName); + void ProcessChildNodes(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node); }; diff --git a/Source/RpmNextGen/Public/RpmActorBase.h b/Source/RpmNextGen/Public/RpmActorBase.h deleted file mode 100644 index 60276e6..0000000 --- a/Source/RpmNextGen/Public/RpmActorBase.h +++ /dev/null @@ -1,82 +0,0 @@ -// Fill out your copyright notice in the Description page of Project Settings. - -#pragma once - -#include "CoreMinimal.h" -#include "GameFramework/Actor.h" -#include "glTFRuntimeAsset.h" -#include "RpmActorBase.generated.h" - -UCLASS() -class RPMNEXTGEN_API ARpmActorBase : public AActor -{ - GENERATED_BODY() - -public: - // Sets default values for this actor's properties - ARpmActorBase(); - -protected: - // Called when the game starts or when spawned - virtual void BeginPlay() override; - - virtual void ProcessNode(USceneComponent* NodeParentComponent, const FName SocketName, FglTFRuntimeNode& Node); - - template - FName GetSafeNodeName(const FglTFRuntimeNode& Node) - { - return MakeUniqueObjectName(this, T::StaticClass(), *Node.Name); - } - - UPROPERTY() - TMap SocketMapping; - UPROPERTY() - TArray DiscoveredSkeletalMeshComponents; - -public: - // Called every frame - virtual void Tick(float DeltaTime) override; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") - UglTFRuntimeAsset* Asset; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") - FglTFRuntimeStaticMeshConfig StaticMeshConfig; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") - FglTFRuntimeSkeletalMeshConfig SkeletalMeshConfig; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") - FglTFRuntimeSkeletalAnimationConfig SkeletalAnimationConfig; - - UFUNCTION(BlueprintNativeEvent, Category = "Ready Player Me|glTFRuntime", meta = (DisplayName = "On StaticMeshComponent Created")) - void ReceiveOnStaticMeshComponentCreated(UStaticMeshComponent* StaticMeshComponent, const FglTFRuntimeNode& Node); - - UFUNCTION(BlueprintNativeEvent, Category = "Ready Player Me|glTFRuntime", meta = (DisplayName = "On SkeletalMeshComponent Created")) - void ReceiveOnSkeletalMeshComponentCreated(USkeletalMeshComponent* SkeletalMeshComponent, const FglTFRuntimeNode& Node); - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") - int32 RootNodeIndex; - - UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (ExposeOnSpawn = true), Category = "Ready Player Me|glTFRuntime") - bool bStaticMeshesAsSkeletalOnMorphTargets; - - DECLARE_MULTICAST_DELEGATE_TwoParams(FglTFRuntimeAssetActorNodeProcessed, const FglTFRuntimeNode&, USceneComponent*); - FglTFRuntimeAssetActorNodeProcessed OnNodeProcessed; - - virtual void PostUnregisterAllComponents() override; - - UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - virtual void LoadGltfAsset(UglTFRuntimeAsset* GltfAsset); - - virtual void SetupAsset(); - -private: - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"), Category="Ready Player Me|glTFRuntime") - USceneComponent* AssetRoot; - - void ProcessBoneNode(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node); - USceneComponent* CreateNewComponent(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node); - void SetupComponentTags(USceneComponent* Component, FglTFRuntimeNode& Node, const FName SocketName); - void ProcessChildNodes(USceneComponent* NodeParentComponent, FglTFRuntimeNode& Node); -}; diff --git a/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h b/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h index 61efd14..106b04a 100644 --- a/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h +++ b/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h @@ -7,6 +7,7 @@ #include "Api/Characters/Models/RpmCharacter.h" #include "RpmPreviewLoaderComponent.generated.h" +class FCharacterApi; struct FCharacterCreateResponse; struct FCharacterUpdateResponse; struct FCharacterFindByIdResponse; diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 1dffdf2..4b0eedb 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -1,9 +1,7 @@ #include "EditorAssetLoader.h" -#include "RpmActor.h" #include "TransientObjectSaverLibrary.h" #include "AssetNameGenerator.h" -#include "glTFRuntimeAssetActor.h" -#include "RpmActorBase.h" +#include "RpmActor.h" FEditorAssetLoader::FEditorAssetLoader() { @@ -14,7 +12,7 @@ FEditorAssetLoader::~FEditorAssetLoader() { } -void FEditorAssetLoader::OnAssetDownloadComplete(FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful, +void FEditorAssetLoader::OnAssetLoadComplete(UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful, FString LoadedAssetId) { if (bWasSuccessful) @@ -54,7 +52,7 @@ USkeletalMesh* FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* GltfAsset, co void FEditorAssetLoader::LoadGLBFromURLWithId(const FString& URL, FString LoadedAssetId) { OnGLtfAssetLoaded.BindLambda( - [LoadedAssetId, this](FString FilePath, UglTFRuntimeAsset* gltfAsset, + [LoadedAssetId, this]( UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful) { if (!gltfAsset) @@ -62,7 +60,7 @@ void FEditorAssetLoader::LoadGLBFromURLWithId(const FString& URL, FString Loaded UE_LOG(LogTemp, Log, TEXT("No gltf asset")); return; } - OnAssetDownloadComplete(FilePath, gltfAsset, bWasSuccessful, LoadedAssetId); + OnAssetLoadComplete(gltfAsset, bWasSuccessful, LoadedAssetId); }); LoadGLBFromURL(URL); } @@ -93,7 +91,7 @@ void FEditorAssetLoader::LoadAssetToWorld(FString AssetId, UglTFRuntimeAsset* gl FTransform Transform = FTransform::Identity; - ARpmActorBase* NewActor = EditorWorld->SpawnActorDeferred(ARpmActorBase::StaticClass(), Transform); + ARpmActor* NewActor = EditorWorld->SpawnActorDeferred(ARpmActor::StaticClass(), Transform); if (NewActor) { diff --git a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h index 7bd3fdb..2dfa540 100644 --- a/Source/RpmNextGenEditor/Public/EditorAssetLoader.h +++ b/Source/RpmNextGenEditor/Public/EditorAssetLoader.h @@ -7,7 +7,7 @@ class RPMNEXTGENEDITOR_API FEditorAssetLoader : public FAssetLoader { public: - void OnAssetDownloadComplete(FString FilePath, UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful, + void OnAssetLoadComplete(UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful, FString LoadedAssetId); FEditorAssetLoader(); virtual ~FEditorAssetLoader() override; From b82ba89b5bde0e28cf9532c529a0c7b37bd9d4b9 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 26 Aug 2024 16:17:28 +0300 Subject: [PATCH 56/74] feat: added custom log category --- .../RpmNextGen/Private/Api/Assets/AssetLoader.cpp | 5 +++-- .../Private/RpmAssetLoaderComponent.cpp | 3 ++- Source/RpmNextGen/Private/RpmNextGen.cpp | 2 ++ .../Private/RpmPreviewLoaderComponent.cpp | 8 +++++--- Source/RpmNextGen/Public/RpmNextGen.h | 2 ++ .../Private/EditorAssetLoader.cpp | 15 ++++++++------- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp index 87ffaef..88f8f09 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetLoader.cpp @@ -1,5 +1,6 @@ #include "Api/Assets/AssetLoader.h" #include "HttpModule.h" +#include "RpmNextGen.h" #include "glTFRuntime/Public/glTFRuntimeFunctionLibrary.h" #include "Interfaces/IHttpResponse.h" #include "Misc/FileHelper.h" @@ -51,7 +52,7 @@ void FAssetLoader::OnLoadComplete(FHttpRequestPtr Request, FHttpResponsePtr Resp if(bSaveToDisk && FFileHelper::SaveArrayToFile(Response->GetContent(), *FilePath)) { - UE_LOG(LogTemp, Log, TEXT("Downloaded GLB file to %s"), *FilePath); + UE_LOG(LogReadyPlayerMe, Log, TEXT("Downloaded GLB file to %s"), *FilePath); AssetFileSaved = true; } if(OnGLtfAssetLoaded.IsBound()) @@ -63,7 +64,7 @@ void FAssetLoader::OnLoadComplete(FHttpRequestPtr Request, FHttpResponsePtr Resp OnAssetSaved.ExecuteIfBound(FilePath, AssetFileSaved); return; } - UE_LOG(LogTemp, Error, TEXT("Failed to load GLB from URL")); + UE_LOG(LogReadyPlayerMe, Error, TEXT("Failed to load GLB from URL")); OnRequestDataReceived.ExecuteIfBound(Content, Content.Num() > 0); OnGLtfAssetLoaded.ExecuteIfBound(gltfAsset, gltfAsset != nullptr); OnAssetSaved.ExecuteIfBound("", AssetFileSaved); diff --git a/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp b/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp index 476c47e..d4fa2c8 100644 --- a/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp +++ b/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp @@ -2,6 +2,7 @@ #include "RpmAssetLoaderComponent.h" +#include "RpmNextGen.h" #include "Api/Assets/AssetLoader.h" class URpmDeveloperSettings; @@ -28,7 +29,7 @@ void URpmAssetLoaderComponent::LoadCharacterFromUrl(const FString Url) { if (!gltfAsset || !bWasSuccessful) { - UE_LOG(LogTemp, Log, TEXT("Failed to load gltf asset")); + UE_LOG(LogReadyPlayerMe, Log, TEXT("Failed to load gltf asset")); return; } OnGltfAssetLoaded.Broadcast(gltfAsset); diff --git a/Source/RpmNextGen/Private/RpmNextGen.cpp b/Source/RpmNextGen/Private/RpmNextGen.cpp index 5f3e828..818e442 100644 --- a/Source/RpmNextGen/Private/RpmNextGen.cpp +++ b/Source/RpmNextGen/Private/RpmNextGen.cpp @@ -2,6 +2,8 @@ #include "RpmNextGen.h" +DEFINE_LOG_CATEGORY(LogReadyPlayerMe); + #define LOCTEXT_NAMESPACE "FRpmNextGenModule" void FRpmNextGenModule::StartupModule() diff --git a/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp index ca02dfc..5d5571a 100644 --- a/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp +++ b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp @@ -2,6 +2,8 @@ #include "RpmPreviewLoaderComponent.h" + +#include "RpmNextGen.h" #include "Api/Assets/Models/Asset.h" #include "Api/Auth/ApiKeyAuthStrategy.h" #include "Api/Characters/CharacterApi.h" @@ -23,7 +25,7 @@ URpmPreviewLoaderComponent::URpmPreviewLoaderComponent() if(RpmSettings->ApiProxyUrl.IsEmpty() && !RpmSettings->ApiKey.IsEmpty()) { CharacterApi->SetAuthenticationStrategy(new FApiKeyAuthStrategy()); - UE_LOG(LogTemp, Warning, TEXT("Adding ApiKeyAuthStrategy")); + UE_LOG(LogReadyPlayerMe, Warning, TEXT("Adding ApiKeyAuthStrategy")); } CharacterApi->OnCharacterCreateResponse.BindUObject(this, &URpmPreviewLoaderComponent::HandleCharacterCreateResponse); CharacterApi->OnCharacterUpdateResponse.BindUObject(this, &URpmPreviewLoaderComponent::HandleCharacterUpdateResponse); @@ -35,7 +37,7 @@ void URpmPreviewLoaderComponent::CreateCharacter() { if(BaseModelId.IsEmpty()) { - UE_LOG(LogTemp, Error, TEXT("BaseModelId is empty on %s"), *GetOwner()->GetName()); + UE_LOG(LogReadyPlayerMe, Error, TEXT("BaseModelId is empty on %s"), *GetOwner()->GetName()); return; } FCharacterCreateRequest CharacterCreateRequest = FCharacterCreateRequest(); @@ -74,7 +76,7 @@ void URpmPreviewLoaderComponent::LoadAssetPreview(FAsset AssetData) { if (Character.Id.IsEmpty()) { - UE_LOG(LogTemp, Warning, TEXT("Character Id is empty")); + UE_LOG(LogReadyPlayerMe, Warning, TEXT("Character Id is empty")); return; } diff --git a/Source/RpmNextGen/Public/RpmNextGen.h b/Source/RpmNextGen/Public/RpmNextGen.h index d788306..48fa711 100644 --- a/Source/RpmNextGen/Public/RpmNextGen.h +++ b/Source/RpmNextGen/Public/RpmNextGen.h @@ -5,6 +5,8 @@ #include "CoreMinimal.h" #include "Modules/ModuleManager.h" +RPMNEXTGEN_API DECLARE_LOG_CATEGORY_EXTERN(LogReadyPlayerMe, Log, All); + class FRpmNextGenModule : public IModuleInterface { public: diff --git a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp index 4b0eedb..2bc520c 100644 --- a/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp +++ b/Source/RpmNextGenEditor/Private/EditorAssetLoader.cpp @@ -2,6 +2,7 @@ #include "TransientObjectSaverLibrary.h" #include "AssetNameGenerator.h" #include "RpmActor.h" +#include "RpmNextGen.h" FEditorAssetLoader::FEditorAssetLoader() { @@ -45,7 +46,7 @@ USkeletalMesh* FEditorAssetLoader::SaveAsUAsset(UglTFRuntimeAsset* GltfAsset, co UTransientObjectSaverLibrary::SaveTransientSkeletalMesh(skeletalMesh, SkeletalMeshAssetPath, SkeletonAssetPath, TEXT(""), NameGenerator->MaterialNameGeneratorDelegate, NameGenerator->TextureNameGeneratorDelegate); - UE_LOG(LogTemp, Log, TEXT("Character model saved: %s"), *LoadedAssetId); + UE_LOG(LogReadyPlayerMe, Log, TEXT("Character model saved: %s"), *LoadedAssetId); return skeletalMesh; } @@ -57,7 +58,7 @@ void FEditorAssetLoader::LoadGLBFromURLWithId(const FString& URL, FString Loaded { if (!gltfAsset) { - UE_LOG(LogTemp, Log, TEXT("No gltf asset")); + UE_LOG(LogReadyPlayerMe, Log, TEXT("No gltf asset")); return; } OnAssetLoadComplete(gltfAsset, bWasSuccessful, LoadedAssetId); @@ -75,14 +76,14 @@ void FEditorAssetLoader::LoadAssetToWorld(FString AssetId, UglTFRuntimeAsset* gl { if (!GEditor) { - UE_LOG(LogTemp, Error, TEXT("GEditor is not available.")); + UE_LOG(LogReadyPlayerMe, Error, TEXT("GEditor is not available.")); return; } UWorld* EditorWorld = GEditor->GetEditorWorldContext().World(); if (!EditorWorld) { - UE_LOG(LogTemp, Error, TEXT("No valid editor world found.")); + UE_LOG(LogReadyPlayerMe, Error, TEXT("No valid editor world found.")); return; } @@ -113,11 +114,11 @@ void FEditorAssetLoader::LoadAssetToWorld(FString AssetId, UglTFRuntimeAsset* gl { NewActor->LoadGltfAsset(gltfAsset); } - UE_LOG(LogTemp, Log, TEXT("Successfully loaded GLB asset into the editor world")); + UE_LOG(LogReadyPlayerMe, Log, TEXT("Successfully loaded GLB asset into the editor world")); return; } - UE_LOG(LogTemp, Error, TEXT("Failed to spawn ARpmActor in the editor world")); + UE_LOG(LogReadyPlayerMe, Error, TEXT("Failed to spawn ARpmActor in the editor world")); } - UE_LOG(LogTemp, Error, TEXT("Failed to load GLB asset from file")); + UE_LOG(LogReadyPlayerMe, Error, TEXT("Failed to load GLB asset from file")); } From b8ce96533bbcab9b55686772b8b488b45c847e06 Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 28 Aug 2024 11:32:55 +0300 Subject: [PATCH 57/74] feat: cleanup and added deletion of previously added components --- .../Blueprints/WBP_LoaderDemoUI.uasset | Bin 110065 -> 109615 bytes Content/Samples/LoaderDemo/LoaderSample.umap | Bin 91328 -> 91328 bytes Source/RpmNextGen/Private/RpmActor.cpp | 20 ++++++++++++++ .../Private/RpmAssetLoaderComponent.cpp | 26 ++++++++++-------- .../Private/RpmPreviewLoaderComponent.cpp | 9 +----- Source/RpmNextGen/Public/RpmActor.h | 1 + .../Public/RpmAssetLoaderComponent.h | 4 +++ 7 files changed, 40 insertions(+), 20 deletions(-) diff --git a/Content/Samples/LoaderDemo/Blueprints/WBP_LoaderDemoUI.uasset b/Content/Samples/LoaderDemo/Blueprints/WBP_LoaderDemoUI.uasset index 397bba20e72b464cf92f74f6e7a6b4ec4380f663..9fe7cdb85e43ac8ca83cd41072292e018af21923 100644 GIT binary patch delta 1084 zcmZXRZETBC6vumxb()&&*_0HEjTBqGY{cH|MT^#K#a0aU(k&Q!By^!nI+=vbhJ-n| zo5gD)TQ^%#@qAe#A_$R?EE4gOWxhx(BqQR358QkEAmKK*=Xd}2{O>vUJ|l19Bd_9b z&H&|AZvCdCks4=zZ^LlhiaV{80|A_MBiaSyG9WJ6ZznUh8>u2&HHYF1azrv{UXG7a zb4=!&x@4crB^6)K7dy#Do@U~+Pv7mzm2$i;Kh@>RLK@2T(eqpd)0`YtyzP=oXP$!P zr2>;hsyOPB?)kh@W;Et2cycwh=BsF5&x4QFEmUw|qrl`*x+f_5;G=g7-Jj#8$pto? zwvC(xD!#9wkwPDhE>v*TE`iC&^F*C~0eqzGmKlRQ<86SWifwzPq7^B)X210OzRz!u za!}R@72EJxh^`i^San(|_ZB6Pe~}eyTlJ+417~zpsK8;tgG3b$6%U`~!AprwD?W;0 z$Z3THDtD}=4_x$~BUsE^5uuVEEVkg+7-lZ9VA*-8c5u9Qo^CBs@w*7IJTH?txYUB( zF)VXg5Zb8PrDA29Y^KqjK&fskmbL3k8}5tYu-k?g+bL<8ipQ_=O*>mv4kn5$%fpUN znVeZ-!{uFczr;t85(U2tRIw;5m1D~lJRjIM_QgSheyo;F!exdtf{pm<|yZs0AYnT;|KF-H;~kn81{oa93_XA4|#u3p=ra zt6Aw>O>=<}YFNcC*v@WbK%Ep=g$gd_*$h)zW)+yUA9gqZ;(#4&a5eLJeD;yZzk`Xy(xl^nkO4hnV5e8e9|5k!?TJ$ zILuq*cnsbLF7;V@xgUf4kP^9hQA^7FbZ|p3qSVKD|H_7fei!OJY>mn zi=99iJx(udQ^-Yr*gDS%3*OxBAqg8c*L%p76I7~Ki1&4?n~`8HT^aO}B@@ooN6Kt? zJLn@_6ZDY=g@jIHa2;zIfDBsG;H81aTsqg_B^{HNDbVO6Iq%WSjbS?7=q49ARLH+J zGuGrL^;10f;KK&(dAklz(dTxAY1a-n`G!Mojf78;l1+ycQof0e00WZ5RKrOo=z;d%C2Wkt&4cNsV zN$Zhyp)O&_DJ`DV#iyl|7>ajmk~YNVH^bxX%xai!ZhRo+0)dOvnAoPHr6(=ws@Rm& zsan6NBb89qJ_B7zHdhG+tbYys(y$$~Hp@eTok#R{4~d!_ zE3$L+$wO$Aba61ANQm9CXr$#R&&o4dDF-C%S~OBT<5E`K2D$8~3VbL3N-WR@tC_hL zjLvqfvX@H*r%5}%?UvGMH4>L}Og$Lad#$`!dA%5wW0H~3#fX|r>zWbaig--qT@6ZU zF{$ceOf`~G7VyLOBYjw-Rn`qHDcYs#R5c+>Ni3ydk7$&VVqBN|Ewg15{Xr>_##k~M zk6;5<&R(1+GFBUWi1=n4FODyzsRQvS_J9o(xxk0@_&RJK>{JPqu|w^!icPPDY;(8> z27%2~fWWSoLxk-w27_PaV`1>K&sFfS_<9Hy`qA$E0sG=n{1L=9oI<2m*6eR@sL;v^ zO2Ei}2$fCAsC-~yh%Of^^+Ub+T?u@V!3wJ2t~vGuypqk{*a&}`i?+aD)!l zL^m=tGB7nVL^(J!L_#nzHbX*0H8(W?lZ^@(x8V>0dn6D@ekB?*64^zr7IL6L6~RW6 zaql>jP)!oIwlx8F78f9{3+5-sKfpc-?KSdzYe4qw%GvwnWS7xN0W}Svm#TF@0001B X6*_T1mXJ(nLO0SdRxOab#3xS>AZ delta 169 zcmV;a09OCN$pyg41+bn05F8xe_rLbsLYKuvZj^KWu7j=tx2^#J`VJvMLpC@vMnOU} zGd4vwIWs{0dn6E=!J5-5asxu7ltD=5#9Awp zaql>jP)!oIwlx8F78ee&*kJ(LRKNt#CnADjcR)NQ-S(_CYM0ST0W}Sv$ftEc0000W X_#ARTmXJ(nLO0SdRxOab#38!|w# diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index 6f16056..e86f280 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -33,10 +33,30 @@ void ARpmActor::BeginPlay() void ARpmActor::LoadGltfAsset(UglTFRuntimeAsset* GltfAsset) { + // Before loading a new asset, clear existing components + ClearLoadedComponents(); + Asset = GltfAsset; SetupAsset(); } +void ARpmActor::ClearLoadedComponents() +{ + if (RootComponent) + { + TArray ChildComponents; + RootComponent->GetChildrenComponents(true, ChildComponents); + + for (USceneComponent* ChildComponent : ChildComponents) + { + if (ChildComponent && ChildComponent != RootComponent) + { + ChildComponent->DestroyComponent(); + } + } + } +} + void ARpmActor::SetupAsset() { if (!Asset) diff --git a/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp b/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp index d4fa2c8..3bc7f19 100644 --- a/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp +++ b/Source/RpmNextGen/Private/RpmAssetLoaderComponent.cpp @@ -14,6 +14,10 @@ URpmAssetLoaderComponent::URpmAssetLoaderComponent() // off to improve performance if you don't need them. PrimaryComponentTick.bCanEverTick = false; AssetLoader = MakeShared(); + AssetLoader->OnGLtfAssetLoaded.BindUObject( + this, + &URpmAssetLoaderComponent::HandleGLtfAssetLoaded + ); } // Called when the game starts @@ -24,21 +28,19 @@ void URpmAssetLoaderComponent::BeginPlay() void URpmAssetLoaderComponent::LoadCharacterFromUrl(const FString Url) { - AssetLoader->OnGLtfAssetLoaded.BindLambda( - [this](UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful) - { - if (!gltfAsset || !bWasSuccessful) - { - UE_LOG(LogReadyPlayerMe, Log, TEXT("Failed to load gltf asset")); - return; - } - OnGltfAssetLoaded.Broadcast(gltfAsset); - }); - AssetLoader->LoadGLBFromURL(Url); } - +void URpmAssetLoaderComponent::HandleGLtfAssetLoaded(UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful) +{ + if (!gltfAsset || !bWasSuccessful) + { + UE_LOG(LogReadyPlayerMe, Log, TEXT("Failed to load gltf asset")); + OnGltfAssetLoaded.Broadcast(nullptr); + return; + } + OnGltfAssetLoaded.Broadcast(gltfAsset); +} // Called every frame void URpmAssetLoaderComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) diff --git a/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp index 5d5571a..47c5159 100644 --- a/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp +++ b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp @@ -5,7 +5,6 @@ #include "RpmNextGen.h" #include "Api/Assets/Models/Asset.h" -#include "Api/Auth/ApiKeyAuthStrategy.h" #include "Api/Characters/CharacterApi.h" #include "Api/Characters/Models/CharacterCreateResponse.h" #include "Api/Characters/Models/CharacterFindByIdResponse.h" @@ -21,16 +20,10 @@ URpmPreviewLoaderComponent::URpmPreviewLoaderComponent() URpmDeveloperSettings* RpmSettings = GetMutableDefault(); AppId = RpmSettings->ApplicationId; CharacterApi = MakeShared(); - // TODO - add smarter setting of auth strategy - if(RpmSettings->ApiProxyUrl.IsEmpty() && !RpmSettings->ApiKey.IsEmpty()) - { - CharacterApi->SetAuthenticationStrategy(new FApiKeyAuthStrategy()); - UE_LOG(LogReadyPlayerMe, Warning, TEXT("Adding ApiKeyAuthStrategy")); - } + PreviewAssetMap = TMap(); CharacterApi->OnCharacterCreateResponse.BindUObject(this, &URpmPreviewLoaderComponent::HandleCharacterCreateResponse); CharacterApi->OnCharacterUpdateResponse.BindUObject(this, &URpmPreviewLoaderComponent::HandleCharacterUpdateResponse); CharacterApi->OnCharacterFindResponse.BindUObject(this, &URpmPreviewLoaderComponent::HandleCharacterFindResponse); - PreviewAssetMap = TMap(); } void URpmPreviewLoaderComponent::CreateCharacter() diff --git a/Source/RpmNextGen/Public/RpmActor.h b/Source/RpmNextGen/Public/RpmActor.h index 8348e9d..5bd9bb0 100644 --- a/Source/RpmNextGen/Public/RpmActor.h +++ b/Source/RpmNextGen/Public/RpmActor.h @@ -68,6 +68,7 @@ class RPMNEXTGEN_API ARpmActor : public AActor UFUNCTION(BlueprintCallable, Category = "Ready Player Me") virtual void LoadGltfAsset(UglTFRuntimeAsset* GltfAsset); + void ClearLoadedComponents(); virtual void SetupAsset(); diff --git a/Source/RpmNextGen/Public/RpmAssetLoaderComponent.h b/Source/RpmNextGen/Public/RpmAssetLoaderComponent.h index 6e24670..2c4b1f5 100644 --- a/Source/RpmNextGen/Public/RpmAssetLoaderComponent.h +++ b/Source/RpmNextGen/Public/RpmAssetLoaderComponent.h @@ -6,6 +6,7 @@ #include "Components/ActorComponent.h" #include "RpmAssetLoaderComponent.generated.h" +class UglTFRuntimeAsset; class FAssetLoader; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGltfAssetLoaded, UglTFRuntimeAsset*, Asset); @@ -19,6 +20,8 @@ class RPMNEXTGEN_API URpmAssetLoaderComponent : public UActorComponent URpmAssetLoaderComponent(); virtual void LoadCharacterFromUrl(FString Url); + + UPROPERTY(BlueprintAssignable, Category = "Ready Player Me" ) FOnGltfAssetLoaded OnGltfAssetLoaded; @@ -27,6 +30,7 @@ class RPMNEXTGEN_API URpmAssetLoaderComponent : public UActorComponent // Called when the game starts virtual void BeginPlay() override; + virtual void HandleGLtfAssetLoaded(UglTFRuntimeAsset* gltfAsset, bool bWasSuccessful); public: // Called every frame virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; From c4a7feb3447d8d66a748f8289a76e04faa89023e Mon Sep 17 00:00:00 2001 From: Harrison Date: Wed, 28 Aug 2024 11:43:41 +0300 Subject: [PATCH 58/74] chore: folder reorg --- .../Samples/BasicLoader/BP_RpmActor.uasset | Bin 0 -> 1459 bytes .../Samples/BasicLoader/BP_RpmActor_C.uasset | Bin 0 -> 1499 bytes .../Blueprints/BP_LoaderDemoUI.uasset | Bin 0 -> 1493 bytes .../Blueprints/BP_LoaderDemoUI_C.uasset | Bin 0 -> 1527 bytes .../Blueprints/BP_RpmPreviewActor.uasset | Bin 52313 -> 52024 bytes .../Default__BP_LoaderDemoUI_C.uasset | Bin 0 -> 1641 bytes .../Blueprints/WBP_LoaderDemoUI.uasset | Bin 109615 -> 109310 bytes .../BasicLoader/Default__BP_RpmActor_C.uasset | Bin 0 -> 1623 bytes .../LoaderSample.umap | Bin 91328 -> 91471 bytes Content/Samples/LoaderDemo/BP_RpmActor.uasset | Bin 2442 -> 0 bytes .../Blueprints/BP_LoaderDemoUI.uasset | Bin 2518 -> 0 bytes 11 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Content/Samples/BasicLoader/BP_RpmActor.uasset create mode 100644 Content/Samples/BasicLoader/BP_RpmActor_C.uasset create mode 100644 Content/Samples/BasicLoader/Blueprints/BP_LoaderDemoUI.uasset create mode 100644 Content/Samples/BasicLoader/Blueprints/BP_LoaderDemoUI_C.uasset rename Content/Samples/{LoaderDemo => BasicLoader}/Blueprints/BP_RpmPreviewActor.uasset (84%) create mode 100644 Content/Samples/BasicLoader/Blueprints/Default__BP_LoaderDemoUI_C.uasset rename Content/Samples/{LoaderDemo => BasicLoader}/Blueprints/WBP_LoaderDemoUI.uasset (81%) create mode 100644 Content/Samples/BasicLoader/Default__BP_RpmActor_C.uasset rename Content/Samples/{LoaderDemo => BasicLoader}/LoaderSample.umap (66%) delete mode 100644 Content/Samples/LoaderDemo/BP_RpmActor.uasset delete mode 100644 Content/Samples/LoaderDemo/Blueprints/BP_LoaderDemoUI.uasset diff --git a/Content/Samples/BasicLoader/BP_RpmActor.uasset b/Content/Samples/BasicLoader/BP_RpmActor.uasset new file mode 100644 index 0000000000000000000000000000000000000000..b37b7dbca2eca927c1a900341befb06081c63cff GIT binary patch literal 1459 zcmbtU&1(}u6rY-EwQ1DWdb3CmQZ;S9KgcCEpCyu}Atg7dj@z+rO*YH!XlqZ3Sn5#} zghDF>LGWS^dJ?o1^luRL;31bn^%SAvM|^L)8`E{gia#=$dGGz^y|-`P%-W5U%bSfx zH{v@$uqHZ9RUsZhz~o#-62J$3Zu-n-KVsTt#9q5wagf6xjVRdm`SL zFW`*_g1$s55={HU9$zpL3CF{!L?{u8BcD8nDDWfm5D7h@JkgF8LZ)HHF$d#9p`F;@ zzf3+_^sl}semF1)T*}VpL^A zL6=9f6Y{Bg;tzJ%q~TcUT?iKsA^u}@+5#w21HT?);u?VztrB(HD@Pv)c`96@Q!HB% zXi1!-T&zerT4t(D1x9-K)rBaK+y%LuW3zmON$zo4mP<@^$Ehj`*$SOxiaTDq%w$EB zIH>byft**^6_L%v1YS|dms=fUgFEq}ZQa^48tyLl;+}Cq5oPX9R1{VmJ3q|?PBt?` z(E}~Elr$y6+0GB&%yfdfnGETT_#IuSX>nYw@s&eO-L^bmC+`LJfGJU;s!I6G?CpIZ zjWSM?lv7f>_T)}CD7LaFut`yYY~cGg*6Z25V9-lR)RBe52Y<;;2TOwwm~liH2k{Ng z1oxT&$Eg7`Kzs!rew-OrJiD0$vcbc07{IQgmXU{5EcAtTD;w%98^GLS5>%|9Cxn~Kc1tHn#J3>H yR4z)|Mb!KK3}~B8J@8+5(e?ZF9MImHqY%Rim=z90Ka3qcm>L`Yu!w8$T>k{Gl{0Pt literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicLoader/BP_RpmActor_C.uasset b/Content/Samples/BasicLoader/BP_RpmActor_C.uasset new file mode 100644 index 0000000000000000000000000000000000000000..5de0f0e4d3a1f4ecfe905cf8a290bdcde6ce316c GIT binary patch literal 1499 zcmbtU%WD&15dUh_*4Fwc76d7!r?#fqJZx-w3C(UERAUlSdyb!*kGh&AK*c~sAvyT51vF2^qPZ(=qX4fro_T6oU^TdqCZQ?t`x& zxa>!5!@l7owh@OgYV-T-POryh9~pJoc)yQFJb4FI;2{~PgqhG*wqt~li||Fg0s47k zIdCjI5L_-S#BV(vYI{39|0MT@=btZJdlf(FxQ+}l4-&vUCX(qHYw3cK**xiUnxibi zsLCu(&Zfr+`MNT=NgO8Wa`#a0OB}p_`X4Z9jX;x%`1e>7$0!KVJCQfN?(6|0LdA1* ziUnnnCZsv4NpdPglT1xhk&!O#!jc<6)@V8zVzXL+rK}S)nNBd(%2QPmgEEaX#mXn9 zSz3`&8n`1d07n#dUScyIQIi$&{c3BN+e#LFsy-(s5bN>mw*xq9LR6%*W))dO zWuit3;}-YcnwT#&CBf=0j$Ii)2<|E!;L8-MvAB?+s!9g$r7hU}630p*j!R~`tKM4X zz{2Ht|7j-#O9`}@*_*q-I>9vRr5YvK`Ny~SgQLVN%Hom&{NSH+>-k_C1j_9tYGA2* z-ygf0;Qr7! zvA=*%t=(BRFsG+H#{F0kJP)XU6G&T^7x5bnzSeRQybQ3%?-+hd3tm>vvKw)HD)g UiHgz(x}}R%N5cyny9(F(Z`8j>QUCw| literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicLoader/Blueprints/BP_LoaderDemoUI.uasset b/Content/Samples/BasicLoader/Blueprints/BP_LoaderDemoUI.uasset new file mode 100644 index 0000000000000000000000000000000000000000..0cc5e936708dd8b8bda12c12643bab60c6d64614 GIT binary patch literal 1493 zcmb7EO=uHQ5S|+U`imB&h*T*as>bH0wz0ulntuanT2exL(AsD6XjhYL$Ue1tkf2nm zpr97SfCa0^9t1rIo{D(Tn-q$8j)y|^AUPDO#+mJI3G0ex*xC2q%(vgn&YSlZZyudn zE0@dT7J#}f0IN8X)S!Nyd{JES&X(#wJLf%bg0{nG%Wng~2cmL@;{ecv+K1>i9FKRp z+?{T3SC_{T3^;>Mhs)dJ3%EOb0uI-SU@%B{SjH&$z#K**CR$(KN*;h4IFdd<{XQN} zE!-=3rVIVg23kHmYA8;YYM-7x{qn$Q)99hzS;~-jC;{TJK%uq2rWt2eYdp|xjqwy` z3h(H)IaEKuxA`M~@#9CTyl!zX)8sA2|45`2f<-Fl(MB<8PC^j15+%#K##USmvBX6- z!6R~lrKD?2NOC61(p<^11czpEb><|3?6GV*%BMt#XY9i)olSAY?q!OUh{!C-b9Qg) z63^zOj6n0yxecSAr{#DU&a7YG-&-BUh>_#YK5u9baSSJNQdZb~a*oFb&tKpP0c+uo zJ}RN^iw}eXNlC~#SQra`*@Ye>+o{`2cekV6mtu+n;>y(hT3iiq!Td}xDCS>1+>M3} z{uob6IgEpRt1G3*PIPD$10^)Gm-w~yCc%?q&p8qN@f)9(Y}53?h?Jb*VzXv2IQITA zZl5YUwDAY-6$LYj8IIC7ijJ$RaA*gm!UAB1(`}VNy|o%O<4?@5p%S3YsmChK_1c1Z z(4)<%Cv~ASLHaK-@)G`+Ar@^a3H>M4nPjnwSb832v51TO>xP)R-LzxOy6+ZNS7=EQ zp&7=MjKI|Bey#{9qaIZ42#lfC&{OSIoLFO@+Kh&F^)@<)QOtz$A|+`<{jV!kMdORg Ixri6~Z^X?)8vpJ;mgjx_hx3lnKy6V&RswB zaHUu*o-GHc+5}L*kx+*6<@|JhQJPqG&9_cU5{gX zdrKhP9trz>t+E^p$Zbbj{cY0GpcMA`T4dQz{*cEg_`nm4L`<~4v6(CYcW@+`MENRe zUwWqJrP+$lGc&G%n@gu_S4Ts`k;hLL8pmJXeoh$@4<$f6<#6A9qO1mIO-=D|pee@F zoawwd;At`J$T-#b2RnXbAQW=DY4Qo^M4kW4TNp9}!WW^$xO3F3okX#B?Ruqp}p&yi$6Y z=QJfNP~D$cQ+Dx;8t+cXpiDe_e{5qUr)(^6t4@mUA&$YMrsRZIRy7_!dF}#F3RvlG z?$};pi}yw$c;?#yDy)qMvw3&5@OBGYjiprZ12wB>ve($had6XsJCwcLl1;1Kkg%wk{aS-y=GWct}($= zYbjm&Fjx{HjU}=Cvu&066(sx>850(W*`5Pt1>wzz*uT zQk~0OP)Bx}bLvnFbVEq~1v@YKui9eKArokybWnta4aBnYaEe7-WdCl9Y3Ut1$hjXU zu)g=qw20E#%aPD>@8Y^pvc}aluS6D!{@P^?8@SQ-PWIRp3Aw@eU+t&uJAm>eA=IP% Q*H72*#ut@%4uAQ-08imjOaK4? literal 0 HcmV?d00001 diff --git a/Content/Samples/LoaderDemo/Blueprints/BP_RpmPreviewActor.uasset b/Content/Samples/BasicLoader/Blueprints/BP_RpmPreviewActor.uasset similarity index 84% rename from Content/Samples/LoaderDemo/Blueprints/BP_RpmPreviewActor.uasset rename to Content/Samples/BasicLoader/Blueprints/BP_RpmPreviewActor.uasset index 33991f242aae64dbfe9e398d47bccf8da5e75637..42c8738d471736233c8700fbca936e7941c88d9f 100644 GIT binary patch delta 935 zcma))Ye*DP6vyveSM!C8I+D3-yXyE@m3C3r2ZoPTSJKd3#ez**Hl2JwTGuoPB0+ki z>j|}tto-1I1hFx+#1}&B8s!Hmkpc^pB>?cKBD6 zt37vYGyc+cYG)SIQpLIFNnzY;((qU8knn6lsEcR<3t=FvDyXl?>pe0txg{h{24c?p~>Z&%tXh(ZcU#HFG76R<7tAXDPFo>&s1Ay@?Fc zTeyoATvK|9wa$V*J#M|t8{NiSLQ?+g9=T-u#-V)PNH4l!&lWHYM1mSET~V zV0Dy+%>V|M-von92Zd6`N7#=LZ+1WE%dRykW%2MWv1(FEL$ZvaY@nA_;gmZ>JO#di z3|=0-DJ|z4nMXf@RGSPX1yv!{) zt6Zb1A<#PjN+bO|{(V5c{BY#@VMtDOhtqn7pV4abK;I{UAJ0m_VrwL90!4oWSUZf+ zHs%M;Fev&9quza6fJH62Q`9hO1#g=fn!1EGXP6(U<-5C^sDpWz@Bf*%KE*&sj}8VW z%6RJok06Cko0*xF8LNwnRi~%NX*BT(Nt*Q7B()|cLmRKlQ1iXx?H-gDs2%=bcEo_n KafAoQEBqT@G)2n* delta 1212 zcmZuwe@q)y9DnyMhb?>%p<~RK!@1Q5 z)xy1euNobsT7;%m*d$cLod2FR$aS@G%;>=@Hm{rIcRoYoS)J~PQEfGrs;YUsw%kxC<0_K}TyV0Af9Y2CWV&lqvxdKtzCp?S^^17161oH7<)gs7`P9dOGj za!TvMAy!3ueY)F@%>3um)V-D*VR+ zYrBu#LImS4%%lf>68z;A@Y(_m;otKxx4|W!h`;@^u%FRb|2J21vjM@S18(S+?pw3M zhVo|^l2*7BY?Pdk_E-LAqKr9zX%+NzJ0R7^8%yEvmh#;7^0}!(2%esf!M^cqSs{yo$UEW%jxjm{4QJrTKN6e8UTVqerxK8@%UaC1CD1$L z=Map}#Ejc;mJ4jWTcRg9lBPP@rfW%Mi1a37T2vcbU2gX1v( zd9|*Rc!I`L2)zKmjz0#s-nGN>WcL0L6ZTU*t0`n>bT~`-u&NHzsKWD)Ca9jE8c{W( zs5)V|P%Ak?XgZ^h!F%U2#`$BT+^P;u9{;}%O`O;zx86Nou0wqq-It`JB>z{F4mF)o z6{_f~PdWbapt0?>@*2|F5o~D=1>0I8t#UBXDsS#++Z>L_opM`8M3w{X#{P>(%DMH> SK3~PH%{`%hK4@t3SMfinD53BG diff --git a/Content/Samples/BasicLoader/Blueprints/Default__BP_LoaderDemoUI_C.uasset b/Content/Samples/BasicLoader/Blueprints/Default__BP_LoaderDemoUI_C.uasset new file mode 100644 index 0000000000000000000000000000000000000000..64cf9ecc8d8ba3f8133bd77c2b9054941b5fc5e4 GIT binary patch literal 1641 zcmb7FO-vI(6n=mrKY|E&K*h)j6lp1;P=p|C7aB}KgfyN^9orFCx7}uUQ1E0zV!)Uf zqh2%`LpbP76K|d%;igv)9tjr`G)j;l{?zxjTimQG$V+D5%=`P^ym>P_J8}H!dODrH zkOxqd3$TowC)i$BoT)FyYg`Q<=QV?dI7$sP_O0s=wNE&vQ>t`~4lw1S*QE<$wq)@~&| zeWz#gD@vEgCzPqL;gJ{f&!syL^H0A2GWVD=q&SoSWt0bxs=IQ^aaLa+>G9VOag{TJ zH~2k`rXJwa^s#^F(aEIN##eV~(un~%C1)cnQkr&8m^3FMT(c6ZdGjUJxEN%yt89da zwHQ;C8%!u#a)2eckzz3pi0fnb4&iExBp3Kp-C?_lGX>m8*@o|PA=>nF z!DJ>FEZ%&5e?JN`i45_$qGLju4$Dic;Q~}xRR$w8d7$jidNaV26EnO5$o!1Jawo#bX$P{xtTns*6k@s&Wl4jm2FHIr^QJX^sN}Qw GllcwiQEIvX literal 0 HcmV?d00001 diff --git a/Content/Samples/LoaderDemo/Blueprints/WBP_LoaderDemoUI.uasset b/Content/Samples/BasicLoader/Blueprints/WBP_LoaderDemoUI.uasset similarity index 81% rename from Content/Samples/LoaderDemo/Blueprints/WBP_LoaderDemoUI.uasset rename to Content/Samples/BasicLoader/Blueprints/WBP_LoaderDemoUI.uasset index 9fe7cdb85e43ac8ca83cd41072292e018af21923..f0f935a39407fefe596d7d6b59e748541957de9b 100644 GIT binary patch delta 1860 zcmZXUdrVVz6vyv5QhWeMV)hO z{4py>{gIg&T$VA#D6Wa9@wp`$GZ%5jL^rd}7^4f>7;|%T57_VCUTBFYxw)V3Ip_O( z-S&6#Zs_oxP|tZdzVAYr{inHtkSz$p_E~~(r(6(#^EWQe{5iKq5Y9*ubMCOS&J)#7 zKk)gS(;ix`JD&}$a<(5n+16V8-Xki3n&hM!LHPTaY7N8)kK+S^aGj@AR%1C0N5_U0 z)@^C1FK@0}zhN`^YEun@^~{LF`x4ephrAaWj4^SB_8EnaY_&oZt$6J*15>EdlmuV1 z$P}1PHk0ISF?kS=53rRhVG-HQR^0aUC?`_vJQ3M1e*J4gBH0o|JUtlDJzT#tG^(w5 z1MJSYsTqta?F=;{qsOi_k1a8^vt)T|Kywg(dr#3R($lIj&o^miiE?% zerNB3L>fsEv1cTp8yARhk3~j99NU-#vDCZ3ig8F+Qme?8DnUJ6POYXMPXBUh!~-E( zY4<`27aBAimXR0!YCB>sr{4ojx;lAA7Nh)_lS8I4#b(ymN5u|*=Tj#M#9Jp$Fz zt9p4%j?p@rGetN{J2M-x!AR-ZZgOOaIK!;sn59Lxe37hm7_%o$uGdqkl$KpTWtXlz z8Jf?)hgYYn)6>xNE7F>0T_Nu}sCWjULuL7nW-s zJ90!^>$5%CkZicu5vdA6$S;L%#kkGF(o<(hG59Tns%cxnR;<+*8M zo`~Nzs5rLO^ZGAkuu1Fi@s6HmjtaiESUlmIDr5`Ot63px~eM delta 2806 zcmbuBe@qld6vuZScN9*L1r}VDtBYOm0FmPqTz;I0!YKl+0@@Oxfx=;FJ*51wVoPeO zwm}O)M!OQDP1_hb6@T1mDb1aJ?_$hf3#2JVi}1$>iu$Ntpx%2__K*WML8JiO& zsPLnthOS>rsl81J@)qs?{pNMNKMg8nud0AEZWjQ4nrU=HXWjC)zCdW-<%QgVo4Hn*@s| zQ&PYuZ9Ei*=upgD9vrNSuFkdK?zt?i3Z;+){*jclg!z%C1d!{PZ~s1@M%(!6JQiwZ zsHi=~g7Z>Xh!iO#!O=KMPFyp74sGN1)Bw{t-|c>~(N>tzSIrTh-7{ac?ends?|gVw zriw=-OPI2`%6I7U*+MpLW4pw{XSq7$CTRIG3Q2J5c}iN;O7{8I{GeF1_oiF$NE!?A zFX>QeItv$8Q%Hi|msP23wmCzDnbkVfM$q*d6%}S$a9}12ey>wV0-=tQT7Ua}N9~t- z+Qx-;7Iru4&`E+y)~jgLE&~=N!q)ZgHJRxkR#&291b4nQ8PhBvYI%MBf&vhinuVB1GHO>P@3k> z6=9WzTn+(8J9xgIk9^7YiO5(OJ*AqN#y~OXLLr zM^B%LgUhBE50f{6U7onI4G6jLZ>t6TO?kvWp5vae@`m$){-h74+zrkuc~cFlmM?-=n5Z3^1Q(%E%i=QaZ^KW-5Q2DB8SGIoW5ck+JvZ^{#{7=JY6Bt z1*Evh6x8(}Yp9k(Qc?3$3OSdGY9|!3PnuZB#ig zD88?1Giou)&JL1syB!@;K3I8MJF@nz+m1p2`EvZ)5C(6sQF%b^myrR5%btxW^uSmL Giuw!h4P#&c diff --git a/Content/Samples/BasicLoader/Default__BP_RpmActor_C.uasset b/Content/Samples/BasicLoader/Default__BP_RpmActor_C.uasset new file mode 100644 index 0000000000000000000000000000000000000000..6253a3c75e193130348a52e22d58bb5a4fbf887b GIT binary patch literal 1623 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 diff --git a/Content/Samples/LoaderDemo/BP_RpmActor.uasset b/Content/Samples/LoaderDemo/BP_RpmActor.uasset deleted file mode 100644 index a8643d7ef78edea1d98b81861b3ff1b6e131749c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2442 zcmb_eOK1~e5dLd?^<}j->cI*{ORTX;n>LLBEzPT?5Yy6!2tCB#NC!4vT?K7)|B>xf9L=InSW;fnVs34 zyL9N@QYw`?Wgw(v10jn*5k)XQpLw*9a8D;oKQ_*~p0!&J!k)31kWD}Xu_yyV$W9oo zAYB8r!P)4tH8;AO?KY3kX>V_GG}!D;r^D^=d0IRzZj>i|U|}Or9v)jV>%E(iL&#B} zIKnU-sqw|`lJg(%ORrxF*7255wdaT;QzQaU$v41}yb@koG8NF6L{?WK`8&qS*~M#D_- zvPjJ8=0{mv;G&Xfb$16rwp(Ch92<9qq?kbVR}4Hf7obEwnLhH(_~~gC9_bAUTwJnx zVgl>$Iz7xn5?Si3Z>>=Ae9<8eGCSFJxw8bev%ZijXH)qe^$vK?1S+wxho_=Q>h8qr z%aE@!Ji8!0+LpT)^bm_BGJ{Fex!h~-?PjAc3(g&}y_<);khDE%eB%0USb5nX9p$B9 zP-Pf|uT;#I?8|4XLkUtZ-oJT^X+F**>ZKAT3l|^X+yNWu==88KCqR@S?2Cz{e=9ID z2_|C4lyUpNbymUM!}l4!6$o?|@dMrz45k)}9997W zi}F7;dzd=}wm0RjCsY-Vc2=^b@Cvb|s8!nzc-ku#$B0KX21Y^uIkGf7pipuu1*=ElA;n6XgTJu9XqhKu diff --git a/Content/Samples/LoaderDemo/Blueprints/BP_LoaderDemoUI.uasset b/Content/Samples/LoaderDemo/Blueprints/BP_LoaderDemoUI.uasset deleted file mode 100644 index d54fc1362f30fd3bc40d10e8644183329891b9ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2518 zcmcH*TSyd9^qQ5aS!P8<8u_JV?kn9~Mag}sq^qqg_M_Xhd(+8%F*81jpfCar3M79L zidcevA}jh3g1|tcD1?lV{tEjL>_PZpR&M9)&TQlCh`Uq=?mhRObMATGduAps9=N#} zi9}AM6Oy%#ka@reX)r!@-JhK^k1S-r(}!yx+cm{NV=@WZ0N4=>P(Xz2fKda|g@6wk zj3$H0Tvk@Av)lA`z0PQ^u-Ht73Y*S&*lxF@Jh==O)&ma1lp*e&8<9f@2N=gFj4w|r z?>)$8ZNtlsG5gVpH+TBR#s`kK*N;@!*ITB#M$tpmgASla>14FvNLn7ON=jU|@)9TY zQHH0bJQEA)lQ{*zXkk3v zOebv&dKe#jmJ!%sz`^(_A7VU|4EY_jU#O!2Z7bss`6#b#3^HEIS*bs$ zHT!yKh+_i+uQj*0|1ew)WX@ACS(TE8Hl>P;srNIOGHt7eV?%=066C0>`Q&Np5y)ag zY0YLC&(%~%jP2j{VNxY|pxL~v5ah_+j)sr9z)@PL9-6*_ClqwIg}gX7{|qZ_@i9D4 zguedaJ+QOVQ%sLfaJv%^e4sOAs{T z?|`vZh=0IRPPh1DC;z-r=a^rNeA&L!qas*}08 wNW$Ov9RFyksEz*CoClL?fHwgLLLrQ#{+XiEFyWh$)`;;<$@loaCg6|mH$#y`xBvhE From 05d2bb88b7dea8e545dfd199f8ce1c934d99f0fc Mon Sep 17 00:00:00 2001 From: Harrison Date: Thu, 29 Aug 2024 13:02:20 +0300 Subject: [PATCH 59/74] chore: small cleanup, reorg and set version number --- .github/CODEOWNERS | 2 +- .../BasicLoader/BasicLoaderSample.umap | Bin 0 -> 2477 bytes .../Blueprints/WBP_LoaderDemoUI.uasset | Bin 109310 -> 2489 bytes .../Blueprints/WBP_LoaderUI.uasset | Bin 0 -> 109246 bytes Content/Samples/BasicLoader/LoaderSample.umap | Bin 91471 -> 2394 bytes .../BasicLoader/RpmBasicLoaderSample.umap | Bin 0 -> 91386 bytes .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 129657 -> 129032 bytes .../Blueprints/WBP_BasicUISample.uasset | Bin 0 -> 2468 bytes ...asicUILevel.umap => RpmBasicUISample.umap} | Bin 71753 -> 71843 bytes README.md | 4 +-- RpmNextGen.uplugin | 2 +- .../Private/RpmNextGenEditor.cpp | 25 +++++++++--------- .../UI/Commands}/LoaderWindowCommands.cpp | 2 +- .../UI/{ => Commands}/LoginWindowCommands.cpp | 2 +- .../UI/{ => Commands}/LoaderWindowCommands.h | 0 .../UI/{ => Commands}/LoginWindowCommands.h | 2 +- .../Public/UI/SRpmDeveloperLoginWidget.h | 3 ++- 17 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 Content/Samples/BasicLoader/BasicLoaderSample.umap create mode 100644 Content/Samples/BasicLoader/Blueprints/WBP_LoaderUI.uasset create mode 100644 Content/Samples/BasicLoader/RpmBasicLoaderSample.umap create mode 100644 Content/Samples/BasicUI/Blueprints/WBP_BasicUISample.uasset rename Content/Samples/BasicUI/{RpmBasicUILevel.umap => RpmBasicUISample.umap} (59%) rename Source/RpmNextGenEditor/{Public/UI => Private/UI/Commands}/LoaderWindowCommands.cpp (84%) rename Source/RpmNextGenEditor/Private/UI/{ => Commands}/LoginWindowCommands.cpp (84%) rename Source/RpmNextGenEditor/Public/UI/{ => Commands}/LoaderWindowCommands.h (100%) rename Source/RpmNextGenEditor/Public/UI/{ => Commands}/LoginWindowCommands.h (93%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aa61f69..56efdb8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ # These owners will be the default owners for everything in -# the repo. Unless a later match takes precedence, +# the repo. Unless a later match takes precedence * @HarrisonHough \ No newline at end of file diff --git a/Content/Samples/BasicLoader/BasicLoaderSample.umap b/Content/Samples/BasicLoader/BasicLoaderSample.umap new file mode 100644 index 0000000000000000000000000000000000000000..f9ca51c7a428cab5733b4d85a96ca1c1c489751e GIT binary patch literal 2477 zcmcImO-NKx6uxR%Y4&58EwYjV^LKP~Y$ie+-rTtv{aO(3*$BWNI6YClfrydQg`Z<;aegZu8e=bU@~?!9Mb_+rt+ z*=RI+GL4XoR6@pKMx?-)IP-LLtnSuy=19e$`^}-UeZX_BBV;wq-mu7EN+nz1TLaqp zFmn#4gLiR!mB(4_vb!ARt_qK{((QIt+3lQ@uLBiAI>Ey#nET)kQueSEw!^F9{LF#)T!;)_m$L^DAVwp|obBN#+qd z1vuEtwU-*Ol1q&}nwpqFV@o7Hj$}-JYu}EtU^k0Mf`|z+i7G6>1*t5P!h4ZYJZNpD z^z>fGlJV>`?G7qFpOwkXx6h|`o0Y6G1lQvGzzsaF!%U$(RVW#~@Z#oXU}$-^uz(;z zzF~=sk4-nN1%ZB2M6Msm+4N_b}Q4r`E@e?i&hPN1ap%KR2`vH3EHCD{mN-rx$Lt7Yn8%#3&g_}6qLqdjG&)92L zH1-;?V;-4i1)VnX#Y==hJ*@I#%md%d!@G#UN9I;3t>x&X+LrL)Cjyv_v5We@7V+?e z5SWt@Tc1&Q;Gv(t`7&h5_`*hqF#EdtpL}I#u3~)MSs`C&8uOlmq;baFjfVIgXp|=d ztb^tAwC*~sa8{P;@mi5D-iCx7rwewL=+(j~tplDYIf0ka)Q-Gd2dX#p&297p;#gAPGyq%)mnsxK(Aq*FRNx;fE-WQT5+SKRC4l fo;?mkK8&RP3aQ%g!PjKaIOD4_ukmFK!ymQZv|2kL literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicLoader/Blueprints/WBP_LoaderDemoUI.uasset b/Content/Samples/BasicLoader/Blueprints/WBP_LoaderDemoUI.uasset index f0f935a39407fefe596d7d6b59e748541957de9b..2e1f21128aff44dfebf3d625c3eb8d3ba4937852 100644 GIT binary patch literal 2489 zcmcImTWkzb7(P|Ex~oexQPHQC)tzqHwh0lPz1h_kl~pgg#_nkid&|zCE{P_TMnnh_ zBt#mTNF+S)Af7yUAUyFPUhyDtPa`PS|IMX)=~OSNFFAA0`Tu{;|DFGw|DSnybotfq z>2!KWA!E}fF!r8$pn%$kU3cER6VLZef92?@f8e!Okj!1g*d*$k5^+u)WAmx4CF?Tk zYaF#s(d%-ra@4Hy){3q=N40llRb9PUUG4OEMKA1GCwZ7i{Y83AG3M(hL&MlE>e0?n z^Ka;VvcIi3aDMlSL-$Wt_+AT-1K+-E3!J=q`@~_-@wZ@zbwB`&3fbk-bp^#Vve|+j zr>&Vsxuoz)r@czkGxo7(*${eqNki+4{wfS?MC=~zG#eD8GVJ!4b44XZVe0;lvx{lw=55m6 zs2U38GY`4goS%-*i>z&ee$t;Rbp{ahMy^V3NtM`}&b#O4l0fIz%p-D&c#`k;@A?|1 zlEMf#Q+(YE=49pr3tU-oonwJ~GVJAZd)GV*1+mz?y6D-=92JGFf8nQ&*?B^;?X9?f z6MkFdNIO>t&W&wseSB%jU^yK)Pf03|=rP;(^jZmSFn~+|e$lO(rVn$TR(8{KEcA(K zhjTPxq_jEJdMi6qmThRhizouWiCH0Cxp|9U_wtWDJ1d1F$pK)+PFr#Odrg zP6R;q)oUZY78ygWUZElkhx~P6|0mBEZWe&On|tWW+BE$9%a^GeiLY63M9B7a;XnDp z1qQNx-54idSQlsWgErky4Kes>^pHV65`dd!$`4muqI3f-UxaW_{&x&zJw#DBM)q|^ zs6cTzHkM2f6Xb`RE2IF?}S0e!E2h*4ER4lks7EPDaUdD|qJC zx-k>Ya0&X$wR(_|=1g%(2zj#t&e^O1Wi%eqZ;`nWlE)mQ OM-pYvkxUQir|mDgdo&~f literal 109310 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 diff --git a/Content/Samples/BasicLoader/Blueprints/WBP_LoaderUI.uasset b/Content/Samples/BasicLoader/Blueprints/WBP_LoaderUI.uasset new file mode 100644 index 0000000000000000000000000000000000000000..ee4eddf3579774fe4ab080e75d8cb8e18bf4060e GIT binary patch literal 109246 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 CSn}}z diff --git a/Content/Samples/BasicLoader/RpmBasicLoaderSample.umap b/Content/Samples/BasicLoader/RpmBasicLoaderSample.umap new file mode 100644 index 0000000000000000000000000000000000000000..c02a1375ed3eb872678f8434de76a6bda7f21865 GIT binary patch literal 91386 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 B%_;x@ literal 0 HcmV?d00001 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset index 2b4ec4f2b1dcc22ab60bffc646d03fa26076d4bb..9c82bc5e94328b4b77be43e3708bf7aac706e69a 100644 GIT binary patch delta 1839 zcmaKsc}!eI9LL}MXv>*0Y?tP77#3Ke_Luwa4imPJ>PQf&?UP+C#MG_}$u zYQ$!%znb-k3DOu6YeFfp+te!JA6i*jOH7PC7g`bwwkc<^rBP>Qw>GgUlgxXc`Tl<2 zx!#+(Gw=J(wC|zQWaRyCjfL@XlBAa;=~kp96<8#RF#fbbl8&(2(JV>rK^LAT?G1)w z9ezVUF2xz&h`-kuzS(6spt*BnJoJk*Xdv}rms#S9zqs{5`G=K}fp3T^ki_D-pr}A| z(~G-Wc1TgS8*2t;gO2(|tYOJ@#WkAZrnctiG_^)=!c^$i)dR;v4^lFMg>edeq{gB+ zEBeM$aE5YJr81)*t0UeKe_M+)@m6%lQ__Re@d~^YE(C5l7{T!&%U6;3`Faz+yB}xd52{QD1(8X}kgSteSOkqu;0#~9%T5ysKu`vSoFl>%Nl5E1sB$?EIkgUMb zST;`sMx_MdV6qH%?>Dt@^>GpDO3{)Z@K}lh`ULDxv0z)O8d?&uDb1?li{h)>JIjiVS(KFGt}F$z^+M1+L7_Yk zqje9aI>)BV%TxJW#$X*Km8j7v@Js=YW?OMmM?op%r~;P8S9fd$H%L-MsmK`2ro@Hk zvlZA=g`K%pbmUM-G;>r1vNUe7do2MCBBM2zl8^DtTm`~+3ZYC-;X|9S1bZzzy%trg z$cW0L>~p8(DG>6y5Ek<&yth|a{Qoyf#D0o1TqeVu z2ltf8}n3xv}49g9VS$S|Nje44Wz~ z=&n#hrw1!5)o_`k4Kl|?F!o6`bd6)kW((RGj(Kp2q5c|EZGim6P6bn@u89? z9|mv9hWa_thkc6{0_HK=#C~}bg_9g@@V7^qWJ<{b532a@!9R(?)0_0}0*;!kIB2GD zo}&%c-WCe?)C+vb({md;Yfbnl4k=Q>BHoCXWJ#xs`4-9iL9J+{&|!5`h=<&v#Y#OT;=y~atE0q+aDcT=lEoX nw z>s-^l)<>2@kuhV)R_Q8>ch#(}eJj4ANLk~(Y&9GDSxoDNnDrdmt++GMgL@M+_*e~L zq&19Ek<{Zt3mu9XDq&qBXJ@c6(SY3=i9S4#z~RkT1wI$sJYG8NNu2G&+#~~*O~kw; z4-O`2;E$7NBx~?x5WA8!b{G4T4Y(dJYt2(N_+yg9@hKX-JXvCWipHvNUy1=`Q*eKZ z2mPrU?43d@6=GYe4SI*rf{rweoyW2?1CA!j`f@wRXuAsbLqMCrhqbH{AyASs}FGz;uoMihPCvv$ApA3?FvQ;E<9dvEIR9_G~;k(}&G7 zIa`SBGYxRgm4c|1I&o!|&nosvY}Vls^h3>`D`fO%aTbLuvJ7Y|l7eFvhvg+WDcffS zvWZCdC=sB2E*COJWpjA4m{5o9HB#vzW$aR!8B0Eq*%(^pBV9V$ayT1_=W=LsS4+X1 z%_00f8Hor*8bXmx8>M6U9C2rx<`}SN3uZWdIDamO)NKN-BSVp+p-5YUbi_G1TZ`#V z1Ezi|g#jmr8=v8g+-J{U6^fXj3mMPma=8B$p$?q~<@?4w+U{nFgM{(l<4%_k>s*{I z!bXYSMXke4&Pp-!HcbhD*SK__s{oY+Ef$-u@WDC4_w^)VPTZB5S z6N48|El}ZX5bX<9Ncam|7J9LXaDzY{E(R6hOS>(L-EXMy;cfYlIVCo@d>d;@bhz_3 z4IYdu)xg>#i*=jmvuf=U#c8%*Vw$=A5?f$C`JHKjqvshy06N&J(VB1$mkPMg zuw4*6IaCh4CE*TqU1BS&qdEmsa|O!ovWT!K3o)JbetFhc6qzUcS*$tO%Pi)uUY7mB la={BL7ISkSbNo;FK_AN-zWh!<8+P6$!`cIt_gKrw{{SMBN3{R| diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUISample.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUISample.uasset new file mode 100644 index 0000000000000000000000000000000000000000..a0e8efac8f38d9c3bcabb1447c63bb55cd67d7a4 GIT binary patch literal 2468 zcmcImO-xfk5MIO|1o5Xx3_GKU#GS*oUsae<7cmR_BelZI(#kKjuf+%y=P?)+~PM@PbnJhMx z)ox>S?q&xuotoc=xm4{zR__<&P?$XIYWM`#L#zQ_l8Ah<@5UZj+2|R*Hz0dF3~G;+ z47x_2V_vk>ab#dLGTQMOlif^Z-p0#3nI3pDvI_)pd@kx2!hjQE&&@70Wr0FTGm#ua z`8)rt6AtbiouGIk1`KiBA8+5DEQ8nwq)r!}ld)7X_Ey(Di5Xj_fn7I0=$sh0L-2P~ zIdNyCrR(+Wz02ir(KO7<)F02;g*W#K@Z}<51t9%^*OVQfCjDyJO9_dMHT;iapZ zvJi}Y7?kdN5~NIz@mNdN{(UId>aXA_7LswUw6--OY(<30oo+oC85||CuQnq{d<)hH u&AS|0akXCf+kZ(oI~P_#x0_AF*BO6b6=mVvU-|(-#O>^J0I`e z_fF6C1hpRy>akG$%~nE!2_eqfwUv08K>8>l*$hhV7#E=}iv{;tX@q$3sxlK|SV#y% zJ;Y6j9#;yk%U|v2xxXtnx3gAn8t#w!A@pwT@~L&-&OaV~;$gu#(__p^c%DA!r$w_( z15f+WAWazA62-CqWE6#WwG7={=aLS?GiFX%=&e~-U1=^|SCj3nU%g^cfzLr&3>WMP z&=6;kb~9BhwIX>MoSC81D0t+RztxF6Lzv>|$T+)5(#GX8F$U|-t6b&XSiPaVe0<{a z95On!Xi#b}xl~Z=)}?Mg6_yo&5#x#E^69?8$pHY&aB`^rx88(!8ly$rlU-8&T<=KO zJ~+tH(S6)QyCA{h zg755FXtda2MXDBtEGua@#LO|$9(nJDgrrzFHs>vR6yBH{BbCgZ%pe7ohSV~Kd4GP* zNZYsDjC2o}Y%XZAsiDC(ed0dlWIEuY%@}+Pd+LA#wpiK+73OJJHp-p?Idilyny97+ z;Z3^>hV1I#Bgi`hJM9MA3peaJ^d@Xe%Y&*kk!7_(Q<_`XsiaktATkr53YRq;DvdUw z8VCj-96t`$@KBVmW%%V}R0Bnyh0F|(u1zUB5n1mm`hzwYcEm$tx*CQuQrYHqh$wPE zd8Wwpd$1t~T*wsZR=A&8#X|PUSbLVplDlLzJX>Tn2V`|Cs@(_S!7sBRJtq=wqq*gf zVp`ep9+`A1M`Y4rS)DacWHWll_|0gx9+Ay^^Q`RGBQmKrS7ePx$4IrgS{TU{n0Vqj z*HVYTZk&`|UplNzI5UP$%M;nc^M0llTJi)Y{3yGe`7v9{m0wJ>1@7dY-yg>N?DhjID_qV`X;DClg0b&ca?9v@MrlC-zn?NYagWn7B-0z}u z4f~K^mcZ2_fqlVM2P74X?EJm4qDjS?=qZ@F1*`FbYFuyQ;u1arr;FFHk4JbQy#~cj zLuiE3)#tJkhUSZ`^S+$8$SLUd`ehmMB_Smbpw8*m?N4}^zV}Z!tdn&743k2P>GYLc1BGUvlM=%

N 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 Date: Mon, 2 Sep 2024 10:51:44 +0300 Subject: [PATCH 72/74] chore: fix footwear default category name and cleanup preview actor --- .../Blueprints/BP_RpmPreviewActor.uasset | Bin 57799 -> 31669 bytes .../Blueprints/WBP_LoaderUI.uasset | Bin 109311 -> 117593 bytes .../BasicLoader/RpmBasicLoaderSample.umap | Bin 119333 -> 133737 bytes .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 129230 -> 129153 bytes .../Blueprints/WBP_RpmCategoryPanel.uasset | Bin 41965 -> 41462 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Content/Samples/BasicLoader/Blueprints/BP_RpmPreviewActor.uasset b/Content/Samples/BasicLoader/Blueprints/BP_RpmPreviewActor.uasset index 9f04b92d45f785aad78be38c67ff8c688b43d233..33daf78d732bee444791672c214b9747a2988c31 100644 GIT binary patch literal 31669 zcmeHQ2Yggjx<8{JMNmSNH9@yJFe(?NhO>4OZ8(mQ}>O?|;tu?!A-TnM?@m`rLQ&ySev%=R4=C z=R4m$bIa4`jJok3t*xz#_Z6b|K0(&3-(f2rlt@_iMfsKXbAACG}`duR)diY7ehW@GQ=TDX&dq+u+j4NL}dy8)j!QRXI z^L>5G&p+znm0LG$n7%6)yqp;Oe(PC9_j%vyf7umL_mDxy5X`e^{hv!_J~8Z$O$V=T z8RUO!7{NYiyzjm3o-4oH|Fz5wg^!ht8$mEJTZrzon-vZjwCg6WrT<^Jgvg-%q{6)7 zi8+~BIoTdhVM$@$45qWbKOEL zqHP6jeR2wht@^g2e8V~YK3o^QW$=>cxBM`==Wk{__Dycfwv71qVVk#X%oB7B{Hb}#CrI^aXk0RTiIzLttexY>xrTEw*b{u0;^ z&}h_%iRLGc{R4bOo=x+M*6ID{!`AYAPBh$A4rf|>NmWsh_n+6ghQhB^8@}$ zmJqexa8r{Jih1I-wLzmI5DFQ7v1HsaWdqw)&J9EZgd=7TJN|gt;-%Dq zIBo93!w*NJGNZv)*D}j6{8izKc%;FoJk1EsG3uk@lDkhiZYZIcx*_7teOC!GKMp#E|dG~{*wu;I+BV@?I@c2bHc8AyN7G{NP zm0$71`%s);`=a<}=k=F>6_%}hS;&Ye`Aau!9e0GSiiIl*L`jSm$!_7rKlE0_ly5VR z(*KfEkA_jG@0hRBs0)YuQSnUG>HGEp(ALX}t3L2Rk3}sZUsIq?>$kl2RHH5y6~5J% zuLq08{y@y|7lrHM7|hc`^5#l0u* zFXb%`#VHc%ST_5=ZzzDga{RcZ{lo{~9(6AqPQoZ^7!e~>XNcQ3yt5JE-dJwbi8FS5 ze;6DACvjpvaLs#nVdg4U=hd}Fqyf&Lhq68Linnh$_aem4`e4`>6a8Cm_&p**3C>Ku zcHaKxF7UwKCpn9XqLCv`fU+43Lso@h5%JlpyQIFj%3}Ps`QHyfUo=k`5z@3@InbJ( zCs!d*n*l@Ix;pm;2?6Ia+-*4fcS|9&Jn>e1*9dRokT=&C2|(M@n@(u$y$gz@N>hm2 zUdf$~**QhTl95V${YCM1`27r{Wmz~vZs;95YYU=FnJ*ekbgZc8bL2iRLScw+7Vo}A zhpd3XXBthUd&$y8M{RxxUZy}5J~CrWtlWO)@rXJKXenvsLyw*a&CU$_<3WR_a9=F2 z)R5lVJoG{z`a!3rY-fMbb2{XOcXFg!JgnyL5Cbr&v6nV=+sJ^5aP(yH+=BD=u>t0Z zNXaJ#d;v|O;?W=02cd46<9xaAbA2%$!g8PqW5zYDu%aGD?7)SuZ{C4n(k}3ewd>X% zd5lwy)08malpXm{UwN$2h{%aYJoWK{oswWY)*LsdN71M0@|i;jv%*(*nyth!#gJ@BjKt;`lb<-hQWdJD~&8N{= zBra##%DPCPnIgjIIc_MvGF%_C16T&4_AMlo(V6n#F@VdOV5l+gR$z zz*TwhISb6G!sOO>-{%H0{#B5RM8#o8Z^%YcL`hP_LExF27Yne<0Y#3TjaRRfNyJ>5 zWx|oHB2hgd*b6ywm(pJG?fE^*!OD20Y%58Yo+!4zent@j4_AA`5u;ieLHuLd*eMW> z>xx5iqFPgM?lgOohG1369CCsH`SjMiKRXXmg_~FzyO^}5Uw8N#S5=4L_{DxPrrR|= zWqJ*{#n`P!&x4bi`K0JSIlC{$kqK!N*GH~z9t)Zd3I4B(uE{|JZ(mj86?s{Id>wSv zXpJpfei^*f94?%KaDM!U!Lh)RgrE}{PFn3V9d-JvSAYK!T*bs94XE(4_5PQdZSBoO zsHU`dR?XbPInxR~WyRv2m8+-1Ba8jYKHb7w7OIp&o~!XiDY1Cu>1+GS2_K2D!d-Zq zYaxOy&xj)O#iQq=vCTqi{)rFCeHJF<`=)mMFrRK^@5dli$Pnr-vR&C zDC%M)dhYzgp=%A~{)$7CkcjoKbRPxQW*RYHkuT;G@13>f3Iq)8YQ~IDTo79aSrIKg zZvf(qJ~x})FyjB zi)Hom>&EX-l7R<_tp%Ps*k1$Rlh#a(Ekn2Ow*^X4p?Uv5gk`zg)K-Nqa0zptw2FRHteEccS%08(L@UoZqV51H1YG-**2vpCWE+(v5i*|%l&BsOx=tAzbqH(fQvTh8+(QU2mNA? zzOhID*rQ*2VV>UAIs^{ZXRz-_8|nwrHh{JWZ53B)bO(ks-*&IN&1I!%)X^4P=N`NKeI|mMb*5DVp?p7G|@7%?2I|GmanO$ zteQr@4M*q)6SF2Nl|N#n)DhJN4gA<;_~lO`^|bOGMN_GLleGK|Aqm$oPa8!931qfS ztQ+2?rB8=n{A;|!rRUg}>5nG*atUxauT0V^kiN}e9F~=^EIC{{%`E*J34T~ntA=Sd zh9k_1H_b7cgMm8yBBd9p1EfFLc!KmlQwv2#RIZHU2XquTtXb)$_VPz&)JQL4d)j}) zL?yFgAOZ!^UNYE&?=%ZWB zrn6?k12j&!^|aj8QSCmuZC82NPxmud)M>8TLs$=cidH%4AVkz$6lRq+El9U^avh){ zWu%#yY!??*G!X=-hY^8&kt@``*hnU3kFvX3ik ztnu7om(QZ-q6Bx(*oVE!;<$)(*(CL8MP=vO{+fr>l8o0_BPlXYA*+lRnYO>Nqal5NF=JwD*NMTK_ zbK&e!Tw6gAG)^NC|1&6t)k_aL&~$1ZW`9^#xM5-2oK?@NhJT%1HlDvL%Iu5^?f zM5Ckn!!;kQLuU>1jD(B^QNhyb5qn4u_Rr}wpL;0cAv*^D z$>5wvbN%jS6RaQ5MSjyjTa-3rU+|fZ>W@rIf9%=y6t|Iix#&iJ9#3ljQ_UmYTF&Ho zq*Y^OjTJJ(jFPY68p)ay(!phlsXdx=q+H5)GAUN%Qzqo07(S8Cyp$JZ(^)p!lHq0OoQ&$=p`W&-8-M>!RIYyX4 z7`Ot*emT`N(VuFA*oN!h4DusH9EClea_?d}Y8t7gnf$nc_DiT`SW-hCfp*9-n`EyI zbZo!IKwro=R?s}=60D!pQt{}hpmw;%jS>yZMlNx5TDsxure@ofiAxOAZTHj&w_o>I z*9X=JPgZ|SS~74pXqWz2-I7Q8Pj!rQ#%9Uh_FA!;Jj*YFqz{gS7G!u@`elFg(0I)v zPtPSk%a_+LxT^8cpO?1DG&0q-jz^B;i85Etl-D|58poMLYeZV~I44$FM4U!ihRlWi zG>$MTk*|!@J@J~8aB-H)SKgU2Cv?e`3V1>4(ZssJ{DaZx5~DSRJv9cZXw27|j;?ka4@)pR;+2))lD?hO^qhzfUS7q2=taw{OGGMluOdIAQ zH7jM(K8McL+=QAU+Po4ci*QTnJd3naM4T~46{M*Qx&}dRin+i;J{uz~Vg8sY?SpI% z>kXXIr>_G@k=hsdTCkGlpb)JP!KaszrRr%mv#4Wbc^qvWj}6W<6W3+hbL`|JE;*xh zOoL+#GJ;(4pi&uYu!539=U&oqHfcPM&Wq@O3BeF;kPWCflS>$Yx#)+2bY$Z3Vp+x| zBUHznvUVHEy4A#Gr5qPnEyP;2y+0lisjbDeY#C?2agqG#$i>&JZEdBpmA31ZF4Ar~ zy|Q(n@qCgQ7APkDPb8ZlZ$l23Pv=<0!k8$cBM#>z!OTK#fVLlndHXDP)oUxuFAwK{8&S(A{$|AE1mT#XKJkCv}EKQxg!~!_&A-e?I+NdPdB12 z6l3TpQ=g$GOCAp;zZxp*a`gE`eV!}N7YIAtY||IEB>;hNbdf+;_xy zsr)z7=1)w+?iGqgBB%Cxmdp;@9Zy2sPNrR~4q_A~a)JA+l!Uo@R*2DU>v1!KWG+>y~7GM*!C*y}3Qr9r1V!pyL~*apV7P+`f%_ zq1CHyaBq9Eo9c1by2A@`2->y#zf;|boz89hM-c<9t)DK#Qwu03sh#=otbU-2Uc5(- zIiN$70{P#1T$FB0RL_n8&X3)iE%9K7y4zemb^s(ikb%O;bP1`R>d2Ge4iIrqIeJ9_ zX%xK13T2wwsb@RN^+~4Q<9!V%UbQnD-=M{B&~RD7NDOS$NO*9{v5eACZon)Wj@ULJ zj3{4qq29K!SXXgB{8Z@@Ta$#t9&i6;P4`2pe%XI&ulsMBzT%fYT{}tdO;ZT5_(ax8 zTW;~d0ZND5LaZdOpIM9!sHDxgLbv{>;QTC;^HViGJC7+I9w|C5StT3=kG7!nQ#;## z41Schob%JJs@C@Zgqrm;)~zTmdvMCe>+US;R?lh$$_S0ezUk=g;M%Vd+erc)Hzlh? zcXo0Zn)?!rpPWKHNV6<>4hF>~+8ri)bVq z=iLaZOkpx-f6fscPNWS5=L$>livl0a-3)#{@usEE!9Reo;;D;APTu)zGlvr*Xdu?Nrm_6*y2{giBjK%Pt6qWO#bKrN&VMT!Xk_lJ=-e^CkG zLl6lE&hNH=CGA-s2|X3fTQw5!(V2_dGz!=NI%-EZl^w5Vz6taKtNcgC zl(201-)3fgZtmo%-q&h!U*&3b&>uB#7nf(2}i6J&GkJ=vIKIx?L5-N&(!PkiLL;POp(e>ryO$_}OFgK3)7w z*+$4zGV@BjnZ;h2@0Ew#csZmU7S$c6V5Uo^nj_n{iXe*J!bw=d*(FAD^NR1Rmf_`_s zenB<8syx;*uJczNw_Mq`X85Sh1y}pW%z5_IznL>T80J9RmyW+a?53@E-*(v42RFs* zDzmd*aDsJVV6iblHr>*xfdzioy>UU_Ny}cGcAxuzA-+KuzU&11F%K;8+vB`p+sEvD ze%jh@XI|2@_SNs4U~n!j<^+iZR=xw@E#Gs~TmE-9@|wi#Wl4%<%*K-f{kp zXGgu8QTo6`OWxdd`Suf>V8`0jXU31=1e71taqBPY^p@XsagT`7r;UQf*ye~)M<1~Y zheit5Jlz47K^=?TOB@jhh%4*~^r11{GhV06)^^R1FMB(GU7RwT4r}IEu1@E-?p?Q( z?CN%W$#stf>$Z$q`Zafiei1KG4y0{Sj~n~{aQ*{@cb@*vha0Bccz_e^$LxZ3x!`)p z-60?3K zmem9tKWbF(UhYwD``16*>aA{L-1J2fcXN>5o`-~uJ|MF!~yj7Z{r;{b@_!Lw%Bf9^q33 zzqiZx#L@TkN~wGKc^djI(P`slHZZP{UhAYv6qvRTxO73HPDmqZ!&L3up%(#RCf58o zIwsA#)z__-(T884=N3ws>B}WicYQcQx~H_-C%*`2*AcGEt#9gdUNg{jdQ=JV|0vV{ z@AUGkMgL@{XJ5=9x2cHN(hDTs$DV#TpRmqS0;8C#724_4o+Y7#W+eS^fs1D~DAJ}30c1oSAdl3tp*I{1$|S-Z=yNF? zKrRHjUbb;(jc#{XeK?B-JH<_eQaR|y$RJ=Wm!A)H`(${ax@EqgQbC9!vdW|vN+@cO%ILGAZW&R+>O+9;M*21u;noJjN_}i}A!g=uPJ&FoSf*$eIr6=}4rH>%VyBhcwB~sGa!}O46m^O)p*6|F-GtX5RjvJA9iSExq(a-Ef-TOR6|_Yk*ziq*CJ6sH9Rt zqyJPW|1u@Cz7ExGaCxi59S=94W04(mp+V>iqk z{^OYU_xxgscJIPD{f}%%ut_sEpE#y0r~CX9=Ra`X)(4vRBG|J1d(T=@;amOo%e`)W z?XRD7Ifh`1Up>u#TS3K#pH3Wm>rwaJ{|I1xUakJ)BNa#9T+%-4@)yon>m5L__jBI3 zt4qZ>hu*(%)9RIDf6hZ&j*tGdX;IN#o_Bg&a#_UPd%q(HRycFT8zti(KK$m@-IuiN z=X>XHf_>I_*ZW%wFaM^~=IoUP>q`b5MX;C7)wDh6JRua+>C{epg}%RXX<8PYhZp1* z56jKY$sJl)SWr@sKVoP}cEQjBk0(1Xe@Jm*A)(0BhfaNZYueFtp505+7SILNq0Jts zX<<4pq+{^M-*~#M=wGtr&)aYLdF6#8=4@R1%h7v1J#PJXc`g4r{)%GYqaHK>E!thX zy7y>Tce*dpC2vNgM+KQP0ay4GPuwi@z<4yygofVxG)gYo5TKKl)#lW zgj^Zc=lb<|1$EI-cu+D(3ti=#*nui82m~gCe0oIN-t(ivy#WY^y)BjDP_rJ6wrFK{ z{nNiEppl3k)tVnZ@^7#eaaZQkwvX*G4Z0ThlR}}W)>;2@4+B!&(yVK*z7QS058>1j zrO>=0l2_Cp3ToTG-1}dh2~14rh%XAmENEp(7HuO&Jj&23r6*D&>JY8 zOVqTJr`+EcO{y)6RK~)3Ni0~Wz41iF(t`ji*BiWbEfaLzR~@3<2EFQJJupeHk7yU& ze$p{85km#k`eriRU=++Yc%youKj`xZ8?-(2`*I*+1G6;d_i3BjA2|5{E0kL`Iignt z11;LJcdwtgpB161B@)$}rs&}aB)K@+;pn(ph1ffHyY!s~6bkx^=SFL4qzMRfu$YIm zo|VbK)okK8l4~2YTrBc?j8V=1;o4o{{r3M zO}p)^yv?F@Em7TQ-G>tfK1QxW^LPURD88mFQry%WZK;{06Mw-eUg}@kko?`RLZ9)F zP8&i*`@4mgKZ&XsFu~iTYZvC7(7CGxSQQFKq5U4+4tXA(M;W)a@qmY}gB2Pr^8~z+ zh?f1!w#f%t!7OZLMG@`%bKdqsUPeLYLXaP`IcqB9Y~)t!!^=tKTLc-2pjao!z+SSQExb^{h_)0 z$@?NZ7Kw(MAVAb6+T&e){+D1I8XKp#Xk8wSO$SZfOR*MzeCl-qL$CM70#R-8_RuJd zTt)J}H0*6|gnepj|MB5mOGZ_l9@K@`dFcFW_kiP2C5#MFYGmcZA3#YeQ_}4E)Hg$+ zN^(4+i)!Efc=bi#fTdkAFQ|v5MoL$18YIa|To$*MRr}`1gDqt^qsSj2iMI&$3oiVn zqikAyVQDIJW*;V-7KnPQ^g2>QMEgtiDZ9fBS!-GFhYt&(@}icYx5;0pOrfH7wq6&F zXx=54u7D*K`-H5>VG0-U&b*6@!BR2lR`(T!>SA!JWx@K8w!Wt8k6=k=$$Bci(MIk0 zg6kHEGUfhya@0-KZ`d>*YUg_zU0e@DbnRb#_qh!%FAk!=`p62%z&0E);jQ)p*x;vu z`+ir^hp@zAs38t<+2}XFhTxKNMvf$|MUC3sxBc=;Cs7&o^2!hmZQ7kDbQ0n!4#sGF zQzO*G*RL53aS33q#ZBY>x63bJj0!9H!jJ$MnJ%*7>rh)@$6iP zNZfcTe`rJ}DGN5oqGVOt`X8pOk$|cw1se@o_rtFL3D(U+7CUfZ-N$Y3_%k#^nFjBx zUd|f}sVn!Yy|w1=>k!nX8G(dowQXM&Z#}|-DEtt2)|!e~l$yx5TA#cA`5;DZu2py? zIi9hi9{bN%_kb00N%KNs>UN%i6V_tLDECI9@jWJ@bv|Ub7obOsf2Q=jGxuEp{>FQo z4gQ8*zIZXLcD&w11|+1oZPJ|fCP+x;{6p8=2Mdr`m0mK>sJ3wHX~)9#N}#FDh4-yH z9>jUr69JkwslNN`Fbs;FVga4PP;b;fR~LP_xzBlCXmva}Q-3*n;>``mzX^IEZZ!3b zul5=%fciXETSWWQpTArKp3r4^pqh1f&DRhWV1lC;SGT(j904#!_ct^~wbfO3ZeyIv zP~-&d$?0eBW&xb2g-bru(KApeqOJR7MF4HY-BiDiJWn9#{|J^R2BvRzf3gcaNl17& zO&_;>I}BPmD(?CPZ?5?SUaTUhv?}@|#fnS6cw!s+a$<&Eetq7XE*$7It z*DqPu6FogC2-42F;H*PnkV!dqM0%d|$w)}7BHE~jMG&Sv`swr^1yAv&+qvn|@equ( zXmq-#x@_r<(4^8^ce1xZFAvorpdoe30XEX?CD+zH+HyAZCT&Uf_x;~mH64+YaGrX& zIs~64{7Olf3_+`2dF<6NEVhV}fVUx{y%{>>9xD_Yk7y6h+e0{i>UIHb$}_hNLc}^r zuhYrs1V4p3`Fw zNXy%=bLmvkHe-$?qS~-lIcP;U^uH?aT-`T?=1vv$^_cXW`t+wo27Fp5)Kt(!;c--} z>2<*~u+gfzM!m_aZC`)hJlJo9!ceVdV0NLWg@j^7e5(!6$iJR+4tihVbE{q^QvV57GYk6MAzsg}(;yiILI! z(I&DwnkU@-(jo|`x-r&N8>HEghy?B_AG!t(xte12YJanK?G-ajwbXwl5(lLCk%X>Jp?5zF{Bvoy4Z9Fp)f zOR}F3!96=D0le*sg`3e92AZz&>+_1bsN%xU{&MP`+3H?IM5TSpJHjbVp|B|w&MDF+ z4+(UD7o8dk2Yl2XOdg~W1L!i2364@igYTBmTzc?H0a1nFSKpVcS+a54` z&YB!VS1$Hx1KKU$OU&>g5^dn7!%jqhF{Y?mj}b$=i1`<vn1>@$ z*REdOVzSx9sLFC{0P7ryjz!IX`@ z?|*HOnCo!>rMd}VNe6guk(?iLi1$~AcuzaP z+d_12bBMRvA>Qo{@$Pnr_kcsZrySzF;1KU+hjXV_QQ1SYw07 z{_h!;htn#DcwE-bpIYnVwzP(KvqQSKIK;cv29L|KZ9;Er)8Mhq0q@&Izr>}YMX(1N}Y3nPKdRpLt1T9&Je3{FmHm8tqbYlvNHP=Fm z)zo-(+tK=mk|WsQF$R2%QY3zEBU-;$^aa~MybL<9jg#8Z7o}rX$a5y&b~3c11^z&5 zgX+}Fugipr#C1vwBtYwLimwxH+;y}-0<=z7e*TD?+lJQ93Ho}Js%3qhtN8j84>TE& zw3Fda3A7%w(7HqEYY+z_J4x%u1X>#`v^J~0yyd&ld?#uBV38rr?k{w(ZCs2CN`(MsnC$rakA)We#^ZD_tt`dYO8{n=oYG<$ zGOe$90-6CmJ6d?6jjI4(Ptd`9d6XUY;7JZ6Iyl?W`YwUilNMSXz#yfm&&zWr-%e>k zP4H00E9sG(s;~Vl-=4k?LuIy-iXrYDrSzP_- z8q0P_Iq%4GCf|0nK1-m*?g+GwRF#j~1+-uautlVP^bjKR<#R~ODqB0g-nZ}tT$lmQ zunnfw?2r}}F_ImvPc5|i5a!EtFs%<&$y0YBE!YV7Vp_HDDxw*j?D+bZg)h*+`U~@Q zimH3edl?W>sCKkq3y>Yo@bib#!F-`lBMWxlL+t_$)DIYF;~Mlq4=}U??Rs6|f+y6$^+6`61NXp1dDM;afPq((N4vo@&fuH* z01e~=4>CXir+Uk1*SH@vjD17)KP#Dr}$C^ESiZE2lB1s^$=aw&mYiu&Ul!J;v!iS17a zq_DF3#L8k?t zc3;$^*MsO%>=gDDMmj+~bA!is6u_k`vLHDWsaX-lqIvyhWwIK!T2AWas4P4$mHvFY?MFcqUUka*VEA;>A83 z97>755INeam$z@6R6wZe6IMGFno6~?)O($rP^#Qr0LpN`u1o|+FY%kmh~b6 z8l!q@30|u;5;Q_3I;&C>>8_dZ0F4oDy%udXcQ;jbXWOu+s;7e56(q_oa`%!$6T))n zrgB`QIF^EWb&!G;5f}ATTa3Q5v?9$<*AY5=0umuuGf5#!tEPM8#Hd_5Rd#2&;qsG- zMl;DD(wswer-JXH=rU)Ti}_+Ha7h=rVVReij=QK*6NL>FDY-OK>mu3$%4;pZeL@TE zWzVgxQ+&I~mSt;qw*6dwEZJlswHca3yIX^E{?5t`bgRgc8t8ysg~*Ozr7X8p`5oJc zf0pbNtxmMS>>nniQxdJc6{MWB9u@s6M)vNbzEDqhwUjG8ic1?KBszz3iUsZ*vRX)( zd*3XrM4Ko0j#+R`-;qAIt+Y@~Er?S4;mPKRUWGnvYN54!vmGp}Wbf#r0883z1zE8v zm&CrzmfeqJgjUpR{iz&F+@!Q){gwM0>edC z8EUt;J&Zyu#X^AxuXT**kLZQ7g=Ko^s*$ck+C*i$E*fGb6}vpvvMDd7k(p;@8byL? zlMz5rmaP_b!^cv<L67n>?vVAVcwCQy=ShchtdB#zsmBCuJv!uh7PW#+9pj*@XOMwVpAA&STJ(&pSs@{=*@Z}`q4 zPgO7Mv{6OaE?Tl`8~fpr02CUi_PInSrcI?QIlnVwkJK1$N1U<0N4$VOXY!Kes60!w zp1cCabr&tuNg43>|J5jk<(oQ6F>}nAq*BB$5t4PSm?iS4>IC;NM;c-I=h1kYO*T4` z#@Ryh5X0!sL!<6cx*JL(Y#x0NrIC0z<&30n_>U6$45$1eg1NNA9nqVtuqM+egqcSc zMP)LM%~JOmOXxSl6#K!#bi%-z1kUp*r-?qY4EhviJXvH%uylz%m?FDkF=J_@oM!3| z4RoGEB}0N5Mk$m-1k)sHZJ=vw1dF;bnk^tN>C%pH#1)Sel~fKhD6AovI&#s*h_r9^ z4AwY~qhxbN+-!+^U0PpNc4z&Y*+w&}Hlt5-reuw}t@%ee;(_A|EBF0W$^YwpJh^u! z&O|1YW%;xK$%8$i3BeeOPkI~S{#r==HHR!ckL+xuh>j667t+T=#|Y|~GLkM7{dky& z#Ii*s?V*00O>~ZSM318>IeK9|4C`X>-I(p^)Tdn9096yO!f7tHX`a_*i-^Oe4Rl14 zWg}PVnyAbmRj#f2KsELGI+BK51594)N|n!XRC*n8A7~&?(ky7WXc3vMU!GeZ=Qy&H zanKwgZ>%oGNVCntXX$aVHY_P7yr7G;EvcNE6m`U}6}aVa%@6Q5gi_ZlS8+w+pY-){lpR0oPj0cDjhJ?ko(B6EL)W3Dxi5(UoIV3D;h|A+Ra{5%~MCJmh9|U!fQcT9fHMS4cuHQ;%Lj7n`OyA z#*sXD^ksWwFJ`suV;$ARE54JdwGpwu7o&2=IV#JuKIy}(k=-aq8oUfabXPJ#5eZa?`nt)+lnPy6fb+qQyc|FUzlbF1Zh>^#- zhdA1iw0I@c%JVESo5$V;&x<5?F0H^(o$NCZ4GyE;1J7PcXRNs9(1Dl`Gd+yBx#U6M z*+&pgzQ_R_>#^uLg^uWD;xn*LVdc#986HQpl6^)lwVXeu&tM47(r3u1)PxTt?vgzT zq8!8%_;9piMz}n7Vcn%k#1$@*l*H!I#D2ENvxP+d?e-Gkt?^-d-O)8J-r2y)n1^=z z(L-vfz3^bQG=gI1-=xxqR%aL!Je7(zv=*Tprm&Lt&oFO*uCdF=c3eg+(dmQ8wuDxN zu)Byk5@K2GJvxyjuYZA`pjZ=#P@e}S885rEGDr2W&$iZsb$R*ZgNvS-aMBv~cGg-< z?=;R)9XsY<${p2h*FJMcXS}02xF5h9OvtDN`gBD`CD8XOGb#aFo#?0pTTY#r(>Brk z0FgB2SXiNi1x|8Q5BIA&?F6#BDD^1$R1N0Ia*m1lMU|?{?8Wxu8$&&0!C3o3e=%2w z;-S@!+Q+^oeoPrl{9+wW_C5AqlO5H_BMM^AVdP7&4v|A2T<3@}B$vMPX%xvJe_KRY zaECdIu^jbH>YY zD(1p?LIu0ub~fX4RDW_5YsRIBSFuBjcoi+@j~T^|6kb-sQ%A8FgYe=0WFMFE{6lIg zi$+rGnw53UEJAg`@3mg1bL}@cs*l(9N{N0tm~IpmErMpJo$|}y2E^N0X;j2=XFa+ z>v)QE0a53+0iG3?6LrKJ+XD9GuzHTsC!ah8*2+ss+qgsYj1?BF#>hPl*a%k9M-m+C zwU~QjK7vnr|D8ON#E%&~lEA)_M-r^?o6uD2A#)wIgy%Vl5}oI$Gl z-iZ@Q@azsNDlzKM5mA%%xtJD5^{@s-xBqTUjru^jqF z@6V?XVq}-FC$yX;WA3`gd!KS@X|vEbOMxNQT|O&+_mI4v zaH^wPSW1Y;Fuz08g*C-t#Cf)m9OQs~Aw171pW?)mBKpb1CFw-{F^ImGNubsZF29mi6o6Z{8_YrhbL8HG`^ZUudLblQZ9NW zj1!Ax--ag+O}<#4vq*CB&%`ZqR4Z!+(w1w!So_6}5uO*oP9Apdpb2ONGdI{Tp1XzL zfp%CkiyhJ9`430P*cnP}`5BIC8ltMZVgcU12rRd}0W{2QYu>Bi9|R zzw~j@Yef>Rfma$Zt~vju6!+7_9Waa$>GGB&(dS+rKVG5j{L73Z45@&Zbid4i)bZSt zT%$0b(#NVMyeY;w^Vw-<*h>}l^rH}4a;iGv(XDM*=BQSV`>j277FlQfu49Slsq)uL zXE~~udkA^~*0D;d56F2UdPNcWFYJ_HMI)CE`OE>LH^g*U(Sn~te{yMOD|+k)Oh1yD zJ#ic_`;GPK4_H+C+;iHf4ZNy}@w=M1#sU9hZH@VxnDkhu&|bn2I!0>KX;kV%62)~N zl64z1^^54Z#ju|M<3^e`?mjA1W9u$3?dl1VTD^L;(@ihAmwuHx!8Wt&dMNXS z-+6(r|2_{Gug_sdZ^^8GViqsupQ$!6A7jn2ZD7@jVXRF=`V33gKe65}@zRfHn9VUB z(_jjSw4{|{{e!XWG|VN^NsIYQFPV5f?5cV(-|zovld$y2?`?2=OacFkrv99A)%*tOB~{(x%+4Qk622c>}2&VhzwoyD>^(mLO}$ zSK6|p&w25hL`zahi}TE}9CSbl^~-X7h?nF(lP@%icB7?I-;fsfBgu>Xi~^eBzQDT2 z-V?*vy0L=HFs5N*{hHnuD}S;jW^csIS;Dxzj3e7>$`n$Pt%V*FWs4kOm^CwxT<=gr z{+ZPb9kfl&Gm_d3o0PSie$;MM#_B2KrL&2)9hLFij-+buTUo+wE%RMf?~abjBaB{} ziRHlGwPrbZ%Ynp6U$dR%a6d6E$8H41Z)coX4px0x*Xg_o_9Pikw%J^5u(uMdzOa?2 zvlMP8_E)4QbSn25qJRGWM`lO@{aB7c*a=QGqF{}_wLIwsG2Tl(*o|eJXW~lP+T+SR z6IXf~dt8|ZT*(cOns(Tn*VZE{;-oI6PU1SInw@m9u#!h3)uG#&P~@dm`AB+ zXCvaSsuvuw&obK#DElKGZ=L$K_>mknnVul^Y=+0f^fF2Io2Z{XooO?GvR^YJF~pwi zxzf*HfXOXNKZ8jZjhXXd)W%x3x10ZlRo_I-%9Zx?v6NG*vD2*Gn!UX@keY8hZvcI< z{U@*6$Lp1?+g`7ziS5(uU)-B_wr@!*j@Q7B&xezvaJE^qXW7kgVB606)&J%=*7;-W zcJX_h<3QW&8%?_a)U+1q#lfreo~`#xU~;e6agS@?2f5z1?tg7pubuakdx%nU>?a^4 z_Dj}rP5LNjQf8~mT*{!%qpB%sv|UE*a&(_gcMQP@!B9k!up8_U18&-o2{kQ9&ROF3 z1yIVwFlA!hdo(T2=}zNsYk4L|tw%oTq%5V8C@(obk#>OFqk+x#^=L3i%W)#=_LEnY9qFP~2F!ttZ20rcwiuOFos*yEk+NKg=x ze0R8f=eq#lkJa!pc%&2LNxTqVT;e_17b##;$v%|9|I`FTB)rKQiID|;yvu)r0GscM zw?)JovHb~%{0APFDY&SSkr;TEq7Q?xP1h@EqdGHrdO3=p>PGo>V9N$Q3>IH6_}F>A!UZ8z<70isXFuD9*B4Id+6V% zqq^JE=nZ@8qI%dJ3c9PJ-f&c7w98eoL0qMf3;s_){in11+jT(IA2|yR^Y!Vf82^Ws zAysG`X_-9H&yQs?5SX{6Dg%EDfW!#L$y@WkM|1^XLtCWoS=_=b7>R-76cYZpz&4?$ zASJc0LBeTuK_-?j-sdh7IHbSz)#<4znX=WHG4ZDgMq(tlf-o1%Hq#ex{%7Q3g-riV zAks{A=EB#gzRldyD;6~Rv7Af{Y*a|cD0tH8*n|RjyFSvz@|5};{}_?tALh?frMMB? znO3H*Ek5Ubbz82^7GW+rp5>F+ax@63)s}Z6B+I{gXWZI9Z~EgIovzkLpZVP0?N3^? z=&>}don^H43eh&xGa4-6A5JEw%7jYc@{#bj9r20fnkeU#PV2au72_^gP+WfRsN1f( zrMz7|ixSn2P<69;aaW8mO;{3IopwODN?~&;gH80DMJjWaI=j`G&7b)YV31?)oak(= z?ZyBGD$pw0j6dlBvEAw53a_MVB$wt^XZf!xnp@sm{sZ+@s*W44{t-kJLvwJj>jWwG zgMj0{9eqoNjRxXBoLF(p4O)RBXkk7bNcb<6*8R1WD7z@b6QN2R0LWytf*yw6?n0hO zXkwYVhNR}IYe)|N6@vsyXNoH+*>-y^(Y_&GMk|<68QmLX=o&n+v|0M73u&P|C4X}T z+;Wz`lqKOq=x3{PwwYW5a1K+5Y{=KEdv1;p5+gD2WQBx3OR-IdD`=xSGgI{E95Usl zLYSvADpDb^%$W&31OEb5D2Z#*#rBZOWKKsXAd7$ZL2$P(9iRe-(U}hZCm*;LSRT0U zMh8ZIxKYGW`1d0mNNmCK4&PPklI6#)D5dLJ^=wZ6W&8Y4a^H%D+p;d&dhiZ2$%1Ez z2z4TuYQx5Ed28uS{q}oo?3%3TBNdPSCt__Z{0vgEOqygyav1$C^_F0&%&w%!=2V$? zQ5OiL%%p13f02?~gWimkoHJWyXdJ3!t9$xAGX5zj>aO>PBT;v)H=?_nLOwm<_WRu4 zpwC?=1IjuMDCvi%GC8(_>D#NXC|UUDv8&#mlV5!NHyf>jMOBISfWg(Uvf*Bk*!|!P zgSZ?(k&GHXA-VP^P#zsKr_J;Mb(%m?QUieNLmks*mOz@lfz)eSyG~VfW@EE$7~Itf zbRc=;DXw8~Up5WPRjAW#iu6!0c`|tUC-K4}U>*`hV3stBK?JPilWeGQr%j;77Sr3N z2vbccA_tiF2-=%4CI~UGNFY>Fz_j*n(}fs?`pMEFV4dcjtL{W0777aOH?#3w;$Xi+ zVTm-+)~P3ez7?m}v^`NR)qhZpwH}rd>M?Nen_&4@WI#n$<9kFvKv_Pb05$ebpd_Lz zVh0E=*zv>ra_osh2OWiPBovRU7gpt3ge;j1O)-GZD8x$R0T3`GoS`%ut4cI=D_v2J z>*`45+0+-%HJdud1(B?c;=gJ}2FwD33CUV^6x1Y3@3oiFQ7Q?D#4drYz$d}U-?a+V zsLn=Btj_~g2Ad^U;W9d9O2^xkbd%EYUQC%8JFqtwUiZj~zxOD)ant@k-f@dBZWv<; zLy|Mbtmv@jf{Q$hk9oBBZb#kujvZ_ab3*6sxF%Z^XZit>OjcDWP%9a6Sn)TaYPKX; z(IZEDuQ_Ghzeg`V4b-+KYSYP_wdJs=U%${_HPFEc?dit!Y))53X4Q^F)n8Uc_9&KRC~h> zdenMpC@-vvvGOoMQ6PEZj5uH`H*qyMVeqZt9j-cQbgAd8BOkilcjf_`!C)e6l7(oT zlIV$7nYgJKNi4|r19cpN&5fvmjSB|&{ORjY{P(QCl;7H`&yia3nj29j9d8Ivk&-<@ zqSq;ri1b^;6sb~LT4%SA74N)n|@?>pOT!zif zJF0M84XKpRMgwLxlukZuW4C36=ec{Y8~0kzQajib3qe7Eh)F`1sGO8dO_^b-GVxMW z&28*Px%a($=q0;teW3i-?d>o6c4@2+3Z&wVD+3?fjy&w+K(CL+uU>yj*L^O!XoMZ? zEG|IjINn@svZ9PN_UyS8Hw1CO( zP1o-p^2)^z9DC`Qt3G+{vhU}Azs?S38y`R`9gFAdX4bEDT@{d)1**!@$#nmuo$9n3aLFgg4k#0qG2X!YTNZ8!c> zcHNB!w2!?$>RCJ3g)QyqCmr;Rdt}Xk(lyUN`t6__tIkF}>H0SE zA(=hzezj@rk#|%qe`w1K4>m7&$_^%^gaVWd9|reTh&2Z>)pM8mI#xx){$PWqT_OK@mKXSaAtuYI=qp8NM*I@Bs=^im`&9Ul6|FC90GSX=S%;u{`6viaNN?O?yX zGegw)*VP}an%L=!(skb+@%qNBUKn3nHEM9P_^oH|Sa;kr#pj>6?en8b{&u?^?Dy}? z(CR0~ytlgho^OqPaIo*FgZiBqw1Z{t%mz};7H3ux3)V&bwB20puMK;{Ee@O*`T$j8 zCabZTQ2VRbRpGcAQaLLg+`#!S(dQm|WAj(#XPoxe2 z&iLmo%l5jjbLsNO&nwz|-9A9 zW;|2qxqZeL4c(`E9+|nYeCEVn?{fpu3ZBqokz~_;FAWTCJ*)iDekVP7Vec(R*}<@m z%gHco09<$z+)eEr2tDPPiwQ0E zYLSxb%-C;S`D1nVNlUksp7oE?s{Jnb{zq;$s%PCI5_abf-~ZcZs>a>%R`k8m(>gA* zgYBT-fH+xdAnP;<`(xyE_5>WXchib>ANM}*j^gus>brOS=Hu_|V85k*fI3&)-r>%6 z-KUSgdDbV(@@Mt0N84ND?N_qwGtd@FY$B;GJEifyK*f(2m5{eqXO7Ql`{a z-ceyB+y88pbE!J>dXRXYy}HpKaR+H9(@kfeKjN(o=<)%ddHz6va_HF~-CgGm5O`3> zqOiBY9jdQ)H~WJTcQoX#jrnQXTd$`0-he;aGAQ%#_lC>6)b#DYX7m-l0h6AX{kb7c zG?>Gsed&7Jf!A%iebs?u?p+{0=e=YH`z<;gbK6?SLVMevefZV^KW;2LtKDfAHJ$a^4|cE?u8__`NaWGBA4WG7 z(MDR+E~tx!!X3qT@`6xOd<%DIsn6_hallq?;%aD(&0ooXEKzOV3IB0Dv2whs=KoXw zaoM!qciw#4f}*w8U->Wh^v=Clnve?n56o?wAIVb_1pJE5w3ID;$ zRLXF4%E&0u-ZgLRdsWL>Ulm>Ldt}2qXXc0PV83Vof%e{f&L>aw|3_BoJ@?Id`{zry z9%ly|ZV{gl68uMB0@7Ms)a$O&>q0?aq+>}S|p5FoZ@+daUoeHCn4%PWrhWd z9_^67&@(PLQxIbg@KcC=j(g_Oe?B{V!a2jUSDkZco#={AxJ=wl1eb|0?i3=tZ%+v7 zAwr{7#s!=mn)oZMcVcxZann|i26o4;TFkY%@3MbehsZETTS}(m~ust=aa{QXFr)aG~F7LWz&Mp(&|&L*8}xqQsMe zCGs})Q|h$}p;4V#K*kTFy()ui?5ggo)j8>u_)F>o6$VCeYjW`88nBwu?ssVO?%OYl zukU})p%shDk9+Fxf9-#Y)d1P(WPA^!xZEC00f&D4FfTqogg1P zHBSEbmdNe@9D8%$4Nska@~WfkU>5?Jk}wTRe2L2pex${+vQkg$%00M z6Cbg)gE52PGv1EF08!%F+x{_N43miuKZ9Z_)gliGeDs~#x7X%@50|dLW%=&)Q?Ko0 z2m7zIT^2VO#S|F|g+TVD1Id`rVRr@@9MU5b1ppg_8%hQ;O50{&oy;}^Zp=2L6J>yA z79B{SHMoC&w|M1=dtRd+bTgC=?##?myUgDFrwv1Xd;C z)BoDYbO@b10WUog7#UpZZPH7^^ffOOK6$WM+8@-YEmxQb;GIzw1t5%Ws;D6t<^p3S zanZ4ZyFVVejL^$>pXH3+4~H)Fr4m9 zZy+~$-TGXe9@4HyM;_qz*Sn+sx|3;j9s9$2P;L)fOdnQJO&CT)oO=YUrc00Dm@<`+ zPWFF>n&e=AL)QUAOd9f_Dp&^$0to|%Clm;X{TX9GnnGn^f^euEEaQVY3b{jMkO6Nq z4NBynv3D#6rvYNH(&u}dnghDqD~2)3o97KkYYmd;_qypvLk$gjSorr2B{aH`bqAvn zIr4?Xuc;#LM(dDG6E0 zn}ZX=3{k%ry;X%f&|OE9rgou%em&*(O z@h>bWDaao&v?RM=Xo1I*otHnPxUkTegrLkps$l9h1t3`CXzO(!RT{nx$#DCh(OUZv z5@x&)$jHR6I1W{rm#Z@)%egU7t2|ZWhF{!50BivX}Uq*f6TKp=4qh5@(LK@Klf_k?f?J) diff --git a/Content/Samples/BasicLoader/Blueprints/WBP_LoaderUI.uasset b/Content/Samples/BasicLoader/Blueprints/WBP_LoaderUI.uasset index 527cff2da4cd91206371da1c56e9d8c9ef867121..a022f5af2f787c7d3344d1194c0b5b9396dd65cb 100644 GIT binary patch literal 117593 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 ku^nAWbmh>6<`&bHO&81r*gAtQ^aHqP<-vo;jkx�OLTQn*aa+ diff --git a/Content/Samples/BasicLoader/RpmBasicLoaderSample.umap b/Content/Samples/BasicLoader/RpmBasicLoaderSample.umap index b076561a8e047effbc23e5d3cd59db0e9bb863cf..bb88ae899015bdc47565d58268221667d8c5980f 100644 GIT binary patch literal 133737 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 BDX9Pe delta 862 zcmZXST}V@57{_}aE!PnCD(B*OQK=o4sAVKX4oQ<-}8U|e;(e$>Dx$| z-bfiS0{hv~J$$hBk?mG#$yMj=S8^Q_4`fM|VmC_~ikajaGV6CQxG;!_aV|m2fruZ&F}p z@Q%vNGM*XX;&-!z8Kbme@ug#8#epOh)Z>n5GQ}(mI%(Y^W8MocM9LX1kMW3hZ>^FO zk>S_e5v*VkrlATM{S$nPr&2!#D&gk5pt;6 z(MYjs0XjH290I&oNye-NI_L1x;Ti$16RT~cph{Dme$@z=vq)*R0qoUY+=ZVO`Hc#k z66Djk(?emWh$Tz(${E16B|eMQrBY9wh@ro&gwNyXa7nO0mt8W>tg6;rC3FpRWT-!d z*_%8Xte0?hld{}CGPoIZQ>I&nH|n#AmfZ~XTiPg1`vpLZ_@|Q|ABVuZSYWAg9wjRlF_uIZSODiMhd-fBTb2&hf9jk4p%kOD^WlAmGyQQ qP{uF7pt5pN9YkB;Y~*18?EZ*;8eZ;IR;M6CG0wn|$l6C3kp2K3IwrpW diff --git a/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset b/Content/Samples/BasicUI/Blueprints/WBP_RpmCategoryPanel.uasset index f6162ada5fe66130d2a66f9b83007e3ac548223a..63c1b4415a7c96bbadbba933c7259792e46db7e1 100644 GIT binary patch delta 9547 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 From bd5265d06632542610c8c6138e0ef25aea8befc9 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 2 Sep 2024 12:36:07 +0300 Subject: [PATCH 73/74] fix: added setting of auth strategy --- Source/RpmNextGen/Private/RpmFunctionLibrary.cpp | 7 ++++++- Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/RpmNextGen/Private/RpmFunctionLibrary.cpp b/Source/RpmNextGen/Private/RpmFunctionLibrary.cpp index 86ec0ba..54e4c42 100644 --- a/Source/RpmNextGen/Private/RpmFunctionLibrary.cpp +++ b/Source/RpmNextGen/Private/RpmFunctionLibrary.cpp @@ -5,13 +5,18 @@ #include "Api/Assets/AssetApi.h" #include "Api/Assets/Models/AssetListRequest.h" #include "Api/Assets/Models/AssetListResponse.h" +#include "Api/Auth/ApiKeyAuthStrategy.h" #include "Settings/RpmDeveloperSettings.h" void URpmFunctionLibrary::FetchFirstAssetId(UObject* WorldContextObject, const FString& AssetType, FOnAssetIdFetched OnAssetIdFetched) { TSharedPtr AssetApi = MakeShared(); - const URpmDeveloperSettings* RpmSettings = GetDefault(); + if(!RpmSettings->ApiKey.IsEmpty() || RpmSettings->ApiProxyUrl.IsEmpty()) + { + AssetApi->SetAuthenticationStrategy(new FApiKeyAuthStrategy()); + } + FAssetListQueryParams QueryParams; QueryParams.Type = AssetType; QueryParams.ApplicationId = RpmSettings->ApplicationId; diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp index a4e5206..d7ab5e3 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp @@ -16,10 +16,9 @@ void URpmAssetPanelWidget::NativeConstruct() const URpmDeveloperSettings* RpmSettings = GetDefault(); AssetApi = MakeShared(); // TODO - add smarter setting of auth strategy - if(RpmSettings->ApiProxyUrl.IsEmpty() && !RpmSettings->ApiKey.IsEmpty()) + if(!RpmSettings->ApiKey.IsEmpty() || RpmSettings->ApiProxyUrl.IsEmpty()) { AssetApi->SetAuthenticationStrategy(new FApiKeyAuthStrategy()); - UE_LOG(LogTemp, Warning, TEXT("Adding ApiKeyAuthStrategy")); } AssetApi->OnListAssetsResponse.BindUObject(this, &URpmAssetPanelWidget::OnAssetListResponse); @@ -33,7 +32,6 @@ void URpmAssetPanelWidget::OnAssetListResponse(const FAssetListResponse& AssetLi if(bWasSuccessful && AssetListResponse.Data.Num() > 0) { CreateButtonsFromAssets(AssetListResponse.Data); - return; } UE_LOG(LogTemp, Error, TEXT("Failed to fetch assets")); From 2a4c106a94055d1a65b9d9855996d421191b7977 Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Tue, 3 Sep 2024 19:28:36 +0300 Subject: [PATCH 74/74] chore: added changelog and license file --- CHANGELOG.md | 19 +++++++++++++++++++ LICENSE.md | 4 ++++ .../Private/Samples/RpmAssetPanelWidget.cpp | 2 -- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 LICENSE.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6fe1934 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [0.1.0] 2024-09-03 + +- Initial release of the Ready Player Me NextGen SDK for Unreal Engine. + +### Added + +- Custom RPM Developer window +- BasicUI sample map +- BasicLoader sample map +- Character base models can be loaded as UAssets from Developer Window + + + + diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3c76baa --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,4 @@ +The MIT License (MIT) +===================== + +This software is provided under the MIT License. diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp index d7ab5e3..a8dc7a4 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp @@ -95,8 +95,6 @@ void URpmAssetPanelWidget::CreateButton(const FAsset& AssetData) } } - - void URpmAssetPanelWidget::OnAssetButtonClicked(const URpmAssetButtonWidget* AssetButton) { UpdateSelectedButton(const_cast(AssetButton));

x{Nae_Rj#dE z@7++jGN;;GUr!c;#vKjmi;a38=YRh%(w4=q((onMM@LooR`Op6iIToqGG7I|HmV_V znJ(;gvYf0Xb)<%@lH!*QPohrH)K$IY=O#%|QKx^7_(|PQ=1^;5vej&vYqlgy)vcRy qq3JfBiPU|on@Wy55ZSyP}Q?vG`| zZ3%=k=dvZ^YapUP>jRxLQ#ZE}r-@-%)MPQ@GCwvmt7aovFzL>{eLBK=n)~`Y=l473 zp5FJ~X`eUrq9@d|k}f>hO-L9aq-uAQ3%|n2I9REv%x@;70sU*4g!mkUFvP#CC*(e^ zc^e4%{=(aXhM&H)wT(EAACl^Kok^;TdE&cXG3bw0Cg}BEiu#qf#$kfmmd9&8^q@nw zVP?r9&i!Y8G~UE!40FuQ3>CFY6DnM9xZHY2^RB{%mL1#HmkFPY9(X@dX=BMLGz(S9 zF!7=;oP1ihqIVfjiU^1rEaBfONvWkQB@uTjcD>s0x_e)J{hV6$MdYi2rV-^}x;a}i;hBv zKJ96smj{$7{b(rdfo5YG?c-OtnUxNwaLrqpPKQL~0HR@mt{^%gP;z!PJr2#;X>?Rv zy>LFei;gK^F27Tu#B@mbvHb1)hbX)i3x<@>D5wa@=TWxdGm=Pf2g_p6~n z3=MReQ^HOmIMQiOV}n9~{S=$C*s*{!3?G^8(H=Et45cIR(40uU5S3R%4?=ZbBfHxT z)Z$poPxYuO)R0iJ6n$<|r)BNpbOgc3Rh-P2Lq_p5w5^>Mr!H7lQxDYSSyTVSc7DYb zP!rOv?*1GG+EjeF2PO+Mz`j}ok@+T;+y^nmR`?)aW?%P#H-9I4zMt2EZ3QwL?ib`0 zgja_I%q^5z>~R5)AY3~F-;`J(wn%16Mg=^8aI;4U?ia~)BS^)K?2=b-7m8(O9TU({ zBC~hKgzU2tnP$WJl1BF9Y2@nQJ*&jtJu754tuj0Loq)DAGW+ME3bk-)jl@n~7H)f~ z7II1@mOQDtGHbdf;7qB^Y&XO%ua!xwW_JV7XVb!Ao5YNN2*!s@egJN&kXiEs(aNTZsDvck zkKYpBh6dMLxVVi!5Q$lpGRyJ_*i|X%`erF()XpsdI=I9K{wXOG!r^%oX_r1Bno2Z1yo&6 zP?XVmS_1}$iJb^JR6tXq-C@*?&8qnH4Oz8F(Dv0!l*F~BkWIlSm=7m;I97M=-s|2@ zf=#~}RP5>wG{ApqkUHSq&>kBk7uGp@gT^Cj3#b^>VPa6jWoHKT*Y407vHqOt`wEQy zmK;YGAhX8RRegisterNomadTabSpawner(TestWindowTabName, FOnSpawnTab::CreateRaw(this, &FRpmNextGenEditorModule::OnSpawnPluginTab)) - .SetDisplayName(LOCTEXT("DeveloperLoginWidget", "RPM Dev Login")) + FGlobalTabmanager::Get()->RegisterNomadTabSpawner(DeveloperWindowName, FOnSpawnTab::CreateRaw(this, &FRpmNextGenEditorModule::OnSpawnPluginTab)) + .SetDisplayName(LOCTEXT("DeveloperLoginWidget", "Ready Player Me")) .SetMenuType(ETabSpawnerMenuType::Hidden); // Don't show Loader window in the menu @@ -100,10 +99,12 @@ void FRpmNextGenEditorModule::ShutdownModule() FLoginWindowStyle::Shutdown(); FLoginWindowCommands::Unregister(); - FLoaderWindowCommands::Unregister(); // Unregister custom commands + // Don't show Loader window in the menu + //FLoaderWindowCommands::Unregister(); - FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(TestWindowTabName); - FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(NewWindowTabName); + FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(DeveloperWindowName); + // Don't show Loader window in the menu + //FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(LoaderWinderName); } TSharedRef FRpmNextGenEditorModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) @@ -133,13 +134,13 @@ TSharedRef FRpmNextGenEditorModule::OnSpawnLoaderWindow(const FSpawnTa void FRpmNextGenEditorModule::PluginButtonClicked() { - FGlobalTabmanager::Get()->TryInvokeTab(TestWindowTabName); + FGlobalTabmanager::Get()->TryInvokeTab(DeveloperWindowName); } void FRpmNextGenEditorModule::OpenLoaderWindow() { - FGlobalTabmanager::Get()->TryInvokeTab(NewWindowTabName); + FGlobalTabmanager::Get()->TryInvokeTab(LoaderWinderName); } #undef LOCTEXT_NAMESPACE diff --git a/Source/RpmNextGenEditor/Public/UI/LoaderWindowCommands.cpp b/Source/RpmNextGenEditor/Private/UI/Commands/LoaderWindowCommands.cpp similarity index 84% rename from Source/RpmNextGenEditor/Public/UI/LoaderWindowCommands.cpp rename to Source/RpmNextGenEditor/Private/UI/Commands/LoaderWindowCommands.cpp index 657ce9d..e37aa1c 100644 --- a/Source/RpmNextGenEditor/Public/UI/LoaderWindowCommands.cpp +++ b/Source/RpmNextGenEditor/Private/UI/Commands/LoaderWindowCommands.cpp @@ -1,4 +1,4 @@ -#include "LoaderWindowCommands.h" +#include "UI/Commands/LoaderWindowCommands.h" #define LOCTEXT_NAMESPACE "FRpmNextGenEditorModule" diff --git a/Source/RpmNextGenEditor/Private/UI/LoginWindowCommands.cpp b/Source/RpmNextGenEditor/Private/UI/Commands/LoginWindowCommands.cpp similarity index 84% rename from Source/RpmNextGenEditor/Private/UI/LoginWindowCommands.cpp rename to Source/RpmNextGenEditor/Private/UI/Commands/LoginWindowCommands.cpp index b182c6e..48d9937 100644 --- a/Source/RpmNextGenEditor/Private/UI/LoginWindowCommands.cpp +++ b/Source/RpmNextGenEditor/Private/UI/Commands/LoginWindowCommands.cpp @@ -1,4 +1,4 @@ -#include "UI/LoginWindowCommands.h" +#include "UI/Commands/LoginWindowCommands.h" #define LOCTEXT_NAMESPACE "FRpmNextGenEditorModule" diff --git a/Source/RpmNextGenEditor/Public/UI/LoaderWindowCommands.h b/Source/RpmNextGenEditor/Public/UI/Commands/LoaderWindowCommands.h similarity index 100% rename from Source/RpmNextGenEditor/Public/UI/LoaderWindowCommands.h rename to Source/RpmNextGenEditor/Public/UI/Commands/LoaderWindowCommands.h diff --git a/Source/RpmNextGenEditor/Public/UI/LoginWindowCommands.h b/Source/RpmNextGenEditor/Public/UI/Commands/LoginWindowCommands.h similarity index 93% rename from Source/RpmNextGenEditor/Public/UI/LoginWindowCommands.h rename to Source/RpmNextGenEditor/Public/UI/Commands/LoginWindowCommands.h index cb92740..86dc7de 100644 --- a/Source/RpmNextGenEditor/Public/UI/LoginWindowCommands.h +++ b/Source/RpmNextGenEditor/Public/UI/Commands/LoginWindowCommands.h @@ -2,7 +2,7 @@ #include "CoreMinimal.h" #include "Framework/Commands/Commands.h" -#include "LoginWindowStyle.h" +#include "UI/LoginWindowStyle.h" class RPMNEXTGENEDITOR_API FLoginWindowCommands: public TCommands { diff --git a/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h index 1b84abd..4ea8aad 100644 --- a/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h @@ -10,6 +10,7 @@ #include "Widgets/SCompoundWidget.h" #include "Containers/Map.h" +struct FApplication; struct FDeveloperLoginResponse; class FDeveloperAuthApi; class URpmDeveloperSettings; @@ -54,7 +55,7 @@ class RPMNEXTGENEDITOR_API SRpmDeveloperLoginWidget : public SCompoundWidget FString UserName; TArray UserApplications; FText GetWelcomeText() const; - FString DemoUserName = TEXT("Guest user"); + const FString DemoUserName = TEXT("Guest user"); FText GetSelectedComboBoxItemText() const; FReply OnLoginClicked(); From af96134279f78b1961854021d2025dc0f3815cc1 Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Thu, 29 Aug 2024 19:13:35 +0300 Subject: [PATCH 60/74] chore: add missing include --- .../RpmNextGenEditor/Public/UI/Commands/LoaderWindowCommands.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/RpmNextGenEditor/Public/UI/Commands/LoaderWindowCommands.h b/Source/RpmNextGenEditor/Public/UI/Commands/LoaderWindowCommands.h index 1a69094..df9a697 100644 --- a/Source/RpmNextGenEditor/Public/UI/Commands/LoaderWindowCommands.h +++ b/Source/RpmNextGenEditor/Public/UI/Commands/LoaderWindowCommands.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "EditorStyleSet.h" #include "Framework/Commands/Commands.h" class FLoaderWindowCommands : public TCommands From 9ec8bce8a766d9ea34f311aa852ebc0b2c944d2c Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Thu, 29 Aug 2024 19:15:27 +0300 Subject: [PATCH 61/74] chore: fix ambiguous comparison for 5.3 --- Source/RpmNextGen/Private/RpmActor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index e86f280..83ea9dc 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -173,7 +173,7 @@ USceneComponent* ARpmActor::CreateNewComponent(USceneComponent* NodeParentCompon SkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); // Attach and register the component - SkeletalMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent); + SkeletalMeshComponent->SetupAttachment(NodeParentComponent == nullptr ? NodeParentComponent : RootComponent); SkeletalMeshComponent->RegisterComponent(); SkeletalMeshComponent->SetRelativeTransform(Node.Transform); From 950abed60bdbcd4cf5c9013c31d16909a815784c Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Thu, 29 Aug 2024 19:27:16 +0300 Subject: [PATCH 62/74] chore: small fix for attachment setup logic --- Source/RpmNextGen/Private/RpmActor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/RpmNextGen/Private/RpmActor.cpp b/Source/RpmNextGen/Private/RpmActor.cpp index 83ea9dc..5a0aef0 100644 --- a/Source/RpmNextGen/Private/RpmActor.cpp +++ b/Source/RpmNextGen/Private/RpmActor.cpp @@ -173,7 +173,7 @@ USceneComponent* ARpmActor::CreateNewComponent(USceneComponent* NodeParentCompon SkeletalMeshComponent->SetSkeletalMesh(SkeletalMesh); // Attach and register the component - SkeletalMeshComponent->SetupAttachment(NodeParentComponent == nullptr ? NodeParentComponent : RootComponent); + SkeletalMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent.Get()); SkeletalMeshComponent->RegisterComponent(); SkeletalMeshComponent->SetRelativeTransform(Node.Transform); @@ -210,7 +210,7 @@ USceneComponent* ARpmActor::CreateNewComponent(USceneComponent* NodeParentCompon StaticMeshComponent->SetStaticMesh(StaticMesh); // Attach and register the component - StaticMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent); + StaticMeshComponent->SetupAttachment(NodeParentComponent ? NodeParentComponent : RootComponent.Get()); StaticMeshComponent->RegisterComponent(); StaticMeshComponent->SetRelativeTransform(Node.Transform); From 06bae76b9fb57d2812bfe8b73cbf38106118ddc6 Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Thu, 29 Aug 2024 19:34:41 +0300 Subject: [PATCH 63/74] fix: remove unrealEd from runtime module --- Source/RpmNextGen/RpmNextGen.Build.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/RpmNextGen/RpmNextGen.Build.cs b/Source/RpmNextGen/RpmNextGen.Build.cs index 07e7a1c..9d09297 100644 --- a/Source/RpmNextGen/RpmNextGen.Build.cs +++ b/Source/RpmNextGen/RpmNextGen.Build.cs @@ -29,7 +29,6 @@ public RpmNextGen(ReadOnlyTargetRules Target) : base(Target) "glTFRuntime", "DeveloperSettings", "glTFRuntime", - "UnrealEd", "Slate", "SlateCore", } From 93ffec98742daae4ea8d698ca5417d6d8cc9159e Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Thu, 29 Aug 2024 19:37:33 +0300 Subject: [PATCH 64/74] fix: fix fore simage include and duplicate gltfRuntime --- Source/RpmNextGen/Private/RpmImageLoader.cpp | 1 + Source/RpmNextGen/RpmNextGen.Build.cs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/RpmNextGen/Private/RpmImageLoader.cpp b/Source/RpmNextGen/Private/RpmImageLoader.cpp index 6c551e7..9a32b8c 100644 --- a/Source/RpmNextGen/Private/RpmImageLoader.cpp +++ b/Source/RpmNextGen/Private/RpmImageLoader.cpp @@ -8,6 +8,7 @@ #include "Modules/ModuleManager.h" #include "Engine/Texture2D.h" #include "Components/Image.h" +#include "Widgets/Images/SImage.h" void FRpmImageLoader::LoadUImageFromURL(UImage* Image, const FString& URL) { diff --git a/Source/RpmNextGen/RpmNextGen.Build.cs b/Source/RpmNextGen/RpmNextGen.Build.cs index 9d09297..4ad0e8a 100644 --- a/Source/RpmNextGen/RpmNextGen.Build.cs +++ b/Source/RpmNextGen/RpmNextGen.Build.cs @@ -28,7 +28,6 @@ public RpmNextGen(ReadOnlyTargetRules Target) : base(Target) "Core", "glTFRuntime", "DeveloperSettings", - "glTFRuntime", "Slate", "SlateCore", } From 707590f6a3c86bf7dc01c3f726633e0db3dfd286 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 30 Aug 2024 11:10:30 +0300 Subject: [PATCH 65/74] fix: minor fixes for 5.4 --- Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h | 1 + Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h index afcd456..5d9c3d2 100644 --- a/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h +++ b/Source/RpmNextGen/Public/Samples/RpmAssetCardWidget.h @@ -3,6 +3,7 @@ #pragma once #include "CoreMinimal.h" +#include "Api/Assets/Models/Asset.h" #include "Blueprint/UserWidget.h" #include "RpmAssetCardWidget.generated.h" diff --git a/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h b/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h index 4ea8aad..c61bfa9 100644 --- a/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h +++ b/Source/RpmNextGenEditor/Public/UI/SRpmDeveloperLoginWidget.h @@ -6,13 +6,14 @@ #include "EditorAssetLoader.h" #include "Api/Assets/AssetApi.h" #include "Api/Assets/Models/AssetListResponse.h" +#include "Auth/DeveloperAuthApi.h" #include "DeveloperAccounts/DeveloperAccountApi.h" +#include "DeveloperAccounts/Models/ApplicationListResponse.h" #include "Widgets/SCompoundWidget.h" #include "Containers/Map.h" -struct FApplication; + struct FDeveloperLoginResponse; -class FDeveloperAuthApi; class URpmDeveloperSettings; class UDeveloperAuthApi; class SEditableTextBox; From ebc38e38edc3b835b1479dabaf958bf774a7bd02 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 30 Aug 2024 12:58:03 +0300 Subject: [PATCH 66/74] chore: update use of settings --- Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp | 7 +++---- .../Private/Api/Auth/ApiKeyAuthStrategy.cpp | 6 +++--- Source/RpmNextGen/Private/Api/Auth/AuthApi.cpp | 4 ++-- .../Private/Api/Characters/CharacterApi.cpp | 14 +++++++------- .../Private/RpmPreviewLoaderComponent.cpp | 2 +- .../Private/Samples/RpmAssetPanelWidget.cpp | 8 ++++---- .../Private/Settings/RpmDeveloperSettings.cpp | 11 +++++------ .../Public/Settings/RpmDeveloperSettings.h | 5 +++-- .../Private/Auth/DeveloperAuthApi.cpp | 4 ++-- .../Private/Auth/DeveloperTokenAuthStrategy.cpp | 1 - .../DeveloperAccounts/DeveloperAccountApi.cpp | 8 ++++---- .../Private/UI/SRpmDeveloperLoginWidget.cpp | 7 ++++--- 12 files changed, 38 insertions(+), 39 deletions(-) diff --git a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp index b204b39..d22540b 100644 --- a/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp +++ b/Source/RpmNextGen/Private/Api/Assets/AssetApi.cpp @@ -11,10 +11,9 @@ FAssetApi::FAssetApi() void FAssetApi::ListAssetsAsync(const FAssetListRequest& Request) { - URpmDeveloperSettings* Settings = GetMutableDefault(); - ApiBaseUrl = Settings->GetApiBaseUrl(); - - if(Settings->ApplicationId.IsEmpty()) + const URpmDeveloperSettings* RpmSettings = GetDefault(); + ApiBaseUrl = RpmSettings->GetApiBaseUrl(); + if(RpmSettings->ApplicationId.IsEmpty()) { UE_LOG(LogTemp, Error, TEXT("Application ID is empty")); OnListAssetsResponse.ExecuteIfBound(FAssetListResponse(), false); diff --git a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp index af50abf..897fbd2 100644 --- a/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp +++ b/Source/RpmNextGen/Private/Api/Auth/ApiKeyAuthStrategy.cpp @@ -9,14 +9,14 @@ FApiKeyAuthStrategy::FApiKeyAuthStrategy() void FApiKeyAuthStrategy::AddAuthToRequest(TSharedPtr Request) { - URpmDeveloperSettings *Settings = GetMutableDefault(); - if(Settings->ApiKey.IsEmpty()) + const URpmDeveloperSettings* RpmSettings = GetDefault(); + if(RpmSettings->ApiKey.IsEmpty()) { UE_LOG(LogTemp, Error, TEXT("API Key is empty")); OnAuthComplete.ExecuteIfBound(false); return; } - Request->Headers.Add(TEXT("X-API-KEY"), Settings->ApiKey); + Request->Headers.Add(TEXT("X-API-KEY"), RpmSettings->ApiKey); OnAuthComplete.ExecuteIfBound(true); } diff --git a/Source/RpmNextGen/Private/Api/Auth/AuthApi.cpp b/Source/RpmNextGen/Private/Api/Auth/AuthApi.cpp index e038845..fa43fa2 100644 --- a/Source/RpmNextGen/Private/Api/Auth/AuthApi.cpp +++ b/Source/RpmNextGen/Private/Api/Auth/AuthApi.cpp @@ -6,8 +6,8 @@ FAuthApi::FAuthApi() { - URpmDeveloperSettings* Settings = GetMutableDefault(); - ApiUrl = FString::Printf(TEXT("%s/refresh"), *Settings->ApiBaseAuthUrl); + const URpmDeveloperSettings* RpmSettings = GetDefault(); + ApiUrl = FString::Printf(TEXT("%s/refresh"), *RpmSettings->ApiBaseAuthUrl); } void FAuthApi::RefreshToken(const FRefreshTokenRequest& Request) diff --git a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp index 114a011..8245e5d 100644 --- a/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp +++ b/Source/RpmNextGen/Private/Api/Characters/CharacterApi.cpp @@ -9,11 +9,11 @@ FCharacterApi::FCharacterApi() { - URpmDeveloperSettings* Settings = GetMutableDefault(); - BaseUrl = FString::Printf(TEXT("%s/v1/characters"), *Settings->GetApiBaseUrl()); + const URpmDeveloperSettings* RpmSettings = GetDefault(); + BaseUrl = FString::Printf(TEXT("%s/v1/characters"), *RpmSettings->GetApiBaseUrl()); Http = &FHttpModule::Get(); SetAuthenticationStrategy(nullptr); - if(!Settings->ApiKey.IsEmpty() && Settings->ApiProxyUrl.IsEmpty()) + if(!RpmSettings->ApiKey.IsEmpty() && RpmSettings->ApiProxyUrl.IsEmpty()) { SetAuthenticationStrategy(new FApiKeyAuthStrategy()); } @@ -65,10 +65,10 @@ void FCharacterApi::OnProcessResponse(FHttpRequestPtr Request, FHttpResponsePtr bool bSuccess = bWasSuccessful && Response.IsValid() && EHttpResponseCodes::IsOk(Response->GetResponseCode()); if (Response->GetResponseCode() == 401) { - UE_LOG(LogTemp, Error, - TEXT( - "The request to the character API failed with a 401 response code. Please ensure that your API Key or proxy is correctly configured." - )); + URpmDeveloperSettings* Settings = GetMutableDefault(); + + UE_LOG(LogTemp, Error,TEXT("The request to the character API failed with a 401 response code. Please ensure that your API Key or proxy is correctly configured.")); + UE_LOG(LogTemp, Error,TEXT("API Key: %s. Proxy url = %s"), *Settings->ApiKey, *Settings->ApiProxyUrl); return; } diff --git a/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp index 47c5159..b26ec19 100644 --- a/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp +++ b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp @@ -17,7 +17,7 @@ class URpmDeveloperSettings; URpmPreviewLoaderComponent::URpmPreviewLoaderComponent() { PrimaryComponentTick.bCanEverTick = false; - URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + const URpmDeveloperSettings* RpmSettings = GetDefault(); AppId = RpmSettings->ApplicationId; CharacterApi = MakeShared(); PreviewAssetMap = TMap(); diff --git a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp index 05015bd..a4e5206 100644 --- a/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp +++ b/Source/RpmNextGen/Private/Samples/RpmAssetPanelWidget.cpp @@ -13,10 +13,10 @@ void URpmAssetPanelWidget::NativeConstruct() { Super::NativeConstruct(); - URpmDeveloperSettings *Settings = GetMutableDefault(); + const URpmDeveloperSettings* RpmSettings = GetDefault(); AssetApi = MakeShared(); // TODO - add smarter setting of auth strategy - if(Settings->ApiProxyUrl.IsEmpty() && !Settings->ApiKey.IsEmpty()) + if(RpmSettings->ApiProxyUrl.IsEmpty() && !RpmSettings->ApiKey.IsEmpty()) { AssetApi->SetAuthenticationStrategy(new FApiKeyAuthStrategy()); UE_LOG(LogTemp, Warning, TEXT("Adding ApiKeyAuthStrategy")); @@ -112,10 +112,10 @@ void URpmAssetPanelWidget::LoadAssetsOfType(const FString& AssetType) UE_LOG(LogTemp, Error, TEXT("AssetApi is null or invalid")); return; } - URpmDeveloperSettings *Settings = GetMutableDefault(); + const URpmDeveloperSettings* RpmSettings = GetDefault(); FAssetListQueryParams QueryParams; QueryParams.Type = AssetType; - QueryParams.ApplicationId = Settings->ApplicationId; + QueryParams.ApplicationId = RpmSettings->ApplicationId; FAssetListRequest AssetListRequest = FAssetListRequest(QueryParams); AssetApi->ListAssetsAsync(AssetListRequest); } diff --git a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp index 67b5ae0..1616a32 100644 --- a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp +++ b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp @@ -4,18 +4,16 @@ URpmDeveloperSettings::URpmDeveloperSettings() { + LoadConfig(); ApiBaseUrl = TEXT("https://api.readyplayer.me"); ApiBaseAuthUrl = TEXT("https://readyplayer.me/api/auth"); - ApplicationId = TEXT(""); - ApiKey = TEXT(""); - ApiProxyUrl = TEXT(""); } void URpmDeveloperSettings::SetupDemoAccount() { ApplicationId = DemoAppId; ApiProxyUrl = DemoProxyUrl; - this->SaveConfig(); + SaveConfig(); } void URpmDeveloperSettings::Reset() @@ -28,10 +26,11 @@ void URpmDeveloperSettings::Reset() { ApiProxyUrl = TEXT(""); } - this->SaveConfig(); + + SaveConfig(); } -FString URpmDeveloperSettings::GetApiBaseUrl() +FString URpmDeveloperSettings::GetApiBaseUrl() const { return ApiProxyUrl.IsEmpty() ? ApiBaseUrl : ApiProxyUrl; } diff --git a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h index aebe2a2..99fa6fc 100644 --- a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h +++ b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h @@ -18,6 +18,7 @@ class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings FString ApiBaseUrl; public: + URpmDeveloperSettings(); UPROPERTY(VisibleAnywhere, Config, Category = "Auth Settings", meta = (ReadOnly = "true", ToolTip = "Base URL for authentication requests.")) @@ -34,14 +35,14 @@ class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings void SetupDemoAccount(); void Reset(); - FString GetApiBaseUrl(); + FString GetApiBaseUrl() const; bool IsValid() const { return !ApplicationId.IsEmpty() && (!ApiKey.IsEmpty() || !ApiProxyUrl.IsEmpty()); } -private: +private: const FString DemoAppId = TEXT("665e05a50c62c921e5a6ab84"); const FString DemoProxyUrl = TEXT("https://api.readyplayer.me/demo"); }; diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp index c3fa1c7..37d4bc6 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperAuthApi.cpp @@ -5,8 +5,8 @@ FDeveloperAuthApi::FDeveloperAuthApi() { - URpmDeveloperSettings* Settings = GetMutableDefault(); - ApiUrl = FString::Printf(TEXT("%s/login"), *Settings->ApiBaseAuthUrl); + const URpmDeveloperSettings* RpmSettings = GetDefault(); + ApiUrl = FString::Printf(TEXT("%s/login"), *RpmSettings->ApiBaseAuthUrl); OnApiResponse.BindRaw(this, &FDeveloperAuthApi::HandleLoginResponse); } diff --git a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp index 07a85f3..365ea42 100644 --- a/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp +++ b/Source/RpmNextGenEditor/Private/Auth/DeveloperTokenAuthStrategy.cpp @@ -9,7 +9,6 @@ DeveloperTokenAuthStrategy::DeveloperTokenAuthStrategy() { AuthApi = FAuthApi(); AuthApi.OnRefreshTokenResponse.BindRaw(this, &DeveloperTokenAuthStrategy::OnRefreshTokenResponse); - } void DeveloperTokenAuthStrategy::AddAuthToRequest(TSharedPtr Request) diff --git a/Source/RpmNextGenEditor/Private/DeveloperAccounts/DeveloperAccountApi.cpp b/Source/RpmNextGenEditor/Private/DeveloperAccounts/DeveloperAccountApi.cpp index 1df33c1..570dbf3 100644 --- a/Source/RpmNextGenEditor/Private/DeveloperAccounts/DeveloperAccountApi.cpp +++ b/Source/RpmNextGenEditor/Private/DeveloperAccounts/DeveloperAccountApi.cpp @@ -17,8 +17,8 @@ FDeveloperAccountApi::FDeveloperAccountApi(IAuthenticationStrategy* InAuthentica void FDeveloperAccountApi::ListApplicationsAsync(const FApplicationListRequest& Request) { // TODO find better way to get settings (or move to editor only code) - URpmDeveloperSettings* Settings = GetMutableDefault(); - ApiBaseUrl = Settings->GetApiBaseUrl(); + const URpmDeveloperSettings* RpmSettings = GetDefault(); + ApiBaseUrl = RpmSettings->GetApiBaseUrl(); const FString QueryString = BuildQueryString(Request.Params); const FString Url = FString::Printf(TEXT("%s/v1/applications%s"), *ApiBaseUrl, *QueryString); FApiRequest ApiRequest; @@ -30,8 +30,8 @@ void FDeveloperAccountApi::ListApplicationsAsync(const FApplicationListRequest& void FDeveloperAccountApi::ListOrganizationsAsync(const FOrganizationListRequest& Request) { // TODO find better way to get settings (or move to editor only code) - URpmDeveloperSettings* Settings = GetMutableDefault(); - ApiBaseUrl = Settings->GetApiBaseUrl(); + const URpmDeveloperSettings* RpmSettings = GetDefault(); + ApiBaseUrl = RpmSettings->GetApiBaseUrl(); const FString QueryString = BuildQueryString(Request.Params); const FString Url = FString::Printf(TEXT("%s/v1/organizations%s"), *ApiBaseUrl, *QueryString); FApiRequest ApiRequest; diff --git a/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp index 6b93425..7020835 100644 --- a/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp +++ b/Source/RpmNextGenEditor/Private/UI/SRpmDeveloperLoginWidget.cpp @@ -324,6 +324,8 @@ FText SRpmDeveloperLoginWidget::GetWelcomeText() const FReply SRpmDeveloperLoginWidget::OnLoginClicked() { + URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + RpmSettings->Reset(); FString Email = EmailTextBox->GetText().ToString(); FString Password = PasswordTextBox->GetText().ToString(); FEditorCache::SetString(CacheKeyEmail, Email); @@ -381,7 +383,7 @@ void SRpmDeveloperLoginWidget::HandleApplicationListResponse(const FApplicationL { if (bWasSuccessful) { - URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + const URpmDeveloperSettings* RpmSettings = GetDefault(); UserApplications = Response.Data; FString Active; TArray Items; @@ -457,7 +459,6 @@ FReply SRpmDeveloperLoginWidget::OnUseDemoAccountClicked() // Unset the authentication strategy for the APIs DeveloperAccountApi->SetAuthenticationStrategy(nullptr); AssetApi->SetAuthenticationStrategy(nullptr); - GetOrgList(); return FReply::Handled(); } @@ -482,7 +483,7 @@ FReply SRpmDeveloperLoginWidget::OnLogoutClicked() void SRpmDeveloperLoginWidget::LoadBaseModelList() { - URpmDeveloperSettings* RpmSettings = GetMutableDefault(); + const URpmDeveloperSettings* RpmSettings = GetDefault(); if (RpmSettings->ApplicationId.IsEmpty()) { UE_LOG(LogTemp, Error, TEXT("Application ID is empty, unable to load base models.")); From 840c3a26bd33875a8a67fa3742dd77220da2564a Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 30 Aug 2024 14:43:05 +0300 Subject: [PATCH 67/74] fix: fix for config settings now persisting to built applications --- .../Private/Settings/RpmDeveloperSettings.cpp | 36 +++++++++++++++---- .../Public/Settings/RpmDeveloperSettings.h | 8 +++-- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp index 1616a32..51fb4b9 100644 --- a/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp +++ b/Source/RpmNextGen/Private/Settings/RpmDeveloperSettings.cpp @@ -2,18 +2,43 @@ #include "Settings/RpmDeveloperSettings.h" -URpmDeveloperSettings::URpmDeveloperSettings() +URpmDeveloperSettings::URpmDeveloperSettings() : ApiBaseUrl(TEXT("https://api.readyplayer.me")), ApiBaseAuthUrl(TEXT("https://readyplayer.me/api/auth")) { LoadConfig(); - ApiBaseUrl = TEXT("https://api.readyplayer.me"); - ApiBaseAuthUrl = TEXT("https://readyplayer.me/api/auth"); } +void URpmDeveloperSettings::PostInitProperties() +{ + Super::PostInitProperties(); +} + +void URpmDeveloperSettings::PreSave(const ITargetPlatform* TargetPlatform) +{ + Super::PreSave(TargetPlatform); + + if(ApiKey.IsEmpty() && ApiProxyUrl.IsEmpty() && !ApplicationId.IsEmpty()) + { + return; + } + + // Ensure settings are saved before the build + SaveConfig(CPF_Config, *GetDefaultConfigFilename()); +} + +#if WITH_EDITOR +void URpmDeveloperSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) +{ + Super::PostEditChangeProperty(PropertyChangedEvent); + SaveConfig(CPF_Config, *GetDefaultConfigFilename()); +} +#endif // WITH_EDITOR + + void URpmDeveloperSettings::SetupDemoAccount() { ApplicationId = DemoAppId; ApiProxyUrl = DemoProxyUrl; - SaveConfig(); + SaveConfig(CPF_Config, *GetDefaultConfigFilename()); } void URpmDeveloperSettings::Reset() @@ -26,8 +51,7 @@ void URpmDeveloperSettings::Reset() { ApiProxyUrl = TEXT(""); } - - SaveConfig(); + SaveConfig(CPF_Config, *GetDefaultConfigFilename()); } FString URpmDeveloperSettings::GetApiBaseUrl() const diff --git a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h index 99fa6fc..ecaeeaf 100644 --- a/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h +++ b/Source/RpmNextGen/Public/Settings/RpmDeveloperSettings.h @@ -18,9 +18,8 @@ class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings FString ApiBaseUrl; public: - URpmDeveloperSettings(); - + UPROPERTY(VisibleAnywhere, Config, Category = "Auth Settings", meta = (ReadOnly = "true", ToolTip = "Base URL for authentication requests.")) FString ApiBaseAuthUrl; @@ -42,7 +41,12 @@ class RPMNEXTGEN_API URpmDeveloperSettings : public UDeveloperSettings return !ApplicationId.IsEmpty() && (!ApiKey.IsEmpty() || !ApiProxyUrl.IsEmpty()); } + virtual void PostInitProperties() override; + virtual void PreSave(const ITargetPlatform* TargetPlatform) override; private: const FString DemoAppId = TEXT("665e05a50c62c921e5a6ab84"); const FString DemoProxyUrl = TEXT("https://api.readyplayer.me/demo"); +#if WITH_EDITOR + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; +#endif // WITH_EDITOR }; From ebed96e22af763abffef58459e7a85311f197d86 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 30 Aug 2024 16:13:28 +0300 Subject: [PATCH 68/74] chore: updated default footwear category name --- .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 129032 -> 129254 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset index 9c82bc5e94328b4b77be43e3708bf7aac706e69a..f8e42955615e4d742dd6837225c949ef3e4c71a4 100644 GIT binary patch delta 2147 zcmbVNdr(wW9KOGUz_PA#!6kPPV>Q%jNt-lqlQPRD{xm-9zGq#!#dmIsK?dXJb!6UGJ3Yn zke$=c^R+xYiL11w=%SGknrI&FNW%+sdH#sXBulT4WZ<}^SrUzXbv(?%YF#Nh zWM;&Wjz{wgnPoPWNtO%lr!)&p(GkOg6?ey!qVu(kAS-xOP{%CZZcCHf(z~50Dr03n z!;`tkcaCNcqE7v3aGK2F}aC6 zO8iuU{*zNWD^-v-X9rV+Ch_Q#&v1N_fI7ZpjJJVDTfb&w=Nm#i0?z}n+hGyjuvmec zl0)z{gA&cYipLBBntP3QChSO7()Bu)l8b{=lrX{2>nTbEKjY&m0?2T>F$?FfQb9jM z=U1uF(>HL2F%xTyDpVms0e$3Rd{?W`0~g#%VO$@ER1?0nT8VZ_P(Xby=JQk~itcB@ z&^1c5uOHjjm~ab)gAx=_+yG-FU9$`JzMw=S18ib3X+d(f`)DDOe!=E60htDAmx+Vd zs!;tP6VCB}wJe?jU&ytp$wxQps{n<%gi9<6& zknS!UGBiVhTQgKJj5{+*(ZPGTD`WE^XChX&l-X-e-#ZEFKl#$JJQ^ zIwf^3&s_!|@#w~x!Z(LU4dZx0j)1zavQl?8k~*$suvyjQ06#WU2K>qcUy`c z(Y0eZ6Y3}yz3L#5RE9w_+>*M@q&^(lfk^l{v2Yo@ggj&EwErZxBVaq|h!6=T(1~4< z5CZh}W)#>0m9A>AYtVJ4(-~Y?S!vsBHP?`84J@NFUJFs=ssBbiqY2bP6WWwyDd$ZC^kR+X#cYiq4Gb-KC2 zTBgn|DWJ4^?MA_Bv8gkyh1MEtg~dwR%Hc|YKlQo9*>zP`d6uoFq@sxU36P?YzE)E? zAG6bB_il&Pq*nlo*ii{b=RBO=l}9r>{wf>@Upif8iRPMu$0pX=0Eft>*C5s>crrVp zJ`8~bnZZJak|2!CeHrW|w-F9LoI?T0-Zfo`!gl}vM24C{NjB|)Xa1Fq_|6_Uq!%;K zK?@@C9uP=O4@6I0cv5hgCi=t$SRz*a2*V(rxdiPm>7wfd>(zdwaFB;Z;ymwpY zDKe&_WeQY+9EwT&LQsje+b{sW3i=58AI)d~(HtY`8igR@yaNjI<{i*IHf(roL_uB} c0nLAe?~OqG^x>jWn08$2v~n?b4D4Qi19)S!`~Uy| delta 1918 zcmaJ?3rrM87~X&6^f*wKDiu8qzzd1uwbA*Vh}LZ z)JIG8S4Y!Wt)?NZQPFE$HLXh0)L2u6*fcqVJ+-LBXb>qi(WZ1}Z>gpY?q+tr`TpBuYNCn zu|pHwi(WL&cOv?M1mHXH`UD;Pf~V%xlfhKXO3cGRayT|6OQbAKYnn+{8m_c>u-qa- zF4kGRBsD`NtSKVN&%^|)$H+&lXJvpDe!Vr9(->hDp%CM(Ub57NU8x?tYZZyt&Y_Vo z8Mm~DaDyOB%2gHTQbo9govB{3t_WMwJxJ3;GTp_YF^tK$rB$;S%2Y*dx(Iu*Io(TQ zs#M)LP1mUDcF8Ioq0vgv{-?)D8&t)Wxq8F! zAimjzH|NSEZ7Zuh*fvijms&YAj?ug)weDJWs){C?NUVEtyGJYLF` zNT?r6^4yq_XClUH49YMOi}PF#_Q9TGVfz~c@X26?--Kq478u%gh7fdUh03Btlc6Y1tqhGbk;xCE!XVIo^^ zV&t1{Y-R995I$uv;}&COg1mC8x_h_OH=0=#Ylz@`m0h;qv7^X|ZAB6(>S0hOt9w*Y zU?DpJ3ng-3xM?OAgRrpJOdj>H33=DhI69amaG!am(WF@rg8PdlY%K}LnI#e_9Z++% zmY9j*4@_{fv)~lTDGp`wcaXX36d?!WolA{;hwvrI3nmq|3}TndgXdf#>Euv`@!08F ziri7k(f=F=|HPKXPF%iNBE1~4MSRRh;G5&Fh|;yO56NV-d*ktRbrjV2ey)B@#vVB` zG&G8RYxup)*HyDG1e3Pw$1W18g*u^1Xkhd>>{{`%@94_9T>N6aer&^Yp);7!{F<*y z(XRzNtxbYG(95gUv}qQcft?)BP^xA_3(=*sjrN4zPliUYQrQA-uqquEFavAeNr8q5 zk=kqXDz3Z`6{| z@3+7fnz9Q_w0;vzrOlRYP{j>+lB6vm@pH+b$`xttBJWN!^-v&t^Ip`Pj zZQ0)Q>wnET5IUj3ew6l05Ce9_^8j=t+Q{_mH&U+tMuz_)G|&gH(%pTKK61QbgwVo= jP(DIP&3gMGWQ`n;r6v84`=WS9KR8|xS1ga<_~?HCf(K^* From 7935db0064a5739e463ec19dcf4c5e7a805758d6 Mon Sep 17 00:00:00 2001 From: Harrison Hough Date: Fri, 30 Aug 2024 19:29:25 +0300 Subject: [PATCH 69/74] chore: update default name --- .../Blueprints/WBP_LoaderUI.uasset | Bin 109246 -> 109311 bytes .../BasicLoader/RpmBasicLoaderSample.umap | Bin 91386 -> 91113 bytes .../BasicUI/Blueprints/WBP_BasicUI.uasset | Bin 129254 -> 129230 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Content/Samples/BasicLoader/Blueprints/WBP_LoaderUI.uasset b/Content/Samples/BasicLoader/Blueprints/WBP_LoaderUI.uasset index ee4eddf3579774fe4ab080e75d8cb8e18bf4060e..527cff2da4cd91206371da1c56e9d8c9ef867121 100644 GIT binary patch delta 2833 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 diff --git a/Content/Samples/BasicLoader/RpmBasicLoaderSample.umap b/Content/Samples/BasicLoader/RpmBasicLoaderSample.umap index c02a1375ed3eb872678f8434de76a6bda7f21865..d81c14b8855a168ef28dc22ac9682dd0a81c507c 100644 GIT binary patch delta 17268 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 delta 17753 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 diff --git a/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset b/Content/Samples/BasicUI/Blueprints/WBP_BasicUI.uasset index f8e42955615e4d742dd6837225c949ef3e4c71a4..ef7c38293c5f7a732a919dc251b3e3e11a419c48 100644 GIT binary patch delta 1072 zcmYk4Ye*DP6vsP9Hy;U(GQm_-R?AXq*#m@1UA5ZQwsf7vmDY&DbZe)ys6YwSu!N*o z@u0U4_5eTZm#77!DM~CU)Wk>hAyz8Q9@d8_DSAS6=k8ivxOaZ%fByfub1(B|I%aq} zCTIcaM^#7?>OriQb4rtJ;L4-YS8aTC5t5U8FpVjjrX&RGKScOJ}4s zJbs+{KkxqL^^wg!@pUA0A1d!VR^sU#kIwZI7mmFskFxuOCM}0G`K~)LgCMju$oBSMy!uWH$%%jo{ z4I21bDqK%qiX&wdnlb$q-78cnP^dr--7VD6`&#tIixQ~5NWqqw(u}vl=x~~0k`6mH z9C^>JsciKch^9+Uh5W?{lu@kU($C@*{$ewZex@a=hZ0o@>L^9k;67i=XiAkZ{)^E{ zGX4eQ{%w33!%3sAGU{XcMep4ZFa^&Urx6 zN1I>(!ZG`K-kyI3_KGZfd*;7z-@gds5mk>r3+t_5W%VIYSVagNY#;=(DTQB?iCuUM i7Jc<2IEeb#5cHbZ^$`DtUix*>CZwCxA+eL8+aN3 delta 1164 zcmZ{jZD>Tn)NI}e1L;`xix?z=L>Evf`hcr zrhU+zvOtXNRyt@4(uVaC4ulNMOy|0pS8LMIvP(E}N+gVRQE^EKo4*vwbFReqi;N|i zxu49m_X@}LQnO`VQfZ=8!?F`JSsJ9lG6~0`24{SkC1N26t9rr zFg>o&u<|z{=#>(_9u*Pe{zj!FBftJB91*7kqjcV>VfR&WioZ(1tQd8A^Qke?5$ZHYbDUsf2&at6N242M{Afbp zC)F~xCy-Uk@G}ipYxp7|?5dH$PL1vW#cQn4C&=x#!XzgR*G*BATcuUCR*X(j#wL{_ z9PcLZWv!J*rA-Eb1pSoj)8ICL zHW1sA0nfzF!ukvv^Ec3_UxFVwX)r6+(>@7p&uQEr%rHI|;}yAh8KZ}K>ix8%UWO~2 zG~6<09QV~)RbQ*Z4{6N16?U-N?XZ{GFM^4URX~t6Hi4NHG{Y&@qrwvQZ9Djw+QKb8 zpL5GZ7gXu1TcHmU;jFg*hhMe56IMd+gr9~$UB=?uU?;oyHQ$M}aXNVjPC)^?)C|^? z%BJ1TtfL+BnXM6y!DphXuai zOXm^jPjq)2o$sD~?0wzY&2PZUwjF;@?-~yj=_^lwrQDRuk2bM}>yW2M$DsrDvTN|W jNndgkhD}h;w#Gna&KS6jzkUJm%4OjgDEj5Uz>@O@%hilF From 6702fcf7128d7e72afbd210242a8e28cb5d110d8 Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 2 Sep 2024 09:59:29 +0300 Subject: [PATCH 70/74] feat: added function library and get first asset id function --- .../Blueprints/BP_RpmPreviewActor.uasset | Bin 52024 -> 57799 bytes .../RpmNextGen/Private/RpmFunctionLibrary.cpp | 37 ++++++++++++++++++ .../Private/RpmPreviewLoaderComponent.cpp | 7 +--- Source/RpmNextGen/Public/RpmFunctionLibrary.h | 22 +++++++++++ .../Public/RpmPreviewLoaderComponent.h | 5 +-- 5 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 Source/RpmNextGen/Private/RpmFunctionLibrary.cpp create mode 100644 Source/RpmNextGen/Public/RpmFunctionLibrary.h diff --git a/Content/Samples/BasicLoader/Blueprints/BP_RpmPreviewActor.uasset b/Content/Samples/BasicLoader/Blueprints/BP_RpmPreviewActor.uasset index 42c8738d471736233c8700fbca936e7941c88d9f..9f04b92d45f785aad78be38c67ff8c688b43d233 100644 GIT binary patch literal 57799 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 literal 52024 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 eR6ZCS^bM)qhC@}m AssetApi = MakeShared(); + + const URpmDeveloperSettings* RpmSettings = GetDefault(); + FAssetListQueryParams QueryParams; + QueryParams.Type = AssetType; + QueryParams.ApplicationId = RpmSettings->ApplicationId; + FAssetListRequest AssetListRequest = FAssetListRequest(QueryParams); + + if (!WorldContextObject) + { + UE_LOG(LogTemp, Error, TEXT("WorldContextObject is null")); + return; + } + + AssetApi->OnListAssetsResponse.BindLambda([OnAssetIdFetched, AssetApi](const FAssetListResponse& Response, bool bWasSuccessful) + { + FString FirstAssetId; + if (bWasSuccessful && Response.Data.Num() > 0) + { + FirstAssetId = Response.Data[0].Id; + } + OnAssetIdFetched.ExecuteIfBound(FirstAssetId); + }); + + AssetApi->ListAssetsAsync(AssetListRequest); +} diff --git a/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp index b26ec19..7ebf553 100644 --- a/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp +++ b/Source/RpmNextGen/Private/RpmPreviewLoaderComponent.cpp @@ -26,13 +26,8 @@ URpmPreviewLoaderComponent::URpmPreviewLoaderComponent() CharacterApi->OnCharacterFindResponse.BindUObject(this, &URpmPreviewLoaderComponent::HandleCharacterFindResponse); } -void URpmPreviewLoaderComponent::CreateCharacter() +void URpmPreviewLoaderComponent::CreateCharacter(const FString& BaseModelId) { - if(BaseModelId.IsEmpty()) - { - UE_LOG(LogReadyPlayerMe, Error, TEXT("BaseModelId is empty on %s"), *GetOwner()->GetName()); - return; - } FCharacterCreateRequest CharacterCreateRequest = FCharacterCreateRequest(); CharacterCreateRequest.Data.Assets = TMap(); CharacterCreateRequest.Data.Assets.Add("baseModel", BaseModelId); diff --git a/Source/RpmNextGen/Public/RpmFunctionLibrary.h b/Source/RpmNextGen/Public/RpmFunctionLibrary.h new file mode 100644 index 0000000..e0ffbdc --- /dev/null +++ b/Source/RpmNextGen/Public/RpmFunctionLibrary.h @@ -0,0 +1,22 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "RpmFunctionLibrary.generated.h" + +DECLARE_DYNAMIC_DELEGATE_OneParam(FOnAssetIdFetched, FString, AssetId); + +/** + * + */ +UCLASS() +class RPMNEXTGEN_API URpmFunctionLibrary : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintCallable, Category = "ReadyPlayerMe", meta = (WorldContext = "WorldContextObject")) + static void FetchFirstAssetId(UObject* WorldContextObject, const FString& AssetType, FOnAssetIdFetched OnAssetIdFetched); +}; diff --git a/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h b/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h index 106b04a..e52e9ec 100644 --- a/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h +++ b/Source/RpmNextGen/Public/RpmPreviewLoaderComponent.h @@ -23,12 +23,9 @@ class RPMNEXTGEN_API URpmPreviewLoaderComponent : public URpmAssetLoaderComponen public: URpmPreviewLoaderComponent(); - - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Ready Player Me") - FString BaseModelId; UFUNCTION(BlueprintCallable, Category = "Ready Player Me") - virtual void CreateCharacter(); + virtual void CreateCharacter(const FString& BaseModelId); UFUNCTION(BlueprintCallable, Category = "Ready Player Me") virtual void LoadCharacter(FRpmCharacter CharacterData); From 6fb300df33eafe5f94340d56add7083bdb9487da Mon Sep 17 00:00:00 2001 From: Harrison Date: Mon, 2 Sep 2024 10:19:41 +0300 Subject: [PATCH 71/74] chore: refactor map blueprint --- .../BasicLoader/RpmBasicLoaderSample.umap | Bin 91113 -> 119333 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Content/Samples/BasicLoader/RpmBasicLoaderSample.umap b/Content/Samples/BasicLoader/RpmBasicLoaderSample.umap index d81c14b8855a168ef28dc22ac9682dd0a81c507c..b076561a8e047effbc23e5d3cd59db0e9bb863cf 100644 GIT binary patch literal 119333 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